SkillAgentSearch skills...

add-camera

Internal implementation skill invoked by /add-native for camera, image picker, barcode scanner, QR scanner, and camera/gallery Dataverse artifact workflows.

Install / Use

npx skills add microsoft/power-platform-skills --skill add-camera

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

85/100

Category

Automation

Supported Platforms

Universal

Our assessment of add-camera

add-camera scores 85/100 on our quality scale, 1962nd of 2,892 Automation skills we index.

Its SKILL.md is 21 KB long, well organised into 11 sections with 9 code examples: a thorough specification that gives an agent plenty to work with.

It has 919 GitHub stars, a meaningful sign that others use it.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
13/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 12 days ago, so add-camera is actively maintained.
  • It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

add-camera compared with similar skills

All 4 of these similar skills score higher than add-camera; compare them before choosing.

SkillScoreStarsUpdatedFormat
add-camera (this skill)by microsoft8591912d agoSKILL.md
Agent-Reachby Panniantong10092.4k21d agoCLAUDE.md
Scraplingby D4Vinci10085.9ktodayMCP Server
LocalAIby mudler10049.4ktodayMCP Server
rufloby ruvnet10074.0ktodayMCP Server

Frequently asked questions

How do I install add-camera?
Run npx skills add microsoft/power-platform-skills --skill add-camera. The install tabs above show the steps for each supported agent.
Which AI agents does add-camera work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is add-camera safe to use?
It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is add-camera still maintained?
The repository was last updated 12 days ago, so add-camera is actively maintained.

name: add-camera description: Internal implementation skill invoked by /add-native for camera, image picker, barcode scanner, QR scanner, and camera/gallery Dataverse artifact workflows. user-invocable: false disable-model-invocation: true allowed-tools: Read, Edit, Write, Grep, Glob, Bash, AskUserQuestion model: sonnet

Shared instructions: shared-instructions.md — read first.

References:

Add Camera

Internal helper. Users should invoke /add-native camera, /add-native image-picker, /add-native barcode-scanner, or /add-native qr-scanner; /add-native routes here after resolving the capability.

Generate typed camera + image-picker wrappers, an optional barcode/QR scanner control, and optional custom-upload guidance for Dataverse image/file workflows.

This skill only writes JS files under src/native/. It does not install modules and does not touch package.json or app.config.js — the underlying Expo modules (expo-camera, expo-image-picker) and their config plugins must already be shipped by the microsoft/power-platform-skills/plugins/mobile-apps/template#main template. If they're missing, STOP and tell the user the template doesn't ship them yet.

Why: customer binaries are built from a pre-built rewrap base, not from the customer's package.json. Adding a native module here would compile against modules the binary doesn't actually contain, causing runtime crashes after rewrap. See /add-native for the same hard rules.

Two modules are required (must already be in package.json):

  • expo-camera — live viewfinder, barcode scanning
  • expo-image-picker — gallery selection + quick camera capture (simpler API, no viewfinder)

Dataverse File/Image boundary: for normal Dataverse File/Image form fields, screens should use FilePicker / ImagePicker from @microsoft/power-apps-native-host (see /add-native File/Image Picker Ownership). /add-native camera owns custom camera/gallery/scanner workflows, such as a dedicated evidence-capture screen, barcode/QR scan gate, or gallery-selected image that is transformed before saving.

Pen/signature boundary: signature, sign-off, ink, drawing, or pen capture belongs to /add-native pen-input (which routes internally to the pen helper). Both camera photos and pen signatures can persist to Dataverse Image/File columns, but the capture wrappers are separate.

Workflow

  1. Verify project → 2. Verify modules are template-shipped → 3. Write camera wrapper → 3b. Write scanner control if requested → 4. Detect Dataverse columns → 5. Write upload helper only for custom capture flows → 6. Type-check → 7. Summary

Step 1 — Verify project

test -f app.config.js && test -f power.config.json && test -f package.json

If any file is missing, report and STOP — this skill requires an initialized Power Apps mobile app.

Step 2 — Verify modules are template-shipped

Both expo-camera and expo-image-picker must already be in package.json. Do not install them — if they're missing, the upstream template hasn't shipped them yet, and this skill STOPs.

node -e "const p = require('./package.json'); const need = ['expo-camera','expo-image-picker']; const missing = need.filter(m => !p.dependencies?.[m]); if (missing.length) { console.error('MISSING from package.json: ' + missing.join(', ') + '. The upstream template must ship these for /add-native camera to run. Do NOT install them yourself — file an issue at the template repo (plugins/mobile-apps/template) instead.'); process.exit(1); } console.log('OK: both modules present');"

If the check fails, STOP. Print the error verbatim. Do not run npx expo install. Do not edit app.config.js. Tell the user the template version they scaffolded from doesn't include the camera modules — they need to wait for a newer template release or open a request upstream.

Also check if the wrapper already exists:

test -f src/native/camera.ts && echo "exists" || echo "missing"

If the wrapper exists, skip Step 3 — do NOT overwrite. Continue to Step 3b / Step 4 as needed.

Detect whether barcode/QR scanning is requested by checking $ARGUMENTS and native-app-plan.md for barcode, bar code, QR, scanner, scan gate, SKU scan, or inventory scan. If present, set SCANNER_NEEDED=yes; otherwise skip Step 3b unless the user explicitly asks for scanner support.

Step 3 — Write camera wrapper

Print before starting:

"→ Writing src/native/camera.ts wrapper (takePhoto + pickImage with discriminated-union results)…"

Create src/native/camera.ts. If the file already exists, do NOT overwrite — append a comment noting "regenerated by /add-native camera" and STOP this step.

// src/native/camera.ts
// Camera capture and image picker wrapper for Power Apps mobile apps.
// Uses expo-image-picker for both camera capture and gallery selection.
// All functions return discriminated-union results — never throw.

import * as ImagePicker from 'expo-image-picker';

// --- Result types ---

export type PhotoResult =
  | { ok: true; uri: string; width: number; height: number; mimeType?: string; fileSize?: number }
  | { ok: false; reason: 'permission-denied' | 'cancelled' | 'unsupported' | 'error'; message?: string };

// --- Permission ---

export async function requestCameraPermission(): Promise<boolean> {
  const { status } = await ImagePicker.requestCameraPermissionsAsync();
  return status === 'granted';
}

export async function requestMediaLibraryPermission(): Promise<boolean> {
  const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
  return status === 'granted';
}

// --- Capture ---

/**
 * Launch the device camera and capture a photo.
 * Returns `{ ok: false, reason: 'unsupported' }` when native camera capture is unavailable.
 */
export async function takePhoto(options?: {
  quality?: number;
  allowsEditing?: boolean;
}): Promise<PhotoResult> {
  const granted = await requestCameraPermission();
  if (!granted) return { ok: false, reason: 'permission-denied' };

  try {
    const result = await ImagePicker.launchCameraAsync({
      mediaTypes: ['images'],
      quality: options?.quality ?? 0.8,
      allowsEditing: options?.allowsEditing ?? false,
      exif: false,
    });

    if (result.canceled) return { ok: false, reason: 'cancelled' };

    const asset = result.assets[0];
    return {
      ok: true,
      uri: asset.uri,
      width: asset.width ?? 0,
      height: asset.height ?? 0,
      mimeType: asset.mimeType ?? undefined,
      fileSize: asset.fileSize ?? undefined,
    };
  } catch (e: any) {
    return { ok: false, reason: 'error', message: e?.message };
  }
}

/**
 * Open the device photo gallery and pick an image.
 * Works on all platforms including web (uses native file picker).
 */
export async function pickImage(options?: {
  quality?: number;
  allowsEditing?: boolean;
  allowsMultipleSelection?: boolean;
}): Promise<PhotoResult> {
  const granted = await requestMediaLibraryPermission();
  if (!granted) return { ok: false, reason: 'permission-denied' };

  try {
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ['images'],
      quality: options?.quality ?? 0.8,
      allowsEditing: options?.allowsEditing ?? false,
      allowsMultipleSelection: options?.allowsMultipleSelection ?? false,
      exif: false,
    });

    if (result.canceled) return { ok: false, reason: 'cancelled' };

    const asset = result.assets[0];
    return {
      ok: true,
      uri: asset.uri,
      width: asset.width ?? 0,
      height: asset.height ?? 0,
      mimeType: asset.mimeType ?? undefined,
      fileSize: asset.fileSize ?? undefined,
    };
  } catch (e: any) {
    return { ok: false, reason: 'error', message: e?.message };
  }
}

Step 3b — Write barcode/QR scanner control when requested

Skip this step unless SCANNER_NEEDED=yes. Photo-only and gallery-only flows do not need a live CameraView.

Print before starting:

"→ Writing src/native/barcodeScanner.tsx (CameraView barcode/QR scanner control)…"

Create src/native/barcodeScanner.tsx. If it already exists, do not overwrite.

// src/native/barcodeScanner.tsx
// Barcode / QR scanner control for Power Apps mobile apps.
// Uses expo-camera CameraView. Never throws; permission state is rendered inline.

import React from 'react';
import { StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
import type { BarcodeScanningResult, BarcodeType } from 'expo-camera';

export type ScannerResult = {
  ok: true;
  data: string;
  type: string;
  raw: BarcodeScanningResult;
};

export type BarcodeScannerViewProps = {
  onScanned: (result: ScannerResult) => void;
  paused?: boolean;
  resetKey?: unknown;
  barcodeTypes?: BarcodeType[];
  style?: StyleProp<ViewStyle>;
  overlay?: React.ReactNode;
  children?: React.ReactNode;
};

const DEFAULT_BARCODE_TYPES = [
  'aztec',
  'qr',
  'ean13',
  'ean8',
  'upc_a',
  'upc_e',
  'datamatrix',
  'code39',
  'code93',
  'code128',
  'pdf417',
  'itf14',
  'codabar',
] as BarcodeType[];

export function BarcodeScannerView({
  onScanned,
  paused = false,
  resetKey,
  barcodeTypes = DEFAULT_BARCODE_TYPES,
  style,
  overlay,
  children,
}: BarcodeScannerViewProps) {
  const [permission, requestPermission] = useCameraPermissions();
  const scanLockedRef = React.useRef(false);

  React.useEffect(() => {
    if (permission && !permission.granted && permission.canAskAgain) {
      requestPermission();
    }
  }, [permission, requestPermission]);

  React.useEffect(() => {
    if (!paused) {
      scanLockedRef.current = false;
    }
  }, [paused, resetKey]);

  const handleBarcodeScanned = React.useCallback((event: BarcodeScanningResult) => {
    if (paused || scanLockedRef.current) return;
    scanLockedRef.current = true;
    onScanned({ ok: true, data: event.data, type: event.type, raw: event });
  }, [onScanned, paused]);

  if (!permission) {
    return <View style={[styles.fallback, style]}><Text>Checking camera permission...</Text></View>;
  }

  if (!permission.granted) {
    return <View style={[styles.fallback, style]}><Text>Camera permission is required to scan codes.</Text></View>;
  }

  return (
    <View style={[styles.container, style]}>
      <CameraView
        style={StyleSheet.absoluteFill}
        facing="back"
        active={!paused}
        barcodeScannerSettings={{ barcodeTypes }}
        onBarcodeScanned={paused ? undefined : handleBarcodeScanned}
      />
      {overlay || children ? <View pointerEvents="box-none" style={styles.overlay}>{overlay ?? children}</View> : null}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, overflow: 'hidden', position: 'relative' },
  overlay: { ...StyleSheet.absoluteFillObject },
  fallback: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 16 },
});

Scanner rendering rule: do not put overlay UI as CameraView children. Expo Camera can render incorrectly when React children are nested inside the native camera preview. The generated control renders the camera as one layer and renders overlay / children as a sibling absolute layer above it.

Scan mutation rule: the generated control has an internal one-shot scan lock so rapid onBarcodeScanned callbacks cannot double-submit. Screens should still set paused=true before navigating or mutating data, then reset paused=false and change resetKey when the screen regains focus. This makes returning to the scanner reliable after a successful scan.

Scan-gate business rule: for QR lookup flows, resolve the scanned code ag

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars919
CategoryAutomation
Updated12d ago
Forks186

Languages

JavaScript

Trust signals

100/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

No cautions