Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<img src="https://img.shields.io/npm/dw/react-native-mediapipe-posedetection?color=blue&style=flat-square&logo=npm" alt="npm weekly downloads"/>
</div>


High-performance pose detection for React Native using Google's MediaPipe models with optimized frame processing for smooth real-time tracking.

You can find the package on npm: [react-native-mediapipe-posedetection](https://www.npmjs.com/package/react-native-mediapipe-posedetection)
Expand Down Expand Up @@ -42,6 +41,35 @@ npm install react-native-mediapipe-posedetection react-native-vision-camera reac
yarn add react-native-mediapipe-posedetection react-native-vision-camera react-native-worklets-core
```

### Expo Configuration

If you are using Expo, you can use the built-in config plugin to automatically copy your MediaPipe model files to the native Android and iOS projects during prebuild.

1. Add your model files (e.g., `pose_landmarker_lite.task`) to a directory in your project (e.g., `./assets/models/`).
2. Update your `app.json` or `app.config.js`:

```json
{
"expo": {
"plugins": [
[
"react-native-mediapipe-posedetection",
{
"assetsPaths": ["./assets/models/"]
}
]
]
}
}
```

This plugin will copy all files from the specified `assetsPaths` to:

- **Android:** `android/app/src/main/assets/`
- **iOS:** The root of the Xcode project (and add them to the build resources).

> **Note:** The `assetsPaths` are relative to your project root.

### Enable New Architecture

If you haven't already enabled the New Architecture in your React Native app:
Expand Down
1 change: 1 addition & 0 deletions app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/commonjs/plugin/withMediapipePosedetection');
56 changes: 48 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,36 @@
"name": "react-native-mediapipe-posedetection",
"version": "0.2.0",
"description": "PoseDetection using google's mediapipe models using poselandmark",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
"main": "./lib/commonjs/index.js",
"module": "./lib/module/index.js",
"types": "./lib/typescript/commonjs/src/index.d.ts",
"source": "./src/index.tsx",
"exports": {
".": {
"source": "./src/index.tsx",
"types": "./lib/typescript/src/index.d.ts",
"default": "./lib/module/index.js"
"import": {
"types": "./lib/typescript/module/src/index.d.ts",
"default": "./lib/module/index.js"
},
"require": {
"types": "./lib/typescript/commonjs/src/index.d.ts",
"default": "./lib/commonjs/index.js"
}
},
"./app.plugin.js": {
"require": {
"default": "./app.plugin.js"
}
},
"./package.json": "./package.json"
"./plugin": {
"import": {
"types": "./lib/typescript/module/src/plugin/withMediapipePosedetection.d.ts",
"default": "./lib/module/plugin/withMediapipePosedetection.js"
},
"require": {
"types": "./lib/typescript/commonjs/src/plugin/withMediapipePosedetection.d.ts",
"default": "./lib/commonjs/plugin/withMediapipePosedetection.js"
}
}
},
"files": [
"src",
Expand All @@ -19,6 +40,7 @@
"ios",
"cpp",
"*.podspec",
"app.plugin.js",
"react-native.config.js",
"!ios/build",
"!android/build",
Expand Down Expand Up @@ -55,6 +77,9 @@
"url": "https://github.com/EndLess728/react-native-mediapipe-posedetection/issues"
},
"homepage": "https://github.com/EndLess728/react-native-mediapipe-posedetection#readme",
"dependencies": {
"fs-extra": "^11.2.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
Expand All @@ -63,10 +88,12 @@
"@eslint/compat": "^1.3.2",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.35.0",
"@expo/config-plugins": "^9.0.0",
"@react-native-community/cli": "20.0.1",
"@react-native/babel-preset": "0.81.1",
"@react-native/eslint-config": "^0.81.1",
"@release-it/conventional-changelog": "^10.0.1",
"@types/fs-extra": "^11.0.4",
"@types/jest": "^29.5.14",
"@types/react": "^19.1.0",
"commitlint": "^19.8.1",
Expand All @@ -87,13 +114,20 @@
"typescript": "^5.9.2"
},
"peerDependencies": {
"react": "*",
"@expo/config-plugins": ">=7",
"@types/react": ">=16.6.1",
"react": ">=16.6.1",
"react-native": ">=0.74.0",
"react-native-vision-camera": "*",
"react-native-worklets-core": "*"
},
"peerDependenciesMeta": {
"react-native": {}
"@expo/config-plugins": {
"optional": true
},
"@types/react": {
"optional": true
}
},
"workspaces": [
"example"
Expand All @@ -103,6 +137,12 @@
"source": "src",
"output": "lib",
"targets": [
[
"commonjs",
{
"esm": true
}
],
[
"module",
{
Expand Down
17 changes: 17 additions & 0 deletions src/plugin/PluginProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Plugin options for react-native-mediapipe-posedetection config plugin
*/
export interface MediapipePluginProps {
/**
* Array of paths to asset directories (relative to project root)
* These assets will be copied to Android assets and iOS project
* @example ["./models/"]
*/
assetsPaths?: string[];

/**
* Optional regex pattern to ignore files during copy
* @example "\\.txt$" to ignore .txt files
*/
ignoredPattern?: string;
}
65 changes: 65 additions & 0 deletions src/plugin/android.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import path from 'path';
import { withDangerousMod, type ConfigPlugin } from '@expo/config-plugins';
import { copyFileSync, ensureDirSync, readdirSync } from 'fs-extra';

import type { MediapipePluginProps } from './PluginProps';

/**
* Android-specific config plugin to copy assets to android/app/src/main/assets/
*/
export const withAssets: ConfigPlugin<MediapipePluginProps> = (
config,
props
) => {
const { assetsPaths = [], ignoredPattern } = props || {};

return withDangerousMod(config, [
'android',
async (cfg) => {
const { projectRoot } = cfg.modRequest;

// Copy to android/app/src/main/assets/
const assetsDir = path.join(
projectRoot,
'android',
'app',
'src',
'main',
'assets'
);
ensureDirSync(assetsDir);

for (const assetSourceDir of assetsPaths) {
const assetSourcePath = path.join(projectRoot, assetSourceDir);

let files;
try {
files = readdirSync(assetSourcePath, { withFileTypes: true });
} catch {
console.warn(
`⚠️ [Android] Could not read directory: ${assetSourcePath}`
);
continue;
}

for (const file of files) {
if (
file.isFile() &&
(!ignoredPattern || !file.name.match(new RegExp(ignoredPattern)))
) {
const srcPath = path.join(assetSourcePath, file.name);
const destPath = path.join(assetsDir, file.name);
copyFileSync(srcPath, destPath);
console.log(`✅ [Android] Copied ${file.name} to assets/`);
}
}
}

return cfg;
},
]);
};

export const android = {
withAssets,
};
67 changes: 67 additions & 0 deletions src/plugin/ios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import path from 'path';
import {
withXcodeProject,
IOSConfig,
type ConfigPlugin,
} from '@expo/config-plugins';
import { copyFileSync, ensureDirSync, readdirSync } from 'fs-extra';

import type { MediapipePluginProps } from './PluginProps';

/**
* iOS-specific config plugin to copy assets to iOS project with Xcode references
*/
export const withAssets: ConfigPlugin<MediapipePluginProps> = (
config,
props
) => {
const { assetsPaths = [], ignoredPattern } = props || {};

return withXcodeProject(config, async (cfg) => {
const { projectRoot, platformProjectRoot } = cfg.modRequest;
const project = cfg.modResults;
const projectName = cfg.modRequest.projectName || 'App';

// Copy files directly to iOS root folder
ensureDirSync(platformProjectRoot);

for (const assetSourceDir of assetsPaths) {
const assetSourcePath = path.join(projectRoot, assetSourceDir);

let files;
try {
files = readdirSync(assetSourcePath, { withFileTypes: true });
} catch {
console.warn(`⚠️ [iOS] Could not read directory: ${assetSourcePath}`);
continue;
}

for (const file of files) {
if (
file.isFile() &&
(!ignoredPattern || !file.name.match(new RegExp(ignoredPattern)))
) {
const srcPath = path.join(assetSourcePath, file.name);
const destPath = path.join(platformProjectRoot, file.name);
copyFileSync(srcPath, destPath);
console.log(`✅ [iOS] Copied ${file.name} to ios/ root`);

// Add the file to the Xcode project with proper reference
IOSConfig.XcodeUtils.addResourceFileToGroup({
filepath: file.name,
groupName: projectName,
project,
isBuildFile: true,
verbose: true,
});
}
}
}

return cfg;
});
};

export const ios = {
withAssets,
};
60 changes: 60 additions & 0 deletions src/plugin/withMediapipePosedetection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { type ConfigPlugin, createRunOncePlugin } from '@expo/config-plugins';

import type { MediapipePluginProps } from './PluginProps';
import { android } from './android';
import { ios } from './ios';

let pkg: { name: string; version?: string } = {
name: 'react-native-mediapipe-posedetection',
};
try {
pkg = require('react-native-mediapipe-posedetection/package.json');
} catch {
// empty catch block
}

/**
* Main config plugin entry point for react-native-mediapipe-posedetection
*
* Copies model assets to:
* - Android: android/app/src/main/assets/
* - iOS: ios/ (root folder with Xcode project references)
*
* Usage in app.json:
* ```json
* [
* "react-native-mediapipe-posedetection",
* {
* "assetsPaths": ["./models/"],
* "ignoredPattern": "\\.txt$" // optional regex pattern
* }
* ]
* ```
*/
const withMediapipePosedetection: ConfigPlugin<MediapipePluginProps> = (
config,
props
) => {
const { assetsPaths = [] } = props || {};

if (assetsPaths.length === 0) {
console.warn(
'⚠️ [react-native-mediapipe-posedetection] No assetsPaths provided to config plugin'
);
return config;
}

// Android
config = android.withAssets(config, props);

// iOS
config = ios.withAssets(config, props);

return config;
};

export default createRunOncePlugin(
withMediapipePosedetection,
pkg.name,
pkg.version
);
Loading
Loading