{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-scanner",
  "title": "Code scanner",
  "description": "QR/barcode viewfinder · simulated emits a sample payload",
  "dependencies": [
    "expo-camera@~57.0.5",
    "lucide-react-native@^1.21.0"
  ],
  "registryDependencies": [
    "@kit-native/button",
    "@kit-native/icon",
    "@kit-native/lib-haptics",
    "@kit-native/lib-native-context",
    "@kit-native/lib-utils",
    "@kit-native/permission-primer",
    "@kit-native/placeholder",
    "@kit-native/text"
  ],
  "files": [
    {
      "path": "components/kit/code-scanner.tsx",
      "content": "import { Placeholder } from '@/components/kit/placeholder';\nimport { PermissionPrimer } from '@/components/kit/permission-primer';\nimport { Button } from '@/components/ui/button';\nimport { Icon } from '@/components/ui/icon';\nimport { Text } from '@/components/ui/text';\nimport { haptic } from '@/lib/haptics';\nimport { useCapability } from '@/lib/native-context';\nimport { cn } from '@/lib/utils';\nimport { CameraView, type BarcodeType } from 'expo-camera';\nimport { XIcon } from 'lucide-react-native';\nimport * as React from 'react';\nimport { Pressable, View } from 'react-native';\n\nexport type CodeType = 'qr' | 'ean13' | 'code128' | 'pdf417' | 'upc_a';\n\ntype Props = {\n  onScan: (value: string, type?: string) => void;\n  /** Symbologies to look for. Default `['qr']`. */\n  types?: CodeType[];\n  /** Copy under the viewfinder frame. */\n  hint?: string;\n  /** Viewfinder aspect ratio. Default 1. */\n  ratio?: number;\n  /** Keep scanning after the first hit. Default false. */\n  continuous?: boolean;\n  /** What the simulated scanner emits, in order. */\n  simulatedValues?: string[];\n  /** Adds a close button over the viewfinder. Without it there is no way to stop the camera. */\n  onClose?: () => void;\n  simulate?: boolean;\n  className?: string;\n};\n\n/** Four corner brackets — reads as a viewfinder without hiding what is behind it. */\nfunction Reticle() {\n  const corner = 'border-primary absolute size-10';\n  return (\n    <View pointerEvents=\"none\" className=\"absolute inset-0 items-center justify-center\">\n      <View className=\"h-2/3 w-2/3\">\n        <View className={cn(corner, 'left-0 top-0 rounded-tl-2xl border-l-4 border-t-4')} />\n        <View className={cn(corner, 'right-0 top-0 rounded-tr-2xl border-r-4 border-t-4')} />\n        <View className={cn(corner, 'bottom-0 left-0 rounded-bl-2xl border-b-4 border-l-4')} />\n        <View className={cn(corner, 'bottom-0 right-0 rounded-br-2xl border-b-4 border-r-4')} />\n      </View>\n    </View>\n  );\n}\n\n/**\n * QR / barcode viewfinder with a framing overlay.\n *\n * Simulated wherever there is no camera — the web preview, the simulator — where it\n * shows the same overlay and a button that emits a sample payload.\n */\nexport function CodeScanner({\n  onScan,\n  types = ['qr'],\n  hint = 'Point the camera at a code',\n  ratio = 1,\n  continuous = false,\n  simulatedValues = ['https://example.com/kit-demo'],\n  onClose,\n  simulate,\n  className,\n}: Props) {\n  const { cap, request } = useCapability('camera', simulate);\n  const [done, setDone] = React.useState(false);\n  const [simIndex, setSimIndex] = React.useState(0);\n\n  const emit = (value: string, type?: string) => {\n    if (done && !continuous) return;\n    if (!continuous) setDone(true);\n    haptic('success');\n    onScan(value, type);\n  };\n\n  const frame = (children: React.ReactNode) => (\n    <View className={cn('gap-3', className)}>\n      <View className=\"bg-muted w-full overflow-hidden rounded-2xl\" style={{ aspectRatio: ratio }}>\n        {children}\n        <Reticle />\n        {onClose ? (\n          <Pressable\n            onPress={onClose}\n            accessibilityRole=\"button\"\n            accessibilityLabel=\"Close the scanner\"\n            className=\"bg-foreground/60 absolute right-3 top-3 size-11 items-center justify-center rounded-full active:opacity-80\">\n            <Icon as={XIcon} size={20} className=\"text-background\" />\n          </Pressable>\n        ) : null}\n      </View>\n      <Text className=\"text-muted-foreground text-center text-sm\">{hint}</Text>\n    </View>\n  );\n\n  if (cap.simulated) {\n    return (\n      <View className={cn('gap-3', className)}>\n        {frame(<Placeholder seed=\"scanner\" ratio={ratio} palette=\"mono\" className=\"h-full rounded-2xl\" />)}\n        <Button\n          variant=\"secondary\"\n          onPress={() => {\n            emit(simulatedValues[simIndex % simulatedValues.length]);\n            setSimIndex((i) => i + 1);\n          }}>\n          <Text>Simulate a scan</Text>\n        </Button>\n      </View>\n    );\n  }\n\n  if (cap.state !== 'ready') {\n    return (\n      <PermissionPrimer\n        capability=\"camera\"\n        className={className}\n        onAllow={() => void request()}\n        onSkip={() => emit(simulatedValues[0])}\n        skipLabel=\"Enter the code manually\"\n      />\n    );\n  }\n\n  return frame(\n    <CameraView\n      style={{ flex: 1 }}\n      facing=\"back\"\n      barcodeScannerSettings={{ barcodeTypes: types as BarcodeType[] }}\n      onBarcodeScanned={done && !continuous ? undefined : ({ data, type }) => emit(data, type)}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "components/kit/code-scanner.tsx"
    }
  ],
  "meta": {
    "exports": [
      "CodeScanner"
    ],
    "api": "<CodeScanner onScan types? hint? ratio? continuous? simulatedValues? simulate? />"
  },
  "docs": "Run `npx expo install --fix` afterwards so Expo SDK packages match your SDK. Needs the kit theme (`@kit-native/theme`) and `KitThemeProvider` from `@kit-native/lib-theme-context` at the root.",
  "categories": [
    "native"
  ],
  "type": "registry:component"
}