@ai-matrx/capture 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/CameraCapture.tsx","../src/cn.ts","../src/hooks/useTrackControls.ts","../src/components/ShutterButton.tsx","../src/components/ModeSelector.tsx","../src/components/ZoomRow.tsx","../src/components/OptionsGridPanel.tsx","../src/components/CaptureSheet.tsx","../src/components/GridOverlay.tsx","../src/components/CountdownOverlay.tsx","../src/components/ImageEditSheet.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CameraCapture — the opinionated iPhone-style camera surface, assembled.\n *\n * Layout (matching the iOS Camera app):\n * - Full-bleed live feed with a semi-transparent near-black bar across the\n * top and bottom — controls read clearly while the feed shows through.\n * - Top bar: close (host-provided), center slot, torch, host extras, and the\n * grid button revealing the two-tap OptionsGridPanel.\n * - Over the feed's bottom edge: real zoom pills (only when the track\n * reports a zoom range).\n * - Bottom bar: injected rows → VIDEO·PHOTO·UPLOAD mode selector → recents\n * thumb · shutter · flip.\n * - Overlays: rule-of-thirds grid, timer countdown, recording chip, blocked\n * sheet (iOS system-sheet presentation).\n *\n * The chrome owns UI state only (options panel, grid, timer, countdown).\n * Capture behavior, streams and persistence come from the injected\n * `CaptureCameraEngine`; domain features attach through `CaptureCameraSlots`.\n *\n * Package source (`@ai-matrx/capture`).\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n Grip,\n Grid3x3,\n Proportions,\n SunMedium,\n Timer,\n X,\n Zap,\n ZapOff,\n RefreshCw,\n} from \"lucide-react\";\nimport { cn } from \"../cn\";\n\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCloudPort,\n CaptureOptionTile,\n CaptureTimerSetting,\n} from \"../types\";\nimport { useTrackControls } from \"../hooks/useTrackControls\";\nimport { ShutterButton } from \"./ShutterButton\";\nimport { ModeSelector } from \"./ModeSelector\";\nimport { ZoomRow } from \"./ZoomRow\";\nimport { OptionsGridPanel } from \"./OptionsGridPanel\";\nimport { CaptureSheet, type CaptureSheetAction } from \"./CaptureSheet\";\nimport { GridOverlay } from \"./GridOverlay\";\nimport { CountdownOverlay } from \"./CountdownOverlay\";\n\nexport interface CameraCaptureProps {\n engine: CaptureCameraEngine;\n /** THE CLOUD LAW: the cloud port is REQUIRED — a camera with no cloud\n * library/persistence is not a valid instance of this system. */\n cloud: CaptureCloudPort;\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n /** The live preview element (host wires its runtime's `CameraPreview`). */\n preview: React.ReactNode;\n /** Close affordance top-left; omitted = no close button. */\n onClose?: () => void;\n /** Body + actions for the camera-blocked iOS-style sheet. */\n blockedSheet?: { body: React.ReactNode; actions: CaptureSheetAction[] };\n /** Hide all chrome except honesty chips (host renders its own toggle). */\n controlsHidden?: boolean;\n shutterDisabled?: boolean;\n slots?: CaptureCameraSlots;\n}\n\nfunction formatElapsed(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = String(totalSeconds % 60).padStart(2, \"0\");\n return `${m}:${s}`;\n}\n\nconst ASPECT_CYCLE: CaptureAspect[] = [\"full\", \"4:3\", \"1:1\", \"16:9\"];\n\nexport function CameraCapture({\n engine,\n cloud,\n mode,\n onModeChange,\n preview,\n onClose,\n blockedSheet,\n controlsHidden = false,\n shutterDisabled = false,\n slots = {},\n}: CameraCaptureProps) {\n const [optionsOpen, setOptionsOpen] = useState(false);\n const [gridOn, setGridOn] = useState(false);\n const [timerSetting, setTimerSetting] = useState<CaptureTimerSetting>(0);\n const [aspect, setAspect] = useState<CaptureAspect>(\"full\");\n const [exposureOpen, setExposureOpen] = useState(false);\n const [countdown, setCountdown] = useState<number | null>(null);\n // The blocked sheet is dismissible — uploads and the system camera keep\n // working, so closing it reveals the chrome rather than leaving the page.\n const [blockedDismissed, setBlockedDismissed] = useState(false);\n const countdownRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const controls = useTrackControls(engine.stream);\n\n const clearCountdown = useCallback(() => {\n if (countdownRef.current) clearInterval(countdownRef.current);\n countdownRef.current = null;\n setCountdown(null);\n }, []);\n useEffect(() => clearCountdown, [clearCountdown]);\n\n const onShutter = useCallback(() => {\n if (mode === \"video\") {\n if (engine.recording) engine.onStopRecording();\n else engine.onStartRecording();\n return;\n }\n if (countdown !== null) {\n // Tapping mid-countdown cancels — matching iOS.\n clearCountdown();\n return;\n }\n if (timerSetting === 0) {\n engine.onCapturePhoto({ aspect });\n return;\n }\n let remaining = timerSetting;\n setCountdown(remaining);\n countdownRef.current = setInterval(() => {\n remaining -= 1;\n if (remaining <= 0) {\n clearCountdown();\n engine.onCapturePhoto({ aspect });\n } else {\n setCountdown(remaining);\n }\n }, 1000);\n }, [mode, engine, countdown, timerSetting, aspect, clearCountdown]);\n\n const cycleTimer = useCallback(() => {\n setTimerSetting((t) => (t === 0 ? 3 : t === 3 ? 10 : 0));\n }, []);\n\n const coreTiles: CaptureOptionTile[] = [\n ...(controls.torchSupported\n ? [\n {\n id: \"flash\",\n label: \"Flash\",\n icon: controls.torchOn ? (\n <Zap className=\"h-6 w-6\" fill=\"currentColor\" />\n ) : (\n <ZapOff className=\"h-6 w-6\" />\n ),\n active: controls.torchOn,\n onPress: controls.toggleTorch,\n },\n ]\n : []),\n {\n id: \"timer\",\n label: \"Timer\",\n icon: <Timer className=\"h-6 w-6\" />,\n active: timerSetting !== 0,\n valueLabel: timerSetting === 0 ? undefined : `${timerSetting}s`,\n onPress: cycleTimer,\n },\n {\n id: \"grid\",\n label: \"Grid\",\n icon: <Grid3x3 className=\"h-6 w-6\" />,\n active: gridOn,\n onPress: () => setGridOn((g) => !g),\n },\n // Aspect applies to PHOTO output (center-cropped from the full sensor).\n {\n id: \"aspect\",\n label: \"Aspect\",\n icon: <Proportions className=\"h-6 w-6\" />,\n active: aspect !== \"full\",\n valueLabel: aspect === \"full\" ? undefined : aspect,\n onPress: () =>\n setAspect(\n (a) =>\n ASPECT_CYCLE[\n (ASPECT_CYCLE.indexOf(a) + 1) % ASPECT_CYCLE.length\n ] ?? \"full\",\n ),\n },\n ...(controls.exposureSupported\n ? [\n {\n id: \"exposure\",\n label: \"Exposure\",\n icon: <SunMedium className=\"h-6 w-6\" />,\n active: controls.exposure !== 0 || exposureOpen,\n valueLabel:\n controls.exposure === 0\n ? undefined\n : `${controls.exposure > 0 ? \"+\" : \"\"}${controls.exposure}`,\n onPress: () => setExposureOpen((o) => !o),\n },\n ]\n : []),\n ];\n const tiles = [...coreTiles, ...(slots.optionTiles ?? [])];\n\n const blocked = engine.blocked !== null;\n\n return (\n <div className=\"absolute inset-0 select-none overflow-hidden bg-black\">\n {/* Full-bleed feed */}\n <div className=\"absolute inset-0\">{preview}</div>\n <GridOverlay visible={gridOn && !blocked} />\n {/* Aspect framing hint — the photo output is center-cropped to the\n selected ratio; the dimmed bands approximate the discarded region\n of the VISIBLE frame (the capture itself crops the full sensor). */}\n {mode === \"photo\" && aspect !== \"full\" && !blocked && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center\"\n >\n <div\n className=\"shadow-[0_0_0_9999px_rgba(0,0,0,0.45)]\"\n style={{\n aspectRatio:\n aspect === \"1:1\" ? \"1 / 1\" : aspect === \"4:3\" ? \"3 / 4\" : \"9 / 16\",\n width: aspect === \"16:9\" ? \"100%\" : undefined,\n height: aspect === \"16:9\" ? undefined : \"70%\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n }}\n />\n </div>\n )}\n <CountdownOverlay seconds={countdown} />\n\n {/* Top bar — ONE compact row; every pixel here is stolen from the\n feed, and a phone browser already spends chrome above us. */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 top-0 z-20 bg-black/65 pt-safe backdrop-blur-[2px]\">\n <div className=\"flex h-11 items-center gap-0.5 px-1.5\">\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close camera\"\n className=\"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"w-10 shrink-0\" />\n )}\n <div className=\"min-w-0 flex-1\">{slots.topBarCenter}</div>\n {controls.torchSupported && (\n <button\n type=\"button\"\n onClick={controls.toggleTorch}\n aria-label={\n controls.torchOn ? \"Turn flash off\" : \"Turn flash on\"\n }\n aria-pressed={controls.torchOn}\n className={cn(\n \"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n controls.torchOn\n ? \"text-[#FFCC00]\"\n : \"text-white hover:bg-white/10\",\n )}\n >\n <Zap\n className=\"h-5 w-5\"\n fill={controls.torchOn ? \"currentColor\" : \"none\"}\n />\n </button>\n )}\n {slots.topBarTrailing}\n <button\n type=\"button\"\n onClick={() => setOptionsOpen((o) => !o)}\n aria-label=\"More camera options\"\n aria-expanded={optionsOpen}\n className={cn(\n \"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n optionsOpen ? \"bg-white/20 text-white\" : \"text-white hover:bg-white/10\",\n )}\n >\n <Grip className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Honesty chips — visible even with controls hidden. */}\n <div className=\"pointer-events-none absolute inset-x-0 top-[50px] z-20 mt-safe flex flex-col items-center gap-1.5\">\n {engine.recording && (\n <span className=\"flex items-center gap-2 rounded-full bg-black/60 px-3 py-1.5 text-sm font-medium text-white\">\n <span className=\"h-2.5 w-2.5 animate-pulse rounded-full bg-[#FF3B30]\" />\n {formatElapsed(engine.recordElapsedSeconds)}\n </span>\n )}\n {slots.statusChips}\n </div>\n\n {/* Bottom stack */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 bottom-0 z-20\">\n {/* Zoom pills float on the FEED, just above the bottom bar. */}\n {!blocked && controls.zoomOptions.length >= 2 && (\n <div className=\"mb-2\">\n <ZoomRow\n options={controls.zoomOptions}\n value={controls.zoom}\n onSelect={controls.setZoom}\n />\n </div>\n )}\n {/* Host content OVER the feed (filmstrip etc.) — zero bar height. */}\n {slots.aboveBar && <div className=\"mb-1.5 px-2\">{slots.aboveBar}</div>}\n {/* Exposure slider — revealed by the EXPOSURE tile, floats above\n the bottom bar like the iOS exposure control. */}\n {exposureOpen && controls.exposureSupported && controls.exposureRange && (\n <div className=\"mx-auto mb-3 flex w-64 items-center gap-3 rounded-full bg-black/55 px-4 py-2\">\n <SunMedium className=\"h-4 w-4 shrink-0 text-[#FFCC00]\" />\n <input\n type=\"range\"\n min={controls.exposureRange.min}\n max={controls.exposureRange.max}\n step={controls.exposureRange.step}\n value={controls.exposure}\n onChange={(e) => controls.setExposure(Number(e.target.value))}\n aria-label=\"Exposure compensation\"\n className=\"w-full accent-[#FFCC00]\"\n />\n <span className=\"w-8 shrink-0 text-right text-xs tabular-nums text-white\">\n {controls.exposure > 0 ? \"+\" : \"\"}\n {controls.exposure}\n </span>\n </div>\n )}\n <div className=\"bg-black/65 px-3 pb-safe backdrop-blur-[2px]\">\n {slots.aboveModeSelector}\n {/* Connected mode bar + the domain action share ONE row as flex\n siblings — absolute positioning collided with the labels on\n narrow phones, twice. Never reintroduce it. */}\n <div className=\"flex items-center justify-center gap-2 py-1.5\">\n <ModeSelector\n mode={mode}\n onModeChange={onModeChange}\n onUpload={engine.onUpload}\n modeDisabled={engine.recording}\n uploadDisabled={shutterDisabled && !blocked}\n extraModes={slots.extraModes}\n />\n {slots.modeRowTrailing && (\n <div className=\"shrink-0\">{slots.modeRowTrailing}</div>\n )}\n </div>\n <div className=\"flex items-center justify-between px-2 pb-1.5 pt-0.5\">\n <div className=\"flex w-14 justify-start\">\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Open your media library\"\n className=\"h-11 w-11 touch-manipulation overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/25 transition-transform active:scale-95\"\n >\n {cloud.recentsThumb ?? (\n <span className=\"block h-full w-full bg-white/5\" />\n )}\n </button>\n </div>\n <ShutterButton\n mode={mode}\n recording={engine.recording}\n disabled={shutterDisabled || blocked}\n onPress={onShutter}\n />\n <div className=\"flex w-14 justify-end\">\n {engine.onFlipCamera ? (\n <button\n type=\"button\"\n onClick={engine.onFlipCamera}\n aria-label=\"Switch camera\"\n className=\"flex h-11 w-11 touch-manipulation items-center justify-center rounded-full bg-white/15 text-white transition-transform active:rotate-180 active:scale-95 duration-300\"\n >\n <RefreshCw className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"h-11 w-11\" />\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n\n <OptionsGridPanel\n open={optionsOpen && !controlsHidden}\n onClose={() => setOptionsOpen(false)}\n tiles={tiles}\n />\n\n {blocked && blockedSheet && !blockedDismissed && (\n <CaptureSheet\n open\n onClose={() => setBlockedDismissed(true)}\n body={blockedSheet.body}\n title=\"Camera unavailable\"\n actions={blockedSheet.actions}\n />\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","/**\n * The package-local `cn` — the documented substitution point for the app's\n * `@/lib/utils` cn during extraction (same pattern as `@ai-matrx/media`).\n */\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(\n ...inputs: Array<string | false | null | undefined>\n): string {\n return twMerge(inputs.filter(Boolean).join(\" \"));\n}\n","\"use client\";\n\n/**\n * useTrackControls — HONEST torch + zoom over a live video track's real\n * capabilities (`MediaTrackCapabilities`). If the hardware doesn't report\n * torch or a zoom range, the corresponding control simply doesn't exist —\n * we never render a fake toggle.\n *\n * Zoom pill options are derived from the reported [min, max] range using the\n * familiar phone ladder (.5 / 1 / 2 / 4 / 8) clipped to what the device can\n * actually do. Applied via `track.applyConstraints({ advanced: [...] })`.\n *\n * Package source (`@ai-matrx/capture`) — browser media APIs only, no app\n * imports.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\n\n// torch/zoom are spec'd in mediacapture-image but absent from lib.dom.\ninterface ExtendedCapabilities extends MediaTrackCapabilities {\n torch?: boolean;\n zoom?: { min: number; max: number; step?: number };\n exposureCompensation?: { min: number; max: number; step?: number };\n}\ninterface ExtendedSettings extends MediaTrackSettings {\n torch?: boolean;\n zoom?: number;\n exposureCompensation?: number;\n}\n\nconst ZOOM_LADDER = [0.5, 1, 2, 4, 8];\n\nexport interface TrackControls {\n torchSupported: boolean;\n torchOn: boolean;\n toggleTorch: () => void;\n /** ≥2 entries when the track reports a usable zoom range, else []. */\n zoomOptions: number[];\n zoom: number;\n setZoom: (factor: number) => void;\n /** Exposure compensation — only when the hardware reports a range. */\n exposureSupported: boolean;\n exposure: number;\n exposureRange: { min: number; max: number; step: number } | null;\n setExposure: (value: number) => void;\n}\n\nexport function useTrackControls(stream: MediaStream | null): TrackControls {\n const [torchOn, setTorchOn] = useState(false);\n const [zoom, setZoomState] = useState(1);\n const [exposure, setExposureState] = useState(0);\n // Capabilities can be empty until the track is fully live; re-read once on\n // stream change and again on a short delayed tick (iOS reports late).\n const [caps, setCaps] = useState<ExtendedCapabilities | null>(null);\n\n const track = stream?.getVideoTracks()[0] ?? null;\n\n useEffect(() => {\n setTorchOn(false);\n if (!track || typeof track.getCapabilities !== \"function\") {\n setCaps(null);\n return;\n }\n const read = () => {\n try {\n setCaps(track.getCapabilities() as ExtendedCapabilities);\n const settings = track.getSettings() as ExtendedSettings;\n if (typeof settings.zoom === \"number\") setZoomState(settings.zoom);\n if (typeof settings.exposureCompensation === \"number\")\n setExposureState(settings.exposureCompensation);\n } catch {\n setCaps(null);\n }\n };\n read();\n const timer = window.setTimeout(read, 750);\n return () => window.clearTimeout(timer);\n }, [track]);\n\n const torchSupported = caps?.torch === true;\n\n const toggleTorch = useCallback(() => {\n if (!track || !torchSupported) return;\n const next = !torchOn;\n track\n .applyConstraints({ advanced: [{ torch: next } as MediaTrackConstraintSet] })\n .then(() => setTorchOn(next))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] torch toggle failed\", err);\n });\n }, [track, torchSupported, torchOn]);\n\n const zoomOptions = useMemo(() => {\n const range = caps?.zoom;\n if (!range || !(range.max > range.min)) return [];\n const options = ZOOM_LADDER.filter(\n (f) => f >= range.min && f <= range.max,\n );\n if (!options.includes(1) && 1 >= range.min && 1 <= range.max) {\n options.push(1);\n options.sort((a, b) => a - b);\n }\n return options.length >= 2 ? options : [];\n }, [caps]);\n\n const setZoom = useCallback(\n (factor: number) => {\n if (!track || !caps?.zoom) return;\n const clamped = Math.min(caps.zoom.max, Math.max(caps.zoom.min, factor));\n track\n .applyConstraints({\n advanced: [{ zoom: clamped } as MediaTrackConstraintSet],\n })\n .then(() => setZoomState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] zoom failed\", err);\n });\n },\n [track, caps],\n );\n\n const exposureCaps = caps?.exposureCompensation;\n const exposureRange =\n exposureCaps && exposureCaps.max > exposureCaps.min\n ? {\n min: exposureCaps.min,\n max: exposureCaps.max,\n step: exposureCaps.step && exposureCaps.step > 0 ? exposureCaps.step : 0.5,\n }\n : null;\n\n const setExposure = useCallback(\n (value: number) => {\n if (!track || !exposureRange) return;\n const clamped = Math.min(\n exposureRange.max,\n Math.max(exposureRange.min, value),\n );\n track\n .applyConstraints({\n advanced: [\n { exposureCompensation: clamped } as MediaTrackConstraintSet,\n ],\n })\n .then(() => setExposureState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] exposure failed\", err);\n });\n },\n [track, exposureRange],\n );\n\n return {\n torchSupported,\n torchOn,\n toggleTorch,\n zoomOptions,\n zoom,\n setZoom,\n exposureSupported: exposureRange !== null,\n exposure,\n exposureRange,\n setExposure,\n };\n}\n","\"use client\";\n\n/**\n * ShutterButton — the iPhone shutter: a thin white ring with a filled inner\n * circle. Photo = white fill; video idle = red circle; video recording = the\n * inner shape morphs to a small red rounded square (the iOS stop affordance).\n * Press feedback is a scale-down of the INNER fill only, like iOS.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureCameraMode } from \"../types\";\n\nexport interface ShutterButtonProps {\n mode: CaptureCameraMode;\n recording: boolean;\n disabled?: boolean;\n onPress: () => void;\n}\n\nexport function ShutterButton({\n mode,\n recording,\n disabled = false,\n onPress,\n}: ShutterButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onPress}\n disabled={disabled}\n aria-label={\n mode === \"photo\"\n ? \"Take photo\"\n : recording\n ? \"Stop recording\"\n : \"Start recording\"\n }\n className={cn(\n \"group flex h-[74px] w-[74px] shrink-0 items-center justify-center rounded-full\",\n \"border-[3.5px] border-white transition-opacity\",\n disabled && \"opacity-30\",\n )}\n >\n <span\n className={cn(\n \"block transition-all duration-200 ease-out group-active:scale-90\",\n mode === \"photo\"\n ? \"h-[62px] w-[62px] rounded-full bg-white\"\n : recording\n ? \"h-8 w-8 rounded-md bg-[#FF3B30]\"\n : \"h-[62px] w-[62px] rounded-full bg-[#FF3B30]\",\n )}\n />\n </button>\n );\n}\n","\"use client\";\n\n/**\n * ModeSelector — the CONNECTED capture-mode bar: one compact rounded track\n * holding VIDEO · PHOTO · UPLOAD (+ injected extras) with a spring-sliding\n * thumb under the active mode (the proven CaptureModeBar interaction, in\n * iPhone dress: uppercase letter-spaced labels, active in iOS camera\n * yellow). Deliberately narrow — the row keeps usable space on BOTH sides\n * for host affordances; screen height is the scarcest resource on a phone\n * browser, so the bar is one short row, never a spread of pills.\n *\n * VIDEO and PHOTO are persistent modes; UPLOAD and extras are immediate\n * actions and never take the thumb.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\n\nimport type { CaptureCameraMode } from \"../types\";\n\n/** Spring curve with a small overshoot (CSS linear() approximation);\n * class-level overshooting cubic-bezier is the fallback. */\nconst SPRING_EASING =\n \"linear(0, 0.0047 0.71%, 0.0189 1.44%, 0.0755 2.93%, 0.1692 4.49%, \" +\n \"0.3921 7.55%, 0.8121 12.94%, 0.9804 15.49%, 1.0946 18.14%, 1.1423 20.16%, \" +\n \"1.1568 21.62%, 1.1541 23.03%, 1.1113 26.4%, 1.0322 31.83%, 0.9902 36.25%, \" +\n \"0.9769 40.24%, 0.9844 45.87%, 1.0028 55.35%, 1.0075 63.42%, 1.0006 85.48%, 1)\";\n\nconst LABEL_BASE =\n \"relative z-10 flex h-8 min-w-0 touch-manipulation items-center justify-center rounded-full px-3 \" +\n \"text-[11px] font-semibold uppercase tracking-[0.12em] transition-colors duration-200 \" +\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 disabled:opacity-40\";\n\nexport interface ModeSelectorProps {\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n onUpload: () => void;\n /** Locks mode switching (while recording). */\n modeDisabled?: boolean;\n uploadDisabled?: boolean;\n /** Host-injected extra entries after UPLOAD (e.g. SCAN) — immediate\n * actions, never the active mode. */\n extraModes?: { id: string; label: string; onSelect: () => void }[];\n}\n\nexport function ModeSelector({\n mode,\n onModeChange,\n onUpload,\n modeDisabled = false,\n uploadDisabled = false,\n extraModes = [],\n}: ModeSelectorProps) {\n const segments = 3 + extraModes.length;\n const activeIndex = mode === \"video\" ? 0 : 1;\n\n return (\n <div\n role=\"tablist\"\n aria-label=\"Capture mode\"\n className=\"relative inline-grid rounded-full bg-white/10 p-1\"\n style={{ gridTemplateColumns: `repeat(${segments}, minmax(0, 1fr))` }}\n >\n {/* Sliding thumb — transform-only, springs with overshoot, instant\n under prefers-reduced-motion. */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute bottom-1 top-1 left-1 rounded-full bg-white/20 transition-transform duration-500 ease-[cubic-bezier(0.34,1.56,0.64,1)] will-change-transform motion-reduce:transition-none\"\n style={{\n width: `calc((100% - 0.5rem) / ${segments})`,\n transform: `translateX(${activeIndex * 100}%)`,\n transitionTimingFunction: SPRING_EASING,\n }}\n />\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === \"video\"}\n disabled={modeDisabled}\n onClick={() => onModeChange(\"video\")}\n className={cn(\n LABEL_BASE,\n mode === \"video\" ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n Video\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === \"photo\"}\n disabled={modeDisabled}\n onClick={() => onModeChange(\"photo\")}\n className={cn(\n LABEL_BASE,\n mode === \"photo\" ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n Photo\n </button>\n <button\n type=\"button\"\n aria-label=\"Upload photos or videos from this device\"\n disabled={modeDisabled || uploadDisabled}\n onClick={onUpload}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n Upload\n </button>\n {extraModes.map((extra) => (\n <button\n key={extra.id}\n type=\"button\"\n aria-label={extra.label}\n disabled={modeDisabled}\n onClick={extra.onSelect}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n {extra.label}\n </button>\n ))}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * ZoomRow — the iPhone zoom pills floating over the bottom of the feed:\n * small translucent circles, the active factor in a slightly larger circle\n * with yellow text and an \"×\" suffix. Rendered ONLY when the host reports a\n * real zoom range (track capabilities) — we never fake unsupported zoom.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface ZoomRowProps {\n /** Available zoom factors in ascending order (e.g. [1, 2, 4]). */\n options: number[];\n /** The currently applied factor (nearest option is highlighted). */\n value: number;\n onSelect: (factor: number) => void;\n}\n\nfunction formatFactor(factor: number): string {\n return factor < 1\n ? `.${String(Math.round(factor * 10))}`\n : String(Math.round(factor * 10) / 10);\n}\n\nexport function ZoomRow({ options, value, onSelect }: ZoomRowProps) {\n if (options.length < 2) return null;\n const active = options.reduce((best, opt) =>\n Math.abs(opt - value) < Math.abs(best - value) ? opt : best,\n );\n return (\n <div className=\"flex items-center justify-center gap-3\">\n {options.map((opt) => {\n const isActive = opt === active;\n return (\n <button\n key={opt}\n type=\"button\"\n onClick={() => onSelect(opt)}\n aria-label={`Zoom ${formatFactor(opt)}x`}\n aria-pressed={isActive}\n className={cn(\n \"flex touch-manipulation items-center justify-center rounded-full font-semibold transition-all duration-200\",\n isActive\n ? \"h-10 w-10 bg-black/45 text-[13px] text-[#FFCC00]\"\n : \"h-8 w-8 bg-black/35 text-[12px] text-white\",\n )}\n >\n {formatFactor(opt)}\n {isActive && <span className=\"text-[10px]\">×</span>}\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * OptionsGridPanel — the iPhone two-tap options surface: tapping the grid\n * button dims the viewfinder and reveals a rounded dark panel pinned to the\n * bottom holding a grid of circular toggle buttons with uppercase labels\n * (one tap to reveal, one tap to act). This is NOT a drawer — no drag\n * handle, no route; tapping the scrim or the grid button again closes it.\n *\n * Tiles are injected (`CaptureOptionTile[]`) so hosts and domain extensions\n * add their own toggles without touching the panel.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureOptionTile } from \"../types\";\n\nexport interface OptionsGridPanelProps {\n open: boolean;\n onClose: () => void;\n tiles: CaptureOptionTile[];\n}\n\nexport function OptionsGridPanel({\n open,\n onClose,\n tiles,\n}: OptionsGridPanelProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-40\">\n {/* Dimming scrim — the viewfinder darkens like iOS; tap to dismiss. */}\n <button\n type=\"button\"\n aria-label=\"Close camera options\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/70\"\n />\n <div className=\"absolute inset-x-3 bottom-3 mb-safe rounded-[2.5rem] bg-[#3a3a3c]/95 px-6 py-8 shadow-2xl\">\n <div className=\"grid grid-cols-3 gap-x-4 gap-y-7\">\n {tiles.map((tile) => (\n <div key={tile.id} className=\"flex flex-col items-center gap-2.5\">\n <button\n type=\"button\"\n onClick={tile.onPress}\n disabled={tile.disabled}\n aria-label={tile.label}\n aria-pressed={tile.active === true}\n className={cn(\n \"flex h-16 w-16 touch-manipulation items-center justify-center rounded-full bg-[#2c2c2e] transition-colors\",\n tile.active ? \"text-[#FFCC00]\" : \"text-white\",\n tile.disabled && \"opacity-40\",\n )}\n >\n {tile.icon}\n </button>\n <span className=\"text-[13px] font-semibold uppercase tracking-[0.14em] text-white\">\n {tile.valueLabel ? `${tile.label} ${tile.valueLabel}` : tile.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CaptureSheet — the iOS-style system sheet used over the camera: a light,\n * heavily-rounded card sliding over the lower portion of the screen with a\n * circular ✕ close top-right; content is an icon + bold title + body text\n * with a filled primary action and a tinted secondary one. `variant=\"busy\"`\n * is the transient state (small spinner + label, like the OS \"Connecting…\"\n * sheet). Deliberately light-on-dark regardless of app theme — it mirrors\n * the OS presentation over camera chrome.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { Loader2, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\nexport interface CaptureSheetAction {\n label: string;\n onPress: () => void;\n kind?: \"primary\" | \"secondary\";\n}\n\nexport interface CaptureSheetProps {\n open: boolean;\n onClose: () => void;\n /** Standard content sheet by default; \"busy\" renders spinner + label. */\n variant?: \"content\" | \"busy\";\n icon?: React.ReactNode;\n title?: string;\n body?: React.ReactNode;\n actions?: CaptureSheetAction[];\n /** The busy variant's label (\"Connecting…\"). */\n busyLabel?: string;\n}\n\nexport function CaptureSheet({\n open,\n onClose,\n variant = \"content\",\n icon,\n title,\n body,\n actions = [],\n busyLabel = \"Working…\",\n}: CaptureSheetProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col justify-end\">\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/30\"\n />\n <div className=\"relative mx-2 mb-2 mb-safe rounded-[2rem] bg-[#f2f2f7] px-6 pb-6 pt-5 text-black shadow-2xl\">\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"absolute right-4 top-4 flex h-9 w-9 items-center justify-center rounded-full bg-black/5 text-black/70 transition-colors hover:bg-black/10\"\n >\n <X className=\"h-5 w-5\" strokeWidth={2.5} />\n </button>\n {variant === \"busy\" ? (\n <div className=\"flex min-h-[220px] items-center justify-center gap-2.5\">\n <Loader2 className=\"h-5 w-5 animate-spin text-black/50\" />\n <span className=\"text-[17px] font-medium text-black/80\">\n {busyLabel}\n </span>\n </div>\n ) : (\n <div className=\"pt-4\">\n {icon && <div className=\"mb-5 text-[#0a84ff]\">{icon}</div>}\n {title && (\n <h2 className=\"mb-2 text-[26px] font-bold leading-tight\">\n {title}\n </h2>\n )}\n {body && (\n <div className=\"text-[17px] leading-snug text-black/85\">\n {body}\n </div>\n )}\n {actions.length > 0 && (\n <div className=\"mt-7 flex flex-col gap-3\">\n {actions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={action.onPress}\n className={cn(\n \"h-[50px] w-full touch-manipulation rounded-full text-[17px] font-semibold transition-transform active:scale-[0.98]\",\n (action.kind ?? \"primary\") === \"primary\"\n ? \"bg-[#0a84ff] text-white\"\n : \"bg-black/[0.06] text-[#0a84ff]\",\n )}\n >\n {action.label}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * GridOverlay — the rule-of-thirds composition grid over the viewfinder.\n * Genuinely supported (pure CSS), toggled from the options grid.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function GridOverlay({ visible }: { visible: boolean }) {\n if (!visible) return null;\n return (\n <div aria-hidden className=\"pointer-events-none absolute inset-0 z-10\">\n <div className=\"absolute inset-y-0 left-1/3 w-px bg-white/40\" />\n <div className=\"absolute inset-y-0 left-2/3 w-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-1/3 h-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-2/3 h-px bg-white/40\" />\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CountdownOverlay — the big centered timer digits (iPhone timer capture):\n * one large white numeral per second, scaling in as it changes.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function CountdownOverlay({ seconds }: { seconds: number | null }) {\n if (seconds === null || seconds <= 0) return null;\n return (\n <div className=\"pointer-events-none absolute inset-0 z-30 flex items-center justify-center\">\n <span\n key={seconds}\n className=\"text-[120px] font-light text-white drop-shadow-lg [@starting-style]:scale-125 [@starting-style]:opacity-0 transition-all duration-300\"\n >\n {seconds}\n </span>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * ImageEditSheet — instant in-browser image editing, core to the package\n * (THE CLOUD LAW's sibling: capture without instant edit is half a system).\n * v1 covers the basics that belong in the browser — crop (free + 1:1, 4:3,\n * 16:9 presets), rotate 90°, flip — full-screen dark chrome in the iOS\n * editing style: Cancel/Save top bar, the image letterboxed center, a\n * draggable/resizable crop frame with corner handles, tool row bottom.\n * Heavier AI edits ride host-injected actions, not this sheet.\n *\n * Output is a JPEG re-encode of the ORIGINAL pixels (rotation/flip/crop\n * applied on canvas) — never a screenshot of the preview.\n *\n * Package source (`@ai-matrx/capture`) — browser canvas only, no app deps.\n */\n\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { Check, FlipHorizontal2, RotateCcw, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\ntype AspectPreset = \"free\" | \"1:1\" | \"4:3\" | \"16:9\";\nconst ASPECTS: { id: AspectPreset; label: string; ratio: number | null }[] = [\n { id: \"free\", label: \"Free\", ratio: null },\n { id: \"1:1\", label: \"Square\", ratio: 1 },\n { id: \"4:3\", label: \"4:3\", ratio: 4 / 3 },\n { id: \"16:9\", label: \"16:9\", ratio: 16 / 9 },\n];\n\n/** Normalized crop rect (0..1) relative to the ROTATED/FLIPPED image. */\ninterface CropRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface ImageEditSheetProps {\n open: boolean;\n /** Source image URL (object URL or resolvable src). */\n src: string | null;\n onClose: () => void;\n /** Receives the edited JPEG. The host persists it (cloud port). */\n onSave: (blob: Blob) => void;\n}\n\nconst FULL_CROP: CropRect = { x: 0, y: 0, w: 1, h: 1 };\nconst MIN_CROP = 0.08;\n\nexport function ImageEditSheet({\n open,\n src,\n onClose,\n onSave,\n}: ImageEditSheetProps) {\n const [rotation, setRotation] = useState(0); // 0|90|180|270, CCW steps\n const [flipped, setFlipped] = useState(false);\n const [aspect, setAspect] = useState<AspectPreset>(\"free\");\n const [crop, setCrop] = useState<CropRect>(FULL_CROP);\n const [saving, setSaving] = useState(false);\n const stageRef = useRef<HTMLDivElement | null>(null);\n const imgRef = useRef<HTMLImageElement | null>(null);\n const dragRef = useRef<{\n kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\";\n startX: number;\n startY: number;\n startCrop: CropRect;\n } | null>(null);\n\n useEffect(() => {\n if (open) {\n setRotation(0);\n setFlipped(false);\n setAspect(\"free\");\n setCrop(FULL_CROP);\n setSaving(false);\n }\n }, [open, src]);\n\n const applyAspect = useCallback((preset: AspectPreset) => {\n setAspect(preset);\n const ratio = ASPECTS.find((a) => a.id === preset)?.ratio ?? null;\n if (ratio === null) return;\n // Fit the largest centered rect of the target ratio inside the frame,\n // in DISPLAYED-image normalized space (needs the element's real aspect).\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n const rotated = rotationSwapsAxes();\n const iw = rotated ? img.naturalHeight : img.naturalWidth;\n const ih = rotated ? img.naturalWidth : img.naturalHeight;\n const imageRatio = iw / ih;\n let w = 1;\n let h = 1;\n if (ratio > imageRatio) h = imageRatio / ratio;\n else w = ratio / imageRatio;\n setCrop({ x: (1 - w) / 2, y: (1 - h) / 2, w, h });\n\n function rotationSwapsAxes() {\n return rotation % 180 !== 0;\n }\n }, [rotation]);\n\n const onPointerDown = useCallback(\n (kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\") =>\n (e: React.PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n dragRef.current = {\n kind,\n startX: e.clientX,\n startY: e.clientY,\n startCrop: crop,\n };\n },\n [crop],\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n const drag = dragRef.current;\n const img = imgRef.current;\n if (!drag || !img) return;\n const rect = img.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const dx = (e.clientX - drag.startX) / rect.width;\n const dy = (e.clientY - drag.startY) / rect.height;\n const c = { ...drag.startCrop };\n const ratio = ASPECTS.find((a) => a.id === aspect)?.ratio ?? null;\n const frameRatio = rect.width / rect.height;\n\n if (drag.kind === \"move\") {\n c.x = Math.min(1 - c.w, Math.max(0, c.x + dx));\n c.y = Math.min(1 - c.h, Math.max(0, c.y + dy));\n } else {\n const left = drag.kind === \"nw\" || drag.kind === \"sw\";\n const top = drag.kind === \"nw\" || drag.kind === \"ne\";\n let x2 = c.x + c.w;\n let y2 = c.y + c.h;\n if (left) c.x = Math.min(x2 - MIN_CROP, Math.max(0, c.x + dx));\n else x2 = Math.max(c.x + MIN_CROP, Math.min(1, x2 + dx));\n if (top) c.y = Math.min(y2 - MIN_CROP, Math.max(0, c.y + dy));\n else y2 = Math.max(c.y + MIN_CROP, Math.min(1, y2 + dy));\n c.w = x2 - c.x;\n c.h = y2 - c.y;\n if (ratio !== null) {\n // Lock the ratio by deriving height from width in SCREEN space.\n const targetH = (c.w * frameRatio) / ratio;\n if (top) c.y = y2 - Math.min(targetH, y2);\n c.h = Math.min(targetH, top ? y2 - c.y : 1 - c.y);\n c.w = (c.h * ratio) / frameRatio;\n if (left) c.x = x2 - c.w;\n }\n }\n setCrop(c);\n },\n [aspect],\n );\n\n const onPointerUp = useCallback(() => {\n dragRef.current = null;\n }, []);\n\n const save = useCallback(() => {\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n setSaving(true);\n try {\n const swap = rotation % 180 !== 0;\n const outW = Math.round((swap ? img.naturalHeight : img.naturalWidth) * crop.w);\n const outH = Math.round((swap ? img.naturalWidth : img.naturalHeight) * crop.h);\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, outW);\n canvas.height = Math.max(1, outH);\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"no 2d context\");\n // Move into crop space, then apply rotation/flip about the image center.\n const rw = swap ? img.naturalHeight : img.naturalWidth;\n const rh = swap ? img.naturalWidth : img.naturalHeight;\n ctx.translate(-crop.x * rw, -crop.y * rh);\n ctx.translate(rw / 2, rh / 2);\n ctx.rotate((rotation * Math.PI) / 180);\n if (flipped) ctx.scale(-1, 1);\n ctx.drawImage(img, -img.naturalWidth / 2, -img.naturalHeight / 2);\n canvas.toBlob(\n (blob) => {\n setSaving(false);\n if (blob) {\n onSave(blob);\n onClose();\n }\n },\n \"image/jpeg\",\n 0.92,\n );\n } catch (err) {\n console.error(\"[capture-camera] edit save failed\", err);\n setSaving(false);\n }\n }, [rotation, flipped, crop, onSave, onClose]);\n\n if (!open || !src) return null;\n\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col bg-black\">\n {/* Top bar */}\n <div className=\"flex shrink-0 items-center justify-between px-4 pt-safe\">\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Cancel editing\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-medium text-white\"\n >\n <X className=\"h-5 w-5\" />\n Cancel\n </button>\n <span className=\"text-[15px] font-semibold text-white/90\">Edit</span>\n <button\n type=\"button\"\n onClick={save}\n disabled={saving}\n aria-label=\"Save edited image\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-semibold text-[#FFCC00] disabled:opacity-50\"\n >\n <Check className=\"h-5 w-5\" />\n Save\n </button>\n </div>\n\n {/* Stage */}\n <div\n ref={stageRef}\n className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-hidden p-4\"\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"relative max-h-full max-w-full\">\n {/* eslint-disable-next-line @next/next/no-img-element -- local object URL being edited */}\n <img\n ref={imgRef}\n src={src}\n alt=\"Image being edited\"\n draggable={false}\n className=\"max-h-[62dvh] max-w-full select-none object-contain\"\n style={{\n transform: `rotate(${rotation}deg) ${flipped ? \"scaleX(-1)\" : \"\"}`,\n }}\n />\n {/* Crop frame in displayed-image space */}\n <div\n role=\"presentation\"\n onPointerDown={onPointerDown(\"move\")}\n className=\"absolute cursor-move touch-none border-2 border-white shadow-[0_0_0_9999px_rgba(0,0,0,0.55)]\"\n style={{\n left: `${crop.x * 100}%`,\n top: `${crop.y * 100}%`,\n width: `${crop.w * 100}%`,\n height: `${crop.h * 100}%`,\n }}\n >\n {([\"nw\", \"ne\", \"sw\", \"se\"] as const).map((corner) => (\n <span\n key={corner}\n role=\"presentation\"\n onPointerDown={onPointerDown(corner)}\n className={cn(\n \"absolute h-6 w-6 touch-none\",\n corner === \"nw\" &&\n \"-left-1.5 -top-1.5 border-l-4 border-t-4 cursor-nwse-resize\",\n corner === \"ne\" &&\n \"-right-1.5 -top-1.5 border-r-4 border-t-4 cursor-nesw-resize\",\n corner === \"sw\" &&\n \"-bottom-1.5 -left-1.5 border-b-4 border-l-4 cursor-nesw-resize\",\n corner === \"se\" &&\n \"-bottom-1.5 -right-1.5 border-b-4 border-r-4 cursor-nwse-resize\",\n \"border-white\",\n )}\n />\n ))}\n </div>\n </div>\n </div>\n\n {/* Tool row */}\n <div className=\"shrink-0 pb-safe\">\n <div className=\"flex items-center justify-center gap-2 pb-2\">\n {ASPECTS.map((a) => (\n <button\n key={a.id}\n type=\"button\"\n onClick={() => applyAspect(a.id)}\n aria-pressed={aspect === a.id}\n className={cn(\n \"touch-manipulation rounded-full px-3.5 py-1.5 text-[12px] font-semibold uppercase tracking-wide transition-colors\",\n aspect === a.id\n ? \"bg-white/20 text-[#FFCC00]\"\n : \"text-white/80\",\n )}\n >\n {a.label}\n </button>\n ))}\n </div>\n <div className=\"flex items-center justify-center gap-6 pb-4\">\n <button\n type=\"button\"\n onClick={() => {\n setRotation((r) => (r + 270) % 360);\n setCrop(FULL_CROP);\n setAspect(\"free\");\n }}\n aria-label=\"Rotate left\"\n className=\"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10 text-white\"\n >\n <RotateCcw className=\"h-5 w-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setFlipped((f) => !f)}\n aria-label=\"Flip horizontally\"\n aria-pressed={flipped}\n className={cn(\n \"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10\",\n flipped ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n <FlipHorizontal2 className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useCallback","useEffect","useState","X","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","jsx","jsxs","useState","useCallback","useEffect","X","useCallback","useEffect","useRef","useState","X","jsx","jsxs","useState","useRef","useEffect","useCallback","X"]}
1
+ {"version":3,"sources":["../src/components/CameraCapture.tsx","../src/cn.ts","../src/safe-area.ts","../src/hooks/useTrackControls.ts","../src/components/ShutterButton.tsx","../src/components/ModeSelector.tsx","../src/components/ZoomRow.tsx","../src/components/OptionsGridPanel.tsx","../src/components/CaptureSheet.tsx","../src/components/GridOverlay.tsx","../src/components/CountdownOverlay.tsx","../src/media/media-cache.ts","../src/components/CaptureFilmstrip.tsx","../src/components/MediaViewer.tsx","../src/components/ImageEditSheet.tsx","../src/components/CameraFeed.tsx","../src/engine/useDefaultEngine.ts"],"sourcesContent":["\"use client\";\n\n/**\n * CameraCapture — the opinionated iPhone-style camera surface, assembled.\n *\n * Layout (matching the iOS Camera app):\n * - Full-bleed live feed with a semi-transparent near-black bar across the\n * top and bottom — controls read clearly while the feed shows through.\n * - Top bar: close (host-provided), center slot, torch, host extras, and the\n * grid button revealing the two-tap OptionsGridPanel.\n * - Over the feed's bottom edge: real zoom pills (only when the track\n * reports a zoom range).\n * - Bottom bar: injected rows → VIDEO·PHOTO·UPLOAD mode selector → recents\n * thumb · shutter · flip.\n * - Overlays: rule-of-thirds grid, timer countdown, recording chip, blocked\n * sheet (iOS system-sheet presentation).\n *\n * The chrome owns UI state only (options panel, grid, timer, countdown).\n * Capture behavior, streams and persistence come from the injected\n * `CaptureCameraEngine`; domain features attach through `CaptureCameraSlots`.\n *\n * Package source (`@ai-matrx/capture`).\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n Grip,\n Grid3x3,\n Proportions,\n SunMedium,\n Timer,\n X,\n Zap,\n ZapOff,\n RefreshCw,\n} from \"lucide-react\";\nimport { cn } from \"../cn\";\nimport { safeTop, safeBottom, safeMarginTop } from \"../safe-area\";\n\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCloudPort,\n CaptureOptionTile,\n CaptureTimerSetting,\n} from \"../types\";\nimport { useTrackControls } from \"../hooks/useTrackControls\";\nimport { ShutterButton } from \"./ShutterButton\";\nimport { ModeSelector } from \"./ModeSelector\";\nimport { ZoomRow } from \"./ZoomRow\";\nimport { OptionsGridPanel } from \"./OptionsGridPanel\";\nimport { CaptureSheet, type CaptureSheetAction } from \"./CaptureSheet\";\nimport { GridOverlay } from \"./GridOverlay\";\nimport { CountdownOverlay } from \"./CountdownOverlay\";\nimport { CaptureFilmstrip } from \"./CaptureFilmstrip\";\nimport { MediaViewer } from \"./MediaViewer\";\nimport { ImageEditSheet } from \"./ImageEditSheet\";\nimport {\n getMediaUrl,\n invalidateMedia,\n type CaptureMediaItem,\n} from \"../media/media-cache\";\n\n/**\n * The session's captured media, handed to the chrome so the WHOLE review\n * loop — filmstrip → swipe viewer → edit → replace — runs inside the\n * package with its tuned gestures, preload and caching. The host only\n * implements the item CRUD.\n */\nexport interface CaptureMediaSession {\n items: CaptureMediaItem[];\n /** Remove one item (viewer trash button). */\n onDelete?: (key: string) => void;\n /** Replace semantics for the built-in editor: persist the edited JPEG in\n * place of the source item. Edit means edit — after saving, the original\n * must be gone. Omit to hide the edit affordance. */\n onReplacePhoto?: (key: string, blob: Blob) => void;\n}\n\nexport interface CameraCaptureProps {\n engine: CaptureCameraEngine;\n /** THE CLOUD LAW: the cloud port is REQUIRED — a camera with no cloud\n * library/persistence is not a valid instance of this system. */\n cloud: CaptureCloudPort;\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n /** The live preview element (`<CameraFeed engine={engine}/>` for the\n * default engine; hosts with their own runtime wire their own). */\n preview: React.ReactNode;\n /** Session media — enables the package-owned filmstrip + viewer + editor. */\n media?: CaptureMediaSession;\n /** Fires when the package review loop (viewer or editor) opens/closes over\n * the live feed — hosts pause things that watch the feed (QR scanning). */\n onReviewOpenChange?: (open: boolean) => void;\n /** Close affordance top-left; omitted = no close button. */\n onClose?: () => void;\n /** Body + actions for the camera-blocked iOS-style sheet. */\n blockedSheet?: { body: React.ReactNode; actions: CaptureSheetAction[] };\n /** Hide all chrome except honesty chips (host renders its own toggle). */\n controlsHidden?: boolean;\n shutterDisabled?: boolean;\n slots?: CaptureCameraSlots;\n}\n\nfunction formatElapsed(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = String(totalSeconds % 60).padStart(2, \"0\");\n return `${m}:${s}`;\n}\n\nconst ASPECT_CYCLE: CaptureAspect[] = [\"full\", \"4:3\", \"1:1\", \"16:9\"];\n\nexport function CameraCapture({\n engine,\n cloud,\n mode,\n onModeChange,\n preview,\n media,\n onReviewOpenChange,\n onClose,\n blockedSheet,\n controlsHidden = false,\n shutterDisabled = false,\n slots = {},\n}: CameraCaptureProps) {\n const [optionsOpen, setOptionsOpen] = useState(false);\n // The package-owned review loop: which item the viewer is open on, and\n // which photo the editor is replacing.\n const [viewerKey, setViewerKey] = useState<string | null>(null);\n const [editTarget, setEditTarget] = useState<{\n key: string;\n url: string;\n } | null>(null);\n const [gridOn, setGridOn] = useState(false);\n const [timerSetting, setTimerSetting] = useState<CaptureTimerSetting>(0);\n const [aspect, setAspect] = useState<CaptureAspect>(\"full\");\n const [exposureOpen, setExposureOpen] = useState(false);\n const [countdown, setCountdown] = useState<number | null>(null);\n // The blocked sheet is dismissible — uploads and the system camera keep\n // working, so closing it reveals the chrome rather than leaving the page.\n const [blockedDismissed, setBlockedDismissed] = useState(false);\n const countdownRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const controls = useTrackControls(engine.stream);\n\n const reviewOpen = viewerKey !== null || editTarget !== null;\n useEffect(() => {\n onReviewOpenChange?.(reviewOpen);\n }, [reviewOpen, onReviewOpenChange]);\n\n const clearCountdown = useCallback(() => {\n if (countdownRef.current) clearInterval(countdownRef.current);\n countdownRef.current = null;\n setCountdown(null);\n }, []);\n useEffect(() => clearCountdown, [clearCountdown]);\n // A photo countdown must not survive a mode switch — it would fire a\n // photo capture while in video mode (possibly mid-recording).\n useEffect(() => {\n if (mode !== \"photo\") clearCountdown();\n }, [mode, clearCountdown]);\n\n const onShutter = useCallback(() => {\n if (mode === \"video\") {\n if (engine.recording) engine.onStopRecording();\n else engine.onStartRecording();\n return;\n }\n if (countdown !== null) {\n // Tapping mid-countdown cancels — matching iOS.\n clearCountdown();\n return;\n }\n if (timerSetting === 0) {\n engine.onCapturePhoto({ aspect });\n return;\n }\n let remaining = timerSetting;\n setCountdown(remaining);\n countdownRef.current = setInterval(() => {\n remaining -= 1;\n if (remaining <= 0) {\n clearCountdown();\n engine.onCapturePhoto({ aspect });\n } else {\n setCountdown(remaining);\n }\n }, 1000);\n }, [mode, engine, countdown, timerSetting, aspect, clearCountdown]);\n\n const cycleTimer = useCallback(() => {\n setTimerSetting((t) => (t === 0 ? 3 : t === 3 ? 10 : 0));\n }, []);\n\n const coreTiles: CaptureOptionTile[] = [\n ...(controls.torchSupported\n ? [\n {\n id: \"flash\",\n label: \"Flash\",\n icon: controls.torchOn ? (\n <Zap className=\"h-6 w-6\" fill=\"currentColor\" />\n ) : (\n <ZapOff className=\"h-6 w-6\" />\n ),\n active: controls.torchOn,\n onPress: controls.toggleTorch,\n },\n ]\n : []),\n {\n id: \"timer\",\n label: \"Timer\",\n icon: <Timer className=\"h-6 w-6\" />,\n active: timerSetting !== 0,\n valueLabel: timerSetting === 0 ? undefined : `${timerSetting}s`,\n onPress: cycleTimer,\n },\n {\n id: \"grid\",\n label: \"Grid\",\n icon: <Grid3x3 className=\"h-6 w-6\" />,\n active: gridOn,\n onPress: () => setGridOn((g) => !g),\n },\n // Aspect applies to PHOTO output (center-cropped from the full sensor).\n {\n id: \"aspect\",\n label: \"Aspect\",\n icon: <Proportions className=\"h-6 w-6\" />,\n active: aspect !== \"full\",\n valueLabel: aspect === \"full\" ? undefined : aspect,\n onPress: () =>\n setAspect(\n (a) =>\n ASPECT_CYCLE[\n (ASPECT_CYCLE.indexOf(a) + 1) % ASPECT_CYCLE.length\n ] ?? \"full\",\n ),\n },\n ...(controls.exposureSupported\n ? [\n {\n id: \"exposure\",\n label: \"Exposure\",\n icon: <SunMedium className=\"h-6 w-6\" />,\n active: controls.exposure !== 0 || exposureOpen,\n valueLabel:\n controls.exposure === 0\n ? undefined\n : `${controls.exposure > 0 ? \"+\" : \"\"}${controls.exposure}`,\n onPress: () => setExposureOpen((o) => !o),\n },\n ]\n : []),\n ];\n const tiles = [...coreTiles, ...(slots.optionTiles ?? [])];\n\n const blocked = engine.blocked !== null;\n\n return (\n <div className=\"absolute inset-0 select-none overflow-hidden bg-black\">\n {/* Full-bleed feed */}\n <div className=\"absolute inset-0\">{preview}</div>\n <GridOverlay visible={gridOn && !blocked} />\n {/* Aspect framing hint — the photo output is center-cropped to the\n selected ratio; the dimmed bands approximate the discarded region\n of the VISIBLE frame (the capture itself crops the full sensor). */}\n {mode === \"photo\" && aspect !== \"full\" && !blocked && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center\"\n >\n <div\n className=\"shadow-[0_0_0_9999px_rgba(0,0,0,0.45)]\"\n style={{\n aspectRatio:\n aspect === \"1:1\" ? \"1 / 1\" : aspect === \"4:3\" ? \"3 / 4\" : \"9 / 16\",\n width: aspect === \"16:9\" ? \"100%\" : undefined,\n height: aspect === \"16:9\" ? undefined : \"70%\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n }}\n />\n </div>\n )}\n <CountdownOverlay seconds={countdown} />\n\n {/* Top bar — ONE compact row; every pixel here is stolen from the\n feed, and a phone browser already spends chrome above us. */}\n {!controlsHidden && (\n <div\n className=\"absolute inset-x-0 top-0 z-20 bg-black/65 backdrop-blur-[2px]\"\n style={safeTop}\n >\n <div className=\"flex h-11 items-center gap-0.5 px-1.5\">\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close camera\"\n className=\"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"w-10 shrink-0\" />\n )}\n <div className=\"min-w-0 flex-1\">{slots.topBarCenter}</div>\n {controls.torchSupported && (\n <button\n type=\"button\"\n onClick={controls.toggleTorch}\n aria-label={\n controls.torchOn ? \"Turn flash off\" : \"Turn flash on\"\n }\n aria-pressed={controls.torchOn}\n className={cn(\n \"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n controls.torchOn\n ? \"text-[#FFCC00]\"\n : \"text-white hover:bg-white/10\",\n )}\n >\n <Zap\n className=\"h-5 w-5\"\n fill={controls.torchOn ? \"currentColor\" : \"none\"}\n />\n </button>\n )}\n {slots.topBarTrailing}\n <button\n type=\"button\"\n onClick={() => setOptionsOpen((o) => !o)}\n aria-label=\"More camera options\"\n aria-expanded={optionsOpen}\n className={cn(\n \"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n optionsOpen ? \"bg-white/20 text-white\" : \"text-white hover:bg-white/10\",\n )}\n >\n <Grip className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Honesty chips — visible even with controls hidden. */}\n <div\n className=\"pointer-events-none absolute inset-x-0 top-[50px] z-20 flex flex-col items-center gap-1.5\"\n style={safeMarginTop}\n >\n {engine.recording && (\n <span className=\"flex items-center gap-2 rounded-full bg-black/60 px-3 py-1.5 text-sm font-medium text-white\">\n <span className=\"h-2.5 w-2.5 animate-pulse rounded-full bg-[#FF3B30]\" />\n {formatElapsed(engine.recordElapsedSeconds)}\n </span>\n )}\n {slots.statusChips}\n </div>\n\n {/* Bottom stack */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 bottom-0 z-20\">\n {/* Zoom pills float on the FEED, just above the bottom bar. */}\n {!blocked && controls.zoomOptions.length >= 2 && (\n <div className=\"mb-2\">\n <ZoomRow\n options={controls.zoomOptions}\n value={controls.zoom}\n onSelect={controls.setZoom}\n />\n </div>\n )}\n {/* The package filmstrip + host content OVER the feed — zero bar\n height. Tapping a tile opens the package viewer. */}\n {media && media.items.length > 0 && (\n <div className=\"mb-1.5 px-2\">\n <CaptureFilmstrip\n items={media.items}\n onOpen={(item) => setViewerKey(item.key)}\n />\n </div>\n )}\n {slots.aboveBar && <div className=\"mb-1.5 px-2\">{slots.aboveBar}</div>}\n {/* Exposure slider — revealed by the EXPOSURE tile, floats above\n the bottom bar like the iOS exposure control. */}\n {exposureOpen && controls.exposureSupported && controls.exposureRange && (\n <div className=\"mx-auto mb-3 flex w-64 items-center gap-3 rounded-full bg-black/55 px-4 py-2\">\n <SunMedium className=\"h-4 w-4 shrink-0 text-[#FFCC00]\" />\n <input\n type=\"range\"\n min={controls.exposureRange.min}\n max={controls.exposureRange.max}\n step={controls.exposureRange.step}\n value={controls.exposure}\n onChange={(e) => controls.setExposure(Number(e.target.value))}\n aria-label=\"Exposure compensation\"\n className=\"w-full accent-[#FFCC00]\"\n />\n <span className=\"w-8 shrink-0 text-right text-xs tabular-nums text-white\">\n {controls.exposure > 0 ? \"+\" : \"\"}\n {controls.exposure}\n </span>\n </div>\n )}\n <div\n className=\"bg-black/65 px-3 backdrop-blur-[2px]\"\n style={safeBottom}\n >\n {slots.aboveModeSelector}\n {/* Connected mode bar + the domain action share ONE row as flex\n siblings — absolute positioning collided with the labels on\n narrow phones, twice. Never reintroduce it. */}\n <div className=\"flex items-center justify-center gap-2 py-1.5\">\n <ModeSelector\n mode={mode}\n onModeChange={onModeChange}\n onUpload={engine.onUpload}\n modeDisabled={engine.recording}\n uploadDisabled={shutterDisabled && !blocked}\n extraModes={slots.extraModes}\n />\n {slots.modeRowTrailing && (\n <div className=\"shrink-0\">{slots.modeRowTrailing}</div>\n )}\n </div>\n <div className=\"flex items-center justify-between px-2 pb-1.5 pt-0.5\">\n <div className=\"flex w-14 justify-start\">\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Open your media library\"\n className=\"h-11 w-11 touch-manipulation overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/25 transition-transform active:scale-95\"\n >\n {cloud.recentsThumb ?? (\n <span className=\"block h-full w-full bg-white/5\" />\n )}\n </button>\n </div>\n <ShutterButton\n mode={mode}\n recording={engine.recording}\n disabled={shutterDisabled || blocked}\n onPress={onShutter}\n />\n <div className=\"flex w-14 justify-end\">\n {engine.onFlipCamera ? (\n <button\n type=\"button\"\n onClick={engine.onFlipCamera}\n aria-label=\"Switch camera\"\n className=\"flex h-11 w-11 touch-manipulation items-center justify-center rounded-full bg-white/15 text-white transition-transform active:rotate-180 active:scale-95 duration-300\"\n >\n <RefreshCw className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"h-11 w-11\" />\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n\n <OptionsGridPanel\n open={optionsOpen && !controlsHidden}\n onClose={() => setOptionsOpen(false)}\n tiles={tiles}\n />\n\n {blocked && blockedSheet && !blockedDismissed && (\n <CaptureSheet\n open\n onClose={() => setBlockedDismissed(true)}\n body={blockedSheet.body}\n title=\"Camera unavailable\"\n actions={blockedSheet.actions}\n />\n )}\n\n {/* Package review loop: viewer over the camera, editor over the\n viewer. Replace semantics — saving an edit persists the new frame,\n removes the source item and drops its cached URL, then closes back\n to the camera. */}\n {media && viewerKey !== null && (\n <MediaViewer\n items={media.items}\n initialKey={viewerKey}\n keysDisabled={editTarget !== null}\n onClose={() => setViewerKey(null)}\n onDelete={\n media.onDelete\n ? (item) => {\n media.onDelete?.(item.key);\n invalidateMedia(item.key);\n }\n : undefined\n }\n onEdit={\n media.onReplacePhoto\n ? (item) => {\n const url = getMediaUrl(item);\n if (url) setEditTarget({ key: item.key, url });\n }\n : undefined\n }\n />\n )}\n {media?.onReplacePhoto && (\n <ImageEditSheet\n open={editTarget !== null}\n src={editTarget?.url ?? null}\n onClose={() => setEditTarget(null)}\n onSave={(blob) => {\n const target = editTarget;\n setEditTarget(null);\n setViewerKey(null);\n if (target) {\n media.onReplacePhoto?.(target.key, blob);\n invalidateMedia(target.key);\n }\n }}\n />\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","/**\n * The package-local `cn` — the documented substitution point for the app's\n * `@/lib/utils` cn during extraction (same pattern as `@ai-matrx/media`).\n */\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(\n ...inputs: Array<string | false | null | undefined>\n): string {\n return twMerge(inputs.filter(Boolean).join(\" \"));\n}\n","/**\n * Safe-area insets as INLINE STYLES — deliberately not utility classes.\n *\n * v0.1.x used the host app's `pt-safe` / `pb-safe` / `mb-safe` Tailwind\n * utilities, which exist only in AI Matrx's globals.css: in any other host\n * (the Vite drop-in test) they silently resolved to nothing and the chrome\n * collided with the notch and home indicator. Inline `env()` styles work in\n * every host with zero setup — the package-completeness rule: if it breaks\n * when the host forgets something, the package must not let the host forget.\n */\n\nimport type { CSSProperties } from \"react\";\n\nexport const safeTop: CSSProperties = {\n paddingTop: \"env(safe-area-inset-top, 0px)\",\n};\n\nexport const safeBottom: CSSProperties = {\n paddingBottom: \"env(safe-area-inset-bottom, 0px)\",\n};\n\nexport const safeMarginTop: CSSProperties = {\n marginTop: \"env(safe-area-inset-top, 0px)\",\n};\n\nexport const safeMarginBottom: CSSProperties = {\n marginBottom: \"env(safe-area-inset-bottom, 0px)\",\n};\n","\"use client\";\n\n/**\n * useTrackControls — HONEST torch + zoom over a live video track's real\n * capabilities (`MediaTrackCapabilities`). If the hardware doesn't report\n * torch or a zoom range, the corresponding control simply doesn't exist —\n * we never render a fake toggle.\n *\n * Zoom pill options are derived from the reported [min, max] range using the\n * familiar phone ladder (.5 / 1 / 2 / 4 / 8) clipped to what the device can\n * actually do. Applied via `track.applyConstraints({ advanced: [...] })`.\n *\n * Package source (`@ai-matrx/capture`) — browser media APIs only, no app\n * imports.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\n\n// torch/zoom are spec'd in mediacapture-image but absent from lib.dom.\ninterface ExtendedCapabilities extends MediaTrackCapabilities {\n torch?: boolean;\n zoom?: { min: number; max: number; step?: number };\n exposureCompensation?: { min: number; max: number; step?: number };\n}\ninterface ExtendedSettings extends MediaTrackSettings {\n torch?: boolean;\n zoom?: number;\n exposureCompensation?: number;\n}\n\nconst ZOOM_LADDER = [0.5, 1, 2, 4, 8];\n\nexport interface TrackControls {\n torchSupported: boolean;\n torchOn: boolean;\n toggleTorch: () => void;\n /** ≥2 entries when the track reports a usable zoom range, else []. */\n zoomOptions: number[];\n zoom: number;\n setZoom: (factor: number) => void;\n /** Exposure compensation — only when the hardware reports a range. */\n exposureSupported: boolean;\n exposure: number;\n exposureRange: { min: number; max: number; step: number } | null;\n setExposure: (value: number) => void;\n}\n\nexport function useTrackControls(stream: MediaStream | null): TrackControls {\n const [torchOn, setTorchOn] = useState(false);\n const [zoom, setZoomState] = useState(1);\n const [exposure, setExposureState] = useState(0);\n // Capabilities can be empty until the track is fully live; re-read once on\n // stream change and again on a short delayed tick (iOS reports late).\n const [caps, setCaps] = useState<ExtendedCapabilities | null>(null);\n\n const track = stream?.getVideoTracks()[0] ?? null;\n\n useEffect(() => {\n setTorchOn(false);\n if (!track || typeof track.getCapabilities !== \"function\") {\n setCaps(null);\n return;\n }\n // iOS can report an EMPTY capability set for seconds after the track\n // goes live — a single delayed re-read missed it and the zoom pills /\n // torch never appeared. Poll on a widening ladder and stop as soon as\n // a usable capability shows up (or the ladder runs out).\n const timers: number[] = [];\n const read = (): boolean => {\n try {\n const next = track.getCapabilities() as ExtendedCapabilities;\n setCaps(next);\n const settings = track.getSettings() as ExtendedSettings;\n if (typeof settings.zoom === \"number\") setZoomState(settings.zoom);\n if (typeof settings.exposureCompensation === \"number\")\n setExposureState(settings.exposureCompensation);\n return (\n next.torch === true ||\n next.zoom !== undefined ||\n next.exposureCompensation !== undefined\n );\n } catch {\n setCaps(null);\n return false;\n }\n };\n if (!read()) {\n for (const delay of [500, 1200, 2500, 5000]) {\n timers.push(\n window.setTimeout(() => {\n if (read()) timers.forEach((t) => window.clearTimeout(t));\n }, delay),\n );\n }\n }\n return () => timers.forEach((t) => window.clearTimeout(t));\n }, [track]);\n\n const torchSupported = caps?.torch === true;\n\n const toggleTorch = useCallback(() => {\n if (!track || !torchSupported) return;\n const next = !torchOn;\n track\n .applyConstraints({ advanced: [{ torch: next } as MediaTrackConstraintSet] })\n .then(() => setTorchOn(next))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] torch toggle failed\", err);\n });\n }, [track, torchSupported, torchOn]);\n\n const zoomOptions = useMemo(() => {\n const range = caps?.zoom;\n if (!range || !(range.max > range.min)) return [];\n const options = ZOOM_LADDER.filter(\n (f) => f >= range.min && f <= range.max,\n );\n if (!options.includes(1) && 1 >= range.min && 1 <= range.max) {\n options.push(1);\n options.sort((a, b) => a - b);\n }\n return options.length >= 2 ? options : [];\n }, [caps]);\n\n const setZoom = useCallback(\n (factor: number) => {\n if (!track || !caps?.zoom) return;\n const clamped = Math.min(caps.zoom.max, Math.max(caps.zoom.min, factor));\n track\n .applyConstraints({\n advanced: [{ zoom: clamped } as MediaTrackConstraintSet],\n })\n .then(() => setZoomState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] zoom failed\", err);\n });\n },\n [track, caps],\n );\n\n const exposureCaps = caps?.exposureCompensation;\n const exposureRange =\n exposureCaps && exposureCaps.max > exposureCaps.min\n ? {\n min: exposureCaps.min,\n max: exposureCaps.max,\n step: exposureCaps.step && exposureCaps.step > 0 ? exposureCaps.step : 0.5,\n }\n : null;\n\n const setExposure = useCallback(\n (value: number) => {\n if (!track || !exposureRange) return;\n const clamped = Math.min(\n exposureRange.max,\n Math.max(exposureRange.min, value),\n );\n track\n .applyConstraints({\n advanced: [\n { exposureCompensation: clamped } as MediaTrackConstraintSet,\n ],\n })\n .then(() => setExposureState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] exposure failed\", err);\n });\n },\n [track, exposureRange],\n );\n\n return {\n torchSupported,\n torchOn,\n toggleTorch,\n zoomOptions,\n zoom,\n setZoom,\n exposureSupported: exposureRange !== null,\n exposure,\n exposureRange,\n setExposure,\n };\n}\n","\"use client\";\n\n/**\n * ShutterButton — the iPhone shutter: a thin white ring with a filled inner\n * circle. Photo = white fill; video idle = red circle; video recording = the\n * inner shape morphs to a small red rounded square (the iOS stop affordance).\n * Press feedback is a scale-down of the INNER fill only, like iOS.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureCameraMode } from \"../types\";\n\nexport interface ShutterButtonProps {\n mode: CaptureCameraMode;\n recording: boolean;\n disabled?: boolean;\n onPress: () => void;\n}\n\nexport function ShutterButton({\n mode,\n recording,\n disabled = false,\n onPress,\n}: ShutterButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onPress}\n disabled={disabled}\n aria-label={\n mode === \"photo\"\n ? \"Take photo\"\n : recording\n ? \"Stop recording\"\n : \"Start recording\"\n }\n className={cn(\n \"group flex h-[74px] w-[74px] shrink-0 items-center justify-center rounded-full\",\n \"border-[3.5px] border-white transition-opacity\",\n disabled && \"opacity-30\",\n )}\n >\n <span\n className={cn(\n \"block transition-all duration-200 ease-out group-active:scale-90\",\n mode === \"photo\"\n ? \"h-[62px] w-[62px] rounded-full bg-white\"\n : recording\n ? \"h-8 w-8 rounded-md bg-[#FF3B30]\"\n : \"h-[62px] w-[62px] rounded-full bg-[#FF3B30]\",\n )}\n />\n </button>\n );\n}\n","\"use client\";\n\n/**\n * ModeSelector — the CONNECTED capture-mode bar: one compact rounded track\n * holding VIDEO · PHOTO · UPLOAD (+ injected extras) with a spring-sliding\n * thumb under the active mode (the proven CaptureModeBar interaction, in\n * iPhone dress: uppercase letter-spaced labels, active in iOS camera\n * yellow). Deliberately narrow — the row keeps usable space on BOTH sides\n * for host affordances; screen height is the scarcest resource on a phone\n * browser, so the bar is one short row, never a spread of pills.\n *\n * VIDEO and PHOTO are persistent modes; UPLOAD and extras are immediate\n * actions and never take the thumb.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\n\nimport type { CaptureCameraMode } from \"../types\";\n\n/** Spring curve with a small overshoot (CSS linear() approximation);\n * class-level overshooting cubic-bezier is the fallback. */\nconst SPRING_EASING =\n \"linear(0, 0.0047 0.71%, 0.0189 1.44%, 0.0755 2.93%, 0.1692 4.49%, \" +\n \"0.3921 7.55%, 0.8121 12.94%, 0.9804 15.49%, 1.0946 18.14%, 1.1423 20.16%, \" +\n \"1.1568 21.62%, 1.1541 23.03%, 1.1113 26.4%, 1.0322 31.83%, 0.9902 36.25%, \" +\n \"0.9769 40.24%, 0.9844 45.87%, 1.0028 55.35%, 1.0075 63.42%, 1.0006 85.48%, 1)\";\n\nconst LABEL_BASE =\n \"relative z-10 flex h-8 min-w-0 touch-manipulation items-center justify-center rounded-full px-3 \" +\n \"text-[11px] font-semibold uppercase tracking-[0.12em] transition-colors duration-200 \" +\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 disabled:opacity-40\";\n\nexport interface ModeSelectorProps {\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n onUpload: () => void;\n /** Locks mode switching (while recording). */\n modeDisabled?: boolean;\n uploadDisabled?: boolean;\n /** Host-injected extra entries after UPLOAD (e.g. SCAN) — immediate\n * actions, never the active mode. */\n extraModes?: { id: string; label: string; onSelect: () => void }[];\n}\n\nexport function ModeSelector({\n mode,\n onModeChange,\n onUpload,\n modeDisabled = false,\n uploadDisabled = false,\n extraModes = [],\n}: ModeSelectorProps) {\n const segments = 3 + extraModes.length;\n const activeIndex = mode === \"video\" ? 0 : 1;\n\n return (\n <div\n role=\"tablist\"\n aria-label=\"Capture mode\"\n className=\"relative inline-grid rounded-full bg-white/10 p-1\"\n style={{ gridTemplateColumns: `repeat(${segments}, minmax(0, 1fr))` }}\n >\n {/* Sliding thumb — transform-only, springs with overshoot, instant\n under prefers-reduced-motion. */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute bottom-1 top-1 left-1 rounded-full bg-white/20 transition-transform duration-500 ease-[cubic-bezier(0.34,1.56,0.64,1)] will-change-transform motion-reduce:transition-none\"\n style={{\n width: `calc((100% - 0.5rem) / ${segments})`,\n transform: `translateX(${activeIndex * 100}%)`,\n transitionTimingFunction: SPRING_EASING,\n }}\n />\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === \"video\"}\n disabled={modeDisabled}\n onClick={() => onModeChange(\"video\")}\n className={cn(\n LABEL_BASE,\n mode === \"video\" ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n Video\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === \"photo\"}\n disabled={modeDisabled}\n onClick={() => onModeChange(\"photo\")}\n className={cn(\n LABEL_BASE,\n mode === \"photo\" ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n Photo\n </button>\n <button\n type=\"button\"\n aria-label=\"Upload photos or videos from this device\"\n disabled={modeDisabled || uploadDisabled}\n onClick={onUpload}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n Upload\n </button>\n {extraModes.map((extra) => (\n <button\n key={extra.id}\n type=\"button\"\n aria-label={extra.label}\n disabled={modeDisabled}\n onClick={extra.onSelect}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n {extra.label}\n </button>\n ))}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * ZoomRow — the iPhone zoom pills floating over the bottom of the feed:\n * small translucent circles, the active factor in a slightly larger circle\n * with yellow text and an \"×\" suffix. Rendered ONLY when the host reports a\n * real zoom range (track capabilities) — we never fake unsupported zoom.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface ZoomRowProps {\n /** Available zoom factors in ascending order (e.g. [1, 2, 4]). */\n options: number[];\n /** The currently applied factor (nearest option is highlighted). */\n value: number;\n onSelect: (factor: number) => void;\n}\n\nfunction formatFactor(factor: number): string {\n return factor < 1\n ? `.${String(Math.round(factor * 10))}`\n : String(Math.round(factor * 10) / 10);\n}\n\nexport function ZoomRow({ options, value, onSelect }: ZoomRowProps) {\n if (options.length < 2) return null;\n const active = options.reduce((best, opt) =>\n Math.abs(opt - value) < Math.abs(best - value) ? opt : best,\n );\n return (\n <div className=\"flex items-center justify-center gap-3\">\n {options.map((opt) => {\n const isActive = opt === active;\n return (\n <button\n key={opt}\n type=\"button\"\n onClick={() => onSelect(opt)}\n aria-label={`Zoom ${formatFactor(opt)}x`}\n aria-pressed={isActive}\n className={cn(\n \"flex touch-manipulation items-center justify-center rounded-full font-semibold transition-all duration-200\",\n isActive\n ? \"h-10 w-10 bg-black/45 text-[13px] text-[#FFCC00]\"\n : \"h-8 w-8 bg-black/35 text-[12px] text-white\",\n )}\n >\n {formatFactor(opt)}\n {isActive && <span className=\"text-[10px]\">×</span>}\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * OptionsGridPanel — the iPhone two-tap options surface: tapping the grid\n * button dims the viewfinder and reveals a rounded dark panel pinned to the\n * bottom holding a grid of circular toggle buttons with uppercase labels\n * (one tap to reveal, one tap to act). This is NOT a drawer — no drag\n * handle, no route; tapping the scrim or the grid button again closes it.\n *\n * Tiles are injected (`CaptureOptionTile[]`) so hosts and domain extensions\n * add their own toggles without touching the panel.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureOptionTile } from \"../types\";\n\nexport interface OptionsGridPanelProps {\n open: boolean;\n onClose: () => void;\n tiles: CaptureOptionTile[];\n}\n\nexport function OptionsGridPanel({\n open,\n onClose,\n tiles,\n}: OptionsGridPanelProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-40\">\n {/* Dimming scrim — the viewfinder darkens like iOS; tap to dismiss. */}\n <button\n type=\"button\"\n aria-label=\"Close camera options\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/70\"\n />\n <div\n className=\"absolute inset-x-3 bottom-3 rounded-[2.5rem] bg-[#3a3a3c]/95 px-6 py-8 shadow-2xl\"\n style={{ marginBottom: \"env(safe-area-inset-bottom, 0px)\" }}\n >\n <div className=\"grid grid-cols-3 gap-x-4 gap-y-7\">\n {tiles.map((tile) => (\n <div key={tile.id} className=\"flex flex-col items-center gap-2.5\">\n <button\n type=\"button\"\n onClick={tile.onPress}\n disabled={tile.disabled}\n aria-label={tile.label}\n aria-pressed={tile.active === true}\n className={cn(\n \"flex h-16 w-16 touch-manipulation items-center justify-center rounded-full bg-[#2c2c2e] transition-colors\",\n tile.active ? \"text-[#FFCC00]\" : \"text-white\",\n tile.disabled && \"opacity-40\",\n )}\n >\n {tile.icon}\n </button>\n <span className=\"text-[13px] font-semibold uppercase tracking-[0.14em] text-white\">\n {tile.valueLabel ? `${tile.label} ${tile.valueLabel}` : tile.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CaptureSheet — the iOS-style system sheet used over the camera: a light,\n * heavily-rounded card sliding over the lower portion of the screen with a\n * circular ✕ close top-right; content is an icon + bold title + body text\n * with a filled primary action and a tinted secondary one. `variant=\"busy\"`\n * is the transient state (small spinner + label, like the OS \"Connecting…\"\n * sheet). Deliberately light-on-dark regardless of app theme — it mirrors\n * the OS presentation over camera chrome.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { Loader2, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\nexport interface CaptureSheetAction {\n label: string;\n onPress: () => void;\n kind?: \"primary\" | \"secondary\";\n}\n\nexport interface CaptureSheetProps {\n open: boolean;\n onClose: () => void;\n /** Standard content sheet by default; \"busy\" renders spinner + label. */\n variant?: \"content\" | \"busy\";\n icon?: React.ReactNode;\n title?: string;\n body?: React.ReactNode;\n actions?: CaptureSheetAction[];\n /** The busy variant's label (\"Connecting…\"). */\n busyLabel?: string;\n}\n\nexport function CaptureSheet({\n open,\n onClose,\n variant = \"content\",\n icon,\n title,\n body,\n actions = [],\n busyLabel = \"Working…\",\n}: CaptureSheetProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col justify-end\">\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/30\"\n />\n <div\n className=\"relative mx-2 mb-2 rounded-[2rem] bg-[#f2f2f7] px-6 pb-6 pt-5 text-black shadow-2xl\"\n style={{ marginBottom: \"calc(0.5rem + env(safe-area-inset-bottom, 0px))\" }}\n >\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"absolute right-4 top-4 flex h-9 w-9 items-center justify-center rounded-full bg-black/5 text-black/70 transition-colors hover:bg-black/10\"\n >\n <X className=\"h-5 w-5\" strokeWidth={2.5} />\n </button>\n {variant === \"busy\" ? (\n <div className=\"flex min-h-[220px] items-center justify-center gap-2.5\">\n <Loader2 className=\"h-5 w-5 animate-spin text-black/50\" />\n <span className=\"text-[17px] font-medium text-black/80\">\n {busyLabel}\n </span>\n </div>\n ) : (\n <div className=\"pt-4\">\n {icon && <div className=\"mb-5 text-[#0a84ff]\">{icon}</div>}\n {title && (\n <h2 className=\"mb-2 text-[26px] font-bold leading-tight\">\n {title}\n </h2>\n )}\n {body && (\n <div className=\"text-[17px] leading-snug text-black/85\">\n {body}\n </div>\n )}\n {actions.length > 0 && (\n <div className=\"mt-7 flex flex-col gap-3\">\n {actions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={action.onPress}\n className={cn(\n \"h-[50px] w-full touch-manipulation rounded-full text-[17px] font-semibold transition-transform active:scale-[0.98]\",\n (action.kind ?? \"primary\") === \"primary\"\n ? \"bg-[#0a84ff] text-white\"\n : \"bg-black/[0.06] text-[#0a84ff]\",\n )}\n >\n {action.label}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * GridOverlay — the rule-of-thirds composition grid over the viewfinder.\n * Genuinely supported (pure CSS), toggled from the options grid.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function GridOverlay({ visible }: { visible: boolean }) {\n if (!visible) return null;\n return (\n <div aria-hidden className=\"pointer-events-none absolute inset-0 z-10\">\n <div className=\"absolute inset-y-0 left-1/3 w-px bg-white/40\" />\n <div className=\"absolute inset-y-0 left-2/3 w-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-1/3 h-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-2/3 h-px bg-white/40\" />\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CountdownOverlay — the big centered timer digits (iPhone timer capture):\n * one large white numeral per second, scaling in as it changes.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function CountdownOverlay({ seconds }: { seconds: number | null }) {\n if (seconds === null || seconds <= 0) return null;\n return (\n <div className=\"pointer-events-none absolute inset-0 z-30 flex items-center justify-center\">\n <span\n key={seconds}\n className=\"text-[120px] font-light text-white drop-shadow-lg [@starting-style]:scale-125 [@starting-style]:opacity-0 transition-all duration-300\"\n >\n {seconds}\n </span>\n </div>\n );\n}\n","/**\n * Package-owned media resolution cache — THE fix for \"every page turn feels\n * like a refetch\".\n *\n * The host supplies, per media item, either a ready `src` (a local object\n * URL for a fresh capture) or an async `resolve()` (a persisted file that\n * needs an authenticated URL). Everything else — memoization, in-flight\n * dedup, LRU eviction, revocation of URLs the cache itself created — is the\n * package's job. This lives IN the package on the completeness rule: if the\n * host had to rebuild this, most hosts wouldn't, and every viewer would lag.\n *\n * Ownership: URLs returned by `resolve()` are revoked on eviction ONLY when\n * the resolver marks them owned (`{ url, revoke: true }`); a plain string is\n * assumed host-owned and never revoked.\n */\n\nimport { useEffect, useState } from \"react\";\n\nexport type ResolvedMedia = string | { url: string; revoke?: boolean };\n\nexport interface CaptureMediaItem {\n /** Stable identity for the item (drives caching + React keys). */\n key: string;\n kind: \"photo\" | \"video\" | \"audio\";\n /** Ready-to-render URL (fresh capture). Host-owned, never revoked here. */\n src?: string | null;\n /** Async URL resolution for persisted items. Called at most once per key\n * while cached; concurrent callers share the in-flight promise. */\n resolve?: () => Promise<ResolvedMedia>;\n /** Upload/processing state — the filmstrip renders it honestly. */\n status?: \"ready\" | \"uploading\" | \"error\";\n /** Accent ring on the filmstrip tile (e.g. a delineator frame). */\n accent?: boolean;\n}\n\ninterface CacheEntry {\n url: string | null;\n revoke: boolean;\n promise: Promise<string | null> | null;\n error: boolean;\n}\n\nconst MAX_ENTRIES = 80;\n\n/** Module-level LRU — survives viewer/filmstrip mount cycles by design. */\nconst cache = new Map<string, CacheEntry>();\nconst listeners = new Map<string, Set<() => void>>();\n\nfunction notify(key: string): void {\n listeners.get(key)?.forEach((fn) => fn());\n}\n\nfunction evictIfNeeded(): void {\n while (cache.size > MAX_ENTRIES) {\n const oldest = cache.keys().next().value;\n if (oldest === undefined) return;\n const entry = cache.get(oldest);\n cache.delete(oldest);\n if (entry?.revoke && entry.url) URL.revokeObjectURL(entry.url);\n }\n}\n\nfunction touch(key: string, entry: CacheEntry): void {\n // Map iteration order = insertion order; re-insert on hit = LRU for free.\n cache.delete(key);\n cache.set(key, entry);\n}\n\n/**\n * Resolve an item's URL through the cache. Synchronous when already known;\n * kicks off (or joins) the resolution otherwise and notifies subscribers.\n */\nexport function getMediaUrl(item: CaptureMediaItem): string | null {\n if (item.src) return item.src;\n if (!item.resolve) return null;\n\n const existing = cache.get(item.key);\n if (existing) {\n touch(item.key, existing);\n if (existing.url || existing.error) return existing.url;\n return null; // in flight\n }\n\n const entry: CacheEntry = { url: null, revoke: false, promise: null, error: false };\n cache.set(item.key, entry);\n evictIfNeeded();\n entry.promise = item\n .resolve()\n .then((resolved) => {\n const url = typeof resolved === \"string\" ? resolved : resolved.url;\n entry.revoke = typeof resolved === \"object\" && resolved.revoke === true;\n entry.url = url;\n entry.promise = null;\n notify(item.key);\n return url;\n })\n .catch(() => {\n entry.error = true;\n entry.promise = null;\n notify(item.key);\n return null;\n });\n return null;\n}\n\n/** Drop one item (e.g. after delete). Revokes only cache-owned URLs. */\nexport function invalidateMedia(key: string): void {\n const entry = cache.get(key);\n cache.delete(key);\n if (entry?.revoke && entry.url) URL.revokeObjectURL(entry.url);\n notify(key);\n}\n\n/** React binding: the item's URL, updating when resolution lands. */\nexport function useMediaUrl(item: CaptureMediaItem | null): string | null {\n const [, bump] = useState(0);\n const key = item?.key ?? null;\n useEffect(() => {\n if (!key) return;\n const set = listeners.get(key) ?? new Set();\n const fn = () => bump((n) => n + 1);\n set.add(fn);\n listeners.set(key, set);\n return () => {\n set.delete(fn);\n if (set.size === 0) listeners.delete(key);\n };\n }, [key]);\n if (!item) return null;\n return getMediaUrl(item);\n}\n\n/** Warm the cache for a set of items (the viewer's neighbor preload). */\nexport function primeMedia(items: (CaptureMediaItem | undefined)[]): void {\n for (const item of items) {\n if (item) void getMediaUrl(item);\n }\n}\n","\"use client\";\n\n/**\n * CaptureFilmstrip — the row of session-capture thumbnails floating over the\n * feed. Package-owned so the tile states (uploading spinner, error ring,\n * accent ring) and the tap-to-view wiring are tuned once, everywhere.\n * Thumbnails resolve through the package media cache, so the strip never\n * re-fetches what the viewer already resolved (and vice versa).\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport { useMediaUrl, type CaptureMediaItem } from \"../media/media-cache\";\n\nexport interface CaptureFilmstripProps {\n items: CaptureMediaItem[];\n onOpen: (item: CaptureMediaItem) => void;\n /** Show at most this many trailing tiles (default 12). */\n limit?: number;\n}\n\nexport function CaptureFilmstrip({\n items,\n onOpen,\n limit = 12,\n}: CaptureFilmstripProps) {\n if (items.length === 0) return null;\n return (\n <div className=\"flex items-center gap-1.5 overflow-x-auto py-1\">\n {items.slice(-limit).map((item) => (\n <button\n key={item.key}\n type=\"button\"\n onClick={() => onOpen(item)}\n aria-label=\"View capture\"\n className={cn(\n \"relative h-12 w-9 shrink-0 touch-manipulation overflow-hidden rounded bg-white/10\",\n item.accent && \"ring-2 ring-inset ring-amber-400\",\n )}\n >\n <FilmstripThumb item={item} />\n {item.status === \"uploading\" && (\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/40\">\n <span className=\"h-3.5 w-3.5 animate-spin rounded-full border-2 border-white/40 border-t-white\" />\n </span>\n )}\n {item.status === \"error\" && (\n <span className=\"absolute inset-0 rounded ring-2 ring-inset ring-red-500\" />\n )}\n </button>\n ))}\n </div>\n );\n}\n\nfunction FilmstripThumb({ item }: { item: CaptureMediaItem }) {\n const url = useMediaUrl(item);\n if (item.kind === \"audio\") {\n return (\n <span className=\"flex h-full w-full items-center justify-center\">\n <svg\n viewBox=\"0 0 24 24\"\n className=\"h-4 w-4 stroke-white/80\"\n fill=\"none\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n aria-hidden\n >\n <path d=\"M12 3v10\" />\n <path d=\"M8 7v4M16 7v4M4 9v1M20 9v1\" />\n <circle cx=\"12\" cy=\"17\" r=\"3\" />\n </svg>\n </span>\n );\n }\n if (!url) return <span className=\"block h-full w-full bg-white/5\" />;\n if (item.kind === \"video\") {\n return (\n <span className=\"relative block h-full w-full\">\n <video\n src={url}\n muted\n playsInline\n preload=\"metadata\"\n className=\"h-full w-full object-cover\"\n />\n <svg\n viewBox=\"0 0 24 24\"\n className=\"absolute inset-0 m-auto h-4 w-4 fill-white drop-shadow\"\n aria-hidden\n >\n <path d=\"M8 5v14l11-7z\" />\n </svg>\n </span>\n );\n }\n return <img src={url} alt=\"\" className=\"h-full w-full object-cover\" />;\n}\n","\"use client\";\n\n/**\n * MediaViewer — the package's full-screen swipe pager (iOS Photos pattern),\n * moved INTO the package after real-phone tuning so every host inherits the\n * tuned gestures, neighbor preload and resolution caching for free.\n *\n * - Pointer-event gestures, zero animation deps: horizontal drag pages\n * (40px travel OR a 0.25px/ms flick), vertical drag ≥110px dismisses,\n * direction locked on first movement.\n * - Neighbors stay MOUNTED and invisible (±2 photos, ±1 video) so a page\n * turn lands on already-resolved, already-decoded pixels — the \"revisit\n * lag\" fix. URLs come from the package media cache (`media-cache.ts`).\n * - Optional per-item Delete and Edit (edit hands off to the host — the\n * `CameraCapture` orchestration pairs it with `ImageEditSheet` and\n * replace semantics).\n * - Desktop: arrow keys page, Escape closes, chevrons on hover-capable\n * screens.\n */\n\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { ChevronLeft, ChevronRight, Pencil, Trash2, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\nimport { safeTop, safeMarginBottom } from \"../safe-area\";\nimport {\n primeMedia,\n useMediaUrl,\n type CaptureMediaItem,\n} from \"../media/media-cache\";\n\n// Tuned on a real phone (2026-08-29): distance OR a modest flick must page.\nconst SWIPE_TRIGGER_PX = 40;\nconst SWIPE_TRIGGER_VELOCITY = 0.25; // px per ms\nconst DISMISS_TRIGGER_PX = 110;\nconst DIRECTION_LOCK_PX = 8;\n\nexport interface MediaViewerProps {\n items: CaptureMediaItem[];\n /** Key of the item to open on. */\n initialKey: string | null;\n onClose: () => void;\n onDelete?: (item: CaptureMediaItem) => void;\n /** Offered on photo items with a renderable URL. */\n onEdit?: (item: CaptureMediaItem) => void;\n /** What deleting costs, stated BEFORE the delete (destructive-actions\n * rule). Override per domain; deletion is never one silent tap. */\n deleteConsequence?: string;\n /** Suppress keyboard handling while another overlay (the editor) is on\n * top — Escape must close the top-most surface, not this one. */\n keysDisabled?: boolean;\n}\n\nexport function MediaViewer({\n items,\n initialKey,\n onClose,\n onDelete,\n onEdit,\n deleteConsequence = \"It is removed from this capture for everyone.\",\n keysDisabled = false,\n}: MediaViewerProps) {\n const count = items.length;\n const [index, setIndex] = useState(() => {\n const i = items.findIndex((m) => m.key === initialKey);\n return i >= 0 ? i : 0;\n });\n const clamped = Math.min(index, count - 1);\n const current = count > 0 ? items[clamped] : undefined;\n\n // Drag state lives in refs; only the applied transform is React state.\n // Delete states its consequence before acting — never a one-tap delete.\n const [confirmingDelete, setConfirmingDelete] = useState(false);\n const dragRef = useRef<{\n x0: number;\n y0: number;\n t0: number;\n axis: \"x\" | \"y\" | null;\n pointerId: number;\n } | null>(null);\n const [drag, setDrag] = useState<{ dx: number; dy: number } | null>(null);\n const [settling, setSettling] = useState(false);\n\n const go = useCallback(\n (dir: 1 | -1) => {\n setIndex((i) => Math.max(0, Math.min(count - 1, i + dir)));\n setDrag(null);\n setSettling(true);\n },\n [count],\n );\n\n // The list can shrink underneath us (a delete) — clamp, close on empty.\n useEffect(() => {\n if (count === 0) {\n const t = setTimeout(onClose, 0);\n return () => clearTimeout(t);\n }\n if (index >= count) setIndex(count - 1);\n }, [count, index, onClose]);\n\n // Paging away drops a pending delete confirmation — it must always refer\n // to the slide on screen.\n useEffect(() => {\n setConfirmingDelete(false);\n }, [clamped]);\n\n // Neighbor warm-up: resolve upcoming URLs before the swipe lands.\n useEffect(() => {\n primeMedia([\n items[clamped - 2],\n items[clamped - 1],\n items[clamped + 1],\n items[clamped + 2],\n ]);\n }, [items, clamped]);\n\n useEffect(() => {\n if (keysDisabled) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"ArrowRight\") go(1);\n else if (e.key === \"ArrowLeft\") go(-1);\n else if (e.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [go, onClose, keysDisabled]);\n\n const onPointerDown = useCallback((e: React.PointerEvent) => {\n // Let native <video>/<audio> controls own their pointer interactions.\n if ((e.target as HTMLElement).closest(\"video, audio\")) return;\n dragRef.current = {\n x0: e.clientX,\n y0: e.clientY,\n t0: performance.now(),\n axis: null,\n pointerId: e.pointerId,\n };\n (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);\n setSettling(false);\n }, []);\n\n const onPointerMove = useCallback((e: React.PointerEvent) => {\n const d = dragRef.current;\n if (!d || e.pointerId !== d.pointerId) return;\n const dx = e.clientX - d.x0;\n const dy = e.clientY - d.y0;\n if (d.axis === null) {\n if (Math.abs(dx) < DIRECTION_LOCK_PX && Math.abs(dy) < DIRECTION_LOCK_PX)\n return;\n d.axis = Math.abs(dx) >= Math.abs(dy) ? \"x\" : \"y\";\n }\n setDrag(d.axis === \"x\" ? { dx, dy: 0 } : { dx: 0, dy });\n }, []);\n\n const endDrag = useCallback(\n (e: React.PointerEvent) => {\n const d = dragRef.current;\n if (!d || e.pointerId !== d.pointerId) return;\n dragRef.current = null;\n const dx = e.clientX - d.x0;\n const dy = e.clientY - d.y0;\n const dt = Math.max(1, performance.now() - d.t0);\n const vx = dx / dt;\n\n if (d.axis === \"y\" && Math.abs(dy) > DISMISS_TRIGGER_PX) {\n onClose();\n return;\n }\n if (d.axis === \"x\") {\n const flick =\n Math.abs(dx) > 10 && Math.abs(vx) > SWIPE_TRIGGER_VELOCITY;\n if ((dx < -SWIPE_TRIGGER_PX || (flick && dx < 0)) && clamped < count - 1) {\n go(1);\n return;\n }\n if ((dx > SWIPE_TRIGGER_PX || (flick && dx > 0)) && clamped > 0) {\n go(-1);\n return;\n }\n }\n setDrag(null);\n setSettling(true);\n },\n [clamped, count, go, onClose],\n );\n\n // Mount only ±1 (a 12MP decoded frame is ~48MB — ±2 mounted flirted with\n // iOS jetsam); ±2 still get their URLs primed above, which is the cheap\n // half of the work.\n const neighborKeys = useMemo(() => {\n const wanted: CaptureMediaItem[] = [];\n for (const i of [clamped - 1, clamped + 1]) {\n const m = items[i];\n if (m) wanted.push(m);\n }\n return wanted;\n }, [items, clamped]);\n\n if (!current) return null;\n const canEdit = onEdit && current.kind === \"photo\";\n\n return (\n <div className=\"absolute inset-0 z-40 flex flex-col bg-black\">\n {/* Top bar */}\n <div\n className=\"absolute inset-x-0 top-0 z-20 flex items-center justify-between bg-gradient-to-b from-black/70 to-transparent p-3\"\n style={safeTop}\n >\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close viewer\"\n className=\"flex h-10 w-10 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n <span className=\"rounded-full bg-black/50 px-3 py-1 text-sm tabular-nums text-white/90\">\n {clamped + 1} / {count}\n </span>\n <span className=\"flex items-center gap-1\">\n {canEdit ? (\n <button\n type=\"button\"\n onClick={() => onEdit(current)}\n aria-label=\"Edit this photo\"\n className=\"flex h-10 w-10 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10\"\n >\n <Pencil className=\"h-5 w-5\" />\n </button>\n ) : null}\n {onDelete ? (\n <button\n type=\"button\"\n onClick={() => setConfirmingDelete(true)}\n aria-label=\"Delete this file\"\n className=\"flex h-10 w-10 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10\"\n >\n <Trash2 className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"h-10 w-10\" aria-hidden />\n )}\n </span>\n </div>\n\n {/* Stage */}\n <div className=\"relative min-h-0 flex-1 overflow-hidden\">\n <div\n className=\"absolute inset-0 touch-none\"\n style={{\n transform: drag\n ? `translate(${drag.dx}px, ${drag.dy}px)`\n : \"translate(0px, 0px)\",\n transition: settling\n ? \"transform 220ms cubic-bezier(0.2, 0.8, 0.3, 1)\"\n : \"none\",\n opacity: drag?.dy\n ? Math.max(0.4, 1 - Math.abs(drag.dy) / 400)\n : 1,\n }}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n >\n <ViewerSlide key={current.key} item={current} active />\n </div>\n\n {/* Neighbor preload — mounted, invisible, decoded before the swipe. */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-0 opacity-0\"\n >\n {neighborKeys.map((m) => (\n <div key={m.key} className=\"absolute inset-0\">\n <ViewerSlide item={m} active={false} />\n </div>\n ))}\n </div>\n\n {/* Desktop chevrons */}\n {clamped > 0 && (\n <button\n type=\"button\"\n onClick={() => go(-1)}\n aria-label=\"Previous file\"\n className=\"absolute left-2 top-1/2 z-20 hidden h-11 w-11 -translate-y-1/2 items-center justify-center rounded-full bg-black/40 text-white hover:bg-black/60 sm:flex\"\n >\n <ChevronLeft className=\"h-6 w-6\" />\n </button>\n )}\n {clamped < count - 1 && (\n <button\n type=\"button\"\n onClick={() => go(1)}\n aria-label=\"Next file\"\n className=\"absolute right-2 top-1/2 z-20 hidden h-11 w-11 -translate-y-1/2 items-center justify-center rounded-full bg-black/40 text-white hover:bg-black/60 sm:flex\"\n >\n <ChevronRight className=\"h-6 w-6\" />\n </button>\n )}\n </div>\n\n {/* Delete confirmation — names the consequence first. */}\n {confirmingDelete && onDelete && (\n <div\n className=\"absolute inset-x-0 bottom-0 z-30 bg-black/85 px-4 pt-4 backdrop-blur-sm\"\n style={{\n paddingBottom: \"calc(1rem + env(safe-area-inset-bottom, 0px))\",\n }}\n >\n <p className=\"pb-3 text-center text-sm text-white/85\">\n Delete this {current.kind === \"video\" ? \"video\" : current.kind === \"audio\" ? \"voice note\" : \"photo\"}? {deleteConsequence}\n </p>\n <div className=\"flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={() => setConfirmingDelete(false)}\n className=\"h-11 touch-manipulation rounded-full bg-white/15 px-6 text-sm font-medium text-white\"\n >\n Keep it\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setConfirmingDelete(false);\n onDelete(current);\n }}\n className=\"h-11 touch-manipulation rounded-full bg-[#FF3B30] px-6 text-sm font-semibold text-white\"\n >\n Delete\n </button>\n </div>\n </div>\n )}\n\n {/* Position dots (small counts only) */}\n {count > 1 && count <= 12 && (\n <div\n className=\"pointer-events-none absolute inset-x-0 bottom-4 z-20 flex justify-center gap-1.5\"\n style={safeMarginBottom}\n >\n {items.map((m, i) => (\n <span\n key={m.key}\n className={cn(\n \"h-1.5 w-1.5 rounded-full transition-colors\",\n i === clamped ? \"bg-white\" : \"bg-white/35\",\n )}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction ViewerSlide({\n item,\n active,\n}: {\n item: CaptureMediaItem;\n active: boolean;\n}) {\n const url = useMediaUrl(item);\n if (!url) {\n return active ? (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"h-8 w-8 animate-spin rounded-full border-2 border-white/30 border-t-white\" />\n </div>\n ) : null;\n }\n if (item.kind === \"audio\") {\n if (!active) return null;\n return (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-6 px-8\">\n <svg\n viewBox=\"0 0 24 24\"\n className=\"h-12 w-12 stroke-white/80\"\n fill=\"none\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n aria-hidden\n >\n <path d=\"M12 2v12\" />\n <path d=\"M7 6v6M17 6v6M2 9v2M22 9v2\" />\n <circle cx=\"12\" cy=\"19\" r=\"3\" />\n </svg>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption -- a just-recorded voice note has no caption track */}\n <audio src={url} controls className=\"w-full max-w-sm\" />\n </div>\n );\n }\n if (item.kind === \"video\") {\n return (\n <video\n src={url}\n controls={active}\n playsInline\n // Hidden neighbors buffer METADATA only — never whole videos.\n preload={active ? \"auto\" : \"metadata\"}\n muted={!active}\n // pointer-events-auto ONLY on the visible slide: a descendant's\n // `auto` re-enables hit-testing under a pointer-events-none ancestor,\n // so an invisible preload video would sit ABOVE the stage (opacity\n // creates a stacking context) and eat every swipe. Found on a real\n // phone as \"swiping barely works\".\n className={cn(\n \"absolute inset-0 h-full w-full object-contain\",\n active ? \"pointer-events-auto\" : \"pointer-events-none\",\n )}\n />\n );\n }\n return (\n <img\n src={url}\n alt=\"\"\n draggable={false}\n decoding={active ? \"auto\" : \"async\"}\n className=\"absolute inset-0 h-full w-full select-none object-contain\"\n />\n );\n}\n","\"use client\";\n\n/**\n * ImageEditSheet — instant in-browser image editing, core to the package\n * (THE CLOUD LAW's sibling: capture without instant edit is half a system).\n * v1 covers the basics that belong in the browser — crop (free + 1:1, 4:3,\n * 16:9 presets), rotate 90°, flip — full-screen dark chrome in the iOS\n * editing style: Cancel/Save top bar, the image letterboxed center, a\n * draggable/resizable crop frame with corner handles, tool row bottom.\n * Heavier AI edits ride host-injected actions, not this sheet.\n *\n * Output is a JPEG re-encode of the ORIGINAL pixels (rotation/flip/crop\n * applied on canvas) — never a screenshot of the preview.\n *\n * Package source (`@ai-matrx/capture`) — browser canvas only, no app deps.\n */\n\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { Check, FlipHorizontal2, RotateCcw, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\ntype AspectPreset = \"free\" | \"1:1\" | \"4:3\" | \"16:9\";\nconst ASPECTS: { id: AspectPreset; label: string; ratio: number | null }[] = [\n { id: \"free\", label: \"Free\", ratio: null },\n { id: \"1:1\", label: \"Square\", ratio: 1 },\n { id: \"4:3\", label: \"4:3\", ratio: 4 / 3 },\n { id: \"16:9\", label: \"16:9\", ratio: 16 / 9 },\n];\n\n/** Normalized crop rect (0..1) relative to the ROTATED/FLIPPED image. */\ninterface CropRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface ImageEditSheetProps {\n open: boolean;\n /** Source image URL (object URL or resolvable src). */\n src: string | null;\n onClose: () => void;\n /** Receives the edited JPEG. The host persists it (cloud port). */\n onSave: (blob: Blob) => void;\n}\n\nconst FULL_CROP: CropRect = { x: 0, y: 0, w: 1, h: 1 };\nconst MIN_CROP = 0.08;\n\nexport function ImageEditSheet({\n open,\n src,\n onClose,\n onSave,\n}: ImageEditSheetProps) {\n // Rotation/flip are BAKED into a working bitmap the moment they are\n // applied (full-res canvas re-encode → object URL). The displayed <img>\n // is therefore always untransformed, so the percentage-positioned crop\n // frame measures exactly the pixels on screen — the earlier CSS\n // `transform: rotate()` approach left the layout box unrotated and the\n // crop frame visibly divergent from the image at 90°/270°.\n const [workingSrc, setWorkingSrc] = useState<string | null>(null);\n const [aspect, setAspect] = useState<AspectPreset>(\"free\");\n const [crop, setCrop] = useState<CropRect>(FULL_CROP);\n const [saving, setSaving] = useState(false);\n const [editError, setEditError] = useState<string | null>(null);\n const stageRef = useRef<HTMLDivElement | null>(null);\n const imgRef = useRef<HTMLImageElement | null>(null);\n // Object URLs THIS sheet minted for baked bitmaps — revoked on replace\n // and on close. The host's `src` is never revoked here.\n const ownedUrlsRef = useRef<string[]>([]);\n const dragRef = useRef<{\n kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\";\n startX: number;\n startY: number;\n startCrop: CropRect;\n } | null>(null);\n\n const revokeOwned = useCallback(() => {\n for (const url of ownedUrlsRef.current) URL.revokeObjectURL(url);\n ownedUrlsRef.current = [];\n }, []);\n\n useEffect(() => {\n if (open) {\n setWorkingSrc(null);\n setAspect(\"free\");\n setCrop(FULL_CROP);\n setSaving(false);\n setEditError(null);\n }\n return revokeOwned;\n }, [open, src, revokeOwned]);\n\n /** Apply rotate/flip by re-encoding the CURRENT working bitmap. */\n const bake = useCallback(\n (op: \"rotate-left\" | \"flip\") => {\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0 || saving) return;\n try {\n const swap = op === \"rotate-left\";\n const canvas = document.createElement(\"canvas\");\n canvas.width = swap ? img.naturalHeight : img.naturalWidth;\n canvas.height = swap ? img.naturalWidth : img.naturalHeight;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"no 2d context\");\n ctx.translate(canvas.width / 2, canvas.height / 2);\n if (swap) ctx.rotate(-Math.PI / 2);\n else ctx.scale(-1, 1);\n ctx.drawImage(img, -img.naturalWidth / 2, -img.naturalHeight / 2);\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n setEditError(\n \"This image is too large to process on this device.\",\n );\n return;\n }\n revokeOwned();\n const url = URL.createObjectURL(blob);\n ownedUrlsRef.current.push(url);\n setWorkingSrc(url);\n setCrop(FULL_CROP);\n setAspect(\"free\");\n setEditError(null);\n },\n \"image/jpeg\",\n 0.95,\n );\n } catch {\n setEditError(\"This image could not be edited on this device.\");\n }\n },\n [saving, revokeOwned],\n );\n\n const applyAspect = useCallback((preset: AspectPreset) => {\n setAspect(preset);\n const ratio = ASPECTS.find((a) => a.id === preset)?.ratio ?? null;\n if (ratio === null) return;\n // Fit the largest centered rect of the target ratio inside the frame,\n // in DISPLAYED-image normalized space. The working bitmap is always\n // untransformed, so natural dimensions ARE the displayed geometry.\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n const imageRatio = img.naturalWidth / img.naturalHeight;\n let w = 1;\n let h = 1;\n if (ratio > imageRatio) h = imageRatio / ratio;\n else w = ratio / imageRatio;\n setCrop({ x: (1 - w) / 2, y: (1 - h) / 2, w, h });\n }, []);\n\n const onPointerDown = useCallback(\n (kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\") =>\n (e: React.PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n dragRef.current = {\n kind,\n startX: e.clientX,\n startY: e.clientY,\n startCrop: crop,\n };\n },\n [crop],\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n const drag = dragRef.current;\n const img = imgRef.current;\n if (!drag || !img) return;\n const rect = img.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const dx = (e.clientX - drag.startX) / rect.width;\n const dy = (e.clientY - drag.startY) / rect.height;\n const c = { ...drag.startCrop };\n const ratio = ASPECTS.find((a) => a.id === aspect)?.ratio ?? null;\n const frameRatio = rect.width / rect.height;\n\n if (drag.kind === \"move\") {\n c.x = Math.min(1 - c.w, Math.max(0, c.x + dx));\n c.y = Math.min(1 - c.h, Math.max(0, c.y + dy));\n } else {\n const left = drag.kind === \"nw\" || drag.kind === \"sw\";\n const top = drag.kind === \"nw\" || drag.kind === \"ne\";\n let x2 = c.x + c.w;\n let y2 = c.y + c.h;\n if (left) c.x = Math.min(x2 - MIN_CROP, Math.max(0, c.x + dx));\n else x2 = Math.max(c.x + MIN_CROP, Math.min(1, x2 + dx));\n if (top) c.y = Math.min(y2 - MIN_CROP, Math.max(0, c.y + dy));\n else y2 = Math.max(c.y + MIN_CROP, Math.min(1, y2 + dy));\n c.w = x2 - c.x;\n c.h = y2 - c.y;\n if (ratio !== null) {\n // Lock the ratio by deriving height from width in SCREEN space.\n const targetH = (c.w * frameRatio) / ratio;\n if (top) c.y = y2 - Math.min(targetH, y2);\n c.h = Math.min(targetH, top ? y2 - c.y : 1 - c.y);\n c.w = (c.h * ratio) / frameRatio;\n if (left) c.x = x2 - c.w;\n }\n }\n setCrop(c);\n },\n [aspect],\n );\n\n const onPointerUp = useCallback(() => {\n dragRef.current = null;\n }, []);\n\n const save = useCallback(() => {\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n setSaving(true);\n setEditError(null);\n try {\n // Rotation/flip are already baked into the working bitmap — save is\n // a pure crop of what the user sees.\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, Math.round(img.naturalWidth * crop.w));\n canvas.height = Math.max(1, Math.round(img.naturalHeight * crop.h));\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"no 2d context\");\n ctx.drawImage(img, -crop.x * img.naturalWidth, -crop.y * img.naturalHeight);\n canvas.toBlob(\n (blob) => {\n setSaving(false);\n if (blob) {\n onSave(blob);\n onClose();\n } else {\n // iOS canvas memory limits can yield null on huge frames —\n // Save must never silently do nothing.\n setEditError(\n \"Saving failed on this device — try a smaller crop.\",\n );\n }\n },\n \"image/jpeg\",\n 0.92,\n );\n } catch (err) {\n console.error(\"[capture-camera] edit save failed\", err);\n setSaving(false);\n setEditError(\"Saving failed on this device — try again.\");\n }\n }, [crop, onSave, onClose]);\n\n if (!open || !src) return null;\n\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col bg-black\">\n {/* Top bar */}\n <div\n className=\"flex shrink-0 items-center justify-between px-4\"\n style={{ paddingTop: \"env(safe-area-inset-top, 0px)\" }}\n >\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Cancel editing\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-medium text-white\"\n >\n <X className=\"h-5 w-5\" />\n Cancel\n </button>\n <span className=\"text-[15px] font-semibold text-white/90\">Edit</span>\n <button\n type=\"button\"\n onClick={save}\n disabled={saving}\n aria-label=\"Save edited image\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-semibold text-[#FFCC00] disabled:opacity-50\"\n >\n <Check className=\"h-5 w-5\" />\n Save\n </button>\n </div>\n\n {/* Stage */}\n <div\n ref={stageRef}\n className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-hidden p-4\"\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"relative max-h-full max-w-full\">\n {/* eslint-disable-next-line @next/next/no-img-element -- local object URL being edited */}\n <img\n ref={imgRef}\n src={workingSrc ?? src}\n alt=\"Image being edited\"\n draggable={false}\n className=\"max-h-[62dvh] max-w-full select-none object-contain\"\n />\n {/* Crop frame in displayed-image space */}\n <div\n role=\"presentation\"\n onPointerDown={onPointerDown(\"move\")}\n className=\"absolute cursor-move touch-none border-2 border-white shadow-[0_0_0_9999px_rgba(0,0,0,0.55)]\"\n style={{\n left: `${crop.x * 100}%`,\n top: `${crop.y * 100}%`,\n width: `${crop.w * 100}%`,\n height: `${crop.h * 100}%`,\n }}\n >\n {([\"nw\", \"ne\", \"sw\", \"se\"] as const).map((corner) => (\n <span\n key={corner}\n role=\"presentation\"\n onPointerDown={onPointerDown(corner)}\n className={cn(\n \"absolute h-6 w-6 touch-none\",\n corner === \"nw\" &&\n \"-left-1.5 -top-1.5 border-l-4 border-t-4 cursor-nwse-resize\",\n corner === \"ne\" &&\n \"-right-1.5 -top-1.5 border-r-4 border-t-4 cursor-nesw-resize\",\n corner === \"sw\" &&\n \"-bottom-1.5 -left-1.5 border-b-4 border-l-4 cursor-nesw-resize\",\n corner === \"se\" &&\n \"-bottom-1.5 -right-1.5 border-b-4 border-r-4 cursor-nwse-resize\",\n \"border-white\",\n )}\n />\n ))}\n </div>\n </div>\n </div>\n\n {/* Tool row */}\n <div className=\"shrink-0\" style={{ paddingBottom: \"env(safe-area-inset-bottom, 0px)\" }}>\n {editError && (\n <p className=\"px-6 pb-2 text-center text-sm text-[#FF6961]\">\n {editError}\n </p>\n )}\n <div className=\"flex items-center justify-center gap-2 pb-2\">\n {ASPECTS.map((a) => (\n <button\n key={a.id}\n type=\"button\"\n onClick={() => applyAspect(a.id)}\n aria-pressed={aspect === a.id}\n className={cn(\n \"touch-manipulation rounded-full px-3.5 py-1.5 text-[12px] font-semibold uppercase tracking-wide transition-colors\",\n aspect === a.id\n ? \"bg-white/20 text-[#FFCC00]\"\n : \"text-white/80\",\n )}\n >\n {a.label}\n </button>\n ))}\n </div>\n <div className=\"flex items-center justify-center gap-6 pb-4\">\n <button\n type=\"button\"\n onClick={() => bake(\"rotate-left\")}\n aria-label=\"Rotate left\"\n className=\"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10 text-white\"\n >\n <RotateCcw className=\"h-5 w-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => bake(\"flip\")}\n aria-label=\"Flip horizontally\"\n className=\"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10 text-white\"\n >\n <FlipHorizontal2 className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CameraFeed — the package's live preview <video> for the default engine\n * (drop-in hosts). Attaches the engine's stream imperatively (srcObject is\n * not a React DOM prop), fills its container with object-cover, and exposes\n * the element through the engine's videoRef so photo capture can read it.\n * Inline styles on purpose: host stylesheets (e.g. mobile `video { height:\n * auto }` resets) must never shrink the live view below the captured frame.\n *\n * Hosts with their own runtime render their own preview component instead —\n * this one is part of the batteries-included path.\n */\n\nimport React, { useEffect } from \"react\";\nimport type { CaptureCameraEngine } from \"../types\";\n\nexport function CameraFeed({\n engine,\n mirror = false,\n}: {\n engine: CaptureCameraEngine;\n /** Preview-only CSS mirror (front camera). Output is never mirrored. */\n mirror?: boolean;\n}) {\n const { stream, videoRef } = engine;\n\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n if (video.srcObject !== stream) video.srcObject = stream;\n return () => {\n video.srcObject = null;\n };\n }, [stream, videoRef]);\n\n return (\n <video\n ref={videoRef}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n ...(mirror ? { transform: \"scaleX(-1)\" } : {}),\n }}\n muted\n autoPlay\n playsInline\n />\n );\n}\n","\"use client\";\n\n/**\n * useDefaultCaptureEngine — the package's OWN working camera engine, so a\n * host with no media runtime (the Vite drop-in test) gets a fully wired\n * camera from a few lines:\n *\n * const engine = useDefaultCaptureEngine({ onPhoto, onVideo, onFiles });\n * <CameraCapture engine={engine} preview={<CameraFeed engine={engine} />} … />\n *\n * Sophisticated hosts (AI Matrx injects its lease-managed runtime) may pass\n * their own `CaptureCameraEngine` instead — this default is the floor, not\n * the ceiling.\n *\n * THE ONE getUserMedia SITE of the package lives here (`src/engine/` — the\n * laws test bans it everywhere else). Behavior:\n * - Environment-facing by default; flip toggles facingMode and reacquires.\n * - Photo: full-sensor canvas capture (JPEG q0.92), optional center-crop to\n * the chrome's aspect setting. Never a screenshot of the cropped preview.\n * - Video: MediaRecorder over the live stream plus (when granted) audio,\n * with a concrete-MIME ladder and a monotonic elapsed clock.\n * - Upload: a package-created hidden <input type=\"file\"> (no `capture`\n * attribute — that is what opens the OS gallery, not the camera).\n * - Tracks stop on unmount and on flip; permission denial reports\n * `blocked: \"permission-denied\"`, everything else \"not-supported\".\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { CaptureAspect, CaptureCameraEngine } from \"../types\";\n\nconst RECORDING_MIME_LADDER = [\n 'video/mp4;codecs=\"avc1.42E01E,mp4a.40.2\"',\n \"video/mp4\",\n 'video/webm;codecs=\"vp9,opus\"',\n 'video/webm;codecs=\"vp8,opus\"',\n \"video/webm\",\n];\n\nexport interface DefaultEngineOptions {\n /** Receives every captured photo as a JPEG File. */\n onPhoto: (file: File) => void;\n /** Receives the finished recording. */\n onVideo: (file: File, durationMs: number) => void;\n /** Receives files chosen through the Upload lane. */\n onFiles: (files: FileList) => void;\n /** Record microphone audio with video (asks once, at record time). */\n withAudio?: boolean;\n facingMode?: \"environment\" | \"user\";\n photoQuality?: number;\n}\n\nfunction cropRect(\n w: number,\n h: number,\n aspect: CaptureAspect,\n): { x: number; y: number; w: number; h: number } {\n if (aspect === \"full\") return { x: 0, y: 0, w, h };\n const [aw, ah] =\n aspect === \"1:1\" ? [1, 1] : aspect === \"4:3\" ? [3, 4] : [9, 16];\n // Portrait ratios above; swap for landscape sensors.\n const [rw, rh] = w >= h ? [ah, aw] : [aw, ah];\n const target = rw / rh;\n let cw = w;\n let ch = w / target;\n if (ch > h) {\n ch = h;\n cw = h * target;\n }\n return { x: (w - cw) / 2, y: (h - ch) / 2, w: cw, h: ch };\n}\n\nexport function useDefaultCaptureEngine(\n options: DefaultEngineOptions,\n): CaptureCameraEngine {\n const {\n onPhoto,\n onVideo,\n onFiles,\n withAudio = true,\n facingMode: initialFacing = \"environment\",\n photoQuality = 0.92,\n } = options;\n\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [blocked, setBlocked] = useState<CaptureCameraEngine[\"blocked\"]>(null);\n const [facing, setFacing] = useState(initialFacing);\n const [multipleCameras, setMultipleCameras] = useState(false);\n const [recording, setRecording] = useState(false);\n const [recordElapsedSeconds, setRecordElapsedSeconds] = useState(0);\n\n const videoRef = useRef<HTMLVideoElement | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const recorderRef = useRef<{\n recorder: MediaRecorder;\n chunks: Blob[];\n startedAt: number;\n micTracks: MediaStreamTrack[];\n timer: ReturnType<typeof setInterval>;\n } | null>(null);\n const callbacksRef = useRef({ onPhoto, onVideo, onFiles });\n callbacksRef.current = { onPhoto, onVideo, onFiles };\n\n // ── Stream lifecycle ─────────────────────────────────────────────────────\n useEffect(() => {\n let cancelled = false;\n let acquired: MediaStream | null = null;\n if (typeof navigator === \"undefined\" || !navigator.mediaDevices) {\n setBlocked({ reason: \"not-supported\" });\n return;\n }\n navigator.mediaDevices\n .getUserMedia({\n video: {\n facingMode: facing,\n width: { ideal: 4096 },\n height: { ideal: 4096 },\n },\n audio: false,\n })\n .then((s) => {\n if (cancelled) {\n s.getTracks().forEach((t) => t.stop());\n return;\n }\n acquired = s;\n streamRef.current = s;\n setStream(s);\n setBlocked(null);\n return navigator.mediaDevices.enumerateDevices().then((devices) => {\n if (!cancelled)\n setMultipleCameras(\n devices.filter((d) => d.kind === \"videoinput\").length > 1,\n );\n });\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n const name =\n err && typeof err === \"object\" && \"name\" in err\n ? String((err as { name: unknown }).name)\n : \"\";\n setBlocked({\n reason:\n name === \"NotAllowedError\" || name === \"SecurityError\"\n ? \"permission-denied\"\n : \"not-supported\",\n });\n });\n return () => {\n cancelled = true;\n acquired?.getTracks().forEach((t) => t.stop());\n if (streamRef.current === acquired) streamRef.current = null;\n setStream(null);\n };\n }, [facing]);\n\n // Never leave a recorder running past unmount.\n useEffect(() => {\n return () => {\n const r = recorderRef.current;\n if (r) {\n clearInterval(r.timer);\n if (r.recorder.state !== \"inactive\") r.recorder.stop();\n r.micTracks.forEach((t) => t.stop());\n }\n };\n }, []);\n\n // ── Photo ────────────────────────────────────────────────────────────────\n const onCapturePhoto = useCallback(\n (opts?: { aspect?: CaptureAspect }) => {\n const video = videoRef.current;\n if (!video || video.videoWidth === 0) return;\n const rect = cropRect(\n video.videoWidth,\n video.videoHeight,\n opts?.aspect ?? \"full\",\n );\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.round(rect.w);\n canvas.height = Math.round(rect.h);\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n ctx.drawImage(\n video,\n rect.x,\n rect.y,\n rect.w,\n rect.h,\n 0,\n 0,\n canvas.width,\n canvas.height,\n );\n canvas.toBlob(\n (blob) => {\n if (!blob) return;\n callbacksRef.current.onPhoto(\n new File([blob], `capture-${new Date().toISOString()}.jpg`, {\n type: \"image/jpeg\",\n }),\n );\n },\n \"image/jpeg\",\n photoQuality,\n );\n },\n [photoQuality],\n );\n\n // ── Video ────────────────────────────────────────────────────────────────\n const onStartRecording = useCallback(() => {\n const base = streamRef.current;\n if (!base || recorderRef.current) return;\n void (async () => {\n let micTracks: MediaStreamTrack[] = [];\n if (withAudio) {\n try {\n const mic = await navigator.mediaDevices.getUserMedia({\n audio: true,\n });\n micTracks = mic.getAudioTracks();\n } catch {\n // Mic denied — record video-only rather than failing the capture.\n }\n }\n const composed = new MediaStream([\n ...base.getVideoTracks(),\n ...micTracks,\n ]);\n const mime = RECORDING_MIME_LADDER.find((m) =>\n typeof MediaRecorder !== \"undefined\" &&\n MediaRecorder.isTypeSupported(m),\n );\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(\n composed,\n mime ? { mimeType: mime } : undefined,\n );\n } catch {\n micTracks.forEach((t) => t.stop());\n return;\n }\n const entry = {\n recorder,\n chunks: [] as Blob[],\n startedAt: performance.now(),\n micTracks,\n timer: setInterval(() => {\n setRecordElapsedSeconds(\n Math.floor((performance.now() - entry.startedAt) / 1000),\n );\n }, 250),\n };\n recorderRef.current = entry;\n recorder.ondataavailable = (e) => {\n if (e.data.size > 0) entry.chunks.push(e.data);\n };\n recorder.onstop = () => {\n clearInterval(entry.timer);\n entry.micTracks.forEach((t) => t.stop());\n recorderRef.current = null;\n setRecording(false);\n const durationMs = Math.round(performance.now() - entry.startedAt);\n const type = recorder.mimeType || entry.chunks[0]?.type || \"video/webm\";\n const blob = new Blob(entry.chunks, { type });\n const ext = type.includes(\"mp4\") ? \"mp4\" : \"webm\";\n callbacksRef.current.onVideo(\n new File([blob], `capture-${new Date().toISOString()}.${ext}`, {\n type,\n }),\n durationMs,\n );\n };\n recorder.start(1000);\n setRecordElapsedSeconds(0);\n setRecording(true);\n })();\n }, [withAudio]);\n\n const onStopRecording = useCallback(() => {\n const r = recorderRef.current;\n if (r && r.recorder.state !== \"inactive\") r.recorder.stop();\n }, []);\n\n // ── Upload lane ──────────────────────────────────────────────────────────\n const onUpload = useCallback(() => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = \"image/*,video/*\";\n input.multiple = true;\n input.onchange = () => {\n if (input.files && input.files.length > 0)\n callbacksRef.current.onFiles(input.files);\n };\n input.click();\n }, []);\n\n const onFlipCamera = useCallback(() => {\n setFacing((f) => (f === \"environment\" ? \"user\" : \"environment\"));\n }, []);\n\n return useMemo(\n () => ({\n stream,\n videoRef,\n blocked,\n onCapturePhoto,\n onStartRecording,\n onStopRecording,\n recording,\n recordElapsedSeconds,\n onUpload,\n onFlipCamera: multipleCameras ? onFlipCamera : null,\n }),\n [\n stream,\n blocked,\n onCapturePhoto,\n onStartRecording,\n onStopRecording,\n recording,\n recordElapsedSeconds,\n onUpload,\n onFlipCamera,\n multipleCameras,\n ],\n );\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","X","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","jsxs","jsx","useEffect","useState","jsx","jsxs","useCallback","useEffect","useMemo","useState","X","jsx","jsxs","useState","useCallback","useEffect","useMemo","X","useCallback","useEffect","useRef","useState","X","jsx","jsxs","useState","useRef","useCallback","useEffect","X","jsx","jsxs","useState","useRef","useEffect","useCallback","X","useEffect","jsx","useCallback","useEffect","useMemo","useRef","useState"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/capture",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "The AI Matrx capture kit: the opinionated iPhone-style camera chrome — translucent viewfinder bars, two-tap options grid, zoom pills, shutter, VIDEO·PHOTO·UPLOAD mode row, iOS-style sheets, instant in-browser crop/rotate editing — cloud-first by law (the cloud port is required), engine-injected (the host owns getUserMedia), and slot-extensible for domain add-ons.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",