diff --git a/.eslintrc.js b/.eslintrc.js index 0fe345ac33..9400740e6f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -73,21 +73,6 @@ module.exports = { 'no-console': 'warn', }, }, - { - // ============================================================================== - // LEGACY SUPPORT: disable v0.10 lint rules for legacy code (remove when legacy is dropped) - // ============================================================================== - files: [ - 'packages/react-native-executorch/legacy/**/*.{js,jsx,ts,tsx}', - 'packages/bare-resource-fetcher/**/*.{js,jsx,ts,tsx}', - 'packages/expo-resource-fetcher/**/*.{js,jsx,ts,tsx}', - ], - rules: { - 'jsdoc/match-description': 'off', - 'camelcase': 'off', - 'eslint-comments/no-unused-disable': 'off', - }, - }, { // The JSDoc rules exist to keep the generated API reference complete. // Test helpers are not part of that surface, and requiring a tag per diff --git a/.gitattributes b/.gitattributes index 9a35d1c7fe..41b0fed59b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,11 +2,10 @@ # specific for windows script files *.bat text eol=crlf -# Exclude apps, third-party, and legacy code from language stats & line counts +# Exclude apps and third-party code from language stats & line counts apps/** linguist-vendored third-party/** linguist-vendored **/third-party/** linguist-vendored -**/legacy/** linguist-vendored # Exclude documentation from codebase language stats docs/** linguist-documentation \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a6860e26d..7b46ab8cf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,11 +70,11 @@ jobs: # eslint, tsc and jest read none of the C++ trees, and none of the # documentation. - ts_irrelevant='^(docs/|[^/]+\.md$|packages/react-native-executorch/(cpp|legacy/cpp)/)' + ts_irrelevant='^(docs/|[^/]+\.md$|packages/react-native-executorch/cpp/)' if echo "$files" | grep -qvE "$ts_irrelevant"; then ts=true; else ts=false; fi # The C++ suite compiles cpp/ only (cpp/tests/CMakeLists.txt globs - # core/ and extensions/, never legacy/). The rest of this list is what + # core/ and extensions/). The rest of this list is what # decides which ExecuTorch release it builds and links against: # package.json carries nativeLibsVersion, which download-libs.js reads # to pick the headers release, and the CI job cross-checks that @@ -147,7 +147,7 @@ jobs: run: yarn lint - name: Typecheck files - run: yarn workspaces foreach --all --exclude react-native-executorch-webrtc --topological-dev run prepare && yarn typecheck + run: yarn workspaces foreach --all --topological-dev run prepare && yarn typecheck build-library: needs: changes @@ -161,7 +161,7 @@ jobs: uses: ./.github/actions/setup - name: Build all packages - run: yarn workspaces foreach --all --exclude react-native-executorch-webrtc --topological-dev run prepare + run: yarn workspaces foreach --all --topological-dev run prepare native-tests: name: C++ unit tests diff --git a/.github/workflows/npm-publish-satellites.yml b/.github/workflows/npm-publish-satellites.yml deleted file mode 100644 index 0513a6ba0b..0000000000 --- a/.github/workflows/npm-publish-satellites.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: NPM publish satellite packages - -on: - workflow_dispatch: - inputs: - package: - description: Package to publish. - type: choice - options: - - react-native-executorch-bare-resource-fetcher - - react-native-executorch-expo-resource-fetcher - - react-native-executorch-webrtc - required: true - release-type: - description: Type of release to publish. - type: choice - options: [stable, nightly, beta, alpha, rc] - default: stable - version: - description: Explicit version in x.y.z format (leave empty to use the version in the package's package.json). - type: string - required: false - default: '' - perform-git-operations: - description: Create and push the release commit + tag. - type: boolean - default: false - dry-run: - description: Dry run — no actual publish or git push. - type: boolean - default: true - -permissions: - id-token: write - contents: read - -concurrency: - group: publish-${{ inputs.package }}-${{ github.ref }} - cancel-in-progress: false - -jobs: - publish: - if: github.repository == 'software-mansion/react-native-executorch' - runs-on: ubuntu-latest - environment: deployment - env: - # The core package's postinstall downloads the native artifacts. Packing a - # satellite runs tsc against the core's type declarations and needs none - # of them. - RNET_SKIP_DOWNLOAD: 1 - permissions: - # `contents: write` is required when perform-git-operations pushes a tag. - contents: write - id-token: write - steps: - - name: Check out - uses: actions/checkout@v6 - - - name: Setup Node - uses: actions/setup-node@v6 - with: - node-version: 24 - registry-url: https://registry.npmjs.org/ - - # The action infers a stable version from the branch name, matching - # /^(\d+)\.(\d+)-stable$/ (see version-utils.js). This repo names its - # release branches release/0.9, so inference throws "Failed to parse - # stable version from branch" on every branch it could run from, main - # included. Resolve it here instead: the package.json version is what - # RELEASE.md already tells you to set, and it is what the core publish - # workflow uses. An explicit input still wins. - - name: Resolve the version to publish - id: version - env: - INPUT_VERSION: ${{ inputs.version }} - PACKAGE: ${{ inputs.package }} - run: | - case "$PACKAGE" in - react-native-executorch-bare-resource-fetcher) DIR=packages/bare-resource-fetcher ;; - react-native-executorch-expo-resource-fetcher) DIR=packages/expo-resource-fetcher ;; - react-native-executorch-webrtc) DIR=packages/react-native-executorch-webrtc ;; - *) echo "::error::unknown package: $PACKAGE"; exit 1 ;; - esac - - if [ -n "$INPUT_VERSION" ]; then - VERSION="$INPUT_VERSION" - echo "using the version input: $VERSION" - else - VERSION=$(jq -r .version "$DIR/package.json") - echo "using $DIR/package.json: $VERSION" - fi - - if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then - echo "::error::expected x.y.z, got '$VERSION'" - exit 1 - fi - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - - name: Publish react-native-executorch-bare-resource-fetcher - if: ${{ inputs.package == 'react-native-executorch-bare-resource-fetcher' }} - uses: software-mansion/npm-package-publish@22b6e632b4310c5c061a047bccc99d0cea88d2bb - with: - package-name: 'react-native-executorch-bare-resource-fetcher' - package-json-path: 'packages/bare-resource-fetcher/package.json' - # `workspaces focus` installs this satellite and the workspaces it - # depends on, instead of the whole monorepo: the root globs pull in - # eight React Native example apps, 1660 packages and 597 MiB, to build - # one small package. Satellites type-check against the core package's - # type declarations, so it still gets built before the satellite packs. - install-dependencies-command: 'yarn workspaces focus react-native-executorch-bare-resource-fetcher && yarn workspace react-native-executorch prepare' - release-type: ${{ inputs.release-type }} - version: ${{ steps.version.outputs.version }} - perform-git-operations: ${{ inputs.perform-git-operations }} - dry-run: ${{ inputs.dry-run }} - - - name: Publish react-native-executorch-expo-resource-fetcher - if: ${{ inputs.package == 'react-native-executorch-expo-resource-fetcher' }} - uses: software-mansion/npm-package-publish@22b6e632b4310c5c061a047bccc99d0cea88d2bb - with: - package-name: 'react-native-executorch-expo-resource-fetcher' - package-json-path: 'packages/expo-resource-fetcher/package.json' - # See the first satellite above for why this is a focused install. - install-dependencies-command: 'yarn workspaces focus react-native-executorch-expo-resource-fetcher && yarn workspace react-native-executorch prepare' - release-type: ${{ inputs.release-type }} - version: ${{ steps.version.outputs.version }} - perform-git-operations: ${{ inputs.perform-git-operations }} - dry-run: ${{ inputs.dry-run }} - - - name: Publish react-native-executorch-webrtc - if: ${{ inputs.package == 'react-native-executorch-webrtc' }} - uses: software-mansion/npm-package-publish@22b6e632b4310c5c061a047bccc99d0cea88d2bb - with: - package-name: 'react-native-executorch-webrtc' - package-json-path: 'packages/react-native-executorch-webrtc/package.json' - # See the first satellite above for why this is a focused install. - install-dependencies-command: 'yarn workspaces focus react-native-executorch-webrtc && yarn workspace react-native-executorch prepare' - release-type: ${{ inputs.release-type }} - version: ${{ steps.version.outputs.version }} - perform-git-operations: ${{ inputs.perform-git-operations }} - dry-run: ${{ inputs.dry-run }} diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 03cf204010..95303d4b91 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -78,7 +78,7 @@ jobs: if: ${{ env.EXECUTORCH_VERSION == 'PLACEHOLDER' }} run: exit 1 # this should never happen - # The root workspaces glob is packages/* + apps/* + apps/legacy/*, so a + # The root workspaces glob is packages/* + apps/*, so a # plain `yarn install` pulled 1659 packages (597 MiB) to build one of # them: 164s of a 197s job, against 19s for the actual build. `workspaces # focus` installs only this package's tree; it is built into Yarn 4, the diff --git a/.gitignore b/.gitignore index 83eef27ecd..384eb8ba29 100644 --- a/.gitignore +++ b/.gitignore @@ -92,11 +92,6 @@ ios/generated android/generated apps/*/ios/ apps/*/android/ -# === LEGACY SUPPORT: Expo prebuild directories for legacy apps (remove when legacy apps are dropped) === -apps/legacy/*/ios/ -apps/legacy/*/android/ -!apps/legacy/bare-rn/ios/ -!apps/legacy/bare-rn/android/ **/Pods/ # generated TypeDoc API reference (regenerated at build time) diff --git a/LEGACY_REMOVAL.md b/LEGACY_REMOVAL.md deleted file mode 100644 index 6300520065..0000000000 --- a/LEGACY_REMOVAL.md +++ /dev/null @@ -1,218 +0,0 @@ -# Removing Legacy Support Checklist - -This document details all directories, files, configurations, and dependencies to remove or clean up when dropping legacy API support (`react-native-executorch/legacy` and `0.10.0-legacy`) from the repository. - -Every legacy touchpoint in the codebase is demarcated with `// === LEGACY SUPPORT ===` or `# === LEGACY SUPPORT ===`. - ---- - -## 1. Packages & Directories to Delete - -### Example Apps - -- [ ] Delete `apps/legacy/` completely: - ```bash - rm -rf apps/legacy - ``` - -### Library Legacy Code - -- [ ] Delete `packages/react-native-executorch/legacy/` completely: - ```bash - rm -rf packages/react-native-executorch/legacy - ``` - -### Obsolete Resource Fetcher Packages - -In the rewrite, resource fetching/caching was moved directly into core (`packages/react-native-executorch/src/fetcher/`). The standalone fetcher packages only exist to support the legacy example apps and can be completely removed: - -- [ ] Delete `packages/expo-resource-fetcher/` completely: - ```bash - rm -rf packages/expo-resource-fetcher - ``` -- [ ] Delete `packages/bare-resource-fetcher/` completely: - ```bash - rm -rf packages/bare-resource-fetcher - ``` - -### Documentation - -- [ ] Delete legacy versioned docs and sidebars: - ```bash - rm -rf docs/versioned_docs/version-0.10.0-legacy - rm -rf docs/versioned_sidebars/version-0.10.0-legacy-sidebars.json - ``` -- [ ] In `docs/versions.json`, remove `"0.10.0-legacy"`. -- [ ] In `docs/docusaurus.config.js`, remove: - - `lastVersion: 'current'` (or revert to standard versioning) - - `'0.10.0-legacy'` entry and comment under `versions` - - The `current` label override if no longer needed -- [ ] In `docs/docs/01-fundamentals/03-migrating-from-v0-9.md`, remove the legacy migration path Option A and link to `0.10.0-legacy`. -- [ ] In `.gitattributes`, remove `**/legacy/** linguist-vendored` and `docs/versioned_docs/version-0.10.0-legacy/** linguist-vendored` if present. - ---- - -## 2. Files to Delete - -- [ ] `packages/react-native-executorch/src/native/NativeETInstaller.ts` -- [ ] `packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt` -- [ ] `packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstallerUnavailable.kt` - ---- - -## 3. Configuration Edits - -### Root `package.json` - -- [ ] Under `"workspaces" -> "packages"`, remove: - - `"apps/legacy/*"` - - `"// === LEGACY SUPPORT: workspaces ==="` - -### Root `.gitignore` - -- [ ] Remove the `# === LEGACY SUPPORT ===` section: - ```gitignore - apps/legacy/*/ios/ - apps/legacy/*/android/ - !apps/legacy/bare-rn/ios/ - !apps/legacy/bare-rn/android/ - ``` - -### Root `.eslintrc.js` - -- [ ] Remove the `overrides` block targeting `"packages/react-native-executorch/legacy/**/*"` and `"apps/legacy/**/*"`. - -### `packages/react-native-executorch/package.json` - -- [ ] In `"exports"`, remove: - - `"./legacy": { ... }` - - `"// === LEGACY SUPPORT: exports ==="` -- [ ] In `"files"`, remove: - - `"legacy"` - - `"!legacy/cpp/**/tests"` - - `"// === LEGACY SUPPORT: files ==="` -- [ ] In `"scripts"`, change `"prepare"` from: - ```json - "prepare": "bob build && tsc -p legacy/tsconfig.json" - ``` - to: - ```json - "prepare": "bob build" - ``` - and remove `"// === LEGACY SUPPORT: prepare script ==="`. -- [ ] In `"dependencies"`, remove legacy-only dependencies: - - `"jsonrepair"` - - `"jsonschema"` - - `"react-native-device-info"` - - `"zod"` - - `"// === LEGACY SUPPORT: dependencies ==="` -- [ ] In `"peerDependencies"` and `"peerDependenciesMeta"`, remove: - - `"@kesha-antonov/react-native-background-downloader"` - - `"// === LEGACY SUPPORT: peerDependencies ==="` - -### `packages/react-native-executorch/tsconfig.json` - -- [ ] Re-enable `verbatimModuleSyntax: true` (or remove `verbatimModuleSyntax: false`). - -### `packages/react-native-executorch/tsconfig.build.json` - -- [ ] In `"exclude"`, remove `"legacy"`. - -### `packages/react-native-executorch/react-native-executorch.podspec` - -- [ ] Remove the `# === LEGACY SUPPORT ===` source files block: - ```ruby - source_files += [ - "legacy/ios/**/*.{h,m,mm}", - "legacy/cpp/**/*.{cpp,c,h,hpp}", - ] - ``` -- [ ] Remove the `# === LEGACY SUPPORT ===` exclusion block: - ```ruby - exclude_files += [ - "legacy/cpp/rnexecutorch/tests/**/*", - "legacy/cpp/rnexecutorch/jsi/*.{h,hpp}", - ] - s.preserve_paths = "legacy/cpp/rnexecutorch/jsi/*.{h,hpp}" - ``` -- [ ] Remove the `# === LEGACY SUPPORT ===` header search path: - ```ruby - "\"$(PODS_TARGET_SRCROOT)/legacy/cpp\"", - ``` - -### `packages/react-native-executorch/android/CMakeLists.txt` - -- [ ] Remove directory definitions: - ```cmake - set(LEGACY_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../legacy/cpp") - set(LEGACY_ANDROID_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../legacy/android") - ``` -- [ ] Remove `find_package(fbjni REQUIRED CONFIG)`. -- [ ] Remove `file(GLOB_RECURSE LEGACY_CPP_SOURCES ...)`. -- [ ] Remove `list(APPEND RNE_SOURCES ${LEGACY_CPP_SOURCES} ${LEGACY_ANDROID_DIR}/ETInstallerModule.cpp)`. -- [ ] Remove `${LEGACY_CPP_DIR}` and `${LEGACY_ANDROID_DIR}` from `target_include_directories`. -- [ ] In `target_link_libraries`, remove: - ```cmake - ReactAndroid::reactnative - fbjni::fbjni - android - ``` - -### `packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/RnExecutorchPackage.kt` - -- [ ] In `getModule`, remove the `ETInstaller.NAME -> { ... }` case. -- [ ] In `getReactModuleInfoProvider`, remove the `ETInstaller.NAME to ReactModuleInfo(...)` entry. - ---- - -## 4. Git Diff Verification Against `rne-rewrite` - -Once the legacy support code and configurations have been removed, run a git diff against the `rne-rewrite` branch. **These modified configuration and build files should be identical to `rne-rewrite` (i.e. produce zero diff)**: - -```bash -# Verify modified configuration/build files are identical to rne-rewrite (must return 0 diff): -git diff --exit-code origin/rne-rewrite -- \ - package.json \ - .gitignore \ - .eslintrc.js \ - packages/react-native-executorch/package.json \ - packages/react-native-executorch/tsconfig.json \ - packages/react-native-executorch/tsconfig.build.json \ - packages/react-native-executorch/react-native-executorch.podspec \ - packages/react-native-executorch/android/CMakeLists.txt \ - packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/RnExecutorchPackage.kt - -# Check overall branch diff (should only show newly added docs or subsequent bugfixes): -git diff --stat origin/rne-rewrite -``` - -Ensure no leftover legacy markers remain in the repository: - -```bash -git grep "LEGACY SUPPORT" -``` - -_(This command should return 0 results after complete removal)._ - ---- - -## 5. Verification After Removal - -Run the following commands to confirm that the clean repository builds and passes all checks: - -```bash -# 1. Re-link workspace dependencies -yarn install - -# 2. Build bundles -yarn prepare - -# 3. Typecheck -yarn typecheck - -# 4. Run test suites -yarn workspace react-native-executorch test - -# 5. Lint -yarn lint -``` diff --git a/LICENSE b/LICENSE index a780ca46db..aa15d47034 100644 --- a/LICENSE +++ b/LICENSE @@ -29,10 +29,8 @@ This software bundles or links the components below. Each is distributed under its own license, reproduced in full further down this file. ExecuTorch BSD 3-Clause native runtime and hardware backends - pffft / FFTPACK BSD 3-Clause legacy/cpp/pfft, audio FFT OpenCV Apache-2.0 vision pipelines and multimodal LLMs KleidiCV Apache-2.0 OpenCV Arm HAL, Android only - ada MIT legacy/cpp/ada, URL parsing phonemis MIT grapheme-to-phoneme for text-to-speech MLX MIT MLX backend, iOS only @@ -84,57 +82,6 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=============================================================================== -pffft and FFTPACK, BSD 3-Clause -=============================================================================== - -Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) - -Based on original fortran 77 code from FFTPACKv4 from NETLIB, -authored by Dr Paul Swarztrauber of NCAR, in 1985. - -As confirmed by the NCAR fftpack software curators, the following -FFTPACKv5 license applies to FFTPACKv4 sources. My changes are -released under the same terms. - -FFTPACK license: - -http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html - -Copyright (c) 2004 the University Corporation for Atmospheric -Research ("UCAR"). All rights reserved. Developed by NCAR's -Computational and Information Systems Laboratory, UCAR, -www.cisl.ucar.edu. - -Redistribution and use of the Software in source and binary forms, -with or without modification, is permitted provided that the -following conditions are met: - -- Neither the names of NCAR's Computational and Information Systems -Laboratory, the University Corporation for Atmospheric Research, -nor the names of its sponsors or contributors may be used to -endorse or promote products derived from this Software without -specific prior written permission. - -- Redistributions of source code must retain the above copyright -notices, this list of conditions, and the disclaimer below. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions, and the disclaimer below in the -documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE -SOFTWARE. - - =============================================================================== phonemis, MIT =============================================================================== @@ -185,31 +132,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -=============================================================================== -ada, MIT -=============================================================================== - -Copyright 2023 Yagiz Nizipli and Daniel Lemire - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - =============================================================================== OpenCV and KleidiCV, Apache License 2.0 =============================================================================== diff --git a/RELEASE.md b/RELEASE.md index b9b329e69d..7813c5bf06 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -17,7 +17,7 @@ Both are plain string literals; there is no `LIB_VERSION` to derive them from. > [!CAUTION] > These constants were renamed **and their meanings swapped** in v0.10. Before > that they lived in `src/constants/modelUrls.ts` / `src/constants/versions.ts`, -> were derived from `LIB_VERSION`, and `VERSION_TAG` meant the *in-development* +> were derived from `LIB_VERSION`, and `VERSION_TAG` meant the _in-development_ > tag while `PREVIOUS_VERSION_TAG` meant the published one. Today `VERSION_TAG` > is the **published** tag and `NEXT_VERSION_TAG` is the in-development one. > Editing `VERSION_TAG` by analogy with the old docs repoints every already @@ -53,21 +53,7 @@ The release process of new minor version consists of the following steps: 1. On `main`, confirm every model URL in `packages/react-native-executorch/src/models.ts` that should ship in this release points at `NEXT_VERSION_TAG`, and that the matching files exist on [🤗 huggingface](https://huggingface.co/software-mansion) under the `v{MAJOR}.{MINOR}.0` tag. See [HuggingFace artifacts](#huggingface-artifacts) for moving those tags. 2. Make sure `NEXT_VERSION_TAG` in `packages/react-native-executorch/src/models.ts` is `resolve/v{MAJOR}.{MINOR}.0`. -3. Ensure the `version` field of `package.json` is `{MAJOR}.{MINOR}.0` for all four published packages: - - `packages/react-native-executorch` (core) - - `packages/bare-resource-fetcher` - - `packages/expo-resource-fetcher` - - `packages/react-native-executorch-webrtc` - - The core publish workflow reads the version from - `packages/react-native-executorch/package.json`, so a wrong value there is - what gets published. The satellite workflow takes an explicit `version` - input instead. - - The two fetcher adapters are still required at `{MAJOR}.{MINOR}.0`: the core - package ships the pre-0.10 API as `react-native-executorch/legacy`, and that - path throws at runtime unless the consumer passes an adapter from one of - them. Only the new API has a built-in fetcher. +3. Ensure the `version` field in `packages/react-native-executorch/package.json` is `{MAJOR}.{MINOR}.0`. The core publish workflow reads this value. 4. If any of the above required changes, commit them on `main` with the message 'Release v{MAJOR}.{MINOR}.0'. 5. Create a new release branch `release/{MAJOR}.{MINOR}` from `main` and push it to the remote. 6. Confirm the native artifacts release the package resolves at install time @@ -76,32 +62,30 @@ The release process of new minor version consists of the following steps: pinned in the core `package.json`; if that release or any of its assets is missing, every consumer's `postinstall` fails. 7. Stability tests are performed on the release branch and all fixes to the new-found issues are pushed into the main branch and cherry-picked into the release branch. This allows for further development on the main branch without interfering with the release process. -8. Once all tests are passed, tag the release branch with proper version tag `v{MAJOR}.{MINOR}.0` and run the following publish workflows. Both take a `dry-run` input; a dry run builds and packs without publishing, which is worth doing first since a failed publish cannot be taken back: +8. Once all tests are passed, tag the release branch with proper version tag `v{MAJOR}.{MINOR}.0` and run the core publish workflow. It takes a `dry-run` input; a dry run builds and packs without publishing, which is worth doing first since a failed publish cannot be taken back: - [npm publish (core)](https://github.com/software-mansion/react-native-executorch/actions/workflows/npm-publish.yml) - - [npm publish satellite packages](https://github.com/software-mansion/react-native-executorch/actions/workflows/npm-publish-satellites.yml) — run once per satellite package, selecting it via the `package` input (`react-native-executorch-bare-resource-fetcher`, `react-native-executorch-expo-resource-fetcher`, `react-native-executorch-webrtc`) -9. Move the `legacy` dist-tag on npm to the previous minor line, for the core package and every satellite: +9. Move the `legacy` dist-tag on npm to the previous minor line, for the core package: ``` npm dist-tag add react-native-executorch@{PREV_MAJOR}.{PREV_MINOR}.{HIGHEST_PATCH} legacy ``` - Use the previous line's **highest published patch**, not its `.0`. Repeat for - each satellite, whose patch numbers advance independently of the core (a - satellite may have no release on that line at all, in which case it has no - `legacy` tag to move). + Use the previous line's **highest published patch**, not its `.0`. This is manual on purpose. The publish action derives a tag itself, but only ever as `latest` for the newest version or `legacy` for an older one, and when it picks `legacy` it then runs `npm dist-tag rm legacy` on the way out. Nothing in the automation repoints `legacy` at the outgoing line, so skipping this step leaves it on whichever release last set it by hand. + 10. Create the release notes on GitHub. 11. Bump `main` to the next development cycle in a single PR: - - Bump `version` in `package.json` to `{MAJOR}.{NEXT_MINOR}.0` for all four published packages. Leave `nativeLibsVersion` alone; it names the GitHub artifacts release that exists today, and moving it ahead breaks every consumer's `postinstall`. - - In `models.ts`, rename `NEXT_VERSION_TAG` to `VERSION_TAG`, keeping its value at `resolve/v{MAJOR}.{MINOR}.0` (the version just published). This rewrites every model URL by construction, which is what you want: the URLs name the constant, not the tag, so bumping the value alone would silently repoint every model at a release that does not exist yet. - - Do **not** add `NEXT_VERSION_TAG` back yet. Exactly one of the two constants exists at a time: whichever has no reader fails `noUnusedLocals`. The first model re-exported for the next release declares `NEXT_VERSION_TAG = 'resolve/v{MAJOR}.{NEXT_MINOR}.0'` and points its own URL at it; the rest stay on `VERSION_TAG`. - - Bump `LIB_VERSION` in `packages/react-native-executorch/src/fetcher/telemetry.ts` to match. A unit test asserts it equals the package version, so a missed bump fails CI rather than mislabelling every download event for the release. - - Commit with the message 'Bump version to v{MAJOR}.{NEXT_MINOR}.0'. + - Bump `version` in `packages/react-native-executorch/package.json` to `{MAJOR}.{NEXT_MINOR}.0`. Leave `nativeLibsVersion` alone; it names the GitHub artifacts release that exists today, and moving it ahead breaks every consumer's `postinstall`. + - In `models.ts`, rename `NEXT_VERSION_TAG` to `VERSION_TAG`, keeping its value at `resolve/v{MAJOR}.{MINOR}.0` (the version just published). This rewrites every model URL by construction, which is what you want: the URLs name the constant, not the tag, so bumping the value alone would silently repoint every model at a release that does not exist yet. + - Do **not** add `NEXT_VERSION_TAG` back yet. Exactly one of the two constants exists at a time: whichever has no reader fails `noUnusedLocals`. The first model re-exported for the next release declares `NEXT_VERSION_TAG = 'resolve/v{MAJOR}.{NEXT_MINOR}.0'` and points its own URL at it; the rest stay on `VERSION_TAG`. + - Bump `LIB_VERSION` in `packages/react-native-executorch/src/fetcher/telemetry.ts` to match. A unit test asserts it equals the package version, so a missed bump fails CI rather than mislabelling every download event for the release. + - Commit with the message 'Bump version to v{MAJOR}.{NEXT_MINOR}.0'. + 12. Create versioned docs by running from repo root `(cd docs && yarn docs:version {MAJOR}.{MINOR}.x)` (the 'x' part is intentional and is not to be substituted). Also, make sure that all the links in `api-reference` are not broken. 13. Create a PR with the updated docs. 14. Update README.md with release video, if available. @@ -116,15 +100,15 @@ After the release branch is created and the version is published to npm we only 1. Create a PR with the fix to the `main` branch. 2. Once the PR is merged, create a new branch off `release/{MAJOR}.{MINOR}`, cherry-pick the relevant commits from `main`, and open a PR targeting `release/{MAJOR}.{MINOR}`. -3. Once the PR is merged, bump version in `package.json` of the core package and any adapter packages that require a fix to the new version `v{MAJOR}.{MINOR}.{REVISION}`. +3. Once the PR is merged, bump version in `package.json` of the core package to the new version `v{MAJOR}.{MINOR}.{REVISION}`. Commit with a message 'Release v{MAJOR}.{MINOR}.{REVISION}'. -4. Tag release branch with proper version tag `v{MAJOR}.{MINOR}.{REVISION}` and run the relevant publish workflows: +4. Tag release branch with proper version tag `v{MAJOR}.{MINOR}.{REVISION}` and run the publish workflow: - [npm publish (core)](https://github.com/software-mansion/react-native-executorch/actions/workflows/npm-publish.yml) - - [npm publish satellite packages](https://github.com/software-mansion/react-native-executorch/actions/workflows/npm-publish-satellites.yml) — run once per affected satellite package via the `package` input _(if applicable)_ A patch on an **older** line needs the `legacy` dist-tag re-added afterwards. The action publishes such a version under `legacy` and then deletes the tag, so the line is left with no `legacy` pointer at all. + 5. Create release notes on GitHub. ## Docs update diff --git a/SECURITY.md b/SECURITY.md index 65cedd6bc3..b83bb54dcd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -43,11 +43,8 @@ each tag currently points at. In scope, because they are what users install and run: -- The published npm packages: `react-native-executorch`, its - `react-native-executorch-bare-resource-fetcher` and - `react-native-executorch-expo-resource-fetcher` adapters, and - `react-native-executorch-webrtc`. This includes the C++, Kotlin, Swift and - Objective-C sources they ship. +- The published npm package `react-native-executorch`, including the C++, + Kotlin, Swift and Objective-C sources it ships. - The prebuilt native artifacts the package downloads on install, published as GitHub Releases tagged `v-libs`. - The model files we publish under diff --git a/apps/legacy/bare-rn/.bundle/config b/apps/legacy/bare-rn/.bundle/config deleted file mode 100644 index 848943bb52..0000000000 --- a/apps/legacy/bare-rn/.bundle/config +++ /dev/null @@ -1,2 +0,0 @@ -BUNDLE_PATH: "vendor/bundle" -BUNDLE_FORCE_RUBY_PLATFORM: 1 diff --git a/apps/legacy/bare-rn/.eslintrc.js b/apps/legacy/bare-rn/.eslintrc.js deleted file mode 100644 index 1d1d89f4b4..0000000000 --- a/apps/legacy/bare-rn/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - root: true, - extends: '@react-native', - parserOptions: { - requireConfigFile: false, - }, -}; diff --git a/apps/legacy/bare-rn/.gitignore b/apps/legacy/bare-rn/.gitignore deleted file mode 100644 index 874daf3051..0000000000 --- a/apps/legacy/bare-rn/.gitignore +++ /dev/null @@ -1,80 +0,0 @@ -# OSX -# -.DS_Store - -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate -**/.xcode.env.local - -# Android/IntelliJ -# -build/ -.idea -.gradle -local.properties -*.iml -*.hprof -.cxx/ -*.keystore -!debug.keystore -.kotlin/ - -# node.js -# -node_modules/ -npm-debug.log -yarn-error.log - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/ - -**/fastlane/report.xml -**/fastlane/Preview.html -**/fastlane/screenshots -**/fastlane/test_output - -# Bundle artifact -*.jsbundle - -# Ruby / CocoaPods -**/Pods/ -/vendor/bundle/ - -# Temporary files created by Metro to check the health of the file watcher -.metro-health-check* - -# testing -/coverage - -# Yarn -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -# Custom -!/ios/ -!/android/ -/assets/ai-models/ diff --git a/apps/legacy/bare-rn/.prettierrc.js b/apps/legacy/bare-rn/.prettierrc.js deleted file mode 100644 index 06860c8d1b..0000000000 --- a/apps/legacy/bare-rn/.prettierrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - arrowParens: 'avoid', - singleQuote: true, - trailingComma: 'all', -}; diff --git a/apps/legacy/bare-rn/.watchmanconfig b/apps/legacy/bare-rn/.watchmanconfig deleted file mode 100644 index 0967ef424b..0000000000 --- a/apps/legacy/bare-rn/.watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/apps/legacy/bare-rn/App.tsx b/apps/legacy/bare-rn/App.tsx deleted file mode 100644 index 104243d3c7..0000000000 --- a/apps/legacy/bare-rn/App.tsx +++ /dev/null @@ -1,405 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - ActivityIndicator, - Keyboard, - KeyboardAvoidingView, - Modal, - Platform, - ScrollView, - StyleSheet, - Text, - TextInput, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native'; -import { models, initExecutorch, useLLM } from 'react-native-executorch/legacy'; -import { BareResourceFetcher } from 'react-native-executorch-bare-resource-fetcher'; -import { setConfig } from '@kesha-antonov/react-native-background-downloader'; -import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; - -// Configure Background Downloader logging -setConfig({ - isLogsEnabled: true, - logCallback: log => { - console.log('[BackgroundDownloader]', log); - }, -}); - -// Initialize Executorch with bare adapter -initExecutorch({ - resourceFetcher: BareResourceFetcher, -}); - -const ColorPalette = { - primary: '#001A72', - blueLight: '#C1C6E5', - blueDark: '#6676AA', - white: '#FFFFFF', - gray100: '#F5F5F5', - gray200: '#E0E0E0', -}; - -function Spinner({ - visible, - textContent, -}: { - visible: boolean; - textContent: string; -}) { - return ( - - - - - {textContent} - - - - ); -} - -const spinnerStyles = StyleSheet.create({ - overlay: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'rgba(0, 0, 0, 0.5)', - }, - container: { - padding: 25, - alignItems: 'center', - justifyContent: 'center', - }, - text: { - marginTop: 15, - color: ColorPalette.white, - fontSize: 18, - fontWeight: 'bold', - }, -}); - -function ErrorBanner({ - message, - onDismiss, -}: { - message: string | null; - onDismiss: () => void; -}) { - if (!message) return null; - return ( - - - {message} - - - ✕ - - - ); -} - -const errorBannerStyles = StyleSheet.create({ - container: { - backgroundColor: '#FEE2E2', - borderLeftWidth: 4, - borderLeftColor: '#EF4444', - borderRadius: 8, - marginHorizontal: 16, - marginVertical: 8, - paddingVertical: 10, - paddingLeft: 12, - paddingRight: 8, - flexDirection: 'row', - alignItems: 'center', - }, - message: { - flex: 1, - color: '#991B1B', - fontSize: 14, - lineHeight: 20, - }, - closeButton: { - padding: 4, - marginLeft: 8, - }, - closeText: { - color: '#991B1B', - fontSize: 16, - fontWeight: '600', - }, -}); - -function App() { - const [userInput, setUserInput] = useState(''); - const [isTextInputFocused, setIsTextInputFocused] = useState(false); - const [error, setError] = useState(null); - const textInputRef = useRef(null); - const scrollViewRef = useRef(null); - - const llm = useLLM({ - model: models.llm.lfm2_5_1_2b_instruct(), - }); - // Alternatively, to use a custom local model, uncomment below: - // const llm = useLLM({ model: { - // modelSource: require('./assets/ai-models/smolLm2/smolLm2_135M/smolLm2_135M_bf16.pte'), - // tokenizerSource: require('./assets/ai-models/smolLm2/tokenizer.json'), - // tokenizerConfigSource: require('./assets/ai-models/smolLm2/tokenizer_config.json'), - // } }); - - useEffect(() => { - if (llm.error) setError(String(llm.error)); - }, [llm.error]); - - const sendMessage = async () => { - if (!userInput.trim()) return; - - setUserInput(''); - textInputRef.current?.clear(); - try { - await llm.sendMessage(userInput); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - return ( - - - - - - setError(null)} /> - {llm.messageHistory.length > 0 || llm.isGenerating ? ( - - scrollViewRef.current?.scrollToEnd({ animated: true }) - } - keyboardShouldPersistTaps="handled" - > - {llm.messageHistory.map((message, index) => ( - - - {message.content} - - - ))} - {llm.isGenerating && llm.response && ( - - {llm.response} - - - )} - - ) : ( - - - Hello! 👋 - - What can I help you with? - - - {[ - 'Explain quantum computing in simple terms', - 'Write a short poem about coding', - 'What are the benefits of on-device AI?', - 'Give me 3 fun facts about space', - ].map((prompt, i) => ( - setUserInput(prompt)} - > - {prompt} - - ))} - - - - )} - - - setIsTextInputFocused(true)} - onBlur={() => setIsTextInputFocused(false)} - onChangeText={setUserInput} - value={userInput} - /> - {userInput.trim() && !llm.isGenerating && ( - - - Send - - - )} - {llm.isGenerating && ( - - - Stop - - - )} - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: ColorPalette.white, - }, - content: { - flex: 1, - }, - chatContainer: { - flex: 1, - width: '100%', - }, - chatContent: { - padding: 16, - flexGrow: 1, - }, - welcomeContainer: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - padding: 20, - }, - welcomeTitle: { - fontSize: 32, - fontWeight: 'bold', - color: ColorPalette.primary, - marginBottom: 12, - }, - welcomeSubtitle: { - fontSize: 18, - color: ColorPalette.blueDark, - textAlign: 'center', - }, - suggestionsContainer: { - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'center', - gap: 8, - paddingTop: 16, - }, - suggestionChip: { - borderWidth: 1, - borderColor: ColorPalette.blueLight, - borderRadius: 20, - paddingHorizontal: 14, - paddingVertical: 8, - backgroundColor: '#fafbff', - }, - suggestionChipText: { - fontSize: 13, - color: ColorPalette.primary, - }, - messageBubble: { - maxWidth: '80%', - padding: 12, - borderRadius: 16, - marginBottom: 8, - }, - userMessage: { - alignSelf: 'flex-end', - backgroundColor: ColorPalette.primary, - }, - aiMessage: { - alignSelf: 'flex-start', - backgroundColor: ColorPalette.gray100, - borderWidth: 1, - borderColor: ColorPalette.gray200, - }, - messageText: { - fontSize: 15, - lineHeight: 20, - }, - userMessageText: { - color: ColorPalette.white, - }, - aiMessageText: { - color: ColorPalette.primary, - }, - inputContainer: { - flexDirection: 'row', - padding: 16, - borderTopWidth: 1, - borderTopColor: ColorPalette.gray200, - alignItems: 'flex-end', - backgroundColor: ColorPalette.white, - }, - textInput: { - flex: 1, - borderWidth: 1, - borderRadius: 20, - paddingHorizontal: 16, - paddingVertical: 10, - fontSize: 15, - color: ColorPalette.primary, - maxHeight: 100, - marginRight: 8, - }, - sendButton: { - backgroundColor: ColorPalette.primary, - paddingHorizontal: 20, - paddingVertical: 10, - borderRadius: 20, - minWidth: 70, - alignItems: 'center', - justifyContent: 'center', - }, - sendButtonText: { - color: ColorPalette.white, - fontSize: 16, - fontWeight: '600', - }, -}); - -export default App; diff --git a/apps/legacy/bare-rn/Gemfile b/apps/legacy/bare-rn/Gemfile deleted file mode 100644 index 87de7b4f47..0000000000 --- a/apps/legacy/bare-rn/Gemfile +++ /dev/null @@ -1,16 +0,0 @@ -source 'https://rubygems.org' - -# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby ">= 2.6.10" - -# Exclude problematic versions of cocoapods and activesupport that causes build failures. -# CocoaPods >= 1.16.0 fixes Ruby 3.4 compatibility (kconv removal). -gem 'cocoapods', '>= 1.16.2' -gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' -gem 'concurrent-ruby', '< 1.3.8' - -# Ruby 3.4.0 has removed some libraries from the standard library. -gem 'bigdecimal' -gem 'logger' -gem 'benchmark' -gem 'mutex_m' diff --git a/apps/legacy/bare-rn/Gemfile.lock b/apps/legacy/bare-rn/Gemfile.lock deleted file mode 100644 index a6772d03e4..0000000000 --- a/apps/legacy/bare-rn/Gemfile.lock +++ /dev/null @@ -1,119 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.8) - activesupport (7.2.3.1) - base64 - benchmark (>= 0.3) - bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - logger (>= 1.4.2) - minitest (>= 5.1, < 6) - securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - addressable (2.9.0) - public_suffix (>= 2.0.2, < 8.0) - algoliasearch (1.27.5) - httpclient (~> 2.8, >= 2.8.3) - json (>= 1.5.1) - atomos (0.1.3) - base64 (0.3.0) - benchmark (0.5.0) - bigdecimal (4.0.1) - claide (1.1.0) - cocoapods (1.16.2) - addressable (~> 2.8) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.16.2) - cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 2.1, < 3.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.6.0, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (>= 2.3.0, < 3.0) - gh_inspector (~> 1.0) - molinillo (~> 0.8.0) - nap (~> 1.0) - ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.27.0, < 2.0) - cocoapods-core (1.16.2) - activesupport (>= 5.0, < 8) - addressable (~> 2.8) - algoliasearch (~> 1.0) - concurrent-ruby (~> 1.1) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - netrc (~> 0.11) - public_suffix (~> 4.0) - typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.5) - cocoapods-downloader (2.1) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.1) - cocoapods-trunk (1.6.0) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.2.0) - colored2 (3.1.2) - concurrent-ruby (1.3.7) - connection_pool (3.0.2) - drb (2.2.3) - escape (0.0.4) - ethon (0.15.0) - ffi (>= 1.15.0) - ffi (1.17.3) - fourflusher (2.3.1) - fuzzy_match (2.0.4) - gh_inspector (1.1.3) - httpclient (2.9.0) - mutex_m - i18n (1.14.8) - concurrent-ruby (~> 1.0) - json (2.19.9) - logger (1.7.0) - minitest (5.27.0) - molinillo (0.8.0) - mutex_m (0.3.0) - nanaimo (0.4.0) - nap (1.1.0) - netrc (0.11.0) - public_suffix (4.0.7) - rexml (3.4.4) - ruby-macho (2.5.1) - securerandom (0.4.1) - typhoeus (1.5.0) - ethon (>= 0.9.0, < 0.16.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - xcodeproj (1.27.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.4.0) - rexml (>= 3.3.6, < 4.0) - -PLATFORMS - ruby - -DEPENDENCIES - activesupport (>= 6.1.7.5, != 7.1.0) - benchmark - bigdecimal - cocoapods (>= 1.16.2) - concurrent-ruby (< 1.3.8) - logger - mutex_m - -RUBY VERSION - ruby 3.4.5p51 - -BUNDLED WITH - 2.6.9 diff --git a/apps/legacy/bare-rn/__tests__/App.test.tsx b/apps/legacy/bare-rn/__tests__/App.test.tsx deleted file mode 100644 index e532f701ee..0000000000 --- a/apps/legacy/bare-rn/__tests__/App.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @format - */ - -import React from 'react'; -import ReactTestRenderer from 'react-test-renderer'; -import App from '../App'; - -test('renders correctly', async () => { - await ReactTestRenderer.act(() => { - ReactTestRenderer.create(); - }); -}); diff --git a/apps/legacy/bare-rn/android/app/build.gradle b/apps/legacy/bare-rn/android/app/build.gradle deleted file mode 100644 index b17658e36c..0000000000 --- a/apps/legacy/bare-rn/android/app/build.gradle +++ /dev/null @@ -1,119 +0,0 @@ -apply plugin: "com.android.application" -apply plugin: "org.jetbrains.kotlin.android" -apply plugin: "com.facebook.react" - -/** - * This is the configuration block to customize your React Native Android app. - * By default you don't need to apply any configuration, just uncomment the lines you need. - */ -react { - /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '../..' - root = file("../../") - // The folder where the react-native NPM package is. Default is ../../node_modules/react-native - reactNativeDir = file("../../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen - codegenDir = file("../../node_modules/@react-native/codegen") - // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js - cliFile = file("../../node_modules/react-native/cli.js") - - /* Variants */ - // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. Default is "debug", "debugOptimized". - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "liteDebugOptimized", "prodDebug", "prodDebugOptimized"] - - /* Bundling */ - // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] - // - // The command to run when bundling. By default is 'bundle' - // bundleCommand = "ram-bundle" - // - // The path to the CLI configuration file. Default is empty. - // bundleConfig = file(../rn-cli.config.js) - // - // The name of the generated asset file containing your JS bundle - // bundleAssetName = "MyApplication.android.bundle" - // - // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' - // entryFile = file("../js/MyApplication.android.js") - // - // A list of extra flags to pass to the 'bundle' commands. - // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle - // extraPackagerArgs = [] - - /* Hermes Commands */ - // The hermes compiler command to run. By default it is 'hermesc' - // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" - // - // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" - // hermesFlags = ["-O", "-output-source-map"] - - /* Autolinking */ - autolinkLibrariesWithApp() -} - -/** - * Set this to true to Run Proguard on Release builds to minify the Java bytecode. - */ -def enableProguardInReleaseBuilds = false - -/** - * The preferred build flavor of JavaScriptCore (JSC) - * - * For example, to use the international variant, you can use: - * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - */ -def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' - -android { - ndkVersion rootProject.ext.ndkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - compileSdk rootProject.ext.compileSdkVersion - - namespace "com.barern" - defaultConfig { - applicationId "com.barern" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" - } - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - buildTypes { - debug { - signingConfig signingConfigs.debug - } - release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig signingConfigs.debug - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - } - } -} - -dependencies { - // The version of react-native is set by the React Native Gradle Plugin - implementation("com.facebook.react:react-android") - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } -} diff --git a/apps/legacy/bare-rn/android/app/debug.keystore b/apps/legacy/bare-rn/android/app/debug.keystore deleted file mode 100644 index 364e105ed3..0000000000 Binary files a/apps/legacy/bare-rn/android/app/debug.keystore and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/proguard-rules.pro b/apps/legacy/bare-rn/android/app/proguard-rules.pro deleted file mode 100644 index 11b025724a..0000000000 --- a/apps/legacy/bare-rn/android/app/proguard-rules.pro +++ /dev/null @@ -1,10 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: diff --git a/apps/legacy/bare-rn/android/app/src/main/AndroidManifest.xml b/apps/legacy/bare-rn/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index fb78f39746..0000000000 --- a/apps/legacy/bare-rn/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - diff --git a/apps/legacy/bare-rn/android/app/src/main/java/com/barern/MainActivity.kt b/apps/legacy/bare-rn/android/app/src/main/java/com/barern/MainActivity.kt deleted file mode 100644 index 7ee40e43b4..0000000000 --- a/apps/legacy/bare-rn/android/app/src/main/java/com/barern/MainActivity.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.barern - -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate - -class MainActivity : ReactActivity() { - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - override fun getMainComponentName(): String = "bare-rn" - - /** - * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] - * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] - */ - override fun createReactActivityDelegate(): ReactActivityDelegate = DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) -} diff --git a/apps/legacy/bare-rn/android/app/src/main/java/com/barern/MainApplication.kt b/apps/legacy/bare-rn/android/app/src/main/java/com/barern/MainApplication.kt deleted file mode 100644 index 39f296c224..0000000000 --- a/apps/legacy/bare-rn/android/app/src/main/java/com/barern/MainApplication.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.barern - -import android.app.Application -import com.barern.BuildConfig -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactHost -import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative -import com.facebook.react.ReactNativeHost -import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost -import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.react.runtime.JSRuntimeFactory -import com.facebook.react.runtime.hermes.HermesInstance - -class MainApplication : - Application(), - ReactApplication { - @Suppress("DEPRECATION") - override val reactNativeHost: ReactNativeHost by lazy { - object : DefaultReactNativeHost(this) { - override fun getPackages() = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here - } - - override fun getJSMainModuleName() = "index" - - override fun getUseDeveloperSupport() = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = true - } - } - - override val reactHost: ReactHost by lazy { - getDefaultReactHost( - context = applicationContext, - packageList = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here - }, - jsMainModulePath = "index", - jsRuntimeFactory = HermesInstance(), - ) - } - - override fun onCreate() { - super.onCreate() - loadReactNative(this) - } -} diff --git a/apps/legacy/bare-rn/android/app/src/main/res/drawable/rn_edit_text_material.xml b/apps/legacy/bare-rn/android/app/src/main/res/drawable/rn_edit_text_material.xml deleted file mode 100644 index 5c25e728ea..0000000000 --- a/apps/legacy/bare-rn/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a2f5908281..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 1b52399808..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index ff10afd6e1..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 115a4c768a..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index dcd3cd8083..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 459ca609d3..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 8ca12fe024..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 8e19b410a1..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index b824ebdd48..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 4c19a13c23..0000000000 Binary files a/apps/legacy/bare-rn/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/apps/legacy/bare-rn/android/app/src/main/res/values/strings.xml b/apps/legacy/bare-rn/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 6a435ace24..0000000000 --- a/apps/legacy/bare-rn/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - bare_rn - diff --git a/apps/legacy/bare-rn/android/app/src/main/res/values/styles.xml b/apps/legacy/bare-rn/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 7ba83a2ad5..0000000000 --- a/apps/legacy/bare-rn/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/apps/legacy/bare-rn/android/build.gradle b/apps/legacy/bare-rn/android/build.gradle deleted file mode 100644 index dad99b022a..0000000000 --- a/apps/legacy/bare-rn/android/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -buildscript { - ext { - buildToolsVersion = "36.0.0" - minSdkVersion = 24 - compileSdkVersion = 36 - targetSdkVersion = 36 - ndkVersion = "27.1.12297006" - kotlinVersion = "2.1.20" - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.android.tools.build:gradle") - classpath("com.facebook.react:react-native-gradle-plugin") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") - } -} - -apply plugin: "com.facebook.react.rootproject" diff --git a/apps/legacy/bare-rn/android/gradle.properties b/apps/legacy/bare-rn/android/gradle.properties deleted file mode 100644 index 9afe61598f..0000000000 --- a/apps/legacy/bare-rn/android/gradle.properties +++ /dev/null @@ -1,44 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true - -# Use this property to specify which architecture you want to build. -# You can also override it from the CLI using -# ./gradlew -PreactNativeArchitectures=x86_64 -reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 - -# Use this property to enable support to the new architecture. -# This will allow you to use TurboModules and the Fabric render in -# your application. You should enable this flag either if you want -# to write custom TurboModules/Fabric components OR use libraries that -# are providing them. -newArchEnabled=true - -# Use this property to enable or disable the Hermes JS engine. -# If set to false, you will be using JSC instead. -hermesEnabled=true - -# Use this property to enable edge-to-edge display support. -# This allows your app to draw behind system bars for an immersive UI. -# Note: Only works with ReactActivity and should not be used with custom Activity. -edgeToEdgeEnabled=false diff --git a/apps/legacy/bare-rn/android/gradle/wrapper/gradle-wrapper.jar b/apps/legacy/bare-rn/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 8bdaf60c75..0000000000 Binary files a/apps/legacy/bare-rn/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/apps/legacy/bare-rn/android/gradle/wrapper/gradle-wrapper.properties b/apps/legacy/bare-rn/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2a84e188b8..0000000000 --- a/apps/legacy/bare-rn/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/apps/legacy/bare-rn/android/gradlew b/apps/legacy/bare-rn/android/gradlew deleted file mode 100755 index ef07e0162b..0000000000 --- a/apps/legacy/bare-rn/android/gradlew +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH="\\\"\\\"" - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/apps/legacy/bare-rn/android/gradlew.bat b/apps/legacy/bare-rn/android/gradlew.bat deleted file mode 100644 index dd2b8eedbd..0000000000 --- a/apps/legacy/bare-rn/android/gradlew.bat +++ /dev/null @@ -1,99 +0,0 @@ -@REM Copyright (c) Meta Platforms, Inc. and affiliates. -@REM -@REM This source code is licensed under the MIT license found in the -@REM LICENSE file in the root directory of this source tree. - -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/apps/legacy/bare-rn/android/settings.gradle b/apps/legacy/bare-rn/android/settings.gradle deleted file mode 100644 index 56ad61524f..0000000000 --- a/apps/legacy/bare-rn/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } -plugins { id("com.facebook.react.settings") } -extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> - // bare-rn pins its own React Native via installConfig.hoistingLimits in package.json, - // so its deps live in apps/bare-rn/node_modules/ rather than the workspace root. - def cliFile = new File(settings.rootDir, "../node_modules/@react-native-community/cli/build/bin.js").absolutePath - ex.autolinkLibrariesFromCommand(["node", cliFile, "config"], new File(settings.rootDir, "../")) -} -rootProject.name = 'bare_rn' -include ':app' -includeBuild('../node_modules/@react-native/gradle-plugin') diff --git a/apps/legacy/bare-rn/app.json b/apps/legacy/bare-rn/app.json deleted file mode 100644 index 0a3ed9b0a2..0000000000 --- a/apps/legacy/bare-rn/app.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "bare-rn", - "displayName": "bare-rn" -} diff --git a/apps/legacy/bare-rn/babel.config.js b/apps/legacy/bare-rn/babel.config.js deleted file mode 100644 index b243a0ffa4..0000000000 --- a/apps/legacy/bare-rn/babel.config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - presets: ['module:@react-native/babel-preset'], - plugins: ['@babel/plugin-transform-export-namespace-from'], -}; diff --git a/apps/legacy/bare-rn/index.js b/apps/legacy/bare-rn/index.js deleted file mode 100644 index 9b73932914..0000000000 --- a/apps/legacy/bare-rn/index.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @format - */ - -import { AppRegistry } from 'react-native'; -import App from './App'; -import { name as appName } from './app.json'; - -AppRegistry.registerComponent(appName, () => App); diff --git a/apps/legacy/bare-rn/ios/.xcode.env b/apps/legacy/bare-rn/ios/.xcode.env deleted file mode 100644 index 3d5782c715..0000000000 --- a/apps/legacy/bare-rn/ios/.xcode.env +++ /dev/null @@ -1,11 +0,0 @@ -# This `.xcode.env` file is versioned and is used to source the environment -# used when running script phases inside Xcode. -# To customize your local environment, you can create an `.xcode.env.local` -# file that is not versioned. - -# NODE_BINARY variable contains the PATH to the node executable. -# -# Customize the NODE_BINARY variable here. -# For example, to use nvm with brew, add the following line -# . "$(brew --prefix nvm)/nvm.sh" --no-use -export NODE_BINARY=$(command -v node) diff --git a/apps/legacy/bare-rn/ios/Podfile b/apps/legacy/bare-rn/ios/Podfile deleted file mode 100644 index cdcb24c068..0000000000 --- a/apps/legacy/bare-rn/ios/Podfile +++ /dev/null @@ -1,51 +0,0 @@ -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip - -platform :ios, '17.0' -prepare_react_native_project! - -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym -end - -target 'bare-rn' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - post_install do |installer| - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true - ) - - # Workaround for Xcode 26.4 + Apple clang 21's stricter consteval - # enforcement breaking fmt 9.x (vendored by RCT-Folly in RN 0.81.x). - # See facebook/react-native#55601, fmtlib/fmt#4740. Forcing the fmt and - # RCT-Folly pod targets to C++17 makes fmt's `__cplusplus >= 202002L` - # gate fail, so `FMT_CONSTEVAL` resolves to empty inside fmt's cached - # clang module — consumers (Yoga, React-performancetimeline, etc.) then - # import a non-consteval module and the basic_format_string call site - # compiles. Applies to fmt/RCT-Folly only; remove once upstream lands. - installer.pods_project.targets.each do |target| - next unless target.name == 'fmt' || target.name == 'RCT-Folly' - target.build_configurations.each do |config| - config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17' - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)'] - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'FMT_USE_CONSTEVAL=0' - end - end - end -end diff --git a/apps/legacy/bare-rn/ios/Podfile.lock b/apps/legacy/bare-rn/ios/Podfile.lock deleted file mode 100644 index 74e55c6a90..0000000000 --- a/apps/legacy/bare-rn/ios/Podfile.lock +++ /dev/null @@ -1,2759 +0,0 @@ -PODS: - - boost (1.84.0) - - DoubleConversion (1.1.6) - - fast_float (8.0.0) - - FBLazyVector (0.81.5) - - fmt (11.0.2) - - glog (0.3.5) - - hermes-engine (0.81.5): - - hermes-engine/Pre-built (= 0.81.5) - - hermes-engine/Pre-built (0.81.5) - - MMKV (2.3.0): - - MMKVCore (~> 2.3.0) - - MMKVCore (2.3.0) - - opencv-rne (4.11.0) - - RCT-Folly (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 8.0.0) - - fmt (= 11.0.2) - - glog - - RCT-Folly/Default (= 2024.11.18.00) - - RCT-Folly/Default (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 8.0.0) - - fmt (= 11.0.2) - - glog - - RCT-Folly/Fabric (2024.11.18.00): - - boost - - DoubleConversion - - fast_float (= 8.0.0) - - fmt (= 11.0.2) - - glog - - RCTDeprecation (0.81.5) - - RCTRequired (0.81.5) - - RCTTypeSafety (0.81.5): - - FBLazyVector (= 0.81.5) - - RCTRequired (= 0.81.5) - - React-Core (= 0.81.5) - - React (0.81.5): - - React-Core (= 0.81.5) - - React-Core/DevSupport (= 0.81.5) - - React-Core/RCTWebSocket (= 0.81.5) - - React-RCTActionSheet (= 0.81.5) - - React-RCTAnimation (= 0.81.5) - - React-RCTBlob (= 0.81.5) - - React-RCTImage (= 0.81.5) - - React-RCTLinking (= 0.81.5) - - React-RCTNetwork (= 0.81.5) - - React-RCTSettings (= 0.81.5) - - React-RCTText (= 0.81.5) - - React-RCTVibration (= 0.81.5) - - React-callinvoker (0.81.5) - - React-Core (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default (= 0.81.5) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/CoreModulesHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/Default (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/DevSupport (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default (= 0.81.5) - - React-Core/RCTWebSocket (= 0.81.5) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTActionSheetHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTAnimationHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTBlobHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTImageHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTLinkingHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTNetworkHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTSettingsHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTTextHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTVibrationHeaders (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-Core/RCTWebSocket (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTDeprecation - - React-Core/Default (= 0.81.5) - - React-cxxreact - - React-featureflags - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsinspectorcdp - - React-jsitooling - - React-perflogger - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-CoreModules (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - RCTTypeSafety (= 0.81.5) - - React-Core/CoreModulesHeaders (= 0.81.5) - - React-jsi (= 0.81.5) - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-NativeModulesApple - - React-RCTBlob - - React-RCTFBReactNativeSpec - - React-RCTImage (= 0.81.5) - - React-runtimeexecutor - - ReactCommon - - SocketRocket - - React-cxxreact (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.5) - - React-debug (= 0.81.5) - - React-jsi (= 0.81.5) - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-logger (= 0.81.5) - - React-perflogger (= 0.81.5) - - React-runtimeexecutor - - React-timing (= 0.81.5) - - SocketRocket - - React-debug (0.81.5) - - React-defaultsnativemodule (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-domnativemodule - - React-featureflagsnativemodule - - React-idlecallbacksnativemodule - - React-jsi - - React-jsiexecutor - - React-microtasksnativemodule - - React-RCTFBReactNativeSpec - - SocketRocket - - React-domnativemodule (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-Fabric - - React-Fabric/bridging - - React-FabricComponents - - React-graphics - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - React-runtimeexecutor - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-Fabric (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/animations (= 0.81.5) - - React-Fabric/attributedstring (= 0.81.5) - - React-Fabric/bridging (= 0.81.5) - - React-Fabric/componentregistry (= 0.81.5) - - React-Fabric/componentregistrynative (= 0.81.5) - - React-Fabric/components (= 0.81.5) - - React-Fabric/consistency (= 0.81.5) - - React-Fabric/core (= 0.81.5) - - React-Fabric/dom (= 0.81.5) - - React-Fabric/imagemanager (= 0.81.5) - - React-Fabric/leakchecker (= 0.81.5) - - React-Fabric/mounting (= 0.81.5) - - React-Fabric/observers (= 0.81.5) - - React-Fabric/scheduler (= 0.81.5) - - React-Fabric/telemetry (= 0.81.5) - - React-Fabric/templateprocessor (= 0.81.5) - - React-Fabric/uimanager (= 0.81.5) - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/animations (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/attributedstring (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/bridging (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/componentregistry (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/componentregistrynative (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/components/legacyviewmanagerinterop (= 0.81.5) - - React-Fabric/components/root (= 0.81.5) - - React-Fabric/components/scrollview (= 0.81.5) - - React-Fabric/components/view (= 0.81.5) - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/legacyviewmanagerinterop (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/root (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/scrollview (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/components/view (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-renderercss - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-Fabric/consistency (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/core (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/dom (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/imagemanager (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/leakchecker (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/mounting (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/observers (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/observers/events (= 0.81.5) - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/observers/events (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/scheduler (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/observers/events - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-performancetimeline - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/telemetry (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/templateprocessor (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/uimanager (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/uimanager/consistency (= 0.81.5) - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererconsistency - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-Fabric/uimanager/consistency (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererconsistency - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - React-FabricComponents (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-FabricComponents/components (= 0.81.5) - - React-FabricComponents/textlayoutmanager (= 0.81.5) - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-FabricComponents/components/inputaccessory (= 0.81.5) - - React-FabricComponents/components/iostextinput (= 0.81.5) - - React-FabricComponents/components/modal (= 0.81.5) - - React-FabricComponents/components/rncore (= 0.81.5) - - React-FabricComponents/components/safeareaview (= 0.81.5) - - React-FabricComponents/components/scrollview (= 0.81.5) - - React-FabricComponents/components/switch (= 0.81.5) - - React-FabricComponents/components/text (= 0.81.5) - - React-FabricComponents/components/textinput (= 0.81.5) - - React-FabricComponents/components/unimplementedview (= 0.81.5) - - React-FabricComponents/components/virtualview (= 0.81.5) - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/inputaccessory (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/iostextinput (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/modal (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/rncore (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/safeareaview (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/scrollview (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/switch (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/text (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/textinput (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/unimplementedview (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/components/virtualview (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricComponents/textlayoutmanager (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-RCTFBReactNativeSpec - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-FabricImage (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired (= 0.81.5) - - RCTTypeSafety (= 0.81.5) - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-jsiexecutor (= 0.81.5) - - React-logger - - React-rendererdebug - - React-utils - - ReactCommon - - SocketRocket - - Yoga - - React-featureflags (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-featureflagsnativemodule (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-featureflags - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - ReactCommon/turbomodule/core - - SocketRocket - - React-graphics (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-jsi - - React-jsiexecutor - - React-utils - - SocketRocket - - React-hermes (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact (= 0.81.5) - - React-jsi - - React-jsiexecutor (= 0.81.5) - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-perflogger (= 0.81.5) - - React-runtimeexecutor - - SocketRocket - - React-idlecallbacksnativemodule (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - React-runtimeexecutor - - React-runtimescheduler - - ReactCommon/turbomodule/core - - SocketRocket - - React-ImageManager (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-Core/Default - - React-debug - - React-Fabric - - React-graphics - - React-rendererdebug - - React-utils - - SocketRocket - - React-jserrorhandler (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact - - React-debug - - React-featureflags - - React-jsi - - ReactCommon/turbomodule/bridging - - SocketRocket - - React-jsi (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-jsiexecutor (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact (= 0.81.5) - - React-jsi (= 0.81.5) - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-perflogger (= 0.81.5) - - React-runtimeexecutor - - SocketRocket - - React-jsinspector (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-featureflags - - React-jsi - - React-jsinspectorcdp - - React-jsinspectornetwork - - React-jsinspectortracing - - React-oscompat - - React-perflogger (= 0.81.5) - - React-runtimeexecutor - - SocketRocket - - React-jsinspectorcdp (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-jsinspectornetwork (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-featureflags - - React-jsinspectorcdp - - React-performancetimeline - - React-timing - - SocketRocket - - React-jsinspectortracing (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-oscompat - - React-timing - - SocketRocket - - React-jsitooling (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact (= 0.81.5) - - React-jsi (= 0.81.5) - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-runtimeexecutor - - SocketRocket - - React-jsitracing (0.81.5): - - React-jsi - - React-logger (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-Mapbuffer (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-debug - - SocketRocket - - React-microtasksnativemodule (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-jsi - - React-jsiexecutor - - React-RCTFBReactNativeSpec - - ReactCommon/turbomodule/core - - SocketRocket - - react-native-background-downloader (4.6.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - "MMKV (!= 2.4.1, >= 1.2.0)" - - "MMKVCore (!= 2.4.1)" - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - react-native-executorch (0.0.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - opencv-rne (~> 4.11.0) - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - react-native-safe-area-context (5.9.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - react-native-safe-area-context/common (= 5.9.1) - - react-native-safe-area-context/fabric (= 5.9.1) - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - react-native-safe-area-context/common (5.9.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - react-native-safe-area-context/fabric (5.9.1): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - react-native-safe-area-context/common - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - React-NativeModulesApple (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker - - React-Core - - React-cxxreact - - React-featureflags - - React-jsi - - React-jsinspector - - React-jsinspectorcdp - - React-runtimeexecutor - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - React-oscompat (0.81.5) - - React-perflogger (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - SocketRocket - - React-performancetimeline (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-featureflags - - React-jsinspectortracing - - React-perflogger - - React-timing - - SocketRocket - - React-RCTActionSheet (0.81.5): - - React-Core/RCTActionSheetHeaders (= 0.81.5) - - React-RCTAnimation (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - RCTTypeSafety - - React-Core/RCTAnimationHeaders - - React-featureflags - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - SocketRocket - - React-RCTAppDelegate (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-CoreModules - - React-debug - - React-defaultsnativemodule - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-jsitooling - - React-NativeModulesApple - - React-RCTFabric - - React-RCTFBReactNativeSpec - - React-RCTImage - - React-RCTNetwork - - React-RCTRuntime - - React-rendererdebug - - React-RuntimeApple - - React-RuntimeCore - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - ReactCommon - - SocketRocket - - React-RCTBlob (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-Core/RCTBlobHeaders - - React-Core/RCTWebSocket - - React-jsi - - React-jsinspector - - React-jsinspectorcdp - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - React-RCTNetwork - - ReactCommon - - SocketRocket - - React-RCTFabric (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-Core - - React-debug - - React-Fabric - - React-FabricComponents - - React-FabricImage - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectornetwork - - React-jsinspectortracing - - React-performancetimeline - - React-RCTAnimation - - React-RCTFBReactNativeSpec - - React-RCTImage - - React-RCTText - - React-rendererconsistency - - React-renderercss - - React-rendererdebug - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - Yoga - - React-RCTFBReactNativeSpec (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec/components (= 0.81.5) - - ReactCommon - - SocketRocket - - React-RCTFBReactNativeSpec/components (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-jsi - - React-NativeModulesApple - - React-rendererdebug - - React-utils - - ReactCommon - - SocketRocket - - Yoga - - React-RCTImage (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - RCTTypeSafety - - React-Core/RCTImageHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - React-RCTNetwork - - ReactCommon - - SocketRocket - - React-RCTLinking (0.81.5): - - React-Core/RCTLinkingHeaders (= 0.81.5) - - React-jsi (= 0.81.5) - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - ReactCommon/turbomodule/core (= 0.81.5) - - React-RCTNetwork (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - RCTTypeSafety - - React-Core/RCTNetworkHeaders - - React-featureflags - - React-jsi - - React-jsinspectorcdp - - React-jsinspectornetwork - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - SocketRocket - - React-RCTRuntime (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-Core - - React-jsi - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-jsitooling - - React-RuntimeApple - - React-RuntimeCore - - React-runtimeexecutor - - React-RuntimeHermes - - SocketRocket - - React-RCTSettings (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - RCTTypeSafety - - React-Core/RCTSettingsHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - SocketRocket - - React-RCTText (0.81.5): - - React-Core/RCTTextHeaders (= 0.81.5) - - Yoga - - React-RCTVibration (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-Core/RCTVibrationHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTFBReactNativeSpec - - ReactCommon - - SocketRocket - - React-rendererconsistency (0.81.5) - - React-renderercss (0.81.5): - - React-debug - - React-utils - - React-rendererdebug (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-debug - - SocketRocket - - React-RuntimeApple (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker - - React-Core/Default - - React-CoreModules - - React-cxxreact - - React-featureflags - - React-jserrorhandler - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-Mapbuffer - - React-NativeModulesApple - - React-RCTFabric - - React-RCTFBReactNativeSpec - - React-RuntimeCore - - React-runtimeexecutor - - React-RuntimeHermes - - React-runtimescheduler - - React-utils - - SocketRocket - - React-RuntimeCore (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-cxxreact - - React-Fabric - - React-featureflags - - React-jserrorhandler - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-jsitooling - - React-performancetimeline - - React-runtimeexecutor - - React-runtimescheduler - - React-utils - - SocketRocket - - React-runtimeexecutor (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - React-debug - - React-featureflags - - React-jsi (= 0.81.5) - - React-utils - - SocketRocket - - React-RuntimeHermes (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-featureflags - - React-hermes - - React-jsi - - React-jsinspector - - React-jsinspectorcdp - - React-jsinspectortracing - - React-jsitooling - - React-jsitracing - - React-RuntimeCore - - React-runtimeexecutor - - React-utils - - SocketRocket - - React-runtimescheduler (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker - - React-cxxreact - - React-debug - - React-featureflags - - React-jsi - - React-jsinspectortracing - - React-performancetimeline - - React-rendererconsistency - - React-rendererdebug - - React-runtimeexecutor - - React-timing - - React-utils - - SocketRocket - - React-timing (0.81.5): - - React-debug - - React-utils (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-debug - - React-jsi (= 0.81.5) - - SocketRocket - - ReactAppDependencyProvider (0.81.5): - - ReactCodegen - - ReactCodegen (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-FabricImage - - React-featureflags - - React-graphics - - React-jsi - - React-jsiexecutor - - React-NativeModulesApple - - React-RCTAppDelegate - - React-rendererdebug - - React-utils - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - ReactCommon (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - RCT-Folly - - RCT-Folly/Fabric - - ReactCommon/turbomodule (= 0.81.5) - - SocketRocket - - ReactCommon/turbomodule (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.5) - - React-cxxreact (= 0.81.5) - - React-jsi (= 0.81.5) - - React-logger (= 0.81.5) - - React-perflogger (= 0.81.5) - - ReactCommon/turbomodule/bridging (= 0.81.5) - - ReactCommon/turbomodule/core (= 0.81.5) - - SocketRocket - - ReactCommon/turbomodule/bridging (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.5) - - React-cxxreact (= 0.81.5) - - React-jsi (= 0.81.5) - - React-logger (= 0.81.5) - - React-perflogger (= 0.81.5) - - SocketRocket - - ReactCommon/turbomodule/core (0.81.5): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - React-callinvoker (= 0.81.5) - - React-cxxreact (= 0.81.5) - - React-debug (= 0.81.5) - - React-featureflags (= 0.81.5) - - React-jsi (= 0.81.5) - - React-logger (= 0.81.5) - - React-perflogger (= 0.81.5) - - React-utils (= 0.81.5) - - SocketRocket - - ReactNativeFs (2.40.2): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - - SocketRocket (0.7.1) - - Yoga (0.0.0) - -DEPENDENCIES: - - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - - RCTRequired (from `../node_modules/react-native/Libraries/Required`) - - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - - React (from `../node_modules/react-native/`) - - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - - React-Core (from `../node_modules/react-native/`) - - React-Core/RCTWebSocket (from `../node_modules/react-native/`) - - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) - - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`) - - React-defaultsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/defaults`) - - React-domnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/dom`) - - React-Fabric (from `../node_modules/react-native/ReactCommon`) - - React-FabricComponents (from `../node_modules/react-native/ReactCommon`) - - React-FabricImage (from `../node_modules/react-native/ReactCommon`) - - React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`) - - React-featureflagsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`) - - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) - - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) - - React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) - - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) - - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) - - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) - - React-jsinspectorcdp (from `../node_modules/react-native/ReactCommon/jsinspector-modern/cdp`) - - React-jsinspectornetwork (from `../node_modules/react-native/ReactCommon/jsinspector-modern/network`) - - React-jsinspectortracing (from `../node_modules/react-native/ReactCommon/jsinspector-modern/tracing`) - - React-jsitooling (from `../node_modules/react-native/ReactCommon/jsitooling`) - - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`) - - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - - "react-native-background-downloader (from `../node_modules/@kesha-antonov/react-native-background-downloader`)" - - react-native-executorch (from `../node_modules/react-native-executorch`) - - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - - React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`) - - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - - React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`) - - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) - - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) - - React-RCTFabric (from `../node_modules/react-native/React`) - - React-RCTFBReactNativeSpec (from `../node_modules/react-native/React`) - - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) - - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) - - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) - - React-RCTRuntime (from `../node_modules/react-native/React/Runtime`) - - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) - - React-RCTText (from `../node_modules/react-native/Libraries/Text`) - - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - - React-rendererconsistency (from `../node_modules/react-native/ReactCommon/react/renderer/consistency`) - - React-renderercss (from `../node_modules/react-native/ReactCommon/react/renderer/css`) - - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`) - - React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) - - React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - - React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`) - - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - - React-timing (from `../node_modules/react-native/ReactCommon/react/timing`) - - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - - ReactAppDependencyProvider (from `build/generated/ios`) - - ReactCodegen (from `build/generated/ios`) - - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - - "ReactNativeFs (from `../node_modules/@dr.pogodin/react-native-fs`)" - - SocketRocket (~> 0.7.1) - - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) - -SPEC REPOS: - trunk: - - MMKV - - MMKVCore - - opencv-rne - - SocketRocket - -EXTERNAL SOURCES: - boost: - :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" - DoubleConversion: - :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" - fast_float: - :podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec" - FBLazyVector: - :path: "../node_modules/react-native/Libraries/FBLazyVector" - fmt: - :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" - glog: - :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" - hermes-engine: - :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2025-07-07-RNv0.81.0-e0fc67142ec0763c6b6153ca2bf96df815539782 - RCT-Folly: - :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" - RCTDeprecation: - :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" - RCTRequired: - :path: "../node_modules/react-native/Libraries/Required" - RCTTypeSafety: - :path: "../node_modules/react-native/Libraries/TypeSafety" - React: - :path: "../node_modules/react-native/" - React-callinvoker: - :path: "../node_modules/react-native/ReactCommon/callinvoker" - React-Core: - :path: "../node_modules/react-native/" - React-CoreModules: - :path: "../node_modules/react-native/React/CoreModules" - React-cxxreact: - :path: "../node_modules/react-native/ReactCommon/cxxreact" - React-debug: - :path: "../node_modules/react-native/ReactCommon/react/debug" - React-defaultsnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/defaults" - React-domnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/dom" - React-Fabric: - :path: "../node_modules/react-native/ReactCommon" - React-FabricComponents: - :path: "../node_modules/react-native/ReactCommon" - React-FabricImage: - :path: "../node_modules/react-native/ReactCommon" - React-featureflags: - :path: "../node_modules/react-native/ReactCommon/react/featureflags" - React-featureflagsnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/featureflags" - React-graphics: - :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" - React-hermes: - :path: "../node_modules/react-native/ReactCommon/hermes" - React-idlecallbacksnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks" - React-ImageManager: - :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" - React-jserrorhandler: - :path: "../node_modules/react-native/ReactCommon/jserrorhandler" - React-jsi: - :path: "../node_modules/react-native/ReactCommon/jsi" - React-jsiexecutor: - :path: "../node_modules/react-native/ReactCommon/jsiexecutor" - React-jsinspector: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern" - React-jsinspectorcdp: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern/cdp" - React-jsinspectornetwork: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern/network" - React-jsinspectortracing: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern/tracing" - React-jsitooling: - :path: "../node_modules/react-native/ReactCommon/jsitooling" - React-jsitracing: - :path: "../node_modules/react-native/ReactCommon/hermes/executor/" - React-logger: - :path: "../node_modules/react-native/ReactCommon/logger" - React-Mapbuffer: - :path: "../node_modules/react-native/ReactCommon" - React-microtasksnativemodule: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" - react-native-background-downloader: - :path: "../node_modules/@kesha-antonov/react-native-background-downloader" - react-native-executorch: - :path: "../node_modules/react-native-executorch" - react-native-safe-area-context: - :path: "../node_modules/react-native-safe-area-context" - React-NativeModulesApple: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" - React-oscompat: - :path: "../node_modules/react-native/ReactCommon/oscompat" - React-perflogger: - :path: "../node_modules/react-native/ReactCommon/reactperflogger" - React-performancetimeline: - :path: "../node_modules/react-native/ReactCommon/react/performance/timeline" - React-RCTActionSheet: - :path: "../node_modules/react-native/Libraries/ActionSheetIOS" - React-RCTAnimation: - :path: "../node_modules/react-native/Libraries/NativeAnimation" - React-RCTAppDelegate: - :path: "../node_modules/react-native/Libraries/AppDelegate" - React-RCTBlob: - :path: "../node_modules/react-native/Libraries/Blob" - React-RCTFabric: - :path: "../node_modules/react-native/React" - React-RCTFBReactNativeSpec: - :path: "../node_modules/react-native/React" - React-RCTImage: - :path: "../node_modules/react-native/Libraries/Image" - React-RCTLinking: - :path: "../node_modules/react-native/Libraries/LinkingIOS" - React-RCTNetwork: - :path: "../node_modules/react-native/Libraries/Network" - React-RCTRuntime: - :path: "../node_modules/react-native/React/Runtime" - React-RCTSettings: - :path: "../node_modules/react-native/Libraries/Settings" - React-RCTText: - :path: "../node_modules/react-native/Libraries/Text" - React-RCTVibration: - :path: "../node_modules/react-native/Libraries/Vibration" - React-rendererconsistency: - :path: "../node_modules/react-native/ReactCommon/react/renderer/consistency" - React-renderercss: - :path: "../node_modules/react-native/ReactCommon/react/renderer/css" - React-rendererdebug: - :path: "../node_modules/react-native/ReactCommon/react/renderer/debug" - React-RuntimeApple: - :path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios" - React-RuntimeCore: - :path: "../node_modules/react-native/ReactCommon/react/runtime" - React-runtimeexecutor: - :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" - React-RuntimeHermes: - :path: "../node_modules/react-native/ReactCommon/react/runtime" - React-runtimescheduler: - :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" - React-timing: - :path: "../node_modules/react-native/ReactCommon/react/timing" - React-utils: - :path: "../node_modules/react-native/ReactCommon/react/utils" - ReactAppDependencyProvider: - :path: build/generated/ios - ReactCodegen: - :path: build/generated/ios - ReactCommon: - :path: "../node_modules/react-native/ReactCommon" - ReactNativeFs: - :path: "../node_modules/@dr.pogodin/react-native-fs" - Yoga: - :path: "../node_modules/react-native/ReactCommon/yoga" - -SPEC CHECKSUMS: - boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb - fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6 - FBLazyVector: 5beb8028d5a2e75dd9634917f23e23d3a061d2aa - fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd - glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 - hermes-engine: 9f4dfe93326146a1c99eb535b1cb0b857a3cd172 - MMKV: c953dbaac0da392c24b005e763c03ce2638b4ed7 - MMKVCore: d078dce7d6586a888b2c2ef5343b6242678e3ee8 - opencv-rne: 2305807573b6e29c8c87e3416ab096d09047a7a0 - RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 - RCTDeprecation: 5eb1d2eeff5fb91151e8a8eef45b6c7658b6c897 - RCTRequired: cebcf9442fc296c9b89ac791dfd463021d9f6f23 - RCTTypeSafety: b99aa872829ee18f6e777e0ef55852521c5a6788 - React: 914f8695f9bf38e6418228c2ffb70021e559f92f - React-callinvoker: 23cd4e33928608bd0cc35357597568b8b9a5f068 - React-Core: 6a0a97598e9455348113bfe4c573fe8edac34469 - React-CoreModules: a88a6ca48b668401b9780e272e2a607e70f9f955 - React-cxxreact: 06265fd7e8d5c3b6b49e00d328ef76e5f1ae9c8b - React-debug: 039d3dbd3078613e02e3960439bbf52f6d321bc4 - React-defaultsnativemodule: 09efbfa17b15445907689c577e371558d8b08135 - React-domnativemodule: 6284a09207d8e0e974affb0d84b43a0c1aee2554 - React-Fabric: 5ffa7f2a10fb3bf835f97990d341419ae338963d - React-FabricComponents: 25173bc205a6b7c18d87121891f3acef1c329b04 - React-FabricImage: aa90e4b2b34a79f9b4ee56328ad9222cb672f1f3 - React-featureflags: 7bdaca8af1def3ec9203743c91b11ac7c2cb2574 - React-featureflagsnativemodule: 6840bc359820d5e44f1de1f9ba69706e0a88a60b - React-graphics: b0a76138e325f9c5dfcc8fbc62491ab252ca736c - React-hermes: a852be3ab9e1f515e46ba3ea9f48c31d4a9df437 - React-idlecallbacksnativemodule: 38895fd946b2dcb0af387f2176f5f2e578b14277 - React-ImageManager: 44409a10adff7091c9e678b97ee59c7b0576b8ae - React-jserrorhandler: 3852205bbfc68277cd4e7392ad1fa74a170150fd - React-jsi: 7b53959aea60909ac6bbe4dd0bdec6c10d7dc597 - React-jsiexecutor: 19938072af05ade148474bac41e0324a2d733f44 - React-jsinspector: 0aecd79939adf576c6dd7bbbddf90b630e7827e4 - React-jsinspectorcdp: 8245973529c78d150aebddd2c497ee290349faf0 - React-jsinspectornetwork: 496a12dbc80835fac10acf29b9c4386ddcc472f1 - React-jsinspectortracing: 1939b3e0cec087983384c5561bf925f35287d760 - React-jsitooling: 86c70336d5c371b4289499e9303b6da118ad3eeb - React-jsitracing: 8eb0d50d7874886fb3ec7f85e0567f1964a20075 - React-logger: a913317214a26565cd4c045347edf1bcacb80a3f - React-Mapbuffer: 94f4264de2cb156960cd82b338a403f4653f2fd9 - React-microtasksnativemodule: 6c4ee39a36958c39c97b074d28f360246a335e84 - react-native-background-downloader: e09c54beb8d17838707917e918371bc931b75c88 - react-native-executorch: 8323cb8d9388cade609ab4e893779db74e63fb47 - react-native-safe-area-context: 6cf08f4151132f9cf2adb948dc6c4ac387f8d30f - React-NativeModulesApple: ebf2ce72b35870036900d6498b33724386540a71 - React-oscompat: eb0626e8ba1a2c61673c991bf9dc21834898475d - React-perflogger: 509e1f9a3ee28df71b0a66de806ac515ce951246 - React-performancetimeline: 43a1ea36ac47853b479ae85e04c1c339721e99f1 - React-RCTActionSheet: 30fe8f9f8d86db4a25ff34595a658ecd837485fc - React-RCTAnimation: 3126eb1cb8e7a6ca33a52fd833d8018aa9311af1 - React-RCTAppDelegate: b03981c790aa40cf26e0f78cc0f1f2df8287ead4 - React-RCTBlob: 53c35e85c85d6bdaa55dc81a0b290d4e78431095 - React-RCTFabric: 4e2a4176f99b6b8f2d2eda9fc82453a3e6c3ef8e - React-RCTFBReactNativeSpec: 947126c649e04b95457a40bc97c4b2a76206534b - React-RCTImage: 074b2faa71a152a456c974e118b60c9eeda94a64 - React-RCTLinking: e5ca17a4f7ae2ad7b0c0483be77e1b383ecd0a8a - React-RCTNetwork: c508d7548c9eceac30a8100a846ea00033a03366 - React-RCTRuntime: 6813778046c775c124179d9e4d7b33d4129bbd84 - React-RCTSettings: dd84c857a4fce42c1e08c1dabcda894e25af4a6e - React-RCTText: 6e4b177d047f98bccb90d6fb1ebdd3391cf8b299 - React-RCTVibration: 9572d4a06a0c92650bcc62913e50eb2a89f19fb6 - React-rendererconsistency: 6f0622076d7b26eda57a582db5ffd8b05fe94652 - React-renderercss: c00b6db35f01e2f17e496d1d0793fc0be89d4f7b - React-rendererdebug: 17f707ba5ba1ed7a10dd997a2e27b2431b24a180 - React-RuntimeApple: b9b9a53afd594eb49c3e6891f84327d1834a2c5e - React-RuntimeCore: 5a0c78665206a44c4a030e2b4af0c8d6ad05ae77 - React-runtimeexecutor: 7f56789cd23bd4ea1f95595eb5c27e08cee3a19e - React-RuntimeHermes: b2d6bc03f4cc9d2eb7ee0a1bfe16c226cb2114ce - React-runtimescheduler: 5cc5c0568bf216e1ee8f3c2c0a1cff2ef3091b32 - React-timing: b1e27e61bd184fab3792947685bebdb2dc55af9a - React-utils: ddf52534853a3b5f19d4615b1a1f172b504673f2 - ReactAppDependencyProvider: 1bcd3527ac0390a1c898c114f81ff954be35ed79 - ReactCodegen: 6c26f8c25d0b5ae66f86a1cce1777076ac8bcbd8 - ReactCommon: 5f0e5c09a64a2717215dd84380e1a747810406f2 - ReactNativeFs: 36cd5a1531487afca7393ebafaf3ba854b945a79 - SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 728df40394d49f3f471688747cf558158b3a3bd1 - -PODFILE CHECKSUM: 3f9008ee1fb32bf8b4f65f3fba5576bf818a2d0f - -COCOAPODS: 1.17.0 diff --git a/apps/legacy/bare-rn/ios/bare-rn.xcodeproj/project.pbxproj b/apps/legacy/bare-rn/ios/bare-rn.xcodeproj/project.pbxproj deleted file mode 100644 index 2a603318fc..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn.xcodeproj/project.pbxproj +++ /dev/null @@ -1,482 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 0AF1DC9B7BF72575F4599DF4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; - 0C80B921A6F3F58F76C31292 /* libPods-bare-rn.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-bare-rn.a */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 13B07F961A680F5B00A75B9A /* bare-rn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "bare-rn.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = "bare-rn/Images.xcassets"; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = "bare-rn/Info.plist"; sourceTree = ""; }; - 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = "bare-rn/PrivacyInfo.xcprivacy"; sourceTree = ""; }; - 3B4392A12AC88292D35C810B /* Pods-bare-rn.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bare-rn.debug.xcconfig"; path = "Target Support Files/Pods-bare-rn/Pods-bare-rn.debug.xcconfig"; sourceTree = ""; }; - 5709B34CF0A7D63546082F79 /* Pods-bare-rn.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-bare-rn.release.xcconfig"; path = "Target Support Files/Pods-bare-rn/Pods-bare-rn.release.xcconfig"; sourceTree = ""; }; - 5DCACB8F33CDC322A6C60F78 /* libPods-bare-rn.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-bare-rn.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = "bare-rn/AppDelegate.swift"; sourceTree = ""; }; - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = "bare-rn/LaunchScreen.storyboard"; sourceTree = ""; }; - ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0C80B921A6F3F58F76C31292 /* libPods-bare-rn.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 13B07FAE1A68108700A75B9A /* bare-rn */ = { - isa = PBXGroup; - children = ( - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 761780EC2CA45674006654EE /* AppDelegate.swift */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, - 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, - ); - name = "bare-rn"; - sourceTree = ""; - }; - 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { - isa = PBXGroup; - children = ( - ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 5DCACB8F33CDC322A6C60F78 /* libPods-bare-rn.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - 832341AE1AAA6A7D00B99B32 /* Libraries */ = { - isa = PBXGroup; - children = ( - ); - name = Libraries; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* bare-rn */, - 832341AE1AAA6A7D00B99B32 /* Libraries */, - 83CBBA001A601CBA00E9B192 /* Products */, - 2D16E6871FA4F8E400B85C8A /* Frameworks */, - BBD78D7AC51CEA395F1C20DB /* Pods */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - usesTabs = 0; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* bare-rn.app */, - ); - name = Products; - sourceTree = ""; - }; - BBD78D7AC51CEA395F1C20DB /* Pods */ = { - isa = PBXGroup; - children = ( - 3B4392A12AC88292D35C810B /* Pods-bare-rn.debug.xcconfig */, - 5709B34CF0A7D63546082F79 /* Pods-bare-rn.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 13B07F861A680F5B00A75B9A /* bare-rn */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "bare-rn" */; - buildPhases = ( - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "bare-rn"; - productName = "bare-rn"; - productReference = 13B07F961A680F5B00A75B9A /* bare-rn.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1210; - TargetAttributes = { - 13B07F861A680F5B00A75B9A = { - LastSwiftMigration = 1120; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "bare-rn" */; - compatibilityVersion = "Xcode 12.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* bare-rn */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 0AF1DC9B7BF72575F4599DF4 /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/.xcode.env.local", - "$(SRCROOT)/.xcode.env", - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"\\\"$WITH_ENVIRONMENT\\\" \\\"$REACT_NATIVE_XCODE\\\"\"\n"; - }; - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bare-rn/Pods-bare-rn-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bare-rn/Pods-bare-rn-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bare-rn/Pods-bare-rn-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-bare-rn-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bare-rn/Pods-bare-rn-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-bare-rn/Pods-bare-rn-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-bare-rn/Pods-bare-rn-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-bare-rn.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = "bare-rn/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "bare-rn"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-bare-rn.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - INFOPLIST_FILE = "bare-rn/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "bare-rn"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "\"$(SDKROOT)/usr/lib/swift\"", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_CPLUSPLUSFLAGS = ( - "$(OTHER_CFLAGS)", - "-DFOLLY_NO_CONFIG", - "-DFOLLY_MOBILE=1", - "-DFOLLY_USE_LIBCPP=1", - "-DFOLLY_CFG_NO_COROUTINES=1", - "-DFOLLY_HAVE_CLOCK_GETTIME=1", - ); - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; - USE_HERMES = true; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "\"$(SDKROOT)/usr/lib/swift\"", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CPLUSPLUSFLAGS = ( - "$(OTHER_CFLAGS)", - "-DFOLLY_NO_CONFIG", - "-DFOLLY_MOBILE=1", - "-DFOLLY_USE_LIBCPP=1", - "-DFOLLY_CFG_NO_COROUTINES=1", - "-DFOLLY_HAVE_CLOCK_GETTIME=1", - ); - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - USE_HERMES = true; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "bare-rn" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "bare-rn" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/apps/legacy/bare-rn/ios/bare-rn.xcodeproj/xcshareddata/xcschemes/bare-rn.xcscheme b/apps/legacy/bare-rn/ios/bare-rn.xcodeproj/xcshareddata/xcschemes/bare-rn.xcscheme deleted file mode 100644 index 6106d33b22..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn.xcodeproj/xcshareddata/xcschemes/bare-rn.xcscheme +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/legacy/bare-rn/ios/bare-rn.xcworkspace/contents.xcworkspacedata b/apps/legacy/bare-rn/ios/bare-rn.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 190b831198..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/apps/legacy/bare-rn/ios/bare-rn/AppDelegate.swift b/apps/legacy/bare-rn/ios/bare-rn/AppDelegate.swift deleted file mode 100644 index 4fb4ca4833..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn/AppDelegate.swift +++ /dev/null @@ -1,48 +0,0 @@ -import UIKit -import React -import React_RCTAppDelegate -import ReactAppDependencyProvider - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - - var reactNativeDelegate: ReactNativeDelegate? - var reactNativeFactory: RCTReactNativeFactory? - - func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil - ) -> Bool { - let delegate = ReactNativeDelegate() - let factory = RCTReactNativeFactory(delegate: delegate) - delegate.dependencyProvider = RCTAppDependencyProvider() - - reactNativeDelegate = delegate - reactNativeFactory = factory - - window = UIWindow(frame: UIScreen.main.bounds) - - factory.startReactNative( - withModuleName: "bare-rn", - in: window, - launchOptions: launchOptions - ) - - return true - } -} - -class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { - override func sourceURL(for bridge: RCTBridge) -> URL? { - self.bundleURL() - } - - override func bundleURL() -> URL? { -#if DEBUG - RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") -#else - Bundle.main.url(forResource: "main", withExtension: "jsbundle") -#endif - } -} diff --git a/apps/legacy/bare-rn/ios/bare-rn/Images.xcassets/AppIcon.appiconset/Contents.json b/apps/legacy/bare-rn/ios/bare-rn/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index ddd7fca89e..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "images": [ - { - "idiom": "iphone", - "scale": "2x", - "size": "20x20" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "20x20" - }, - { - "idiom": "iphone", - "scale": "2x", - "size": "29x29" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "29x29" - }, - { - "idiom": "iphone", - "scale": "2x", - "size": "40x40" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "40x40" - }, - { - "idiom": "iphone", - "scale": "2x", - "size": "60x60" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "60x60" - }, - { - "idiom": "ios-marketing", - "scale": "1x", - "size": "1024x1024" - } - ], - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/apps/legacy/bare-rn/ios/bare-rn/Images.xcassets/Contents.json b/apps/legacy/bare-rn/ios/bare-rn/Images.xcassets/Contents.json deleted file mode 100644 index 97a8662ebd..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info": { - "version": 1, - "author": "xcode" - } -} diff --git a/apps/legacy/bare-rn/ios/bare-rn/Info.plist b/apps/legacy/bare-rn/ios/bare-rn/Info.plist deleted file mode 100644 index 37fc9b5e77..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn/Info.plist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - bare-rn - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSLocationWhenInUseUsageDescription - - RCTNewArchEnabled - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - arm64 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/apps/legacy/bare-rn/ios/bare-rn/LaunchScreen.storyboard b/apps/legacy/bare-rn/ios/bare-rn/LaunchScreen.storyboard deleted file mode 100644 index 4bc47247dc..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn/LaunchScreen.storyboard +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/legacy/bare-rn/ios/bare-rn/PrivacyInfo.xcprivacy b/apps/legacy/bare-rn/ios/bare-rn/PrivacyInfo.xcprivacy deleted file mode 100644 index 41da7af967..0000000000 --- a/apps/legacy/bare-rn/ios/bare-rn/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,47 +0,0 @@ - - - - - NSPrivacyAccessedAPITypes - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryFileTimestamp - NSPrivacyAccessedAPITypeReasons - - C617.1 - 0A2A.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults - NSPrivacyAccessedAPITypeReasons - - CA92.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryDiskSpace - NSPrivacyAccessedAPITypeReasons - - 85F4.1 - E174.1 - - - - NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategorySystemBootTime - NSPrivacyAccessedAPITypeReasons - - 35F9.1 - - - - NSPrivacyCollectedDataTypes - - NSPrivacyTracking - - - diff --git a/apps/legacy/bare-rn/jest.config.js b/apps/legacy/bare-rn/jest.config.js deleted file mode 100644 index 8eb675e9bc..0000000000 --- a/apps/legacy/bare-rn/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - preset: 'react-native', -}; diff --git a/apps/legacy/bare-rn/metro.config.js b/apps/legacy/bare-rn/metro.config.js deleted file mode 100644 index 8f418e97f9..0000000000 --- a/apps/legacy/bare-rn/metro.config.js +++ /dev/null @@ -1,30 +0,0 @@ -const path = require('path'); -const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); - -const workspaceRoot = path.resolve(__dirname, '../../../'); // Adjust the path to your monorepo root -const projectRoot = __dirname; -const defaultConfig = getDefaultConfig(projectRoot); - -const config = { - watchFolders: [ - workspaceRoot, // Watch the entire monorepo - ], - resolver: { - // Workspace packages (react-native-executorch, bare-resource-fetcher) declare - // their own `react` devDependency, so yarn installs a second React copy at - // packages/*/node_modules/react. Without disabling hierarchical lookup, Metro - // resolves `react` per-file and the bundle ends up with two React instances — - // useState's dispatcher comes back null at runtime. - disableHierarchicalLookup: true, - nodeModulesPaths: [ - path.resolve(projectRoot, 'node_modules'), - path.resolve(workspaceRoot, 'node_modules'), - ], - assetExts: [ - ...defaultConfig.resolver.assetExts, - 'pte', // ExecuTorch model files - ], - }, -}; - -module.exports = mergeConfig(defaultConfig, config); diff --git a/apps/legacy/bare-rn/package.json b/apps/legacy/bare-rn/package.json deleted file mode 100644 index d7e9d2d033..0000000000 --- a/apps/legacy/bare-rn/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "legacy-bare-rn", - "version": "0.0.1", - "private": true, - "scripts": { - "android": "react-native run-android", - "ios": "react-native run-ios", - "typecheck": "tsc", - "lint": "eslint . --ext .ts,.tsx --fix", - "start": "react-native start", - "test": "jest" - }, - "dependencies": { - "@dr.pogodin/react-native-fs": "^2.36.2", - "@kesha-antonov/react-native-background-downloader": "^4.4.5", - "react": "19.1.0", - "react-native": "0.81.5", - "react-native-executorch": "workspace:*", - "react-native-executorch-bare-resource-fetcher": "workspace:*", - "react-native-safe-area-context": "^5.5.2" - }, - "devDependencies": { - "@babel/core": "^7.29.7", - "@babel/plugin-transform-export-namespace-from": "^7.25.2", - "@babel/preset-env": "^7.25.3", - "@babel/runtime": "^7.25.0", - "@react-native-community/cli": "20.0.0", - "@react-native-community/cli-platform-android": "20.0.0", - "@react-native-community/cli-platform-ios": "20.0.0", - "@react-native/babel-preset": "0.81.5", - "@react-native/eslint-config": "0.81.5", - "@react-native/metro-config": "0.81.5", - "@react-native/typescript-config": "0.81.5", - "@types/jest": "^29.5.13", - "@types/react": "^19.1.0", - "@types/react-test-renderer": "^19.1.0", - "eslint": "^8.19.0", - "jest": "^29.6.3", - "prettier": "2.8.8", - "react-test-renderer": "19.1.0", - "typescript": "^5.8.3" - }, - "engines": { - "node": ">=22" - }, - "installConfig": { - "hoistingLimits": "workspaces" - } -} diff --git a/apps/legacy/bare-rn/tsconfig.json b/apps/legacy/bare-rn/tsconfig.json deleted file mode 100644 index 4fe6f139ef..0000000000 --- a/apps/legacy/bare-rn/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "jsx": "react-native", - "lib": ["es2022"], - "moduleResolution": "bundler", - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "esnext" - }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["**/node_modules", "**/Pods", "**/__tests__"] -} diff --git a/apps/legacy/computer-vision/.gitignore b/apps/legacy/computer-vision/.gitignore deleted file mode 100644 index f779c900a7..0000000000 --- a/apps/legacy/computer-vision/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files - -# dependencies -node_modules/ - -# Expo -.expo/ -dist/ -web-build/ -expo-env.d.ts - -# Native -*.orig.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision - -# Metro -.metro-health-check* - -# debug -npm-debug.* -yarn-debug.* -yarn-error.* - -# macOS -.DS_Store -*.pem - -# local env files -.env*.local - -# typescript -*.tsbuildinfo - -.yarn diff --git a/apps/legacy/computer-vision/ScreenWrapper.tsx b/apps/legacy/computer-vision/ScreenWrapper.tsx deleted file mode 100644 index 31f70e4442..0000000000 --- a/apps/legacy/computer-vision/ScreenWrapper.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { useIsFocused } from 'expo-router'; -import { PropsWithChildren } from 'react'; - -export default function ScreenWrapper({ children }: PropsWithChildren) { - const isFocused = useIsFocused(); - - return isFocused ? <>{children} : null; -} diff --git a/apps/legacy/computer-vision/app.json b/apps/legacy/computer-vision/app.json deleted file mode 100644 index 9dd5e072ca..0000000000 --- a/apps/legacy/computer-vision/app.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "expo": { - "name": "legacy-computer-vision", - "slug": "legacy-computer-vision", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icons/icon.png", - "userInterfaceStyle": "light", - "newArchEnabled": true, - "scheme": "rne-legacy-computer-vision", - "splash": { - "image": "./assets/icons/splash.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.anonymous.legacycomputervision", - "infoPlist": { - "NSCameraUsageDescription": "Process photo from camera" - } - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/icons/adaptive-icon.png", - "backgroundColor": "#ffffff" - }, - "package": "com.anonymous.legacycomputervision", - "permissions": ["android.permission.CAMERA"] - }, - "web": { - "favicon": "./assets/icons/favicon.png" - }, - "plugins": [ - "expo-font", - "expo-router", - [ - "expo-build-properties", - { - "android": { - "minSdkVersion": 26 - }, - "ios": { - "deploymentTarget": "17.0" - } - } - ] - ] - } -} diff --git a/apps/legacy/computer-vision/app/_layout.tsx b/apps/legacy/computer-vision/app/_layout.tsx deleted file mode 100644 index cd46bb708e..0000000000 --- a/apps/legacy/computer-vision/app/_layout.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import { Drawer } from 'expo-router/drawer'; -import { initExecutorch } from 'react-native-executorch/legacy'; -import { ExpoResourceFetcher } from 'react-native-executorch-expo-resource-fetcher'; - -import ColorPalette from '../colors'; -import React, { useState } from 'react'; -import { Text, StyleSheet, View, TouchableOpacity, type ColorValue } from 'react-native'; - -import { - type DrawerContentComponentProps, - DrawerContentScrollView, - DrawerItemList, -} from 'expo-router/build/react-navigation/drawer'; -import { DrawerActions } from 'expo-router/build/react-navigation/routers'; -import { useNavigation } from 'expo-router'; -import Svg, { Rect } from 'react-native-svg'; -import { GeneratingContext } from '../context'; - -initExecutorch({ - resourceFetcher: ExpoResourceFetcher, -}); - -function HamburgerIcon({ tintColor }: { tintColor?: ColorValue }) { - const navigation = useNavigation(); - const fill = typeof tintColor === 'string' ? tintColor : '#000'; - return ( - navigation.dispatch(DrawerActions.toggleDrawer())} - style={styles.hamburger} - > - - - - - - - ); -} - -interface CustomDrawerProps extends DrawerContentComponentProps { - isGenerating: boolean; -} - -function CustomDrawerContent(props: CustomDrawerProps) { - const { isGenerating, ...otherProps } = props; - return ( - - {!isGenerating ? ( - - ) : ( - - Model is generating... - Interrupt before switching model - - )} - - ); -} - -export default function _layout() { - const [isGenerating, setIsGenerating] = useState(false); - - return ( - { - setIsGenerating(newState); - }, - }} - > - } - screenOptions={{ - drawerActiveTintColor: ColorPalette.primary, - drawerInactiveTintColor: '#888', - headerTintColor: ColorPalette.primary, - headerTitleStyle: { color: ColorPalette.primary }, - headerLeft: (props) => , - }} - > - null, - title: 'Main Menu', - drawerItemStyle: { display: 'none' }, - }} - /> - - - - - - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - hamburger: { - marginLeft: 12, - }, - centerContent: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: 20, - }, - mainText: { - fontSize: 18, - fontWeight: 'bold', - marginBottom: 10, - color: ColorPalette.primary, - }, - subText: { - fontSize: 14, - color: ColorPalette.strongPrimary, - }, -}); diff --git a/apps/legacy/computer-vision/app/classification/index.tsx b/apps/legacy/computer-vision/app/classification/index.tsx deleted file mode 100644 index 0cc5a78ed3..0000000000 --- a/apps/legacy/computer-vision/app/classification/index.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { getImage } from '../../utils'; -import { - models, - useClassification, - ClassificationModelSources, -} from 'react-native-executorch/legacy'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -const classification = models.classification; - -const MODELS: ModelOption[] = [ - { - label: 'EfficientNet V2 S Quantized', - value: classification.efficientnet_v2_s(), - }, - { - label: 'EfficientNet V2 S', - value: classification.efficientnet_v2_s({ quant: false }), - }, -]; -import { View, StyleSheet, Image, Text, ScrollView } from 'react-native'; -import { BottomBar } from '../../components/BottomBar'; -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import { StatsBar } from '../../components/StatsBar'; -import ErrorBanner from '../../components/ErrorBanner'; - -export default function ClassificationScreen() { - const [selectedModel, setSelectedModel] = useState( - classification.efficientnet_v2_s() - ); - const [results, setResults] = useState<{ label: string; score: number }[]>([]); - const [imageUri, setImageUri] = useState(''); - const [inferenceTime, setInferenceTime] = useState(null); - - const [error, setError] = useState(null); - - const model = useClassification({ model: selectedModel }); - const { setGlobalGenerating } = useContext(GeneratingContext); - - useEffect(() => { - setGlobalGenerating(model.isGenerating); - }, [model.isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - const uri = image?.uri; - if (typeof uri === 'string') { - setImageUri(uri as string); - setResults([]); - setInferenceTime(null); - } - }; - - const runForward = async () => { - if (imageUri) { - try { - const start = Date.now(); - const output = await model.forward(imageUri); - setInferenceTime(Date.now() - start); - const top10 = Object.entries(output) - .sort(([, a], [, b]) => (b as number) - (a as number)) - .slice(0, 10) - .map(([label, score]) => ({ label, score: score as number })); - setResults(top10); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - } - }; - - if (!model.isReady && !model.error) { - return ( - - ); - } - - return ( - - setError(null)} /> - - - - {!imageUri && ( - - Image Classification - - This model analyzes an image and returns the top 10 most likely labels with confidence - scores. Use the gallery or camera icons below to pick an image, then tap the button to - run the model. - - - )} - {results.length > 0 && ( - - Results Top 10 - - {results.map(({ label, score }) => ( - - {label} - {score.toFixed(3)} - - ))} - - - )} - - { - setSelectedModel(m); - setResults([]); - }} - /> - - - - ); -} - -const styles = StyleSheet.create({ - imageContainer: { - flex: 6, - width: '100%', - padding: 16, - }, - image: { - flex: 2, - borderRadius: 8, - width: '100%', - }, - results: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - gap: 4, - padding: 4, - }, - resultHeader: { - fontSize: 18, - color: 'navy', - }, - resultsList: { - flex: 1, - }, - resultRecord: { - flexDirection: 'row', - width: '100%', - justifyContent: 'space-between', - padding: 8, - borderBottomWidth: 1, - }, - resultLabel: { - flex: 1, - marginRight: 4, - }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/index.tsx b/apps/legacy/computer-vision/app/index.tsx deleted file mode 100644 index 3a2714b734..0000000000 --- a/apps/legacy/computer-vision/app/index.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { useRouter } from 'expo-router'; -import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; -import ColorPalette from '../colors'; -import ExecutorchLogo from '../assets/icons/executorch.svg'; - -export default function Home() { - const router = useRouter(); - - return ( - - - Select a demo model - - router.navigate('vision_camera/')}> - Vision Camera - - router.navigate('classification/')}> - Classification - - router.navigate('semantic_segmentation/')} - > - Semantic Segmentation - - router.navigate('object_detection/')} - > - Object Detection - - router.navigate('instance_segmentation/')} - > - Instance Segmentation - - router.navigate('pose_estimation/')}> - Pose Estimation - - router.navigate('segment_anything/')} - > - Segment Anything - - router.navigate('ocr/')}> - OCR - - router.navigate('ocr_vertical/')}> - OCR Vertical - - router.navigate('style_transfer/')}> - Style Transfer - - router.navigate('text_to_image/')}> - Image Generation - - - - ); -} - -export const fontSizes = { - xxl: 34, - xl: 22, - lg: 18, - md: 16, - sm: 14, - xs: 12, - xxs: 10, -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#fff', - }, - headerText: { - fontSize: fontSizes.lg, - color: ColorPalette.strongPrimary, - margin: 20, - }, - buttonContainer: { - width: '80%', - justifyContent: 'space-evenly', - marginBottom: 20, - }, - button: { - backgroundColor: ColorPalette.strongPrimary, - borderRadius: 8, - padding: 10, - alignItems: 'center', - marginBottom: 10, - }, - buttonText: { - color: 'white', - fontSize: fontSizes.md, - }, -}); diff --git a/apps/legacy/computer-vision/app/instance_segmentation/index.tsx b/apps/legacy/computer-vision/app/instance_segmentation/index.tsx deleted file mode 100644 index a2fa46ba07..0000000000 --- a/apps/legacy/computer-vision/app/instance_segmentation/index.tsx +++ /dev/null @@ -1,345 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { BottomBar } from '../../components/BottomBar'; -import { getImage } from '../../utils'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { - models, - useInstanceSegmentation, - InstanceSegmentationModelSources, -} from 'react-native-executorch/legacy'; -import { View, StyleSheet, ScrollView, Text, TouchableOpacity } from 'react-native'; -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import ImageWithMasks, { - buildDisplayInstances, - DisplayInstance, -} from '../../components/ImageWithMasks'; -import { StatsBar } from '../../components/StatsBar'; -const instanceSegmentation = models.instance_segmentation; - -const MODELS: ModelOption[] = [ - { label: 'Yolo26N', value: instanceSegmentation.yolo26n() }, - { label: 'Yolo26S', value: instanceSegmentation.yolo26s() }, - { label: 'Yolo26M', value: instanceSegmentation.yolo26m() }, - { label: 'Yolo26L', value: instanceSegmentation.yolo26l() }, - { label: 'Yolo26X', value: instanceSegmentation.yolo26x() }, - { - label: 'RF-DeTR Nano', - value: instanceSegmentation.rf_detr_nano(), - }, - { label: 'FastSAM-S', value: instanceSegmentation.fastsam_s() }, - { label: 'FastSAM-X', value: instanceSegmentation.fastsam_x() }, -]; - -export default function InstanceSegmentationScreen() { - const [selectedModel, setSelectedModel] = useState( - instanceSegmentation.yolo26n() - ); - const [inferenceTime, setInferenceTime] = useState(null); - - const { setGlobalGenerating } = useContext(GeneratingContext); - - const { isReady, isGenerating, downloadProgress, forward, error, getAvailableInputSizes } = - useInstanceSegmentation({ - model: selectedModel, - }); - - const [imageUri, setImageUri] = useState(''); - const [imageSize, setImageSize] = useState({ width: 0, height: 0 }); - const [instances, setInstances] = useState([]); - const [selectedInputSize, setSelectedInputSize] = useState(null); - - const availableInputSizes = getAvailableInputSizes(); - - useEffect(() => { - setGlobalGenerating(isGenerating); - }, [isGenerating, setGlobalGenerating]); - - // Set default input size when model is ready - useEffect(() => { - if (!isReady) return; - - if (availableInputSizes && availableInputSizes.length > 0) { - setSelectedInputSize((prev) => { - if (typeof prev === 'number' && availableInputSizes.includes(prev)) { - return prev; - } - return availableInputSizes[0]; - }); - return; - } - - setSelectedInputSize(null); - }, [isReady, availableInputSizes]); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - if (!image?.uri) return; - setImageUri(image.uri); - setImageSize({ - width: image.width ?? 0, - height: image.height ?? 0, - }); - setInstances([]); - setInferenceTime(null); - }; - - const runForward = async () => { - if (!imageUri || imageSize.width === 0 || imageSize.height === 0) return; - - const inputSize = - availableInputSizes && - typeof selectedInputSize === 'number' && - availableInputSizes.includes(selectedInputSize) - ? selectedInputSize - : undefined; - - try { - const start = Date.now(); - const output = await forward(imageUri, { - confidenceThreshold: 0.5, - iouThreshold: 0.55, - maxInstances: 20, - returnMaskAtOriginalResolution: true, - inputSize, - }); - - setInferenceTime(Date.now() - start); - - // Convert raw masks → small Skia images immediately. - // Raw Uint8Array mask buffers (backed by native OwningArrayBuffer) - // go out of scope here and become eligible for GC right away. - setInstances(buildDisplayInstances(output)); - } catch (e) { - console.error(e); - } - }; - - if (!isReady && error) { - return ( - - - Error Loading Model - {error?.message || 'Unknown error occurred'} - Code: {error?.code || 'N/A'} - - - ); - } - - if (!isReady) { - return ( - - ); - } - - return ( - - - - - {!imageUri && ( - - Instance Segmentation - - This model detects individual objects and draws a precise mask over each one. Pick - an image from your gallery or take one with your camera to get started. - - - )} - - - {imageUri && availableInputSizes && availableInputSizes.length > 0 && ( - - Input Size: - - {availableInputSizes.map((size) => ( - setSelectedInputSize(size)} - > - - {size} - - - ))} - - - )} - - {instances.length > 0 && ( - - Detected {instances.length} instance(s) - - {instances.map((instance, idx) => ( - - - {instance.label || 'Unknown'} ({(instance.score * 100).toFixed(1)}%) - - - ))} - - - )} - - - { - setSelectedModel(m); - setInstances([]); - setInferenceTime(null); - }} - /> - - 0 ? instances.length : null} - /> - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 6, - width: '100%', - }, - imageContainer: { - flex: 1, - width: '100%', - padding: 16, - }, - inputSizeContainer: { - paddingHorizontal: 16, - paddingVertical: 12, - backgroundColor: '#fff', - borderTopWidth: 1, - borderTopColor: '#e0e0e0', - }, - inputSizeLabel: { - fontSize: 14, - fontWeight: '600', - color: '#333', - marginBottom: 8, - }, - inputSizeScroll: { - flexDirection: 'row', - }, - sizeButton: { - paddingHorizontal: 16, - paddingVertical: 8, - marginRight: 8, - borderRadius: 6, - backgroundColor: '#f0f0f0', - }, - sizeButtonActive: { - backgroundColor: '#007AFF', - }, - sizeButtonText: { - fontSize: 14, - fontWeight: '600', - color: '#666', - }, - sizeButtonTextActive: { - color: '#fff', - }, - resultsContainer: { - maxHeight: 200, - paddingHorizontal: 16, - paddingVertical: 12, - backgroundColor: '#fff', - borderTopWidth: 1, - borderTopColor: '#e0e0e0', - }, - resultsHeader: { - fontSize: 16, - fontWeight: '600', - marginBottom: 8, - color: '#333', - }, - resultsList: { - flex: 1, - }, - resultRow: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: 6, - paddingHorizontal: 8, - marginBottom: 4, - backgroundColor: '#f9f9f9', - borderRadius: 6, - }, - resultText: { - fontSize: 14, - fontWeight: '500', - color: '#333', - }, - errorContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: 32, - }, - errorTitle: { - fontSize: 20, - fontWeight: '700', - color: '#e74c3c', - marginBottom: 12, - }, - errorText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - marginBottom: 8, - }, - errorCode: { - fontSize: 12, - color: '#999', - fontFamily: 'Courier', - }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/object_detection/index.tsx b/apps/legacy/computer-vision/app/object_detection/index.tsx deleted file mode 100644 index 37a3ab7b32..0000000000 --- a/apps/legacy/computer-vision/app/object_detection/index.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { BottomBar } from '../../components/BottomBar'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { getImage } from '../../utils'; -import { - models, - Detection, - useObjectDetection, - ObjectDetectionModelSources, -} from 'react-native-executorch/legacy'; -import { View, StyleSheet, Image, Text } from 'react-native'; -import ImageWithBboxes from '../../components/ImageWithBboxes'; -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import { StatsBar } from '../../components/StatsBar'; -const objectDetection = models.object_detection; - -const MODELS: ModelOption[] = [ - { - label: 'RF-DeTR Nano', - value: objectDetection.rf_detr_nano(), - }, - { - label: 'SSDLite MobileNet', - value: objectDetection.ssdlite_320_mobilenet_v3_large(), - }, - { label: 'YOLO26N', value: objectDetection.yolo26n() }, - { label: 'YOLO26S', value: objectDetection.yolo26s() }, - { label: 'YOLO26M', value: objectDetection.yolo26m() }, - { label: 'YOLO26L', value: objectDetection.yolo26l() }, - { label: 'YOLO26X', value: objectDetection.yolo26x() }, -]; -import ErrorBanner from '../../components/ErrorBanner'; - -export default function ObjectDetectionScreen() { - const [imageUri, setImageUri] = useState(''); - const [results, setResults] = useState([]); - const [error, setError] = useState(null); - const [imageDimensions, setImageDimensions] = useState<{ - width: number; - height: number; - }>(); - const [selectedModel, setSelectedModel] = useState( - objectDetection.rf_detr_nano() - ); - const [inferenceTime, setInferenceTime] = useState(null); - - const model = useObjectDetection({ model: selectedModel }); - const { setGlobalGenerating } = useContext(GeneratingContext); - useEffect(() => { - setGlobalGenerating(model.isGenerating); - }, [model.isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - const uri = image?.uri; - const width = image?.width; - const height = image?.height; - - if (uri && width && height) { - setImageUri(image.uri as string); - setImageDimensions({ width: width as number, height: height as number }); - setResults([]); - setInferenceTime(null); - } - }; - - const runForward = async () => { - if (imageUri) { - try { - const start = Date.now(); - const output = await model.forward(imageUri); - setInferenceTime(Date.now() - start); - setResults(output); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - } - }; - - if (!model.isReady) { - return ( - - ); - } - - return ( - - setError(null)} /> - - - {imageUri && imageDimensions?.width && imageDimensions?.height ? ( - - ) : ( - - )} - - {!imageUri && ( - - Object Detection - - This model detects objects in an image and draws bounding boxes around them with class - labels and confidence scores. Pick an image from your gallery or take one with your - camera to get started. - - - )} - - { - setSelectedModel(m); - setResults([]); - }} - /> - 0 ? results.length : null} - /> - - - ); -} - -const styles = StyleSheet.create({ - imageContainer: { - flex: 6, - width: '100%', - padding: 16, - }, - image: { - flex: 2, - borderRadius: 8, - width: '100%', - }, - fullSizeImage: { - width: '100%', - height: '100%', - }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/ocr/index.tsx b/apps/legacy/computer-vision/app/ocr/index.tsx deleted file mode 100644 index a1542ab953..0000000000 --- a/apps/legacy/computer-vision/app/ocr/index.tsx +++ /dev/null @@ -1,207 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { BottomBar } from '../../components/BottomBar'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { getImage } from '../../utils'; -import { models, useOCR, OCRProps } from 'react-native-executorch/legacy'; -import { View, StyleSheet, Image, Text, ScrollView } from 'react-native'; -import ImageWithBboxes2 from '../../components/ImageWithOCRBboxes'; -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import { StatsBar } from '../../components/StatsBar'; - -type OCRModelSources = OCRProps['model']; - -const ocr = models.ocr.craft; - -const MODELS: ModelOption[] = [ - { label: 'English', value: ocr({ language: 'en' }) }, - { label: 'German', value: ocr({ language: 'de' }) }, - { label: 'French', value: ocr({ language: 'fr' }) }, - { label: 'Spanish', value: ocr({ language: 'es' }) }, - { label: 'Italian', value: ocr({ language: 'it' }) }, - { label: 'Japanese', value: ocr({ language: 'ja' }) }, - { label: 'Korean', value: ocr({ language: 'ko' }) }, -]; -import ErrorBanner from '../../components/ErrorBanner'; - -export default function OCRScreen() { - const [imageUri, setImageUri] = useState(''); - const [results, setResults] = useState([]); - const [error, setError] = useState(null); - const [imageDimensions, setImageDimensions] = useState<{ - width: number; - height: number; - }>(); - const [selectedModel, setSelectedModel] = useState(ocr({ language: 'en' })); - const [inferenceTime, setInferenceTime] = useState(null); - - const model = useOCR({ - model: selectedModel, - }); - const { setGlobalGenerating } = useContext(GeneratingContext); - useEffect(() => { - setGlobalGenerating(model.isGenerating); - }, [model.isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - const width = image?.width; - const height = image?.height; - setImageDimensions({ width: width as number, height: height as number }); - const uri = image?.uri; - if (typeof uri === 'string') { - setImageUri(uri as string); - setResults([]); - setInferenceTime(null); - } - }; - - const runForward = async () => { - try { - const start = Date.now(); - const output = await model.forward(imageUri); - setInferenceTime(Date.now() - start); - setResults(output); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - if (!model.isReady && !model.error) { - return ( - - ); - } - - return ( - - setError(null)} /> - - - {imageUri && imageDimensions?.width && imageDimensions?.height ? ( - - ) : ( - - )} - - {!imageUri && ( - - OCR - - This model reads and extracts text from images, returning each detected text region - with its bounding box and confidence score. Pick an image from your gallery or take - one with your camera to get started. - - - )} - {results.length > 0 && ( - - Results - - {results.map(({ text, score }, index) => ( - - {text} - {score.toFixed(3)} - - ))} - - - )} - - { - setSelectedModel(m); - setResults([]); - }} - /> - 0 ? results.length : null} - /> - - - ); -} - -const styles = StyleSheet.create({ - imageContainer: { - flex: 2, - borderRadius: 8, - width: '100%', - }, - container: { - flex: 6, - width: '100%', - padding: 16, - }, - image: { - width: '100%', - height: '100%', - }, - results: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - gap: 4, - padding: 4, - }, - resultHeader: { - fontSize: 18, - color: 'navy', - }, - resultsList: { - flex: 1, - }, - resultRecord: { - flexDirection: 'row', - width: '100%', - justifyContent: 'space-between', - padding: 8, - borderBottomWidth: 1, - }, - resultLabel: { - flex: 1, - marginRight: 4, - }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/ocr_vertical/index.tsx b/apps/legacy/computer-vision/app/ocr_vertical/index.tsx deleted file mode 100644 index 3aa8c02dd6..0000000000 --- a/apps/legacy/computer-vision/app/ocr_vertical/index.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { BottomBar } from '../../components/BottomBar'; -import { getImage } from '../../utils'; -import { models, useVerticalOCR } from 'react-native-executorch/legacy'; -import { View, StyleSheet, Image, Text, ScrollView } from 'react-native'; -import ImageWithBboxes2 from '../../components/ImageWithOCRBboxes'; -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import { StatsBar } from '../../components/StatsBar'; -import ErrorBanner from '../../components/ErrorBanner'; - -export default function VerticalOCRScreen() { - const [imageUri, setImageUri] = useState(''); - const [results, setResults] = useState([]); - const [imageDimensions, setImageDimensions] = useState<{ - width: number; - height: number; - }>(); - const [inferenceTime, setInferenceTime] = useState(null); - - const [error, setError] = useState(null); - - const model = useVerticalOCR({ - model: models.ocr.craft({ language: 'en' }), - independentCharacters: true, - }); - - const { setGlobalGenerating } = useContext(GeneratingContext); - - useEffect(() => { - setGlobalGenerating(model.isGenerating); - }, [model.isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - const width = image?.width; - const height = image?.height; - setImageDimensions({ width: width as number, height: height as number }); - const uri = image?.uri; - if (typeof uri === 'string') { - setImageUri(uri as string); - setResults([]); - setInferenceTime(null); - } - }; - - const runForward = async () => { - try { - const start = Date.now(); - const output = await model.forward(imageUri); - setInferenceTime(Date.now() - start); - setResults(output); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - if (!model.isReady && !model.error) { - return ( - - ); - } - - return ( - - - setError(null)} /> - - - {imageUri && imageDimensions?.width && imageDimensions?.height ? ( - - ) : ( - - )} - - {!imageUri && ( - - Vertical OCR - - This model reads vertical text (e.g. Japanese, Korean, Chinese columns) from images, - returning each detected text region with its bounding box and confidence score. Pick - an image from your gallery or take one with your camera to get started. - - - )} - {imageUri && inferenceTime !== null && results.length === 0 && ( - - No text detected - - The model did not find any vertical text in this image. Try an image containing - vertical Japanese, Korean, or Chinese text. - - - )} - {results.length > 0 && ( - - Results - - {results.map(({ text, score }, index) => ( - - {text} - {score?.toFixed(3)} - - ))} - - - )} - - 0 ? results.length : null} - /> - - - ); -} - -const styles = StyleSheet.create({ - imageContainer: { - flex: 2, - borderRadius: 8, - width: '100%', - }, - container: { - flex: 6, - width: '100%', - padding: 16, - }, - image: { - width: '100%', - height: '100%', - }, - results: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - gap: 4, - padding: 4, - }, - resultHeader: { - fontSize: 18, - color: 'navy', - }, - resultsList: { - flex: 1, - }, - resultRecord: { - flexDirection: 'row', - width: '100%', - justifyContent: 'space-between', - padding: 8, - borderBottomWidth: 1, - }, - resultLabel: { - flex: 1, - marginRight: 4, - }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/pose_estimation/index.tsx b/apps/legacy/computer-vision/app/pose_estimation/index.tsx deleted file mode 100644 index 68c371a9ed..0000000000 --- a/apps/legacy/computer-vision/app/pose_estimation/index.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { BottomBar } from '../../components/BottomBar'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { getImage } from '../../utils'; -import { - models, - usePoseEstimation, - PoseDetections, - PoseEstimationModelSources, - RnExecutorchError, - RnExecutorchErrorCode, -} from 'react-native-executorch/legacy'; -import { View, StyleSheet, Image, Text } from 'react-native'; -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import { StatsBar } from '../../components/StatsBar'; -import Svg, { Circle, Line } from 'react-native-svg'; -import ErrorBanner from '../../components/ErrorBanner'; -import { COCO_SKELETON_CONNECTIONS } from '../../components/utils/cocoSkeleton'; - -const poseEstimation = models.pose_estimation; - -const MODELS: ModelOption[] = [ - { label: 'YOLO26N Pose', value: poseEstimation.yolo26n() }, - { - label: 'RF-DETR Keypoint (beta)', - value: poseEstimation.rfdetr_keypoint_preview(), - }, -]; - -// Colors for different people -const PERSON_COLORS = ['lime', 'cyan', 'magenta', 'yellow', 'orange', 'pink']; - -export default function PoseEstimationScreen() { - const [imageUri, setImageUri] = useState(''); - const [results, setResults] = useState([]); - const [error, setError] = useState(null); - const [imageDimensions, setImageDimensions] = useState<{ - width: number; - height: number; - }>(); - const [inferenceTime, setInferenceTime] = useState(null); - const [layout, setLayout] = useState({ width: 0, height: 0 }); - const [selectedModel, setSelectedModel] = useState( - poseEstimation.yolo26n() - ); - - const model = usePoseEstimation({ model: selectedModel }); - const { setGlobalGenerating } = useContext(GeneratingContext); - - useEffect(() => { - setGlobalGenerating(model.isGenerating); - }, [model.isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - const uri = image?.uri; - const width = image?.width; - const height = image?.height; - - if (uri && width && height) { - setImageUri(image.uri as string); - setImageDimensions({ width, height }); - setResults([]); - setInferenceTime(null); - } - }; - - const runForward = async () => { - if (imageUri) { - try { - const start = Date.now(); - const output = await model.forward(imageUri); - setInferenceTime(Date.now() - start); - setResults(output); - } catch (e) { - if (e instanceof RnExecutorchError) { - switch (e.code) { - case RnExecutorchErrorCode.FileReadFailed: - setError('Could not read the selected image.'); - break; - case RnExecutorchErrorCode.ModelGenerating: - setError('Model is busy — wait for the current run to finish.'); - break; - case RnExecutorchErrorCode.InvalidUserInput: - case RnExecutorchErrorCode.InvalidArgument: - setError(`Invalid input: ${e.message}`); - break; - default: - setError(e.message); - } - } else { - setError(e instanceof Error ? e.message : String(e)); - } - } - } - }; - - if (!model.isReady) { - return ( - - ); - } - - return ( - - setError(null)} /> - - - {imageUri && imageDimensions?.width && imageDimensions?.height ? ( - - setLayout({ - width: e.nativeEvent.layout.width, - height: e.nativeEvent.layout.height, - }) - } - > - - {results.length > 0 && - layout.width > 0 && - layout.height > 0 && - (() => { - // Account for resizeMode="contain" letterboxing: the image's - // displayed area is smaller than the container in one axis. - const imageRatio = imageDimensions.width / imageDimensions.height; - const layoutRatio = layout.width / layout.height; - let scaleX: number, scaleY: number; - if (imageRatio > layoutRatio) { - scaleX = layout.width / imageDimensions.width; - scaleY = layout.width / imageRatio / imageDimensions.height; - } else { - scaleY = layout.height / imageDimensions.height; - scaleX = (layout.height * imageRatio) / imageDimensions.width; - } - const offsetX = (layout.width - imageDimensions.width * scaleX) / 2; - const offsetY = (layout.height - imageDimensions.height * scaleY) / 2; - const isInBounds = (kp: { x: number; y: number }) => - kp.x >= 0 && - kp.y >= 0 && - kp.x <= imageDimensions.width && - kp.y <= imageDimensions.height; - return ( - - {results.map((personKeypoints, personIdx) => { - const color = PERSON_COLORS[personIdx % PERSON_COLORS.length]; - return ( - - {COCO_SKELETON_CONNECTIONS.map(([from, to], lineIdx) => { - const kp1 = personKeypoints[from]; - const kp2 = personKeypoints[to]; - if (!kp1 || !kp2) return null; - if (!isInBounds(kp1) || !isInBounds(kp2)) return null; - return ( - - ); - })} - {Object.entries(personKeypoints) - .filter(([, kp]) => isInBounds(kp)) - .map(([name, kp]) => ( - - ))} - - ); - })} - - ); - })()} - - ) : ( - - )} - - {!imageUri && ( - - Pose Estimation - - This model detects human body keypoints (17 COCO keypoints) and draws a skeleton - overlay. Pick an image from your gallery or take one with your camera to get started. - - - )} - - { - setSelectedModel(m); - setResults([]); - setInferenceTime(null); - }} - /> - 0 ? results.length : null} - /> - - - ); -} - -const styles = StyleSheet.create({ - imageContainer: { - flex: 6, - width: '100%', - padding: 16, - }, - image: { - flex: 2, - borderRadius: 8, - width: '100%', - }, - imageWrapper: { - flex: 1, - width: '100%', - height: '100%', - }, - fullSizeImage: { - width: '100%', - height: '100%', - }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/segment_anything/index.tsx b/apps/legacy/computer-vision/app/segment_anything/index.tsx deleted file mode 100644 index f8cfc1b375..0000000000 --- a/apps/legacy/computer-vision/app/segment_anything/index.tsx +++ /dev/null @@ -1,586 +0,0 @@ -import React, { useContext, useEffect, useRef, useState } from 'react'; -import { - View, - StyleSheet, - Text, - TextInput, - TouchableOpacity, - TouchableWithoutFeedback, - GestureResponderEvent, - Keyboard, - KeyboardAvoidingView, - Platform, -} from 'react-native'; -import { - Canvas, - Rect, - Skia, - useImage, - type SkImage, - ColorType, - AlphaType, -} from '@shopify/react-native-skia'; -import { - models, - useInstanceSegmentation, - useImageEmbeddings, - useTextEmbeddings, - InstanceSegmentationModelSources, - SegmentedInstance, - FastSAMLabel, - selectByPoint, - selectByBox, - selectByText, - Bbox, -} from 'react-native-executorch/legacy'; -import { GeneratingContext } from '../../context'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { BottomBar } from '../../components/BottomBar'; -import { StatsBar } from '../../components/StatsBar'; -import Spinner from '../../components/Spinner'; -import ScreenWrapper from '../../ScreenWrapper'; -import ImageWithMasks, { - buildDisplayInstances, - DisplayInstance, -} from '../../components/ImageWithMasks'; -import { getImage } from '../../utils'; -import ColorPalette from '../../colors'; -const instanceSegmentation = models.instance_segmentation; - -type PromptMode = 'point' | 'box' | 'text'; - -const MODELS: ModelOption[] = [ - { label: 'FastSAM-S', value: instanceSegmentation.fastsam_s() }, - { label: 'FastSAM-X', value: instanceSegmentation.fastsam_x() }, -]; - -export default function SegmentAnythingScreen() { - const { setGlobalGenerating } = useContext(GeneratingContext); - - const [selectedModel, setSelectedModel] = useState( - instanceSegmentation.fastsam_s() - ); - const [mode, setMode] = useState('point'); - const [inferenceTime, setInferenceTime] = useState(null); - - const [imageUri, setImageUri] = useState(''); - const [imageSize, setImageSize] = useState({ width: 0, height: 0 }); - - const rawInstancesRef = useRef[]>([]); - const [selection, setSelection] = useState([]); - - const [draftBox, setDraftBox] = useState(null); - const boxStartRef = useRef<{ x: number; y: number } | null>(null); - const layoutRef = useRef({ width: 0, height: 0 }); - - const { isReady, isGenerating, downloadProgress, forward, error } = useInstanceSegmentation({ - model: selectedModel, - }); - - const clipImage = useImageEmbeddings({ - model: models.image_embedding.clip_vit_base_patch32_image(), - }); - const clipText = useTextEmbeddings({ - model: models.text_embedding.clip_vit_base_patch32_text(), - }); - const skiaSource = useImage(imageUri || null); - - const [textPrompt, setTextPrompt] = useState(''); - const [textBusy, setTextBusy] = useState(false); - const [embeddingProgress, setEmbeddingProgress] = useState<{ - done: number; - total: number; - } | null>(null); - const instanceEmbeddingsRef = useRef(null); - - useEffect(() => { - setGlobalGenerating(isGenerating); - }, [isGenerating, setGlobalGenerating]); - - function applyMatch(match: SegmentedInstance | null): void { - setSelection(match ? buildDisplayInstances([match]) : []); - } - - function touchToImageCoords(touchX: number, touchY: number) { - const { width: cw, height: ch } = layoutRef.current; - const { width: iw, height: ih } = imageSize; - if (iw === 0 || ih === 0) return null; - const scale = Math.min(cw / iw, ch / ih); - return { - x: (touchX - (cw - iw * scale) / 2) / scale, - y: (touchY - (ch - ih * scale) / 2) / scale, - }; - } - - function handleTap(e: GestureResponderEvent) { - if (mode !== 'point' || rawInstancesRef.current.length === 0) return; - const c = touchToImageCoords(e.nativeEvent.locationX, e.nativeEvent.locationY); - if (!c) return; - applyMatch(selectByPoint(rawInstancesRef.current, Math.round(c.x), Math.round(c.y))); - } - - function handleBoxStart(e: GestureResponderEvent) { - if (mode !== 'box') return; - const c = touchToImageCoords(e.nativeEvent.locationX, e.nativeEvent.locationY); - if (!c) return; - boxStartRef.current = c; - setDraftBox({ x1: c.x, y1: c.y, x2: c.x, y2: c.y }); - } - - function handleBoxMove(e: GestureResponderEvent) { - if (mode !== 'box' || !boxStartRef.current) return; - const c = touchToImageCoords(e.nativeEvent.locationX, e.nativeEvent.locationY); - if (!c) return; - const s = boxStartRef.current; - setDraftBox({ - x1: Math.min(s.x, c.x), - y1: Math.min(s.y, c.y), - x2: Math.max(s.x, c.x), - y2: Math.max(s.y, c.y), - }); - } - - function handleBoxEnd(e: GestureResponderEvent) { - if (mode !== 'box' || !boxStartRef.current) return; - const c = touchToImageCoords(e.nativeEvent.locationX, e.nativeEvent.locationY); - const s = boxStartRef.current; - boxStartRef.current = null; - setDraftBox(null); - if (!c || rawInstancesRef.current.length === 0) return; - applyMatch( - selectByBox(rawInstancesRef.current, { - x1: Math.min(s.x, c.x), - y1: Math.min(s.y, c.y), - x2: Math.max(s.x, c.x), - y2: Math.max(s.y, c.y), - }) - ); - } - - async function runTextPrompt() { - Keyboard.dismiss(); - const instances = rawInstancesRef.current; - if ( - !textPrompt.trim() || - instances.length === 0 || - !skiaSource || - !clipImage.isReady || - !clipText.isReady || - textBusy - ) { - return; - } - setTextBusy(true); - try { - if (!instanceEmbeddingsRef.current) { - setEmbeddingProgress({ done: 0, total: instances.length }); - const embeddings: Float32Array[] = []; - for (let i = 0; i < instances.length; i++) { - const inst = instances[i]!; - embeddings.push( - await cropAndEmbed( - skiaSource, - inst.bbox, - inst.mask, - inst.maskWidth, - inst.maskHeight, - clipImage.forward - ) - ); - setEmbeddingProgress({ done: i + 1, total: instances.length }); - } - instanceEmbeddingsRef.current = embeddings; - setEmbeddingProgress(null); - } - const textEmb = await clipText.forward(textPrompt); - const match = selectByText(instances, instanceEmbeddingsRef.current, textEmb); - applyMatch(match); - } catch (e) { - console.error(e); - } finally { - setTextBusy(false); - } - } - - const handleCameraPress = async (isCamera: boolean) => { - Keyboard.dismiss(); - const image = await getImage(isCamera); - if (!image?.uri) return; - setImageUri(image.uri); - setImageSize({ width: image.width ?? 0, height: image.height ?? 0 }); - rawInstancesRef.current = []; - instanceEmbeddingsRef.current = null; - setSelection([]); - setInferenceTime(null); - }; - - const runForward = async () => { - Keyboard.dismiss(); - if (!imageUri) return; - try { - const start = Date.now(); - const output = await forward(imageUri, { - confidenceThreshold: 0.4, - iouThreshold: 0.9, - maxInstances: 50, - returnMaskAtOriginalResolution: true, - }); - setInferenceTime(Date.now() - start); - rawInstancesRef.current = output; - instanceEmbeddingsRef.current = null; - setSelection([]); - } catch (e) { - console.error(e); - } - }; - - if (!isReady && error) { - return ( - - - Error Loading Model - {error.message} - - - ); - } - - if (!isReady) { - return ( - - ); - } - - const { width: cw, height: ch } = layoutRef.current; - const { width: iw, height: ih } = imageSize; - const drawScale = iw > 0 && ih > 0 ? Math.min(cw / iw, ch / ih) : 1; - const offsetX = (cw - iw * drawScale) / 2; - const offsetY = (ch - ih * drawScale) / 2; - - const stepHint = !imageUri - ? null - : inferenceTime === null - ? 'Tap Run to detect instances' - : rawInstancesRef.current.length === 0 - ? 'No instances detected — try another image' - : selection.length === 0 - ? 'Tap a point, draw a box, or describe an object' - : null; - - return ( - - - - - - { - layoutRef.current = { - width: e.nativeEvent.layout.width, - height: e.nativeEvent.layout.height, - }; - }} - onTouchStart={(e) => { - Keyboard.dismiss(); - if (mode === 'point') handleTap(e); - else if (mode === 'box') handleBoxStart(e); - }} - onTouchMove={handleBoxMove} - onTouchEnd={handleBoxEnd} - > - - {draftBox && iw > 0 && ( - - - - )} - - {!imageUri && ( - - Segment Anything - - Segment any object in an image. (1) Pick an image, (2) tap Run to detect - instances, (3) tap a point, draw a box, or describe an object to segment it. - - - )} - - - - {stepHint && {stepHint}} - - - {(['point', 'box', 'text'] as PromptMode[]).map((m) => { - const promptDisabled = rawInstancesRef.current.length === 0; - return ( - { - if (m !== 'text') Keyboard.dismiss(); - setMode(m); - }} - disabled={promptDisabled} - > - - {m[0]!.toUpperCase() + m.slice(1)} - - - ); - })} - - - {mode === 'text' && ( - - - {(() => { - const findInactive = - !textPrompt.trim() || - rawInstancesRef.current.length === 0 || - !clipImage.isReady || - !clipText.isReady; - return ( - - Find - - ); - })()} - - )} - {mode === 'text' && embeddingProgress && ( - - Embedding instances {embeddingProgress.done}/{embeddingProgress.total} (subsequent - text queries are instant) - - )} - - { - if (m.modelName === selectedModel.modelName) return; - setSelectedModel(m); - rawInstancesRef.current = []; - instanceEmbeddingsRef.current = null; - setSelection([]); - setInferenceTime(null); - }} - /> - - 0 ? rawInstancesRef.current.length : null - } - /> - - - - - - ); -} - -async function cropAndEmbed( - image: SkImage, - bbox: Bbox, - mask: Uint8Array, - maskWidth: number, - maskHeight: number, - forward: (input: string) => Promise -): Promise { - const imgW = image.width(); - const imgH = image.height(); - const surface = Skia.Surface.MakeOffscreen(imgW, imgH); - if (!surface) throw new Error('Failed to create offscreen Skia surface'); - const canvas = surface.getCanvas(); - canvas.clear(Skia.Color('white')); - - const x1 = Math.max(0, Math.round(bbox.x1)); - const y1 = Math.max(0, Math.round(bbox.y1)); - const x2 = Math.min(imgW, Math.round(bbox.x2)); - const y2 = Math.min(imgH, Math.round(bbox.y2)); - const w = x2 - x1; - const h = y2 - y1; - if (w > 0 && h > 0) { - canvas.drawImageRect( - image, - { x: x1, y: y1, width: w, height: h }, - { x: x1, y: y1, width: w, height: h }, - Skia.Paint() - ); - } - - const inversePixels = new Uint8Array(mask.length * 4); - for (let i = 0; i < mask.length; i++) { - const outside = mask[i]! === 0; - const idx = i * 4; - inversePixels[idx] = outside ? 255 : 0; - inversePixels[idx + 1] = outside ? 255 : 0; - inversePixels[idx + 2] = outside ? 255 : 0; - inversePixels[idx + 3] = outside ? 255 : 0; - } - const inverseData = Skia.Data.fromBytes(inversePixels); - const inverseMaskImg = Skia.Image.MakeImage( - { - width: maskWidth, - height: maskHeight, - colorType: ColorType.RGBA_8888, - alphaType: AlphaType.Premul, - }, - inverseData, - maskWidth * 4 - ); - if (inverseMaskImg) { - canvas.drawImageRect( - inverseMaskImg, - { x: 0, y: 0, width: maskWidth, height: maskHeight }, - { - x: bbox.x1, - y: bbox.y1, - width: bbox.x2 - bbox.x1, - height: bbox.y2 - bbox.y1, - }, - Skia.Paint() - ); - } - - const base64 = surface.makeImageSnapshot().encodeToBase64(); - inverseData.dispose(); - return forward(`data:image/png;base64,${base64}`); -} - -const styles = StyleSheet.create({ - flex: { flex: 1 }, - container: { flex: 6, width: '100%' }, - imageContainer: { flex: 1, width: '100%', padding: 16 }, - imageTouchArea: { flex: 1, position: 'relative' }, - infoContainer: { alignItems: 'center', padding: 16, gap: 8 }, - infoTitle: { fontSize: 18, fontWeight: '600', color: 'navy' }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, - modeRow: { - flexDirection: 'row', - justifyContent: 'center', - paddingVertical: 8, - gap: 8, - }, - modeBtn: { - paddingHorizontal: 18, - paddingVertical: 8, - borderRadius: 8, - borderWidth: 1, - borderColor: ColorPalette.primary, - backgroundColor: '#fff', - }, - modeBtnActive: { backgroundColor: ColorPalette.primary }, - modeBtnDisabled: { borderColor: '#cbd5e1', backgroundColor: '#f8fafc' }, - modeBtnText: { fontSize: 14, fontWeight: '600', color: ColorPalette.primary }, - modeBtnTextActive: { color: '#fff' }, - modeBtnTextDisabled: { color: '#cbd5e1' }, - textRow: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 16, - paddingBottom: 8, - gap: 8, - }, - textInput: { - flex: 1, - backgroundColor: '#fff', - borderWidth: 1, - borderColor: ColorPalette.primary, - borderRadius: 12, - paddingHorizontal: 14, - paddingVertical: 12, - fontSize: 16, - color: '#0f172a', - }, - textBtn: { - backgroundColor: ColorPalette.primary, - borderRadius: 12, - paddingVertical: 14, - width: 80, - alignItems: 'center', - }, - textBtnDisabled: { backgroundColor: '#cbd5e1' }, - textBtnLabel: { color: '#fff', fontWeight: '700', fontSize: 16 }, - statusLine: { - paddingHorizontal: 16, - paddingBottom: 6, - fontSize: 12, - color: '#64748b', - }, - stepHint: { - paddingHorizontal: 16, - paddingTop: 6, - fontSize: 13, - fontWeight: '500', - color: ColorPalette.primary, - textAlign: 'center', - }, - errorContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: 32, - }, - errorTitle: { - fontSize: 20, - fontWeight: '700', - color: '#e74c3c', - marginBottom: 12, - }, - errorText: { fontSize: 14, color: '#555', textAlign: 'center' }, -}); diff --git a/apps/legacy/computer-vision/app/semantic_segmentation/index.tsx b/apps/legacy/computer-vision/app/semantic_segmentation/index.tsx deleted file mode 100644 index 019ea8b71b..0000000000 --- a/apps/legacy/computer-vision/app/semantic_segmentation/index.tsx +++ /dev/null @@ -1,260 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { BottomBar } from '../../components/BottomBar'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { getImage } from '../../utils'; -import { - models, - useSemanticSegmentation, - SemanticSegmentationModelSources, -} from 'react-native-executorch/legacy'; -import { - Canvas, - Image as SkiaImage, - Skia, - AlphaType, - ColorType, - SkImage, -} from '@shopify/react-native-skia'; -import { View, StyleSheet, Image, Text } from 'react-native'; -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import { StatsBar } from '../../components/StatsBar'; -import ErrorBanner from '../../components/ErrorBanner'; -const semanticSegmentation = models.semantic_segmentation; - -const numberToColor: number[][] = [ - [255, 87, 51], // 0 Red - [51, 255, 87], // 1 Green - [51, 87, 255], // 2 Blue - [255, 51, 246], // 3 Magenta - [51, 255, 246], // 4 Cyan - [243, 255, 51], // 5 Yellow - [141, 51, 255], // 6 Purple - [255, 131, 51], // 7 Orange - [51, 255, 131], // 8 Spring Green - [131, 51, 255], // 9 Violet - [255, 255, 51], // 10 Bright Yellow - [51, 255, 255], // 11 Aqua - [255, 51, 143], // 12 Deep Pink - [127, 51, 255], // 13 Dark Orchid - [51, 255, 175], // 14 Medium Spring Green - [255, 175, 51], // 15 Sandy Brown - [179, 255, 51], // 16 Chartreuse - [255, 87, 51], // 17 Red (darker shade) - [255, 51, 162], // 18 Hot Pink - [51, 162, 255], // 19 Sky Blue - [162, 51, 255], // 20 Amethyst -]; - -const MODELS: ModelOption[] = [ - { - label: 'DeepLab MobileNet', - value: semanticSegmentation.deeplab_v3_mobilenet_v3_large(), - }, - { - label: 'DeepLab ResNet50', - value: semanticSegmentation.deeplab_v3_resnet50(), - }, - { - label: 'DeepLab ResNet101', - value: semanticSegmentation.deeplab_v3_resnet101(), - }, - { - label: 'LRASPP MobileNet', - value: semanticSegmentation.lraspp_mobilenet_v3_large(), - }, - { - label: 'FCN ResNet50', - value: semanticSegmentation.fcn_resnet50(), - }, - { - label: 'FCN ResNet101', - value: semanticSegmentation.fcn_resnet101(), - }, - { - label: 'Selfie Segmentation', - value: semanticSegmentation.selfie_segmentation(), - }, -]; - -export default function SemanticSegmentationScreen() { - const { setGlobalGenerating } = useContext(GeneratingContext); - const [selectedModel, setSelectedModel] = useState( - semanticSegmentation.deeplab_v3_mobilenet_v3_large() - ); - - const { - isReady, - isGenerating, - downloadProgress, - forward, - error: modelError, - } = useSemanticSegmentation({ model: selectedModel }); - - const [imageUri, setImageUri] = useState(''); - const [imageSize, setImageSize] = useState({ width: 0, height: 0 }); - const [segImage, setSegImage] = useState(null); - const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 }); - const [inferenceTime, setInferenceTime] = useState(null); - const [error, setError] = useState(null); - - useEffect(() => { - setGlobalGenerating(isGenerating); - }, [isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (modelError) setError(String(modelError)); - }, [modelError]); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - if (!image?.uri) return; - setImageUri(image.uri); - setImageSize({ width: image.width ?? 0, height: image.height ?? 0 }); - setSegImage(null); - setInferenceTime(null); - }; - - const runForward = async () => { - if (!imageUri || imageSize.width === 0 || imageSize.height === 0) return; - try { - const start = Date.now(); - const { width, height } = imageSize; - const output = await forward(imageUri, [], true); - const argmax = output.ARGMAX || []; - const pixels = new Uint8Array(width * height * 4); - - for (let row = 0; row < height; row++) { - for (let col = 0; col < width; col++) { - const idx = row * width + col; - const color = numberToColor[argmax[idx]] || [0, 0, 0]; - pixels[idx * 4] = color[0]; - pixels[idx * 4 + 1] = color[1]; - pixels[idx * 4 + 2] = color[2]; - pixels[idx * 4 + 3] = 255; - } - } - - const data = Skia.Data.fromBytes(pixels); - const img = Skia.Image.MakeImage( - { - width, - height, - alphaType: AlphaType.Opaque, - colorType: ColorType.RGBA_8888, - }, - data, - width * 4 - ); - setSegImage(img); - setInferenceTime(Date.now() - start); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - if (!isReady && !modelError) { - return ( - - ); - } - - return ( - - setError(null)} /> - - - - {!imageUri && ( - - Semantic Segmentation - - This model assigns a class label to every pixel in an image, painting each region - with a distinct color. Pick an image from your gallery or take one with your camera - to get started. - - - )} - - {segImage && ( - - setCanvasSize({ - width: e.nativeEvent.layout.width, - height: e.nativeEvent.layout.height, - }) - } - > - - - - - )} - - { - setSelectedModel(m); - setSegImage(null); - }} - /> - - - - ); -} - -const styles = StyleSheet.create({ - imageCanvasContainer: { flex: 6, width: '100%', padding: 16 }, - imageContainer: { flex: 1, width: '100%' }, - image: { flex: 1, borderRadius: 8, width: '100%' }, - canvasContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - gap: 4, - padding: 4, - }, - canvas: { width: '100%', height: '100%' }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/style_transfer/index.tsx b/apps/legacy/computer-vision/app/style_transfer/index.tsx deleted file mode 100644 index 0cb6ab7979..0000000000 --- a/apps/legacy/computer-vision/app/style_transfer/index.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import Spinner from '../../components/Spinner'; -import { BottomBar } from '../../components/BottomBar'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { getImage } from '../../utils'; -import { - models, - useStyleTransfer, - StyleTransferModelName, - ResourceSource, -} from 'react-native-executorch/legacy'; - -import { View, StyleSheet, Image, Text } from 'react-native'; - -import React, { useContext, useEffect, useState } from 'react'; -import { GeneratingContext } from '../../context'; -import ScreenWrapper from '../../ScreenWrapper'; -import { StatsBar } from '../../components/StatsBar'; -const styleTransfer = models.style_transfer; - -type StyleTransferModelSources = { - modelName: StyleTransferModelName; - modelSource: ResourceSource; -}; - -const MODELS: ModelOption[] = [ - { label: 'Candy', value: styleTransfer.candy() }, - { label: 'Mosaic', value: styleTransfer.mosaic() }, - { - label: 'Rain Princess', - value: styleTransfer.rain_princess(), - }, - { label: 'Udnie', value: styleTransfer.udnie() }, -]; -import ErrorBanner from '../../components/ErrorBanner'; - -export default function StyleTransferScreen() { - const [selectedModel, setSelectedModel] = useState( - styleTransfer.candy() - ); - - const model = useStyleTransfer({ model: selectedModel }); - const { setGlobalGenerating } = useContext(GeneratingContext); - useEffect(() => { - setGlobalGenerating(model.isGenerating); - }, [model.isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const [imageUri, setImageUri] = useState(''); - const [styledUri, setStyledUri] = useState(''); - const [inferenceTime, setInferenceTime] = useState(null); - const [error, setError] = useState(null); - - const handleCameraPress = async (isCamera: boolean) => { - const image = await getImage(isCamera); - const uri = image?.uri; - if (typeof uri === 'string') { - setImageUri(uri); - setStyledUri(''); - setInferenceTime(null); - } - }; - - const runForward = async () => { - if (imageUri) { - try { - const start = Date.now(); - const uri = await model.forward(imageUri, 'url'); - setInferenceTime(Date.now() - start); - setStyledUri(uri); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - } - }; - - if (!model.isReady && !model.error) { - return ( - - ); - } - - return ( - - setError(null)} /> - - - {!imageUri && ( - - Style Transfer - - This model applies artistic styles to your images, transforming them to look like - famous paintings. Pick an image from your gallery or take one with your camera to get - started. - - - )} - - { - setSelectedModel(m); - setStyledUri(''); - }} - /> - - - - ); -} - -const styles = StyleSheet.create({ - imageContainer: { flex: 6, width: '100%', padding: 16 }, - image: { flex: 1, borderRadius: 8, width: '100%' }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/text_to_image/index.tsx b/apps/legacy/computer-vision/app/text_to_image/index.tsx deleted file mode 100644 index 1e40b62762..0000000000 --- a/apps/legacy/computer-vision/app/text_to_image/index.tsx +++ /dev/null @@ -1,317 +0,0 @@ -import { - View, - StyleSheet, - Text, - Image, - Keyboard, - TextInput, - TouchableOpacity, - TouchableWithoutFeedback, - KeyboardAvoidingView, - Platform, -} from 'react-native'; -import React, { useContext, useEffect, useState } from 'react'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import Spinner from '../../components/Spinner'; -import { models, useTextToImage, TextToImageProps } from 'react-native-executorch/legacy'; -import { ModelPicker, ModelOption } from '../../components/ModelPicker'; -import { GeneratingContext } from '../../context'; -import ColorPalette from '../../colors'; -import ProgressBar from '../../components/ProgressBar'; -import { Ionicons } from '@expo/vector-icons'; -import { StatsBar } from '../../components/StatsBar'; -import ErrorBanner from '../../components/ErrorBanner'; -const imageGeneration = models.image_generation; - -type TextToImageModelSources = TextToImageProps['model']; - -const MODELS: ModelOption[] = [ - { - label: 'BK-SDM 256', - value: imageGeneration.bk_sdm_tiny_vpred_256(), - }, - { - label: 'BK-SDM 512', - value: imageGeneration.bk_sdm_tiny_vpred_512(), - }, -]; - -export default function TextToImageScreen() { - const { bottom } = useSafeAreaInsets(); - const [inferenceStepIdx, setInferenceStepIdx] = useState(0); - const [image, setImage] = useState(null); - const [steps, setSteps] = useState(40); - - const [input, setInput] = useState(''); - const [selectedModel, setSelectedModel] = useState( - imageGeneration.bk_sdm_tiny_vpred_256() - ); - const [generationTime, setGenerationTime] = useState(null); - - const [keyboardVisible, setKeyboardVisible] = useState(false); - const [error, setError] = useState(null); - const [imageTitle, setImageTitle] = useState(null); - - const imageSize = 224; - const model = useTextToImage({ - model: selectedModel, - inferenceCallback: (x) => setInferenceStepIdx(x), - }); - - const { setGlobalGenerating } = useContext(GeneratingContext); - - useEffect(() => { - setGlobalGenerating(model.isGenerating); - }, [model.isGenerating, setGlobalGenerating]); - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - useEffect(() => { - const showSub = Keyboard.addListener('keyboardDidShow', () => { - setKeyboardVisible(true); - }); - const hideSub = Keyboard.addListener('keyboardDidHide', () => { - setKeyboardVisible(false); - }); - return () => { - showSub.remove(); - hideSub.remove(); - }; - }, []); - - const runForward = async () => { - if (!input.trim()) return; - - setImageTitle(input); - - try { - const start = Date.now(); - const output = await model.generate(input, imageSize, steps); - - if (output.length) { - setImage(output); - setGenerationTime(Date.now() - start); - } - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - setImageTitle(null); - } finally { - setInferenceStepIdx(0); - } - }; - - if (!model.isReady && !model.error) { - return ( - - ); - } - - return ( - { - Keyboard.dismiss(); - }} - > - - {keyboardVisible && } - - setError(null)} /> - - - {imageTitle && {imageTitle}} - - - - {model.isGenerating ? ( - - Generating... - - - ) : image?.length ? ( - - ) : ( - - Text to Image - - This model generates images from text descriptions using a diffusion process. Type a - prompt below and tap the send button to generate an image. - - - )} - - - { - setSelectedModel(m); - setImage(null); - setGenerationTime(null); - }} - /> - - - Steps: {steps} - setSteps((s) => Math.max(5, s - 5))} - > - − - - setSteps((s) => Math.min(50, s + 5))} - > - + - - - - - - - - {model.isGenerating ? ( - - - - ) : ( - - - - )} - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - width: '100%', - }, - overlay: { - ...StyleSheet.absoluteFill, - backgroundColor: 'rgba(0,0,0,0.1)', - zIndex: 1, - }, - titleContainer: { - paddingHorizontal: 16, - paddingTop: 8, - alignItems: 'center', - }, - titleText: { - fontSize: 18, - fontWeight: '600', - color: '#333', - textAlign: 'center', - }, - imageContainer: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - padding: 16, - zIndex: 0, - }, - image: { - width: 256, - height: 256, - }, - progressContainer: { - alignItems: 'center', - gap: 12, - }, - text: { - fontSize: 16, - color: ColorPalette.primary, - }, - stepsRow: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 16, - paddingVertical: 6, - gap: 8, - }, - stepsLabel: { - flex: 1, - fontSize: 14, - color: ColorPalette.primary, - }, - stepButton: { - width: 36, - height: 36, - borderRadius: 8, - backgroundColor: ColorPalette.primary, - alignItems: 'center', - justifyContent: 'center', - }, - stepButtonText: { - color: '#fff', - fontSize: 20, - lineHeight: 22, - }, - inputRow: { - flexDirection: 'row', - alignItems: 'center', - margin: 12, - gap: 8, - }, - textInput: { - flex: 1, - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 10, - fontSize: 14, - color: ColorPalette.primary, - }, - sendButton: { - width: 44, - height: 44, - borderRadius: 8, - backgroundColor: ColorPalette.primary, - alignItems: 'center', - justifyContent: 'center', - }, - sendButtonDisabled: { - backgroundColor: '#888', - }, - infoContainer: { - alignItems: 'center', - padding: 16, - gap: 8, - }, - infoTitle: { - fontSize: 18, - fontWeight: '600', - color: 'navy', - }, - infoText: { - fontSize: 14, - color: '#555', - textAlign: 'center', - lineHeight: 20, - }, -}); diff --git a/apps/legacy/computer-vision/app/vision_camera/index.tsx b/apps/legacy/computer-vision/app/vision_camera/index.tsx deleted file mode 100644 index 860bf6a297..0000000000 --- a/apps/legacy/computer-vision/app/vision_camera/index.tsx +++ /dev/null @@ -1,533 +0,0 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { ScrollView, StatusBar, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useIsFocused } from 'expo-router'; -import { - Camera, - useCameraDevices, - useCameraPermission, - useFrameOutput, -} from 'react-native-vision-camera'; -import { createSynchronizable } from 'react-native-worklets'; -import Svg, { Path, Polygon } from 'react-native-svg'; -import { useRouter } from 'expo-router'; -import { Ionicons } from '@expo/vector-icons'; -import { GeneratingContext } from '../../context'; -import Spinner from '../../components/Spinner'; -import ColorPalette from '../../colors'; -import ClassificationTask from '../../components/vision_camera/tasks/ClassificationTask'; -import ObjectDetectionTask from '../../components/vision_camera/tasks/ObjectDetectionTask'; -import SegmentationTask from '../../components/vision_camera/tasks/SegmentationTask'; -import InstanceSegmentationTask from '../../components/vision_camera/tasks/InstanceSegmentationTask'; -import OCRTask from '../../components/vision_camera/tasks/OCRTask'; -import StyleTransferTask from '../../components/vision_camera/tasks/StyleTransferTask'; -import PoseEstimationTask from '../../components/vision_camera/tasks/PoseEstimationTask'; -// 1. Import ErrorBanner -import ErrorBanner from '../../components/ErrorBanner'; - -type TaskId = - | 'classification' - | 'objectDetection' - | 'segmentation' - | 'instanceSegmentation' - | 'poseEstimation' - | 'ocr' - | 'styleTransfer'; -type ModelId = - | 'classification' - | 'objectDetectionSsdlite' - | 'objectDetectionRfdetr' - | 'objectDetectionYolo26n' - | 'segmentationDeeplabResnet50' - | 'segmentationDeeplabResnet101' - | 'segmentationDeeplabMobilenet' - | 'segmentationLraspp' - | 'segmentationFcnResnet50' - | 'segmentationFcnResnet101' - | 'segmentationSelfie' - | 'instanceSegmentationYolo26n' - | 'instanceSegmentationRfdetr' - | 'instanceSegmentationFastsamS' - | 'instanceSegmentationFastsamX' - | 'poseEstimationYolo26n' - | 'ocr' - | 'styleTransferCandy' - | 'styleTransferMosaic'; - -type TaskVariant = { id: ModelId; label: string }; -type Task = { id: TaskId; label: string; variants: TaskVariant[] }; - -const TASKS: Task[] = [ - { - id: 'classification', - label: 'Classify', - variants: [{ id: 'classification', label: 'EfficientNet V2 S' }], - }, - { - id: 'segmentation', - label: 'Segment', - variants: [ - { id: 'segmentationDeeplabResnet50', label: 'DeepLab ResNet50' }, - { id: 'segmentationDeeplabResnet101', label: 'DeepLab ResNet101' }, - { id: 'segmentationDeeplabMobilenet', label: 'DeepLab MobileNet' }, - { id: 'segmentationLraspp', label: 'LRASPP MobileNet' }, - { id: 'segmentationFcnResnet50', label: 'FCN ResNet50' }, - { id: 'segmentationFcnResnet101', label: 'FCN ResNet101' }, - { id: 'segmentationSelfie', label: 'Selfie' }, - ], - }, - { - id: 'instanceSegmentation', - label: 'Inst Seg', - variants: [ - { id: 'instanceSegmentationYolo26n', label: 'YOLO26N Seg' }, - { id: 'instanceSegmentationRfdetr', label: 'RF-DETR Nano Seg' }, - { id: 'instanceSegmentationFastsamS', label: 'FastSAM-S' }, - { id: 'instanceSegmentationFastsamX', label: 'FastSAM-X' }, - ], - }, - { - id: 'poseEstimation', - label: 'Pose', - variants: [{ id: 'poseEstimationYolo26n', label: 'YOLO26N Pose' }], - }, - { - id: 'objectDetection', - label: 'Detect', - variants: [ - { id: 'objectDetectionSsdlite', label: 'SSDLite MobileNet' }, - { id: 'objectDetectionRfdetr', label: 'RF-DETR Nano' }, - { id: 'objectDetectionYolo26n', label: 'YOLO26N' }, - ], - }, - { - id: 'ocr', - label: 'OCR', - variants: [{ id: 'ocr', label: 'English' }], - }, - { - id: 'styleTransfer', - label: 'Style', - variants: [ - { id: 'styleTransferCandy', label: 'Candy' }, - { id: 'styleTransferMosaic', label: 'Mosaic' }, - ], - }, -]; - -export default function VisionCameraScreen() { - const insets = useSafeAreaInsets(); - const router = useRouter(); - const [frameKillSwitch] = useState(() => createSynchronizable(false)); - const [cameraPositionSync] = useState(() => createSynchronizable<'front' | 'back'>('back')); - const [activeTask, setActiveTask] = useState('classification'); - const [activeModel, setActiveModel] = useState('classification'); - const [canvasSize, setCanvasSize] = useState({ width: 1, height: 1 }); - const [cameraPosition, setCameraPosition] = useState<'front' | 'back'>('back'); - const [fps, setFps] = useState(0); - const [frameMs, setFrameMs] = useState(0); - const [isReady, setIsReady] = useState(false); - const [downloadProgress, setDownloadProgress] = useState(0); - const [frameOutput, setFrameOutput] = useState | null>(null); - - const [error, setError] = useState(null); - - const { setGlobalGenerating } = useContext(GeneratingContext); - - const isFocused = useIsFocused(); - const cameraPermission = useCameraPermission(); - const devices = useCameraDevices(); - const device = devices.find((d) => d.position === cameraPosition) ?? devices[0]; - useEffect(() => { - frameKillSwitch.setBlocking(true); - setError(null); - const id = setTimeout(() => { - frameKillSwitch.setBlocking(false); - }, 300); - return () => clearTimeout(id); - }, [activeModel, frameKillSwitch]); - - useEffect(() => { - cameraPositionSync.setBlocking(cameraPosition); - }, [cameraPosition, cameraPositionSync]); - - const handleFpsChange = useCallback((newFps: number, newMs: number) => { - setFps(newFps); - setFrameMs(newMs); - }, []); - - const handleGeneratingChange = useCallback( - (generating: boolean) => { - setGlobalGenerating(generating); - }, - [setGlobalGenerating] - ); - - const handleErrorChange = useCallback((errorMessage: string | null) => { - setError(errorMessage); - }, []); - - if (!cameraPermission.hasPermission) { - return ( - - Camera access needed - cameraPermission.requestPermission()} - style={styles.button} - > - Grant Permission - - - ); - } - - if (device == null) { - return ( - - No camera device found - - ); - } - - const activeTaskInfo = TASKS.find((t) => t.id === activeTask)!; - const activeVariantLabel = - activeTaskInfo.variants.find((v) => v.id === activeModel)?.label ?? - activeTaskInfo.variants[0]!.label; - - const taskProps = { - activeModel, - canvasSize, - cameraPositionSync, - frameKillSwitch, - onFrameOutputChange: setFrameOutput, - onReadyChange: setIsReady, - onProgressChange: setDownloadProgress, - onGeneratingChange: handleGeneratingChange, - onFpsChange: handleFpsChange, - onErrorChange: handleErrorChange, - }; - - return ( - - - - - setError(null)} /> - - - { - console.warn('[Camera] onError', e); - setError(e.message); - }} - onStarted={() => console.log('[Camera] session started')} - onPreviewStarted={() => console.log('[Camera] preview got first frame')} - /> - - - setCanvasSize({ - width: e.nativeEvent.layout.width, - height: e.nativeEvent.layout.height, - }) - } - /> - - {activeTask === 'classification' && } - {activeTask === 'objectDetection' && ( - - )} - {activeTask === 'segmentation' && ( - - )} - {activeTask === 'instanceSegmentation' && ( - - )} - {activeTask === 'poseEstimation' && ( - - )} - {activeTask === 'ocr' && } - {activeTask === 'styleTransfer' && ( - - )} - - {!isReady && !error && ( - - - - )} - - - router.navigate('/')} - > - - - - - {activeVariantLabel} - - {fps} FPS – {frameMs.toFixed(0)} ms - - - - - {TASKS.map((t) => ( - { - setActiveTask(t.id); - setActiveModel(t.variants[0]!.id); - }} - > - - {t.label} - - - ))} - - - - {activeTaskInfo.variants.map((v) => ( - setActiveModel(v.id)} - > - - {v.label} - - - ))} - - - - - setCameraPosition((p) => (p === 'back' ? 'front' : 'back'))} - > - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: 'black' }, - errorOverlay: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - zIndex: 100, - }, - centered: { - flex: 1, - backgroundColor: 'black', - justifyContent: 'center', - alignItems: 'center', - gap: 16, - }, - message: { color: 'white', fontSize: 18 }, - button: { - paddingHorizontal: 24, - paddingVertical: 12, - backgroundColor: ColorPalette.primary, - borderRadius: 24, - }, - buttonText: { color: 'white', fontSize: 15, fontWeight: '600' }, - loadingOverlay: { - ...StyleSheet.absoluteFill, - backgroundColor: 'rgba(0,0,0,0.6)', - justifyContent: 'center', - alignItems: 'center', - zIndex: 10, - }, - topOverlay: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - alignItems: 'center', - gap: 8, - zIndex: 5, - }, - titleRow: { - alignItems: 'center', - paddingHorizontal: 16, - }, - modelTitle: { - color: 'white', - fontSize: 22, - fontWeight: '700', - textShadowColor: 'rgba(0,0,0,0.7)', - textShadowOffset: { width: 0, height: 1 }, - textShadowRadius: 4, - }, - fpsText: { - color: 'rgba(255,255,255,0.85)', - fontSize: 14, - fontWeight: '500', - marginTop: 2, - textShadowColor: 'rgba(0,0,0,0.7)', - textShadowOffset: { width: 0, height: 1 }, - textShadowRadius: 4, - }, - tabsContent: { - paddingHorizontal: 12, - gap: 6, - }, - tab: { - paddingHorizontal: 18, - paddingVertical: 7, - borderRadius: 20, - backgroundColor: 'rgba(0,0,0,0.45)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.25)', - }, - tabActive: { - backgroundColor: 'rgba(255,255,255,0.2)', - borderColor: 'white', - }, - tabText: { - color: 'rgba(255,255,255,0.7)', - fontSize: 14, - fontWeight: '600', - }, - tabTextActive: { color: 'white' }, - chipsContent: { - paddingHorizontal: 12, - gap: 6, - }, - variantChip: { - paddingHorizontal: 14, - paddingVertical: 5, - borderRadius: 16, - backgroundColor: 'rgba(0,0,0,0.35)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.15)', - }, - variantChipActive: { - backgroundColor: ColorPalette.primary, - borderColor: ColorPalette.primary, - }, - variantChipText: { - color: 'rgba(255,255,255,0.6)', - fontSize: 12, - fontWeight: '500', - }, - variantChipTextActive: { color: 'white' }, - bottomOverlay: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - alignItems: 'center', - zIndex: 5, - }, - flipButton: { - width: 56, - height: 56, - borderRadius: 28, - backgroundColor: 'rgba(255,255,255,0.2)', - justifyContent: 'center', - alignItems: 'center', - borderWidth: 1.5, - borderColor: 'rgba(255,255,255,0.4)', - }, - backButton: { - position: 'absolute', - left: 12, - width: 40, - height: 40, - borderRadius: 20, - backgroundColor: 'rgba(0,0,0,0.45)', - justifyContent: 'center', - alignItems: 'center', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.25)', - zIndex: 10, - }, -}); diff --git a/apps/legacy/computer-vision/assets/fonts/Aeonik-Medium.otf b/apps/legacy/computer-vision/assets/fonts/Aeonik-Medium.otf deleted file mode 100644 index cd9981f439..0000000000 Binary files a/apps/legacy/computer-vision/assets/fonts/Aeonik-Medium.otf and /dev/null differ diff --git a/apps/legacy/computer-vision/assets/fonts/Aeonik-Regular.otf b/apps/legacy/computer-vision/assets/fonts/Aeonik-Regular.otf deleted file mode 100644 index 9bd378ed2b..0000000000 Binary files a/apps/legacy/computer-vision/assets/fonts/Aeonik-Regular.otf and /dev/null differ diff --git a/apps/legacy/computer-vision/assets/icons/adaptive-icon.png b/apps/legacy/computer-vision/assets/icons/adaptive-icon.png deleted file mode 100644 index 03d6f6b6c6..0000000000 Binary files a/apps/legacy/computer-vision/assets/icons/adaptive-icon.png and /dev/null differ diff --git a/apps/legacy/computer-vision/assets/icons/executorch.svg b/apps/legacy/computer-vision/assets/icons/executorch.svg deleted file mode 100644 index e548ea4201..0000000000 --- a/apps/legacy/computer-vision/assets/icons/executorch.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/legacy/computer-vision/assets/icons/executorch_logo.png b/apps/legacy/computer-vision/assets/icons/executorch_logo.png deleted file mode 100644 index 73bdec1b7c..0000000000 Binary files a/apps/legacy/computer-vision/assets/icons/executorch_logo.png and /dev/null differ diff --git a/apps/legacy/computer-vision/assets/icons/favicon.png b/apps/legacy/computer-vision/assets/icons/favicon.png deleted file mode 100644 index e75f697b18..0000000000 Binary files a/apps/legacy/computer-vision/assets/icons/favicon.png and /dev/null differ diff --git a/apps/legacy/computer-vision/assets/icons/icon.png b/apps/legacy/computer-vision/assets/icons/icon.png deleted file mode 100644 index a0b1526fc7..0000000000 Binary files a/apps/legacy/computer-vision/assets/icons/icon.png and /dev/null differ diff --git a/apps/legacy/computer-vision/assets/icons/splash.png b/apps/legacy/computer-vision/assets/icons/splash.png deleted file mode 100644 index 0e89705a94..0000000000 Binary files a/apps/legacy/computer-vision/assets/icons/splash.png and /dev/null differ diff --git a/apps/legacy/computer-vision/assets/icons/swm_icon.svg b/apps/legacy/computer-vision/assets/icons/swm_icon.svg deleted file mode 100644 index 8c62f039be..0000000000 --- a/apps/legacy/computer-vision/assets/icons/swm_icon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/apps/legacy/computer-vision/babel.config.js b/apps/legacy/computer-vision/babel.config.js deleted file mode 100644 index 6b2006979c..0000000000 --- a/apps/legacy/computer-vision/babel.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - plugins: ['react-native-worklets/plugin'], - }; -}; diff --git a/apps/legacy/computer-vision/colors.ts b/apps/legacy/computer-vision/colors.ts deleted file mode 100644 index feb75ac336..0000000000 --- a/apps/legacy/computer-vision/colors.ts +++ /dev/null @@ -1,6 +0,0 @@ -const ColorPalette = { - primary: '#001A72', - strongPrimary: '#020F3C', -}; - -export default ColorPalette; diff --git a/apps/legacy/computer-vision/components/BottomBar.tsx b/apps/legacy/computer-vision/components/BottomBar.tsx deleted file mode 100644 index 4edad755d2..0000000000 --- a/apps/legacy/computer-vision/components/BottomBar.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import ColorPalette from '../colors'; -import FontAwesome from '@expo/vector-icons/FontAwesome'; -import { View, TouchableOpacity, StyleSheet, Text } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import DeviceInfo from 'react-native-device-info'; - -const isDevice = !DeviceInfo.isEmulatorSync(); - -export const BottomBar = ({ - handleCameraPress, - runForward, - hasImage = true, - isGenerating = false, -}: { - handleCameraPress: (isCamera: boolean) => void; - runForward: () => void; - hasImage?: boolean; - isGenerating?: boolean; -}) => { - const { bottom } = useSafeAreaInsets(); - const disabled = !hasImage || isGenerating; - return ( - - - handleCameraPress(false)}> - - - isDevice && handleCameraPress(true)}> - - - - - - {isGenerating ? 'Running...' : hasImage ? 'Run model' : 'Pick an image to run the model'} - - - - ); -}; - -const styles = StyleSheet.create({ - bottomContainer: { - width: '100%', - gap: 15, - alignItems: 'center', - paddingTop: 16, - paddingHorizontal: 16, - }, - bottomIconsContainer: { - flexDirection: 'row', - justifyContent: 'space-evenly', - width: '100%', - }, - button: { - width: '100%', - height: 50, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: ColorPalette.primary, - color: '#fff', - borderRadius: 8, - }, - buttonDisabled: { - backgroundColor: '#888', - }, - buttonText: { - color: '#fff', - fontSize: 16, - }, -}); diff --git a/apps/legacy/computer-vision/components/BottomBarWithTextInput.tsx b/apps/legacy/computer-vision/components/BottomBarWithTextInput.tsx deleted file mode 100644 index 045ea06c16..0000000000 --- a/apps/legacy/computer-vision/components/BottomBarWithTextInput.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React, { useState } from 'react'; -import { - View, - Text, - TextInput, - TouchableOpacity, - StyleSheet, - KeyboardAvoidingView, - Platform, -} from 'react-native'; -import { Ionicons } from '@expo/vector-icons'; -import ColorPalette from '../colors'; - -interface BottomBarProps { - runModel: (input: string, numSteps: number) => void; - stopModel: () => void; - numSteps: number; - setSteps: React.Dispatch>; - isGenerating?: boolean; - isReady?: boolean; - showTextInput: boolean; - setShowTextInput: React.Dispatch>; - keyboardVisible: boolean; -} - -export const BottomBarWithTextInput = ({ - runModel, - stopModel, - numSteps, - setSteps, - isGenerating, - isReady, - showTextInput, - setShowTextInput, - keyboardVisible, -}: BottomBarProps) => { - const [input, setInput] = useState(''); - - const decreaseSteps = () => setSteps((prev) => Math.max(5, prev - 5)); - const increaseSteps = () => setSteps((prev) => Math.min(50, prev + 5)); - - if (!showTextInput) { - if (isGenerating) { - return ( - - Stop model - - ); - } else { - return ( - setShowTextInput(true)} - disabled={!isReady} - > - Run model - - ); - } - } - - return ( - - - - { - setShowTextInput(false); - setInput(''); - runModel(input, numSteps); - }} - disabled={!isReady || isGenerating} - > - - - - - - Steps: {numSteps} - - - - - - - + - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - alignItems: 'center', - }, - inputContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - input: { - flex: 1, - borderRadius: 6, - padding: 8, - marginRight: 8, - backgroundColor: '#fff', - color: '#000', - }, - stepsContainer: { - width: '100%', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - marginTop: 10, - }, - stepsButtons: { - flexDirection: 'row', - }, - button: { - width: '100%', - height: 40, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: ColorPalette.primary, - borderRadius: 8, - }, - buttonText: { - color: '#fff', - fontSize: 16, - textAlign: 'center', - }, - iconButton: { - marginHorizontal: 5, - width: 40, - }, - text: { - flex: 1, - fontSize: 16, - color: '#000', - }, - textWhite: { - color: '#fff', - }, -}); diff --git a/apps/legacy/computer-vision/components/BoundingBoxes.tsx b/apps/legacy/computer-vision/components/BoundingBoxes.tsx deleted file mode 100644 index 2fef444ce4..0000000000 --- a/apps/legacy/computer-vision/components/BoundingBoxes.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; -import { Detection, LabelEnum } from 'react-native-executorch/legacy'; -import { labelColor, labelColorBg } from './utils/colors'; - -interface Props { - detections: Detection[]; - scaleX: number; - scaleY: number; - offsetX: number; - offsetY: number; - mirrorLabels?: boolean; - containerWidth?: number; -} - -export default function BoundingBoxes({ - detections, - scaleX, - scaleY, - offsetX, - offsetY, - mirrorLabels = false, - containerWidth, -}: Props) { - return ( - <> - {detections.map((det, i) => { - const left = det.bbox.x1 * scaleX + offsetX; - const top = det.bbox.y1 * scaleY + offsetY; - const width = (det.bbox.x2 - det.bbox.x1) * scaleX; - const height = (det.bbox.y2 - det.bbox.y1) * scaleY; - const labelTop = top < 26 ? top + height + 2 : top - 26; - - return ( - - - - - {String(det.label)} ({(det.score * 100).toFixed(1)}%) - - - - ); - })} - - ); -} - -const styles = StyleSheet.create({ - bbox: { - position: 'absolute', - borderWidth: 2, - borderRadius: 4, - }, - label: { - position: 'absolute', - paddingHorizontal: 6, - paddingVertical: 2, - borderRadius: 4, - }, - labelText: { - color: 'white', - fontSize: 11, - fontWeight: '600', - }, -}); diff --git a/apps/legacy/computer-vision/components/ErrorBanner.tsx b/apps/legacy/computer-vision/components/ErrorBanner.tsx deleted file mode 100644 index a5bebc504f..0000000000 --- a/apps/legacy/computer-vision/components/ErrorBanner.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; - -interface ErrorBannerProps { - message: string | null; - onDismiss: () => void; -} - -export default function ErrorBanner({ message, onDismiss }: ErrorBannerProps) { - if (!message) return null; - - return ( - - - {message} - - - ✕ - - - ); -} - -const styles = StyleSheet.create({ - container: { - backgroundColor: '#FEE2E2', - borderLeftWidth: 4, - borderLeftColor: '#EF4444', - borderRadius: 8, - marginHorizontal: 16, - marginVertical: 8, - paddingVertical: 10, - paddingLeft: 12, - paddingRight: 8, - flexDirection: 'row', - alignItems: 'center', - }, - message: { - flex: 1, - color: '#991B1B', - fontSize: 14, - lineHeight: 20, - }, - closeButton: { - padding: 4, - marginLeft: 8, - }, - closeText: { - color: '#991B1B', - fontSize: 16, - fontWeight: '600', - }, -}); diff --git a/apps/legacy/computer-vision/components/ImageWithBboxes.tsx b/apps/legacy/computer-vision/components/ImageWithBboxes.tsx deleted file mode 100644 index 82b31ec515..0000000000 --- a/apps/legacy/computer-vision/components/ImageWithBboxes.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { Image, StyleSheet, View } from 'react-native'; -import { Detection } from 'react-native-executorch/legacy'; -import BoundingBoxes from './BoundingBoxes'; - -interface Props { - imageUri: string; - detections: Detection[]; - imageWidth: number; - imageHeight: number; -} - -export default function ImageWithBboxes({ imageUri, detections, imageWidth, imageHeight }: Props) { - const [layout, setLayout] = React.useState({ width: 0, height: 0 }); - - const calculateAdjustedDimensions = () => { - const imageRatio = imageWidth / imageHeight; - const layoutRatio = layout.width / layout.height; - - let sx, sy; - if (imageRatio > layoutRatio) { - // image is more "wide" - sx = layout.width / imageWidth; - sy = layout.width / imageRatio / imageHeight; - } else { - // image is more "tall" - sy = layout.height / imageHeight; - sx = (layout.height * imageRatio) / imageWidth; - } - - return { - scaleX: sx, - scaleY: sy, - offsetX: (layout.width - imageWidth * sx) / 2, - offsetY: (layout.height - imageHeight * sy) / 2, - }; - }; - - const { scaleX, scaleY, offsetX, offsetY } = calculateAdjustedDimensions(); - - return ( - { - const { width, height } = event.nativeEvent.layout; - setLayout({ width, height }); - }} - > - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - position: 'relative', - }, - image: { - flex: 1, - width: '100%', - height: '100%', - }, -}); diff --git a/apps/legacy/computer-vision/components/ImageWithMasks.tsx b/apps/legacy/computer-vision/components/ImageWithMasks.tsx deleted file mode 100644 index 9bd542566a..0000000000 --- a/apps/legacy/computer-vision/components/ImageWithMasks.tsx +++ /dev/null @@ -1,244 +0,0 @@ -import React, { useState } from 'react'; -import { Image, StyleSheet, View, Text } from 'react-native'; -import { - Canvas, - Image as SkiaImage, - Skia, - AlphaType, - ColorType, - SkImage, - Rect, - Group, -} from '@shopify/react-native-skia'; - -const INSTANCE_COLORS = [ - [255, 87, 51, 180], - [51, 255, 87, 180], - [51, 87, 255, 180], - [255, 51, 246, 180], - [51, 255, 246, 180], - [243, 255, 51, 180], - [141, 51, 255, 180], - [255, 131, 51, 180], - [51, 255, 131, 180], - [131, 51, 255, 180], -]; - -const MAX_MASK_DIM = 256; - -/** Display-only data — no raw mask buffers. */ -export interface DisplayInstance { - bbox: { x1: number; y1: number; x2: number; y2: number }; - label: string; - score: number; - maskImage: SkImage; -} - -/** - * Convert raw segmentation output into lightweight display instances. - * Call this eagerly (in the forward callback) so raw Uint8Array masks - * can be garbage-collected immediately. - * @param rawInstances - Array of raw segmentation instances with mask buffers to convert. - * @returns Array of lightweight {@link DisplayInstance} objects with pre-rendered Skia images. - */ -export function buildDisplayInstances( - rawInstances: { - bbox: { x1: number; y1: number; x2: number; y2: number }; - mask: Uint8Array; - maskWidth: number; - maskHeight: number; - label: string | number; - score: number; - }[] -): DisplayInstance[] { - return rawInstances - .map((inst, i) => { - const color = INSTANCE_COLORS[i % INSTANCE_COLORS.length]; - const img = createMaskImage(inst.mask, inst.maskWidth, inst.maskHeight, color); - if (!img) return null; - return { - bbox: inst.bbox, - label: String(inst.label), - score: inst.score, - maskImage: img, - }; - }) - .filter((d): d is DisplayInstance => d !== null); -} - -function createMaskImage( - mask: Uint8Array, - srcW: number, - srcH: number, - color: number[] -): SkImage | null { - const downscale = Math.min(1, MAX_MASK_DIM / Math.max(srcW, srcH)); - const dstW = Math.max(1, Math.round(srcW * downscale)); - const dstH = Math.max(1, Math.round(srcH * downscale)); - - const pixels = new Uint8Array(dstW * dstH * 4); - const r = color[0], - g = color[1], - b = color[2], - a = color[3]; - - for (let dy = 0; dy < dstH; dy++) { - const sy = Math.min(Math.floor(dy / downscale), srcH - 1); - for (let dx = 0; dx < dstW; dx++) { - const sx = Math.min(Math.floor(dx / downscale), srcW - 1); - if (mask[sy * srcW + sx] > 0) { - const idx = (dy * dstW + dx) * 4; - pixels[idx] = r; - pixels[idx + 1] = g; - pixels[idx + 2] = b; - pixels[idx + 3] = a; - } - } - } - - const data = Skia.Data.fromBytes(pixels); - const image = Skia.Image.MakeImage( - { - width: dstW, - height: dstH, - alphaType: AlphaType.Premul, - colorType: ColorType.RGBA_8888, - }, - data, - dstW * 4 - ); - data.dispose(); - return image; -} - -interface Props { - imageUri: string; - instances: DisplayInstance[]; - imageWidth: number; - imageHeight: number; -} - -export default function ImageWithMasks({ imageUri, instances, imageWidth, imageHeight }: Props) { - const [layout, setLayout] = useState({ width: 0, height: 0 }); - - const scaleX = layout.width / (imageWidth || 1); - const scaleY = layout.height / (imageHeight || 1); - const scale = Math.min(scaleX, scaleY); - const offsetX = (layout.width - imageWidth * scale) / 2; - const offsetY = (layout.height - imageHeight * scale) / 2; - - return ( - { - const { width, height } = e.nativeEvent.layout; - setLayout({ width, height }); - }} - > - - - {instances.length > 0 && ( - - - {instances.map((inst, idx) => { - const mx = inst.bbox.x1 * scale + offsetX; - const my = inst.bbox.y1 * scale + offsetY; - const mw = (inst.bbox.x2 - inst.bbox.x1) * scale; - const mh = (inst.bbox.y2 - inst.bbox.y1) * scale; - return ( - - ); - })} - - {instances.map((inst, idx) => { - const color = INSTANCE_COLORS[idx % INSTANCE_COLORS.length]; - const bboxX = inst.bbox.x1 * scale + offsetX; - const bboxY = inst.bbox.y1 * scale + offsetY; - const bboxW = (inst.bbox.x2 - inst.bbox.x1) * scale; - const bboxH = (inst.bbox.y2 - inst.bbox.y1) * scale; - - return ( - - - - ); - })} - - - {instances.map((inst, idx) => { - const color = INSTANCE_COLORS[idx % INSTANCE_COLORS.length]; - const bboxX = inst.bbox.x1 * scale + offsetX; - const bboxY = inst.bbox.y1 * scale + offsetY; - - return ( - - - {inst.label || 'Unknown'} {(inst.score * 100).toFixed(0)}% - - - ); - })} - - )} - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - position: 'relative', - }, - image: { - width: '100%', - height: '100%', - }, - overlay: { - ...StyleSheet.absoluteFill, - }, - canvas: { - width: '100%', - height: '100%', - }, - labelContainer: { - position: 'absolute', - paddingHorizontal: 6, - paddingVertical: 2, - borderRadius: 4, - }, - labelText: { - color: 'white', - fontSize: 12, - fontWeight: '600', - }, -}); diff --git a/apps/legacy/computer-vision/components/ImageWithOCRBboxes.tsx b/apps/legacy/computer-vision/components/ImageWithOCRBboxes.tsx deleted file mode 100644 index 07175b8596..0000000000 --- a/apps/legacy/computer-vision/components/ImageWithOCRBboxes.tsx +++ /dev/null @@ -1,93 +0,0 @@ -// Import necessary components -import React from 'react'; -import { Image, StyleSheet, View } from 'react-native'; -import Svg, { Polygon } from 'react-native-svg'; -import { OCRDetection } from 'react-native-executorch/legacy'; - -interface Props { - imageUri: string; - detections: OCRDetection[]; - imageWidth: number; - imageHeight: number; -} - -export default function ImageWithOCRBboxes({ - imageUri, - detections, - imageWidth, - imageHeight, -}: Props) { - const [layout, setLayout] = React.useState({ width: 0, height: 0 }); - - const calculateAdjustedDimensions = () => { - const imageRatio = imageWidth / imageHeight; - const layoutRatio = layout.width / layout.height; - let sx, sy; - if (imageRatio > layoutRatio) { - sx = layout.width / imageWidth; - sy = layout.width / imageRatio / imageHeight; - } else { - sy = layout.height / imageHeight; - sx = (layout.height * imageRatio) / imageWidth; - } - return { - scaleX: sx, - scaleY: sy, - offsetX: (layout.width - imageWidth * sx) / 2, - offsetY: (layout.height - imageHeight * sy) / 2, - }; - }; - - return ( - { - const { width, height } = event.nativeEvent.layout; - setLayout({ width, height }); - }} - > - - - {detections.map((detection, index) => { - const { scaleX, scaleY, offsetX, offsetY } = calculateAdjustedDimensions(); - const { x1, y1, x2, y2 } = detection.bbox; - const pointsString = [ - [x1, y1], - [x2, y1], - [x2, y2], - [x1, y2], - ] - .map(([x, y]) => `${x * scaleX + offsetX},${y * scaleY + offsetY}`) - .join(' '); - - return ( - - ); - })} - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - position: 'relative', - }, - image: { - flex: 1, - width: '100%', - height: '100%', - }, - svgContainer: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, -}); diff --git a/apps/legacy/computer-vision/components/ModelPicker.tsx b/apps/legacy/computer-vision/components/ModelPicker.tsx deleted file mode 100644 index 9a81bec371..0000000000 --- a/apps/legacy/computer-vision/components/ModelPicker.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Dimensions, - Modal, - ScrollView, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; - -export type ModelOption = { - label: string; - value: T; -}; - -type Props = { - models: ModelOption[]; - selectedModel: T; - onSelect: (model: T) => void; - label?: string; - disabled?: boolean; -}; - -const DROPDOWN_MAX_HEIGHT = 200; - -export function ModelPicker({ models, selectedModel, onSelect, label, disabled }: Props) { - const [open, setOpen] = useState(false); - const [triggerHeight, setTriggerHeight] = useState(0); - const [expandUp, setExpandUp] = useState(false); - const [dropdownTop, setDropdownTop] = useState(0); - const triggerRef = useRef>(null); - const selected = models.find((m) => m.value === selectedModel); - - useEffect(() => { - if (disabled) setOpen(false); - }, [disabled]); - - const handlePress = () => { - if (disabled) return; - if (open) { - setOpen(false); - return; - } - triggerRef.current?.measure( - (_x: number, _y: number, _width: number, height: number, _pageX: number, pageY: number) => { - setTriggerHeight(height); - const spaceBelow = Dimensions.get('window').height - (pageY + height); - setExpandUp(spaceBelow < DROPDOWN_MAX_HEIGHT); - setDropdownTop(pageY); - setOpen(true); - } - ); - }; - - const dropdownStylePosition = expandUp - ? { - bottom: Dimensions.get('window').height - dropdownTop, - left: 12, - right: 12, - } - : { - top: dropdownTop + triggerHeight + 2, - left: 12, - right: 12, - }; - - return ( - <> - - - {label && {label}} - {selected?.label ?? '—'} - {open ? '▲' : '▼'} - - - - {open && ( - setOpen(false)} - animationType="none" - > - setOpen(false)} - /> - - - {models.map((item) => { - const isSelected = item.value === selectedModel; - return ( - { - onSelect(item.value); - setOpen(false); - }} - activeOpacity={0.7} - > - - {item.label} - - - ); - })} - - - - )} - - ); -} - -const styles = StyleSheet.create({ - container: { - marginHorizontal: 12, - marginVertical: 4, - alignSelf: 'stretch', - zIndex: 100, - }, - trigger: { - flexDirection: 'row', - alignItems: 'center', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 10, - backgroundColor: '#f5f5f5', - }, - triggerDisabled: { - opacity: 0.4, - }, - label: { - fontSize: 12, - color: '#888', - marginRight: 6, - }, - triggerText: { - flex: 1, - fontSize: 14, - color: '#001A72', - fontWeight: '500', - }, - chevron: { - fontSize: 10, - color: '#888', - marginLeft: 6, - }, - modalBackdrop: { - flex: 1, - backgroundColor: 'rgba(0, 0, 0, 0.3)', - }, - dropdown: { - position: 'absolute', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - backgroundColor: '#fff', - maxHeight: DROPDOWN_MAX_HEIGHT, - zIndex: 1000, - elevation: 5, - shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.15, - shadowRadius: 6, - }, - option: { - paddingHorizontal: 12, - paddingVertical: 10, - borderBottomWidth: 1, - borderBottomColor: '#f0f0f0', - }, - optionSelected: { - backgroundColor: '#e8ecf8', - }, - optionText: { - fontSize: 14, - color: '#333', - }, - optionTextSelected: { - color: '#001A72', - fontWeight: '600', - }, -}); diff --git a/apps/legacy/computer-vision/components/ProgressBar.tsx b/apps/legacy/computer-vision/components/ProgressBar.tsx deleted file mode 100644 index 304f964b4c..0000000000 --- a/apps/legacy/computer-vision/components/ProgressBar.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { View, StyleSheet } from 'react-native'; -import ColorPalette from '../colors'; - -type ProgressBarProps = { - numSteps: number; - currentStep: number; -}; - -export default function ProgressBar({ numSteps, currentStep }: ProgressBarProps) { - return ( - - {Array.from({ length: numSteps }).map((_, i) => ( - - ))} - - ); -} - -const styles = StyleSheet.create({ - progressBarContainer: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - marginVertical: 16, - width: '80%', - }, - progressStep: { - flex: 1, - height: 15, - }, - progressStepActive: { - backgroundColor: ColorPalette.primary, - }, - progressStepInactive: { - backgroundColor: '#e0e0e0', - }, - progressStepFirst: { - borderTopLeftRadius: 8, - borderBottomLeftRadius: 8, - }, - progressStepLast: { - borderTopRightRadius: 8, - borderBottomRightRadius: 8, - }, -}); diff --git a/apps/legacy/computer-vision/components/Spinner.tsx b/apps/legacy/computer-vision/components/Spinner.tsx deleted file mode 100644 index f436869ab8..0000000000 --- a/apps/legacy/computer-vision/components/Spinner.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import { View, Text, StyleSheet, ActivityIndicator, Modal } from 'react-native'; -import ColorPalette from '../colors'; - -interface SpinnerProps { - visible: boolean; - textContent: string; -} - -const Spinner = ({ visible, textContent }: SpinnerProps) => { - return ( - - - - - {textContent} - - - - ); -}; - -const styles = StyleSheet.create({ - overlay: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'rgba(0, 0, 0, 0.1)', - }, - container: { - padding: 25, - alignItems: 'center', - justifyContent: 'center', - }, - text: { - marginTop: 15, - color: ColorPalette.primary, - fontSize: 18, - fontWeight: 'bold', - }, -}); - -export default Spinner; diff --git a/apps/legacy/computer-vision/components/StatsBar.tsx b/apps/legacy/computer-vision/components/StatsBar.tsx deleted file mode 100644 index 621ecd21d1..0000000000 --- a/apps/legacy/computer-vision/components/StatsBar.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { View, Text, StyleSheet } from 'react-native'; - -interface Props { - inferenceTime: number | null; - detectionCount?: number | null; -} - -export function StatsBar({ inferenceTime, detectionCount }: Props) { - if (inferenceTime === null) return null; - - return ( - - Inference: {inferenceTime} ms - {detectionCount != null && ( - <> - · - - {detectionCount} detection{detectionCount !== 1 ? 's' : ''} - - - )} - - ); -} - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - gap: 8, - paddingVertical: 6, - }, - stat: { - fontSize: 13, - color: '#334155', - fontWeight: '500', - }, - separator: { - fontSize: 13, - color: '#94A3B8', - }, -}); diff --git a/apps/legacy/computer-vision/components/utils/cocoSkeleton.ts b/apps/legacy/computer-vision/components/utils/cocoSkeleton.ts deleted file mode 100644 index 9f1e051ab3..0000000000 --- a/apps/legacy/computer-vision/components/utils/cocoSkeleton.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const COCO_SKELETON_CONNECTIONS = [ - ['NOSE', 'LEFT_EYE'], - ['NOSE', 'RIGHT_EYE'], - ['LEFT_EYE', 'LEFT_EAR'], - ['RIGHT_EYE', 'RIGHT_EAR'], - ['LEFT_SHOULDER', 'RIGHT_SHOULDER'], - ['LEFT_SHOULDER', 'LEFT_ELBOW'], - ['LEFT_ELBOW', 'LEFT_WRIST'], - ['RIGHT_SHOULDER', 'RIGHT_ELBOW'], - ['RIGHT_ELBOW', 'RIGHT_WRIST'], - ['LEFT_SHOULDER', 'LEFT_HIP'], - ['RIGHT_SHOULDER', 'RIGHT_HIP'], - ['LEFT_HIP', 'RIGHT_HIP'], - ['LEFT_HIP', 'LEFT_KNEE'], - ['LEFT_KNEE', 'LEFT_ANKLE'], - ['RIGHT_HIP', 'RIGHT_KNEE'], - ['RIGHT_KNEE', 'RIGHT_ANKLE'], -] as const; diff --git a/apps/legacy/computer-vision/components/utils/colors.ts b/apps/legacy/computer-vision/components/utils/colors.ts deleted file mode 100644 index c38493a3b0..0000000000 --- a/apps/legacy/computer-vision/components/utils/colors.ts +++ /dev/null @@ -1,41 +0,0 @@ -export const CLASS_COLORS: number[][] = [ - [0, 0, 0, 0], - [51, 255, 87, 180], - [51, 87, 255, 180], - [255, 51, 246, 180], - [51, 255, 246, 180], - [243, 255, 51, 180], - [141, 51, 255, 180], - [255, 131, 51, 180], - [51, 255, 131, 180], - [131, 51, 255, 180], - [255, 255, 51, 180], - [51, 255, 255, 180], - [255, 51, 143, 180], - [127, 51, 255, 180], - [51, 255, 175, 180], - [255, 175, 51, 180], - [179, 255, 51, 180], - [255, 87, 51, 180], - [255, 51, 162, 180], - [51, 162, 255, 180], - [162, 51, 255, 180], -]; - -export function hashLabel(label: string): number { - let hash = 5381; - for (let i = 0; i < label.length; i++) { - hash = (hash + hash * 32 + label.charCodeAt(i)) % 1000003; - } - return 1 + (Math.abs(hash) % (CLASS_COLORS.length - 1)); -} - -export function labelColor(label: string): string { - const color = CLASS_COLORS[hashLabel(label)]!; - return `rgba(${color[0]},${color[1]},${color[2]},1)`; -} - -export function labelColorBg(label: string): string { - const color = CLASS_COLORS[hashLabel(label)]!; - return `rgba(${color[0]},${color[1]},${color[2]},0.75)`; -} diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/ClassificationTask.tsx b/apps/legacy/computer-vision/components/vision_camera/tasks/ClassificationTask.tsx deleted file mode 100644 index c99a7cdffa..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/ClassificationTask.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { StyleSheet, Text, View } from 'react-native'; -import { Frame, useFrameOutput } from 'react-native-vision-camera'; -import { scheduleOnRN } from 'react-native-worklets'; -import { models, useClassification } from 'react-native-executorch/legacy'; -import { FRAME_TARGET_RESOLUTION, TaskProps } from './types'; - -type Props = Omit; - -export default function ClassificationTask({ - frameKillSwitch, - onFrameOutputChange, - onReadyChange, - onProgressChange, - onGeneratingChange, - onFpsChange, - onErrorChange, -}: Props) { - const model = useClassification({ - model: models.classification.efficientnet_v2_s(), - }); - const [classResult, setClassResult] = useState({ label: '', score: 0 }); - const lastFrameTimeRef = useRef(Date.now()); - - useEffect(() => { - onErrorChange(model.error ? String(model.error) : null); - }, [model.error, onErrorChange]); - - useEffect(() => { - onReadyChange(model.isReady); - }, [model.isReady, onReadyChange]); - - useEffect(() => { - onProgressChange(model.downloadProgress); - }, [model.downloadProgress, onProgressChange]); - - useEffect(() => { - onGeneratingChange(model.isGenerating); - }, [model.isGenerating, onGeneratingChange]); - - const classRof = model.runOnFrame; - - const updateClass = useCallback( - (r: { label: string; score: number }) => { - setClassResult(r); - const now = Date.now(); - const diff = now - lastFrameTimeRef.current; - if (diff > 0) onFpsChange(Math.round(1000 / diff), diff); - lastFrameTimeRef.current = now; - }, - [onFpsChange] - ); - - const frameOutput = useFrameOutput({ - targetResolution: FRAME_TARGET_RESOLUTION, - pixelFormat: 'rgb', - dropFramesWhileBusy: true, - enablePreviewSizedOutputBuffers: true, - onFrame: useCallback( - (frame: Frame) => { - 'worklet'; - if (frameKillSwitch.getDirty()) { - frame.dispose(); - return; - } - try { - if (!classRof) return; - const result = classRof(frame); - if (result) { - let bestLabel = ''; - let bestScore = -1; - const entries = Object.entries(result); - for (let i = 0; i < entries.length; i++) { - const [label, score] = entries[i]!; - if ((score as number) > bestScore) { - bestScore = score as number; - bestLabel = label; - } - } - scheduleOnRN(updateClass, { label: bestLabel, score: bestScore }); - } - } catch { - // Frame may be disposed before processing completes — transient, safe to ignore. - } finally { - frame.dispose(); - } - }, - [classRof, frameKillSwitch, updateClass] - ), - }); - - useEffect(() => { - onFrameOutputChange(frameOutput); - }, [frameOutput, onFrameOutputChange]); - - return classResult.label ? ( - - {classResult.label} - {(classResult.score * 100).toFixed(1)}% - - ) : null; -} - -const styles = StyleSheet.create({ - overlay: { - ...StyleSheet.absoluteFill, - justifyContent: 'center', - alignItems: 'center', - }, - label: { - color: 'white', - fontSize: 28, - fontWeight: '700', - textAlign: 'center', - textShadowColor: 'rgba(0,0,0,0.8)', - textShadowOffset: { width: 0, height: 1 }, - textShadowRadius: 6, - paddingHorizontal: 24, - }, - score: { - color: 'rgba(255,255,255,0.75)', - fontSize: 18, - fontWeight: '500', - marginTop: 4, - textShadowColor: 'rgba(0,0,0,0.8)', - textShadowOffset: { width: 0, height: 1 }, - textShadowRadius: 6, - }, -}); diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx b/apps/legacy/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx deleted file mode 100644 index 153214c122..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/InstanceSegmentationTask.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { StyleSheet, Text, View } from 'react-native'; -import { Frame, useFrameOutput } from 'react-native-vision-camera'; -import { scheduleOnRN } from 'react-native-worklets'; -import { - models, - SegmentedInstance, - useInstanceSegmentation, - CocoLabel, - CocoLabelYolo, - FastSAMLabel, -} from 'react-native-executorch/legacy'; -import { Canvas, Image as SkiaImage } from '@shopify/react-native-skia'; -import { labelColor, labelColorBg } from '../../utils/colors'; -import { FRAME_TARGET_RESOLUTION, TaskProps } from './types'; -import { buildDisplayInstances, DisplayInstance } from '../../../components/ImageWithMasks'; -const instanceSegmentation = models.instance_segmentation; - -type InstSegModelId = - | 'instanceSegmentationYolo26n' - | 'instanceSegmentationRfdetr' - | 'instanceSegmentationFastsamS' - | 'instanceSegmentationFastsamX'; - -type Props = TaskProps & { activeModel: InstSegModelId }; - -export default function InstanceSegmentationTask({ - activeModel, - canvasSize, - cameraPositionSync, - frameKillSwitch, - onFrameOutputChange, - onReadyChange, - onProgressChange, - onGeneratingChange, - onFpsChange, - onErrorChange, -}: Props) { - const yolo26n = useInstanceSegmentation({ - model: instanceSegmentation.yolo26n(), - preventLoad: activeModel !== 'instanceSegmentationYolo26n', - }); - const rfdetr = useInstanceSegmentation({ - model: instanceSegmentation.rf_detr_nano(), - preventLoad: activeModel !== 'instanceSegmentationRfdetr', - }); - const fastsamS = useInstanceSegmentation({ - model: instanceSegmentation.fastsam_s(), - preventLoad: activeModel !== 'instanceSegmentationFastsamS', - }); - const fastsamX = useInstanceSegmentation({ - model: instanceSegmentation.fastsam_x(), - preventLoad: activeModel !== 'instanceSegmentationFastsamX', - }); - - const active = { - instanceSegmentationYolo26n: yolo26n, - instanceSegmentationRfdetr: rfdetr, - instanceSegmentationFastsamS: fastsamS, - instanceSegmentationFastsamX: fastsamX, - }[activeModel]; - - const [instances, setInstances] = useState([]); - const [imageSize, setImageSize] = useState({ width: 1, height: 1 }); - const lastFrameTimeRef = useRef(Date.now()); - - useEffect(() => { - onErrorChange(active.error ? String(active.error) : null); - }, [active.error, onErrorChange]); - - useEffect(() => { - onReadyChange(active.isReady); - }, [active.isReady, onReadyChange]); - - useEffect(() => { - onProgressChange(active.downloadProgress); - }, [active.downloadProgress, onProgressChange]); - - useEffect(() => { - onGeneratingChange(active.isGenerating); - }, [active.isGenerating, onGeneratingChange]); - - const instSegRof = active.runOnFrame; - - const updateInstances = useCallback( - (p: { - results: - | SegmentedInstance[] - | SegmentedInstance[] - | SegmentedInstance[]; - imageWidth: number; - imageHeight: number; - }) => { - const displayInstances = buildDisplayInstances( - p.results.map((inst) => ({ - ...inst, - label: String(inst.label), - })) - ); - setInstances((prev) => { - // Dispose old mask images - prev.forEach((inst) => inst.maskImage.dispose()); - return displayInstances; - }); - setImageSize({ width: p.imageWidth, height: p.imageHeight }); - const now = Date.now(); - const diff = now - lastFrameTimeRef.current; - if (diff > 0) onFpsChange(Math.round(1000 / diff), diff); - lastFrameTimeRef.current = now; - }, - [onFpsChange] - ); - - const frameOutput = useFrameOutput({ - targetResolution: FRAME_TARGET_RESOLUTION, - pixelFormat: 'rgb', - dropFramesWhileBusy: true, - enablePreviewSizedOutputBuffers: true, - onFrame: useCallback( - (frame: Frame) => { - 'worklet'; - if (frameKillSwitch.getDirty()) { - frame.dispose(); - return; - } - try { - if (!instSegRof) return; - const isFrontCamera = cameraPositionSync.getDirty() === 'front'; - const iw = frame.width > frame.height ? frame.height : frame.width; - const ih = frame.width > frame.height ? frame.width : frame.height; - const result = instSegRof(frame, isFrontCamera, { - confidenceThreshold: 0.5, - iouThreshold: 0.5, - maxInstances: 5, - returnMaskAtOriginalResolution: false, - ...(activeModel === 'instanceSegmentationYolo26n' && { - inputSize: 384, - }), - }); - if (result) { - scheduleOnRN(updateInstances, { - results: result, - imageWidth: iw, - imageHeight: ih, - }); - } - } catch { - // ignore - } finally { - frame.dispose(); - } - }, - [instSegRof, frameKillSwitch, updateInstances, activeModel, cameraPositionSync] - ), - }); - - useEffect(() => { - onFrameOutputChange(frameOutput); - }, [frameOutput, onFrameOutputChange]); - - const scale = Math.max(canvasSize.width / imageSize.width, canvasSize.height / imageSize.height); - const offsetX = (canvasSize.width - imageSize.width * scale) / 2; - const offsetY = (canvasSize.height - imageSize.height * scale) / 2; - - return ( - - {/* Render masks */} - - {instances.map((inst, i) => { - const x = inst.bbox.x1 * scale + offsetX; - const y = inst.bbox.y1 * scale + offsetY; - const w = (inst.bbox.x2 - inst.bbox.x1) * scale; - const h = (inst.bbox.y2 - inst.bbox.y1) * scale; - return ( - - ); - })} - - {/* Render bounding boxes */} - {instances.map((inst, i) => { - const left = inst.bbox.x1 * scale + offsetX; - const top = inst.bbox.y1 * scale + offsetY; - const w = (inst.bbox.x2 - inst.bbox.x1) * scale; - const h = (inst.bbox.y2 - inst.bbox.y1) * scale; - const label = String(inst.label); - return ( - - - - {label} {(inst.score * 100).toFixed(1)} - - - - ); - })} - - ); -} - -const styles = StyleSheet.create({ - bbox: { - position: 'absolute', - borderWidth: 2, - borderColor: 'cyan', - borderRadius: 4, - }, - bboxLabel: { - position: 'absolute', - top: -22, - left: -2, - paddingHorizontal: 6, - paddingVertical: 2, - borderRadius: 4, - }, - bboxLabelText: { color: 'white', fontSize: 11, fontWeight: '600' }, -}); diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/OCRTask.tsx b/apps/legacy/computer-vision/components/vision_camera/tasks/OCRTask.tsx deleted file mode 100644 index 4cb55a7467..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/OCRTask.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { Frame, useFrameOutput } from 'react-native-vision-camera'; -import { scheduleOnRN } from 'react-native-worklets'; -import { models, OCRDetection, useOCR } from 'react-native-executorch/legacy'; -import Svg, { Polygon, Text as SvgText } from 'react-native-svg'; -import { FRAME_TARGET_RESOLUTION, TaskProps } from './types'; - -type Props = Omit; - -export default function OCRTask({ - canvasSize, - cameraPositionSync, - frameKillSwitch, - onFrameOutputChange, - onReadyChange, - onProgressChange, - onGeneratingChange, - onFpsChange, - onErrorChange, -}: Props) { - const model = useOCR({ model: models.ocr.craft({ language: 'en' }) }); - const [detections, setDetections] = useState([]); - const [imageSize, setImageSize] = useState({ width: 1, height: 1 }); - const lastFrameTimeRef = useRef(Date.now()); - - useEffect(() => { - onErrorChange(model.error ? String(model.error) : null); - }, [model.error, onErrorChange]); - - useEffect(() => { - onReadyChange(model.isReady); - }, [model.isReady, onReadyChange]); - - useEffect(() => { - onProgressChange(model.downloadProgress); - }, [model.downloadProgress, onProgressChange]); - - useEffect(() => { - onGeneratingChange(model.isGenerating); - }, [model.isGenerating, onGeneratingChange]); - - const ocrRof = model.runOnFrame; - - const updateDetections = useCallback( - (p: { results: OCRDetection[]; frameW: number; frameH: number }) => { - setDetections(p.results); - setImageSize({ width: p.frameW, height: p.frameH }); - const now = Date.now(); - const diff = now - lastFrameTimeRef.current; - if (diff > 0) onFpsChange(Math.round(1000 / diff), diff); - lastFrameTimeRef.current = now; - }, - [onFpsChange] - ); - - const frameOutput = useFrameOutput({ - targetResolution: FRAME_TARGET_RESOLUTION, - pixelFormat: 'rgb', - dropFramesWhileBusy: true, - enablePreviewSizedOutputBuffers: true, - onFrame: useCallback( - (frame: Frame) => { - 'worklet'; - if (frameKillSwitch.getDirty()) { - frame.dispose(); - return; - } - try { - if (!ocrRof) return; - const isFrontCamera = cameraPositionSync.getDirty() === 'front'; - const result = ocrRof(frame, isFrontCamera); - if (result) { - // Sensor frames are landscape-native, so width/height are swapped - // relative to portrait screen orientation. - scheduleOnRN(updateDetections, { - results: result, - frameW: frame.height, - frameH: frame.width, - }); - } - } catch { - // Frame may be disposed before processing completes — transient, safe to ignore. - } finally { - frame.dispose(); - } - }, - [cameraPositionSync, frameKillSwitch, ocrRof, updateDetections] - ), - }); - - useEffect(() => { - onFrameOutputChange(frameOutput); - }, [frameOutput, onFrameOutputChange]); - - const scale = Math.max(canvasSize.width / imageSize.width, canvasSize.height / imageSize.height); - const offsetX = (canvasSize.width - imageSize.width * scale) / 2; - const offsetY = (canvasSize.height - imageSize.height * scale) / 2; - - if (!detections.length) return null; - - return ( - - - {detections.map((det, i) => { - const { x1, y1, x2, y2 } = det.bbox; - const pts = [ - [x1, y1], - [x2, y1], - [x2, y2], - [x1, y2], - ] - .map(([x, y]) => `${x * scale + offsetX},${y * scale + offsetY}`) - .join(' '); - const labelX = x1 * scale + offsetX; - const labelY = y1 * scale + offsetY - 4; - return ( - - - - {det.text} - - - ); - })} - - - ); -} diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/ObjectDetectionTask.tsx b/apps/legacy/computer-vision/components/vision_camera/tasks/ObjectDetectionTask.tsx deleted file mode 100644 index 1ccc54e0a4..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/ObjectDetectionTask.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { Frame, useFrameOutput } from 'react-native-vision-camera'; -import { scheduleOnRN } from 'react-native-worklets'; -import { - models, - Detection, - useObjectDetection, - CocoLabel, - CocoLabelYolo, -} from 'react-native-executorch/legacy'; -import BoundingBoxes from '../../BoundingBoxes'; -import { FRAME_TARGET_RESOLUTION, TaskProps } from './types'; -const objectDetection = models.object_detection; - -type ObjModelId = 'objectDetectionSsdlite' | 'objectDetectionRfdetr' | 'objectDetectionYolo26n'; - -type Props = TaskProps & { activeModel: ObjModelId }; - -export default function ObjectDetectionTask({ - activeModel, - canvasSize, - cameraPositionSync, - frameKillSwitch, - onFrameOutputChange, - onReadyChange, - onProgressChange, - onGeneratingChange, - onFpsChange, - onErrorChange, -}: Props) { - const ssdlite = useObjectDetection({ - model: objectDetection.ssdlite_320_mobilenet_v3_large(), - preventLoad: activeModel !== 'objectDetectionSsdlite', - }); - const rfdetr = useObjectDetection({ - model: objectDetection.rf_detr_nano(), - preventLoad: activeModel !== 'objectDetectionRfdetr', - }); - const yolo26n = useObjectDetection({ - model: objectDetection.yolo26n(), - preventLoad: activeModel !== 'objectDetectionYolo26n', - }); - - const active = - activeModel === 'objectDetectionSsdlite' - ? ssdlite - : activeModel === 'objectDetectionRfdetr' - ? rfdetr - : yolo26n; - - type CommonDetection = Omit & { label: string }; - - const [detections, setDetections] = useState([]); - const [imageSize, setImageSize] = useState({ width: 1, height: 1 }); - const lastFrameTimeRef = useRef(Date.now()); - - useEffect(() => { - onErrorChange(active.error ? String(active.error) : null); - }, [active.error, onErrorChange]); - - useEffect(() => { - onReadyChange(active.isReady); - }, [active.isReady, onReadyChange]); - - useEffect(() => { - onProgressChange(active.downloadProgress); - }, [active.downloadProgress, onProgressChange]); - - useEffect(() => { - onGeneratingChange(active.isGenerating); - }, [active.isGenerating, onGeneratingChange]); - - const detRof = active.runOnFrame; - - const updateDetections = useCallback( - (p: { - results: Detection[] | Detection[]; - imageWidth: number; - imageHeight: number; - }) => { - setDetections( - p.results.map((det) => ({ - ...det, - label: String(det.label), - })) - ); - setImageSize({ width: p.imageWidth, height: p.imageHeight }); - const now = Date.now(); - const diff = now - lastFrameTimeRef.current; - if (diff > 0) onFpsChange(Math.round(1000 / diff), diff); - lastFrameTimeRef.current = now; - }, - [onFpsChange] - ); - - const frameOutput = useFrameOutput({ - targetResolution: FRAME_TARGET_RESOLUTION, - pixelFormat: 'rgb', - dropFramesWhileBusy: true, - enablePreviewSizedOutputBuffers: true, - - onFrame: useCallback( - (frame: Frame) => { - 'worklet'; - if (frameKillSwitch.getDirty()) { - frame.dispose(); - return; - } - try { - if (!detRof) return; - const isFrontCamera = cameraPositionSync.getDirty() === 'front'; - const result = detRof(frame, isFrontCamera, { - detectionThreshold: 0.5, - }); - // Sensor frames are landscape-native, so width/height are swapped - // relative to portrait screen orientation. - const screenW = frame.height; - const screenH = frame.width; - if (result) { - scheduleOnRN(updateDetections, { - results: result, - imageWidth: screenW, - imageHeight: screenH, - }); - } - } catch { - // Frame may be disposed before processing completes — transient, safe to ignore. - } finally { - frame.dispose(); - } - }, - [cameraPositionSync, detRof, frameKillSwitch, updateDetections] - ), - }); - - useEffect(() => { - onFrameOutputChange(frameOutput); - }, [frameOutput, onFrameOutputChange]); - - const scale = Math.max(canvasSize.width / imageSize.width, canvasSize.height / imageSize.height); - const offsetX = (canvasSize.width - imageSize.width * scale) / 2; - const offsetY = (canvasSize.height - imageSize.height * scale) / 2; - - return ( - - - - ); -} diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/PoseEstimationTask.tsx b/apps/legacy/computer-vision/components/vision_camera/tasks/PoseEstimationTask.tsx deleted file mode 100644 index 3d6452b9bb..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/PoseEstimationTask.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { Frame, useFrameOutput } from 'react-native-vision-camera'; -import { scheduleOnRN } from 'react-native-worklets'; -import Svg, { Circle, Line } from 'react-native-svg'; -import { models, usePoseEstimation, PoseDetections } from 'react-native-executorch/legacy'; -import { TaskProps } from './types'; -import { COCO_SKELETON_CONNECTIONS } from '../../utils/cocoSkeleton'; - -type Props = TaskProps & { activeModel: 'poseEstimationYolo26n' }; - -// Colors for different people -const PERSON_COLORS = ['lime', 'cyan', 'magenta', 'yellow', 'orange', 'pink']; - -export default function PoseEstimationTask({ - activeModel, - canvasSize, - cameraPositionSync, - frameKillSwitch, - onFrameOutputChange, - onReadyChange, - onProgressChange, - onGeneratingChange, - onFpsChange, - onErrorChange, -}: Props) { - const poseModel = usePoseEstimation({ - model: models.pose_estimation.yolo26n(), - preventLoad: activeModel !== 'poseEstimationYolo26n', - }); - - const [detections, setDetections] = useState([]); - const [imageSize, setImageSize] = useState({ width: 1, height: 1 }); - const lastFrameTimeRef = useRef(Date.now()); - - useEffect(() => { - onErrorChange(poseModel.error ? String(poseModel.error) : null); - }, [poseModel.error, onErrorChange]); - - useEffect(() => { - onReadyChange(poseModel.isReady); - }, [poseModel.isReady, onReadyChange]); - - useEffect(() => { - onProgressChange(poseModel.downloadProgress); - }, [poseModel.downloadProgress, onProgressChange]); - - useEffect(() => { - onGeneratingChange(poseModel.isGenerating); - }, [poseModel.isGenerating, onGeneratingChange]); - - const poseRof = poseModel.runOnFrame; - - const updateDetections = useCallback( - (p: { results: PoseDetections; imageWidth: number; imageHeight: number }) => { - setDetections(p.results); - setImageSize({ width: p.imageWidth, height: p.imageHeight }); - const now = Date.now(); - const diff = now - lastFrameTimeRef.current; - if (diff > 0) onFpsChange(Math.round(1000 / diff), diff); - lastFrameTimeRef.current = now; - }, - [onFpsChange] - ); - - const frameOutput = useFrameOutput({ - pixelFormat: 'rgb', - dropFramesWhileBusy: true, - enablePreviewSizedOutputBuffers: true, - - onFrame: useCallback( - (frame: Frame) => { - 'worklet'; - if (frameKillSwitch.getDirty()) { - frame.dispose(); - return; - } - try { - if (!poseRof) return; - const isFrontCamera = cameraPositionSync.getDirty() === 'front'; - const result = poseRof(frame, isFrontCamera, { - detectionThreshold: 0.5, - }); - const screenW = frame.height; - const screenH = frame.width; - if (result) { - scheduleOnRN(updateDetections, { - results: result, - imageWidth: screenW, - imageHeight: screenH, - }); - } - } catch { - // Frame may be disposed before processing completes - } finally { - frame.dispose(); - } - }, - [cameraPositionSync, poseRof, frameKillSwitch, updateDetections] - ), - }); - - useEffect(() => { - onFrameOutputChange(frameOutput); - }, [frameOutput, onFrameOutputChange]); - - const scale = Math.max(canvasSize.width / imageSize.width, canvasSize.height / imageSize.height); - const offsetX = (canvasSize.width - imageSize.width * scale) / 2; - const offsetY = (canvasSize.height - imageSize.height * scale) / 2; - - return ( - - - {detections.map((personKeypoints, personIdx) => { - const color = PERSON_COLORS[personIdx % PERSON_COLORS.length]; - const isVisible = (kp: { x: number; y: number }) => - kp.x >= 0 && kp.y >= 0 && kp.x <= imageSize.width && kp.y <= imageSize.height; - return ( - - {/* Draw skeleton lines */} - {COCO_SKELETON_CONNECTIONS.map(([from, to], lineIdx) => { - const kp1 = personKeypoints[from]; - const kp2 = personKeypoints[to]; - if (!kp1 || !kp2) return null; - if (!isVisible(kp1) || !isVisible(kp2)) return null; - const x1 = kp1.x * scale + offsetX; - const y1 = kp1.y * scale + offsetY; - const x2 = kp2.x * scale + offsetX; - const y2 = kp2.y * scale + offsetY; - return ( - - ); - })} - {/* Draw keypoints */} - {Object.entries(personKeypoints) - .filter(([, kp]) => isVisible(kp)) - .map(([name, kp]) => { - const cx = kp.x * scale + offsetX; - const cy = kp.y * scale + offsetY; - return ( - - ); - })} - - ); - })} - - - ); -} diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/SegmentationTask.tsx b/apps/legacy/computer-vision/components/vision_camera/tasks/SegmentationTask.tsx deleted file mode 100644 index 82c10e4b0f..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/SegmentationTask.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { Frame, useFrameOutput } from 'react-native-vision-camera'; -import { scheduleOnRN } from 'react-native-worklets'; -import { models, useSemanticSegmentation } from 'react-native-executorch/legacy'; -import { - AlphaType, - Canvas, - ColorType, - Image as SkiaImage, - Skia, - SkImage, -} from '@shopify/react-native-skia'; -import { CLASS_COLORS } from '../../utils/colors'; -import { FRAME_TARGET_RESOLUTION, TaskProps } from './types'; -const semanticSegmentation = models.semantic_segmentation; - -type SegModelId = - | 'segmentationDeeplabResnet50' - | 'segmentationDeeplabResnet101' - | 'segmentationDeeplabMobilenet' - | 'segmentationLraspp' - | 'segmentationFcnResnet50' - | 'segmentationFcnResnet101' - | 'segmentationSelfie'; - -type Props = TaskProps & { activeModel: SegModelId }; - -export default function SegmentationTask({ - activeModel, - canvasSize, - cameraPositionSync, - frameKillSwitch, - onFrameOutputChange, - onReadyChange, - onProgressChange, - onGeneratingChange, - onFpsChange, - onErrorChange, -}: Props) { - const segDeeplabResnet50 = useSemanticSegmentation({ - model: semanticSegmentation.deeplab_v3_resnet50(), - preventLoad: activeModel !== 'segmentationDeeplabResnet50', - }); - const segDeeplabResnet101 = useSemanticSegmentation({ - model: semanticSegmentation.deeplab_v3_resnet101(), - preventLoad: activeModel !== 'segmentationDeeplabResnet101', - }); - const segDeeplabMobilenet = useSemanticSegmentation({ - model: semanticSegmentation.deeplab_v3_mobilenet_v3_large(), - preventLoad: activeModel !== 'segmentationDeeplabMobilenet', - }); - const segLraspp = useSemanticSegmentation({ - model: semanticSegmentation.lraspp_mobilenet_v3_large(), - preventLoad: activeModel !== 'segmentationLraspp', - }); - const segFcnResnet50 = useSemanticSegmentation({ - model: semanticSegmentation.fcn_resnet50(), - preventLoad: activeModel !== 'segmentationFcnResnet50', - }); - const segFcnResnet101 = useSemanticSegmentation({ - model: semanticSegmentation.fcn_resnet101(), - preventLoad: activeModel !== 'segmentationFcnResnet101', - }); - const segSelfie = useSemanticSegmentation({ - model: semanticSegmentation.selfie_segmentation(), - preventLoad: activeModel !== 'segmentationSelfie', - }); - - const active = { - segmentationDeeplabResnet50: segDeeplabResnet50, - segmentationDeeplabResnet101: segDeeplabResnet101, - segmentationDeeplabMobilenet: segDeeplabMobilenet, - segmentationLraspp: segLraspp, - segmentationFcnResnet50: segFcnResnet50, - segmentationFcnResnet101: segFcnResnet101, - segmentationSelfie: segSelfie, - }[activeModel]; - - const [maskImage, setMaskImage] = useState(null); - const [imageSize, setImageSize] = useState({ width: 1, height: 1 }); - const lastFrameTimeRef = useRef(Date.now()); - - useEffect(() => { - onErrorChange(active.error ? String(active.error) : null); - }, [active.error, onErrorChange]); - - useEffect(() => { - onReadyChange(active.isReady); - }, [active.isReady, onReadyChange]); - - useEffect(() => { - onProgressChange(active.downloadProgress); - }, [active.downloadProgress, onProgressChange]); - - useEffect(() => { - onGeneratingChange(active.isGenerating); - }, [active.isGenerating, onGeneratingChange]); - - // Clear stale mask when the segmentation model variant changes - useEffect(() => { - setMaskImage((prev) => { - prev?.dispose(); - return null; - }); - }, [activeModel]); - - // Dispose native Skia image on unmount to prevent memory leaks - useEffect(() => { - return () => { - setMaskImage((prev) => { - prev?.dispose(); - return null; - }); - }; - }, []); - - const segRof = active.runOnFrame; - - const updateMask = useCallback( - (p: { img: SkImage; screenW: number; screenH: number }) => { - setMaskImage((prev) => { - prev?.dispose(); - return p.img; - }); - setImageSize({ width: p.screenW, height: p.screenH }); - const now = Date.now(); - const diff = now - lastFrameTimeRef.current; - if (diff > 0) onFpsChange(Math.round(1000 / diff), diff); - lastFrameTimeRef.current = now; - }, - [onFpsChange] - ); - - // CLASS_COLORS captured directly in closure — worklets cannot import modules - const colors = CLASS_COLORS; - - const frameOutput = useFrameOutput({ - targetResolution: FRAME_TARGET_RESOLUTION, - pixelFormat: 'rgb', - dropFramesWhileBusy: true, - enablePreviewSizedOutputBuffers: true, - onFrame: useCallback( - (frame: Frame) => { - 'worklet'; - if (frameKillSwitch.getDirty()) { - frame.dispose(); - return; - } - try { - if (!segRof) return; - const isFrontCamera = cameraPositionSync.getDirty() === 'front'; - const result = segRof(frame, isFrontCamera, [], false); - if (result?.ARGMAX) { - const argmax: Int32Array = result.ARGMAX; - // Both the preview and the mask live in a portrait coord system: - // the activity is portrait-locked (so CameraX's PreviewView always - // renders the preview in portrait orientation regardless of how - // the device is physically tilted), and the native side runs - // `inverseRotateMat` which always converts the mask into the same - // portrait coord system. Treat sensor-native dims as portrait by - // swapping height/width — same convention as the sibling OCR and - // ObjectDetection tasks. - const screenW = frame.height; - const screenH = frame.width; - // Mask buffer dims: the C++ side returns the mask at model output - // resolution (the `resizeToInput=false` arg below). All built-in - // segmentation models output a square spatial map (e.g. 520×520), - // so sqrt(length) recovers the side. Non-square model outputs - // would need dims exposed from native. - const maskSide = Math.round(Math.sqrt(argmax.length)); - const maskW = maskSide; - const maskH = maskSide; - const pixels = new Uint8Array(maskW * maskH * 4); - for (let i = 0; i < argmax.length; i++) { - const color = colors[argmax[i]!] ?? [0, 0, 0, 0]; - pixels[i * 4] = color[0]!; - pixels[i * 4 + 1] = color[1]!; - pixels[i * 4 + 2] = color[2]!; - pixels[i * 4 + 3] = color[3]!; - } - const skData = Skia.Data.fromBytes(pixels); - const img = Skia.Image.MakeImage( - { - width: maskW, - height: maskH, - alphaType: AlphaType.Unpremul, - colorType: ColorType.RGBA_8888, - }, - skData, - maskW * 4 - ); - if (img) scheduleOnRN(updateMask, { img, screenW, screenH }); - } - } catch { - // Frame may be disposed before processing completes — transient, safe to ignore. - } finally { - frame.dispose(); - } - }, - [cameraPositionSync, colors, frameKillSwitch, segRof, updateMask] - ), - }); - - useEffect(() => { - onFrameOutputChange(frameOutput); - }, [frameOutput, onFrameOutputChange]); - - if (!maskImage) return null; - - // Match the camera preview's cover-scale + center layout so the mask - // aligns pixel-for-pixel with what the user sees. `fit="fill"` lets the - // (square) mask stretch into the preview rect — which is computed in - // screen-space dims rather than the sensor-native ones. - const scale = Math.max(canvasSize.width / imageSize.width, canvasSize.height / imageSize.height); - const dstW = imageSize.width * scale; - const dstH = imageSize.height * scale; - const offsetX = (canvasSize.width - dstW) / 2; - const offsetY = (canvasSize.height - dstH) / 2; - - return ( - - - - - - ); -} diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/StyleTransferTask.tsx b/apps/legacy/computer-vision/components/vision_camera/tasks/StyleTransferTask.tsx deleted file mode 100644 index e4fa579432..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/StyleTransferTask.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { Frame, useFrameOutput } from 'react-native-vision-camera'; -import { scheduleOnRN } from 'react-native-worklets'; -import { models, useStyleTransfer } from 'react-native-executorch/legacy'; -import { - AlphaType, - Canvas, - ColorType, - Image as SkiaImage, - Skia, - SkImage, -} from '@shopify/react-native-skia'; -import { FRAME_TARGET_RESOLUTION, TaskProps } from './types'; -const styleTransfer = models.style_transfer; - -type StyleModelId = 'styleTransferCandy' | 'styleTransferMosaic'; - -type Props = TaskProps & { activeModel: StyleModelId }; - -export default function StyleTransferTask({ - activeModel, - canvasSize, - cameraPositionSync, - frameKillSwitch, - onFrameOutputChange, - onReadyChange, - onProgressChange, - onGeneratingChange, - onFpsChange, - onErrorChange, -}: Props) { - const candy = useStyleTransfer({ - model: styleTransfer.candy(), - preventLoad: activeModel !== 'styleTransferCandy', - }); - const mosaic = useStyleTransfer({ - model: styleTransfer.mosaic(), - preventLoad: activeModel !== 'styleTransferMosaic', - }); - - const active = activeModel === 'styleTransferCandy' ? candy : mosaic; - - const [styledImage, setStyledImage] = useState(null); - const lastFrameTimeRef = useRef(Date.now()); - - useEffect(() => { - onErrorChange(active.error ? String(active.error) : null); - }, [active.error, onErrorChange]); - - useEffect(() => { - onReadyChange(active.isReady); - }, [active.isReady, onReadyChange]); - - useEffect(() => { - onProgressChange(active.downloadProgress); - }, [active.downloadProgress, onProgressChange]); - - useEffect(() => { - onGeneratingChange(active.isGenerating); - }, [active.isGenerating, onGeneratingChange]); - - useEffect(() => { - setStyledImage((prev) => { - prev?.dispose(); - return null; - }); - }, [activeModel]); - - useEffect(() => { - return () => { - setStyledImage((prev) => { - prev?.dispose(); - return null; - }); - }; - }, []); - - const styleRof = active.runOnFrame; - - const updateImage = useCallback( - (img: SkImage) => { - setStyledImage((prev) => { - prev?.dispose(); - return img; - }); - const now = Date.now(); - const diff = now - lastFrameTimeRef.current; - if (diff > 0) onFpsChange(Math.round(1000 / diff), diff); - lastFrameTimeRef.current = now; - }, - [onFpsChange] - ); - - const frameOutput = useFrameOutput({ - targetResolution: FRAME_TARGET_RESOLUTION, - pixelFormat: 'rgb', - dropFramesWhileBusy: true, - enablePreviewSizedOutputBuffers: true, - onFrame: useCallback( - (frame: Frame) => { - 'worklet'; - if (frameKillSwitch.getDirty()) { - frame.dispose(); - return; - } - try { - if (!styleRof) return; - const isFrontCamera = cameraPositionSync.getDirty() === 'front'; - const result = styleRof(frame, isFrontCamera); - if (result) { - const pixels = new Uint8Array(result.dataPtr.buffer); - const skData = Skia.Data.fromBytes(pixels); - const img = Skia.Image.MakeImage( - { - width: result.sizes[1]!, - height: result.sizes[0]!, - alphaType: AlphaType.Unpremul, - colorType: ColorType.RGBA_8888, - }, - skData, - result.sizes[1]! * 4 - ); - if (img) scheduleOnRN(updateImage, img); - } - } catch { - // Frame may be disposed before processing completes — transient, safe to ignore. - } finally { - frame.dispose(); - } - }, - [cameraPositionSync, frameKillSwitch, styleRof, updateImage] - ), - }); - - useEffect(() => { - onFrameOutputChange(frameOutput); - }, [frameOutput, onFrameOutputChange]); - - if (!styledImage) return null; - - return ( - - - - - - ); -} diff --git a/apps/legacy/computer-vision/components/vision_camera/tasks/types.ts b/apps/legacy/computer-vision/components/vision_camera/tasks/types.ts deleted file mode 100644 index a3293e51ec..0000000000 --- a/apps/legacy/computer-vision/components/vision_camera/tasks/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useFrameOutput } from 'react-native-vision-camera'; -import type { Synchronizable } from 'react-native-worklets'; - -export const FRAME_TARGET_RESOLUTION = { width: 1280, height: 720 } as const; - -export type TaskProps = { - activeModel: string; - canvasSize: { width: number; height: number }; - cameraPositionSync: Synchronizable<'front' | 'back'>; - frameKillSwitch: Synchronizable; - onFrameOutputChange: (frameOutput: ReturnType) => void; - onReadyChange: (isReady: boolean) => void; - onProgressChange: (progress: number) => void; - onGeneratingChange: (isGenerating: boolean) => void; - onFpsChange: (fps: number, frameMs: number) => void; - onErrorChange: (error: string | null) => void; -}; diff --git a/apps/legacy/computer-vision/context.ts b/apps/legacy/computer-vision/context.ts deleted file mode 100644 index 0eb0560768..0000000000 --- a/apps/legacy/computer-vision/context.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react'; - -export const GeneratingContext = createContext({ - setGlobalGenerating: (_newState: boolean) => {}, -}); diff --git a/apps/legacy/computer-vision/declarations.d.ts b/apps/legacy/computer-vision/declarations.d.ts deleted file mode 100644 index 85e178f497..0000000000 --- a/apps/legacy/computer-vision/declarations.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module '*.svg' { - import { SvgProps } from 'react-native-svg'; - const content: React.FV; - export default content; -} diff --git a/apps/legacy/computer-vision/index.ts b/apps/legacy/computer-vision/index.ts deleted file mode 100644 index 3f443dcf95..0000000000 --- a/apps/legacy/computer-vision/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerRootComponent } from 'expo'; - -import App from './app'; - -// registerRootComponent calls AppRegistry.registerComponent('main', () => App); -// It also ensures that whether you load the app in Expo Go or in a native build, -// the environment is set up appropriately -registerRootComponent(App); diff --git a/apps/legacy/computer-vision/metro.config.js b/apps/legacy/computer-vision/metro.config.js deleted file mode 100644 index f8ab2ab96d..0000000000 --- a/apps/legacy/computer-vision/metro.config.js +++ /dev/null @@ -1,21 +0,0 @@ -// Learn more https://docs.expo.io/guides/customizing-metro -const { getDefaultConfig } = require('expo/metro-config'); - -/** @type {import('expo/metro-config').MetroConfig} */ -const config = getDefaultConfig(__dirname); - -const { transformer, resolver } = config; - -config.transformer = { - ...transformer, - babelTransformerPath: require.resolve('react-native-svg-transformer/expo'), -}; -config.resolver = { - ...resolver, - assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'), - sourceExts: [...resolver.sourceExts, 'svg'], -}; - -config.resolver.assetExts.push('pte'); - -module.exports = config; diff --git a/apps/legacy/computer-vision/package.json b/apps/legacy/computer-vision/package.json deleted file mode 100644 index 598d4b390e..0000000000 --- a/apps/legacy/computer-vision/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "legacy-computer-vision", - "version": "1.0.0", - "main": "expo-router/entry", - "scripts": { - "start": "expo start", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "typecheck": "tsc", - "lint": "eslint . --ext .ts,.tsx --fix" - }, - "dependencies": { - "@expo/log-box": "~56.0.12", - "@expo/vector-icons": "^15.0.2", - "@react-native/metro-config": "^0.86.0", - "@react-navigation/drawer": "^7.9.4", - "@react-navigation/native": "^7.2.2", - "@shopify/react-native-skia": "2.6.2", - "expo": "~57.0.28", - "expo-build-properties": "~57.0.22", - "expo-constants": "~57.0.21", - "expo-font": "~57.0.4", - "expo-linking": "~57.0.12", - "expo-router": "~57.0.26", - "expo-status-bar": "~57.0.1", - "metro-config": "^0.84.0", - "react": "19.2.3", - "react-native": "0.86.3", - "react-native-device-info": "^15.0.2", - "react-native-executorch": "workspace:*", - "react-native-executorch-expo-resource-fetcher": "workspace:*", - "react-native-gesture-handler": "~2.32.0", - "react-native-image-picker": "^8.2.1", - "react-native-loading-spinner-overlay": "^3.0.1", - "react-native-nitro-image": "^0.15.2", - "react-native-nitro-modules": "^0.37.1", - "react-native-reanimated": "4.7.1", - "react-native-safe-area-context": "~5.7.0", - "react-native-screens": "~4.26.0", - "react-native-svg": "15.15.4", - "react-native-svg-transformer": "^1.5.3", - "react-native-vision-camera": "^5.2.3", - "react-native-vision-camera-worklets": "^5.2.3", - "react-native-worklets": "0.13.0" - }, - "devDependencies": { - "@babel/core": "^7.29.7", - "@types/react": "~19.2.0", - "@types/react-refresh": "^0", - "babel-preset-expo": "~57.0.15", - "react-refresh": "^0.18.0" - }, - "private": true -} diff --git a/apps/legacy/computer-vision/tsconfig.json b/apps/legacy/computer-vision/tsconfig.json deleted file mode 100644 index 20b5fc01ed..0000000000 --- a/apps/legacy/computer-vision/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "jsx": "react-native", - "lib": ["es2022"], - "moduleResolution": "bundler", - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "esnext", - "paths": { - "react-native-executorch/legacy": ["../../packages/react-native-executorch/legacy/src"], - "react-native-executorch-expo-resource-fetcher": ["../../packages/expo-resource-fetcher"] - } - }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["**/node_modules", "**/Pods", "**/__tests__"], - "extends": "expo/tsconfig.base" -} diff --git a/apps/legacy/computer-vision/utils.ts b/apps/legacy/computer-vision/utils.ts deleted file mode 100644 index b576d63e4b..0000000000 --- a/apps/legacy/computer-vision/utils.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CameraOptions, launchCamera, launchImageLibrary } from 'react-native-image-picker'; - -export const getImage = async (useCamera: boolean) => { - const options: CameraOptions = { - mediaType: 'photo', - }; - try { - const output = useCamera ? await launchCamera(options) : await launchImageLibrary(options); - - if (!output.assets || output.assets.length === 0) return; - - return output.assets[0]; - } catch (err) { - console.error(err); - } -}; diff --git a/apps/legacy/llm/.gitignore b/apps/legacy/llm/.gitignore deleted file mode 100644 index f779c900a7..0000000000 --- a/apps/legacy/llm/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files - -# dependencies -node_modules/ - -# Expo -.expo/ -dist/ -web-build/ -expo-env.d.ts - -# Native -*.orig.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision - -# Metro -.metro-health-check* - -# debug -npm-debug.* -yarn-debug.* -yarn-error.* - -# macOS -.DS_Store -*.pem - -# local env files -.env*.local - -# typescript -*.tsbuildinfo - -.yarn diff --git a/apps/legacy/llm/app.json b/apps/legacy/llm/app.json deleted file mode 100644 index 2598330062..0000000000 --- a/apps/legacy/llm/app.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "expo": { - "name": "legacy-llm", - "slug": "legacy-llm", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icons/icon.png", - "userInterfaceStyle": "light", - "scheme": "rne-llm", - "plugins": [ - [ - "expo-font", - { - "fonts": ["./assets/fonts/Aeonik-Regular.otf", "./assets/fonts/Aeonik-Medium.otf"] - } - ], - [ - "expo-calendar", - { - "calendarPermission": "The app needs to access your calendar." - } - ], - "expo-router", - [ - "react-native-audio-api", - { - "iosBackgroundMode": true, - "iosMicrophonePermission": "This app requires access to the microphone to record audio.", - "androidPermissions": [ - "android.permission.MODIFY_AUDIO_SETTINGS", - "android.permission.FOREGROUND_SERVICE", - "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK", - "android.permission.RECORD_AUDIO" - ], - "androidForegroundService": true, - "androidFSTypes": ["mediaPlayback"] - } - ], - [ - "expo-build-properties", - { - "ios": { - "deploymentTarget": "17.0" - } - } - ] - ], - "newArchEnabled": true, - "splash": { - "image": "./assets/icons/splash.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.anonymous.llm", - "infoPlist": { - "NSMicrophoneUsageDescription": "This app needs access to your microphone to record audio.", - "NSCalendarsUsageDescription": "This app needs access to your calendar to manage events." - }, - "entitlements": { - "com.apple.developer.kernel.increased-memory-limit": true - } - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/icons/adaptive-icon.png", - "backgroundColor": "#ffffff" - }, - "package": "com.anonymous.llm", - "permissions": [ - "android.permission.READ_CALENDAR", - "android.permission.WRITE_CALENDAR", - "android.permission.MODIFY_AUDIO_SETTINGS", - "android.permission.FOREGROUND_SERVICE", - "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK", - "android.permission.RECORD_AUDIO" - ] - }, - "web": { - "favicon": "./assets/icons/favicon.png" - } - } -} diff --git a/apps/legacy/llm/app/_layout.tsx b/apps/legacy/llm/app/_layout.tsx deleted file mode 100644 index ee2fd12ce8..0000000000 --- a/apps/legacy/llm/app/_layout.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { Drawer } from 'expo-router/drawer'; -import { initExecutorch } from 'react-native-executorch/legacy'; -import { ExpoResourceFetcher } from 'react-native-executorch-expo-resource-fetcher'; -import ColorPalette from '../colors'; -import React, { useState } from 'react'; -import { Text, StyleSheet, View, TouchableOpacity, type ColorValue } from 'react-native'; -import { - type DrawerContentComponentProps, - DrawerContentScrollView, - DrawerItemList, -} from 'expo-router/build/react-navigation/drawer'; -import { DrawerActions } from 'expo-router/build/react-navigation/routers'; -import { useNavigation } from 'expo-router'; -import Svg, { Rect } from 'react-native-svg'; -import { GeneratingContext } from '../context'; - -function HamburgerIcon({ tintColor }: { tintColor?: ColorValue }) { - const navigation = useNavigation(); - const fill = typeof tintColor === 'string' ? tintColor : '#000'; - return ( - navigation.dispatch(DrawerActions.toggleDrawer())} - style={styles.hamburger} - > - - - - - - - ); -} - -initExecutorch({ - resourceFetcher: ExpoResourceFetcher, -}); - -interface CustomDrawerProps extends DrawerContentComponentProps { - isGenerating: boolean; -} - -function CustomDrawerContent(props: CustomDrawerProps) { - const { isGenerating, ...otherProps } = props; - return ( - - {!isGenerating ? ( - - ) : ( - - Model is generating... - Interrupt before switching model - - )} - - ); -} - -export default function _layout() { - const [isGenerating, setIsGenerating] = useState(false); - - return ( - { - setIsGenerating(newState); - }, - }} - > - } - screenOptions={{ - drawerActiveTintColor: ColorPalette.primary, - drawerInactiveTintColor: '#888', - headerTintColor: ColorPalette.primary, - headerTitleStyle: { color: ColorPalette.primary }, - headerLeft: (props) => , - }} - > - null, - title: 'Main Menu', - drawerItemStyle: { display: 'none' }, - }} - /> - - - - - - - - ); -} - -const styles = StyleSheet.create({ - hamburger: { - marginLeft: 12, - }, - centerContent: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: 20, - }, - mainText: { - fontSize: 18, - fontWeight: 'bold', - marginBottom: 10, - color: ColorPalette.primary, - }, - subText: { - fontSize: 14, - color: ColorPalette.strongPrimary, - }, -}); diff --git a/apps/legacy/llm/app/index.tsx b/apps/legacy/llm/app/index.tsx deleted file mode 100644 index 61f7872ff6..0000000000 --- a/apps/legacy/llm/app/index.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useRouter } from 'expo-router'; -import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; -import ColorPalette from '../colors'; -import ExecutorchLogo from '../assets/icons/executorch.svg'; - -export default function Home() { - const router = useRouter(); - - return ( - - - Select a demo model - - router.navigate('llm/')}> - LLM - - router.navigate('llm_tool_calling/')} - > - LLM Tool Calling - - router.navigate('llm_structured_output/')} - > - LLM Structured Output - - router.navigate('multimodal_llm/')}> - Multimodal LLM (VLM) - - router.navigate('privacy_filter/')}> - Privacy Filter (PII) - - - - ); -} - -export const fontSizes = { - xxl: 34, - xl: 22, - lg: 18, - md: 16, - sm: 14, - xs: 12, - xxs: 10, -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#fff', - }, - headerText: { - fontSize: fontSizes.lg, - color: ColorPalette.strongPrimary, - margin: 20, - }, - buttonContainer: { - width: '80%', - justifyContent: 'space-evenly', - marginBottom: 20, - }, - button: { - backgroundColor: ColorPalette.strongPrimary, - borderRadius: 8, - padding: 10, - alignItems: 'center', - marginBottom: 10, - }, - buttonText: { - color: 'white', - fontSize: fontSizes.md, - }, -}); diff --git a/apps/legacy/llm/app/llm/index.tsx b/apps/legacy/llm/app/llm/index.tsx deleted file mode 100644 index 681a134b94..0000000000 --- a/apps/legacy/llm/app/llm/index.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import { useContext, useEffect, useRef, useState } from 'react'; -import { - Keyboard, - KeyboardAvoidingView, - StyleSheet, - Text, - TextInput, - Platform, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native'; -import SendIcon from '../../assets/icons/send_icon.svg'; -import { models, useLLM } from 'react-native-executorch/legacy'; -import { ModelPicker } from '../../components/ModelPicker'; -import { LLM_MODELS, LLMModelSources } from '../../components/llmModels'; -import PauseIcon from '../../assets/icons/pause_icon.svg'; -import ColorPalette from '../../colors'; -import Messages from '../../components/Messages'; -import { useIsFocused } from 'expo-router'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { GeneratingContext } from '../../context'; -import Spinner from '../../components/Spinner'; -import SuggestedPrompts from '../../components/SuggestedPrompts'; - -const SUGGESTED_PROMPTS = [ - 'Explain quantum computing in simple terms', - 'Write a short poem about coding', - 'What are the benefits of on-device AI?', - 'Give me 3 fun facts about space', -]; -import { useLLMStats } from '../../hooks/useLLMStats'; -import { StatsBar } from '../../components/StatsBar'; -import ErrorBanner from '../../components/ErrorBanner'; - -export default function LLMScreenWrapper() { - const isFocused = useIsFocused(); - return isFocused ? : null; -} - -function LLMScreen() { - const { bottom } = useSafeAreaInsets(); - const [isTextInputFocused, setIsTextInputFocused] = useState(false); - const [userInput, setUserInput] = useState(''); - const [selectedModel, setSelectedModel] = useState( - models.llm.lfm2_5_1_2b_instruct() - ); - const textInputRef = useRef(null); - const { setGlobalGenerating } = useContext(GeneratingContext); - - const llm = useLLM({ model: selectedModel }); - const tokenCount = llm.isReady ? llm.getGeneratedTokenCount() : 0; - const { stats, onMessageSend } = useLLMStats(llm.response, llm.isGenerating, tokenCount); - const [error, setError] = useState(null); - - useEffect(() => { - if (llm.error) setError(String(llm.error)); - }, [llm.error]); - - useEffect(() => { - setGlobalGenerating(llm.isGenerating); - }, [llm.isGenerating, setGlobalGenerating]); - - const sendMessage = async () => { - onMessageSend(); - setUserInput(''); - textInputRef.current?.clear(); - try { - await llm.sendMessage(userInput); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - return !llm.isReady && !llm.error ? ( - - ) : ( - - - - setError(null)} /> - {llm.messageHistory.length ? ( - - - - ) : ( - - Hello! 👋 - What can I help you with? - - - )} - - setSelectedModel(m)} - disabled={llm.isGenerating} - /> - - - setIsTextInputFocused(true)} - onBlur={() => setIsTextInputFocused(false)} - style={{ - ...styles.textInput, - borderColor: isTextInputFocused ? ColorPalette.blueDark : ColorPalette.blueLight, - }} - placeholder={isTextInputFocused ? '' : 'Your message'} - placeholderTextColor={'#C1C6E5'} - multiline={true} - ref={textInputRef} - value={userInput} - onChangeText={(text: string) => setUserInput(text)} - /> - {userInput && ( - !llm.isGenerating && (await sendMessage())} - > - - - )} - {llm.isGenerating && ( - - - - )} - - - - - ); -} - -const styles = StyleSheet.create({ - keyboardAvoidingView: { - flex: 1, - paddingBottom: Platform.OS === 'android' ? 20 : 0, - }, - container: { flex: 1 }, - chatContainer: { flex: 10, width: '100%' }, - helloMessageContainer: { - flex: 10, - width: '100%', - alignItems: 'center', - justifyContent: 'center', - overflow: 'hidden', - }, - helloText: { - fontFamily: 'medium', - fontSize: 30, - color: ColorPalette.primary, - }, - bottomHelloText: { - fontFamily: 'regular', - fontSize: 20, - lineHeight: 28, - textAlign: 'center', - color: ColorPalette.primary, - }, - bottomContainer: { - height: 100, - width: '100%', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 16, - }, - textInput: { - flex: 1, - borderWidth: 1, - borderRadius: 8, - lineHeight: 19.6, - fontFamily: 'regular', - fontSize: 14, - color: ColorPalette.primary, - padding: 16, - }, - sendChatTouchable: { - height: '100%', - width: 48, - justifyContent: 'center', - alignItems: 'flex-end', - }, -}); diff --git a/apps/legacy/llm/app/llm_structured_output/index.tsx b/apps/legacy/llm/app/llm_structured_output/index.tsx deleted file mode 100644 index ae22afa4a7..0000000000 --- a/apps/legacy/llm/app/llm_structured_output/index.tsx +++ /dev/null @@ -1,269 +0,0 @@ -import { useContext, useEffect, useRef, useState } from 'react'; -import { - Keyboard, - KeyboardAvoidingView, - StyleSheet, - Text, - TextInput, - Platform, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native'; -import SendIcon from '../../assets/icons/send_icon.svg'; -import Spinner from '../../components/Spinner'; -import SuggestedPrompts from '../../components/SuggestedPrompts'; - -const SUGGESTED_PROMPTS = [ - "I'm John. Is this damaged? I offer $100.", - "Hi, I'm Alice! How old is it? Offering $250 USD.", - "I'm Bob. Does it have warranty? I'll pay €50.", - "Name's Sara. What condition? My bid is $75.", -]; -import { useLLMStats } from '../../hooks/useLLMStats'; -import { StatsBar } from '../../components/StatsBar'; -import ErrorBanner from '../../components/ErrorBanner'; -import { - models, - useLLM, - fixAndValidateStructuredOutput, - getStructuredOutputPrompt, -} from 'react-native-executorch/legacy'; -import { ModelPicker } from '../../components/ModelPicker'; -import { LLM_MODELS, LLMModelSources } from '../../components/llmModels'; -import PauseIcon from '../../assets/icons/pause_icon.svg'; -import ColorPalette from '../../colors'; -import Messages from '../../components/Messages'; -import { useIsFocused } from 'expo-router'; -import { GeneratingContext } from '../../context'; -import { Schema } from 'jsonschema'; -import * as z from 'zod/v4'; - -// Defining schemas -const responseSchema: Schema = { - properties: { - username: { - type: 'string', - description: 'Name of user, that is asking a question.', - }, - question: { - type: 'string', - description: 'Question that user asks.', - }, - bid: { - type: 'number', - description: 'Amount of money, that user offers.', - }, - currency: { - type: 'string', - description: 'Currency of offer.', - }, - }, - required: ['username', 'bid'], - type: 'object', -}; - -const responseSchemaWithZod = z.object({ - username: z.string().meta({ description: 'Name of user, that is asking a question.' }), - question: z.optional(z.string().meta({ description: 'Question that user asks.' })), - bid: z.number().meta({ description: 'Amount of money, that user offers.' }), - currency: z.optional(z.string().meta({ description: 'Currency of offer.' })), -}); - -export default function LLMScreenWrapper() { - const isFocused = useIsFocused(); - - return isFocused ? : null; -} - -function LLMScreen() { - const [isTextInputFocused, setIsTextInputFocused] = useState(false); - const [userInput, setUserInput] = useState(''); - const [selectedModel, setSelectedModel] = useState(models.llm.qwen3_1_7b()); - const textInputRef = useRef(null); - const { setGlobalGenerating } = useContext(GeneratingContext); - - const llm = useLLM({ model: selectedModel }); - const tokenCount = llm.isReady ? llm.getGeneratedTokenCount() : 0; - const { stats, onMessageSend } = useLLMStats(llm.response, llm.isGenerating, tokenCount); - const [error, setError] = useState(null); - - useEffect(() => { - setGlobalGenerating(llm.isGenerating); - }, [llm.isGenerating, setGlobalGenerating]); - - const { configure } = llm; - useEffect(() => { - const formattingInstructions = getStructuredOutputPrompt(responseSchema); - const prompt = `Your goal is to parse user's messages and return them in JSON format. Don't respond to user. Simply return JSON with user's question parsed. \n${formattingInstructions}\n /no_think`; - - configure({ - chatConfig: { - systemPrompt: prompt, - }, - }); - }, [configure]); - - useEffect(() => { - const lastMessage = llm.messageHistory.at(-1); - if (!llm.isGenerating && lastMessage?.role === 'assistant') { - try { - const formattedOutput = fixAndValidateStructuredOutput(lastMessage.content, responseSchema); - const formattedOutputWithZod = fixAndValidateStructuredOutput( - lastMessage.content, - responseSchemaWithZod - ); - console.log('Formatted output:', formattedOutput, formattedOutputWithZod); - } catch (e) { - console.log("Error parsing output and/or output doesn't match required schema!", e); - } - } - }, [llm.messageHistory, llm.isGenerating]); - - useEffect(() => { - if (llm.error) setError(String(llm.error)); - }, [llm.error]); - - const sendMessage = async () => { - onMessageSend(); - setUserInput(''); - textInputRef.current?.clear(); - try { - await llm.sendMessage(userInput); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - return !llm.isReady && !llm.error ? ( - - ) : ( - - - - setError(null)} /> - {llm.messageHistory.length ? ( - - - - ) : ( - - Hello! 👋 - - I can parse user's questions! Introduce yourself, ask questions and offer a price - for some product. - - - - )} - - setSelectedModel(m)} - disabled={llm.isGenerating} - /> - - - setIsTextInputFocused(true)} - onBlur={() => setIsTextInputFocused(false)} - style={{ - ...styles.textInput, - borderColor: isTextInputFocused ? ColorPalette.blueDark : ColorPalette.blueLight, - }} - placeholder={ - isTextInputFocused - ? '' - : "Your message e.g. I'm John. Is this product damaged? I can give you $100 for this." - } - placeholderTextColor={'#C1C6E5'} - multiline={true} - ref={textInputRef} - value={userInput} - onChangeText={(text: string) => setUserInput(text)} - /> - {userInput && ( - !llm.isGenerating && (await sendMessage())} - > - - - )} - {llm.isGenerating && ( - - - - )} - - - - - ); -} - -const styles = StyleSheet.create({ - keyboardAvoidingView: { flex: 1 }, - container: { flex: 1, paddingBottom: Platform.OS === 'android' ? 20 : 0 }, - chatContainer: { flex: 10, width: '100%' }, - helloMessageContainer: { - flex: 10, - width: '100%', - alignItems: 'center', - justifyContent: 'center', - }, - helloText: { - fontFamily: 'medium', - fontSize: 30, - color: ColorPalette.primary, - }, - bottomHelloText: { - padding: 20, - fontFamily: 'regular', - fontSize: 20, - lineHeight: 28, - textAlign: 'center', - color: ColorPalette.primary, - }, - bottomContainer: { - height: 100, - width: '100%', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 16, - }, - textInput: { - flex: 1, - borderWidth: 1, - borderRadius: 8, - lineHeight: 19.6, - fontFamily: 'regular', - fontSize: 14, - color: ColorPalette.primary, - padding: 16, - }, - sendChatTouchable: { - height: '100%', - width: 48, - justifyContent: 'center', - alignItems: 'flex-end', - }, -}); diff --git a/apps/legacy/llm/app/llm_tool_calling/index.tsx b/apps/legacy/llm/app/llm_tool_calling/index.tsx deleted file mode 100644 index c67c91c373..0000000000 --- a/apps/legacy/llm/app/llm_tool_calling/index.tsx +++ /dev/null @@ -1,330 +0,0 @@ -import { useContext, useEffect, useRef, useState } from 'react'; -import { - Keyboard, - KeyboardAvoidingView, - Platform, - StyleSheet, - Text, - TextInput, - TouchableOpacity, - TouchableWithoutFeedback, - View, - Linking, - Alert, - AppState, -} from 'react-native'; -import SWMIcon from '../../assets/icons/swm_icon.svg'; -import SendIcon from '../../assets/icons/send_icon.svg'; -import Spinner from '../../components/Spinner'; -import ErrorBanner from '../../components/ErrorBanner'; -import { models, useLLM, DEFAULT_SYSTEM_PROMPT } from 'react-native-executorch/legacy'; -import { ModelPicker } from '../../components/ModelPicker'; -import { LLM_MODELS, LLMModelSources } from '../../components/llmModels'; -import PauseIcon from '../../assets/icons/pause_icon.svg'; -import ColorPalette from '../../colors'; -import Messages from '../../components/Messages'; -import * as Brightness from 'expo-brightness'; -import * as Calendar from 'expo-calendar'; -import { PermissionStatus } from 'expo'; -import { executeTool, TOOL_DEFINITIONS_PHONE } from '../../utils/tools'; -import { useIsFocused } from 'expo-router'; -import { GeneratingContext } from '../../context'; -import SuggestedPrompts from '../../components/SuggestedPrompts'; - -const SUGGESTED_PROMPTS = [ - 'What events do I have today?', - 'Add a meeting tomorrow at 2pm', - 'Set screen brightness to 50%', - 'What do I have scheduled this week?', -]; -import { useLLMStats } from '../../hooks/useLLMStats'; -import { StatsBar } from '../../components/StatsBar'; - -export default function LLMToolCallingScreenWrapper() { - const isFocused = useIsFocused(); - - return isFocused ? : null; -} - -function LLMToolCallingScreen() { - const [isTextInputFocused, setIsTextInputFocused] = useState(false); - const [userInput, setUserInput] = useState(''); - const [hasCalendarPermission, setHasCalendarPermission] = useState(true); - const [hasBrightnessPermission, setHasBrightnessPermission] = useState(true); - const [selectedModel, setSelectedModel] = useState(models.llm.hammer2_1_1_5b()); - const textInputRef = useRef(null); - const { setGlobalGenerating } = useContext(GeneratingContext); - - const llm = useLLM({ model: selectedModel }); - const tokenCount = llm.isReady ? llm.getGeneratedTokenCount() : 0; - const { stats, onMessageSend } = useLLMStats(llm.response, llm.isGenerating, tokenCount); - const [error, setError] = useState(null); - - useEffect(() => { - setGlobalGenerating(llm.isGenerating); - }, [llm.isGenerating, setGlobalGenerating]); - - const { configure } = llm; - useEffect(() => { - configure({ - chatConfig: { - systemPrompt: `${DEFAULT_SYSTEM_PROMPT} Current time and date: ${new Date().toString()}`, - }, - toolsConfig: { - tools: TOOL_DEFINITIONS_PHONE, - executeToolCallback: executeTool, - displayToolCalls: true, - }, - }); - }, [configure]); - - useEffect(() => { - if (llm.error) setError(String(llm.error)); - }, [llm.error]); - - const requestCalendarPermission = async () => { - const { status, canAskAgain } = await Calendar.getCalendarPermissionsAsync(); - - if (status === PermissionStatus.GRANTED) { - setHasCalendarPermission(true); - return; - } - - if (status === PermissionStatus.UNDETERMINED || canAskAgain) { - const { status: nextStatus } = await Calendar.requestCalendarPermissionsAsync(); - setHasCalendarPermission(nextStatus === PermissionStatus.GRANTED); - return; - } - - setHasCalendarPermission(false); - Alert.alert( - 'Calendar Permission Required', - 'To read or add events, the app requires "Full Access" to your calendar. Please enable this in your device settings.', - [ - { text: 'Cancel', style: 'cancel' }, - { text: 'Open Settings', onPress: () => Linking.openSettings() }, - ] - ); - }; - - const requestBrightnessPermission = async () => { - const { status, canAskAgain } = await Brightness.getPermissionsAsync(); - - if (status === Brightness.PermissionStatus.GRANTED) { - setHasBrightnessPermission(true); - return; - } - - if (status === Brightness.PermissionStatus.UNDETERMINED || canAskAgain) { - const { status: nextStatus } = await Brightness.requestPermissionsAsync(); - setHasBrightnessPermission(nextStatus === Brightness.PermissionStatus.GRANTED); - return; - } - - setHasBrightnessPermission(false); - Alert.alert( - 'Brightness Permission Required', - 'To change screen brightness, the app requires permission. Please enable this in your device settings.', - [ - { text: 'Cancel', style: 'cancel' }, - { text: 'Open Settings', onPress: () => Linking.openSettings() }, - ] - ); - }; - - useEffect(() => { - requestCalendarPermission(); - requestBrightnessPermission(); - }, []); - - useEffect(() => { - const subscription = AppState.addEventListener('change', (nextAppState) => { - if (nextAppState === 'active') { - requestCalendarPermission(); - requestBrightnessPermission(); - } - }); - - return () => { - subscription.remove(); - }; - }, []); - - const sendMessage = async () => { - onMessageSend(); - setUserInput(''); - textInputRef.current?.clear(); - try { - await llm.sendMessage(userInput); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - return !llm.isReady && !llm.error ? ( - - ) : ( - - - - - - - setError(null)} /> - {llm.messageHistory.length ? ( - - - - ) : ( - - Hello! 👋 - - I can use calendar! Ask me to check it or add an event for you! - - - - )} - - {!hasCalendarPermission && ( - - - Calendar access is required.{' '} - Grant Access - - - )} - {!hasBrightnessPermission && ( - - - Brightness access is required.{' '} - Grant Access - - - )} - - setSelectedModel(m)} - disabled={llm.isGenerating} - /> - - - setIsTextInputFocused(true)} - onBlur={() => setIsTextInputFocused(false)} - style={{ - ...styles.textInput, - borderColor: isTextInputFocused ? ColorPalette.blueDark : ColorPalette.blueLight, - }} - placeholder={isTextInputFocused ? '' : 'Your message'} - placeholderTextColor={'#C1C6E5'} - multiline={true} - ref={textInputRef} - value={userInput} - onChangeText={(text: string) => setUserInput(text)} - /> - {userInput && ( - !llm.isGenerating && (await sendMessage())} - > - - - )} - {llm.isGenerating && ( - - - - )} - - - - - ); -} - -const styles = StyleSheet.create({ - container: { flex: 1, paddingBottom: Platform.OS === 'android' ? 20 : 0 }, - keyboardAvoidingView: { flex: 1 }, - topContainer: { - height: 68, - width: '100%', - alignItems: 'center', - justifyContent: 'center', - }, - chatContainer: { flex: 10, width: '100%' }, - textModelName: { color: ColorPalette.primary }, - helloMessageContainer: { - flex: 10, - width: '100%', - alignItems: 'center', - justifyContent: 'center', - }, - helloText: { - fontFamily: 'medium', - fontSize: 30, - color: ColorPalette.primary, - }, - bottomHelloText: { - padding: 20, - fontFamily: 'regular', - fontSize: 20, - lineHeight: 28, - textAlign: 'center', - color: ColorPalette.primary, - }, - bottomContainer: { - height: 100, - width: '100%', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 16, - }, - textInput: { - flex: 1, - borderWidth: 1, - borderRadius: 8, - lineHeight: 19.6, - fontFamily: 'regular', - fontSize: 14, - color: ColorPalette.primary, - padding: 16, - }, - sendChatTouchable: { - height: '100%', - width: 48, - justifyContent: 'center', - alignItems: 'flex-end', - }, - permissionBanner: { - backgroundColor: '#FFF3CD', - paddingVertical: 10, - paddingHorizontal: 16, - alignItems: 'center', - }, - permissionBannerText: { - fontFamily: 'regular', - fontSize: 13, - color: '#856404', - }, - permissionBannerLink: { - fontFamily: 'medium', - color: ColorPalette.blueDark, - }, -}); diff --git a/apps/legacy/llm/app/multimodal_llm/index.tsx b/apps/legacy/llm/app/multimodal_llm/index.tsx deleted file mode 100644 index 8e657e2509..0000000000 --- a/apps/legacy/llm/app/multimodal_llm/index.tsx +++ /dev/null @@ -1,545 +0,0 @@ -import { useContext, useEffect, useRef, useState } from 'react'; -import { - Image, - Keyboard, - KeyboardAvoidingView, - Platform, - StyleSheet, - Text, - TextInput, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native'; -import { launchImageLibrary } from 'react-native-image-picker'; -import { AudioManager, AudioRecorder, AudioContext } from 'react-native-audio-api'; -import { useIsFocused } from 'expo-router'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { models, useLLM } from 'react-native-executorch/legacy'; -import SendIcon from '../../assets/icons/send_icon.svg'; -import PauseIcon from '../../assets/icons/pause_icon.svg'; -import ColorPalette from '../../colors'; -import Messages from '../../components/Messages'; -import Spinner from '../../components/Spinner'; -import { GeneratingContext } from '../../context'; -import SuggestedPrompts from '../../components/SuggestedPrompts'; -import ErrorBanner from '../../components/ErrorBanner'; -import AudioWaveform from '../../components/AudioWaveform'; - -const SUGGESTED_PROMPTS = [ - "What's in this image?", - 'Describe this scene in detail', - 'What objects can you see?', - 'What text appears in this image?', - 'Transcribe the audio', -]; -import { useLLMStats } from '../../hooks/useLLMStats'; -import { StatsBar } from '../../components/StatsBar'; - -export default function MultimodalLLMScreenWrapper() { - const isFocused = useIsFocused(); - return isFocused ? : null; -} - -function MultimodalLLMScreen() { - const { bottom } = useSafeAreaInsets(); - const [imageUri, setImageUri] = useState(null); - const [userInput, setUserInput] = useState(''); - const [isTextInputFocused, setIsTextInputFocused] = useState(false); - const textInputRef = useRef(null); - const { setGlobalGenerating } = useContext(GeneratingContext); - - const [audioBuffer, setAudioBuffer] = useState(null); - const [audioLabel, setAudioLabel] = useState(null); - const [audioUrl, setAudioUrl] = useState(''); - const [isFetchingAudio, setIsFetchingAudio] = useState(false); - const [isRecording, setIsRecording] = useState(false); - const [hasMicPermission, setHasMicPermission] = useState(false); - const recorder = useRef(new AudioRecorder()); - const recordChunks = useRef([]); - - const [error, setError] = useState(null); - const model = models.llm.gemma4_e2b_multimodal(); - const vlm = useLLM({ model: model }); - const tokenCount = vlm.isReady ? vlm.getGeneratedTokenCount() : 0; - const { stats, onMessageSend } = useLLMStats(vlm.response, vlm.isGenerating, tokenCount); - - useEffect(() => { - setGlobalGenerating(vlm.isGenerating); - }, [vlm.isGenerating, setGlobalGenerating]); - - // Updated to use local error state - useEffect(() => { - if (vlm.error) setError(String(vlm.error)); - }, [vlm.error]); - - useEffect(() => { - AudioManager.setAudioSessionOptions({ - iosCategory: 'playAndRecord', - iosMode: 'spokenAudio', - iosOptions: ['allowBluetoothHFP', 'defaultToSpeaker'], - }); - (async () => { - const status = await AudioManager.requestRecordingPermissions(); - setHasMicPermission(status === 'Granted'); - })(); - - return () => { - if (vlm.isGenerating) vlm.interrupt(); - // eslint-disable-next-line react-hooks/exhaustive-deps - recorder.current.stop(); - AudioManager.setAudioSessionActivity(false); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const loadAudioFromUrl = async () => { - const url = audioUrl.trim(); - if (!url) return; - setIsFetchingAudio(true); - try { - const ctx = new AudioContext({ sampleRate: 16000 }); - const decoded = await ctx.decodeAudioData(url); - const pcm = decoded.getChannelData(0); - const name = url.split('/').pop() || 'audio'; - setAudioBuffer(pcm); - setAudioLabel(`${name} · ${(pcm.length / 16000).toFixed(1)}s`); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } finally { - setIsFetchingAudio(false); - } - }; - - const startRecording = async () => { - if (!hasMicPermission) { - setError('Microphone permission denied. Please enable it in Settings.'); - return; - } - recordChunks.current = []; - const sampleRate = 16000; - recorder.current.onAudioReady( - { sampleRate, bufferLength: 0.1 * sampleRate, channelCount: 1 }, - ({ buffer }) => { - recordChunks.current.push(new Float32Array(buffer.getChannelData(0))); - } - ); - try { - await AudioManager.setAudioSessionActivity(true); - const result = await recorder.current.start(); - if (result.status === 'error') { - setError(`Recording problems: ${result.message}`); - return; - } - setIsRecording(true); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - const stopRecording = () => { - recorder.current.stop(); - setIsRecording(false); - const total = recordChunks.current.reduce((n, c) => n + c.length, 0); - if (total === 0) return; - const pcm = new Float32Array(total); - let off = 0; - for (const c of recordChunks.current) { - pcm.set(c, off); - off += c.length; - } - recordChunks.current = []; - setAudioBuffer(pcm); - setAudioLabel(`Recording · ${(pcm.length / 16000).toFixed(1)}s`); - }; - - const clearAudio = () => { - setAudioBuffer(null); - setAudioLabel(null); - }; - - const pickImage = async () => { - try { - const result = await launchImageLibrary({ mediaType: 'photo' }); - if (result.assets && result.assets.length > 0) { - const uri = result.assets[0]?.uri; - if (uri) setImageUri(uri); - } - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - const sendMessage = async () => { - if (!(imageUri || audioBuffer || userInput.trim()) || vlm.isGenerating) return; - onMessageSend(); - const text = userInput.trim(); - setUserInput(''); - textInputRef.current?.clear(); - Keyboard.dismiss(); - const currentImageUri = imageUri; - const currentAudio = audioBuffer; - setImageUri(null); - setAudioBuffer(null); - setAudioLabel(null); - try { - const media = - currentImageUri || currentAudio - ? { - ...(currentImageUri ? { imagePath: currentImageUri } : {}), - ...(currentAudio ? { audioBuffer: currentAudio } : {}), - } - : undefined; - await vlm.sendMessage(text, media); - } catch (e) { - // Updated to set UI error instead of just console.error - setError(e instanceof Error ? e.message : String(e)); - } - }; - - // Updated Spinner check so it doesn't block the ErrorBanner if loading fails - if (!vlm.isReady && !vlm.error) { - return ( - - ); - } - - return ( - - - - {/* Injected ErrorBanner here */} - setError(null)} /> - - {vlm.messageHistory.length ? ( - - - - ) : ( - - Hello! 👋 - - {model.capabilities.find((c) => c === 'audio') - ? 'Say hi, or pick an image, and ask me anything about it.' - : 'Pick an image and ask me anything about it.'} - - - - )} - - {/* Image thumbnail strip */} - {imageUri && ( - - - Tap to change - - )} - - {/* Audio URL input */} - - - - {isFetchingAudio ? '…' : 'Load'} - - - - {/* Audio attachment strip */} - {audioLabel && ( - - - 🎵 {audioLabel} - - ✕ - - - - - )} - - - - {/* Image picker button */} - - 📷 - - - {/* Mic record / stop button */} - - {isRecording ? '⏹️' : '🎤'} - - - setIsTextInputFocused(true)} - onBlur={() => setIsTextInputFocused(false)} - style={[ - styles.textInput, - { - borderColor: isTextInputFocused ? ColorPalette.blueDark : ColorPalette.blueLight, - }, - ]} - placeholder={imageUri ? 'Ask about the image…' : 'Your message'} - placeholderTextColor="#C1C6E5" - multiline - value={userInput} - onChangeText={setUserInput} - /> - - {(imageUri || audioBuffer || userInput.trim()) && !vlm.isGenerating && ( - - - - )} - {vlm.isGenerating && ( - - - - )} - - - - - ); -} - -const styles = StyleSheet.create({ - // Setup phase - setupContainer: { - flex: 1, - padding: 24, - backgroundColor: '#fff', - justifyContent: 'center', - }, - setupTitle: { - fontSize: 20, - fontFamily: 'medium', - color: ColorPalette.primary, - marginBottom: 8, - }, - setupHint: { - fontSize: 13, - fontFamily: 'regular', - color: ColorPalette.blueDark, - marginBottom: 32, - lineHeight: 18, - }, - filePickerRow: { - flexDirection: 'row', - alignItems: 'center', - borderWidth: 1, - borderColor: ColorPalette.blueLight, - borderRadius: 10, - padding: 14, - marginBottom: 12, - backgroundColor: '#fafbff', - }, - filePickerInfo: { flex: 1 }, - filePickerLabel: { - fontSize: 12, - fontFamily: 'medium', - color: ColorPalette.blueDark, - marginBottom: 2, - }, - filePickerValue: { fontSize: 14, fontFamily: 'regular' }, - filePickerValueSet: { color: ColorPalette.primary }, - filePickerValueEmpty: { color: ColorPalette.blueLight }, - filePickerChevron: { - fontSize: 24, - color: ColorPalette.blueLight, - marginLeft: 8, - }, - loadButton: { - marginTop: 16, - backgroundColor: ColorPalette.strongPrimary, - borderRadius: 10, - padding: 14, - alignItems: 'center', - }, - loadButtonDisabled: { backgroundColor: ColorPalette.blueLight }, - loadButtonText: { color: '#fff', fontFamily: 'medium', fontSize: 15 }, - - // Chat phase - container: { flex: 1 }, - chatContainer: { flex: 10, width: '100%' }, - helloMessageContainer: { - flex: 10, - width: '100%', - alignItems: 'center', - justifyContent: 'center', - overflow: 'hidden', - }, - helloText: { - fontFamily: 'medium', - fontSize: 30, - color: ColorPalette.primary, - }, - bottomHelloText: { - fontFamily: 'regular', - fontSize: 20, - lineHeight: 28, - textAlign: 'center', - color: ColorPalette.primary, - paddingHorizontal: 24, - }, - imageThumbnailContainer: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 16, - paddingVertical: 6, - gap: 8, - }, - imageThumbnail: { - width: 48, - height: 48, - borderRadius: 8, - borderWidth: 1, - borderColor: ColorPalette.blueLight, - }, - imageThumbnailHint: { - fontSize: 12, - fontFamily: 'regular', - color: ColorPalette.blueDark, - }, - audioAttachmentContainer: { - flexDirection: 'column', - paddingHorizontal: 16, - paddingVertical: 8, - marginHorizontal: 16, - marginBottom: 4, - borderRadius: 8, - borderWidth: 1, - borderColor: ColorPalette.blueLight, - backgroundColor: '#fafbff', - }, - audioAttachmentRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }, - audioAttachmentText: { - fontSize: 13, - fontFamily: 'regular', - color: ColorPalette.blueDark, - }, - audioAttachmentClear: { - fontSize: 16, - color: ColorPalette.blueDark, - paddingHorizontal: 8, - }, - audioWaveform: { - marginTop: 6, - minWidth: 0, - }, - audioUrlRow: { - flexDirection: 'row', - alignItems: 'center', - marginHorizontal: 16, - marginBottom: 4, - }, - audioUrlInput: { - flex: 1, - padding: 10, - borderTopLeftRadius: 8, - borderBottomLeftRadius: 8, - borderWidth: 1, - borderColor: ColorPalette.blueLight, - borderRightWidth: 0, - fontFamily: 'regular', - fontSize: 13, - color: ColorPalette.primary, - }, - audioUrlButton: { - paddingVertical: 10, - paddingHorizontal: 16, - backgroundColor: ColorPalette.strongPrimary, - borderTopRightRadius: 8, - borderBottomRightRadius: 8, - justifyContent: 'center', - alignItems: 'center', - }, - audioUrlButtonText: { - color: '#fff', - fontFamily: 'medium', - fontSize: 13, - }, - disabled: { - opacity: 0.5, - }, - bottomContainer: { - height: 100, - width: '100%', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 16, - }, - imageButton: { - width: 40, - height: 40, - justifyContent: 'center', - alignItems: 'center', - marginRight: 4, - }, - imageButtonText: { fontSize: 22 }, - textInput: { - flex: 1, - borderWidth: 1, - borderRadius: 8, - lineHeight: 19.6, - fontFamily: 'regular', - fontSize: 14, - color: ColorPalette.primary, - padding: 16, - }, - sendChatTouchable: { - height: '100%', - width: 48, - justifyContent: 'center', - alignItems: 'flex-end', - }, -}); diff --git a/apps/legacy/llm/app/privacy_filter/index.tsx b/apps/legacy/llm/app/privacy_filter/index.tsx deleted file mode 100644 index b713c96aa6..0000000000 --- a/apps/legacy/llm/app/privacy_filter/index.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { useMemo, useState } from 'react'; -import { - ActivityIndicator, - ScrollView, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; -import { useIsFocused } from 'expo-router'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { - models, - PiiEntity, - PrivacyFilterModelSources, - usePrivacyFilter, -} from 'react-native-executorch/legacy'; - -const privacyFilter = models.privacy_filter; -import ColorPalette from '../../colors'; -import { ModelOption, ModelPicker } from '../../components/ModelPicker'; -import { buildSegments, colorForLabel, matchEntities } from '../../utils/piiMatching'; - -/* cspell:disable */ -// Sample tuned for the OpenAI base model — exercises the 8 entity types it -// recognizes (person, email, phone, account_number, address, date, url, -// secret). -const OPENAI_SAMPLE = `My name is Sarah Chen and I work as a senior engineer at Acme Corp. You can reach me at sarah.chen@acmecorp.io or call my direct line at (415) 923-0847. For billing inquiries, my account number is ACC-8821-4490-3371. - -I've been living at 17 Birchwood Lane, Portland, OR 97201 since October 3rd, 2019. Before that I was at 8 Rue de Rivoli, Paris, 75001, France. My personal website is https://sarahchen.dev and my GitHub is https://github.com/schen-eng. Feel free to connect — I usually respond within a business day. - -My date of birth is June 12, 1991, and my backup email is s.chen.personal@gmail.com in case the primary address is unreachable. This message also contains a confidential API key: sk-T93kXpLm2NvBqR7dYwZ4. Please do not share it outside the team. You can also reach my colleague James Okonkwo at j.okonkwo@acmecorp.io or at his mobile +44 7911 123456.`; -// Sample tuned for the OpenMed Nemotron model — covers categories the base -// OpenAI model doesn't have (medical, financial, technical, demographic). - -const NEMOTRON_SAMPLE = `Patient intake for Maria Lopez, female, age 47, blood type O+, born 1978-05-12. MRN 994-2210-AB; health plan beneficiary number HPBN-552-9931 with Aetna. SSN 412-55-7821, national ID DNI 88-7762-X. Primary occupation: registered nurse, currently employed full-time at Mercy General. Religion: Catholic; political view: independent. - -Reach her at maria.lopez@example.com or +1 (415) 555-0142. Mailing address: 84 Cedar Hill Road, Apt 3B, Berkeley, CA 94703, United States. Vehicle plate 7XKL922; driver license CA-D1294883. - -Payment for last visit: Visa ending 4992-1133-7820-4419, expires 11/28, CVV 884. Bank routing 021000089, SWIFT BIC CHASUS33. Employer EIN tax ID 47-3320118. Customer ID CUST-553201, employee ID EMP-A0093. - -Workstation MAC 3C:22:FB:8E:01:9A, IPv4 10.0.42.118, device IMEI 359888061234560. Service account API key sk-live-Tn8x3pLm2NvBqR7dYwZ4QF, password Hunter2!Spring. Session cookie sid=eyJ1c2VyIjoiOTk0MjIxMCJ9.`; -/* cspell:enable */ - -const MODEL_OPTIONS: ModelOption[] = [ - { - label: 'OpenAI Privacy Filter (8 entities)', - value: privacyFilter.openai(), - }, - { - label: 'OpenMed Nemotron (55 entities)', - value: privacyFilter.nemotron(), - }, -]; - -// Pick the right sample to display/run based on the active model. -function sampleFor(model: PrivacyFilterModelSources): string { - return model.modelName === privacyFilter.nemotron().modelName ? NEMOTRON_SAMPLE : OPENAI_SAMPLE; -} - -function HighlightedText({ source, entities }: { source: string; entities: PiiEntity[] }) { - const segments = useMemo( - () => buildSegments(source, matchEntities(source, entities)), - [source, entities] - ); - return ( - - {segments.map((seg, i) => - seg.label ? ( - - {seg.text} - - ) : ( - {seg.text} - ) - )} - - ); -} - -function PrivacyFilterScreen() { - const { bottom } = useSafeAreaInsets(); - const [entities, setEntities] = useState(null); - const [runError, setRunError] = useState(null); - const [inferenceMs, setInferenceMs] = useState(null); - const [selectedModel, setSelectedModel] = useState( - privacyFilter.openai() - ); - - const filter = usePrivacyFilter({ model: selectedModel }); - const sampleText = sampleFor(selectedModel); - - const onRun = async () => { - setRunError(null); - setEntities(null); - setInferenceMs(null); - const startedAt = Date.now(); - try { - const result = await filter.generate(sampleText); - const elapsed = Date.now() - startedAt; - setInferenceMs(elapsed); - setEntities(result); - } catch (e) { - setRunError(e instanceof Error ? e.message : String(e)); - } - }; - - const disabled = !filter.isReady || filter.isGenerating; - - return ( - - { - setEntities(null); - setRunError(null); - setInferenceMs(null); - setSelectedModel(m); - }} - label="Model" - disabled={filter.isGenerating} - /> - - {filter.error && ( - - Load error: {filter.error.message} - - )} - - {!filter.isReady && !filter.error && ( - - - - Downloading model… {Math.round((filter.downloadProgress ?? 0) * 100)}% - - - )} - - - {entities ? ( - - ) : ( - {sampleText} - )} - - - - {filter.isGenerating ? ( - - ) : ( - - Detect PII - {inferenceMs !== null && ` · ${inferenceMs} ms`} - - )} - - - {runError && ( - - Run error: {runError} - - )} - - ); -} - -export default function PrivacyFilterScreenWrapper() { - const isFocused = useIsFocused(); - return isFocused ? : null; -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - padding: 16, - backgroundColor: '#fff', - gap: 10, - }, - textBox: { - flex: 1, - borderWidth: 1, - borderColor: '#e0e0e0', - borderRadius: 8, - padding: 10, - }, - sampleText: { - fontSize: 13, - color: '#222', - lineHeight: 19, - }, - highlight: { - fontWeight: '600', - borderRadius: 3, - }, - runButton: { - backgroundColor: ColorPalette.primary, - borderRadius: 8, - paddingVertical: 12, - alignItems: 'center', - }, - runButtonText: { - color: '#fff', - fontSize: 15, - fontWeight: '600', - }, - buttonDisabled: { - opacity: 0.5, - }, - centerBlock: { - alignItems: 'center', - gap: 6, - paddingVertical: 8, - }, - muted: { - color: '#666', - fontSize: 12, - }, - errorBanner: { - backgroundColor: '#fdecea', - borderColor: '#f5c6cb', - borderWidth: 1, - borderRadius: 6, - padding: 8, - }, - errorText: { - color: '#a94442', - fontSize: 12, - }, -}); diff --git a/apps/legacy/llm/assets/fonts/Aeonik-Medium.otf b/apps/legacy/llm/assets/fonts/Aeonik-Medium.otf deleted file mode 100644 index cd9981f439..0000000000 Binary files a/apps/legacy/llm/assets/fonts/Aeonik-Medium.otf and /dev/null differ diff --git a/apps/legacy/llm/assets/fonts/Aeonik-Regular.otf b/apps/legacy/llm/assets/fonts/Aeonik-Regular.otf deleted file mode 100644 index 9bd378ed2b..0000000000 Binary files a/apps/legacy/llm/assets/fonts/Aeonik-Regular.otf and /dev/null differ diff --git a/apps/legacy/llm/assets/icons/adaptive-icon.png b/apps/legacy/llm/assets/icons/adaptive-icon.png deleted file mode 100644 index 03d6f6b6c6..0000000000 Binary files a/apps/legacy/llm/assets/icons/adaptive-icon.png and /dev/null differ diff --git a/apps/legacy/llm/assets/icons/executorch.svg b/apps/legacy/llm/assets/icons/executorch.svg deleted file mode 100644 index e548ea4201..0000000000 --- a/apps/legacy/llm/assets/icons/executorch.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/legacy/llm/assets/icons/favicon.png b/apps/legacy/llm/assets/icons/favicon.png deleted file mode 100644 index e75f697b18..0000000000 Binary files a/apps/legacy/llm/assets/icons/favicon.png and /dev/null differ diff --git a/apps/legacy/llm/assets/icons/icon.png b/apps/legacy/llm/assets/icons/icon.png deleted file mode 100644 index a0b1526fc7..0000000000 Binary files a/apps/legacy/llm/assets/icons/icon.png and /dev/null differ diff --git a/apps/legacy/llm/assets/icons/llama_icon.svg b/apps/legacy/llm/assets/icons/llama_icon.svg deleted file mode 100644 index 99e29696ec..0000000000 --- a/apps/legacy/llm/assets/icons/llama_icon.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/apps/legacy/llm/assets/icons/mic_icon.svg b/apps/legacy/llm/assets/icons/mic_icon.svg deleted file mode 100644 index 955808bfba..0000000000 --- a/apps/legacy/llm/assets/icons/mic_icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/apps/legacy/llm/assets/icons/pause_icon.svg b/apps/legacy/llm/assets/icons/pause_icon.svg deleted file mode 100644 index 2f5164d284..0000000000 --- a/apps/legacy/llm/assets/icons/pause_icon.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/legacy/llm/assets/icons/send_icon.svg b/apps/legacy/llm/assets/icons/send_icon.svg deleted file mode 100644 index dfe227a3b2..0000000000 --- a/apps/legacy/llm/assets/icons/send_icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/apps/legacy/llm/assets/icons/splash.png b/apps/legacy/llm/assets/icons/splash.png deleted file mode 100644 index 0e89705a94..0000000000 Binary files a/apps/legacy/llm/assets/icons/splash.png and /dev/null differ diff --git a/apps/legacy/llm/assets/icons/stop_icon.svg b/apps/legacy/llm/assets/icons/stop_icon.svg deleted file mode 100644 index cb051899fe..0000000000 --- a/apps/legacy/llm/assets/icons/stop_icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/apps/legacy/llm/assets/icons/swm_icon.svg b/apps/legacy/llm/assets/icons/swm_icon.svg deleted file mode 100644 index 8c62f039be..0000000000 --- a/apps/legacy/llm/assets/icons/swm_icon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/apps/legacy/llm/babel.config.js b/apps/legacy/llm/babel.config.js deleted file mode 100644 index d872de3f55..0000000000 --- a/apps/legacy/llm/babel.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - plugins: ['react-native-reanimated/plugin'], - }; -}; diff --git a/apps/legacy/llm/colors.ts b/apps/legacy/llm/colors.ts deleted file mode 100644 index 03defcda9e..0000000000 --- a/apps/legacy/llm/colors.ts +++ /dev/null @@ -1,11 +0,0 @@ -const ColorPalette = { - primary: '#001A72', - seaBlueLight: '#E1F3FA', - seaBlueMedium: '#B5E1F1', - seaBlueDark: '#87CCE8', - blueLight: '#C1C6E5', - blueDark: '#6676AA', - strongPrimary: '#020F3C', -}; - -export default ColorPalette; diff --git a/apps/legacy/llm/components/AnimatedChatLoading.tsx b/apps/legacy/llm/components/AnimatedChatLoading.tsx deleted file mode 100644 index faf750f263..0000000000 --- a/apps/legacy/llm/components/AnimatedChatLoading.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { StyleSheet, View } from 'react-native'; -import Animated, { - interpolateColor, - useAnimatedStyle, - useSharedValue, - withRepeat, - withTiming, -} from 'react-native-reanimated'; -import ColorPalette from '../colors'; - -export default function AnimatedChatLoading() { - const progress = useSharedValue(0); - progress.value = withRepeat(withTiming(1, { duration: 500 }), -1, true); - - const animatedStyle = useAnimatedStyle(() => { - return { - backgroundColor: interpolateColor( - progress.value, - [0, 1], - [ColorPalette.seaBlueLight, ColorPalette.seaBlueMedium, ColorPalette.seaBlueDark] - ), - }; - }); - - return ( - - - - - - ); -} - -const styles = StyleSheet.create({ - messageLoadingContainer: { - flex: 1, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - loadingDot: { - width: 8, - height: 8, - borderRadius: 4, - }, -}); diff --git a/apps/legacy/llm/components/AudioWaveform.tsx b/apps/legacy/llm/components/AudioWaveform.tsx deleted file mode 100644 index cac4035614..0000000000 --- a/apps/legacy/llm/components/AudioWaveform.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { useMemo } from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; -import ColorPalette from '../colors'; - -interface AudioWaveformProps { - buffer: Float32Array | null | undefined; - style?: StyleProp; -} - -const NUM_BARS = 32; - -export default function AudioWaveform({ buffer, style }: AudioWaveformProps) { - const bars = useMemo(() => { - if (!buffer || buffer.length === 0) return null; - const chunkSize = Math.max(1, Math.floor(buffer.length / NUM_BARS)); - const peaks: number[] = []; - let max = 0; - for (let i = 0; i < NUM_BARS; i++) { - const start = i * chunkSize; - const end = Math.min(start + chunkSize, buffer.length); - let peak = 0; - for (let j = start; j < end; j++) { - const v = Math.abs(buffer[j] ?? 0); - if (v > peak) peak = v; - } - peaks.push(peak); - if (peak > max) max = peak; - } - return max > 0 ? peaks.map((p) => p / max) : peaks; - }, [buffer]); - - if (!bars) return null; - - return ( - - {bars.map((amp, i) => ( - - ))} - - ); -} - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - alignItems: 'center', - height: 16, - minWidth: 160, - gap: 2, - }, - bar: { - flex: 1, - borderRadius: 1, - backgroundColor: ColorPalette.blueDark, - opacity: 0.35, - }, -}); diff --git a/apps/legacy/llm/components/ErrorBanner.tsx b/apps/legacy/llm/components/ErrorBanner.tsx deleted file mode 100644 index a5bebc504f..0000000000 --- a/apps/legacy/llm/components/ErrorBanner.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; - -interface ErrorBannerProps { - message: string | null; - onDismiss: () => void; -} - -export default function ErrorBanner({ message, onDismiss }: ErrorBannerProps) { - if (!message) return null; - - return ( - - - {message} - - - ✕ - - - ); -} - -const styles = StyleSheet.create({ - container: { - backgroundColor: '#FEE2E2', - borderLeftWidth: 4, - borderLeftColor: '#EF4444', - borderRadius: 8, - marginHorizontal: 16, - marginVertical: 8, - paddingVertical: 10, - paddingLeft: 12, - paddingRight: 8, - flexDirection: 'row', - alignItems: 'center', - }, - message: { - flex: 1, - color: '#991B1B', - fontSize: 14, - lineHeight: 20, - }, - closeButton: { - padding: 4, - marginLeft: 8, - }, - closeText: { - color: '#991B1B', - fontSize: 16, - fontWeight: '600', - }, -}); diff --git a/apps/legacy/llm/components/MarkdownComponent.tsx b/apps/legacy/llm/components/MarkdownComponent.tsx deleted file mode 100644 index 111019a363..0000000000 --- a/apps/legacy/llm/components/MarkdownComponent.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Platform } from 'react-native'; -import Markdown from 'react-native-markdown-display'; -import ColorPalette from '../colors'; - -interface MarkdownComponentProps { - text: string; -} - -export default function MarkdownComponent({ text }: MarkdownComponentProps) { - const fontSize = Platform.OS === 'ios' ? 16 : 14; - return ( - - {text} - - ); -} diff --git a/apps/legacy/llm/components/MessageItem.tsx b/apps/legacy/llm/components/MessageItem.tsx deleted file mode 100644 index 7e1c17a8e4..0000000000 --- a/apps/legacy/llm/components/MessageItem.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import React, { memo } from 'react'; -import { View, StyleSheet, TouchableOpacity, Text, Image, Platform } from 'react-native'; -import MarkdownComponent from './MarkdownComponent'; -import LlamaIcon from '../assets/icons/llama_icon.svg'; -import ColorPalette from '../colors'; -import { Message } from 'react-native-executorch/legacy'; -import AudioWaveform from './AudioWaveform'; - -interface MessageItemProps { - message: Message; - deleteMessage: () => void; -} - -const MessageItem = memo(({ message, deleteMessage }: MessageItemProps) => { - if (message.role === 'assistant') { - return ( - - - - - - - - - - ); - } - - return ( - - - - {message.mediaPath && ( - - )} - {message.audioWaveform && ( - - )} - - - - ); -}); - -const CloseButton = ({ deleteMessage, role }: { deleteMessage: () => void; role: string }) => { - return ( - - ✕ - - ); -}; - -export default MessageItem; - -const styles = StyleSheet.create({ - aiMessage: { - flexDirection: 'row', - maxWidth: '75%', - alignSelf: 'flex-start', - marginVertical: 8, - alignItems: 'center', - }, - aiMessageContent: { - flex: 1, - }, - userMessageWrapper: { - flexDirection: 'row-reverse', - marginRight: 8, - marginVertical: 8, - maxWidth: '75%', - alignSelf: 'flex-end', - alignItems: 'flex-start', - }, - userMessageBubble: { - flexDirection: 'column', - paddingHorizontal: 12, - paddingVertical: 8, - borderRadius: 8, - backgroundColor: ColorPalette.seaBlueLight, - }, - userMessageImage: { - width: 200, - height: 200, - borderRadius: 6, - marginBottom: 6, - }, - userMessageWaveform: { - marginBottom: 6, - }, - aiMessageIconContainer: { - backgroundColor: ColorPalette.seaBlueLight, - height: 32, - width: 32, - alignItems: 'center', - justifyContent: 'center', - borderRadius: 16, - marginHorizontal: 7, - }, - closeButton: { - borderRadius: 11, - backgroundColor: ColorPalette.blueLight, - alignItems: 'center', - justifyContent: 'center', - width: 22, - height: 22, - }, - closeButtonRight: { - marginLeft: 8, - }, - closeButtonLeft: { - marginRight: 8, - }, - buttonText: { - fontSize: Platform.OS === 'ios' ? 16 : 14, - color: '#000', - }, -}); diff --git a/apps/legacy/llm/components/Messages.tsx b/apps/legacy/llm/components/Messages.tsx deleted file mode 100644 index 18ef88aa7b..0000000000 --- a/apps/legacy/llm/components/Messages.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useRef } from 'react'; -import { ScrollView, StyleSheet, View, Text } from 'react-native'; -import AnimatedChatLoading from './AnimatedChatLoading'; -import LlamaIcon from '../assets/icons/llama_icon.svg'; -import ColorPalette from '../colors'; -import MessageItem from './MessageItem'; -import { Message } from 'react-native-executorch/legacy'; - -interface MessagesComponentProps { - chatHistory: Message[]; - llmResponse: string; - isGenerating: boolean; - deleteMessage: (index: number) => void; -} - -export default function Messages({ - chatHistory, - llmResponse, - isGenerating, - deleteMessage, -}: MessagesComponentProps) { - const scrollViewRef = useRef(null); - - return ( - - scrollViewRef?.current?.scrollToEnd()} - > - true}> - {chatHistory.map((message, index) => ( - deleteMessage(index)} /> - ))} - {isGenerating && ( - - - - - {!llmResponse ? ( - - - - ) : ( - {llmResponse.trim()} - )} - - )} - - - - ); -} - -const styles = StyleSheet.create({ - chatContainer: { flex: 1, width: '100%' }, - aiMessage: { - flexDirection: 'row', - maxWidth: '80%', - alignSelf: 'flex-start', - marginVertical: 8, - }, - messageLoadingContainer: { width: 28 }, - aiMessageIconContainer: { - backgroundColor: ColorPalette.seaBlueLight, - height: 32, - width: 32, - alignItems: 'center', - justifyContent: 'center', - borderRadius: 16, - marginHorizontal: 7, - }, - messageText: { - fontSize: 14, - lineHeight: 19.6, - color: ColorPalette.primary, - fontFamily: 'regular', - }, -}); diff --git a/apps/legacy/llm/components/ModelPicker.tsx b/apps/legacy/llm/components/ModelPicker.tsx deleted file mode 100644 index 9a81bec371..0000000000 --- a/apps/legacy/llm/components/ModelPicker.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Dimensions, - Modal, - ScrollView, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; - -export type ModelOption = { - label: string; - value: T; -}; - -type Props = { - models: ModelOption[]; - selectedModel: T; - onSelect: (model: T) => void; - label?: string; - disabled?: boolean; -}; - -const DROPDOWN_MAX_HEIGHT = 200; - -export function ModelPicker({ models, selectedModel, onSelect, label, disabled }: Props) { - const [open, setOpen] = useState(false); - const [triggerHeight, setTriggerHeight] = useState(0); - const [expandUp, setExpandUp] = useState(false); - const [dropdownTop, setDropdownTop] = useState(0); - const triggerRef = useRef>(null); - const selected = models.find((m) => m.value === selectedModel); - - useEffect(() => { - if (disabled) setOpen(false); - }, [disabled]); - - const handlePress = () => { - if (disabled) return; - if (open) { - setOpen(false); - return; - } - triggerRef.current?.measure( - (_x: number, _y: number, _width: number, height: number, _pageX: number, pageY: number) => { - setTriggerHeight(height); - const spaceBelow = Dimensions.get('window').height - (pageY + height); - setExpandUp(spaceBelow < DROPDOWN_MAX_HEIGHT); - setDropdownTop(pageY); - setOpen(true); - } - ); - }; - - const dropdownStylePosition = expandUp - ? { - bottom: Dimensions.get('window').height - dropdownTop, - left: 12, - right: 12, - } - : { - top: dropdownTop + triggerHeight + 2, - left: 12, - right: 12, - }; - - return ( - <> - - - {label && {label}} - {selected?.label ?? '—'} - {open ? '▲' : '▼'} - - - - {open && ( - setOpen(false)} - animationType="none" - > - setOpen(false)} - /> - - - {models.map((item) => { - const isSelected = item.value === selectedModel; - return ( - { - onSelect(item.value); - setOpen(false); - }} - activeOpacity={0.7} - > - - {item.label} - - - ); - })} - - - - )} - - ); -} - -const styles = StyleSheet.create({ - container: { - marginHorizontal: 12, - marginVertical: 4, - alignSelf: 'stretch', - zIndex: 100, - }, - trigger: { - flexDirection: 'row', - alignItems: 'center', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 10, - backgroundColor: '#f5f5f5', - }, - triggerDisabled: { - opacity: 0.4, - }, - label: { - fontSize: 12, - color: '#888', - marginRight: 6, - }, - triggerText: { - flex: 1, - fontSize: 14, - color: '#001A72', - fontWeight: '500', - }, - chevron: { - fontSize: 10, - color: '#888', - marginLeft: 6, - }, - modalBackdrop: { - flex: 1, - backgroundColor: 'rgba(0, 0, 0, 0.3)', - }, - dropdown: { - position: 'absolute', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - backgroundColor: '#fff', - maxHeight: DROPDOWN_MAX_HEIGHT, - zIndex: 1000, - elevation: 5, - shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.15, - shadowRadius: 6, - }, - option: { - paddingHorizontal: 12, - paddingVertical: 10, - borderBottomWidth: 1, - borderBottomColor: '#f0f0f0', - }, - optionSelected: { - backgroundColor: '#e8ecf8', - }, - optionText: { - fontSize: 14, - color: '#333', - }, - optionTextSelected: { - color: '#001A72', - fontWeight: '600', - }, -}); diff --git a/apps/legacy/llm/components/Spinner.tsx b/apps/legacy/llm/components/Spinner.tsx deleted file mode 100644 index f436869ab8..0000000000 --- a/apps/legacy/llm/components/Spinner.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import { View, Text, StyleSheet, ActivityIndicator, Modal } from 'react-native'; -import ColorPalette from '../colors'; - -interface SpinnerProps { - visible: boolean; - textContent: string; -} - -const Spinner = ({ visible, textContent }: SpinnerProps) => { - return ( - - - - - {textContent} - - - - ); -}; - -const styles = StyleSheet.create({ - overlay: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'rgba(0, 0, 0, 0.1)', - }, - container: { - padding: 25, - alignItems: 'center', - justifyContent: 'center', - }, - text: { - marginTop: 15, - color: ColorPalette.primary, - fontSize: 18, - fontWeight: 'bold', - }, -}); - -export default Spinner; diff --git a/apps/legacy/llm/components/StatsBar.tsx b/apps/legacy/llm/components/StatsBar.tsx deleted file mode 100644 index 20bad6cfa1..0000000000 --- a/apps/legacy/llm/components/StatsBar.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import { View, Text, StyleSheet } from 'react-native'; -import { LLMStats } from '../hooks/useLLMStats'; - -interface Props { - stats: LLMStats | null; -} - -export function StatsBar({ stats }: Props) { - if (!stats) return null; - - return ( - - TTFT: {stats.ttft} ms - · - {stats.tokensPerSec} tok/s - · - {stats.totalTokens} tokens - - ); -} - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - gap: 8, - paddingVertical: 6, - }, - stat: { - fontSize: 13, - color: '#334155', - fontWeight: '500', - }, - separator: { - fontSize: 13, - color: '#94A3B8', - }, -}); diff --git a/apps/legacy/llm/components/SuggestedPrompts.tsx b/apps/legacy/llm/components/SuggestedPrompts.tsx deleted file mode 100644 index 1303f28e30..0000000000 --- a/apps/legacy/llm/components/SuggestedPrompts.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; -import ColorPalette from '../colors'; - -interface Props { - prompts: string[]; - onSelect: (prompt: string) => void; -} - -export default function SuggestedPrompts({ prompts, onSelect }: Props) { - return ( - - {prompts.map((prompt, i) => ( - onSelect(prompt)}> - {prompt} - - ))} - - ); -} - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'center', - gap: 8, - paddingHorizontal: 16, - paddingTop: 16, - }, - chip: { - borderWidth: 1, - borderColor: ColorPalette.blueLight, - borderRadius: 20, - paddingHorizontal: 14, - paddingVertical: 8, - backgroundColor: '#fafbff', - }, - chipText: { - fontFamily: 'regular', - fontSize: 13, - color: ColorPalette.primary, - }, -}); diff --git a/apps/legacy/llm/components/llmModels.ts b/apps/legacy/llm/components/llmModels.ts deleted file mode 100644 index e7334bfc56..0000000000 --- a/apps/legacy/llm/components/llmModels.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { - models, - LLAMA3_2_1B_QLORA, - LLAMA3_2_3B_QLORA, - LLMProps, -} from 'react-native-executorch/legacy'; -import { ModelOption } from './ModelPicker'; -const llm = models.llm; - -export type LLMModelSources = LLMProps['model']; - -export const LLM_MODELS: ModelOption[] = [ - //Gemma 4 - { - label: 'Gemma 4 E2B', - value: llm.gemma4_e2b(), - }, - // Llama 3.2 - { - label: 'Llama 3.2 1B', - value: llm.llama3_2_1b({ quant: false }), - }, - { label: 'Llama 3.2 1B QLoRA', value: LLAMA3_2_1B_QLORA }, - { label: 'Llama 3.2 1B SpinQuant', value: llm.llama3_2_1b() }, - { - label: 'Llama 3.2 3B', - value: llm.llama3_2_3b({ quant: false }), - }, - { label: 'Llama 3.2 3B QLoRA', value: LLAMA3_2_3B_QLORA }, - { label: 'Llama 3.2 3B SpinQuant', value: llm.llama3_2_3b() }, - // Qwen3 - { - label: 'Qwen3 0.6B', - value: llm.qwen3_0_6b({ quant: false }), - }, - { label: 'Qwen3 0.6B Quantized', value: llm.qwen3_0_6b() }, - { - label: 'Qwen3 1.7B', - value: llm.qwen3_1_7b({ quant: false }), - }, - { label: 'Qwen3 1.7B Quantized', value: llm.qwen3_1_7b() }, - { label: 'Qwen3 4B', value: llm.qwen3_4b({ quant: false }) }, - { label: 'Qwen3 4B Quantized', value: llm.qwen3_4b() }, - // Hammer 2.1 - { - label: 'Hammer 2.1 0.5B', - value: llm.hammer2_1_0_5b({ quant: false }), - }, - { - label: 'Hammer 2.1 0.5B Quantized', - value: llm.hammer2_1_0_5b(), - }, - { - label: 'Hammer 2.1 1.5B', - value: llm.hammer2_1_1_5b({ quant: false }), - }, - { - label: 'Hammer 2.1 1.5B Quantized', - value: llm.hammer2_1_1_5b(), - }, - { - label: 'Hammer 2.1 3B', - value: llm.hammer2_1_3b({ quant: false }), - }, - { - label: 'Hammer 2.1 3B Quantized', - value: llm.hammer2_1_3b(), - }, - // SmolLM2 - { - label: 'SmolLM2 135M', - value: llm.smollm2_1_135m({ quant: false }), - }, - { - label: 'SmolLM2 135M Quantized', - value: llm.smollm2_1_135m(), - }, - { - label: 'SmolLM2 360M', - value: llm.smollm2_1_360m({ quant: false }), - }, - { - label: 'SmolLM2 360M Quantized', - value: llm.smollm2_1_360m(), - }, - { - label: 'SmolLM2 1.7B', - value: llm.smollm2_1_1_7b({ quant: false }), - }, - { - label: 'SmolLM2 1.7B Quantized', - value: llm.smollm2_1_1_7b(), - }, - // Qwen2.5 - { - label: 'Qwen2.5 0.5B', - value: llm.qwen2_5_0_5b({ quant: false }), - }, - { - label: 'Qwen2.5 0.5B Quantized', - value: llm.qwen2_5_0_5b(), - }, - { - label: 'Qwen2.5 1.5B', - value: llm.qwen2_5_1_5b({ quant: false }), - }, - { - label: 'Qwen2.5 1.5B Quantized', - value: llm.qwen2_5_1_5b(), - }, - { - label: 'Qwen2.5 3B', - value: llm.qwen2_5_3b({ quant: false }), - }, - { label: 'Qwen2.5 3B Quantized', value: llm.qwen2_5_3b() }, - // Qwen3.5 - { label: 'Qwen3.5 0.8B Quantized', value: llm.qwen3_5_0_8b() }, - { label: 'Qwen3.5 2B Quantized', value: llm.qwen3_5_2b() }, - // Phi-4 - { - label: 'Phi-4 Mini 4B', - value: llm.phi_4_mini_4b({ quant: false }), - }, - { - label: 'Phi-4 Mini 4B Quantized', - value: llm.phi_4_mini_4b(), - }, - // LFM2.5 - { - label: 'LFM2.5 350M', - value: llm.lfm2_5_350m({ quant: false }), - }, - { label: 'LFM2.5 350M Quantized', value: llm.lfm2_5_350m() }, - { - label: 'LFM2.5 1.2B Instruct', - value: llm.lfm2_5_1_2b_instruct({ quant: false }), - }, - { - label: 'LFM2.5 1.2B Instruct Quantized', - value: llm.lfm2_5_1_2b_instruct(), - }, - // Bielik v3.0 - { - label: 'Bielik v3.0 1.5B', - value: llm.bielik_v3_0_1_5b({ quant: false }), - }, - { - label: 'Bielik v3.0 1.5B Quantized', - value: llm.bielik_v3_0_1_5b(), - }, -]; diff --git a/apps/legacy/llm/context.ts b/apps/legacy/llm/context.ts deleted file mode 100644 index 0eb0560768..0000000000 --- a/apps/legacy/llm/context.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react'; - -export const GeneratingContext = createContext({ - setGlobalGenerating: (_newState: boolean) => {}, -}); diff --git a/apps/legacy/llm/declarations.d.ts b/apps/legacy/llm/declarations.d.ts deleted file mode 100644 index 85e178f497..0000000000 --- a/apps/legacy/llm/declarations.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module '*.svg' { - import { SvgProps } from 'react-native-svg'; - const content: React.FV; - export default content; -} diff --git a/apps/legacy/llm/hooks/useLLMStats.ts b/apps/legacy/llm/hooks/useLLMStats.ts deleted file mode 100644 index b3e21bb5fa..0000000000 --- a/apps/legacy/llm/hooks/useLLMStats.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; - -export interface LLMStats { - ttft: number; - tokensPerSec: number; - totalTokens: number; -} - -export function useLLMStats(response: string, isGenerating: boolean, totalTokens: number) { - const sendTimeRef = useRef(null); - const firstTokenTimeRef = useRef(null); - const lastResponseRef = useRef(''); - const [stats, setStats] = useState(null); - - useEffect(() => { - if (isGenerating && response.length > 0) { - lastResponseRef.current = response; - if (firstTokenTimeRef.current === null && sendTimeRef.current !== null) { - firstTokenTimeRef.current = Date.now(); - } - } - }, [response, isGenerating, totalTokens]); - - useEffect(() => { - if (!isGenerating && sendTimeRef.current !== null && firstTokenTimeRef.current !== null) { - const endTime = Date.now(); - const ttft = firstTokenTimeRef.current - sendTimeRef.current; - const totalTime = (endTime - firstTokenTimeRef.current) / 1000; - const tokensPerSec = totalTime > 0 ? Math.round(totalTokens / totalTime) : 0; - setStats({ ttft, tokensPerSec, totalTokens }); - sendTimeRef.current = null; - firstTokenTimeRef.current = null; - } - }, [isGenerating, totalTokens]); - - const onMessageSend = () => { - sendTimeRef.current = Date.now(); - firstTokenTimeRef.current = null; - lastResponseRef.current = ''; - setStats(null); - }; - - return { stats, onMessageSend }; -} diff --git a/apps/legacy/llm/index.ts b/apps/legacy/llm/index.ts deleted file mode 100644 index 3f443dcf95..0000000000 --- a/apps/legacy/llm/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerRootComponent } from 'expo'; - -import App from './app'; - -// registerRootComponent calls AppRegistry.registerComponent('main', () => App); -// It also ensures that whether you load the app in Expo Go or in a native build, -// the environment is set up appropriately -registerRootComponent(App); diff --git a/apps/legacy/llm/metro.config.js b/apps/legacy/llm/metro.config.js deleted file mode 100644 index f58bbc2131..0000000000 --- a/apps/legacy/llm/metro.config.js +++ /dev/null @@ -1,19 +0,0 @@ -const { getDefaultConfig } = require('expo/metro-config'); - -const config = getDefaultConfig(__dirname); - -const { transformer, resolver } = config; - -config.transformer = { - ...transformer, - babelTransformerPath: require.resolve('react-native-svg-transformer/expo'), -}; -config.resolver = { - ...resolver, - assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'), - sourceExts: [...resolver.sourceExts, 'svg'], -}; - -config.resolver.assetExts.push('pte'); - -module.exports = config; diff --git a/apps/legacy/llm/package.json b/apps/legacy/llm/package.json deleted file mode 100644 index b0385d4edb..0000000000 --- a/apps/legacy/llm/package.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "legacy-llm", - "version": "1.0.0", - "main": "expo-router/entry", - "scripts": { - "start": "expo start", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "typecheck": "tsc", - "lint": "eslint . --ext .ts,.tsx --fix" - }, - "dependencies": { - "@expo/log-box": "~56.0.12", - "@react-native/metro-config": "^0.86.0", - "@react-navigation/drawer": "^7.9.4", - "@react-navigation/native": "^7.2.2", - "expo": "~57.0.28", - "expo-brightness": "~57.0.2", - "expo-build-properties": "~57.0.22", - "expo-calendar": "~57.0.5", - "expo-constants": "~57.0.21", - "expo-document-picker": "~57.0.3", - "expo-font": "~57.0.4", - "expo-linking": "~57.0.12", - "expo-router": "~57.0.26", - "expo-status-bar": "~57.0.1", - "metro-config": "^0.84.0", - "react": "19.2.3", - "react-native": "0.86.3", - "react-native-audio-api": "0.13.6", - "react-native-device-info": "^15.0.2", - "react-native-executorch": "workspace:*", - "react-native-executorch-expo-resource-fetcher": "workspace:*", - "react-native-gesture-handler": "~2.32.0", - "react-native-image-picker": "^8.2.1", - "react-native-loading-spinner-overlay": "^3.0.1", - "react-native-markdown-display": "^7.0.2", - "react-native-reanimated": "4.7.1", - "react-native-safe-area-context": "~5.7.0", - "react-native-screens": "~4.26.0", - "react-native-svg": "15.15.4", - "react-native-svg-transformer": "^1.5.3", - "react-native-worklets": "0.13.0" - }, - "devDependencies": { - "@babel/core": "^7.29.7", - "@types/react": "~19.2.14", - "@types/react-refresh": "^0", - "babel-preset-expo": "~57.0.15", - "react-refresh": "^0.18.0" - }, - "private": true -} diff --git a/apps/legacy/llm/tsconfig.json b/apps/legacy/llm/tsconfig.json deleted file mode 100644 index fc43a7226d..0000000000 --- a/apps/legacy/llm/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "jsx": "react-native", - "lib": ["es2022"], - "moduleResolution": "bundler", - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "esnext", - "paths": { - "react-native-executorch/legacy": ["../../packages/react-native-executorch/legacy/src"], - "react-native-executorch-expo-resource-fetcher": ["../../packages/expo-resource-fetcher"], - "zod": ["../../node_modules/zod"], - "zod/*": ["../../node_modules/zod/*"] - } - }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["**/node_modules", "**/Pods", "**/__tests__"] -} diff --git a/apps/legacy/llm/utils/piiMatching.ts b/apps/legacy/llm/utils/piiMatching.ts deleted file mode 100644 index 66f9e2ec99..0000000000 --- a/apps/legacy/llm/utils/piiMatching.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { PiiEntity } from 'react-native-executorch/legacy'; - -/** - * A detected entity span pinned to a character range in the source text. - */ -export interface EntityMatch { - start: number; - end: number; - label: string; -} - -const escapeRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - -// Word-boundary-anchored search so e.g. "John" doesn't match the "John" -// inside "Johnson". -function findWordBounded(source: string, needle: string, from: number): number { - const re = new RegExp(`\\b${escapeRegex(needle)}\\b`); - const match = re.exec(source.slice(from)); - return match ? from + match.index : -1; -} - -/** - * Map detected entities (which carry decoded text only) onto character - * ranges in the source text by scanning forward. - * - * The native runner's `text` field is the BPE-detokenized form of the - * entity, which often differs from the source by whitespace, punctuation - * spacing, or stripped specials. Strategy: - * 1) Try a word-bounded match for the whole entity from the cursor onward. - * 2) On miss, fall back to each non-trivial word from the entity text - * individually so we still highlight most of the span. - * - * Order-preserving: cursor advances after each successful match so - * duplicate strings resolve left-to-right. - * @param source - The full text the model was run against. - * @param entities - Entities returned by the native runner. - * @returns Sorted, non-overlapping char-range matches. - */ -export function matchEntities(source: string, entities: PiiEntity[]): EntityMatch[] { - const matches: EntityMatch[] = []; - let cursor = 0; - for (const e of entities) { - if (!e.text) continue; - const exact = findWordBounded(source, e.text, cursor); - if (exact !== -1) { - matches.push({ - start: exact, - end: exact + e.text.length, - label: e.label, - }); - cursor = exact + e.text.length; - continue; - } - const words = e.text.split(/\s+/).filter((w) => w.length > 1); - let localCursor = cursor; - for (const w of words) { - const idx = findWordBounded(source, w, localCursor); - if (idx === -1) continue; - matches.push({ start: idx, end: idx + w.length, label: e.label }); - localCursor = idx + w.length; - } - if (localCursor > cursor) cursor = localCursor; - } - matches.sort((a, b) => a.start - b.start); - return matches; -} - -export interface Segment { - text: string; - label: string | null; -} - -/** - * Slice the source text into alternating plain / labeled runs based on the - * matched entity ranges. Overlaps are dropped (the earlier match wins). - * @param source - The full text to slice. - * @param matches - Char-range matches from {@link matchEntities}. - * @returns An ordered array of segments covering the entire source. - */ -export function buildSegments(source: string, matches: EntityMatch[]): Segment[] { - const segments: Segment[] = []; - let pos = 0; - for (const m of matches) { - if (m.start < pos) continue; - if (m.start > pos) { - segments.push({ text: source.slice(pos, m.start), label: null }); - } - segments.push({ text: source.slice(m.start, m.end), label: m.label }); - pos = m.end; - } - if (pos < source.length) { - segments.push({ text: source.slice(pos), label: null }); - } - return segments; -} - -/** - * Pastel color palette + stable label-to-color mapping. Same label always - * gets the same color across renders / runs. - */ -const PALETTE = [ - '#ffd4a8', - '#b8e1ff', - '#d4c5f9', - '#c3e8c3', - '#ffe8b8', - '#f8c6c6', - '#e3c8a8', - '#ff9aa2', - '#b6e3d4', - '#ffd6e0', - '#cdb4db', - '#ffc8a2', - '#a2d2ff', - '#bde0fe', - '#fcd5ce', -]; - -export function colorForLabel(label: string): string { - let hash = 0; - for (let i = 0; i < label.length; i++) { - // eslint-disable-next-line no-bitwise - hash = (hash * 31 + label.charCodeAt(i)) | 0; - } - return PALETTE[Math.abs(hash) % PALETTE.length] as string; -} diff --git a/apps/legacy/llm/utils/tools.ts b/apps/legacy/llm/utils/tools.ts deleted file mode 100644 index 730a1937e9..0000000000 --- a/apps/legacy/llm/utils/tools.ts +++ /dev/null @@ -1,198 +0,0 @@ -import * as Calendar from 'expo-calendar'; -import * as Brightness from 'expo-brightness'; -import { PermissionStatus } from 'expo'; -import { clamp } from 'react-native-reanimated'; -import { Platform } from 'react-native'; -import { ToolCall } from 'react-native-executorch/legacy'; - -export const executeTool: (call: ToolCall) => Promise = async (call) => { - switch (call.toolName) { - case 'brightness': - return await brightness(call); - case 'read_calendar': - return await readCalendar(call); - case 'add_event_to_calendar': - return await addEventToCalendar(call); - default: - console.error(`Wrong function! We don't handle it!`); - return null; - } -}; - -export const TOOL_DEFINITIONS_PHONE = [ - { - name: 'brightness', - description: - 'Change screen brightness. Change can be relative (higher/lower) or set to minimal or maximal.', - parameters: { - type: 'dict', - properties: { - relativeChange: { - type: 'number', - description: - 'Relative change of brightness (from 0 to 100). Change should be negative if user asks for less bright screen.', - }, - targetBrightness: { - type: 'number', - description: 'Relative change of brightness (from 0 to 100).', - }, - }, - }, - }, - { - name: 'read_calendar', - description: - 'Read calendar events from now up to given point in time. If nothing is specified leave both empty.', - parameters: { - type: 'dict', - properties: { - timeStart: { - type: 'string', - description: 'Date and time from which we want to read calendar.', - }, - timeEnd: { - type: 'string', - description: 'Date and time to which we want to read calendar.', - }, - }, - required: [], - }, - }, - { - name: 'add_event_to_calendar', - description: 'Schedules event in your calendar at given time.', - parameters: { - type: 'dict', - properties: { - time: { - type: 'string', - description: 'Date and time of an event.', - }, - title: { - type: 'string', - description: 'Title of an event', - }, - }, - required: ['time', 'title'], - }, - }, -]; - -const brightness = async (call: ToolCall) => { - console.log('Changing brightness!', call); - const { status } = await Brightness.getPermissionsAsync(); - if (status !== Brightness.PermissionStatus.GRANTED) { - return 'Brightness permission denied. Inform the user they need to grant brightness access in the app.'; - } - if ('targetBrightness' in call.arguments && typeof call.arguments.targetBrightness === 'number') { - await Brightness.setBrightnessAsync(call.arguments.targetBrightness / 100); - } else if ( - 'relativeChange' in call.arguments && - typeof call.arguments.relativeChange === 'number' - ) { - await Brightness.setBrightnessAsync( - clamp((await Brightness.getBrightnessAsync()) + call.arguments.relativeChange / 100, 0, 1) - ); - } - return null; -}; - -const readCalendar = async (call: ToolCall) => { - console.log('Reading calendar!', call); - const { status } = await Calendar.getCalendarPermissionsAsync(); - if (status !== PermissionStatus.GRANTED) { - return 'Calendar permission denied. Inform the user they need to grant calendar access in the app.'; - } - - let startTime = Date.parse( - 'timeStart' in call.arguments && typeof call.arguments.timeStart === 'string' - ? call.arguments.timeStart - : '' - ); - let endTime = Date.parse( - 'timeEnd' in call.arguments && typeof call.arguments.timeEnd === 'string' - ? call.arguments.timeEnd - : '' - ); - - if (startTime === endTime || (Number.isNaN(startTime) && Number.isNaN(endTime))) { - // default to today for empty function calls - let date; - if (Number.isNaN(startTime)) { - date = new Date(); - } else { - date = new Date(startTime); - } - // Set the time to 00:00:00 for the start of the day - startTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0).valueOf(); - - // Set the time to 23:59:59 for the end of the day - endTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59).valueOf(); - } else if (Number.isNaN(startTime) || Number.isNaN(endTime)) { - if (Number.isNaN(startTime)) { - const today = new Date(); - startTime = new Date( - today.getFullYear(), - today.getMonth(), - today.getDate(), - 0, - 0, - 0 - ).valueOf(); - } else if (Number.isNaN(endTime)) { - const today = new Date(); - - endTime = new Date( - today.getFullYear(), - today.getMonth(), - today.getDate(), - 23, - 59, - 59 - ).valueOf(); - } - } - const startDate = new Date(startTime); - const endDate = new Date(endTime); - const calendars = await Calendar.getCalendarsAsync( - Platform.OS === 'ios' ? Calendar.EntityTypes.EVENT : undefined - ); - const events = await Calendar.getEventsAsync( - calendars.map((calendar) => calendar.id), - startDate, - endDate - ); - - const eventsStringRepresentation = events.map( - (event) => `${event.title}, from: ${event.startDate}, to: ${event.endDate}` - ); - return eventsStringRepresentation.join('\n'); -}; - -const addEventToCalendar = async (call: ToolCall) => { - console.log('Adding event to calendar!', call); - const { status } = await Calendar.getCalendarPermissionsAsync(); - if (status !== PermissionStatus.GRANTED) { - return 'Calendar permission denied. Inform the user they need to grant calendar access in the app.'; - } - if ( - 'time' in call.arguments && - typeof call.arguments.time === 'string' && - 'title' in call.arguments && - typeof call.arguments.title === 'string' - ) { - const calendars = await Calendar.getCalendarsAsync( - Platform.OS === 'ios' ? Calendar.EntityTypes.EVENT : undefined - ); - let startDate = new Date(Date.parse(call.arguments.time)); - const endDate = new Date(startDate); - endDate.setHours(endDate.getHours() + 1); - - await Calendar.createEventAsync(calendars[0].id, { - title: call.arguments.title, - startDate: startDate, - endDate: endDate, - }); - } - return null; -}; diff --git a/apps/legacy/speech/.gitignore b/apps/legacy/speech/.gitignore deleted file mode 100644 index 4a2f68b457..0000000000 --- a/apps/legacy/speech/.gitignore +++ /dev/null @@ -1,39 +0,0 @@ -# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files - -# dependencies -node_modules/ - -# Expo -.expo/ -dist/ -web-build/ -expo-env.d.ts - -# Native -*.orig.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision - -# Metro -.metro-health-check* - -# debug -npm-debug.* -yarn-debug.* -yarn-error.* - -# macOS -.DS_Store -*.pem - -# local env files -.env*.local - -# typescript -*.tsbuildinfo - -.yarn - diff --git a/apps/legacy/speech/App.tsx b/apps/legacy/speech/App.tsx deleted file mode 100644 index 3bbd8263c1..0000000000 --- a/apps/legacy/speech/App.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, { useState } from 'react'; -import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; -import { TextToSpeechScreen } from './screens/TextToSpeechScreen'; -import { SpeechToTextScreen } from './screens/SpeechToTextScreen'; -import { VoiceActivityDetectionScreen } from './screens/VoiceActivityDetectionScreen'; -import ColorPalette from './colors'; -import ExecutorchLogo from './assets/executorch.svg'; -import { Quiz } from './screens/Quiz'; -import { TextToSpeechLLMScreen } from './screens/TextToSpeechLLMScreen'; -import { initExecutorch } from 'react-native-executorch/legacy'; -import { ExpoResourceFetcher } from 'react-native-executorch-expo-resource-fetcher'; - -initExecutorch({ - resourceFetcher: ExpoResourceFetcher, -}); - -export default function App() { - const [currentScreen, setCurrentScreen] = useState< - 'menu' | 'speech-to-text' | 'text-to-speech' | 'quiz' | 'text-to-speech-llm' | 'vad' - >('menu'); - - const goToMenu = () => setCurrentScreen('menu'); - - if (currentScreen === 'text-to-speech') { - return ; - } - - if (currentScreen === 'speech-to-text') { - return ; - } - - if (currentScreen === 'vad') { - return ; - } - - if (currentScreen === 'quiz') { - return ; - } - - if (currentScreen === 'text-to-speech-llm') { - return ; - } - - return ( - - - Select a demo model - - setCurrentScreen('speech-to-text')}> - Speech to Text - - setCurrentScreen('vad')}> - Voice Activity Detection - - setCurrentScreen('text-to-speech')}> - Text to Speech - - setCurrentScreen('quiz')}> - Text to Speech - Quiz - - setCurrentScreen('text-to-speech-llm')} - > - Text to Speech - LLM Streaming - - - - ); -} - -export const fontSizes = { - xxl: 34, - xl: 22, - lg: 18, - md: 16, - sm: 14, - xs: 12, - xxs: 10, -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#fff', - }, - headerText: { - fontSize: fontSizes.lg, - color: ColorPalette.strongPrimary, - margin: 20, - }, - buttonContainer: { - width: '80%', - justifyContent: 'space-evenly', - marginBottom: 20, - }, - button: { - backgroundColor: ColorPalette.strongPrimary, - borderRadius: 8, - padding: 10, - alignItems: 'center', - marginBottom: 10, - }, - buttonText: { - color: 'white', - fontSize: fontSizes.md, - }, -}); diff --git a/apps/legacy/speech/app.json b/apps/legacy/speech/app.json deleted file mode 100644 index 734503bb04..0000000000 --- a/apps/legacy/speech/app.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "expo": { - "name": "legacy-speech", - "slug": "legacy-speech", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icon.png", - "userInterfaceStyle": "light", - "newArchEnabled": true, - "scheme": "rne-legacy-speech", - "splash": { - "image": "./assets/splash-icon.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.anonymous.legacyspeech", - "infoPlist": { - "NSMicrophoneUsageDescription": "This app needs access to your microphone to record audio." - }, - "entitlements": { - "com.apple.developer.kernel.increased-memory-limit": true - } - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", - "backgroundColor": "#ffffff" - }, - "package": "com.anonymous.legacyspeech", - "permissions": [ - "android.permission.RECORD_AUDIO", - "android.permission.MODIFY_AUDIO_SETTINGS", - "android.permission.FOREGROUND_SERVICE", - "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" - ] - }, - "web": { - "favicon": "./assets/favicon.png" - }, - "plugins": [ - "expo-font", - [ - "react-native-audio-api", - { - "iosBackgroundMode": true, - "iosMicrophonePermission": "This app requires access to the microphone to record audio.", - "androidPermissions": [ - "android.permission.MODIFY_AUDIO_SETTINGS", - "android.permission.FOREGROUND_SERVICE", - "android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK", - "android.permission.RECORD_AUDIO" - ], - "androidForegroundService": true, - "androidFSTypes": ["mediaPlayback", "microphone"] - } - ], - [ - "expo-build-properties", - { - "ios": { - "deploymentTarget": "17.0" - } - } - ] - ] - } -} diff --git a/apps/legacy/speech/assets/adaptive-icon.png b/apps/legacy/speech/assets/adaptive-icon.png deleted file mode 100644 index 03d6f6b6c6..0000000000 Binary files a/apps/legacy/speech/assets/adaptive-icon.png and /dev/null differ diff --git a/apps/legacy/speech/assets/executorch.svg b/apps/legacy/speech/assets/executorch.svg deleted file mode 100644 index e548ea4201..0000000000 --- a/apps/legacy/speech/assets/executorch.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/legacy/speech/assets/favicon.png b/apps/legacy/speech/assets/favicon.png deleted file mode 100644 index e75f697b18..0000000000 Binary files a/apps/legacy/speech/assets/favicon.png and /dev/null differ diff --git a/apps/legacy/speech/assets/icon.png b/apps/legacy/speech/assets/icon.png deleted file mode 100644 index a0b1526fc7..0000000000 Binary files a/apps/legacy/speech/assets/icon.png and /dev/null differ diff --git a/apps/legacy/speech/assets/quiz-data.ts b/apps/legacy/speech/assets/quiz-data.ts deleted file mode 100644 index 10158b39f3..0000000000 --- a/apps/legacy/speech/assets/quiz-data.ts +++ /dev/null @@ -1,844 +0,0 @@ -// --- Data --- -// cspell:ignoreRegExp /export const QUESTIONS = \[[\s\S]*?\];/ -export const QUESTIONS = [ - { - q: 'What is the capital of Japan?', - a: ['Beijing', 'Seoul', 'Tokyo', 'Kyoto'], - c: 2, - e: 'Tokyo is the capital of Japan.', - context: - "Tokyo, formerly known as Edo, became the capital of Japan in 1868. It is one of the world's most populous cities and a major center for finance, culture, and technology. Tokyo hosts the Imperial Palace and is famous for its blend of traditional and modern architecture.", - }, - { - q: 'Who wrote "Romeo and Juliet"?', - a: ['Mark Twain', 'William Shakespeare', 'Charles Dickens', 'Jane Austen'], - c: 1, - e: 'William Shakespeare wrote Romeo and Juliet.', - context: - 'William Shakespeare was an English playwright and poet, widely regarded as the greatest writer in the English language. "Romeo and Juliet" is one of his most famous tragedies, exploring themes of love, fate, and family conflict in Renaissance Italy.', - }, - { - q: 'Which planet has the most moons in our solar system?', - a: ['Mars', 'Earth', 'Jupiter', 'Venus'], - c: 2, - e: 'Jupiter has the most known moons.', - context: - 'Jupiter has over 90 known moons, including Ganymede, the largest moon in the solar system. Its strong gravity allows it to capture many objects as moons. Saturn also has many moons, but Jupiter currently holds the record.', - }, - { - q: 'What gas do plants use for photosynthesis?', - a: ['Oxygen', 'Carbon dioxide', 'Nitrogen', 'Helium'], - c: 1, - e: 'Plants use carbon dioxide for photosynthesis.', - context: - 'Photosynthesis is the process by which plants convert carbon dioxide and water into glucose and oxygen using sunlight. This process is essential for life on Earth, as it provides oxygen and food for many organisms.', - }, - { - q: 'Who painted the Sistine Chapel ceiling?', - a: ['Raphael', 'Leonardo da Vinci', 'Michelangelo', 'Donatello'], - c: 2, - e: 'Michelangelo painted the Sistine Chapel ceiling.', - context: - 'Michelangelo was an Italian Renaissance artist known for his sculptures and paintings. The Sistine Chapel ceiling, painted between 1508 and 1512, features scenes from the Book of Genesis and is considered a masterpiece of Western art.', - }, - { - q: 'What is the largest continent by land area?', - a: ['Africa', 'Asia', 'Europe', 'Antarctica'], - c: 1, - e: 'Asia is the largest continent by land area.', - context: - "Asia covers about 30% of Earth's land area and is home to more than half of the world's population. It includes diverse regions such as the Middle East, South Asia, East Asia, and Siberia.", - }, - { - q: 'Which element has the chemical symbol "O"?', - a: ['Gold', 'Oxygen', 'Silver', 'Iron'], - c: 1, - e: 'O is the symbol for oxygen.', - context: - "Oxygen is a vital element for life, making up about 21% of Earth's atmosphere. It is essential for respiration in most living organisms and is highly reactive, forming compounds with many other elements.", - }, - { - q: 'In which year did the first man land on the moon?', - a: ['1969', '1959', '1979', '1965'], - c: 0, - e: 'The first moon landing was in 1969.', - context: - 'Apollo 11 was the mission that first landed humans on the Moon. Neil Armstrong and Buzz Aldrin walked on the lunar surface, while Michael Collins orbited above. The event marked a major milestone in space exploration.', - }, - { - q: 'What is the smallest prime number?', - a: ['0', '1', '2', '3'], - c: 2, - e: '2 is the smallest prime number.', - context: - 'A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself. 2 is the only even prime number, as all other even numbers are divisible by 2.', - }, - { - q: 'Which ocean lies on the east coast of the United States?', - a: ['Pacific', 'Atlantic', 'Indian', 'Arctic'], - c: 1, - e: 'The Atlantic Ocean is on the east coast.', - context: - 'The Atlantic Ocean is the second-largest ocean and separates North America from Europe and Africa. Major cities like New York, Miami, and Boston are located along its coast.', - }, - { - q: 'What currency is used in the United Kingdom?', - a: ['Euro', 'Pound', 'Dollar', 'Franc'], - c: 1, - e: 'The British pound is the currency of the UK.', - context: - 'The pound sterling, commonly known as the pound, is the official currency of the United Kingdom. It is one of the oldest currencies still in use and is subdivided into 100 pence.', - }, - { - q: 'Who discovered penicillin?', - a: ['Alexander Fleming', 'Marie Curie', 'Louis Pasteur', 'Gregor Mendel'], - c: 0, - e: 'Alexander Fleming discovered penicillin.', - context: - 'Penicillin was the first true antibiotic discovered by Alexander Fleming in 1928. It has saved countless lives by effectively treating bacterial infections.', - }, - { - q: 'What is the human body organ that pumps blood?', - a: ['Liver', 'Lung', 'Heart', 'Kidney'], - c: 2, - e: 'The heart pumps blood in the body.', - context: - 'The heart is a muscular organ about the size of a fist, located slightly left of the center of the chest. It pumps blood through the circulatory system, supplying oxygen and nutrients to the body.', - }, - { - q: 'Which country is known for the pyramids at Giza?', - a: ['Mexico', 'Peru', 'Egypt', 'Sudan'], - c: 2, - e: 'Egypt is famous for the pyramids at Giza.', - context: - 'The Giza pyramid complex is one of the most famous archaeological sites in the world. The Great Pyramid of Giza is the largest pyramid in Egypt and one of the Seven Wonders of the Ancient World.', - }, - { - q: 'Which language has the most native speakers worldwide?', - a: ['English', 'Spanish', 'Mandarin', 'Hindi'], - c: 2, - e: 'Mandarin Chinese has the most native speakers.', - context: - 'Mandarin is the most widely spoken language in the world, with over a billion native speakers. It is the official language of China and Taiwan, and one of the official languages of Singapore.', - }, - { - q: 'What is H2O commonly called?', - a: ['Salt', 'Hydrogen', 'Water', 'Oxygen'], - c: 2, - e: 'H2O is the chemical formula for water.', - context: - "Water is essential for all known forms of life. It covers about 71% of Earth's surface and is vital for drinking, agriculture, and industry.", - }, - { - q: 'Which metal is liquid at room temperature?', - a: ['Mercury', 'Gold', 'Silver', 'Copper'], - c: 0, - e: 'Mercury is liquid at room temperature.', - context: - 'Mercury is the only metal that is liquid at standard conditions for temperature and pressure. It is used in thermometers, barometers, and some electrical switches.', - }, - { - q: 'What is the fastest land animal?', - a: ['Lion', 'Cheetah', 'Horse', 'Kangaroo'], - c: 1, - e: 'The cheetah is the fastest land animal.', - context: - 'The cheetah can reach speeds of up to 75 miles per hour (120 kilometers per hour) in short bursts covering distances up to 500 meters. It is built for speed with a lightweight body and long legs.', - }, - { - q: 'Which organ in plants makes food using sunlight?', - a: ['Root', 'Stem', 'Leaf', 'Flower'], - c: 2, - e: 'Leaves perform photosynthesis to make food.', - context: - 'Photosynthesis occurs in the chloroplasts of plant cells, which contain chlorophyll that captures sunlight. This process converts carbon dioxide and water into glucose and oxygen.', - }, - { - q: 'Who composed the Fifth Symphony known as "fate"?', - a: ['Mozart', 'Beethoven', 'Bach', 'Chopin'], - c: 1, - e: 'Beethoven composed the Fifth Symphony.', - context: - 'Ludwig van Beethoven was a German composer and pianist. His Fifth Symphony, composed between 1804 and 1808, is one of the most performed symphonies and is known for its distinctive four-note motif.', - }, - { - q: 'What is the boiling point of water at sea level in Celsius?', - a: ['90', '95', '100', '105'], - c: 2, - e: 'Water boils at 100 degrees Celsius at sea level.', - context: - 'The boiling point of water can change depending on the atmospheric pressure. At higher altitudes, water boils at a lower temperature due to reduced pressure.', - }, - { - q: 'Which city is known as the Big Apple?', - a: ['Los Angeles', 'Chicago', 'New York', 'Miami'], - c: 2, - e: 'New York City is nicknamed the Big Apple.', - context: - 'The nickname "The Big Apple" originally referred to New York City\'s horse racing tracks. It later became popularized in the 1970s and is now a widely recognized nickname for the city.', - }, - { - q: 'What is the longest river in the world by length?', - a: ['Nile', 'Amazon', 'Yangtze', 'Mississippi'], - c: 0, - e: 'The Nile has long been considered the longest.', - context: - 'The Nile River in Africa is approximately 6650 kilometers (4130 miles) long. It flows through eleven countries and is essential for agriculture and water supply in the region.', - }, - { - q: 'Which planet is closest to the Sun?', - a: ['Venus', 'Mercury', 'Earth', 'Mars'], - c: 1, - e: 'Mercury is the planet closest to the Sun.', - context: - 'Mercury is the smallest planet in our solar system and orbits the Sun at an average distance of about 57.91 million kilometers (36 million miles).', - }, - { - q: 'Who developed the theory of relativity?', - a: ['Isaac Newton', 'Albert Einstein', 'Niels Bohr', 'Galileo'], - c: 1, - e: 'Albert Einstein developed the theory of relativity.', - context: - "Einstein's theory of relativity, published in 1905, revolutionized our understanding of space, time, and gravity. It introduced the famous equation E=mc², linking energy and mass.", - }, - { - q: 'What is the main language spoken in Brazil?', - a: ['Spanish', 'Portuguese', 'French', 'English'], - c: 1, - e: 'Portuguese is the main language in Brazil.', - context: - "Brazil is the largest country in South America and the only one in the region where Portuguese is the official language. This is due to Brazil's colonization by Portugal in the 16th century.", - }, - { - q: 'What instrument has keys, pedals, and strings?', - a: ['Guitar', 'Violin', 'Piano', 'Flute'], - c: 2, - e: 'The piano has keys, pedals, and strings.', - context: - 'The piano is a musical instrument played by pressing keys that cause hammers to strike strings, producing sound. It is widely used in classical and popular music.', - }, - { - q: 'Which country gifted the Statue of Liberty to the USA?', - a: ['Germany', 'France', 'Italy', 'Spain'], - c: 1, - e: 'France gifted the Statue of Liberty to the USA.', - context: - 'The Statue of Liberty was a gift from the people of France to the United States, dedicated in 1886. It was designed by French sculptor Frédéric Auguste Bartholdi and symbolizes freedom and democracy.', - }, - { - q: "Which gas is most abundant in Earth's atmosphere?", - a: ['Oxygen', 'Carbon dioxide', 'Nitrogen', 'Argon'], - c: 2, - e: 'Nitrogen is the most abundant gas in the atmosphere.', - context: - "Nitrogen makes up about 78% of Earth's atmosphere by volume. It is a colorless, odorless gas that is essential for life, as it is a key component of amino acids and nucleic acids.", - }, - { - q: 'What is the chemical symbol for gold?', - a: ['Au', 'Ag', 'Gd', 'Go'], - c: 0, - e: 'Au is the chemical symbol for gold.', - context: - 'Gold is a dense, malleable metal with the chemical symbol Au (from Latin: aurum) and atomic number 79. It is highly valued for its use in jewelry, currency, and other arts.', - }, - { - q: 'Who painted "The Starry Night"?', - a: ['Paul Cezanne', 'Vincent van Gogh', 'Pablo Picasso', 'Claude Monet'], - c: 1, - e: 'Vincent van Gogh painted The Starry Night.', - context: - "The Starry Night is one of Vincent van Gogh's most famous paintings, created in 1889. It depicts a swirling night sky over a quiet town, expressing van Gogh's emotional turmoil and fascination with the night.", - }, - { - q: 'Which year did World War 2 end?', - a: ['1944', '1945', '1946', '1947'], - c: 1, - e: 'World War 2 ended in 1945.', - context: - "World War 2 was a global conflict that lasted from 1939 to 1945. It involved most of the world's nations and resulted in significant changes to the global political and social landscape.", - }, - { - q: 'What is the largest mammal?', - a: ['Elephant', 'Blue whale', 'Giraffe', 'Hippopotamus'], - c: 1, - e: 'The blue whale is the largest mammal.', - context: - 'The blue whale is the largest animal known to have ever existed, reaching lengths of up to 100 feet (30 meters) and weights of up to 200 tons. They are found in oceans worldwide and primarily eat small shrimp-like animals called krill.', - }, - { - q: 'Which element is needed to make steel?', - a: ['Carbon', 'Helium', 'Nitrogen', 'Neon'], - c: 0, - e: 'Carbon is combined with iron to make steel.', - context: - 'Steel is an alloy made primarily of iron and carbon. The carbon content determines the hardness and strength of the steel. Other elements may also be added to create different types of steel.', - }, - { - q: 'Who is the author of the Harry Potter series?', - a: ['C S Lewis', 'J R R Tolkien', 'J K Rowling', 'Philip Pullman'], - c: 2, - e: 'J K Rowling wrote the Harry Potter series.', - context: - 'The Harry Potter series is a globally popular fantasy book series written by J.K. Rowling. It follows the life and adventures of a young wizard, Harry Potter, and his friends.', - }, - { - q: 'Which country uses the Yen as its currency?', - a: ['China', 'Japan', 'South Korea', 'Vietnam'], - c: 1, - e: 'Japan uses the Yen as its currency.', - context: - 'The yen is the official currency of Japan, introduced in 1871. It is one of the most traded currencies in the world and is known for its stability.', - }, - { - q: 'Which vitamin is produced when skin is exposed to sunlight?', - a: ['Vitamin A', 'Vitamin B', 'Vitamin C', 'Vitamin D'], - c: 3, - e: 'Vitamin D is produced in skin after sunlight exposure.', - context: - 'Vitamin D is essential for maintaining healthy bones and teeth, and it plays a role in immune system function. The body produces vitamin D in response to skin being exposed to sunlight.', - }, - { - q: 'What is the tallest mountain in the world above sea level?', - a: ['K2', 'Kangchenjunga', 'Mount Everest', 'Lhotse'], - c: 2, - e: 'Mount Everest is the tallest above sea level.', - context: - "Mount Everest, located in the Himalayas on the border of Nepal and the Tibet Autonomous Region of China, is the Earth's highest mountain above sea level, with a peak at 8848.86 meters (29031.7 ft).", - }, - { - q: 'What device converts alternating current to direct current?', - a: ['Transformer', 'Rectifier', 'Generator', 'Inverter'], - c: 1, - e: 'A rectifier converts AC to DC.', - context: - 'A rectifier is an electrical device that converts alternating current (AC) to direct current (DC). It allows current to flow in one direction only, effectively converting the AC waveform to a DC waveform.', - }, - { - q: 'Which two colors make green when mixed in paint?', - a: ['Red and Blue', 'Blue and Yellow', 'Red and Yellow', 'Blue and Green'], - c: 1, - e: 'Blue and yellow mix to make green.', - context: - 'In color theory, blue and yellow are primary colors that, when mixed together, create green, which is a secondary color. This is due to the way our eyes perceive color and the way light wavelengths combine.', - }, - { - q: 'Who was the first president of United States?', - a: ['Abraham Lincoln', 'George Washington', 'Thomas Jefferson', 'John Adams'], - c: 1, - e: 'George Washington was the first president of United States.', - context: - 'George Washington was unanimously elected as the first President of the United States in 1788. He served two terms from 1789 to 1797 and is often called the "Father of His Country".', - }, - { - q: 'Which organ breaks down food and absorbs nutrients?', - a: ['Lung', 'Kidney', 'Stomach and intestine', 'Heart'], - c: 2, - e: 'Stomach and intestines digest and absorb nutrients.', - context: - 'The digestive system breaks down food into smaller molecules, which are then absorbed into the bloodstream through the walls of the intestines. The stomach and intestines play key roles in this process.', - }, - { - q: 'Which bird is known for its ability to mimic human speech?', - a: ['Eagle', 'Parrot', 'Sparrow', 'Ostrich'], - c: 1, - e: 'Parrots can mimic human speech.', - context: - 'Some species of parrots are known for their ability to imitate human speech and other sounds. This ability varies among individual birds and is thought to be a form of social learning.', - }, - { - q: 'What is the study of past human activity called?', - a: ['Anthropology', 'Archaeology', 'Sociology', 'Geology'], - c: 1, - e: 'Archaeology studies past human activity.', - context: - 'Archaeology is the scientific study of ancient cultures and human activity through the examination of artifacts, structures, and other physical remains.', - }, - { - q: 'Which substance makes up the majority of the Sun?', - a: ['Iron', 'Hydrogen', 'Carbon', 'Silicon'], - c: 1, - e: 'Hydrogen is the main element in the Sun.', - context: - 'The Sun is composed of about 74% hydrogen, 24% helium, and 2% heavier elements. Hydrogen is the primary fuel for the nuclear fusion reactions that power the Sun.', - }, - { - q: 'Who invented the telephone?', - a: ['Thomas Edison', 'Alexander Graham Bell', 'Nikola Tesla', 'Guglielmo Marconi'], - c: 1, - e: 'Alexander Graham Bell is credited with the telephone.', - context: - 'Alexander Graham Bell was a Scottish-born inventor, scientist, and teacher who is credited with inventing the first practical telephone. He was awarded the first US patent for the invention of the telephone.', - }, - { - q: 'What is the capital of Canada?', - a: ['Toronto', 'Montreal', 'Vancouver', 'Ottawa'], - c: 3, - e: 'Ottawa is the capital of Canada.', - context: - 'Ottawa is the capital city of Canada, located in the province of Ontario. It became the capital in 1857 and is home to many national institutions, including the Parliament of Canada.', - }, - { - q: 'Which planet is known for its rings?', - a: ['Mars', 'Jupiter', 'Saturn', 'Uranus'], - c: 2, - e: 'Saturn is famous for its rings.', - context: - "Saturn is the sixth planet from the Sun and is known for its prominent ring system, which is made up of ice particles, rocky debris, and dust. The rings are thought to be remnants of moons or comets that were torn apart by Saturn's gravity.", - }, - { - q: 'Which chemical is used as table salt?', - a: ['Sodium chloride', 'Potassium', 'Magnesium', 'Calcium'], - c: 0, - e: 'Table salt is sodium chloride.', - context: - 'Table salt is chemically known as sodium chloride (NaCl). It is composed of sodium ions and chloride ions and is used in food preparation and preservation.', - }, - { - q: 'Who painted "Guernica"?', - a: ['Salvador Dali', 'Pablo Picasso', 'Henri Matisse', 'Jackson Pollock'], - c: 1, - e: 'Pablo Picasso painted Guernica.', - context: - 'Guernica is a mural-sized oil painting on canvas by Spanish artist Pablo Picasso, completed in 1937. It is one of the most famous anti-war artworks, depicting the suffering caused by war and violence.', - }, - { - q: 'What is the largest desert in the world?', - a: ['Sahara', 'Gobi', 'Arabian', 'Antarctic desert'], - c: 3, - e: 'The Antarctic is the largest desert by area.', - context: - 'The Antarctic Desert is the largest desert in the world, covering an area of about 14 million square kilometers (5.5 million square miles). It is classified as a desert due to its extremely low humidity and precipitation.', - }, - { - q: 'Which sport uses a shuttlecock?', - a: ['Tennis', 'Badminton', 'Squash', 'Table tennis'], - c: 1, - e: 'Badminton uses a shuttlecock.', - context: - "Badminton is a racquet sport played using shuttlecocks and a lightweight racquet. The game is played on a rectangular court divided by a net, and the objective is to hit the shuttlecock over the net and into the opponent's court.", - }, - { - q: 'What is the freezing point of water in Fahrenheit?', - a: ['0', '32', '100', '212'], - c: 1, - e: 'Water freezes at 32 degrees Fahrenheit.', - context: - 'The freezing point of water is 32 degrees Fahrenheit (0 degrees Celsius) at standard atmospheric pressure. At this temperature, water molecules slow down and form a crystalline structure, resulting in ice.', - }, - { - q: 'Which continent has the most countries?', - a: ['Asia', 'Africa', 'Europe', 'South America'], - c: 1, - e: 'Africa has the most countries of any continent.', - context: - 'Africa is the second-largest and second-most populous continent, with 54 recognized sovereign states. It has a diverse range of cultures, languages, and ecosystems.', - }, - { - q: 'What is the main ingredient in traditional sushi?', - a: ['Beef', 'Rice', 'Potatoes', 'Cheese'], - c: 1, - e: 'Rice is the main ingredient in sushi.', - context: - 'Sushi is a Japanese dish typically made with vinegared rice, raw fish, and other ingredients like vegetables and seaweed. The rice is the essential component that defines sushi.', - }, - { - q: 'Which famous physicist wrote "A Brief History of Time"?', - a: ['Richard Feynman', 'Stephen Hawking', 'Carl Sagan', 'Brian Cox'], - c: 1, - e: 'Stephen Hawking wrote A Brief History of Time.', - context: - 'A Brief History of Time is a popular science book written by physicist Stephen Hawking. It explains complex concepts in cosmology, such as the Big Bang, black holes, and light cones, in accessible language.', - }, - { - q: 'Which city is the capital of Australia?', - a: ['Sydney', 'Melbourne', 'Canberra', 'Perth'], - c: 2, - e: 'Canberra is the capital of Australia.', - context: - 'Canberra is the capital city of Australia, located in the Australian Capital Territory. It was selected as the capital in 1908 as a compromise between rivals Sydney and Melbourne.', - }, - { - q: 'What is the largest organ in the human body?', - a: ['Liver', 'Skin', 'Heart', 'Brain'], - c: 1, - e: 'Skin is the largest organ of the human body.', - context: - "The skin is the body's largest organ, covering an area of about 2 square meters (22 square feet) in adults. It protects internal organs, regulates temperature, and enables the sense of touch.", - }, - { - q: 'Which gas do humans inhale to survive?', - a: ['Carbon dioxide', 'Nitrogen', 'Oxygen', 'Helium'], - c: 2, - e: 'Humans inhale oxygen to survive.', - context: - 'Oxygen is essential for human survival as it is required for cellular respiration, the process by which cells produce energy. Humans inhale air containing oxygen through the respiratory system.', - }, - { - q: 'Who developed the theory of evolution by natural selection?', - a: ['Gregor Mendel', 'Charles Darwin', 'Louis Pasteur', 'Alfred Wallace'], - c: 1, - e: 'Charles Darwin proposed natural selection.', - context: - 'Charles Darwin was an English naturalist, geologist, and biologist best known for his contributions to the science of evolution. He proposed the theory of natural selection as the mechanism of evolution.', - }, - { - q: 'What instrument measures temperature?', - a: ['Barometer', 'Thermometer', 'Hygrometer', 'Ammeter'], - c: 1, - e: 'A thermometer measures temperature.', - context: - 'A thermometer is a device that measures temperature, typically using a glass tube filled with mercury or alcohol that expands and contracts with temperature changes.', - }, - { - q: 'Which country has the largest population?', - a: ['India', 'United States', 'China', 'Russia'], - c: 2, - e: 'China has the largest population.', - context: - 'China is the most populous country in the world, with a population of over 1.4 billion people. It is followed by India, the United States, and Indonesia.', - }, - { - q: 'What is the chemical formula for table sugar (sucrose)?', - a: ['C6H12O6', 'C12H22O11', 'H2O', 'CO2'], - c: 1, - e: 'Sucrose has formula C12H22O11.', - context: - 'Table sugar, or sucrose, is a carbohydrate composed of glucose and fructose units. It is commonly used as a sweetener in food and drinks.', - }, - { - q: 'Which author wrote "Pride and Prejudice"?', - a: ['Emily Bronte', 'Charlotte Bronte', 'Jane Austen', 'Mary Shelley'], - c: 2, - e: 'Jane Austen wrote Pride and Prejudice.', - context: - 'Pride and Prejudice is a novel written by Jane Austen, published in 1813. It is a romantic fiction that critiques the British landed gentry at the end of the 18th century.', - }, - { - q: 'What is the largest island in the world?', - a: ['Greenland', 'Madagascar', 'Borneo', 'New Guinea'], - c: 0, - e: 'Greenland is the largest island.', - context: - "Greenland is the world's largest island that is not a continent. It is located between the Arctic and Atlantic Oceans and is an autonomous territory within the Kingdom of Denmark.", - }, - { - q: 'Which planet is known as the Red Planet?', - a: ['Earth', 'Mars', 'Venus', 'Mercury'], - c: 1, - e: 'Mars is known as the Red Planet.', - context: - 'Mars is often called the Red Planet because of its reddish appearance, which is due to iron oxide (rust) on its surface. It is the fourth planet from the Sun and has the largest dust storms in the solar system.', - }, - { - q: 'Who is credited with inventing the light bulb?', - a: ['Nikola Tesla', 'Thomas Edison', 'Alexander Graham Bell', 'James Watt'], - c: 1, - e: 'Thomas Edison is commonly credited for the light bulb.', - context: - 'Thomas Edison was an American inventor and businessman who is credited with developing the first commercially successful incandescent light bulb.', - }, - { - q: 'What is the capital of Italy?', - a: ['Milan', 'Naples', 'Rome', 'Florence'], - c: 2, - e: 'Rome is the capital of Italy.', - context: - 'Rome, the "Eternal City", is the capital of Italy and of the Lazio region. It is known for its nearly 3000 years of globally influential art, architecture, and culture.', - }, - { - q: 'Which metal has the highest electrical conductivity?', - a: ['Gold', 'Silver', 'Copper', 'Aluminum'], - c: 1, - e: 'Silver has the highest conductivity of common metals.', - context: - 'Silver is a metal known for its high electrical conductivity, thermal conductivity, and reflectivity. It is used in electrical contacts, conductors, and in various electronic devices.', - }, - { - q: 'What is the largest city in India by population?', - a: ['Delhi', 'Mumbai', 'Bangalore', 'Kolkata'], - c: 1, - e: 'Mumbai is the largest city by population in India.', - context: - 'Mumbai, formerly known as Bombay, is the most populous city in India and the seventh most populous city in the world. It is the financial, commercial, and entertainment hub of India.', - }, - { - q: 'Which mountain range includes Mount Kilimanjaro?', - a: ['Andes', 'Himalayas', 'Kilimanjaro is a standalone mountain', 'Rockies'], - c: 2, - e: 'Kilimanjaro is a free standing mountain, not part of a range.', - context: - 'Mount Kilimanjaro is a dormant stratovolcano located in Tanzania. It is the highest mountain in Africa, standing at 5895 meters (19341 feet) above sea level.', - }, - { - q: 'Which animal is known as the king of the jungle?', - a: ['Tiger', 'Elephant', 'Lion', 'Bear'], - c: 2, - e: 'The lion is often called the king of the jungle.', - context: - 'The lion is a large cat species found in Africa and India. It is known for its strength, courage, and majestic appearance. The term "king of the jungle" is a colloquial expression and lions actually inhabit grasslands and savannas.', - }, - { - q: 'What is the primary language of Egypt?', - a: ['Arabic', 'English', 'French', 'Greek'], - c: 0, - e: 'Arabic is the primary language in Egypt.', - context: - 'Arabic is the official language of Egypt and is spoken by the vast majority of the population. Egypt is also known for its ancient civilization and historical monuments.', - }, - { - q: 'Which country has the largest area in the world?', - a: ['United States', 'China', 'Russia', 'Canada'], - c: 2, - e: 'Russia is the largest country by area.', - context: - 'Russia is the largest country in the world by land area, covering more than 17 million square kilometers. It spans Eastern Europe and northern Asia, and has a wide range of environments and landscapes.', - }, - { - q: 'Who painted the Mona Lisa?', - a: ['Michelangelo', 'Leonardo da Vinci', 'Rembrandt', 'Raphael'], - c: 1, - e: 'Leonardo da Vinci painted the Mona Lisa.', - context: - 'The Mona Lisa is a half-length portrait painting by the Italian Renaissance artist Leonardo da Vinci. It is considered an archetypal masterpiece of the Italian Renaissance and is one of the most famous paintings in the world.', - }, - { - q: 'Which chemical element has atomic number 1?', - a: ['Helium', 'Hydrogen', 'Oxygen', 'Lithium'], - c: 1, - e: 'Hydrogen has atomic number 1.', - context: - 'Hydrogen is the chemical element with the symbol H and atomic number 1. It is the lightest and most abundant element in the universe, making up about 75% of its elemental mass.', - }, - { - q: 'What is the capital of Germany?', - a: ['Munich', 'Frankfurt', 'Berlin', 'Hamburg'], - c: 2, - e: 'Berlin is the capital of Germany.', - context: - 'Berlin is the capital and largest city of Germany, located in the northeastern part of the country. It is known for its cultural heritage, modern architecture, and vibrant arts scene.', - }, - { - q: 'Which famous scientist formulated the laws of motion?', - a: ['Albert Einstein', 'Isaac Newton', 'Galileo Galilei', 'Max Planck'], - c: 1, - e: 'Isaac Newton formulated the classical laws of motion.', - context: - 'Isaac Newton was an English mathematician, physicist, and astronomer who is widely recognized for formulating the laws of motion and universal gravitation.', - }, - { - q: 'What is the main ingredient in hummus?', - a: ['Lentils', 'Chickpeas', 'Beans', 'Peas'], - c: 1, - e: 'Hummus is made primarily from chickpeas.', - context: - 'Hummus is a spread made from cooked, mashed chickpeas or other beans, and is a common part of Levantine and Middle Eastern cuisines. It is often served with pita bread.', - }, - { - q: 'What is the largest lake by area in Africa?', - a: ['Lake Victoria', 'Lake Tanganyika', 'Lake Malawi', 'Lake Turkana'], - c: 0, - e: 'Lake Victoria is the largest lake in Africa by area.', - context: - 'Lake Victoria is the largest lake in Africa and the second-largest freshwater lake in the world by surface area. It is bordered by three countries: Tanzania, Uganda, and Kenya.', - }, - { - q: 'Which composer wrote the opera La Traviata?', - a: ['Wagner', 'Verdi', 'Puccini', 'Mozart'], - c: 1, - e: 'Giuseppe Verdi composed La Traviata.', - context: - 'La Traviata is an opera in three acts by Giuseppe Verdi, premiered in 1853. It is based on the novel "La Dame aux Camélias" by Alexandre Dumas fils and is one of the most performed operas worldwide.', - }, - { - q: 'Which city is home to the Colosseum?', - a: ['Athens', 'Rome', 'Istanbul', 'Naples'], - c: 1, - e: 'The Colosseum is located in Rome.', - context: - 'The Colosseum, also known as the Flavian Amphitheatre, is an ancient oval amphitheatre located in the center of Rome. It is the largest amphitheatre ever built and is considered one of the greatest works of Roman architecture and engineering.', - }, - { - q: 'What is the capital of Spain?', - a: ['Valencia', 'Seville', 'Madrid', 'Barcelona'], - c: 2, - e: 'Madrid is the capital of Spain.', - context: - 'Madrid is the capital and largest city of Spain, located in the center of the country. It is known for its cultural and artistic heritage, as well as its vibrant nightlife.', - }, - { - q: 'Which planet has a day longer than its year?', - a: ['Mercury', 'Venus', 'Mars', 'Earth'], - c: 1, - e: 'Venus rotates slowly so its day is longer than its year.', - context: - 'Venus is the second planet from the Sun and has a very slow rotation on its axis, taking about 243 Earth days to complete one rotation. However, its orbit around the Sun takes only about 225 Earth days.', - }, - { - q: 'Who wrote "The Odyssey"?', - a: ['Homer', 'Virgil', 'Sophocles', 'Plato'], - c: 0, - e: 'Homer is attributed as the author of The Odyssey.', - context: - 'The Odyssey is an ancient Greek epic poem attributed to Homer. It is one of the two major ancient Greek epic poems, the other being the Iliad, and it consists of 24 books.', - }, - { - q: 'Which organ is primarily responsible for detoxifying chemicals?', - a: ['Heart', 'Lung', 'Liver', 'Spleen'], - c: 2, - e: 'The liver detoxifies many chemicals in the body.', - context: - 'The liver is a vital organ that plays a key role in metabolism, digestion, and detoxification. It filters blood coming from the digestive tract and detoxifies chemicals, metabolizes drugs, and secretes bile.', - }, - { - q: 'Which country is famous for maple syrup?', - a: ['United States', 'Canada', 'Norway', 'Sweden'], - c: 1, - e: 'Canada is famous for maple syrup.', - context: - 'Canada is the largest producer of maple syrup in the world, accounting for about 71% of the global market share. Maple syrup is a traditional Canadian sweetener made from the sap of sugar maple trees.', - }, - { - q: 'What is the common name for sodium bicarbonate?', - a: ['Baking soda', 'Table salt', 'Vinegar', 'Baking powder'], - c: 0, - e: 'Sodium bicarbonate is known as baking soda.', - context: - 'Sodium bicarbonate, commonly known as baking soda, is a chemical compound with the formula NaHCO₃. It is used in baking as a leavening agent, and also has various household and industrial uses.', - }, - { - q: 'Which sea creature has eight arms?', - a: ['Shark', 'Octopus', 'Dolphin', 'Jellyfish'], - c: 1, - e: 'An octopus has eight arms.', - context: - 'Octopuses are marine animals known for their eight arms, which are lined with sensitive suckers. They are intelligent creatures and can change color and texture to blend in with their surroundings.', - }, - { - q: 'What is the capital of Russia?', - a: ['Saint Petersburg', 'Moscow', 'Novosibirsk', 'Sochi'], - c: 1, - e: 'Moscow is the capital of Russia.', - context: - 'Moscow is the capital and largest city of Russia, located in the western part of the country. It is known for its rich cultural history, architecture, and as a major political, economic, and scientific center.', - }, - { - q: 'Which instrument is primarily used in jazz as a brass reed instrument?', - a: ['Violin', 'Saxophone', 'Clarinet', 'Oboe'], - c: 1, - e: 'The saxophone is a common brass reed instrument in jazz.', - context: - 'The saxophone is a musical instrument invented by Adolphe Sax in the 1840s. It is a key instrument in jazz music, known for its expressive range and timbre.', - }, - { - q: 'What is the largest planet in our solar system?', - a: ['Earth', 'Saturn', 'Jupiter', 'Neptune'], - c: 2, - e: 'Jupiter is the largest planet in the solar system.', - context: - 'Jupiter is the fifth planet from the Sun and is more than twice as massive as all the other planets in the solar system combined. It has a thick atmosphere made up mostly of hydrogen and helium.', - }, - { - q: 'Which chemical element is liquid and used in thermometers?', - a: ['Mercury', 'Lead', 'Sodium', 'Iron'], - c: 0, - e: 'Mercury is used in some thermometers.', - context: - 'Mercury is a chemical element with the symbol Hg and atomic number 80. It is a heavy, silvery-white liquid metal that is used in thermometers, barometers, and other scientific instruments.', - }, - { - q: 'Who is known as the father of modern physics and developed the laws of motion?', - a: ['Benjamin Franklin', 'Isaac Newton', 'Albert Einstein', 'Nikola Tesla'], - c: 1, - e: 'Isaac Newton developed the laws of motion.', - context: - 'Isaac Newton is often referred to as the father of modern physics for his groundbreaking work in the 17th century. He formulated the laws of motion and universal gravitation, laying the foundation for classical mechanics.', - }, - { - q: 'Which country is famous for the tango dance?', - a: ['Brazil', 'Argentina', 'Mexico', 'Chile'], - c: 1, - e: 'Argentina is famous for the tango.', - context: - 'The tango is a partner dance that originated in the 1880s along the River Plate, the natural border between Argentina and Uruguay. It is now popular worldwide and is known for its passionate and dramatic style.', - }, - { - q: 'What is the capital of South Africa (one of them)?', - a: ['Cape Town', 'Pretoria', 'Johannesburg', 'Durban'], - c: 1, - e: "Pretoria is one of South Africa's capitals (administrative).", - context: - 'South Africa has three capital cities: Pretoria (administrative), Cape Town (legislative), and Bloemfontein (judicial). Pretoria is known for its diplomatic missions and embassies.', - }, - { - q: 'Which substance is needed for combustion?', - a: ['Oxygen', 'Helium', 'Nitrogen', 'Carbon dioxide'], - c: 0, - e: 'Oxygen supports combustion.', - context: - 'Combustion is a chemical process that occurs when a substance reacts rapidly with oxygen and releases energy in the form of light and heat. Oxygen is essential for combustion to occur.', - }, - { - q: 'Who wrote the novel "Moby Dick"?', - a: ['Herman Melville', 'Ernest Hemingway', 'F Scott Fitzgerald', 'Mark Twain'], - c: 0, - e: 'Herman Melville is the author of Moby Dick.', - context: - "Moby Dick is an 1851 novel by Herman Melville. The book is the sailor Ishmael's narrative of the obsessive quest of Ahab, captain of the whaling ship Pequod, for revenge on Moby Dick.", - }, - { - q: 'What is the primary material used to make glass?', - a: ['Iron', 'Sand', 'Wood', 'Clay'], - c: 1, - e: 'Glass is primarily made from silica sand.', - context: - 'Glass is a solid material that is typically made from silica (silicon dioxide) sand, soda ash, and limestone. It is used in windows, bottles, and many other applications.', - }, - { - q: 'Which famous tower leans and is in Italy?', - a: ['Eiffel Tower', 'Leaning Tower of Pisa', 'Big Ben', 'CN Tower'], - c: 1, - e: 'The Leaning Tower of Pisa is in Italy.', - context: - 'The Leaning Tower of Pisa is a freestanding bell tower located in Pisa, Italy. It is famous for its unintended tilt, which began during its construction in the 12th century.', - }, - { - q: 'Which organ controls the nervous system?', - a: ['Heart', 'Brain', 'Liver', 'Kidney'], - c: 1, - e: 'The brain controls the nervous system.', - context: - 'The brain is the central organ of the human nervous system, and with the spinal cord, it makes up the central nervous system (CNS). It is responsible for processing sensory information and coordinating bodily functions.', - }, - { - q: 'What is the capital of Turkey?', - a: ['Istanbul', 'Ankara', 'Izmir', 'Bursa'], - c: 1, - e: 'Ankara is the capital of Turkey.', - context: - 'Ankara is the capital of Turkey, located in the central part of the country. It became the capital in 1923, replacing Istanbul as the capital of the Republic of Turkey.', - }, - { - q: 'Which sport is played at Wimbledon?', - a: ['Cricket', 'Tennis', 'Football', 'Rugby'], - c: 1, - e: 'Wimbledon is a major tennis tournament.', - context: - 'The Wimbledon Championships is the oldest tennis tournament in the world, and is considered the most prestigious. It has been held at the All England Club in Wimbledon, London, since 1877.', - }, - { - q: 'What metal is primarily used to make aircraft bodies due to its light weight?', - a: ['Steel', 'Titanium', 'Aluminum', 'Copper'], - c: 2, - e: 'Aluminum is widely used for aircraft bodies.', - context: - 'Aluminum is a lightweight, durable metal that is resistant to corrosion, making it ideal for aircraft construction. It is used in the manufacture of aircraft bodies, wings, and other components.', - }, - { - q: 'Which ocean is between Africa and Australia?', - a: ['Atlantic', 'Pacific', 'Indian', 'Arctic'], - c: 2, - e: 'The Indian Ocean lies between Africa and Australia.', - context: - "The Indian Ocean is the third-largest ocean, covering about 20% of the Earth's water surface. It is bounded by Africa, Asia, Australia, and the Indian subcontinent.", - }, -]; diff --git a/apps/legacy/speech/assets/splash-icon.png b/apps/legacy/speech/assets/splash-icon.png deleted file mode 100644 index 03d6f6b6c6..0000000000 Binary files a/apps/legacy/speech/assets/splash-icon.png and /dev/null differ diff --git a/apps/legacy/speech/assets/swm_icon.svg b/apps/legacy/speech/assets/swm_icon.svg deleted file mode 100644 index 8c62f039be..0000000000 --- a/apps/legacy/speech/assets/swm_icon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/apps/legacy/speech/colors.ts b/apps/legacy/speech/colors.ts deleted file mode 100644 index feb75ac336..0000000000 --- a/apps/legacy/speech/colors.ts +++ /dev/null @@ -1,6 +0,0 @@ -const ColorPalette = { - primary: '#001A72', - strongPrimary: '#020F3C', -}; - -export default ColorPalette; diff --git a/apps/legacy/speech/components/ErrorBanner.tsx b/apps/legacy/speech/components/ErrorBanner.tsx deleted file mode 100644 index a5bebc504f..0000000000 --- a/apps/legacy/speech/components/ErrorBanner.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; - -interface ErrorBannerProps { - message: string | null; - onDismiss: () => void; -} - -export default function ErrorBanner({ message, onDismiss }: ErrorBannerProps) { - if (!message) return null; - - return ( - - - {message} - - - ✕ - - - ); -} - -const styles = StyleSheet.create({ - container: { - backgroundColor: '#FEE2E2', - borderLeftWidth: 4, - borderLeftColor: '#EF4444', - borderRadius: 8, - marginHorizontal: 16, - marginVertical: 8, - paddingVertical: 10, - paddingLeft: 12, - paddingRight: 8, - flexDirection: 'row', - alignItems: 'center', - }, - message: { - flex: 1, - color: '#991B1B', - fontSize: 14, - lineHeight: 20, - }, - closeButton: { - padding: 4, - marginLeft: 8, - }, - closeText: { - color: '#991B1B', - fontSize: 16, - fontWeight: '600', - }, -}); diff --git a/apps/legacy/speech/components/ModelPicker.tsx b/apps/legacy/speech/components/ModelPicker.tsx deleted file mode 100644 index 951de6cce8..0000000000 --- a/apps/legacy/speech/components/ModelPicker.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Dimensions, - Modal, - ScrollView, - StyleSheet, - Text, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native'; - -export type ModelOption = { - label: string; - value: T; -}; - -type Props = { - models: ModelOption[]; - selectedModel: T; - onSelect: (model: T) => void; - label?: string; - disabled?: boolean; -}; - -const DROPDOWN_MAX_HEIGHT = 300; - -export function ModelPicker({ models, selectedModel, onSelect, label, disabled }: Props) { - const [open, setOpen] = useState(false); - const [dropdownLayout, setDropdownLayout] = useState({ - x: 0, - y: 0, - width: 0, - }); - const triggerRef = useRef>(null); - const selected = models.find((m) => m.value === selectedModel); - - useEffect(() => { - if (disabled) setOpen(false); - }, [disabled]); - - const handlePress = () => { - if (disabled) return; - if (open) { - setOpen(false); - return; - } - triggerRef.current?.measure( - (_x: number, _y: number, width: number, height: number, pageX: number, pageY: number) => { - const spaceBelow = Dimensions.get('window').height - (pageY + height); - const y = - spaceBelow >= DROPDOWN_MAX_HEIGHT - ? pageY + height + 2 - : pageY - Math.min(DROPDOWN_MAX_HEIGHT, models.length * 42) - 2; - setDropdownLayout({ x: pageX, y, width }); - setOpen(true); - } - ); - }; - - return ( - - - {label && {label}} - {selected?.label ?? '—'} - {open ? '▲' : '▼'} - - - setOpen(false)}> - setOpen(false)}> - - - {models.map((item) => { - const isSelected = item.value === selectedModel; - return ( - { - onSelect(item.value); - setOpen(false); - }} - > - - {item.label} - - - ); - })} - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - marginHorizontal: 12, - marginVertical: 4, - alignSelf: 'stretch', - }, - trigger: { - flexDirection: 'row', - alignItems: 'center', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 10, - backgroundColor: '#f5f5f5', - }, - triggerDisabled: { - opacity: 0.4, - }, - label: { - fontSize: 12, - color: '#888', - marginRight: 6, - }, - triggerText: { - flex: 1, - fontSize: 14, - color: '#001A72', - fontWeight: '500', - }, - chevron: { - fontSize: 10, - color: '#888', - marginLeft: 6, - }, - dropdown: { - position: 'absolute', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - backgroundColor: '#fff', - maxHeight: DROPDOWN_MAX_HEIGHT, - elevation: 8, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.15, - shadowRadius: 4, - }, - option: { - paddingHorizontal: 12, - paddingVertical: 10, - borderBottomWidth: 1, - borderBottomColor: '#f0f0f0', - }, - optionSelected: { - backgroundColor: '#e8ecf8', - }, - optionText: { - fontSize: 14, - color: '#333', - }, - optionTextSelected: { - color: '#001A72', - fontWeight: '600', - }, -}); diff --git a/apps/legacy/speech/components/VerboseTranscription.tsx b/apps/legacy/speech/components/VerboseTranscription.tsx deleted file mode 100644 index 7b5c7a3bb2..0000000000 --- a/apps/legacy/speech/components/VerboseTranscription.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import React from 'react'; -import { View, Text, StyleSheet } from 'react-native'; -import { TranscriptionResult } from 'react-native-executorch/legacy'; - -export const VerboseTranscription = ({ data }: { data: TranscriptionResult }) => { - if (!data) return null; - - const hasSegments = Array.isArray(data.segments) && data.segments.length > 0; - - const hasLanguage = !!data.language && data.language !== 'N/A' && data.language.trim() !== ''; - - const hasDuration = typeof data.duration === 'number' && data.duration > 0; - - const hasMetadata = hasLanguage || hasDuration; - - return ( - - - Full Text: - {data.text || ''} - - {hasMetadata && ( - - {hasLanguage && Language: {data.language}} - {hasDuration && ( - Duration: {data.duration?.toFixed(2)}s - )} - - )} - - - {hasSegments && ( - <> - Segments ({data.segments?.length}) - - {data.segments?.map((seg, index) => ( - - - - {seg.start.toFixed(2)}s - {seg.end.toFixed(2)}s - - ID: {index} - - - "{seg.text}" - - {seg.words && seg.words.length > 0 && ( - - Word Timestamps: - - {seg.words.map((w, wIdx) => ( - - {w.word.trim()} - {w.start.toFixed(2)}s - - ))} - - - )} - - - - Avg LogProb - - {data.task === 'transcribe' ? seg.avgLogprob?.toFixed(4) : 'N/A'} - - - - Temp - - {data.task === 'transcribe' ? seg.temperature?.toFixed(2) : 'N/A'} - - - - {/*eslint-disable-next-line @cspell/spellchecker*/} - Compr. - - {data.task === 'transcribe' ? seg.compressionRatio?.toFixed(2) : 'N/A'} - - - - - ))} - - )} - - ); -}; - -const styles = StyleSheet.create({ - container: { - padding: 4, - }, - metaContainer: { - marginBottom: 16, - padding: 12, - backgroundColor: '#f0f2f5', - borderRadius: 8, - }, - label: { - fontWeight: 'bold', - color: '#0f186e', - marginBottom: 4, - }, - text: { - fontSize: 16, - color: '#333', - marginBottom: 8, - }, - row: { - flexDirection: 'row', - gap: 10, - marginTop: 8, - }, - metaItem: { - fontSize: 12, - color: '#666', - backgroundColor: '#e1e4e8', - paddingHorizontal: 8, - paddingVertical: 2, - borderRadius: 4, - overflow: 'hidden', - }, - sectionHeader: { - fontSize: 18, - fontWeight: 'bold', - color: '#0f186e', - marginBottom: 8, - marginTop: 8, - }, - segmentCard: { - backgroundColor: '#fff', - borderRadius: 8, - borderWidth: 1, - borderColor: '#e1e4e8', - marginBottom: 12, - padding: 12, - shadowColor: '#000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.1, - shadowRadius: 2, - elevation: 2, - }, - segmentHeader: { - flexDirection: 'row', - justifyContent: 'space-between', - marginBottom: 8, - }, - timeBadge: { - fontSize: 12, - fontWeight: 'bold', - color: '#fff', - backgroundColor: '#0f186e', - paddingHorizontal: 8, - paddingVertical: 2, - borderRadius: 12, - overflow: 'hidden', - }, - segmentId: { - fontSize: 12, - color: '#888', - }, - segmentText: { - fontSize: 15, - fontStyle: 'italic', - color: '#333', - marginBottom: 12, - }, - statsGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 8, - borderTopWidth: 1, - borderTopColor: '#f0f0f0', - paddingTop: 8, - }, - statItem: { - flex: 1, - minWidth: '45%', - flexDirection: 'row', - justifyContent: 'space-between', - }, - statLabel: { - fontSize: 11, - color: '#888', - }, - statValue: { - fontSize: 11, - fontWeight: '600', - color: '#444', - }, - wordsContainer: { - marginVertical: 8, - backgroundColor: '#f8f9fa', - padding: 8, - borderRadius: 6, - }, - wordsGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: 6, - marginTop: 4, - }, - wordChip: { - backgroundColor: '#ffffff', - borderWidth: 1, - borderColor: '#e1e4e8', - borderRadius: 4, - paddingHorizontal: 6, - paddingVertical: 2, - alignItems: 'center', - }, - wordText: { - fontSize: 12, - color: '#333', - }, - wordTime: { - fontSize: 9, - color: '#888', - marginTop: 1, - }, -}); diff --git a/apps/legacy/speech/declarations.d.ts b/apps/legacy/speech/declarations.d.ts deleted file mode 100644 index 85e178f497..0000000000 --- a/apps/legacy/speech/declarations.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module '*.svg' { - import { SvgProps } from 'react-native-svg'; - const content: React.FV; - export default content; -} diff --git a/apps/legacy/speech/index.ts b/apps/legacy/speech/index.ts deleted file mode 100644 index 1d6e981ef6..0000000000 --- a/apps/legacy/speech/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerRootComponent } from 'expo'; - -import App from './App'; - -// registerRootComponent calls AppRegistry.registerComponent('main', () => App); -// It also ensures that whether you load the app in Expo Go or in a native build, -// the environment is set up appropriately -registerRootComponent(App); diff --git a/apps/legacy/speech/metro.config.js b/apps/legacy/speech/metro.config.js deleted file mode 100644 index f58bbc2131..0000000000 --- a/apps/legacy/speech/metro.config.js +++ /dev/null @@ -1,19 +0,0 @@ -const { getDefaultConfig } = require('expo/metro-config'); - -const config = getDefaultConfig(__dirname); - -const { transformer, resolver } = config; - -config.transformer = { - ...transformer, - babelTransformerPath: require.resolve('react-native-svg-transformer/expo'), -}; -config.resolver = { - ...resolver, - assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'), - sourceExts: [...resolver.sourceExts, 'svg'], -}; - -config.resolver.assetExts.push('pte'); - -module.exports = config; diff --git a/apps/legacy/speech/package.json b/apps/legacy/speech/package.json deleted file mode 100644 index 9985e6edbf..0000000000 --- a/apps/legacy/speech/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "legacy-speech", - "version": "1.0.0", - "main": "index.ts", - "scripts": { - "start": "expo start", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "typecheck": "tsc", - "lint": "eslint . --ext .ts,.tsx --fix" - }, - "dependencies": { - "@expo/vector-icons": "^15.0.2", - "@react-native/metro-config": "^0.86.0", - "buffer": "^6.0.3", - "expo": "~57.0.28", - "expo-build-properties": "~57.0.22", - "expo-font": "~57.0.4", - "expo-status-bar": "~57.0.1", - "metro-config": "^0.84.0", - "react": "19.2.3", - "react-native": "0.86.3", - "react-native-audio-api": "0.13.6", - "react-native-device-info": "^15.0.2", - "react-native-executorch": "workspace:*", - "react-native-executorch-expo-resource-fetcher": "workspace:*", - "react-native-reanimated": "4.7.1", - "react-native-safe-area-context": "~5.7.0", - "react-native-svg": "15.15.4", - "react-native-svg-transformer": "^1.5.3", - "react-native-worklets": "0.13.0" - }, - "devDependencies": { - "@babel/core": "^7.29.7", - "@types/react": "~19.2.14", - "@types/react-refresh": "^0", - "babel-preset-expo": "~57.0.15", - "react-refresh": "^0.18.0" - }, - "private": true -} diff --git a/apps/legacy/speech/screens/Quiz.tsx b/apps/legacy/speech/screens/Quiz.tsx deleted file mode 100644 index 3e69da52f3..0000000000 --- a/apps/legacy/speech/screens/Quiz.tsx +++ /dev/null @@ -1,479 +0,0 @@ -import React, { useEffect, useRef, useState, useCallback } from 'react'; -import { - Text, - View, - StyleSheet, - TouchableOpacity, - Platform, - ScrollView, - KeyboardAvoidingView, -} from 'react-native'; -import Animated, { - useSharedValue, - useAnimatedStyle, - withTiming, - withSequence, - withDelay, - runOnJS, -} from 'react-native-reanimated'; -import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; -import { models, useTextToSpeech } from 'react-native-executorch/legacy'; -import { AudioManager, AudioContext, AudioBuffer } from 'react-native-audio-api'; -import FontAwesome from '@expo/vector-icons/FontAwesome'; -import SWMIcon from '../assets/swm_icon.svg'; -import { QUESTIONS } from '../assets/quiz-data'; - -// Shuffle helper -function shuffleArray(array: T[]): T[] { - const arr = array.slice(); - for (let i = arr.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [arr[i], arr[j]] = [arr[j], arr[i]]; - } - return arr; -} - -// --- Audio Helper --- -const createAudioBufferFromVector = ( - audioVector: Float32Array, - audioContext: AudioContext | null = null, - sampleRate: number = 24000 -): AudioBuffer => { - if (audioContext == null) audioContext = new AudioContext({ sampleRate }); - const audioBuffer = audioContext.createBuffer(1, audioVector.length, sampleRate); - const channelData = audioBuffer.getChannelData(0); - channelData.set(audioVector); - return audioBuffer; -}; - -export const Quiz = ({ onBack }: { onBack: () => void }) => { - // --- Hooks & State --- - const model = useTextToSpeech(models.text_to_speech.kokoro.en_us.santa()); - - const [shuffledQuestions] = useState(() => shuffleArray(QUESTIONS)); - const [currentIndex, setCurrentIndex] = useState(0); - const [selectedAnswer, setSelectedAnswer] = useState(null); - const [isAnswerCorrect, setIsAnswerCorrect] = useState(null); - const [showNext, setShowNext] = useState(false); - const [isSpeaking, setIsSpeaking] = useState(false); - const fadeAnim = useSharedValue(1); - const feedbackAnim = useSharedValue(0); - const nextButtonAnim = useSharedValue(0); - const buttonsInactiveAnim = useSharedValue(1); - - const audioContextRef = useRef(null); - const currentSourceRef = useRef(null); - const isTransitioningRef = useRef(false); - const autoSpeakRef = useRef(true); - - // Animated Styles - const containerStyle = useAnimatedStyle(() => ({ - opacity: fadeAnim.value, - })); - - const feedbackStyle = useAnimatedStyle(() => ({ - opacity: feedbackAnim.value, - })); - - const nextButtonStyle = useAnimatedStyle(() => ({ - opacity: nextButtonAnim.value * buttonsInactiveAnim.value, - transform: [ - { - translateY: (1 - nextButtonAnim.value) * 12, - }, - ], - })); - - // --- Audio Setup --- - useEffect(() => { - AudioManager.setAudioSessionOptions({ - iosCategory: 'playAndRecord', - iosMode: 'spokenAudio', - iosOptions: ['defaultToSpeaker'], - }); - - audioContextRef.current = new AudioContext({ sampleRate: 24000 }); - audioContextRef.current.suspend(); - - return () => { - audioContextRef.current?.close(); - audioContextRef.current = null; - }; - }, []); - - // --- TTS Function --- - const speak = useCallback( - async (text: string) => { - if (!text.trim() || !model.isReady) { - setIsSpeaking(false); - return; - } - - // Stop previous audio if any - if (currentSourceRef.current) { - try { - currentSourceRef.current.stop(); - } catch (e) {} - } - - setIsSpeaking(true); - try { - const audioContext = audioContextRef.current; - if (!audioContext) return; - if (audioContext.state === 'suspended') await audioContext.resume(); - - const onNext = async (audioVec: Float32Array) => { - return new Promise((resolve) => { - const audioBuffer = createAudioBufferFromVector(audioVec, audioContext, 24000); - const source = audioContext.createBufferSource(); - source.buffer = audioBuffer; - source.connect(audioContext.destination); - currentSourceRef.current = source; - source.onEnded = () => resolve(); - source.start(); - }); - }; - - await model.stream({ text, speed: 0.9, onNext, onEnd: async () => {} }); - } catch (e) { - console.error(e); - } finally { - setIsSpeaking(false); - } - }, - [model] - ); - - // --- Game Logic --- - const currentQ = shuffledQuestions[currentIndex]; - - // Speak question on load - useEffect(() => { - if (!model.isReady) return; - if (!autoSpeakRef.current) { - autoSpeakRef.current = true; - return; - } - setIsSpeaking(true); - const t = setTimeout(() => speak(currentQ.q), 500); - return () => clearTimeout(t); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentIndex, model.isReady]); - - const handleAnswer = async (index: number) => { - if (selectedAnswer !== null || isSpeaking) return; // Prevent double taps or clicks while reading - - setSelectedAnswer(index); - const correct = index === currentQ.c; - setIsAnswerCorrect(correct); - - // 1. Visual Feedback Animation (1s) - feedbackAnim.value = withSequence( - withTiming(1, { duration: 200 }), - withDelay(1000, withTiming(0, { duration: 200 })) - ); - - // 2. Audio Feedback - if (correct) { - await speak('Correct!'); - } else { - // Play "Incorrect" and explanation as one string - await speak(`Incorrect. ${currentQ.e}`); - } - - // 3. Show Next Button - setShowNext(true); - }; - - const updateQuestionState = (nextIdx: number) => { - setSelectedAnswer(null); - setIsAnswerCorrect(null); - setCurrentIndex(nextIdx); - - setTimeout(() => { - isTransitioningRef.current = false; - fadeAnim.value = withTiming(1, { duration: 500 }); - }, 500); - }; - - const handleNext = () => { - const nextIdx = (currentIndex + 1) % shuffledQuestions.length; - buttonsInactiveAnim.value = 1; // Ensure buttons are active for next round - autoSpeakRef.current = false; - speak(shuffledQuestions[nextIdx].q); - - isTransitioningRef.current = true; - setShowNext(false); - - fadeAnim.value = withTiming(0, { duration: 500 }, (finished) => { - if (finished) { - runOnJS(updateQuestionState)(nextIdx); - } - }); - }; - - const handleLearnMore = async () => { - if (isSpeaking) return; - - buttonsInactiveAnim.value = withTiming(0.5, { duration: 800 }); - - // Play the context for the current question - await speak(currentQ.context); - - buttonsInactiveAnim.value = withTiming(1, { duration: 800 }); - }; - - const getButtonColor = (index: number) => { - if (selectedAnswer === null) return styles.optionButton; - - if (index === currentQ.c) return styles.correctButton; // Highlight correct answer always if answered - if (index === selectedAnswer && !isAnswerCorrect) return styles.wrongButton; // Highlight mistake - - return styles.disabledOption; - }; - - // Animate next button appearance - useEffect(() => { - nextButtonAnim.value = withTiming(showNext ? 1 : 0, { - duration: 450, - }); - }, [showNext, nextButtonAnim]); - - return ( - - - - - - - - Text to Speech - Quiz - - - {!model.isReady ? ( - - - Loading Model: {Math.round(model.downloadProgress * 100)}% - - - ) : ( - - - - - Question {currentIndex + 1} - {currentQ.q} - - - - {currentQ.a.map((opt, idx) => ( - handleAnswer(idx)} - disabled={selectedAnswer !== null || isSpeaking} - > - {opt} - - ))} - - - {/* Feedback Animation Overlay Text */} - {selectedAnswer !== null && ( - - - {isAnswerCorrect ? 'Correct!' : 'Incorrect'} - - - )} - - - - {showNext && ( - - - Next Question - - - - - - Learn More - - - - - )} - - )} - - - ); -}; - -// Reuse stylistic approach from TextToSpeechScreen -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: 'white', - }, - centerContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - loadingText: { - fontSize: 18, - color: '#0f186e', - }, - header: { - flexDirection: 'row', - alignItems: 'center', - padding: 16, - borderBottomWidth: 1, - borderBottomColor: '#eee', - position: 'relative', - }, - backButton: { - paddingRight: 15, - }, - headerText: { - fontSize: 20, - fontWeight: 'bold', - color: '#0f186e', - marginLeft: 10, - }, - scrollContent: { - padding: 20, - paddingBottom: 160, // Space for bottom container - flexGrow: 1, // Ensures content scales to screen height - justifyContent: 'center', - }, - quizContainer: { - width: '100%', - }, - questionCard: { - backgroundColor: '#0f186e', - borderRadius: 16, - padding: 24, - marginBottom: 24, - minHeight: 150, - justifyContent: 'center', - }, - questionIndex: { - color: 'rgba(255,255,255,0.7)', - fontSize: 14, - marginBottom: 8, - textTransform: 'uppercase', - fontWeight: 'bold', - }, - questionText: { - color: 'white', - fontSize: 22, - fontWeight: '600', - lineHeight: 30, - }, - optionsContainer: { - gap: 12, - }, - baseOption: { - padding: 16, - borderRadius: 12, - borderWidth: 2, - flexDirection: 'row', - alignItems: 'center', - }, - // States of options - optionButton: { - backgroundColor: 'white', - borderColor: '#e0e0e0', - }, - correctButton: { - backgroundColor: '#E8F5E9', - borderColor: '#4CAF50', - }, - wrongButton: { - backgroundColor: '#FFEBEE', - borderColor: '#F44336', - }, - disabledOption: { - backgroundColor: '#f5f5f5', - borderColor: '#eee', - opacity: 0.6, - }, - - optionText: { - fontSize: 18, - color: '#333', - fontWeight: '500', - }, - feedbackContainer: { - alignItems: 'center', - marginVertical: 10, - }, - feedbackText: { - fontSize: 24, - fontWeight: 'bold', - marginVertical: 10, - }, - feedbackTextCorrect: { - color: '#4CAF50', - }, - feedbackTextIncorrect: { - color: '#F44336', - }, - nextButton: { - // Removed marginTop to rely on container padding - backgroundColor: '#0f186e', - paddingVertical: 16, - paddingHorizontal: 32, - borderRadius: 30, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - gap: 10, - alignSelf: 'center', - width: '100%', - }, - learnMoreButton: { - marginTop: 0, - backgroundColor: 'white', - borderWidth: 2, - borderColor: '#0f186e', - }, - nextButtonText: { - color: 'white', - fontSize: 18, - fontWeight: 'bold', - }, - learnMoreButtonText: { - color: '#0f186e', - }, - bottomContainer: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - padding: 20, - gap: 12, - alignItems: 'center', - backgroundColor: 'rgba(255,255,255,0.95)', - }, - flex1: { - flex: 1, - }, -}); diff --git a/apps/legacy/speech/screens/SpeechToTextScreen.tsx b/apps/legacy/speech/screens/SpeechToTextScreen.tsx deleted file mode 100644 index b206b8f886..0000000000 --- a/apps/legacy/speech/screens/SpeechToTextScreen.tsx +++ /dev/null @@ -1,585 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Text, - View, - StyleSheet, - TouchableOpacity, - ScrollView, - TextInput, - KeyboardAvoidingView, - Platform, - Switch, - Keyboard, -} from 'react-native'; -import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; -import { - models, - useSpeechToText, - TranscriptionResult, - SpeechToTextProps, -} from 'react-native-executorch/legacy'; -import { ModelPicker, ModelOption } from '../components/ModelPicker'; -const speechToText = models.speech_to_text; -const vad = models.vad; - -const isSimulator = DeviceInfo.isEmulatorSync(); -const backend = Platform.OS === 'ios' && !isSimulator ? 'coreml' : 'xnnpack'; - -type STTModelSources = SpeechToTextProps['model']; - -const MODELS: ModelOption[] = [ - { - label: 'Whisper Tiny EN', - value: speechToText.whisper_tiny_en({ backend }), - }, - { - label: 'Whisper Base EN', - value: speechToText.whisper_base_en({ backend }), - }, - { - label: 'Whisper Small EN', - value: speechToText.whisper_small_en({ backend }), - }, -]; -import FontAwesome from '@expo/vector-icons/FontAwesome'; -import { AudioManager, AudioRecorder, AudioContext } from 'react-native-audio-api'; -import * as FileSystem from 'expo-file-system/legacy'; -import SWMIcon from '../assets/swm_icon.svg'; -import DeviceInfo from 'react-native-device-info'; - -import { VerboseTranscription } from '../components/VerboseTranscription'; -import ErrorBanner from '../components/ErrorBanner'; - -export const SpeechToTextScreen = ({ onBack }: { onBack: () => void }) => { - const [selectedModel, setSelectedModel] = useState( - Platform.OS === 'ios' && !isSimulator - ? speechToText.whisper_base_en({ backend }) - : speechToText.whisper_tiny_en() - ); - - const model = useSpeechToText({ - model: selectedModel, - vad: vad.fsmn_vad(), - }); - - const [transcription, setTranscription] = useState(null); - const [transcriptionTime, setTranscriptionTime] = useState(null); - - const [liveResult, setLiveResult] = useState<{ - fullText: string; - segments: any[]; - } | null>(null); - - const [enableTimestamps, setEnableTimestamps] = useState(false); - const [useVAD, setUseVAD] = useState(true); - const [error, setError] = useState(null); - const [audioURL, setAudioURL] = useState(''); - const [hasMicPermission, setHasMicPermission] = useState(false); - - const isRecordingRef = useRef(false); - const [liveTranscribing, setLiveTranscribing] = useState(false); - const scrollViewRef = useRef(null); - - const recorder = useRef(new AudioRecorder()); - - useEffect(() => { - AudioManager.setAudioSessionOptions({ - iosCategory: 'playAndRecord', - iosMode: 'spokenAudio', - iosOptions: ['allowBluetoothHFP', 'defaultToSpeaker'], - }); - const checkPerms = async () => { - const status = await AudioManager.requestRecordingPermissions(); - setHasMicPermission(status === 'Granted'); - }; - checkPerms(); - }, []); - - async function getAudioFile(sourceUri: string) { - const destination = FileSystem.cacheDirectory + 'audio_file.wav'; - - if (sourceUri.startsWith('http')) { - const { uri } = await FileSystem.downloadAsync(sourceUri, destination); - return uri; - } else { - await FileSystem.copyAsync({ - from: sourceUri, - to: destination, - }); - return destination; - } - } - - const handleTranscribeFromURL = async () => { - if (!audioURL.trim() || model.isGenerating) { - if (!audioURL.trim()) { - console.warn('Please provide a valid audio file URL'); - } - return; - } - - Keyboard.dismiss(); - - // Reset previous states - setTranscription(null); - setLiveResult(null); - - try { - const uri = await getAudioFile(audioURL); - const audioContext = new AudioContext({ sampleRate: 16000 }); - const decodedAudioData = await audioContext.decodeAudioData(uri); - const audioBuffer = decodedAudioData.getChannelData(0); - const start = Date.now(); - const result = await model.transcribe(audioBuffer, { - verbose: enableTimestamps, - }); - setTranscriptionTime(Date.now() - start); - setTranscription(result); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - return; - } - }; - - const handleStartTranscribeFromMicrophone = async () => { - if (!hasMicPermission || model.isGenerating || liveTranscribing) { - if (!hasMicPermission) { - setError('Microphone permission denied. Please enable it in Settings.'); - } - return; - } - - isRecordingRef.current = true; - setLiveTranscribing(true); - - setTranscription(null); - setLiveResult({ fullText: '', segments: [] }); - - const sampleRate = 16000; - - recorder.current.onAudioReady( - { - sampleRate, - bufferLength: 0.1 * sampleRate, // 100 ms - channelCount: 1, - }, - ({ buffer }) => { - model.streamInsert(buffer.getChannelData(0)); - } - ); - - try { - await AudioManager.setAudioSessionActivity(true); - const result = await recorder.current.start(); - if (result.status === 'error') { - setError(`Recording problems: ${result.message}`); - } - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - isRecordingRef.current = false; - setLiveTranscribing(false); - return; - } - - let accumulatedText = ''; - let accumulatedSegments: any[] = []; - - try { - const streamIter = model.stream({ - verbose: enableTimestamps, - timeout: 200, - useVAD: useVAD, - vadDetectionMargin: 1200, - }); - - for await (const { committed, nonCommitted } of streamIter) { - if (!isRecordingRef.current) break; - - if (committed.text) { - accumulatedText += committed.text; - } - if (committed.segments) { - accumulatedSegments = [...accumulatedSegments, ...committed.segments]; - } - - const currentDisplay = { - fullText: accumulatedText + nonCommitted.text, - segments: [...accumulatedSegments, ...(nonCommitted.segments || [])], - }; - - setLiveResult(currentDisplay); - } - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } finally { - setLiveTranscribing(false); - } - }; - - const handleStopTranscribeFromMicrophone = () => { - isRecordingRef.current = false; - - recorder.current.stop(); - model.streamStop(); - console.log('Live transcription stopped'); - setLiveTranscribing(false); - - if (liveResult) { - setTranscription({ - text: liveResult.fullText, - segments: liveResult.segments, - language: 'en', - duration: 0, - }); - setLiveResult(null); - } - }; - - const getModelStatus = () => { - if (model.isGenerating) return 'Transcribing...'; - if (model.isReady) return 'Ready to transcribe'; - return `Loading model: ${(100 * model.downloadProgress).toFixed(2)}%`; - }; - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const readyToTranscribe = !model.isGenerating && model.isReady; - const recordingButtonDisabled = isSimulator || !readyToTranscribe; - - const getDisplayData = (): TranscriptionResult | null => { - if (liveTranscribing && liveResult) { - return { - text: liveResult.fullText, - segments: liveResult.segments, - language: 'en', - duration: 0, - }; - } - return transcription; - }; - - const displayData = getDisplayData(); - - return ( - - - - - - - - - React Native ExecuTorch - Speech to Text - - - - Status: {getModelStatus()} - {transcriptionTime !== null && ( - Transcription: {transcriptionTime} ms - )} - - setError(null)} /> - - { - setSelectedModel(m); - setTranscription(null); - setLiveResult(null); - }} - /> - - - Enable Timestamps (Verbose) - { - setEnableTimestamps(val); - setTranscription(null); - setLiveResult(null); - }} - trackColor={{ false: '#767577', true: '#0f186e' }} - thumbColor={enableTimestamps ? '#fff' : '#f4f3f4'} - disabled={model.isGenerating} - /> - - - - Transcription - scrollViewRef.current?.scrollToEnd({ animated: true })} - > - {displayData ? ( - - ) : ( - - {liveTranscribing ? 'Listening...' : 'No transcription yet...'} - - )} - - - - - - - - Start - - - - {liveTranscribing ? ( - - - Stop Live Transcription - - ) : ( - - - - {isSimulator ? 'No Mic' : 'Start Live'} - - - setUseVAD(!useVAD)} - activeOpacity={0.7} - accessibilityRole="switch" - accessibilityState={{ checked: useVAD }} - accessibilityLabel={`Voice Activity Detection ${useVAD ? 'on' : 'off'}`} - style={[ - styles.vadButton, - useVAD ? styles.vadActive : styles.vadInactive, - recordingButtonDisabled && styles.disabled, - ]} - > - - - - VAD - - - {useVAD ? 'ON' : 'OFF'} - - - - - )} - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - backgroundColor: 'white', - paddingHorizontal: 16, - }, - keyboardAvoidingView: { - flex: 1, - width: '100%', - }, - header: { - alignItems: 'center', - position: 'relative', - width: '100%', - }, - backButton: { - position: 'absolute', - left: 0, - top: 10, - padding: 10, - zIndex: 1, - }, - headerText: { - fontSize: 22, - fontWeight: 'bold', - color: '#0f186e', - }, - statusContainer: { - marginTop: 12, - alignItems: 'center', - }, - statsText: { - fontSize: 13, - color: '#334155', - fontWeight: '500', - marginTop: 4, - }, - toggleContainer: { - flexDirection: 'row', - alignItems: 'center', - marginTop: 10, - marginBottom: 5, - }, - toggleLabel: { - fontSize: 16, - marginRight: 10, - color: '#0f186e', - }, - transcriptionContainer: { - flex: 1, - width: '100%', - marginVertical: 12, - }, - transcriptionLabel: { - marginLeft: 12, - marginBottom: 4, - color: '#0f186e', - }, - transcriptionScrollContainer: { - borderRadius: 12, - borderWidth: 1, - borderColor: '#0f186e', - padding: 12, - maxHeight: 400, - }, - placeholderText: { - color: '#aaa', - fontStyle: 'italic', - }, - inputContainer: { - marginBottom: 12, - }, - urlTranscriptionContainer: { - width: '100%', - flexDirection: 'row', - }, - urlTranscriptionInput: { - flex: 1, - padding: 12, - borderTopLeftRadius: 12, - borderBottomLeftRadius: 12, - borderWidth: 1, - borderColor: '#0f186e', - borderRightWidth: 0, - color: '#0f186e', - }, - urlTranscriptionButton: { - backgroundColor: '#0f186e', - justifyContent: 'center', - alignItems: 'center', - padding: 12, - borderTopRightRadius: 12, - borderBottomRightRadius: 12, - }, - buttonText: { - color: 'white', - fontWeight: '600', - letterSpacing: -0.5, - fontSize: 16, - }, - liveTranscriptionButton: { - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - padding: 12, - borderRadius: 12, - marginTop: 12, - gap: 8, - }, - backgroundRed: { - backgroundColor: 'red', - }, - backgroundBlue: { - backgroundColor: '#0f186e', - }, - buttonRow: { - flexDirection: 'row', - gap: 8, - marginTop: 12, - }, - flex1: { - flex: 1, - marginTop: 0, - }, - vadButton: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - paddingHorizontal: 14, - borderRadius: 12, - gap: 10, - }, - vadActive: { - backgroundColor: '#0f186e', - }, - vadInactive: { - backgroundColor: '#f1f5f9', - }, - vadTextContainer: { - alignItems: 'flex-start', - }, - vadButtonLabel: { - fontWeight: '800', - fontSize: 13, - letterSpacing: 0.5, - }, - vadButtonLabelActive: { - color: 'white', - }, - vadButtonLabelInactive: { - color: '#64748b', - }, - vadButtonState: { - fontWeight: '700', - fontSize: 10, - letterSpacing: 1, - }, - vadButtonStateActive: { - color: '#bbf7d0', - }, - vadButtonStateInactive: { - color: '#94a3b8', - }, - disabled: { - opacity: 0.5, - }, -}); diff --git a/apps/legacy/speech/screens/TextToSpeechLLMScreen.tsx b/apps/legacy/speech/screens/TextToSpeechLLMScreen.tsx deleted file mode 100644 index 0066c7552f..0000000000 --- a/apps/legacy/speech/screens/TextToSpeechLLMScreen.tsx +++ /dev/null @@ -1,295 +0,0 @@ -import React, { useEffect, useState, useRef } from 'react'; -import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native'; -import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; -import FontAwesome from '@expo/vector-icons/FontAwesome'; -import SWMIcon from '../assets/swm_icon.svg'; -import { models, useLLM, useTextToSpeech } from 'react-native-executorch/legacy'; -import { - AudioManager, - AudioContext, - AudioBuffer, - AudioBufferSourceNode, -} from 'react-native-audio-api'; - -interface TextToSpeechLLMProps { - onBack: () => void; -} - -/** - * Converts an audio vector (Float32Array) to an AudioBuffer for playback - * @param audioVector - The generated audio samples from the model - * @param audioContext - The audio context used to create the buffer. - * @param sampleRate - The sample rate (default: 24000 Hz for Kokoro) - * @returns AudioBuffer ready for playback - */ -const createAudioBufferFromVector = ( - audioVector: Float32Array, - audioContext: AudioContext, - sampleRate: number = 24000 -): AudioBuffer => { - const audioBuffer = audioContext.createBuffer(1, audioVector.length, sampleRate); - const channelData = audioBuffer.getChannelData(0); - channelData.set(audioVector); - - return audioBuffer; -}; - -export const TextToSpeechLLMScreen = ({ onBack }: TextToSpeechLLMProps) => { - const [displayText, setDisplayText] = useState(''); - const [isTtsStreaming, setIsTtsStreaming] = useState(false); - const llm = useLLM({ model: models.llm.llama3_2_1b() }); - const tts = useTextToSpeech(models.text_to_speech.kokoro.en_us.heart()); - - const processedLengthRef = useRef(0); - const audioContextRef = useRef(null); - const sourceRef = useRef(null); - - useEffect(() => { - AudioManager.setAudioSessionOptions({ - iosCategory: 'playAndRecord', - iosMode: 'spokenAudio', - iosOptions: ['defaultToSpeaker'], - }); - - audioContextRef.current = new AudioContext({ sampleRate: 24000 }); - audioContextRef.current.suspend(); - - return () => { - audioContextRef.current?.close(); - audioContextRef.current = null; - }; - }, []); - - // Update displayText gradually as response gets generated and insert new text chunks into TTS stream - useEffect(() => { - if (llm.response && tts.isReady) { - setDisplayText(llm.response); - - const previousLength = processedLengthRef.current; - if (llm.response.length > previousLength && isTtsStreaming) { - const newChunk = llm.response.slice(previousLength); - tts.streamInsert(newChunk); - processedLengthRef.current = llm.response.length; - } - } else { - processedLengthRef.current = 0; - } - }, [llm.response, tts, isTtsStreaming]); - - const handleGenerate = async () => { - setDisplayText(''); - processedLengthRef.current = 0; - setIsTtsStreaming(true); - - const startTTS = async () => { - try { - const audioContext = audioContextRef.current; - if (!audioContext) return; - - if (audioContext.state === 'suspended') { - await audioContext.resume(); - } - - const onNext = async (audioVec: Float32Array) => { - return new Promise((resolve) => { - const audioBuffer = createAudioBufferFromVector(audioVec, audioContext, 24000); - - const source = (sourceRef.current = audioContext.createBufferSource()); - source.buffer = audioBuffer; - source.connect(audioContext.destination); - - source.onEnded = () => resolve(); - - source.start(); - }); - }; - - await tts.stream({ - speed: 0.9, - stopAutomatically: false, - onNext, - }); - } catch (e) { - console.error('TTS streaming error:', e); - } finally { - setIsTtsStreaming(false); - } - }; - - const ttsPromise = startTTS(); - - try { - await llm.sendMessage( - 'Generate a short story about a robot learning to paint. The story should be around 200 words long.' - ); - } catch (e) { - console.error('Generation failed:', e); - } finally { - // LLM finished — partition any trailing un-terminated tail so it gets - // synthesized before the stream closes. - tts.streamFlush(); - tts.streamStop(false); - await ttsPromise; - - if (audioContextRef.current && audioContextRef.current.state === 'running') { - await audioContextRef.current.suspend(); - } - } - }; - - const handleStop = () => { - llm.interrupt(); - tts.streamStop(true); - if (sourceRef.current) { - try { - sourceRef.current.stop(); - } catch (e) { - // Source might have already stopped or disconnected - } - } - }; - - const isProcessing = llm.isGenerating || isTtsStreaming; - const isModelsReady = llm.isReady && tts.isReady; - - const getModelStatus = () => { - if (llm.error) return `LLM Error: ${llm.error.message}`; - if (tts.error) return `TTS Error: ${tts.error.message}`; - if (!llm.isReady) return `Loading LLM: ${(100 * llm.downloadProgress).toFixed(2)}%`; - if (!tts.isReady) return `Loading TTS: ${(100 * tts.downloadProgress).toFixed(2)}%`; - if (isProcessing) return 'Generating/Streaming...'; - return 'Ready'; - }; - - return ( - - - - - - - - React Native ExecuTorch - LLM to Speech Demo - - - - Status: {getModelStatus()} - - - - Generated Story - - - - {displayText || - (isModelsReady - ? 'Press the button to generate a story and hear it spoken aloud.' - : 'Please wait for models to load...')} - - - - - - - {isProcessing ? ( - - - Stop Generation - - ) : ( - - - Generate & Stream Speech - - )} - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - backgroundColor: 'white', - paddingHorizontal: 16, - }, - header: { - alignItems: 'center', - position: 'relative', - width: '100%', - }, - backButton: { - position: 'absolute', - left: 0, - top: 10, - padding: 10, - zIndex: 1, - }, - headerText: { - fontSize: 22, - fontWeight: 'bold', - color: '#0f186e', - }, - statusContainer: { - marginTop: 12, - alignItems: 'center', - }, - contentContainer: { - width: '100%', - marginTop: 24, - flex: 1, - marginBottom: 24, - }, - label: { - marginLeft: 12, - marginBottom: 4, - color: '#0f186e', - fontWeight: '600', - }, - responseContainer: { - borderRadius: 12, - borderWidth: 1, - borderColor: '#0f186e', - flex: 1, - }, - responseContent: { - padding: 12, - }, - responseText: { - fontSize: 16, - color: '#333', - lineHeight: 24, - }, - buttonContainer: { - marginBottom: 24, - width: '100%', - }, - actionButton: { - backgroundColor: '#0f186e', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - padding: 12, - borderRadius: 12, - gap: 8, - }, - stopButton: { - backgroundColor: '#ff4444', - }, - buttonText: { - color: 'white', - fontWeight: '600', - letterSpacing: -0.5, - fontSize: 16, - }, - disabled: { - opacity: 0.5, - }, -}); diff --git a/apps/legacy/speech/screens/TextToSpeechScreen.tsx b/apps/legacy/speech/screens/TextToSpeechScreen.tsx deleted file mode 100644 index 8da2be6e6f..0000000000 --- a/apps/legacy/speech/screens/TextToSpeechScreen.tsx +++ /dev/null @@ -1,310 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Text, - View, - StyleSheet, - TouchableOpacity, - TextInput, - KeyboardAvoidingView, - Platform, - Keyboard, -} from 'react-native'; -import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; -import { models, useTextToSpeech, TextToSpeechModelConfig } from 'react-native-executorch/legacy'; -import { ModelPicker, ModelOption } from '../components/ModelPicker'; - -const tts = models.text_to_speech.kokoro; - -const VOICES: ModelOption[] = [ - { label: '🇺🇸 AF Heart', value: tts.en_us.heart() }, - { label: '🇺🇸 AF River', value: tts.en_us.river() }, - { label: '🇺🇸 AF Sarah', value: tts.en_us.sarah() }, - { label: '🇺🇸 AM Adam', value: tts.en_us.adam() }, - { label: '🇺🇸 AM Michael', value: tts.en_us.michael() }, - { label: '🇺🇸 AM Santa', value: tts.en_us.santa() }, - { label: '🇬🇧 BF Emma', value: tts.en_gb.emma() }, - { label: '🇬🇧 BM Daniel', value: tts.en_gb.daniel() }, - { label: '🇫🇷 FF Siwis', value: tts.fr.siwis() }, - { label: '🇪🇸 EF Dora', value: tts.es.dora() }, - { label: '🇪🇸 EM Alex', value: tts.es.alex() }, - { label: '🇮🇹 IF Sara', value: tts.it.sara() }, - { label: '🇮🇹 IM Nicola', value: tts.it.nicola() }, - { label: '🇵🇹 PF Dora', value: tts.pt.dora() }, - { label: '🇵🇹 PM Santa', value: tts.pt.santa() }, - { label: '🇩🇪 DF Anna', value: tts.de.anna() }, - { label: '🇵🇱 PM Mateusz', value: tts.pl.mateusz() }, - { label: '🇮🇳 HF Alpha', value: tts.hi.alpha() }, - { label: '🇮🇳 HM Omega', value: tts.hi.omega() }, - { label: '🇮🇳 HM Psi', value: tts.hi.psi() }, -]; - -import FontAwesome from '@expo/vector-icons/FontAwesome'; -import { - AudioManager, - AudioContext, - AudioBuffer, - AudioBufferSourceNode, -} from 'react-native-audio-api'; -import SWMIcon from '../assets/swm_icon.svg'; -import ErrorBanner from '../components/ErrorBanner'; - -/** - * Converts an audio vector (Float32Array) to an AudioBuffer for playback - * @param audioVector - The generated audio samples from the model - * @param audioContext - An optional AudioContext to create the buffer in. If not provided, a new one will be created. - * @param sampleRate - The sample rate (default: 24000 Hz for Kokoro) - * @returns AudioBuffer ready for playback - */ -const createAudioBufferFromVector = ( - audioVector: Float32Array, - audioContext: AudioContext | null = null, - sampleRate: number = 24000 -): AudioBuffer => { - if (audioContext == null) audioContext = new AudioContext({ sampleRate }); - - const audioBuffer = audioContext.createBuffer(1, audioVector.length, sampleRate); - const channelData = audioBuffer.getChannelData(0); - channelData.set(audioVector); - - return audioBuffer; -}; - -export const TextToSpeechScreen = ({ onBack }: { onBack: () => void }) => { - const [selectedSpeaker, setSelectedSpeaker] = useState( - tts.en_us.heart() - ); - - const model = useTextToSpeech(selectedSpeaker); - - const [inputText, setInputText] = useState(''); - const [isPlaying, setIsPlaying] = useState(false); - const [readyToGenerate, setReadyToGenerate] = useState(false); - const [error, setError] = useState(null); - - const audioContextRef = useRef(null); - const gainNodeRef = useRef(null); - const sourceRef = useRef(null); - - useEffect(() => { - AudioManager.setAudioSessionOptions({ - iosCategory: 'playAndRecord', - iosMode: 'spokenAudio', - iosOptions: ['defaultToSpeaker'], - }); - - const context = new AudioContext({ sampleRate: 24000 }); - audioContextRef.current = context; - context.suspend(); - - // Increase the audio volume - const gainNode = context.createGain(); - gainNode.gain.value = 2.0; // Increase volume by 2x - gainNode.connect(context.destination); - gainNodeRef.current = gainNode; - - return () => { - audioContextRef.current?.close(); - audioContextRef.current = null; - gainNodeRef.current = null; - }; - }, []); - - useEffect(() => { - setReadyToGenerate(!model.isGenerating && model.isReady && !isPlaying); - }, [model.isGenerating, model.isReady, isPlaying]); - - const handlePlayAudio = async () => { - if (!inputText.trim()) { - return; - } - - Keyboard.dismiss(); - setIsPlaying(true); - - try { - const audioContext = audioContextRef.current; - if (!audioContext) return; - - if (audioContext.state === 'suspended') { - await audioContext.resume(); - } - - const onNext = async (audioVec: Float32Array) => { - return new Promise((resolve) => { - const audioBuffer = createAudioBufferFromVector(audioVec, audioContext, 24000); - - const source = (sourceRef.current = audioContext.createBufferSource()); - source.buffer = audioBuffer; - - if (gainNodeRef.current) { - source.connect(gainNodeRef.current); - } else { - source.connect(audioContext.destination); - } - - source.onEnded = () => resolve(); - - source.start(); - }); - }; - - const onEnd = async () => { - setIsPlaying(false); - await audioContext.suspend(); - }; - - await model.stream({ - text: inputText, - speed: 0.9, - phonemize: true, - onNext, - onEnd, - }); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - setIsPlaying(false); - } - }; - - const getModelStatus = () => { - if (model.isGenerating) return 'Generating audio...'; - if (model.isReady) return 'Ready to synthesize'; - return `Loading model: ${(100 * model.downloadProgress).toFixed(2)}%`; - }; - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - return ( - - - - - - - - - React Native ExecuTorch - Text to Speech - - - - Status: {getModelStatus()} - - setError(null)} /> - - setSelectedSpeaker(m)} - /> - - - Enter text to synthesize - - - - - - - {isPlaying ? 'Playing...' : 'Generate & Play'} - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - backgroundColor: 'white', - paddingHorizontal: 16, - }, - keyboardAvoidingView: { - flex: 1, - width: '100%', - }, - header: { - alignItems: 'center', - position: 'relative', - width: '100%', - }, - backButton: { - position: 'absolute', - left: 0, - top: 10, - padding: 10, - zIndex: 1, - }, - headerText: { - fontSize: 22, - fontWeight: 'bold', - color: '#0f186e', - }, - statusContainer: { - marginTop: 12, - alignItems: 'center', - }, - inputContainer: { - width: '100%', - marginTop: 24, - }, - inputLabel: { - marginLeft: 12, - marginBottom: 4, - color: '#0f186e', - fontWeight: '600', - }, - textInput: { - borderRadius: 12, - borderWidth: 1, - borderColor: '#0f186e', - padding: 12, - minHeight: 120, - fontSize: 16, - color: '#0f186e', - }, - buttonContainer: { - marginTop: 24, - }, - playButton: { - backgroundColor: '#0f186e', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - padding: 12, - borderRadius: 12, - gap: 8, - }, - buttonText: { - color: 'white', - fontWeight: '600', - letterSpacing: -0.5, - fontSize: 16, - }, - disabled: { - opacity: 0.5, - }, -}); diff --git a/apps/legacy/speech/screens/VoiceActivityDetectionScreen.tsx b/apps/legacy/speech/screens/VoiceActivityDetectionScreen.tsx deleted file mode 100644 index aacad9bc0e..0000000000 --- a/apps/legacy/speech/screens/VoiceActivityDetectionScreen.tsx +++ /dev/null @@ -1,300 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { Text, View, StyleSheet, TouchableOpacity, ScrollView, Platform } from 'react-native'; -import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; -import { models, useVAD } from 'react-native-executorch/legacy'; -import FontAwesome from '@expo/vector-icons/FontAwesome'; -import { AudioManager, AudioRecorder } from 'react-native-audio-api'; -import SWMIcon from '../assets/swm_icon.svg'; -import DeviceInfo from 'react-native-device-info'; -import ErrorBanner from '../components/ErrorBanner'; - -const isSimulator = DeviceInfo.isEmulatorSync(); - -export const VoiceActivityDetectionScreen = ({ onBack }: { onBack: () => void }) => { - const model = useVAD({ - model: models.vad.fsmn_vad(), - }); - - const [isSpeaking, setIsSpeaking] = useState(false); - const [error, setError] = useState(null); - const [hasMicPermission, setHasMicPermission] = useState(false); - const [isStreaming, setIsStreaming] = useState(false); - - const recorder = useRef(new AudioRecorder()); - const logScrollRef = useRef(null); - const [logs, setLogs] = useState([]); - - const addLog = (msg: string) => { - setLogs((prev) => [...prev, `${new Date().toLocaleTimeString()}: ${msg}`]); - }; - - useEffect(() => { - AudioManager.setAudioSessionOptions({ - iosCategory: 'playAndRecord', - iosMode: 'spokenAudio', - iosOptions: ['allowBluetoothHFP', 'defaultToSpeaker'], - }); - const checkPerms = async () => { - const status = await AudioManager.requestRecordingPermissions(); - setHasMicPermission(status === 'Granted'); - }; - checkPerms(); - }, []); - - const handleStartStreaming = async () => { - if (isStreaming || model.isGenerating || !model.isReady) { - return; - } - - setIsStreaming(true); - if (!hasMicPermission) { - setError('Microphone permission denied. Please enable it in Settings.'); - setIsStreaming(false); - return; - } - - setLogs([]); - addLog('Starting VAD stream...'); - - const sampleRate = 16000; - - recorder.current.onAudioReady( - { - sampleRate, - bufferLength: 0.1 * sampleRate, - channelCount: 1, - }, - ({ buffer }) => { - model.streamInsert(buffer.getChannelData(0)); - } - ); - - try { - await AudioManager.setAudioSessionActivity(true); - const result = await recorder.current.start(); - if (result.status === 'error') { - setError(`Recording problems: ${result.status}`); - } - - await model.stream({ - onSpeechBegin: () => { - setIsSpeaking(true); - addLog('Speech detected (Begin)'); - }, - onSpeechEnd: () => { - setIsSpeaking(false); - addLog('Silence detected (End)'); - }, - options: { - timeout: 100, - detectionMargin: 300, - }, - }); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - setIsStreaming(false); - } - }; - - const handleStopStreaming = () => { - recorder.current.stop(); - model.streamStop(); - setIsStreaming(false); - setIsSpeaking(false); - addLog('VAD stream stopped'); - }; - - const getModelStatus = () => { - if (isStreaming || model.isGenerating) return 'Processing...'; - if (model.isReady) return 'Ready'; - return `Loading model: ${(100 * model.downloadProgress).toFixed(2)}%`; - }; - - useEffect(() => { - if (model.error) setError(String(model.error)); - }, [model.error]); - - const readyToStream = model.isReady; - const recordingButtonDisabled = isSimulator || !readyToStream || model.isGenerating; - - return ( - - - - - - - - React Native ExecuTorch - Voice Activity Detection - - - - Status: {getModelStatus()} - - - setError(null)} /> - - - - - {isSpeaking ? 'SPEAKING' : 'SILENT'} - - - - - VAD Events - logScrollRef.current?.scrollToEnd({ animated: true })} - > - {logs.length > 0 ? ( - logs.map((log, i) => ( - - {log} - - )) - ) : ( - No events logged yet... - )} - - - - - {isStreaming ? ( - - - Stop VAD Stream - - ) : ( - - - - {isSimulator ? 'Recording not available on Simulator' : 'Start VAD Stream'} - - - )} - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - backgroundColor: 'white', - paddingHorizontal: 16, - }, - header: { - alignItems: 'center', - position: 'relative', - width: '100%', - }, - backButton: { - position: 'absolute', - left: 0, - top: 10, - padding: 10, - zIndex: 1, - }, - headerText: { - fontSize: 22, - fontWeight: 'bold', - color: '#0f186e', - }, - statusContainer: { - marginTop: 12, - alignItems: 'center', - }, - visualizerContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - visualizerText: { - marginTop: 20, - fontSize: 24, - fontWeight: '800', - letterSpacing: 2, - }, - speakingText: { - color: '#22c55e', - }, - silentText: { - color: '#ef4444', - }, - logContainer: { - height: 150, - width: '100%', - marginVertical: 12, - }, - logLabel: { - marginLeft: 12, - marginBottom: 4, - color: '#0f186e', - fontWeight: '600', - }, - logScrollContainer: { - borderRadius: 12, - borderWidth: 1, - borderColor: '#0f186e', - padding: 12, - backgroundColor: '#f8fafc', - }, - logText: { - fontSize: 12, - fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', - color: '#334155', - marginBottom: 2, - }, - placeholderText: { - color: '#aaa', - fontStyle: 'italic', - }, - inputContainer: { - marginBottom: 30, - width: '100%', - }, - liveButton: { - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - padding: 16, - borderRadius: 12, - gap: 8, - }, - backgroundRed: { - backgroundColor: '#ef4444', - }, - backgroundBlue: { - backgroundColor: '#0f186e', - }, - buttonText: { - color: 'white', - fontWeight: '600', - fontSize: 16, - }, - disabled: { - opacity: 0.5, - }, -}); diff --git a/apps/legacy/speech/tsconfig.json b/apps/legacy/speech/tsconfig.json deleted file mode 100644 index 20b5fc01ed..0000000000 --- a/apps/legacy/speech/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "jsx": "react-native", - "lib": ["es2022"], - "moduleResolution": "bundler", - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "esnext", - "paths": { - "react-native-executorch/legacy": ["../../packages/react-native-executorch/legacy/src"], - "react-native-executorch-expo-resource-fetcher": ["../../packages/expo-resource-fetcher"] - } - }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["**/node_modules", "**/Pods", "**/__tests__"], - "extends": "expo/tsconfig.base" -} diff --git a/apps/legacy/text-embeddings/.gitignore b/apps/legacy/text-embeddings/.gitignore deleted file mode 100644 index d7eba44af8..0000000000 --- a/apps/legacy/text-embeddings/.gitignore +++ /dev/null @@ -1,39 +0,0 @@ -# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files - -# dependencies -node_modules/ - -# Expo -.expo/ -dist/ -web-build/ -expo-env.d.ts - -# Native -*.orig.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision - -# Metro -.metro-health-check* - -# debug -npm-debug.* -yarn-debug.* -yarn-error.* - -# macOS -.DS_Store -*.pem - -# local env files -.env*.local - -# typescript -*.tsbuildinfo - -# yarn -.yarn \ No newline at end of file diff --git a/apps/legacy/text-embeddings/app.json b/apps/legacy/text-embeddings/app.json deleted file mode 100644 index 38f9a95046..0000000000 --- a/apps/legacy/text-embeddings/app.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "expo": { - "name": "legacy-text-embeddings", - "slug": "legacy-text-embeddings", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/icon.png", - "userInterfaceStyle": "light", - "newArchEnabled": true, - "scheme": "rne-embeddings", - "splash": { - "image": "./assets/splash-icon.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.anonymous.text-embeddings" - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", - "backgroundColor": "#ffffff" - }, - "package": "com.anonymous.textembeddings" - }, - "web": { - "favicon": "./assets/favicon.png" - }, - "plugins": [ - "expo-router", - [ - "expo-build-properties", - { - "ios": { - "deploymentTarget": "17.0" - } - } - ] - ] - } -} diff --git a/apps/legacy/text-embeddings/app/_layout.tsx b/apps/legacy/text-embeddings/app/_layout.tsx deleted file mode 100644 index 8ef0ed37f1..0000000000 --- a/apps/legacy/text-embeddings/app/_layout.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { Drawer } from 'expo-router/drawer'; -import { initExecutorch } from 'react-native-executorch/legacy'; -import { ExpoResourceFetcher } from 'react-native-executorch-expo-resource-fetcher'; -import ColorPalette from '../colors'; -import React, { useState } from 'react'; -import { Text, StyleSheet, View, TouchableOpacity, type ColorValue } from 'react-native'; - -import { - type DrawerContentComponentProps, - DrawerContentScrollView, - DrawerItemList, -} from 'expo-router/build/react-navigation/drawer'; -import { DrawerActions } from 'expo-router/build/react-navigation/routers'; -import { useNavigation } from 'expo-router'; -import Svg, { Rect } from 'react-native-svg'; -import { GeneratingContext } from '../context'; - -function HamburgerIcon({ tintColor }: { tintColor?: ColorValue }) { - const navigation = useNavigation(); - const fill = typeof tintColor === 'string' ? tintColor : '#000'; - return ( - navigation.dispatch(DrawerActions.toggleDrawer())} - style={styles.hamburger} - > - - - - - - - ); -} - -initExecutorch({ - resourceFetcher: ExpoResourceFetcher, -}); - -interface CustomDrawerProps extends DrawerContentComponentProps { - isGenerating: boolean; -} - -function CustomDrawerContent(props: CustomDrawerProps) { - const { isGenerating, ...otherProps } = props; - return ( - - {!isGenerating ? ( - - ) : ( - - Model is generating... - Interrupt before switching model - - )} - - ); -} - -export default function _layout() { - const [isGenerating, setIsGenerating] = useState(false); - - return ( - { - setIsGenerating(newState); - }, - }} - > - ( - - )} - screenOptions={{ - drawerActiveTintColor: ColorPalette.primary, - drawerInactiveTintColor: '#888', - headerTintColor: ColorPalette.primary, - headerTitleStyle: { color: ColorPalette.primary }, - headerLeft: (props) => , - }} - > - null, - title: 'Main Menu', - drawerItemStyle: { display: 'none' }, - }} - /> - - - - - ); -} - -const styles = StyleSheet.create({ - hamburger: { - marginLeft: 12, - }, - centerContent: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: 20, - }, - mainText: { - fontSize: 18, - fontWeight: 'bold', - marginBottom: 10, - color: ColorPalette.primary, - }, - subText: { - fontSize: 14, - color: ColorPalette.strongPrimary, - }, -}); diff --git a/apps/legacy/text-embeddings/app/clip-embeddings/index.tsx b/apps/legacy/text-embeddings/app/clip-embeddings/index.tsx deleted file mode 100644 index f59176c612..0000000000 --- a/apps/legacy/text-embeddings/app/clip-embeddings/index.tsx +++ /dev/null @@ -1,354 +0,0 @@ -import { useState } from 'react'; -import { - StyleSheet, - Text, - TextInput, - TouchableOpacity, - View, - SafeAreaView, - ScrollView, - Image, - KeyboardAvoidingView, - Platform, -} from 'react-native'; -import { Ionicons } from '@expo/vector-icons'; -import { - models, - useTextEmbeddings, - useImageEmbeddings, - ImageEmbeddingsProps, -} from 'react-native-executorch/legacy'; - -type ImageEmbeddingModel = ImageEmbeddingsProps['model']; - -const IMAGE_MODELS: { label: string; value: ImageEmbeddingModel }[] = [ - { - label: 'ViT-B/32 Quantized', - value: models.image_embedding.clip_vit_base_patch32_image(), - }, - { - label: 'ViT-B/32 FP32', - value: models.image_embedding.clip_vit_base_patch32_image({ - quant: false, - }), - }, -]; -import { launchImageLibrary } from 'react-native-image-picker'; -import { useIsFocused } from 'expo-router'; -import { dotProduct } from '../../utils/math'; -import { ModelPicker } from '../../components/ModelPicker'; - -const DEFAULT_LABELS = [ - 'a photo of a dog', - 'a photo of a cat', - 'a landscape photo', - 'a photo of food', - 'a photo of people', -]; - -export default function ClipEmbeddingsScreenWrapper() { - const isFocused = useIsFocused(); - - return isFocused ? : null; -} - -function ClipEmbeddingsScreen() { - const [selectedImageModel, setSelectedImageModel] = useState( - models.image_embedding.clip_vit_base_patch32_image() - ); - - const textModel = useTextEmbeddings({ - model: models.text_embedding.clip_vit_base_patch32_text(), - }); - const imageModel = useImageEmbeddings({ model: selectedImageModel }); - - const [imageUri, setImageUri] = useState(null); - const [newLabel, setNewLabel] = useState(''); - const [labels, setLabels] = useState(DEFAULT_LABELS); - const [results, setResults] = useState<{ label: string; similarity: number }[]>([]); - const [imageEmbeddingTime, setImageEmbeddingTime] = useState(null); - const [textEmbeddingTime, setTextEmbeddingTime] = useState(null); - - const getModelStatusText = (model: typeof textModel | typeof imageModel) => { - if (model.error) return `Oops! ${model.error}`; - if (!model.isReady) return `Loading ${(model.downloadProgress * 100).toFixed(0)}%`; - return model.isGenerating ? 'Generating…' : 'Ready'; - }; - - const pickImage = async () => { - const output = await launchImageLibrary({ mediaType: 'photo' }); - if (!output.assets?.[0]?.uri) return; - setImageUri(output.assets[0].uri); - setResults([]); - }; - - const classify = async () => { - if (!imageUri || !imageModel.isReady || !textModel.isReady) return; - - try { - const imgStart = Date.now(); - const imageEmbedding = await imageModel.forward(imageUri); - setImageEmbeddingTime(Date.now() - imgStart); - - const txtStart = Date.now(); - const scored: { label: string; similarity: number }[] = []; - for (const label of labels) { - const textEmbedding = await textModel.forward(label); - scored.push({ - label, - similarity: dotProduct(imageEmbedding, textEmbedding), - }); - } - setTextEmbeddingTime(Math.round((Date.now() - txtStart) / labels.length)); - - scored.sort((a, b) => b.similarity - a.similarity); - setResults(scored); - } catch (e) { - console.error('Error during classification:', e); - } - }; - - const addLabel = () => { - const trimmed = newLabel.trim(); - if (!trimmed || labels.includes(trimmed)) return; - setLabels((prev) => [...prev, trimmed]); - setNewLabel(''); - setResults([]); - }; - - const removeLabel = (label: string) => { - setLabels((prev) => prev.filter((l) => l !== label)); - setResults((prev) => prev.filter((r) => r.label !== label)); - }; - - const modelsReady = textModel.isReady && imageModel.isReady; - - return ( - - - - CLIP Image Embeddings - - - Text model: {getModelStatusText(textModel)} - Image model: {getModelStatusText(imageModel)} - - - { - setSelectedImageModel(m); - setResults([]); - }} - /> - - {/* Image picker */} - - {imageUri ? ( - - ) : ( - - - Tap to pick an image - - )} - - - {/* Classify button */} - - - - {!imageUri ? 'Pick an image first' : 'Find best matching label'} - - - - {/* Results */} - {results.length > 0 && ( - - Results - {results.map((item, index) => ( - - - {index === 0 ? '🥇 ' : ''} - {item.label} - - {item.similarity.toFixed(3)} - - ))} - {(imageEmbeddingTime !== null || textEmbeddingTime !== null) && ( - - {imageEmbeddingTime !== null && ( - Image embedding: {imageEmbeddingTime} ms - )} - {textEmbeddingTime !== null && ( - Text embeddings: {textEmbeddingTime} ms - )} - - )} - - )} - - {/* Labels */} - - Text Labels - {labels.map((label) => ( - - {label} - removeLabel(label)}> - - - - ))} - - - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: '#F8FAFC' }, - flex: { flex: 1 }, - scrollContainer: { padding: 20, alignItems: 'center', flexGrow: 1 }, - heading: { - fontSize: 24, - fontWeight: '500', - marginBottom: 12, - color: '#0F172A', - }, - statusRow: { - width: '100%', - marginBottom: 16, - gap: 2, - }, - statusText: { fontSize: 13, color: '#64748B' }, - imagePicker: { - width: '100%', - height: 220, - borderRadius: 16, - overflow: 'hidden', - marginBottom: 20, - borderWidth: 2, - borderColor: '#E2E8F0', - backgroundColor: '#fff', - }, - image: { width: '100%', height: '100%' }, - imagePlaceholder: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - gap: 8, - }, - imagePlaceholderText: { fontSize: 14, color: '#94A3B8' }, - card: { - backgroundColor: '#FFFFFF', - width: '100%', - padding: 16, - borderRadius: 16, - borderColor: '#E2E8F0', - borderWidth: 2, - marginBottom: 20, - }, - sectionTitle: { - fontSize: 16, - fontWeight: '600', - marginBottom: 12, - color: '#1E293B', - }, - labelRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingVertical: 6, - borderBottomWidth: 1, - borderBottomColor: '#F1F5F9', - }, - labelText: { fontSize: 14, color: '#334155', flex: 1 }, - addLabelRow: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - marginTop: 12, - }, - input: { - flex: 1, - backgroundColor: '#F1F5F9', - borderRadius: 10, - padding: 10, - fontSize: 14, - color: '#0F172A', - }, - addButton: { - backgroundColor: 'navy', - borderRadius: 10, - width: 40, - height: 40, - alignItems: 'center', - justifyContent: 'center', - }, - classifyButton: { - width: '100%', - backgroundColor: 'navy', - padding: 14, - borderRadius: 12, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - gap: 8, - marginBottom: 20, - }, - classifyButtonText: { color: 'white', fontWeight: '600', fontSize: 15 }, - buttonDisabled: { backgroundColor: '#f0f0f0', borderColor: '#d3d3d3' }, - buttonTextDisabled: { color: 'gray' }, - resultRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingVertical: 6, - borderBottomWidth: 1, - borderBottomColor: '#F1F5F9', - }, - resultLabel: { fontSize: 14, color: '#334155', flex: 1 }, - topResultLabel: { fontWeight: '700', color: '#0F172A' }, - resultScore: { fontSize: 13, color: '#64748B', marginLeft: 8 }, - statsContainer: { marginTop: 12, gap: 2 }, - statsText: { fontSize: 12, color: '#94A3B8' }, -}); diff --git a/apps/legacy/text-embeddings/app/index.tsx b/apps/legacy/text-embeddings/app/index.tsx deleted file mode 100644 index 320fcfebf9..0000000000 --- a/apps/legacy/text-embeddings/app/index.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useRouter } from 'expo-router'; -import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; -import ColorPalette from '../colors'; -import ExecutorchLogo from '../assets/executorch.svg'; - -export default function Home() { - const router = useRouter(); - - return ( - - - Select a demo model - - router.navigate('text-embeddings/')}> - Text embeddings - - router.navigate('clip-embeddings/')}> - Clip embeddings - - - - ); -} - -export const fontSizes = { - xxl: 34, - xl: 22, - lg: 18, - md: 16, - sm: 14, - xs: 12, - xxs: 10, -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#fff', - }, - headerText: { - fontSize: fontSizes.lg, - color: ColorPalette.strongPrimary, - margin: 20, - }, - buttonContainer: { - width: '80%', - justifyContent: 'space-evenly', - marginBottom: 20, - }, - button: { - backgroundColor: ColorPalette.strongPrimary, - borderRadius: 8, - padding: 10, - alignItems: 'center', - marginBottom: 10, - }, - buttonText: { - color: 'white', - fontSize: fontSizes.md, - }, -}); diff --git a/apps/legacy/text-embeddings/app/text-embeddings/index.tsx b/apps/legacy/text-embeddings/app/text-embeddings/index.tsx deleted file mode 100644 index 958b828455..0000000000 --- a/apps/legacy/text-embeddings/app/text-embeddings/index.tsx +++ /dev/null @@ -1,364 +0,0 @@ -import { useEffect, useState } from 'react'; -import { - StyleSheet, - Text, - TextInput, - TouchableOpacity, - View, - SafeAreaView, - ScrollView, - KeyboardAvoidingView, - Platform, -} from 'react-native'; -import { Ionicons } from '@expo/vector-icons'; -import { ModelPicker } from '../../components/ModelPicker'; -import { models, useTextEmbeddings, TextEmbeddingsProps } from 'react-native-executorch/legacy'; -const textEmbedding = models.text_embedding; - -type TextEmbeddingModel = TextEmbeddingsProps['model']; - -const MODELS: { label: string; value: TextEmbeddingModel }[] = [ - { label: 'MiniLM L6', value: textEmbedding.all_minilm_l6_v2() }, - { - label: 'MPNet Base', - value: textEmbedding.all_mpnet_base_v2(), - }, - { - label: 'MultiQA MiniLM', - value: textEmbedding.multi_qa_minilm_l6_cos_v1(), - }, - { - label: 'MultiQA MPNet', - value: textEmbedding.multi_qa_mpnet_base_dot_v1(), - }, - { - label: 'Multilingual DistilUSE', - value: textEmbedding.distiluse_base_multilingual_cased_v2(), - }, - { - label: 'Multilingual Paraphrase', - value: textEmbedding.paraphrase_multilingual_minilm_l12_v2(), - }, -]; -import { useIsFocused } from 'expo-router'; -import { dotProduct } from '../../utils/math'; -import ErrorBanner from '../../components/ErrorBanner'; - -export default function TextEmbeddingsScreenWrapper() { - const isFocused = useIsFocused(); - - return isFocused ? : null; -} - -function TextEmbeddingsScreen() { - const [selectedModel, setSelectedModel] = useState( - textEmbedding.all_minilm_l6_v2() - ); - const model = useTextEmbeddings({ model: selectedModel }); - const [error, setError] = useState(null); - - const [inputSentence, setInputSentence] = useState(''); - const [sentencesWithEmbeddings, setSentencesWithEmbeddings] = useState< - { sentence: string; embedding: Float32Array }[] - >([]); - const [topMatches, setTopMatches] = useState<{ sentence: string; similarity: number }[]>([]); - const [embeddingTime, setEmbeddingTime] = useState(null); - - useEffect( - () => { - const computeEmbeddings = async () => { - if (!model.isReady) return; - - const sentences = [ - 'The weather is lovely today.', - "It's so sunny outside!", - 'He drove to the stadium.', - ]; - - try { - const embeddings = []; - for (const sentence of sentences) { - const embedding = await model.forward(sentence); - embeddings.push({ sentence, embedding }); - } - - setSentencesWithEmbeddings(embeddings); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - computeEmbeddings(); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [model.isReady] - ); - - const checkSimilarities = async () => { - if (!model.isReady || !inputSentence.trim()) return; - - try { - const start = Date.now(); - const inputEmbedding = await model.forward(inputSentence); - setEmbeddingTime(Date.now() - start); - const matches = sentencesWithEmbeddings.map(({ sentence, embedding }) => ({ - sentence, - similarity: dotProduct(inputEmbedding, embedding), - })); - matches.sort((a, b) => b.similarity - a.similarity); - setTopMatches(matches.slice(0, 3)); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - const addToSentences = async () => { - if (!model.isReady || !inputSentence.trim()) return; - - try { - const start = Date.now(); - const embedding = await model.forward(inputSentence); - setEmbeddingTime(Date.now() - start); - setSentencesWithEmbeddings((prev) => [...prev, { sentence: inputSentence, embedding }]); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - - setInputSentence(''); - setTopMatches([]); - }; - - const clearList = async () => { - if (!model.isReady) return; - try { - setSentencesWithEmbeddings([]); - } catch (e) { - setError(e instanceof Error ? e.message : String(e)); - } - }; - - const getModelStatusText = () => { - if (model.error) { - return `Oops! Error: ${model.error}`; - } - if (!model.isReady) { - return `Loading model ${(model.downloadProgress * 100).toFixed(2)}%`; - } - return model.isGenerating ? 'Generating...' : 'Model is ready'; - }; - - return ( - - - - Text Embeddings Playground - {getModelStatusText()} - { - setSelectedModel(m); - setSentencesWithEmbeddings([]); - setTopMatches([]); - }} - /> - setError(null)} /> - - - List of Existing Sentences - {sentencesWithEmbeddings.map((item, index) => ( - - - {item.sentence} - - ))} - - - Try Your Sentence - - - - - - Find Similar - - - - - - - Add to List - - - - - - Clear List - - - - - {embeddingTime !== null && ( - Embedding time: {embeddingTime} ms - )} - {topMatches.length > 0 && ( - - Top Matches - {topMatches.map((item, index) => ( - - {item.sentence} ({item.similarity.toFixed(2)}) - - ))} - - )} - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#F8FAFC', - }, - scrollContainer: { - padding: 20, - alignItems: 'center', - flexGrow: 1, - }, - heading: { - fontSize: 24, - fontWeight: '500', - marginBottom: 20, - color: '#0F172A', - }, - card: { - backgroundColor: '#FFFFFF', - width: '100%', - padding: 16, - borderRadius: 16, - borderColor: '#E2E8F0', - borderWidth: 2, - marginBottom: 20, - }, - sectionTitle: { - fontSize: 18, - fontWeight: '500', - marginBottom: 12, - color: '#1E293B', - }, - sentenceText: { - fontSize: 14, - marginBottom: 6, - color: '#334155', - }, - input: { - backgroundColor: '#F1F5F9', - borderRadius: 10, - padding: 10, - marginBottom: 10, - fontSize: 16, - color: '#0F172A', - minHeight: 40, - textAlignVertical: 'top', - }, - buttonContainer: { - width: '100%', - gap: 10, - }, - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-between', - gap: 10, - }, - buttonPrimary: { - flex: 1, - backgroundColor: 'navy', - padding: 12, - borderRadius: 10, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - buttonSecondary: { - flex: 1, - backgroundColor: 'transparent', - borderWidth: 2, - borderColor: 'navy', - padding: 12, - borderRadius: 10, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - buttonDisabled: { - backgroundColor: '#f0f0f0', - borderColor: '#d3d3d3', - }, - buttonText: { - color: 'white', - textAlign: 'center', - fontWeight: '500', - }, - buttonTextOutline: { - color: 'navy', - textAlign: 'center', - fontWeight: '500', - }, - buttonTextDisabled: { - color: 'gray', - }, - topMatchesContainer: { - marginTop: 20, - }, - statsText: { - fontSize: 13, - color: '#64748B', - marginTop: 8, - textAlign: 'center', - }, - flexContainer: { - flex: 1, - }, -}); diff --git a/apps/legacy/text-embeddings/assets/adaptive-icon.png b/apps/legacy/text-embeddings/assets/adaptive-icon.png deleted file mode 100644 index 03d6f6b6c6..0000000000 Binary files a/apps/legacy/text-embeddings/assets/adaptive-icon.png and /dev/null differ diff --git a/apps/legacy/text-embeddings/assets/executorch.svg b/apps/legacy/text-embeddings/assets/executorch.svg deleted file mode 100644 index e548ea4201..0000000000 --- a/apps/legacy/text-embeddings/assets/executorch.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/legacy/text-embeddings/assets/favicon.png b/apps/legacy/text-embeddings/assets/favicon.png deleted file mode 100644 index e75f697b18..0000000000 Binary files a/apps/legacy/text-embeddings/assets/favicon.png and /dev/null differ diff --git a/apps/legacy/text-embeddings/assets/icon.png b/apps/legacy/text-embeddings/assets/icon.png deleted file mode 100644 index a0b1526fc7..0000000000 Binary files a/apps/legacy/text-embeddings/assets/icon.png and /dev/null differ diff --git a/apps/legacy/text-embeddings/assets/splash-icon.png b/apps/legacy/text-embeddings/assets/splash-icon.png deleted file mode 100644 index 03d6f6b6c6..0000000000 Binary files a/apps/legacy/text-embeddings/assets/splash-icon.png and /dev/null differ diff --git a/apps/legacy/text-embeddings/babel.config.js b/apps/legacy/text-embeddings/babel.config.js deleted file mode 100644 index 9d89e13119..0000000000 --- a/apps/legacy/text-embeddings/babel.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function (api) { - api.cache(true); - return { - presets: ['babel-preset-expo'], - }; -}; diff --git a/apps/legacy/text-embeddings/colors.ts b/apps/legacy/text-embeddings/colors.ts deleted file mode 100644 index 03defcda9e..0000000000 --- a/apps/legacy/text-embeddings/colors.ts +++ /dev/null @@ -1,11 +0,0 @@ -const ColorPalette = { - primary: '#001A72', - seaBlueLight: '#E1F3FA', - seaBlueMedium: '#B5E1F1', - seaBlueDark: '#87CCE8', - blueLight: '#C1C6E5', - blueDark: '#6676AA', - strongPrimary: '#020F3C', -}; - -export default ColorPalette; diff --git a/apps/legacy/text-embeddings/components/ErrorBanner.tsx b/apps/legacy/text-embeddings/components/ErrorBanner.tsx deleted file mode 100644 index a5bebc504f..0000000000 --- a/apps/legacy/text-embeddings/components/ErrorBanner.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; - -interface ErrorBannerProps { - message: string | null; - onDismiss: () => void; -} - -export default function ErrorBanner({ message, onDismiss }: ErrorBannerProps) { - if (!message) return null; - - return ( - - - {message} - - - ✕ - - - ); -} - -const styles = StyleSheet.create({ - container: { - backgroundColor: '#FEE2E2', - borderLeftWidth: 4, - borderLeftColor: '#EF4444', - borderRadius: 8, - marginHorizontal: 16, - marginVertical: 8, - paddingVertical: 10, - paddingLeft: 12, - paddingRight: 8, - flexDirection: 'row', - alignItems: 'center', - }, - message: { - flex: 1, - color: '#991B1B', - fontSize: 14, - lineHeight: 20, - }, - closeButton: { - padding: 4, - marginLeft: 8, - }, - closeText: { - color: '#991B1B', - fontSize: 16, - fontWeight: '600', - }, -}); diff --git a/apps/legacy/text-embeddings/components/ModelPicker.tsx b/apps/legacy/text-embeddings/components/ModelPicker.tsx deleted file mode 100644 index 9a81bec371..0000000000 --- a/apps/legacy/text-embeddings/components/ModelPicker.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Dimensions, - Modal, - ScrollView, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; - -export type ModelOption = { - label: string; - value: T; -}; - -type Props = { - models: ModelOption[]; - selectedModel: T; - onSelect: (model: T) => void; - label?: string; - disabled?: boolean; -}; - -const DROPDOWN_MAX_HEIGHT = 200; - -export function ModelPicker({ models, selectedModel, onSelect, label, disabled }: Props) { - const [open, setOpen] = useState(false); - const [triggerHeight, setTriggerHeight] = useState(0); - const [expandUp, setExpandUp] = useState(false); - const [dropdownTop, setDropdownTop] = useState(0); - const triggerRef = useRef>(null); - const selected = models.find((m) => m.value === selectedModel); - - useEffect(() => { - if (disabled) setOpen(false); - }, [disabled]); - - const handlePress = () => { - if (disabled) return; - if (open) { - setOpen(false); - return; - } - triggerRef.current?.measure( - (_x: number, _y: number, _width: number, height: number, _pageX: number, pageY: number) => { - setTriggerHeight(height); - const spaceBelow = Dimensions.get('window').height - (pageY + height); - setExpandUp(spaceBelow < DROPDOWN_MAX_HEIGHT); - setDropdownTop(pageY); - setOpen(true); - } - ); - }; - - const dropdownStylePosition = expandUp - ? { - bottom: Dimensions.get('window').height - dropdownTop, - left: 12, - right: 12, - } - : { - top: dropdownTop + triggerHeight + 2, - left: 12, - right: 12, - }; - - return ( - <> - - - {label && {label}} - {selected?.label ?? '—'} - {open ? '▲' : '▼'} - - - - {open && ( - setOpen(false)} - animationType="none" - > - setOpen(false)} - /> - - - {models.map((item) => { - const isSelected = item.value === selectedModel; - return ( - { - onSelect(item.value); - setOpen(false); - }} - activeOpacity={0.7} - > - - {item.label} - - - ); - })} - - - - )} - - ); -} - -const styles = StyleSheet.create({ - container: { - marginHorizontal: 12, - marginVertical: 4, - alignSelf: 'stretch', - zIndex: 100, - }, - trigger: { - flexDirection: 'row', - alignItems: 'center', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 10, - backgroundColor: '#f5f5f5', - }, - triggerDisabled: { - opacity: 0.4, - }, - label: { - fontSize: 12, - color: '#888', - marginRight: 6, - }, - triggerText: { - flex: 1, - fontSize: 14, - color: '#001A72', - fontWeight: '500', - }, - chevron: { - fontSize: 10, - color: '#888', - marginLeft: 6, - }, - modalBackdrop: { - flex: 1, - backgroundColor: 'rgba(0, 0, 0, 0.3)', - }, - dropdown: { - position: 'absolute', - borderWidth: 1, - borderColor: '#C1C6E5', - borderRadius: 8, - backgroundColor: '#fff', - maxHeight: DROPDOWN_MAX_HEIGHT, - zIndex: 1000, - elevation: 5, - shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.15, - shadowRadius: 6, - }, - option: { - paddingHorizontal: 12, - paddingVertical: 10, - borderBottomWidth: 1, - borderBottomColor: '#f0f0f0', - }, - optionSelected: { - backgroundColor: '#e8ecf8', - }, - optionText: { - fontSize: 14, - color: '#333', - }, - optionTextSelected: { - color: '#001A72', - fontWeight: '600', - }, -}); diff --git a/apps/legacy/text-embeddings/context.ts b/apps/legacy/text-embeddings/context.ts deleted file mode 100644 index 0eb0560768..0000000000 --- a/apps/legacy/text-embeddings/context.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react'; - -export const GeneratingContext = createContext({ - setGlobalGenerating: (_newState: boolean) => {}, -}); diff --git a/apps/legacy/text-embeddings/declarations.d.ts b/apps/legacy/text-embeddings/declarations.d.ts deleted file mode 100644 index 85e178f497..0000000000 --- a/apps/legacy/text-embeddings/declarations.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module '*.svg' { - import { SvgProps } from 'react-native-svg'; - const content: React.FV; - export default content; -} diff --git a/apps/legacy/text-embeddings/index.ts b/apps/legacy/text-embeddings/index.ts deleted file mode 100644 index 3f443dcf95..0000000000 --- a/apps/legacy/text-embeddings/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerRootComponent } from 'expo'; - -import App from './app'; - -// registerRootComponent calls AppRegistry.registerComponent('main', () => App); -// It also ensures that whether you load the app in Expo Go or in a native build, -// the environment is set up appropriately -registerRootComponent(App); diff --git a/apps/legacy/text-embeddings/metro.config.js b/apps/legacy/text-embeddings/metro.config.js deleted file mode 100644 index f8ab2ab96d..0000000000 --- a/apps/legacy/text-embeddings/metro.config.js +++ /dev/null @@ -1,21 +0,0 @@ -// Learn more https://docs.expo.io/guides/customizing-metro -const { getDefaultConfig } = require('expo/metro-config'); - -/** @type {import('expo/metro-config').MetroConfig} */ -const config = getDefaultConfig(__dirname); - -const { transformer, resolver } = config; - -config.transformer = { - ...transformer, - babelTransformerPath: require.resolve('react-native-svg-transformer/expo'), -}; -config.resolver = { - ...resolver, - assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'), - sourceExts: [...resolver.sourceExts, 'svg'], -}; - -config.resolver.assetExts.push('pte'); - -module.exports = config; diff --git a/apps/legacy/text-embeddings/package.json b/apps/legacy/text-embeddings/package.json deleted file mode 100644 index d26de3d022..0000000000 --- a/apps/legacy/text-embeddings/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "legacy-text-embeddings", - "version": "1.0.0", - "main": "expo-router/entry", - "scripts": { - "start": "expo start", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "typecheck": "tsc", - "lint": "eslint . --ext .ts,.tsx --fix" - }, - "dependencies": { - "@expo/log-box": "~56.0.12", - "@expo/vector-icons": "^15.0.2", - "@react-native/metro-config": "^0.86.0", - "@react-navigation/drawer": "^7.9.4", - "@react-navigation/native": "^7.2.2", - "expo": "~57.0.28", - "expo-build-properties": "~57.0.22", - "expo-constants": "~57.0.21", - "expo-font": "~57.0.4", - "expo-linking": "~57.0.12", - "expo-router": "~57.0.26", - "expo-status-bar": "~57.0.1", - "react": "19.2.3", - "react-native": "0.86.3", - "react-native-executorch": "workspace:*", - "react-native-executorch-expo-resource-fetcher": "workspace:*", - "react-native-gesture-handler": "~2.32.0", - "react-native-image-picker": "^8.2.1", - "react-native-reanimated": "4.7.1", - "react-native-safe-area-context": "~5.7.0", - "react-native-screens": "~4.26.0", - "react-native-svg": "15.15.4", - "react-native-worklets": "0.13.0" - }, - "devDependencies": { - "@babel/core": "^7.29.7", - "@types/react": "~19.2.14", - "@types/react-refresh": "^0", - "babel-preset-expo": "~57.0.15", - "react-refresh": "^0.18.0" - }, - "private": true -} diff --git a/apps/legacy/text-embeddings/tsconfig.json b/apps/legacy/text-embeddings/tsconfig.json deleted file mode 100644 index 1063e3da3b..0000000000 --- a/apps/legacy/text-embeddings/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "jsx": "react-native", - "lib": ["es2022"], - "moduleResolution": "bundler", - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "esnext", - "paths": { - "react-native-executorch/legacy": ["../../packages/react-native-executorch/legacy/src"], - "react-native-executorch-expo-resource-fetcher": ["../../packages/expo-resource-fetcher"] - } - }, - "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["**/node_modules", "**/Pods", "**/__tests__"] -} diff --git a/apps/legacy/text-embeddings/utils/math.ts b/apps/legacy/text-embeddings/utils/math.ts deleted file mode 100644 index 35816e6681..0000000000 --- a/apps/legacy/text-embeddings/utils/math.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { RnExecutorchError, RnExecutorchErrorCode } from 'react-native-executorch/legacy'; - -export const dotProduct = (a: Float32Array, b: Float32Array) => { - if (a.length !== b.length) { - throw new RnExecutorchError( - RnExecutorchErrorCode.WrongDimensions, - `dotProduct needs both vector to have the same length: got a: ${a.length}, b: ${b.length}` - ); - } - - let sum = 0; - for (let i = 0; i < a.length; i++) { - sum += a[i] * b[i]; - } - return sum; -}; diff --git a/docs/docs/01-fundamentals/03-migrating-from-v0-9.md b/docs/docs/01-fundamentals/03-migrating-from-v0-9.md index 9986ed86df..ee067abdbb 100644 --- a/docs/docs/01-fundamentals/03-migrating-from-v0-9.md +++ b/docs/docs/01-fundamentals/03-migrating-from-v0-9.md @@ -2,16 +2,7 @@ title: Migrating from v0.9.x slug: /fundamentals/migrating-from-v0-9 description: 'Step-by-step migration guide from legacy v0.9.x to the new v0.10.0 architecture in React Native ExecuTorch.' -keywords: - [ - react native, - react native ai, - executorch, - migration guide, - v0.10, - legacy api, - react-native-executorch/legacy, - ] +keywords: [react native, react native ai, executorch, migration guide, v0.10, legacy api] --- import Tabs from '@theme/Tabs'; @@ -28,43 +19,10 @@ While the new API is not backwards-compatible with v0.9.x, the library provides **100% functional parity**: every single task, model, and capability supported in v0.9.x is supported in v0.10.0, alongside numerous new models and features. -To ensure your existing applications continue to function without requiring an -immediate code overhaul, v0.10.0 ships the complete pre-0.10 implementation under -a dedicated legacy entry point: `'react-native-executorch/legacy'`. +The legacy compatibility entry point has been removed. Applications upgrading +from v0.9.x must migrate to the new API. -## Upgrading Paths - -When updating to `0.10.x`, you have two ways forward: - -### Option A: The Legacy Compatibility Module (`/legacy`) - -We did not want to break existing applications overnight with an abrupt, all-or-nothing -upgrade. To support a **gradual refactoring process**, we preserved the entire v0.9.x API -under a dedicated legacy subpath. You can update your package version right away and -transition individual screens or pipelines to the new v0.10 architecture at your own pace. - -To keep existing code working, simply update your imports to point to `'react-native-executorch/legacy'`: - -```typescript -// Before (v0.9.x) -import { initExecutorch, useClassification, models } from 'react-native-executorch'; - -// After (v0.10.0 legacy compatibility) -import { initExecutorch, useClassification, models } from 'react-native-executorch/legacy'; -``` - -All previous hooks, modules (`ClassificationModule`, `LLMModule`, etc.), options, -and types will continue to function exactly as they did in v0.9.x. Both the legacy and -new APIs can even coexist in the same project while you migrate component by component. - -:::caution Legacy Deprecation -The legacy API is deprecated and will be removed in a future release. We -recommend using this entry point as a temporary stepping stone to migrate to the -new v0.10 API incrementally. Refer to the [v0.10.0-legacy archived documentation](/react-native-executorch/docs/0.10.0-legacy/fundamentals/getting-started) -for legacy reference. -::: - -### Option B: Migrating to the New v0.10 Architecture +## Migrating to the New Architecture Migrating to the new v0.10 API gives you access to transparent pipelines, deterministic memory management, worklet-safe thread dispatch, and lower-level @@ -72,10 +30,6 @@ runtime primitives. ## Dependencies -The peer dependencies required by your application depend on which API you use: - -### For the New v0.10 API - In v0.10, resource fetching and threading have been built directly into the core runtime: @@ -126,17 +80,6 @@ pnpm add react-native-executorch react-native-worklets react-native-blob-util -### For the Legacy API (`react-native-executorch/legacy`) - -If you remain on `'react-native-executorch/legacy'`, you must keep your existing -fetcher adapter package: - -- Bare React Native: `@software-mansion/react-native-executorch-bare-resource-fetcher` -- Expo: `@software-mansion/react-native-executorch-expo-resource-fetcher` - -You must also continue calling `initExecutorch({ resourceFetcher: ... })` at the -root of your application before using any legacy modules. - ## Old vs. New: What Changed and Why React Native ExecuTorch v0.10 achieves **100% functional parity** with v0.9: every computer @@ -213,8 +156,7 @@ However, the internal mechanics and Developer Experience have been fundamentally the app binary, inflating application size regardless of which tasks were used. - **In v0.10**: Native libraries and hardware backends are now downloaded on demand at install time and can be selectively filtered through the `react-native-executorch` block in your - `package.json`. This optimization benefits all applications upgrading to v0.10, even those - temporarily using the legacy compatibility entry point (`react-native-executorch/legacy`). + `package.json`. This optimization benefits all applications using the new API. See [Native Libraries](../03-core-and-advanced/08-native-libraries.md). ## Next Steps diff --git a/docs/docs/03-core-and-advanced/08-native-libraries.md b/docs/docs/03-core-and-advanced/08-native-libraries.md index ae56a31275..2fa7fb6ac4 100644 --- a/docs/docs/03-core-and-advanced/08-native-libraries.md +++ b/docs/docs/03-core-and-advanced/08-native-libraries.md @@ -95,7 +95,7 @@ Specifying a task under `features` is shorthand: it automatically expands to the | `classification` | xnnpack, coreml | opencv | | `depthEstimation` | xnnpack, coreml, vulkan | opencv | | `objectDetection` | xnnpack, coreml, vulkan | opencv | -| `keypointDetection` | xnnpack, coreml, mlx, vulkan | opencv | +| `keypointDetection` | xnnpack, coreml, vulkan | opencv | | `semanticSegmentation` | xnnpack, coreml, vulkan | opencv | | `instanceSegmentation` | xnnpack, coreml, vulkan | opencv | | `ocr` | xnnpack, coreml, vulkan | opencv | diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index aac31cab85..7d86f4c50a 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -36,7 +36,7 @@ const config = { editUrl: 'https://github.com/software-mansion/react-native-executorch/edit/main/docs', lastVersion: '0.11.x', versions: { - // === LEGACY SUPPORT ===: versioned docs for the legacy API (remove when legacy API is dropped) + // Keep the archived legacy API documentation available for existing releases. '0.10.0-legacy': { label: '0.10.0-legacy', banner: 'unmaintained', diff --git a/package.json b/package.json index 30b0b8d049..41f61bca8b 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,16 @@ "name": "react-native-executorch-monorepo", "version": "0.0.0", "packageManager": "yarn@4.1.1", - "// === LEGACY SUPPORT: workspaces ===": "apps/legacy/* contains preserved legacy example apps (remove when legacy apps are dropped)", "workspaces": { "packages": [ "packages/*", - "apps/*", - "apps/legacy/*" + "apps/*" ] }, "scripts": { - "prepare": "yarn workspaces foreach --all --exclude react-native-executorch-webrtc --topological-dev run prepare", - "lint": "yarn workspaces foreach --all --exclude react-native-executorch-webrtc --parallel run lint", - "typecheck": "yarn prepare && yarn workspaces foreach --all --exclude react-native-executorch-webrtc --parallel run typecheck" + "prepare": "yarn workspaces foreach --all --topological-dev run prepare", + "lint": "yarn workspaces foreach --all --parallel run lint", + "typecheck": "yarn prepare && yarn workspaces foreach --all --parallel run typecheck" }, "private": true, "devDependencies": { diff --git a/packages/bare-resource-fetcher/LICENSE b/packages/bare-resource-fetcher/LICENSE deleted file mode 100644 index 10b6cc18c3..0000000000 --- a/packages/bare-resource-fetcher/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Software Mansion - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/bare-resource-fetcher/README.md b/packages/bare-resource-fetcher/README.md deleted file mode 100644 index 13da7647c0..0000000000 --- a/packages/bare-resource-fetcher/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# react-native-executorch-bare-resource-fetcher - -Bare React Native adapter for `react-native-executorch` that provides resource fetching capabilities using native filesystem libraries. - -## Installation - -```bash -yarn add react-native-executorch-bare-resource-fetcher -yarn add @dr.pogodin/react-native-fs @kesha-antonov/react-native-background-downloader -``` - -### Native Dependencies Setup - -After installing, follow the setup guides for the native dependencies: - -- **[@dr.pogodin/react-native-fs](https://github.com/birdofpreyru/react-native-fs#getting-started)** - Filesystem operations -- **[@kesha-antonov/react-native-background-downloader](https://github.com/kesha-antonov/react-native-background-downloader#bare-react-native-projects)** - Background download support - -> **Note**: Make sure to complete the native setup (iOS/Android configuration) for both dependencies before using this adapter. - -## Usage - -```typescript -import { initExecutorch } from 'react-native-executorch'; -import { BareResourceFetcher } from 'react-native-executorch-bare-resource-fetcher'; - -initExecutorch({ - resourceFetcher: BareResourceFetcher, -}); -``` - -## When to Use - -Use this adapter if you're working with: -- Bare React Native projects (created with `npx @react-native-community/cli@latest init`) -- Projects that need true background downloads -- Projects requiring direct native filesystem access diff --git a/packages/bare-resource-fetcher/package.json b/packages/bare-resource-fetcher/package.json deleted file mode 100644 index c68def9648..0000000000 --- a/packages/bare-resource-fetcher/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "react-native-executorch-bare-resource-fetcher", - "version": "1.0.0", - "description": "Bare React Native resource fetcher for react-native-executorch", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "exports": { - ".": { - "import": "./lib/index.js", - "types": "./lib/index.d.ts" - } - }, - "files": [ - "lib" - ], - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/software-mansion/react-native-executorch.git", - "directory": "packages/bare-resource-fetcher" - }, - "scripts": { - "prepare": "tsc", - "typecheck": "tsc --noEmit", - "lint": "eslint \"**/*.{js,ts,tsx}\"", - "clean": "del-cli lib" - }, - "peerDependencies": { - "@dr.pogodin/react-native-fs": "^2.0.0", - "@kesha-antonov/react-native-background-downloader": "^4.0.0", - "react-native": "*", - "react-native-executorch": "*" - }, - "devDependencies": { - "@dr.pogodin/react-native-fs": "^2.36.2", - "@kesha-antonov/react-native-background-downloader": "^4.4.5", - "@types/react": "~19.1.10", - "react": "19.1.0", - "react-native": "0.81.5", - "react-native-executorch": "workspace:*", - "typescript": "~5.9.2" - } -} diff --git a/packages/bare-resource-fetcher/src/ResourceFetcher.ts b/packages/bare-resource-fetcher/src/ResourceFetcher.ts deleted file mode 100644 index b09671c511..0000000000 --- a/packages/bare-resource-fetcher/src/ResourceFetcher.ts +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Resource Fetcher for React Native applications. - * - * This module provides functions to download and manage files stored in the application's document directory - * inside the `react-native-executorch/` directory. These utilities help manage storage and clean up downloaded - * files when they are no longer needed. - * @category Utilities - General - * @remarks - * **Key Functionality:** - * - **Download Control**: Pause, resume, and cancel operations through: - * - {@link pauseFetching} - Pause ongoing downloads - * - {@link resumeFetching} - Resume paused downloads - * - {@link cancelFetching} - Cancel ongoing or paused downloads - * - **File Management**: - * - {@link getFilesTotalSize} - Get total size of resources - * - {@link listDownloadedFiles} - List all downloaded files - * - {@link listDownloadedModels} - List downloaded model files (.pte) - * - {@link deleteResources} - Delete downloaded resources - * - * **Important Notes:** - * - Pause/resume operations are only supported on iOS - * - Most functions accept multiple `ResourceSource` arguments (string, number, or object) - * - The {@link fetch} method accepts a progress callback (0-1) and returns file paths or null if interrupted - */ - -import * as RNFS from '@dr.pogodin/react-native-fs'; -import { Platform } from 'react-native'; -import { RNEDirectory } from './constants/directories'; -import { - ResourceSource, - RnExecutorchErrorCode, - RnExecutorchError, - BaseResourceFetcherClass, -} from 'react-native-executorch/legacy'; -import { ResourceFetcherUtils, DownloadStatus } from './ResourceFetcherUtils'; -import { - type ActiveDownload, - handleObject, - handleLocalFile, - handleAsset, - handleRemote, -} from './handlers'; - -class BareResourceFetcherClass extends BaseResourceFetcherClass { - protected downloads = new Map(); - - protected async getFilesSizes(sources: ResourceSource[]) { - return ResourceFetcherUtils.getFilesSizes(sources); - } - - protected async handleObject(source: object): Promise { - return handleObject(source); - } - - protected handleLocalFile(source: string): string { - return handleLocalFile(source); - } - - protected handleAsset( - source: number, - progressCallback: (progress: number) => void - ): Promise { - return handleAsset(source, progressCallback, this.downloads); - } - - protected handleRemote( - uri: string, - source: ResourceSource, - progressCallback: (progress: number) => void - ): Promise<{ path: string; wasDownloaded: boolean }> { - return handleRemote(uri, source, progressCallback, this.downloads); - } - - protected async pause(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - if (downloadHandle.status === DownloadStatus.PAUSED) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherAlreadyPaused, - "The file download is currently paused. Can't pause the download of the same file twice." - ); - } - if (Platform.OS === 'android') { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherPlatformNotSupported, - 'Pause is not supported on Android. Use cancelFetching and re-fetch instead.' - ); - } - if (!downloadHandle.task) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - 'Download task is missing. This should not happen on iOS.' - ); - } - downloadHandle.status = DownloadStatus.PAUSED; - downloadHandle.task.pause(); - } - - protected async resume(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - if (downloadHandle.status === DownloadStatus.ONGOING) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherAlreadyOngoing, - "The file download is currently ongoing. Can't resume the ongoing download." - ); - } - if (Platform.OS === 'android') { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherPlatformNotSupported, - 'Resume is not supported on Android. Use fetch to restart the download.' - ); - } - if (!downloadHandle.task) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - 'Download task is missing. This should not happen on iOS.' - ); - } - // Set status back to ONGOING before resuming so the .done() callback - // registered in handleRemote knows it's safe to proceed (not paused/canceled). - downloadHandle.status = DownloadStatus.ONGOING; - downloadHandle.task.resume(); - } - - protected async cancel(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - - if (Platform.OS === 'ios') { - if (!downloadHandle.task) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - 'Download task is missing. This should not happen on iOS.' - ); - } - downloadHandle.task.stop(); - } else if (Platform.OS === 'android') { - if (downloadHandle.jobId === undefined) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - 'Download job ID is missing. This should not happen on Android.' - ); - } - RNFS.stopDownload(downloadHandle.jobId); - if (await ResourceFetcherUtils.checkFileExists(downloadHandle.cacheFileUri)) { - await RNFS.unlink(downloadHandle.cacheFileUri); - } - } - - this.downloads.delete(source); - downloadHandle.reject( - new RnExecutorchError(RnExecutorchErrorCode.DownloadInterrupted, 'Download was canceled.') - ); - } - - /** - * Lists all the downloaded files used by React Native ExecuTorch. - * @returns A promise that resolves to an array of URIs for all the downloaded files. - */ - async listDownloadedFiles(): Promise { - const files = await RNFS.readDir(RNEDirectory); - return files.map((file: any) => file.path); - } - - /** - * Lists all the downloaded models used by React Native ExecuTorch. - * @returns A promise, which resolves to an array of URIs for all the downloaded models. - */ - async listDownloadedModels() { - const files = await this.listDownloadedFiles(); - return files.filter((file: string) => file.endsWith('.pte')); - } - - /** - * Deletes downloaded resources from the local filesystem. - * @param sources - The resource identifiers used when calling `fetch`. - * @returns A promise that resolves once all specified resources have been removed. - */ - async deleteResources(...sources: ResourceSource[]): Promise { - for (const source of sources) { - const filename = ResourceFetcherUtils.getFilenameFromUri(source as string); - const fileUri = `${RNEDirectory}${filename}`; - if (await ResourceFetcherUtils.checkFileExists(fileUri)) { - await RNFS.unlink(fileUri); - } - } - } - - /** - * Fetches the total size of remote files. Works only for remote files. - * @param sources - The resource identifiers (URLs). - * @returns A promise that resolves to the combined size of files in bytes. - */ - async getFilesTotalSize(...sources: ResourceSource[]): Promise { - return (await ResourceFetcherUtils.getFilesSizes(sources)).totalLength; - } - - /** - * Reads the contents of a file as a string. - * @param path - Absolute file path to read. - * @returns A promise that resolves to the file contents as a string. - */ - async readAsString(path: string): Promise { - return RNFS.readFile(path, 'utf8'); - } -} - -export const BareResourceFetcher = new BareResourceFetcherClass(); diff --git a/packages/bare-resource-fetcher/src/ResourceFetcherUtils.ts b/packages/bare-resource-fetcher/src/ResourceFetcherUtils.ts deleted file mode 100644 index 0b619b5df1..0000000000 --- a/packages/bare-resource-fetcher/src/ResourceFetcherUtils.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { RNEDirectory } from './constants/directories'; -import { - ResourceSource, - Logger, - ResourceFetcherUtils as CoreUtils, - HTTP_CODE, - DownloadStatus, - SourceType, - RnExecutorchError, - RnExecutorchErrorCode, -} from 'react-native-executorch/legacy'; -import { Image } from 'react-native'; -import * as RNFS from '@dr.pogodin/react-native-fs'; - -export { HTTP_CODE, DownloadStatus, SourceType }; - -/** - * Utility functions for fetching and managing resources. - * @category Utilities - General - */ -export namespace ResourceFetcherUtils { - export const removeFilePrefix = CoreUtils.removeFilePrefix; - export const hashObject = CoreUtils.hashObject; - export const calculateDownloadProgress = CoreUtils.calculateDownloadProgress; - export const triggerHuggingFaceDownloadCounter = CoreUtils.triggerHuggingFaceDownloadCounter; - export const triggerDownloadEvent = CoreUtils.triggerDownloadEvent; - export const getFilenameFromUri = CoreUtils.getFilenameFromUri; - - export function getType(source: ResourceSource): SourceType { - if (typeof source === 'object') { - return SourceType.OBJECT; - } else if (typeof source === 'number') { - const uri = Image.resolveAssetSource(source).uri; - if (uri.startsWith('http')) { - return SourceType.DEV_MODE_FILE; - } - return SourceType.RELEASE_MODE_FILE; - } - // typeof source == 'string' - if (source.startsWith('file://')) { - return SourceType.LOCAL_FILE; - } - return SourceType.REMOTE_FILE; - } - - export async function getFilesSizes(sources: ResourceSource[]) { - const results: Array<{ - source: ResourceSource; - type: SourceType; - length: number; - previousFilesTotalLength: number; - }> = []; - let totalLength = 0; - let previousFilesTotalLength = 0; - for (const source of sources) { - const type = ResourceFetcherUtils.getType(source); - let length = 0; - try { - if (type === SourceType.REMOTE_FILE && typeof source === 'string') { - const response = await fetch(source, { method: 'HEAD' }); - if (!response.ok) { - Logger.warn(`Failed to fetch HEAD for ${source}: ${response.status}`); - continue; - } - - const contentLength = response.headers.get('content-length'); - if (!contentLength) { - Logger.warn(`No content-length header for ${source}`); - } - - length = contentLength ? parseInt(contentLength, 10) : 0; - previousFilesTotalLength = totalLength; - totalLength += length; - } - } catch (error) { - Logger.warn(`Error fetching HEAD for ${source}:`, error); - continue; - } finally { - results.push({ source, type, length, previousFilesTotalLength }); - } - } - return { results, totalLength }; - } - - export async function createDirectoryIfNoExists() { - if (!(await checkFileExists(RNEDirectory))) { - try { - await RNFS.mkdir(RNEDirectory); - } catch (error) { - throw new RnExecutorchError( - RnExecutorchErrorCode.FileWriteFailed, - `Failed to create directory at ${RNEDirectory}`, - error - ); - } - } - } - - export async function checkFileExists(fileUri: string) { - return await RNFS.exists(fileUri); - } -} diff --git a/packages/bare-resource-fetcher/src/constants/directories.ts b/packages/bare-resource-fetcher/src/constants/directories.ts deleted file mode 100644 index 4ba8b84011..0000000000 --- a/packages/bare-resource-fetcher/src/constants/directories.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { directories } from '@kesha-antonov/react-native-background-downloader'; - -export const RNEDirectory = `${directories.documents}/react-native-executorch/`; diff --git a/packages/bare-resource-fetcher/src/declarations.d.ts b/packages/bare-resource-fetcher/src/declarations.d.ts deleted file mode 100644 index 8a3a9e1023..0000000000 --- a/packages/bare-resource-fetcher/src/declarations.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -declare module '@kesha-antonov/react-native-background-downloader' { - export const directories: { - documents: string; - library: string; - temp: string; - }; - - export interface DownloadTask { - id: string; - percent: number; - stop(): void; - pause(): void; - resume(): void; - start(): void; - - begin(handler: (params: BeginHandlerParams) => void): DownloadTask; - progress(handler: (params: ProgressHandlerParams) => void): DownloadTask; - done(handler: () => void): DownloadTask; - error(handler: (error: any) => void): DownloadTask; - } - - export interface BeginHandlerParams { - expectedBytes: number; - headers: { [key: string]: string }; - } - - export interface ProgressHandlerParams { - percent: number; - bytesDownloaded: number; - bytesTotal: number; - } - - export function createDownloadTask(options: { - id: string; - url: string; - destination: string; - headers?: object; - }): DownloadTask; - - export function completeHandler(jobId: string): void; - - const RNBackgroundDownloader: { - download(options: { id: string; url: string; destination: string }): DownloadTask; - directories: { documents: string; library: string; temp: string }; - }; - export default RNBackgroundDownloader; -} - -declare module '@dr.pogodin/react-native-fs'; diff --git a/packages/bare-resource-fetcher/src/handlers.ts b/packages/bare-resource-fetcher/src/handlers.ts deleted file mode 100644 index 7c66894d9e..0000000000 --- a/packages/bare-resource-fetcher/src/handlers.ts +++ /dev/null @@ -1,270 +0,0 @@ -import { - createDownloadTask, - completeHandler, - DownloadTask, - BeginHandlerParams, - ProgressHandlerParams, -} from '@kesha-antonov/react-native-background-downloader'; -import * as RNFS from '@dr.pogodin/react-native-fs'; -import { Image, Platform } from 'react-native'; -import { - ResourceSource, - RnExecutorchErrorCode, - RnExecutorchError, -} from 'react-native-executorch/legacy'; -import { RNEDirectory } from './constants/directories'; -import { ResourceFetcherUtils, DownloadStatus } from './ResourceFetcherUtils'; - -export interface ActiveDownload { - status: DownloadStatus; - uri: string; - fileUri: string; - cacheFileUri: string; - // resolve and reject are the resolve/reject of the Promise returned by handleRemote. - // They are stored here so that cancel() and resume() in the fetcher class can - // unblock the fetch() loop from outside the download flow. - resolve: (path: string) => void; - reject: (error: unknown) => void; - // iOS only: background downloader task, used for pause/resume/cancel - task?: DownloadTask; - // Android only: RNFS job ID, used for cancel via RNFS.stopDownload - jobId?: number; -} - -interface DownloadContext { - uri: string; - source: ResourceSource; - fileUri: string; - cacheFileUri: string; - progressCallback: (progress: number) => void; - downloads: Map; - resolve: (path: string) => void; - reject: (error: unknown) => void; -} - -export async function handleObject(source: object): Promise { - const jsonString = JSON.stringify(source); - const digest = ResourceFetcherUtils.hashObject(jsonString); - const path = `${RNEDirectory}${digest}.json`; - - if (await ResourceFetcherUtils.checkFileExists(path)) { - return ResourceFetcherUtils.removeFilePrefix(path); - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - await RNFS.writeFile(path, jsonString, 'utf8'); - return ResourceFetcherUtils.removeFilePrefix(path); -} - -export function handleLocalFile(source: string): string { - return ResourceFetcherUtils.removeFilePrefix(source); -} - -export async function handleAsset( - source: number, - progressCallback: (progress: number) => void, - downloads: Map -): Promise { - const assetSource = Image.resolveAssetSource(source); - const uri = assetSource.uri; - - if (uri.startsWith('http')) { - // Dev mode: asset served from Metro dev server. - // uri is the resolved HTTP URL; source is the original require() number the - // user holds, so it must be used as the downloads map key for pause/cancel to work. - return (await handleRemote(uri, source, progressCallback, downloads)).path; - } - - // Release mode: asset bundled locally, copy to RNEDirectory - const filename = ResourceFetcherUtils.getFilenameFromUri(uri); - const fileUri = `${RNEDirectory}${filename}`; - - if (await ResourceFetcherUtils.checkFileExists(fileUri)) { - return ResourceFetcherUtils.removeFilePrefix(fileUri); - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - if (uri.startsWith('file')) { - await RNFS.copyFile(uri, fileUri); - } - return ResourceFetcherUtils.removeFilePrefix(fileUri); -} - -function startAndroidDownload(ctx: DownloadContext): void { - const { uri, source, fileUri, cacheFileUri, progressCallback, downloads, resolve, reject } = ctx; - - const rnfsDownload = RNFS.downloadFile({ - fromUrl: uri, - toFile: cacheFileUri, - progress: (res: { bytesWritten: number; contentLength: number }) => { - if (res.contentLength > 0) { - progressCallback(res.bytesWritten / res.contentLength); - } - }, - progressInterval: 500, - }); - - downloads.set(source, { - status: DownloadStatus.ONGOING, - uri, - fileUri, - cacheFileUri, - resolve, - reject, - jobId: rnfsDownload.jobId, - }); - - rnfsDownload.promise - .then(async (result: { statusCode: number }) => { - if (!downloads.has(source)) return; // canceled externally via cancel() - - if (result.statusCode < 200 || result.statusCode >= 300) { - downloads.delete(source); - reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to fetch resource from '${uri}', status: ${result.statusCode}` - ) - ); - return; - } - - try { - await RNFS.moveFile(cacheFileUri, fileUri); - } catch (error) { - downloads.delete(source); - reject(error); - return; - } - - downloads.delete(source); - resolve(ResourceFetcherUtils.removeFilePrefix(fileUri)); - }) - .catch((error: unknown) => { - if (!downloads.has(source)) return; // canceled externally - downloads.delete(source); - reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to fetch resource from '${uri}', context: ${error}` - ) - ); - }); -} - -function startIOSDownload(ctx: DownloadContext): void { - const { uri, source, fileUri, cacheFileUri, progressCallback, downloads, resolve, reject } = ctx; - const filename = cacheFileUri.split('/').pop()!; - - const task = createDownloadTask({ - id: filename, - url: uri, - destination: cacheFileUri, - }) - .begin((_: BeginHandlerParams) => progressCallback(0)) - .progress((progress: ProgressHandlerParams) => { - progressCallback(progress.bytesDownloaded / progress.bytesTotal); - }) - .done(async () => { - const downloadHandle = downloads.get(source); - // If paused or canceled, resolve/reject will be called externally — do nothing here. - if (!downloadHandle || downloadHandle.status === DownloadStatus.PAUSED) return; - - try { - await RNFS.moveFile(cacheFileUri, fileUri); - // Required by the background downloader library to signal iOS that the - // background download session is complete. - const fn = fileUri.split('/').pop(); - if (fn) completeHandler(fn); - } catch (error) { - downloads.delete(source); - reject(error); - return; - } - - downloads.delete(source); - resolve(ResourceFetcherUtils.removeFilePrefix(fileUri)); - }) - .error((error: any) => { - if (!downloads.has(source)) return; // canceled externally - downloads.delete(source); - reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to fetch resource from '${uri}', context: ${error}` - ) - ); - }); - - task.start(); - - downloads.set(source, { - status: DownloadStatus.ONGOING, - uri, - fileUri, - cacheFileUri, - resolve, - reject, - task, - }); -} - -// uri and source are separate parameters because for asset sources (dev mode), -// source is the require() number the user holds (used as the downloads map key), -// while uri is the resolved HTTP URL needed for the actual download. -// For plain remote strings they are the same value. -export async function handleRemote( - uri: string, - source: ResourceSource, - progressCallback: (progress: number) => void, - downloads: Map -): Promise<{ path: string; wasDownloaded: boolean }> { - if (downloads.has(source)) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadInProgress, - 'Already downloading this file' - ); - } - - const filename = ResourceFetcherUtils.getFilenameFromUri(uri); - const fileUri = `${RNEDirectory}${filename}`; - const cacheFileUri = `${RNFS.CachesDirectoryPath}/${filename}`; - - if (await ResourceFetcherUtils.checkFileExists(fileUri)) { - return { - path: ResourceFetcherUtils.removeFilePrefix(fileUri), - wasDownloaded: false, - }; - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - - // We need a Promise whose resolution can be triggered from outside this function — - // by cancel() or resume() in the fetcher class. A plain async function can't do that, - // so we create the Promise manually and store resolve/reject in the downloads map. - let resolve: (path: string) => void = () => {}; - let reject: (error: unknown) => void = () => {}; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - - const ctx: DownloadContext = { - uri, - source, - fileUri, - cacheFileUri, - progressCallback, - downloads, - resolve, - reject, - }; - - if (Platform.OS === 'android') { - startAndroidDownload(ctx); - } else { - startIOSDownload(ctx); - } - - return promise.then((path) => ({ path, wasDownloaded: true })); -} diff --git a/packages/bare-resource-fetcher/src/index.ts b/packages/bare-resource-fetcher/src/index.ts deleted file mode 100644 index c0ec4024f4..0000000000 --- a/packages/bare-resource-fetcher/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ResourceFetcher'; diff --git a/packages/bare-resource-fetcher/tsconfig.json b/packages/bare-resource-fetcher/tsconfig.json deleted file mode 100644 index 428fb9c744..0000000000 --- a/packages/bare-resource-fetcher/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib", - "declaration": true, - "declarationMap": true, - "tsBuildInfoFile": "./.cache/tsconfig.tsbuildinfo", - "composite": true, - "allowJs": false, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "bundler", - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noStrictGenericChecks": false, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noUncheckedIndexedAccess": true, - "strict": true, - "types": ["react", "node"] - }, - "include": ["src"], - "exclude": ["node_modules", "lib"] -} diff --git a/packages/expo-resource-fetcher/LICENSE b/packages/expo-resource-fetcher/LICENSE deleted file mode 100644 index 10b6cc18c3..0000000000 --- a/packages/expo-resource-fetcher/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Software Mansion - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/expo-resource-fetcher/README.md b/packages/expo-resource-fetcher/README.md deleted file mode 100644 index 5b8e9f2aca..0000000000 --- a/packages/expo-resource-fetcher/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# react-native-executorch-expo-resource-fetcher - -Expo adapter for `react-native-executorch` that provides resource fetching capabilities using Expo's filesystem APIs. - -## Installation - -```bash -yarn add react-native-executorch-expo-resource-fetcher -yarn add expo-file-system expo-asset -``` - -## Usage - -```typescript -import { initExecutorch } from 'react-native-executorch'; -import { ExpoResourceFetcher } from 'react-native-executorch-expo-resource-fetcher'; - -initExecutorch({ - resourceFetcher: ExpoResourceFetcher, -}); -``` - -## When to Use - -Use this adapter if you're working with: -- Expo projects -- Expo Router projects -- Projects using Expo managed workflow - -This adapter leverages `expo-file-system` and `expo-asset` to handle file operations and downloads. diff --git a/packages/expo-resource-fetcher/package.json b/packages/expo-resource-fetcher/package.json deleted file mode 100644 index a4a8eb0aff..0000000000 --- a/packages/expo-resource-fetcher/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "react-native-executorch-expo-resource-fetcher", - "version": "1.0.0", - "description": "Expo resource fetcher for react-native-executorch", - "main": "lib/index.js", - "// === LEGACY SUPPORT: exports ===": "exports['./legacy'] provides legacy resource fetcher support (remove when legacy API is dropped)", - "exports": { - ".": { - "import": "./lib/index.js", - "types": "./lib/index.d.ts" - }, - "./legacy": { - "import": "./lib/legacy/index.js", - "types": "./lib/legacy/index.d.ts" - } - }, - "files": [ - "lib" - ], - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/software-mansion/react-native-executorch.git", - "directory": "packages/expo-resource-fetcher" - }, - "scripts": { - "prepare": "tsc", - "typecheck": "tsc --noEmit", - "lint": "eslint \"**/*.{js,ts,tsx}\"", - "clean": "del-cli lib" - }, - "peerDependencies": { - "expo": ">=54.0.0", - "expo-asset": ">=12.0.0", - "expo-file-system": ">=19.0.0", - "react-native": "*", - "react-native-executorch": "*" - }, - "devDependencies": { - "@types/react": "~19.2.0", - "expo": "^56.0.8", - "expo-asset": "~56.0.15", - "expo-file-system": "~56.0.7", - "react": "19.2.3", - "react-native": "0.85.3", - "react-native-executorch": "workspace:*", - "typescript": "~5.9.2" - } -} diff --git a/packages/expo-resource-fetcher/src/ResourceFetcher.ts b/packages/expo-resource-fetcher/src/ResourceFetcher.ts deleted file mode 100644 index 58ad0ffc19..0000000000 --- a/packages/expo-resource-fetcher/src/ResourceFetcher.ts +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Resource Fetcher for Expo applications. - * - * This module provides functions to download and manage files stored in the application's document directory - * inside the `react-native-executorch/` directory. These utilities help manage storage and clean up downloaded - * files when they are no longer needed. - * @category Utilities - General - * @remarks - * **Key Functionality:** - * - **Download Control**: Pause, resume, and cancel operations through: - * - {@link pauseFetching} - Pause ongoing downloads - * - {@link resumeFetching} - Resume paused downloads - * - {@link cancelFetching} - Cancel ongoing or paused downloads - * - **File Management**: - * - {@link getFilesTotalSize} - Get total size of resources - * - {@link listDownloadedFiles} - List all downloaded files - * - {@link listDownloadedModels} - List downloaded model files (.pte) - * - {@link deleteResources} - Delete downloaded resources - * - * **Important Notes:** - * - Pause/resume/cancel operations work only for remote resources - * - Most functions accept multiple `ResourceSource` arguments (string, number, or object) - * - The {@link fetch} method accepts a progress callback (0-1) and returns file paths or throws if interrupted - */ - -import { Directory, File } from 'expo-file-system'; -import { RNEDirectory } from './constants/directories'; -import { - ResourceSource, - RnExecutorchErrorCode, - RnExecutorchError, - BaseResourceFetcherClass, -} from 'react-native-executorch/legacy'; - -import { ResourceFetcherUtils, DownloadStatus } from './ResourceFetcherUtils'; -import { - type ActiveDownload, - handleObject, - handleLocalFile, - handleAsset, - handleRemote, -} from './handlers'; - -if ( - typeof Directory !== 'function' || - typeof File !== 'function' || - typeof (File as unknown as { createDownloadTask?: unknown }).createDownloadTask !== 'function' -) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherFileSystemApiUnavailable, - "react-native-executorch-expo-resource-fetcher: the new 'expo-file-system' API " + - "(Directory/File.createDownloadTask) is unavailable — you're likely on Expo SDK <56. " + - "Import from 'react-native-executorch-expo-resource-fetcher/legacy' instead." - ); -} - -class ExpoResourceFetcherClass extends BaseResourceFetcherClass { - protected downloads = new Map(); - - protected async getFilesSizes(sources: ResourceSource[]) { - return ResourceFetcherUtils.getFilesSizes(sources); - } - - protected async handleObject(source: object): Promise { - return handleObject(source); - } - - protected handleLocalFile(source: string): string { - return handleLocalFile(source); - } - - protected handleAsset( - source: number, - progressCallback: (progress: number) => void - ): Promise { - return handleAsset(source, progressCallback, this.downloads); - } - - protected handleRemote( - uri: string, - source: ResourceSource, - progressCallback: (progress: number) => void - ): Promise<{ path: string; wasDownloaded: boolean }> { - return handleRemote(uri, source, progressCallback, this.downloads); - } - - protected async pause(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - if (downloadHandle.status === DownloadStatus.PAUSED) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherAlreadyPaused, - "The file download is currently paused. Can't pause the download of the same file twice." - ); - } - downloadHandle.status = DownloadStatus.PAUSED; - await downloadHandle.downloadTask.pauseAsync(); - } - - protected async resume(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - if (downloadHandle.status === DownloadStatus.ONGOING) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherAlreadyOngoing, - "The file download is currently ongoing. Can't resume the ongoing download." - ); - } - downloadHandle.status = DownloadStatus.ONGOING; - - let downloadedFile; - try { - downloadedFile = await downloadHandle.downloadTask.resumeAsync(); - } catch (error) { - const current = this.downloads.get(source); - // Paused again or canceled during resume — resolve/reject handled elsewhere. - if (!current || current.status === DownloadStatus.PAUSED) return; - this.downloads.delete(source); - downloadHandle.reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to resume download from '${downloadHandle.uri}'`, - error - ) - ); - return; - } - - const current = this.downloads.get(source); - if (!current || current.status === DownloadStatus.PAUSED) return; - - // null means the task was paused again before completion — pause() handles it. - if (!downloadedFile) return; - - await downloadedFile.move(new File(downloadHandle.fileUri)); - this.downloads.delete(source); - downloadHandle.resolve(ResourceFetcherUtils.removeFilePrefix(downloadHandle.fileUri)); - } - - protected async cancel(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - downloadHandle.downloadTask.cancel(); - this.downloads.delete(source); - downloadHandle.reject( - new RnExecutorchError(RnExecutorchErrorCode.DownloadInterrupted, 'Download was canceled.') - ); - } - - /** - * Reads the contents of a file as a string. - * @param path - Absolute file path or file URI to read. - * @returns A promise that resolves to the file contents as a string. - */ - async readAsString(path: string): Promise { - const uri = path.startsWith('file://') ? path : `file://${path}`; - return new File(uri).text(); - } - - /** - * Lists all the downloaded files used by React Native ExecuTorch. - * @returns A promise that resolves to an array of URIs for all the downloaded files. - */ - async listDownloadedFiles(): Promise { - return new Directory(RNEDirectory).list().map((entry) => entry.uri); - } - - /** - * Deletes downloaded resources from the local filesystem. - * @param sources - The resource identifiers used when calling `fetch`. - * @returns A promise that resolves once all specified resources have been removed. - */ - async deleteResources(...sources: ResourceSource[]): Promise { - for (const source of sources) { - const filename = ResourceFetcherUtils.getFilenameFromUri(source as string); - const file = new File(`${RNEDirectory}${filename}`); - if (file.exists) { - file.delete(); - } - } - } - - /** - * Fetches the total size of remote files. Works only for remote files. - * @param sources - The resource identifiers (URLs). - * @returns A promise that resolves to the combined size of files in bytes. - */ - async getFilesTotalSize(...sources: ResourceSource[]): Promise { - return (await ResourceFetcherUtils.getFilesSizes(sources)).totalLength; - } -} - -export const ExpoResourceFetcher = new ExpoResourceFetcherClass(); diff --git a/packages/expo-resource-fetcher/src/ResourceFetcherUtils.ts b/packages/expo-resource-fetcher/src/ResourceFetcherUtils.ts deleted file mode 100644 index 9fc7a9f9ae..0000000000 --- a/packages/expo-resource-fetcher/src/ResourceFetcherUtils.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { RNEDirectory } from './constants/directories'; -import { - HTTP_CODE, - DownloadStatus, - SourceType, - RnExecutorchError, - RnExecutorchErrorCode, -} from 'react-native-executorch/legacy'; -import * as SharedUtils from './sharedUtils'; - -/** - * @internal - */ -import { Directory } from 'expo-file-system'; - -export { HTTP_CODE, DownloadStatus, SourceType }; - -/** - * Utility functions for fetching and managing resources. - * @category Utilities - General - */ -export namespace ResourceFetcherUtils { - export const removeFilePrefix = SharedUtils.removeFilePrefix; - export const hashObject = SharedUtils.hashObject; - export const calculateDownloadProgress = SharedUtils.calculateDownloadProgress; - export const triggerHuggingFaceDownloadCounter = SharedUtils.triggerHuggingFaceDownloadCounter; - export const triggerDownloadEvent = SharedUtils.triggerDownloadEvent; - export const getFilenameFromUri = SharedUtils.getFilenameFromUri; - export const getType = SharedUtils.getType; - export const getFilesSizes = SharedUtils.getFilesSizes; - - export async function createDirectoryIfNoExists() { - const directory = new Directory(RNEDirectory); - if (directory.exists) return; - try { - directory.create({ intermediates: true, idempotent: true }); - } catch (error) { - throw new RnExecutorchError( - RnExecutorchErrorCode.FileWriteFailed, - `Failed to create directory at ${RNEDirectory}`, - error - ); - } - } -} diff --git a/packages/expo-resource-fetcher/src/constants/directories.ts b/packages/expo-resource-fetcher/src/constants/directories.ts deleted file mode 100644 index a0db917564..0000000000 --- a/packages/expo-resource-fetcher/src/constants/directories.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Paths } from 'expo-file-system'; - -export const RNEDirectory = `${Paths.document.uri}react-native-executorch/`; diff --git a/packages/expo-resource-fetcher/src/handlers.ts b/packages/expo-resource-fetcher/src/handlers.ts deleted file mode 100644 index e8629b6a82..0000000000 --- a/packages/expo-resource-fetcher/src/handlers.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { File, Paths, type DownloadTask } from 'expo-file-system'; -import { Asset } from 'expo-asset'; -import { Platform } from 'react-native'; -import { - ResourceSource, - RnExecutorchErrorCode, - RnExecutorchError, -} from 'react-native-executorch/legacy'; -import { RNEDirectory } from './constants/directories'; -import { ResourceFetcherUtils, DownloadStatus } from './ResourceFetcherUtils'; - -export interface ActiveDownload { - downloadTask: DownloadTask; - status: DownloadStatus; - uri: string; - fileUri: string; - cacheFileUri: string; - // resolve and reject are the resolve/reject of the Promise returned by handleRemote. - // They are stored here so that cancel() and resume() in the fetcher class can - // unblock the fetch() loop from outside the download flow. - resolve: (path: string) => void; - reject: (error: unknown) => void; -} - -export async function handleObject(source: object): Promise { - const jsonString = JSON.stringify(source); - const digest = ResourceFetcherUtils.hashObject(jsonString); - const path = `${RNEDirectory}${digest}.json`; - - const file = new File(path); - if (file.exists) { - return ResourceFetcherUtils.removeFilePrefix(path); - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - file.create(); - file.write(jsonString, { encoding: 'utf8' }); - return ResourceFetcherUtils.removeFilePrefix(path); -} - -export function handleLocalFile(source: string): string { - return ResourceFetcherUtils.removeFilePrefix(source); -} - -export async function handleAsset( - source: number, - progressCallback: (progress: number) => void, - downloads: Map -): Promise { - const asset = Asset.fromModule(source); - const uri = asset.uri; - - if (uri.startsWith('http')) { - // Dev mode: asset served from Metro dev server. - // uri is the resolved HTTP URL; source is the original require() number the - // user holds, so it must be used as the downloads map key for pause/cancel to work. - return (await handleRemote(uri, source, progressCallback, downloads)).path; - } - - // Release mode: asset bundled locally, copy to RNEDirectory - const filename = ResourceFetcherUtils.getFilenameFromUri(uri); - const fileUri = `${RNEDirectory}${filename}`; - // On Android, the bundled URI has no extension, so we append it manually - const fileUriWithType = Platform.OS === 'android' ? `${fileUri}.${asset.type}` : fileUri; - - if (new File(fileUri).exists) { - return ResourceFetcherUtils.removeFilePrefix(fileUri); - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - await new File(uri).copy(new File(fileUriWithType)); - return ResourceFetcherUtils.removeFilePrefix(fileUriWithType); -} - -// uri and source are separate parameters because for asset sources (dev mode), -// source is the require() number the user holds (used as the downloads map key), -// while uri is the resolved HTTP URL needed for the actual download. -// For plain remote strings they are the same value. -export async function handleRemote( - uri: string, - source: ResourceSource, - progressCallback: (progress: number) => void, - downloads: Map -): Promise<{ path: string; wasDownloaded: boolean }> { - if (downloads.has(source)) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadInProgress, - 'Already downloading this file' - ); - } - - const filename = ResourceFetcherUtils.getFilenameFromUri(uri); - const fileUri = `${RNEDirectory}${filename}`; - const cacheFileUri = `${Paths.cache.uri}${filename}`; - - if (new File(fileUri).exists) { - return { - path: ResourceFetcherUtils.removeFilePrefix(fileUri), - wasDownloaded: false, - }; - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - - // We need a Promise whose resolution can be triggered from outside this function — - // by cancel() or resume() in the fetcher class. A plain async function can't do that, - // so we create the Promise manually and store resolve/reject in the downloads map. - let resolve: (path: string) => void = () => {}; - let reject: (error: unknown) => void = () => {}; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - - const cacheFile = new File(cacheFileUri); - const downloadTask = File.createDownloadTask(uri, cacheFile, { - sessionType: 'background', - }); - downloadTask.addListener('progress', ({ bytesWritten, totalBytes }) => { - if (totalBytes === -1) { - progressCallback(0); - } else { - progressCallback(bytesWritten / totalBytes); - } - }); - - downloads.set(source, { - downloadTask, - status: DownloadStatus.ONGOING, - uri, - fileUri, - cacheFileUri, - resolve, - reject, - }); - - downloadTask - .downloadAsync() - .then(async (downloadedFile) => { - // null means the task was paused before completion — resume() will continue it. - if (!downloadedFile) return; - - // missing handle means the task was canceled — cancel() already rejected. - const downloadHandle = downloads.get(source); - if (!downloadHandle) return; - - try { - await downloadedFile.move(new File(fileUri)); - } catch (error) { - downloads.delete(source); - reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to move downloaded file to '${fileUri}'`, - error - ) - ); - return; - } - - downloads.delete(source); - resolve(ResourceFetcherUtils.removeFilePrefix(fileUri)); - }) - .catch((error) => { - // If paused, the rejection is expected — cancel/resume will resolve later. - const downloadHandle = downloads.get(source); - if (downloadHandle?.status === DownloadStatus.PAUSED) return; - downloads.delete(source); - reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to fetch resource from '${uri}'`, - error - ) - ); - }); - - return promise.then((path) => ({ path, wasDownloaded: true })); -} diff --git a/packages/expo-resource-fetcher/src/index.ts b/packages/expo-resource-fetcher/src/index.ts deleted file mode 100644 index c0ec4024f4..0000000000 --- a/packages/expo-resource-fetcher/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ResourceFetcher'; diff --git a/packages/expo-resource-fetcher/src/legacy/ResourceFetcher.ts b/packages/expo-resource-fetcher/src/legacy/ResourceFetcher.ts deleted file mode 100644 index b4137090a5..0000000000 --- a/packages/expo-resource-fetcher/src/legacy/ResourceFetcher.ts +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Resource Fetcher for Expo applications (legacy `expo-file-system` API). - * - * This module provides functions to download and manage files stored in the application's document directory - * inside the `react-native-executorch/` directory. These utilities help manage storage and clean up downloaded - * files when they are no longer needed. - * - * It is functionally equivalent to the default `ExpoResourceFetcher` export, but uses the legacy - * `expo-file-system/legacy` APIs under the hood. Prefer this build only if you cannot adopt the new - * file-system API yet. - * @deprecated This is a frozen code path that will be deprecated in the next major release. - * We recommend migrating to the non-legacy imports. - * @category Utilities - General - * @remarks - * **Key Functionality:** - * - **Download Control**: Pause, resume, and cancel operations through: - * - {@link pauseFetching} - Pause ongoing downloads - * - {@link resumeFetching} - Resume paused downloads - * - {@link cancelFetching} - Cancel ongoing or paused downloads - * - **File Management**: - * - {@link getFilesTotalSize} - Get total size of resources - * - {@link listDownloadedFiles} - List all downloaded files - * - {@link listDownloadedModels} - List downloaded model files (.pte) - * - {@link deleteResources} - Delete downloaded resources - * - * **Important Notes:** - * - Pause/resume/cancel operations work only for remote resources - * - Most functions accept multiple `ResourceSource` arguments (string, number, or object) - * - The {@link fetch} method accepts a progress callback (0-1) and returns file paths or throws if interrupted - */ - -import { - deleteAsync, - readDirectoryAsync, - readAsStringAsync, - moveAsync, -} from 'expo-file-system/legacy'; -import { RNEDirectory } from './constants/directories'; -import { - ResourceSource, - RnExecutorchErrorCode, - RnExecutorchError, - BaseResourceFetcherClass, -} from 'react-native-executorch/legacy'; -import { ResourceFetcherUtils, HTTP_CODE, DownloadStatus } from './ResourceFetcherUtils'; -import { - type ActiveDownload, - handleObject, - handleLocalFile, - handleAsset, - handleRemote, -} from './handlers'; - -class ExpoResourceFetcherClass extends BaseResourceFetcherClass { - protected downloads = new Map(); - - protected async getFilesSizes(sources: ResourceSource[]) { - return ResourceFetcherUtils.getFilesSizes(sources); - } - - protected async handleObject(source: object): Promise { - return handleObject(source); - } - - protected handleLocalFile(source: string): string { - return handleLocalFile(source); - } - - protected handleAsset( - source: number, - progressCallback: (progress: number) => void - ): Promise { - return handleAsset(source, progressCallback, this.downloads); - } - - protected handleRemote( - uri: string, - source: ResourceSource, - progressCallback: (progress: number) => void - ): Promise<{ path: string; wasDownloaded: boolean }> { - return handleRemote(uri, source, progressCallback, this.downloads); - } - - protected async pause(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - if (downloadHandle.status === DownloadStatus.PAUSED) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherAlreadyPaused, - "The file download is currently paused. Can't pause the download of the same file twice." - ); - } - downloadHandle.status = DownloadStatus.PAUSED; - await downloadHandle.downloadResumable.pauseAsync(); - } - - protected async resume(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - if (downloadHandle.status === DownloadStatus.ONGOING) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherAlreadyOngoing, - "The file download is currently ongoing. Can't resume the ongoing download." - ); - } - downloadHandle.status = DownloadStatus.ONGOING; - const result = await downloadHandle.downloadResumable.resumeAsync(); - const current = this.downloads.get(source); - // Paused again or canceled during resume — resolve/reject handled elsewhere. - if (!current || current.status === DownloadStatus.PAUSED) return; - - if ( - !result || - (result.status !== HTTP_CODE.OK && result.status !== HTTP_CODE.PARTIAL_CONTENT) - ) { - this.downloads.delete(source); - downloadHandle.reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to resume download from '${downloadHandle.uri}', status: ${result?.status}` - ) - ); - return; - } - - await moveAsync({ - from: downloadHandle.cacheFileUri, - to: downloadHandle.fileUri, - }); - this.downloads.delete(source); - downloadHandle.resolve(ResourceFetcherUtils.removeFilePrefix(downloadHandle.fileUri)); - } - - protected async cancel(source: ResourceSource): Promise { - const downloadHandle = this.downloads.get(source)!; - await downloadHandle.downloadResumable.cancelAsync(); - this.downloads.delete(source); - downloadHandle.reject( - new RnExecutorchError(RnExecutorchErrorCode.DownloadInterrupted, 'Download was canceled.') - ); - } - - /** - * Reads the contents of a file as a string. - * @param path - Absolute file path or file URI to read. - * @returns A promise that resolves to the file contents as a string. - */ - async readAsString(path: string): Promise { - const uri = path.startsWith('file://') ? path : `file://${path}`; - return readAsStringAsync(uri); - } - - /** - * Lists all the downloaded files used by React Native ExecuTorch. - * @returns A promise that resolves to an array of URIs for all the downloaded files. - */ - async listDownloadedFiles(): Promise { - const files = await readDirectoryAsync(RNEDirectory); - return files.map((file: string) => `${RNEDirectory}${file}`); - } - - /** - * Deletes downloaded resources from the local filesystem. - * @param sources - The resource identifiers used when calling `fetch`. - * @returns A promise that resolves once all specified resources have been removed. - */ - async deleteResources(...sources: ResourceSource[]): Promise { - for (const source of sources) { - const filename = ResourceFetcherUtils.getFilenameFromUri(source as string); - const fileUri = `${RNEDirectory}${filename}`; - if (await ResourceFetcherUtils.checkFileExists(fileUri)) { - await deleteAsync(fileUri); - } - } - } - - /** - * Fetches the total size of remote files. Works only for remote files. - * @param sources - The resource identifiers (URLs). - * @returns A promise that resolves to the combined size of files in bytes. - */ - async getFilesTotalSize(...sources: ResourceSource[]): Promise { - return (await ResourceFetcherUtils.getFilesSizes(sources)).totalLength; - } -} - -/** - * @deprecated This is a frozen code path that will be deprecated in the next major release. - * We recommend migrating to the non-legacy imports. - */ -export const ExpoResourceFetcher = new ExpoResourceFetcherClass(); diff --git a/packages/expo-resource-fetcher/src/legacy/ResourceFetcherUtils.ts b/packages/expo-resource-fetcher/src/legacy/ResourceFetcherUtils.ts deleted file mode 100644 index 03a310131b..0000000000 --- a/packages/expo-resource-fetcher/src/legacy/ResourceFetcherUtils.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { RNEDirectory } from './constants/directories'; -import { - HTTP_CODE, - DownloadStatus, - SourceType, - RnExecutorchError, - RnExecutorchErrorCode, -} from 'react-native-executorch/legacy'; -import * as SharedUtils from '../sharedUtils'; - -/** - * @internal - */ -import { getInfoAsync, makeDirectoryAsync } from 'expo-file-system/legacy'; - -export { HTTP_CODE, DownloadStatus, SourceType }; - -/** - * Utility functions for fetching and managing resources. - * @category Utilities - General - */ -export namespace ResourceFetcherUtils { - export const removeFilePrefix = SharedUtils.removeFilePrefix; - export const hashObject = SharedUtils.hashObject; - export const calculateDownloadProgress = SharedUtils.calculateDownloadProgress; - export const triggerHuggingFaceDownloadCounter = SharedUtils.triggerHuggingFaceDownloadCounter; - export const triggerDownloadEvent = SharedUtils.triggerDownloadEvent; - export const getFilenameFromUri = SharedUtils.getFilenameFromUri; - export const getType = SharedUtils.getType; - export const getFilesSizes = SharedUtils.getFilesSizes; - - export async function createDirectoryIfNoExists() { - if (!(await checkFileExists(RNEDirectory))) { - try { - await makeDirectoryAsync(RNEDirectory, { intermediates: true }); - } catch (error) { - throw new RnExecutorchError( - RnExecutorchErrorCode.FileWriteFailed, - `Failed to create directory at ${RNEDirectory}`, - error - ); - } - } - } - - export async function checkFileExists(fileUri: string) { - const fileInfo = await getInfoAsync(fileUri); - return fileInfo.exists; - } -} diff --git a/packages/expo-resource-fetcher/src/legacy/constants/directories.ts b/packages/expo-resource-fetcher/src/legacy/constants/directories.ts deleted file mode 100644 index 6d70d56cb9..0000000000 --- a/packages/expo-resource-fetcher/src/legacy/constants/directories.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { documentDirectory } from 'expo-file-system/legacy'; - -export const RNEDirectory = `${documentDirectory}react-native-executorch/`; diff --git a/packages/expo-resource-fetcher/src/legacy/handlers.ts b/packages/expo-resource-fetcher/src/legacy/handlers.ts deleted file mode 100644 index 317fa26495..0000000000 --- a/packages/expo-resource-fetcher/src/legacy/handlers.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { - cacheDirectory, - copyAsync, - createDownloadResumable, - moveAsync, - FileSystemSessionType, - writeAsStringAsync, - EncodingType, - type DownloadResumable, -} from 'expo-file-system/legacy'; -import { Asset } from 'expo-asset'; -import { Platform } from 'react-native'; -import { - ResourceSource, - RnExecutorchErrorCode, - RnExecutorchError, -} from 'react-native-executorch/legacy'; -import { RNEDirectory } from './constants/directories'; -import { ResourceFetcherUtils, HTTP_CODE, DownloadStatus } from './ResourceFetcherUtils'; - -export interface ActiveDownload { - downloadResumable: DownloadResumable; - status: DownloadStatus; - uri: string; - fileUri: string; - cacheFileUri: string; - // resolve and reject are the resolve/reject of the Promise returned by handleRemote. - // They are stored here so that cancel() and resume() in the fetcher class can - // unblock the fetch() loop from outside the download flow. - resolve: (path: string) => void; - reject: (error: unknown) => void; -} - -export async function handleObject(source: object): Promise { - const jsonString = JSON.stringify(source); - const digest = ResourceFetcherUtils.hashObject(jsonString); - const path = `${RNEDirectory}${digest}.json`; - - if (await ResourceFetcherUtils.checkFileExists(path)) { - return ResourceFetcherUtils.removeFilePrefix(path); - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - await writeAsStringAsync(path, jsonString, { encoding: EncodingType.UTF8 }); - return ResourceFetcherUtils.removeFilePrefix(path); -} - -export function handleLocalFile(source: string): string { - return ResourceFetcherUtils.removeFilePrefix(source); -} - -export async function handleAsset( - source: number, - progressCallback: (progress: number) => void, - downloads: Map -): Promise { - const asset = Asset.fromModule(source); - const uri = asset.uri; - - if (uri.startsWith('http')) { - // Dev mode: asset served from Metro dev server. - // uri is the resolved HTTP URL; source is the original require() number the - // user holds, so it must be used as the downloads map key for pause/cancel to work. - return (await handleRemote(uri, source, progressCallback, downloads)).path; - } - - // Release mode: asset bundled locally, copy to RNEDirectory - const filename = ResourceFetcherUtils.getFilenameFromUri(uri); - const fileUri = `${RNEDirectory}${filename}`; - // On Android, the bundled URI has no extension, so we append it manually - const fileUriWithType = Platform.OS === 'android' ? `${fileUri}.${asset.type}` : fileUri; - - if (await ResourceFetcherUtils.checkFileExists(fileUri)) { - return ResourceFetcherUtils.removeFilePrefix(fileUri); - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - await copyAsync({ from: uri, to: fileUriWithType }); - return ResourceFetcherUtils.removeFilePrefix(fileUriWithType); -} - -// uri and source are separate parameters because for asset sources (dev mode), -// source is the require() number the user holds (used as the downloads map key), -// while uri is the resolved HTTP URL needed for the actual download. -// For plain remote strings they are the same value. -export async function handleRemote( - uri: string, - source: ResourceSource, - progressCallback: (progress: number) => void, - downloads: Map -): Promise<{ path: string; wasDownloaded: boolean }> { - if (downloads.has(source)) { - throw new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadInProgress, - 'Already downloading this file' - ); - } - - const filename = ResourceFetcherUtils.getFilenameFromUri(uri); - const fileUri = `${RNEDirectory}${filename}`; - const cacheFileUri = `${cacheDirectory}${filename}`; - - if (await ResourceFetcherUtils.checkFileExists(fileUri)) { - return { - path: ResourceFetcherUtils.removeFilePrefix(fileUri), - wasDownloaded: false, - }; - } - - await ResourceFetcherUtils.createDirectoryIfNoExists(); - - // We need a Promise whose resolution can be triggered from outside this function — - // by cancel() or resume() in the fetcher class. A plain async function can't do that, - // so we create the Promise manually and store resolve/reject in the downloads map. - let resolve: (path: string) => void = () => {}; - let reject: (error: unknown) => void = () => {}; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - - const downloadResumable = createDownloadResumable( - uri, - cacheFileUri, - { sessionType: FileSystemSessionType.BACKGROUND }, - ({ - totalBytesWritten, - totalBytesExpectedToWrite, - }: { - totalBytesWritten: number; - totalBytesExpectedToWrite: number; - }) => { - if (totalBytesExpectedToWrite === -1) { - progressCallback(0); - } else { - progressCallback(totalBytesWritten / totalBytesExpectedToWrite); - } - } - ); - - downloads.set(source, { - downloadResumable, - status: DownloadStatus.ONGOING, - uri, - fileUri, - cacheFileUri, - resolve, - reject, - }); - - downloadResumable - .downloadAsync() - .then(async (result) => { - const downloadHandle = downloads.get(source); - // If paused or canceled during the download, resolve/reject will be called - // externally by resume() or cancel() — do nothing here. - if (!downloadHandle || downloadHandle.status === DownloadStatus.PAUSED) return; - - if ( - !result || - (result.status !== HTTP_CODE.OK && result.status !== HTTP_CODE.PARTIAL_CONTENT) - ) { - downloads.delete(source); - reject( - new RnExecutorchError( - RnExecutorchErrorCode.ResourceFetcherDownloadFailed, - `Failed to fetch resource from '${uri}', status: ${result?.status}` - ) - ); - return; - } - - try { - await moveAsync({ from: cacheFileUri, to: fileUri }); - } catch (error) { - downloads.delete(source); - reject(error); - return; - } - - downloads.delete(source); - resolve(ResourceFetcherUtils.removeFilePrefix(fileUri)); - }) - .catch((error) => { - downloads.delete(source); - reject(error); - }); - - return promise.then((path) => ({ path, wasDownloaded: true })); -} diff --git a/packages/expo-resource-fetcher/src/legacy/index.ts b/packages/expo-resource-fetcher/src/legacy/index.ts deleted file mode 100644 index c0ec4024f4..0000000000 --- a/packages/expo-resource-fetcher/src/legacy/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ResourceFetcher'; diff --git a/packages/expo-resource-fetcher/src/sharedUtils.ts b/packages/expo-resource-fetcher/src/sharedUtils.ts deleted file mode 100644 index 460a2c90d1..0000000000 --- a/packages/expo-resource-fetcher/src/sharedUtils.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Utilities shared between the current (`expo-file-system`) and legacy - * (`expo-file-system/legacy`) resource fetcher implementations. - * - * Everything in this module must stay free of `expo-file-system` imports — - * it is pulled in by both entry points, so it can only depend on APIs that - * exist regardless of which file-system API the consumer's SDK ships. - */ -import { - ResourceSource, - Logger, - ResourceFetcherUtils as CoreUtils, - SourceType, -} from 'react-native-executorch/legacy'; -import { Asset } from 'expo-asset'; - -export const removeFilePrefix = CoreUtils.removeFilePrefix; -export const hashObject = CoreUtils.hashObject; -export const calculateDownloadProgress = CoreUtils.calculateDownloadProgress; -export const triggerHuggingFaceDownloadCounter = CoreUtils.triggerHuggingFaceDownloadCounter; -export const triggerDownloadEvent = CoreUtils.triggerDownloadEvent; -export const getFilenameFromUri = CoreUtils.getFilenameFromUri; - -export function getType(source: ResourceSource): SourceType { - if (typeof source === 'object') { - return SourceType.OBJECT; - } else if (typeof source === 'number') { - const uri = Asset.fromModule(source).uri; - if (uri.startsWith('http')) { - return SourceType.DEV_MODE_FILE; - } - return SourceType.RELEASE_MODE_FILE; - } - // typeof source == 'string' - if (source.startsWith('file://')) { - return SourceType.LOCAL_FILE; - } - return SourceType.REMOTE_FILE; -} - -export async function getFilesSizes(sources: ResourceSource[]) { - const results: Array<{ - source: ResourceSource; - type: SourceType; - length: number; - previousFilesTotalLength: number; - }> = []; - let totalLength = 0; - - for (const source of sources) { - const type = getType(source); - let length = 0; - - if (type === SourceType.REMOTE_FILE && typeof source === 'string') { - try { - const response = await fetch(source, { method: 'HEAD' }); - if (!response.ok) { - Logger.warn(`Failed to fetch HEAD for ${source}: ${response.status}`); - continue; - } - - const contentLength = response.headers.get('content-length'); - if (!contentLength) { - Logger.warn(`No content-length header for ${source}`); - } - - length = contentLength ? parseInt(contentLength, 10) : 0; - } catch (error) { - Logger.warn(`Error fetching HEAD for ${source}:`, error); - continue; - } - } - - const previousFilesTotalLength = totalLength; - totalLength += length; - results.push({ source, type, length, previousFilesTotalLength }); - } - - return { results, totalLength }; -} diff --git a/packages/expo-resource-fetcher/tsconfig.json b/packages/expo-resource-fetcher/tsconfig.json deleted file mode 100644 index 428fb9c744..0000000000 --- a/packages/expo-resource-fetcher/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "lib", - "declaration": true, - "declarationMap": true, - "tsBuildInfoFile": "./.cache/tsconfig.tsbuildinfo", - "composite": true, - "allowJs": false, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "bundler", - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "noStrictGenericChecks": false, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noUncheckedIndexedAccess": true, - "strict": true, - "types": ["react", "node"] - }, - "include": ["src"], - "exclude": ["node_modules", "lib"] -} diff --git a/packages/react-native-executorch-webrtc/LICENSE b/packages/react-native-executorch-webrtc/LICENSE deleted file mode 100644 index 10b6cc18c3..0000000000 --- a/packages/react-native-executorch-webrtc/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Software Mansion - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/react-native-executorch-webrtc/README.md b/packages/react-native-executorch-webrtc/README.md deleted file mode 100644 index 711fa74522..0000000000 --- a/packages/react-native-executorch-webrtc/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# react-native-executorch-webrtc - -Real-time background blur for Fishjam WebRTC applications, powered by ExecuTorch segmentation models. - -This package provides GPU-accelerated background blur effects using on-device ExecuTorch models for foreground / background segmentation. - -## Requirements - -- iOS 13.0+ -- Android SDK 26+ -- Peer dependencies: -- `@fishjam-cloud/react-native-client` -- `@fishjam-cloud/react-native-webrtc` -- `react-native-executorch` - -## Installation - -```bash -yarn add react-native-executorch-webrtc -``` - -For iOS: -```bash -cd ios && pod install -``` - -## Usage - -### With Fishjam SDK - -```tsx -import { useBackgroundBlur } from 'react-native-executorch-webrtc'; -import { useCamera } from '@fishjam-cloud/react-native-client'; - -function VideoCall() { - const [blurEnabled, setBlurEnabled] = useState(true); - - const { blurMiddleware } = useBackgroundBlur({ - // NOTE: you can use React Native Executorch's Resource Fetcher to download model files - modelUri: 'file:///path/to/selfie_segmenter.pte', - blurRadius: 15, - }); - - const { toggleCamera } = useCamera({ - cameraTrackMiddleware: blurEnabled ? blurMiddleware : undefined, - }); - - return ( -