@sigx/lynx-camera
v0.26.0
Published
Camera capture for sigx-lynx
Downloads
2,646
Maintainers
Readme
@sigx/lynx-camera
Photo & video capture via the system camera for sigx-lynx. iOS uses UIImagePickerController; Android uses an ACTION_IMAGE_CAPTURE / ACTION_VIDEO_CAPTURE intent fed by a FileProvider URI (the cache-path is wired by the app template's manifest).
📚 Documentation
Full guides, API reference and live examples → https://sigx.dev/lynx/modules/camera/overview/
Install
pnpm add @sigx/lynx-camerasigx prebuild auto-discovers the package, links the native module, injects android.permission.CAMERA, and adds the iOS usage descriptions:
NSCameraUsageDescriptionNSMicrophoneUsageDescriptionNSPhotoLibraryAddUsageDescriptionOverride the prompts in yoursignalx.config.tsunderios.usageDescriptionsif you want app-specific copy:
// signalx.config.ts
export default defineLynxConfig({
ios: {
usageDescriptions: {
NSCameraUsageDescription: 'Acme uses the camera to scan QR codes.',
},
},
});On Android the runtime permission prompt + Activity Result wiring comes from @sigx/lynx-permissions, a dependency of this package — the auto-linker pulls it in, nothing to install.
Usage
import { Camera } from '@sigx/lynx-camera';
const { status } = await Camera.requestPermission();
if (status === 'granted') {
try {
const photo = await Camera.takePicture({ quality: 0.8, facing: 'back' });
if (photo.uri) console.log(photo.uri, photo.width, photo.height);
// else: the user cancelled (resolves with `{ cancelled: true }`)
// Record a clip — the returned URI loads directly in @sigx/lynx-video.
const clip = await Camera.recordVideo({ maxDurationMs: 30_000 });
if (clip.uri) console.log(clip.uri, clip.durationMs);
} catch (e) {
// Failure (permission denied, no camera, …) rejects.
console.warn('capture failed:', e);
}
}Each capture has three outcomes: it resolves with a result (always carrying a
uri), resolves with { cancelled: true } (no uri) if the user dismisses the
camera, or throws on failure. Narrow on result.uri and wrap in try/catch.
API
| Method | Notes |
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| takePicture(options?: CameraOptions): Promise<PhotoResult \| CameraCancelled> | Opens the system camera in photo mode. Resolves with the captured photo's URI (plus dimensions where available — iOS only), or { cancelled: true } on cancel; throws on failure. |
| recordVideo(options?: CameraVideoOptions): Promise<VideoResult \| CameraCancelled> | Opens the system camera in video mode. Resolves with the recorded clip's URI (file:// on iOS, content:// on Android) loadable by @sigx/lynx-video, or { cancelled: true } on cancel; throws on failure. |
| requestPermission(): Promise<PermissionResponse> | Shows the OS permission dialog if needed. Re-call to surface the dialog again on first denial. |
| getPermissionStatus(): Promise<PermissionResponse> | Read-only check — no prompt. |
| isAvailable(): boolean | Whether the native module is registered in the current build. |
interface CameraOptions { // Android's intent ignores all of these
facing?: 'front' | 'back'; // iOS only; default: 'back'
quality?: number; // iOS only; 0..1 (default 0.8)
maxWidth?: number; // reserved — not yet applied on either platform
maxHeight?: number; // reserved — not yet applied on either platform
}
interface PhotoResult {
uri: string; // file:// (iOS) or content:// (Android)
width?: number; // iOS only — Android's intent doesn't report dimensions
height?: number; // iOS only
fileSize?: number; // iOS only
base64?: string; // populated only if requested
}
interface CameraVideoOptions { // all iOS-only — Android's intent ignores options
facing?: 'front' | 'back'; // default: 'back'
maxDurationMs?: number; // iOS only — Android's intent has no duration cap
}
interface VideoResult {
uri: string; // file:// (iOS) or content:// (Android)
durationMs?: number; // reported where the platform provides it
width?: number;
height?: number;
fileSize?: number;
}
interface CameraCancelled {
cancelled: true;
uri?: undefined; // present so callers can narrow on `result.uri`
}Gotchas
- Permission is auto-requested —
takePicture/recordVideorequest the camera permission for you before opening the camera (iOS viaAVCaptureDevice; Android via the runtimeCAMERAprompt, plus microphone forrecordVideowhen the app declaresRECORD_AUDIO). CallingrequestPermission()first is optional — useful only to gate UI on the status ahead of time. On Android it's also required internally: the manifest declaresCAMERA, and the OS refusesACTION_IMAGE_CAPTURE/ACTION_VIDEO_CAPTUREunless it's been granted. - Android FileProvider — the auto-injected
<provider>in the app template'sAndroidManifest.xmlexposes the cache directory under${applicationId}.fileprovider. If you customize the manifest, keep that authority intact or the camera intent won't have a valid write target. - iOS simulator camera — the simulator has no real camera, so
takePicture/recordVideoreport "Camera not available". Test capture on a physical device. - Options are honored on iOS only — Android delegates to the system camera intent, which ignores
CameraOptions/CameraVideoOptionsentirely (the user can still switch cameras in its UI; just don't rely onfacingto pick one programmatically on Android). On iOS,facing,quality, andmaxDurationMs(video) are applied;maxWidth/maxHeightare reserved and not yet applied on either platform. - A single in-camera photo/video toggle (one screen, switch mode in-camera) is iOS-only at the system level; Android has separate photo/video intents. Present your own chooser (Take Photo / Record Video) for a consistent cross-platform flow — see
examples/showcase'sMediaCaptureCard. An iOS-nativecapture({ mediaType: 'mixed' })toggle may be added later.
