@ai-matrx/capture 0.5.4 → 0.5.5

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts","../src/components/CameraCapture.tsx","../src/components/icons.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/CameraCaptureV3.tsx","../src/components/CaptureRail.tsx","../src/components/HoldShutter.tsx","../src/components/CaptureExpandingField.tsx","../src/components/CameraFeed.tsx","../src/engine/useDefaultEngine.ts","../src/engine/crop.ts","../src/engine/permissions.ts","../src/engine/warm-mic.ts","../src/components/CloudLibrarySheet.tsx"],"sourcesContent":["/**\n * @ai-matrx/capture/react — the DOM chrome + the batteries-included engine\n * (all \"use client\").\n *\n * Drop-in (any React host, no media runtime required):\n *\n * const engine = useDefaultCaptureEngine({ onPhoto, onVideo, onFiles });\n * <CameraCapture\n * engine={engine}\n * preview={<CameraFeed engine={engine} />}\n * cloud={cloudPort}\n * media={{ items, onDelete, onReplacePhoto }}\n * mode={mode}\n * onModeChange={setMode}\n * />\n */\n\nexport { CameraCapture, type CameraCaptureProps, type CaptureMediaSession } from \"./components/CameraCapture\";\n// v3 — the vertical-rail chrome (one shutter, right rail, expanding entry).\nexport { CameraCaptureV3, type CameraCaptureV3Props } from \"./components/CameraCaptureV3\";\nexport { CaptureExpandingField, type CaptureExpandingFieldProps } from \"./components/CaptureExpandingField\";\nexport { CaptureRail, type CaptureRailProps } from \"./components/CaptureRail\";\nexport { HoldShutter, type HoldShutterProps } from \"./components/HoldShutter\";\nexport { CameraFeed } from \"./components/CameraFeed\";\nexport { CaptureFilmstrip, type CaptureFilmstripProps } from \"./components/CaptureFilmstrip\";\nexport { CaptureSheet, type CaptureSheetAction, type CaptureSheetProps } from \"./components/CaptureSheet\";\nexport { CountdownOverlay } from \"./components/CountdownOverlay\";\nexport { GridOverlay } from \"./components/GridOverlay\";\nexport { ImageEditSheet, type ImageEditSheetProps } from \"./components/ImageEditSheet\";\nexport { MediaViewer, type MediaViewerProps } from \"./components/MediaViewer\";\nexport { ModeSelector, type ModeSelectorProps } from \"./components/ModeSelector\";\nexport { OptionsGridPanel, type OptionsGridPanelProps } from \"./components/OptionsGridPanel\";\nexport { ShutterButton, type ShutterButtonProps } from \"./components/ShutterButton\";\nexport { ZoomRow, type ZoomRowProps } from \"./components/ZoomRow\";\nexport { useTrackControls, type TrackControls } from \"./hooks/useTrackControls\";\nexport {\n useDefaultCaptureEngine,\n type DefaultCaptureEngine,\n type DefaultEngineOptions,\n} from \"./engine/useDefaultEngine\";\n// The engine's hard parts, exported for host adapters too (C22: the quirk\n// branches live in the package; a host runtime adapter injects identity and\n// borrows THESE instead of re-implementing them).\nexport {\n cropBlobToAspect,\n nextCameraDevice,\n PHOTO_JPEG_QUALITY,\n} from \"./engine/crop\";\nexport {\n classifyCameraBlockReason,\n isMediaDenialError,\n queryCameraPermission,\n queryMicPermission,\n shouldCombineMicPrompt,\n type MediaPermissionState,\n} from \"./engine/permissions\";\nexport {\n acquireWarmMic,\n adoptWarmAudioStream,\n buildWarmMicConstraints,\n getWarmMicState,\n hardStopWarmMic,\n notifyMicPermissionRevoked,\n releaseWarmMic,\n setPreferredMicDeviceId,\n subscribeWarmMic,\n subscribeWarmMicInterruption,\n warmMicDebug,\n type MicInterruptionReason,\n type MicStreamState,\n} from \"./engine/warm-mic\";\nexport {\n CloudLibrarySheet,\n type CloudLibrarySheetProps,\n} from \"./components/CloudLibrarySheet\";\nexport {\n getMediaUrl,\n invalidateMedia,\n primeMedia,\n useMediaUrl,\n type CaptureMediaItem,\n type ResolvedMedia,\n} from \"./media/media-cache\";\nexport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCameraV3Slots,\n CaptureCloudLibraryItem,\n CaptureCloudPort,\n CaptureOptionTile,\n CaptureRailAction,\n CaptureTimerSetting,\n} from \"./types\";\n","\"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 Grid3x3Icon,\n GripIcon,\n ProportionsIcon,\n RefreshCwIcon,\n SunMediumIcon,\n TimerIcon,\n XIcon,\n ZapIcon,\n ZapOffIcon,\n} from \"./icons\";\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 invalidateMedia,\n prepareMediaForEdit,\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 revoke: boolean;\n } | null>(null);\n const [editPreparing, setEditPreparing] = useState(false);\n const [editPreparationError, setEditPreparationError] = useState<string | null>(\n null,\n );\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\n const openLibraryFromRecents = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n const target = event.target;\n if (\n target instanceof Element &&\n target.closest(\n \"button, a, input, select, textarea, [role='button'], [role='link']\",\n )\n ) {\n // The injected thumbnail can render an informative media fallback\n // with its own remedy controls. Let that control act without also\n // opening the library.\n return;\n }\n cloud.onOpenLibrary();\n },\n [cloud],\n );\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 useEffect(\n () => () => {\n if (editTarget?.revoke) URL.revokeObjectURL(editTarget.url);\n },\n [editTarget],\n );\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 <ZapIcon className=\"h-6 w-6\" fill=\"currentColor\" />\n ) : (\n <ZapOffIcon 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: <TimerIcon 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: <Grid3x3Icon 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: <ProportionsIcon 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: <SunMediumIcon 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 <XIcon 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 <ZapIcon\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 <GripIcon 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 <SunMediumIcon 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 <div\n onClick={openLibraryFromRecents}\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 </div>\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Open your media library\"\n className=\"sr-only\"\n >\n Open your media library\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 <RefreshCwIcon 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 if (editPreparing) return;\n setEditPreparing(true);\n setEditPreparationError(null);\n void prepareMediaForEdit(item)\n .then((prepared) =>\n setEditTarget({ key: item.key, ...prepared }),\n )\n .catch(() =>\n setEditPreparationError(\n \"This photo could not be prepared for editing. Try again.\",\n ),\n )\n .finally(() => setEditPreparing(false));\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 {editPreparationError && (\n <p\n role=\"alert\"\n className=\"fixed inset-x-4 bottom-24 z-[70] rounded-xl bg-black/85 px-4 py-3 text-center text-sm text-[#FF6961]\"\n >\n {editPreparationError}\n </p>\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","/**\n * src/components/icons.tsx — the package's OWN inlined SVG icons.\n *\n * Per campaign ruling C19 (2026-08-29): no icon-library dependency, ever.\n * Every glyph below is copied from lucide-react v0.545.0 (ISC license)\n * exactly — the version this package shipped with before the swap, so\n * pixels don't change — following the @ai-matrx/media precedent\n * (`media/src/react/icons.tsx`). Add a new icon by copying the lucide\n * `__iconNode` data from the version noted here — never by adding a\n * lucide dep back.\n *\n * Internal module: icons are exported for the package's own components\n * only; they are not part of the public entry surface.\n */\n\nimport type { SVGProps } from \"react\";\n\nexport type CaptureIconProps = SVGProps<SVGSVGElement>;\n\nfunction svgProps(props: CaptureIconProps): CaptureIconProps {\n return {\n xmlns: \"http://www.w3.org/2000/svg\",\n viewBox: \"0 0 24 24\",\n width: 24,\n height: 24,\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n \"aria-hidden\": true,\n ...props,\n };\n}\n\n// lucide: check\nexport function CheckIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\n// lucide: chevron-left\nexport function ChevronLeftIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n );\n}\n\n// lucide: chevron-right\nexport function ChevronRightIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n );\n}\n\n// lucide: flip-horizontal-2\nexport function FlipHorizontal2Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m3 7 5 5-5 5V7\" />\n <path d=\"m21 7-5 5 5 5V7\" />\n <path d=\"M12 20v2\" />\n <path d=\"M12 14v2\" />\n <path d=\"M12 8v2\" />\n <path d=\"M12 2v2\" />\n </svg>\n );\n}\n\n// lucide: grid-3x3\nexport function Grid3x3Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n <path d=\"M3 9h18\" />\n <path d=\"M3 15h18\" />\n <path d=\"M9 3v18\" />\n <path d=\"M15 3v18\" />\n </svg>\n );\n}\n\n// lucide: grip\nexport function GripIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\n <circle cx=\"19\" cy=\"5\" r=\"1\" />\n <circle cx=\"5\" cy=\"5\" r=\"1\" />\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\n <circle cx=\"19\" cy=\"12\" r=\"1\" />\n <circle cx=\"5\" cy=\"12\" r=\"1\" />\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\n <circle cx=\"19\" cy=\"19\" r=\"1\" />\n <circle cx=\"5\" cy=\"19\" r=\"1\" />\n </svg>\n );\n}\n\n// lucide: images\nexport function ImagesIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m22 11-1.296-1.296a2.4 2.4 0 0 0-3.408 0L11 16\" />\n <path d=\"M4 8a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2\" />\n <circle cx=\"13\" cy=\"7\" r=\"1\" fill=\"currentColor\" />\n <rect x=\"8\" y=\"2\" width=\"14\" height=\"14\" rx=\"2\" />\n </svg>\n );\n}\n\n// lucide: loader-circle (a.k.a. Loader2)\nexport function Loader2Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n );\n}\n\n// lucide: upload\nexport function UploadIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M12 3v12\" />\n <path d=\"m17 8-5-5-5 5\" />\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n </svg>\n );\n}\n\n// lucide: image-off\nexport function ImageOffIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" />\n <path d=\"M10.41 10.41a2 2 0 1 1-2.83-2.83\" />\n <line x1=\"13.5\" x2=\"6\" y1=\"13.5\" y2=\"21\" />\n <line x1=\"18\" x2=\"21\" y1=\"12\" y2=\"15\" />\n <path d=\"M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59\" />\n <path d=\"M21 15V5a2 2 0 0 0-2-2H9\" />\n </svg>\n );\n}\n\n// lucide: lock\nexport function LockIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n );\n}\n\n// lucide: pencil\nexport function PencilIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\" />\n <path d=\"m15 5 4 4\" />\n </svg>\n );\n}\n\n// lucide: play\nexport function PlayIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\" />\n </svg>\n );\n}\n\n// lucide: proportions\nexport function ProportionsIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"M12 9v11\" />\n <path d=\"M2 9h13a2 2 0 0 1 2 2v9\" />\n </svg>\n );\n}\n\n// lucide: refresh-cw\nexport function RefreshCwIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\n <path d=\"M21 3v5h-5\" />\n <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\n <path d=\"M8 16H3v5\" />\n </svg>\n );\n}\n\n// lucide: rotate-ccw\nexport function RotateCcwIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </svg>\n );\n}\n\n// lucide: sun-medium\nexport function SunMediumIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 3v1\" />\n <path d=\"M12 20v1\" />\n <path d=\"M3 12h1\" />\n <path d=\"M20 12h1\" />\n <path d=\"m18.364 5.636-.707.707\" />\n <path d=\"m6.343 17.657-.707.707\" />\n <path d=\"m5.636 5.636.707.707\" />\n <path d=\"m17.657 17.657.707.707\" />\n </svg>\n );\n}\n\n// lucide: timer\nexport function TimerIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <line x1=\"10\" x2=\"14\" y1=\"2\" y2=\"2\" />\n <line x1=\"12\" x2=\"15\" y1=\"14\" y2=\"11\" />\n <circle cx=\"12\" cy=\"14\" r=\"8\" />\n </svg>\n );\n}\n\n// lucide: trash-2\nexport function Trash2Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M10 11v6\" />\n <path d=\"M14 11v6\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" />\n <path d=\"M3 6h18\" />\n <path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </svg>\n );\n}\n\n// lucide: x\nexport function XIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\n// lucide: zap\nexport function ZapIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\" />\n </svg>\n );\n}\n\n// lucide: zap-off\nexport function ZapOffIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M10.513 4.856 13.12 2.17a.5.5 0 0 1 .86.46l-1.377 4.317\" />\n <path d=\"M15.656 10H20a1 1 0 0 1 .78 1.63l-1.72 1.773\" />\n <path d=\"M16.273 16.273 10.88 21.83a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14H4a1 1 0 0 1-.78-1.63l4.507-4.643\" />\n <path d=\"m2 2 20 20\" />\n </svg>\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 }[] | undefined;\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 {\n Loader2Icon,\n XIcon,\n} from \"./icons\";\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 <XIcon 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 <Loader2Icon 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 /** Fresh bytes for pixel editing. Persisted hosts should provide this\n * instead of asking the editor to borrow a cache-owned viewer URL. */\n resolveEditBlob?: () => Promise<Blob>;\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\nexport interface PreparedMediaEdit {\n /** URL handed to the editor. Revoke it only when `revoke` is true. */\n url: string;\n revoke: 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\ninterface MediaCacheState {\n cache: Map<string, CacheEntry>;\n listeners: Map<string, Set<() => void>>;\n}\n\n// The LRU survives viewer/filmstrip mount cycles by design — but it must\n// NOT live in module-level variables: this package builds `splitting: false`\n// in dual ESM/CJS format, so a host whose loaders pull both graphs (Next.js\n// RSC + Jest, for example) would instantiate TWO caches and silently split\n// resolutions from subscribers. State lives on `globalThis` under a\n// `Symbol.for` slot instead (the kit `confirm/opener.ts` exemplar). Behavior\n// is unchanged; never \"clean this up\" into module locals.\nconst STATE_SLOT = Symbol.for(\"ai-matrx.capture.media-cache-state\");\n\nfunction getState(): MediaCacheState {\n const holder = globalThis as Record<symbol, MediaCacheState | undefined>;\n let state = holder[STATE_SLOT];\n if (!state) {\n state = { cache: new Map(), listeners: new Map() };\n holder[STATE_SLOT] = state;\n }\n return state;\n}\n\nfunction notify(key: string): void {\n getState().listeners.get(key)?.forEach((fn) => fn());\n}\n\nfunction evictIfNeeded(): void {\n while (getState().cache.size > MAX_ENTRIES) {\n const oldest = getState().cache.keys().next().value;\n if (oldest === undefined) return;\n const entry = getState().cache.get(oldest);\n getState().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 getState().cache.delete(key);\n getState().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 = getState().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 getState().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 = getState().cache.get(key);\n getState().cache.delete(key);\n if (entry?.revoke && entry.url) URL.revokeObjectURL(entry.url);\n notify(key);\n}\n\n/**\n * Materialize an editor-private snapshot of a media item.\n *\n * Viewer URLs are deliberately long-lived cache entries. A host-owned blob URL\n * can nevertheless be revoked when the host's byte cache evicts it: an already\n * decoded viewer image remains visible, while mounting the same stale URL in a\n * second `<img>` fails. Editing therefore crosses an ownership boundary. It\n * re-resolves persisted media, copies the bytes, and gives the editor its own\n * object URL instead of borrowing the viewer's cached URL.\n */\nexport async function prepareMediaForEdit(\n item: CaptureMediaItem,\n): Promise<PreparedMediaEdit> {\n if (item.resolveEditBlob) {\n const blob = await item.resolveEditBlob();\n if (blob.size === 0) throw new Error(\"media snapshot was empty\");\n return { url: URL.createObjectURL(blob), revoke: true };\n }\n\n // Fresh captures already carry a host-owned object URL. Retain the old\n // direct path for those callers; persisted media must supply fresh bytes.\n const url = item.src ?? getMediaUrl(item);\n if (!url) throw new Error(\"media item has no editable source\");\n return { url, revoke: false };\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 = getState().listeners.get(key) ?? new Set();\n const fn = () => bump((n) => n + 1);\n set.add(fn);\n getState().listeners.set(key, set);\n return () => {\n set.delete(fn);\n if (set.size === 0) getState().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 // The #t media fragment forces iOS Safari to decode and PAINT the\n // first frame — without it a preload=\"metadata\" video tile renders\n // black (\"videos don't show a thumbnail\", real-phone 2026-08-30).\n src={`${url}#t=0.01`}\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 {\n ChevronLeftIcon,\n ChevronRightIcon,\n PencilIcon,\n PlayIcon,\n Trash2Icon,\n XIcon,\n} from \"./icons\";\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) | undefined;\n /** Offered on photo items with a renderable URL. */\n onEdit?: ((item: CaptureMediaItem) => void) | undefined;\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 // The ACTIVE slide's <video> element (tap-to-play toggle target).\n const activeVideoRef = useRef<HTMLVideoElement | null>(null);\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 // Audio keeps native controls, so it keeps its pointer events. Video does\n // NOT: the slide is a control-less tap-to-play surface precisely so the\n // stage owns every gesture and a video swipes exactly like a photo.\n if ((e.target as HTMLElement).closest(\"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 // No direction ever locked = a TAP. On a video slide that toggles\n // playback (the control-less player's one control).\n if (d.axis === null) {\n const video = activeVideoRef.current;\n if (video) {\n if (video.paused) void video.play().catch(() => {});\n else video.pause();\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 <XIcon 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 <PencilIcon 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 <Trash2Icon 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\n key={current.key}\n item={current}\n active\n videoRef={activeVideoRef}\n />\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 <ChevronLeftIcon 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 <ChevronRightIcon 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 videoRef,\n}: {\n item: CaptureMediaItem;\n active: boolean;\n /** Receives the ACTIVE slide's video element (the tap-to-play target). */\n videoRef?: React.MutableRefObject<HTMLVideoElement | null> | undefined;\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 <VideoSlide url={url} active={active} videoRef={videoRef} />;\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\n/**\n * The control-less video slide. Native `controls` made the whole slide a\n * pointer sink — a video could not be swiped like a photo, which is the one\n * behavior a pager must keep uniform. Instead: the STAGE owns every gesture\n * (this element is pointer-events-none), a tap toggles playback, a centre\n * glyph shows the paused state, and a thin bottom bar shows progress.\n */\nfunction VideoSlide({\n url,\n active,\n videoRef,\n}: {\n url: string;\n active: boolean;\n videoRef?: React.MutableRefObject<HTMLVideoElement | null> | undefined;\n}) {\n const [paused, setPaused] = useState(true);\n const [progress, setProgress] = useState(0);\n const localRef = useRef<HTMLVideoElement | null>(null);\n\n // Hand the ACTIVE element to the stage's tap handler; clear on unmount so\n // a swiped-away slide can never receive the next tap.\n useEffect(() => {\n if (!active || !videoRef) return;\n videoRef.current = localRef.current;\n return () => {\n if (videoRef.current === localRef.current) videoRef.current = null;\n };\n }, [active, videoRef]);\n\n return (\n <div className=\"pointer-events-none absolute inset-0\">\n <video\n ref={localRef}\n src={url}\n playsInline\n // Hidden neighbors buffer METADATA only — never whole videos.\n preload={active ? \"auto\" : \"metadata\"}\n muted={!active}\n loop={false}\n onPlay={() => setPaused(false)}\n onPause={() => setPaused(true)}\n onEnded={() => setPaused(true)}\n onTimeUpdate={(e) => {\n const v = e.currentTarget;\n setProgress(v.duration > 0 ? v.currentTime / v.duration : 0);\n }}\n className=\"absolute inset-0 h-full w-full object-contain\"\n />\n {active && paused && (\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"flex h-16 w-16 items-center justify-center rounded-full bg-black/45\">\n <PlayIcon className=\"ml-1 h-8 w-8 fill-white text-white\" />\n </span>\n </span>\n )}\n {active && !paused && (\n <span className=\"absolute inset-x-4 bottom-2 h-0.5 overflow-hidden rounded-full bg-white/25\">\n <span\n className=\"block h-full rounded-full bg-white\"\n style={{ width: `${Math.round(progress * 100)}%` }}\n />\n </span>\n )}\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 {\n CheckIcon,\n FlipHorizontal2Icon,\n RotateCcwIcon,\n XIcon,\n} from \"./icons\";\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 [imageReady, setImageReady] = useState(false);\n const [transforming, setTransforming] = useState(false);\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 setImageReady(false);\n setTransforming(false);\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 (\n !img ||\n !imageReady ||\n img.naturalWidth === 0 ||\n saving ||\n transforming\n ) {\n setEditError(\"The image is still loading — wait a moment and try again.\");\n return;\n }\n setTransforming(true);\n setEditError(null);\n try {\n const sourceX = Math.round(crop.x * img.naturalWidth);\n const sourceY = Math.round(crop.y * img.naturalHeight);\n const sourceWidth = Math.max(\n 1,\n Math.min(\n img.naturalWidth - sourceX,\n Math.round(crop.w * img.naturalWidth),\n ),\n );\n const sourceHeight = Math.max(\n 1,\n Math.min(\n img.naturalHeight - sourceY,\n Math.round(crop.h * img.naturalHeight),\n ),\n );\n const swap = op === \"rotate-left\";\n const canvas = document.createElement(\"canvas\");\n canvas.width = swap ? sourceHeight : sourceWidth;\n canvas.height = swap ? sourceWidth : sourceHeight;\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(\n img,\n sourceX,\n sourceY,\n sourceWidth,\n sourceHeight,\n -sourceWidth / 2,\n -sourceHeight / 2,\n sourceWidth,\n sourceHeight,\n );\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n setTransforming(false);\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 // The existing <img> element temporarily reports zero natural\n // dimensions while its new object URL decodes. Keep every pixel\n // operation unavailable until its onLoad proves the bitmap ready.\n setImageReady(false);\n setWorkingSrc(url);\n setCrop(FULL_CROP);\n setAspect(\"free\");\n setTransforming(false);\n setEditError(null);\n },\n \"image/jpeg\",\n 0.95,\n );\n } catch {\n setTransforming(false);\n setEditError(\"This image could not be edited on this device.\");\n }\n },\n [crop, imageReady, saving, transforming, revokeOwned],\n );\n\n const applyAspect = useCallback((preset: AspectPreset) => {\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) {\n setEditError(\"The image is still loading — wait a moment and try again.\");\n return;\n }\n setAspect(preset);\n const ratio = ASPECTS.find((a) => a.id === preset)?.ratio ?? null;\n if (ratio === null) 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 || !imageReady || img.naturalWidth === 0) {\n setEditError(\"The image is still loading — wait a moment and try again.\");\n return;\n }\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, imageReady, 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 <XIcon 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 || transforming || !imageReady}\n aria-label=\"Save edited image\"\n aria-busy={saving || transforming || !imageReady}\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 <CheckIcon className=\"h-5 w-5\" />\n {saving\n ? \"Saving…\"\n : transforming || !imageReady\n ? \"Loading…\"\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 onLoad={() => setImageReady(true)}\n onError={() => {\n setImageReady(false);\n setTransforming(false);\n setEditError(\"This image could not be loaded on this device.\");\n }}\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 disabled={saving || transforming || !imageReady}\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 disabled={saving || transforming || !imageReady}\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 <RotateCcwIcon className=\"h-5 w-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => bake(\"flip\")}\n disabled={saving || transforming || !imageReady}\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 <FlipHorizontal2Icon className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CameraCaptureV3 — the vertical-rail camera chrome.\n *\n * A SECOND chrome over the same ports as `CameraCapture`, not a replacement.\n * Same engine, same cloud port, same package-owned review loop (filmstrip →\n * viewer → editor). What differs is the layout and the input model, and every\n * difference answers something the v2 chrome got wrong on a real phone\n * (Arman, 2026-08-30):\n *\n * ONE SHUTTER. Tap = photo, press-and-hold = video. v2 spent a whole\n * row on a mode selector, so the user paid bar height AND\n * a decision before the moment they were trying to catch.\n * THE RIGHT EDGE. Options live on a rail over the feed instead of a\n * two-tap grid behind a button. No layout cost, one tap\n * to anything, and the viewfinder stays whole.\n * IT COLLAPSES. Extras hide behind a chevron so the idle rail is short.\n * TEXT IS A BUTTON. Entry expands from a pill (see CaptureExpandingField) —\n * no permanent field, no keyboard-bait over the frame.\n * ONE MEDIA DOOR. There is no UPLOAD mode. The library button is the only\n * way to existing media, and picking files is an option\n * INSIDE that drawer — the host renders it there.\n *\n * Deliberately NOT copied from the app this borrows its shape from: fixed\n * duration choices (they belong to that product's format, not to a camera),\n * and the oversized promo pill for a new feature (chrome is not a billboard).\n *\n * Package source (`@ai-matrx/capture`). UI state only — capture, streams and\n * persistence come from the injected engine.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n Grid3x3Icon,\n ImagesIcon,\n ProportionsIcon,\n RefreshCwIcon,\n SunMediumIcon,\n TimerIcon,\n XIcon,\n ZapIcon,\n ZapOffIcon,\n} from \"./icons\";\nimport { cn } from \"../cn\";\nimport { safeTop, safeBottom } from \"../safe-area\";\n\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraV3Slots,\n CaptureCloudPort,\n CaptureRailAction,\n CaptureTimerSetting,\n} from \"../types\";\nimport { CaptureRail } from \"./CaptureRail\";\nimport { CaptureSheet, type CaptureSheetAction } from \"./CaptureSheet\";\nimport { CaptureFilmstrip } from \"./CaptureFilmstrip\";\nimport { CountdownOverlay } from \"./CountdownOverlay\";\nimport { GridOverlay } from \"./GridOverlay\";\nimport { HoldShutter } from \"./HoldShutter\";\nimport { ImageEditSheet } from \"./ImageEditSheet\";\nimport { MediaViewer } from \"./MediaViewer\";\nimport { invalidateMedia, prepareMediaForEdit } from \"../media/media-cache\";\nimport { useTrackControls } from \"../hooks/useTrackControls\";\nimport type { CaptureMediaSession } from \"./CameraCapture\";\n\nconst ASPECT_CYCLE: CaptureAspect[] = [\"full\", \"4:3\", \"1:1\", \"16:9\"];\n\nexport interface CameraCaptureV3Props {\n engine: CaptureCameraEngine;\n /** THE CLOUD LAW: still required. v3 changes where the door is, not whether\n * there is one — and in v3 it is the ONLY door to existing media. */\n cloud: CaptureCloudPort;\n preview: React.ReactNode;\n media?: CaptureMediaSession;\n /**\n * Fired on pointer-DOWN, before the hold threshold decides photo vs video.\n *\n * 🚨 THIS EXISTS FOR iOS. Hosts warm the microphone when they believe a\n * recording is coming (one permission prompt per medium). With a mode row\n * that signal arrived when the user chose VIDEO — early. With a hold\n * shutter there is no such moment, so waiting for the recording to actually\n * start means warming DURING the take, which on iOS Safari costs the first\n * second of audio or throws a prompt over the viewfinder mid-recording.\n *\n * The host is expected to make this idempotent: it fires on every press,\n * including the taps that turn out to be photos.\n */\n onRecordIntent?: () => void;\n /** Ring completes here; presentational only — it never stops the recorder. */\n maxRecordSeconds?: number;\n onReviewOpenChange?: (open: boolean) => void;\n onClose?: () => void;\n blockedSheet?: { body: React.ReactNode; actions: CaptureSheetAction[] };\n /** Hide all chrome except status chips (host renders its own toggle). */\n controlsHidden?: boolean;\n shutterDisabled?: boolean;\n slots?: CaptureCameraV3Slots;\n}\n\nexport function CameraCaptureV3({\n engine,\n cloud,\n preview,\n media,\n onRecordIntent,\n maxRecordSeconds,\n onReviewOpenChange,\n onClose,\n blockedSheet,\n controlsHidden = false,\n shutterDisabled = false,\n slots = {},\n}: CameraCaptureV3Props) {\n const [viewerKey, setViewerKey] = useState<string | null>(null);\n const [editTarget, setEditTarget] = useState<{\n key: string;\n url: string;\n revoke: boolean;\n } | null>(null);\n const [editPreparing, setEditPreparing] = useState(false);\n const [editPreparationError, setEditPreparationError] = useState<string | null>(\n null,\n );\n const [gridOn, setGridOn] = useState(false);\n const [timerSetting, setTimerSetting] = useState<CaptureTimerSetting>(0);\n const [aspect, setAspect] = useState<CaptureAspect>(\"full\");\n const [countdown, setCountdown] = useState<number | null>(null);\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 useEffect(\n () => () => {\n if (editTarget?.revoke) URL.revokeObjectURL(editTarget.url);\n },\n [editTarget],\n );\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 countdown must never survive into a recording — it would fire a photo\n // capture mid-take (the v2 bug, kept fixed here).\n useEffect(() => {\n if (engine.recording) clearCountdown();\n }, [engine.recording, clearCountdown]);\n\n const onPhoto = useCallback(() => {\n if (countdown !== null) {\n clearCountdown(); // tapping mid-countdown cancels\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 }, [aspect, clearCountdown, countdown, engine, timerSetting]);\n\n const blocked = engine.blocked !== null;\n\n // ── The rail ───────────────────────────────────────────────────────────\n // Primary (always visible): flip and flash — the two a user reaches for\n // without thinking. Everything else collapses.\n const coreRail: CaptureRailAction[] = [\n ...(engine.onFlipCamera\n ? [\n {\n id: \"flip\",\n label: \"Switch camera\",\n icon: <RefreshCwIcon className=\"h-[22px] w-[22px]\" />,\n primary: true,\n onPress: engine.onFlipCamera,\n },\n ]\n : []),\n ...(controls.torchSupported\n ? [\n {\n id: \"flash\",\n label: \"Flash\",\n icon: controls.torchOn ? (\n <ZapIcon className=\"h-[22px] w-[22px]\" fill=\"currentColor\" />\n ) : (\n <ZapOffIcon className=\"h-[22px] w-[22px]\" />\n ),\n active: controls.torchOn,\n primary: true,\n onPress: controls.toggleTorch,\n },\n ]\n : []),\n {\n id: \"timer\",\n label: \"Timer\",\n icon: <TimerIcon className=\"h-[22px] w-[22px]\" />,\n active: timerSetting !== 0,\n valueLabel: timerSetting === 0 ? undefined : `${timerSetting}s`,\n onPress: () => setTimerSetting((t) => (t === 0 ? 3 : t === 3 ? 10 : 0)),\n },\n {\n id: \"grid\",\n label: \"Grid\",\n icon: <Grid3x3Icon className=\"h-[22px] w-[22px]\" />,\n active: gridOn,\n onPress: () => setGridOn((g) => !g),\n },\n {\n id: \"aspect\",\n label: \"Aspect\",\n icon: <ProportionsIcon className=\"h-[22px] w-[22px]\" />,\n active: aspect !== \"full\",\n valueLabel: aspect === \"full\" ? undefined : aspect,\n onPress: () =>\n setAspect(\n (a) =>\n ASPECT_CYCLE[(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: <SunMediumIcon className=\"h-[22px] w-[22px]\" />,\n active: controls.exposure !== 0,\n valueLabel:\n controls.exposure === 0\n ? undefined\n : `${controls.exposure > 0 ? \"+\" : \"\"}${controls.exposure}`,\n onPress: () => controls.setExposure(controls.exposure === 0 ? 1 : 0),\n },\n ]\n : []),\n ];\n // A blocked camera disables the CORE actions (they steer hardware that is\n // not there) but the rail itself stays: host actions keep working through\n // the upload lane.\n const railActions = [\n ...coreRail.map((a) => (blocked ? { ...a, disabled: true } : a)),\n ...(slots.railActions ?? []),\n ];\n\n return (\n <div className=\"absolute inset-0 select-none overflow-hidden bg-black\">\n <div className=\"absolute inset-0\">{preview}</div>\n <GridOverlay visible={gridOn && !blocked} />\n\n {/* Aspect framing hint — the dimmed bands approximate the region the\n photo will discard. Never shown while recording: video is not\n cropped, so the bands would be a lie about the take in progress. */}\n {aspect !== \"full\" && !blocked && !engine.recording && (\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 row: no bar. Controls float over the feed so the viewfinder\n keeps its full height — the single biggest v2 complaint. ── */}\n {!controlsHidden && (\n <div\n className=\"pointer-events-none absolute inset-x-0 top-0 z-20 px-2\"\n style={safeTop}\n >\n <div className=\"flex min-h-[44px] items-center gap-2 py-1.5\">\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close camera\"\n className=\"pointer-events-auto flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full text-white drop-shadow-[0_1px_3px_rgba(0,0,0,0.6)] transition-colors hover:bg-white/10\"\n >\n <XIcon className=\"h-6 w-6\" />\n </button>\n ) : (\n <span className=\"h-10 w-10 shrink-0\" />\n )}\n\n {/* The expanding entry sits centre-top, where the eye already is\n and where a keyboard rising will not cover it. */}\n <div className=\"pointer-events-auto flex min-w-0 flex-1 justify-center\">\n {slots.topEntry}\n </div>\n\n <span className=\"h-10 w-10 shrink-0\" />\n </div>\n\n {slots.topCenter ? (\n <div className=\"pointer-events-none pb-1 text-center\">\n {slots.topCenter}\n </div>\n ) : null}\n </div>\n )}\n\n {/* Honesty chips stay visible even with controls hidden — they are the\n camera telling the truth, not chrome. */}\n {slots.statusChips ? (\n <div\n className=\"pointer-events-none absolute inset-x-0 z-20 flex flex-col items-center gap-1.5 px-3\"\n style={{\n // Clears the top row AND the topCenter line beneath it — chips\n // rendered at the label's own height read as a collision.\n top: `calc(env(safe-area-inset-top, 0px) + ${\n controlsHidden ? 12 : slots.topCenter ? 92 : 64\n }px)`,\n }}\n >\n {slots.statusChips}\n </div>\n ) : null}\n\n {/* ── The right rail. Rendered even when the camera is BLOCKED: host\n actions (notes, QR, process) are not camera controls and keep\n working through the upload lane — only the core camera actions\n disable (they are built with `disabled: blocked` above). ── */}\n {!controlsHidden && (\n <div\n className=\"pointer-events-none absolute right-1 z-20 flex justify-end\"\n style={{ top: `calc(env(safe-area-inset-top, 0px) + 64px)` }}\n >\n <CaptureRail actions={railActions} />\n </div>\n )}\n\n {/* ── Bottom: filmstrip over the feed, then the shutter row ── */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 bottom-0 z-20\" style={safeBottom}>\n {/* The session's captures, over the feed — same package-owned strip\n v2 renders, so the review loop is identical across chromes. */}\n {media && media.items.length > 0 ? (\n <div className=\"px-2 pb-1.5\">\n <CaptureFilmstrip\n items={media.items}\n onOpen={(item) => setViewerKey(item.key)}\n />\n </div>\n ) : null}\n {slots.aboveShutter ? (\n <div className=\"px-2 pb-1.5\">{slots.aboveShutter}</div>\n ) : null}\n\n <div className=\"flex items-center justify-between px-3 pb-2\">\n {/* ONE door to existing media. There is no upload lane here by\n design — the host's library drawer carries the \"pick files\"\n option, so a user has one place to look instead of two\n controls that both mean \"media I already have\". */}\n <div className=\"flex w-[86px] justify-start\">\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Your media — library and upload\"\n className=\"relative h-12 w-12 touch-manipulation overflow-hidden rounded-xl bg-black/40 ring-1 ring-white/30 backdrop-blur-md transition-transform active:scale-95\"\n >\n {cloud.recentsThumb ?? (\n <span className=\"flex h-full w-full items-center justify-center\">\n <ImagesIcon className=\"h-5 w-5 text-white/80\" />\n </span>\n )}\n </button>\n </div>\n\n <HoldShutter\n recording={engine.recording}\n elapsedSeconds={engine.recordElapsedSeconds}\n {...(maxRecordSeconds !== undefined\n ? { maxSeconds: maxRecordSeconds }\n : {})}\n disabled={shutterDisabled || blocked}\n {...(onRecordIntent ? { onPressStart: onRecordIntent } : {})}\n onPhoto={onPhoto}\n onStartRecording={engine.onStartRecording}\n onStopRecording={engine.onStopRecording}\n />\n\n <div className=\"flex w-[86px] justify-end\">\n {slots.shutterTrailing}\n </div>\n </div>\n\n {/* The gesture, stated once. A single-button camera is only obvious\n to the person who built it. */}\n {!engine.recording && !blocked ? (\n <p className=\"pb-1.5 text-center text-[11px] text-white/60 drop-shadow-[0_1px_2px_rgba(0,0,0,0.8)]\">\n Tap for a photo · hold to record\n </p>\n ) : null}\n </div>\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 {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 if (editPreparing) return;\n setEditPreparing(true);\n setEditPreparationError(null);\n void prepareMediaForEdit(item)\n .then((prepared) =>\n setEditTarget({ key: item.key, ...prepared }),\n )\n .catch(() =>\n setEditPreparationError(\n \"This photo could not be prepared for editing. Try again.\",\n ),\n )\n .finally(() => setEditPreparing(false));\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 {editPreparationError && (\n <p\n role=\"alert\"\n className=\"fixed inset-x-4 bottom-24 z-[70] rounded-xl bg-black/85 px-4 py-3 text-center text-sm text-[#FF6961]\"\n >\n {editPreparationError}\n </p>\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CaptureRail — the vertical action rail down the RIGHT edge of the frame.\n *\n * Why the edge and not a bar: a bottom bar steals height from the viewfinder\n * on every phone, and the options grid it fed cost two taps to reach anything.\n * A rail sits over the feed, costs no layout, and puts every option one tap\n * away under the thumb that is already holding the phone.\n *\n * Why it collapses: a complete rail is a long rail, and a long rail is a wall\n * of icons nobody reads. Actions marked `primary` stay; the rest hide behind a\n * chevron. The chevron is only rendered when there is something to hide —\n * a control that toggles nothing is a lie about the interface.\n *\n * Presentational only.\n */\n\nimport React, { useState } from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureRailAction } from \"../types\";\n\nexport interface CaptureRailProps {\n actions: CaptureRailAction[];\n /** Start expanded. Default false — an idle camera shows the short rail. */\n defaultExpanded?: boolean;\n className?: string;\n}\n\nexport function CaptureRail({\n actions,\n defaultExpanded = false,\n className,\n}: CaptureRailProps) {\n const [expanded, setExpanded] = useState(defaultExpanded);\n\n const primary = actions.filter((a) => a.primary);\n const extra = actions.filter((a) => !a.primary);\n // With nothing marked primary, the whole rail is always-on rather than\n // collapsing to nothing — a rail that can hide everything reads as broken.\n const shown = primary.length === 0 ? actions : expanded ? actions : primary;\n const collapsible = primary.length > 0 && extra.length > 0;\n\n if (actions.length === 0) return null;\n\n return (\n <div\n className={cn(\n \"pointer-events-auto flex flex-col items-center gap-1\",\n className,\n )}\n >\n {shown.map((action) => (\n <RailButton key={action.id} action={action} />\n ))}\n\n {collapsible ? (\n <button\n type=\"button\"\n onClick={() => setExpanded((open) => !open)}\n aria-expanded={expanded}\n aria-label={expanded ? \"Fewer options\" : \"More options\"}\n className={cn(\n \"mt-0.5 flex h-9 w-9 touch-manipulation items-center justify-center\",\n \"rounded-full text-white/90 transition-colors hover:bg-white/10\",\n )}\n >\n <Chevron\n className={cn(\n \"h-5 w-5 transition-transform duration-200\",\n expanded && \"rotate-180\",\n )}\n />\n </button>\n ) : null}\n </div>\n );\n}\n\nfunction RailButton({ action }: { action: CaptureRailAction }) {\n return (\n <button\n type=\"button\"\n onClick={action.onPress}\n disabled={action.disabled}\n aria-label={action.label}\n aria-pressed={action.active ?? undefined}\n className={cn(\n \"flex w-12 shrink-0 touch-manipulation flex-col items-center justify-center gap-0.5\",\n \"rounded-2xl py-1.5 transition-colors\",\n action.active ? \"text-[#FFCC00]\" : \"text-white\",\n action.disabled ? \"opacity-40\" : \"hover:bg-white/10 active:bg-white/15\",\n )}\n >\n {/* The icon carries a shadow rather than a plate: a chip per action turns\n the rail into a stack of boxes over the picture the user is framing. */}\n <span className=\"drop-shadow-[0_1px_3px_rgba(0,0,0,0.6)]\">\n {action.icon}\n </span>\n {action.valueLabel ? (\n <span className=\"text-[10px] font-semibold leading-none tabular-nums drop-shadow-[0_1px_2px_rgba(0,0,0,0.7)]\">\n {action.valueLabel}\n </span>\n ) : null}\n </button>\n );\n}\n\n/** Inlined so the package keeps zero icon dependencies. */\nfunction Chevron({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n );\n}\n","\"use client\";\n\n/**\n * HoldShutter — ONE button for photo and video.\n *\n * Tap takes a photo. Press and hold records, and releasing stops. The mode is\n * therefore a gesture, not a control you must set before the moment you are\n * trying to catch — which is the whole reason the v2 mode row was wrong on a\n * phone: it cost bar height AND a decision, and the decision was usually made\n * after the thing had happened.\n *\n * The interaction, precisely:\n *\n * pointerdown → arm a hold timer (HOLD_MS)\n * timer fires → start recording, ring begins filling\n * pointerup < HOLD_MS → cancel the timer, take a photo\n * pointerup >= HOLD_MS → stop recording (recording follows the finger)\n * slide up ≥ LOCK_DRAG_PX while holding → LOCK: release keeps recording,\n * the next tap stops\n * pointercancel / leave → same as pointerup (a drag off the button must not\n * leave the camera recording with nothing to stop it)\n *\n * A LOCK is a GESTURE, never a timer (Arman, 2026-08-30: recording follows\n * the finger — releasing stops, full stop — unless the user deliberately\n * locks): while holding a recording, slide UP to the lock target that\n * appears above the button. Locked, release keeps recording and the next\n * tap stops. An automatic time-based latch shipped first and was wrong —\n * it silently converted \"I let go\" into \"still recording\".\n *\n * Presentational only — the host owns the recorder.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n LockIcon,\n} from \"./icons\";\nimport { cn } from \"../cn\";\n\n/** Hold this long before a press becomes a recording. */\nconst HOLD_MS = 320;\n/** Slide the held finger this far UP to lock the recording. */\nconst LOCK_DRAG_PX = 64;\n\nexport interface HoldShutterProps {\n recording: boolean;\n /** Elapsed recording seconds, for the ring + read-out. */\n elapsedSeconds: number;\n /**\n * Ring completes at this many seconds. Presentational only — it does not\n * stop the recorder, because a chrome that silently ends a take is worse\n * than one that shows a full ring. Omit for no progress arc.\n */\n maxSeconds?: number;\n disabled?: boolean;\n /**\n * Fired on pointer-DOWN, before the hold threshold decides photo vs video.\n * Hosts use it to warm the microphone — see `CameraCaptureV3Props`.\n */\n onPressStart?: () => void;\n onPhoto: () => void;\n onStartRecording: () => void;\n onStopRecording: () => void;\n}\n\nexport function HoldShutter({\n recording,\n elapsedSeconds,\n maxSeconds,\n disabled = false,\n onPressStart,\n onPhoto,\n onStartRecording,\n onStopRecording,\n}: HoldShutterProps) {\n const holdTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const startedRecording = useRef(false);\n const pressOriginY = useRef(0);\n const [pressed, setPressed] = useState(false);\n const [locked, setLocked] = useState(false);\n\n const clearHold = useCallback(() => {\n if (holdTimer.current !== null) {\n clearTimeout(holdTimer.current);\n holdTimer.current = null;\n }\n }, []);\n\n // An externally-stopped recording (mode change, error, host abort) must\n // always unlatch.\n useEffect(() => {\n if (!recording) setLocked(false);\n }, [recording]);\n\n useEffect(() => clearHold, [clearHold]);\n\n const onPointerDown = useCallback(\n (event: React.PointerEvent<HTMLButtonElement>) => {\n if (disabled) return;\n // Keep receiving move/up even if the finger slides off the button.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n pressOriginY.current = event.clientY;\n setPressed(true);\n onPressStart?.();\n\n // Locked: this press is a STOP, decided on release so a tap-and-hold\n // does not stop and immediately restart.\n if (recording && locked) return;\n\n startedRecording.current = false;\n holdTimer.current = setTimeout(() => {\n holdTimer.current = null;\n startedRecording.current = true;\n onStartRecording();\n }, HOLD_MS);\n },\n [disabled, locked, onPressStart, onStartRecording, recording],\n );\n\n // Slide-to-lock: while THIS press is recording, dragging up past the lock\n // target latches the take.\n const onPointerMove = useCallback(\n (event: React.PointerEvent<HTMLButtonElement>) => {\n if (!pressed || locked || !startedRecording.current) return;\n if (pressOriginY.current - event.clientY >= LOCK_DRAG_PX) {\n setLocked(true);\n navigator.vibrate?.(30);\n }\n },\n [pressed, locked],\n );\n\n const endPress = useCallback(() => {\n if (!pressed) return;\n setPressed(false);\n const wasHold = startedRecording.current;\n clearHold();\n startedRecording.current = false;\n\n if (recording && locked && !wasHold) {\n onStopRecording();\n return;\n }\n if (wasHold) {\n // Recording follows the finger: release stops — unless the user slid\n // up to the lock during this press.\n if (!locked) onStopRecording();\n return;\n }\n if (!recording) onPhoto();\n }, [clearHold, locked, onPhoto, onStopRecording, pressed, recording]);\n\n const progress =\n maxSeconds && maxSeconds > 0\n ? Math.min(1, elapsedSeconds / maxSeconds)\n : 0;\n\n // 74px button, 3.5px ring — the arc rides just outside the border.\n const R = 39;\n const C = 2 * Math.PI * R;\n\n return (\n <div className=\"relative flex h-[86px] w-[86px] shrink-0 items-center justify-center\">\n {recording && maxSeconds ? (\n <svg\n className=\"pointer-events-none absolute inset-0 -rotate-90\"\n viewBox=\"0 0 86 86\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"43\"\n cy=\"43\"\n r={R}\n fill=\"none\"\n stroke=\"rgba(255,255,255,0.25)\"\n strokeWidth=\"4\"\n />\n <circle\n cx=\"43\"\n cy=\"43\"\n r={R}\n fill=\"none\"\n stroke=\"#FF3B30\"\n strokeWidth=\"4\"\n strokeLinecap=\"round\"\n strokeDasharray={C}\n strokeDashoffset={C * (1 - progress)}\n />\n </svg>\n ) : null}\n\n {/* The lock target — appears above the button while an UNLOCKED\n recording is held; sliding the finger up to it latches the take. */}\n {recording && pressed && !locked && (\n <span\n className=\"pointer-events-none absolute left-1/2 flex -translate-x-1/2 flex-col items-center gap-0.5\"\n style={{ top: -LOCK_DRAG_PX - 22 }}\n aria-hidden\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/60 ring-1 ring-white/30\">\n <LockIcon className=\"h-4 w-4 text-white/90\" />\n </span>\n <span className=\"text-[10px] font-medium text-white/80 drop-shadow\">\n Slide up to lock\n </span>\n </span>\n )}\n {recording && locked && (\n <span\n className=\"pointer-events-none absolute left-1/2 flex h-9 w-9 -translate-x-1/2 items-center justify-center rounded-full bg-[#FF3B30]\"\n style={{ top: -LOCK_DRAG_PX - 22 }}\n aria-hidden\n >\n <LockIcon className=\"h-4 w-4 text-white\" fill=\"currentColor\" />\n </span>\n )}\n\n <button\n type=\"button\"\n disabled={disabled}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endPress}\n onPointerCancel={endPress}\n onLostPointerCapture={endPress}\n // The press IS the gesture; a click would fire a second photo after\n // pointerup on desktop.\n onContextMenu={(e) => e.preventDefault()}\n aria-label={\n recording\n ? locked\n ? \"Stop recording\"\n : \"Recording — release to stop\"\n : \"Tap for a photo, hold to record\"\n }\n className={cn(\n \"group flex h-[74px] w-[74px] shrink-0 touch-none select-none items-center justify-center\",\n \"rounded-full 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\",\n recording\n ? \"h-7 w-7 rounded-[6px] bg-[#FF3B30]\"\n : cn(\n \"h-[62px] w-[62px] rounded-full bg-white\",\n pressed && \"scale-90\",\n ),\n )}\n />\n </button>\n\n {recording ? (\n <span\n className={cn(\n \"pointer-events-none absolute -bottom-1 left-1/2 -translate-x-1/2 translate-y-full\",\n \"rounded-full bg-black/60 px-2 py-0.5 text-[11px] font-medium tabular-nums text-white\",\n )}\n >\n {formatElapsed(elapsedSeconds)}\n {locked ? \" · tap to stop\" : \"\"}\n </span>\n ) : null}\n </div>\n );\n}\n\nfunction formatElapsed(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = String(Math.floor(totalSeconds % 60)).padStart(2, \"0\");\n return `${m}:${s}`;\n}\n","\"use client\";\n\n/**\n * CaptureExpandingField — a pill BUTTON that becomes a text field on press,\n * and goes back to being a button when it is done.\n *\n * 🚨 WHY IT IS NOT AN INPUT (Arman, 2026-08-30). v2 pinned a live `<input>` in\n * the bottom bar. On a camera that is wrong three ways: it occupies a row\n * permanently for something used occasionally, it invites the keyboard over\n * the viewfinder on an accidental tap, and it makes an idle camera look like a\n * form. The pattern that fits is the one Apple uses for search — a compact\n * control that admits it is a control, and grows only when you mean it.\n *\n * COMMIT ON UNMOUNT, always. The v2 input already learned this the hard way:\n * hiding the controls or switching item remounts the field, `onBlur` never\n * fires, and a typed value evaporates. Anything typed is committed on the way\n * out, whatever caused the exit.\n *\n * Presentational + local draft state only; the host owns what a value means.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface CaptureExpandingFieldProps {\n /** Collapsed label, e.g. \"Serial / tag\". Also the accessible name. */\n label: string;\n /** Placeholder once expanded. Defaults to `label`. */\n placeholder?: string;\n /** Icon shown in the collapsed pill and at the field's leading edge. */\n icon?: React.ReactNode;\n /** Commit a non-empty trimmed value. Called on Enter, blur, and unmount. */\n onCommit: (value: string) => void;\n /** Start expanded (a host that knows entry is imminent). */\n defaultExpanded?: boolean;\n disabled?: boolean;\n /** Shown in the collapsed pill instead of `label` — e.g. the current tag. */\n currentValue?: string | null;\n className?: string;\n}\n\nexport function CaptureExpandingField({\n label,\n placeholder,\n icon,\n onCommit,\n defaultExpanded = false,\n disabled = false,\n currentValue = null,\n className,\n}: CaptureExpandingFieldProps) {\n const [expanded, setExpanded] = useState(defaultExpanded);\n const [draft, setDraft] = useState(\"\");\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n // Refs so the unmount commit reads the LATEST draft without re-running the\n // effect (which would fire the cleanup on every keystroke).\n const draftRef = useRef(draft);\n draftRef.current = draft;\n const onCommitRef = useRef(onCommit);\n onCommitRef.current = onCommit;\n\n useEffect(() => {\n return () => {\n const trimmed = draftRef.current.trim();\n if (trimmed) onCommitRef.current(trimmed);\n };\n }, []);\n\n const commit = useCallback(() => {\n const trimmed = draftRef.current.trim();\n if (trimmed) onCommitRef.current(trimmed);\n setDraft(\"\");\n }, []);\n\n const open = useCallback(() => {\n setExpanded(true);\n // Focus after paint so the keyboard rises with the field, not before it.\n requestAnimationFrame(() => inputRef.current?.focus());\n }, []);\n\n const close = useCallback(() => {\n commit();\n setExpanded(false);\n }, [commit]);\n\n if (!expanded) {\n return (\n <button\n type=\"button\"\n onClick={open}\n disabled={disabled}\n aria-label={label}\n aria-expanded={false}\n className={cn(\n \"flex h-9 max-w-[62vw] items-center gap-1.5 rounded-full px-3.5\",\n \"bg-black/45 text-[13px] font-semibold text-white backdrop-blur-md\",\n \"transition-colors hover:bg-black/60 active:bg-black/70\",\n disabled && \"opacity-40\",\n className,\n )}\n >\n {icon ? <span className=\"shrink-0\">{icon}</span> : null}\n <span className=\"truncate\">{currentValue?.trim() || label}</span>\n </button>\n );\n }\n\n return (\n <div\n className={cn(\n \"flex h-9 w-[min(78vw,320px)] items-center gap-1.5 rounded-full px-3\",\n \"bg-black/60 backdrop-blur-md\",\n className,\n )}\n >\n {icon ? <span className=\"shrink-0 text-white/70\">{icon}</span> : null}\n <input\n ref={inputRef}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={close}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n commit();\n (e.target as HTMLInputElement).blur();\n }\n if (e.key === \"Escape\") {\n setDraft(\"\");\n setExpanded(false);\n }\n }}\n placeholder={placeholder ?? label}\n aria-label={label}\n enterKeyHint=\"done\"\n autoCapitalize=\"characters\"\n autoCorrect=\"off\"\n spellCheck={false}\n // 16px floor: anything smaller makes iOS zoom the whole page on focus\n // and it never zooms back out.\n className={cn(\n \"min-w-0 flex-1 bg-transparent text-base text-white\",\n \"placeholder:text-white/40 focus:outline-none\",\n )}\n />\n <button\n type=\"button\"\n // onMouseDown, not onClick: the input's blur would otherwise fire\n // first, collapse the field, and unmount this button mid-click.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setDraft(\"\");\n setExpanded(false);\n }}\n aria-label={`Close ${label}`}\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-white/70 hover:bg-white/10 hover:text-white\"\n >\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n className=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\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 production camera engine, so a\n * host with no media runtime gets a fully wired camera from a few lines:\n *\n * const engine = useDefaultCaptureEngine({ onPhoto, onVideo, onFiles, mode });\n * <CameraCapture engine={engine} preview={<CameraFeed engine={engine} />} … />\n *\n * Hosts with a cross-feature media runtime (AI Matrx injects its\n * lease-managed runtime) pass their own `CaptureCameraEngine` instead — the\n * injection seam remains the mechanism; this is the standard implementation\n * of it (THE ALL-INCLUSIVE LAW, C23).\n *\n * getUserMedia lives ONLY under `src/engine/` (the laws test bans it\n * everywhere else). What this engine now carries — the hard parts that used\n * to live host-side (C22 retrofit, 2026-08-30, ported from matrx-frontend's\n * proven camera runtime):\n *\n * - PERMISSION PRE-CHECK: a known-denied camera permission reports\n * `blocked: \"permission-denied\"` WITHOUT another getUserMedia call —\n * never hammer a rejection the browser already told us about.\n * - COMBINED CAMERA+MIC PROMPT: when audio is wanted and a mic prompt would\n * actually appear, ONE `getUserMedia({video, audio})` browser prompt covers\n * both permissions (instead of a camera prompt at open plus a mic prompt\n * mid-action — the classic double-prompt annoyance; on iOS Safari it recurs\n * every session). The audio tracks are banked as the warm-mic manager's\n * stream; a combined-call denial is retried ONCE video-only, so a mic\n * denial never takes the camera down (bounded — never a fallback loop).\n * - MIC WARM HOLD: while the chrome is in video mode, a warm-mic hold keeps\n * the OS grant alive so iOS Safari prompts at most once per medium per\n * session (`warm-mic.ts` carries the four recovery branches).\n * - REAL DEVICE FLIP: cycles actual `videoinput` deviceIds (not a blind\n * facingMode toggle), hidden while recording — a flip reacquires the\n * stream, which would kill the take (`onFlipCamera` goes null, and the\n * handler backstops it).\n * - TRACK HEALTH: a camera track that ENDS (OS interruption, device pulled)\n * is reported loudly and reacquired once instead of leaving a frozen frame.\n * - MONOTONIC RECORDING CLOCK, one-capture-at-a-time shutter guard, a 120 ms\n * `flash` flag, and loud shutter failures through the `onError` sink.\n *\n * Photo path: full-sensor canvas capture (JPEG q0.92) then the ONE\n * center-crop implementation (`crop.ts`) — never a screenshot of the cropped\n * preview. Upload lane: a package-created hidden <input type=\"file\"> (no\n * `capture` attribute — that is what opens the OS gallery, not the camera).\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n} from \"../types\";\nimport { cropBlobToAspect, nextCameraDevice } from \"./crop\";\nimport {\n classifyCameraBlockReason,\n queryCameraPermission,\n queryMicPermission,\n shouldCombineMicPrompt,\n} from \"./permissions\";\nimport {\n acquireWarmMic,\n adoptWarmAudioStream,\n buildWarmMicConstraints,\n releaseWarmMic,\n} from \"./warm-mic\";\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 (full sensor, aspect crop\n * already applied). */\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. Default true. With audio on, the\n * engine folds the mic permission into the CAMERA's browser prompt (one\n * combined prompt) whenever a mic prompt would actually appear. */\n withAudio?: boolean;\n /** The chrome's current mode. In \"video\" the engine warm-holds the mic so\n * iOS Safari prompts at most once per medium per session. Omitting it\n * skips the warm hold (the record path still acquires the mic). */\n mode?: CaptureCameraMode;\n facingMode?: \"environment\" | \"user\";\n photoQuality?: number;\n /** Filename prefix for captures (default \"capture\"). */\n fileNamePrefix?: string;\n /** Loud failure sink (a toast, a logger). Default: console.error. A tapped\n * shutter that produced nothing must say so — never a silent drop. */\n onError?: (message: string, err: unknown) => void;\n}\n\n/** The engine plus the extras the default path owns (white shutter flash). */\nexport interface DefaultCaptureEngine extends CaptureCameraEngine {\n /** White shutter-flash flag (120 ms), for the host's flash overlay. */\n flash: boolean;\n /** Take a photo with a non-default filename prefix (e.g. delineators). */\n capturePhotoWith: (opts: {\n fileNamePrefix: string;\n aspect?: CaptureAspect;\n }) => void;\n}\n\nexport function useDefaultCaptureEngine(\n options: DefaultEngineOptions,\n): DefaultCaptureEngine {\n const {\n onPhoto,\n onVideo,\n onFiles,\n withAudio = true,\n mode,\n facingMode: initialFacing = \"environment\",\n photoQuality = 0.92,\n fileNamePrefix = \"capture\",\n onError,\n } = options;\n\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [blocked, setBlocked] = useState<CaptureCameraEngine[\"blocked\"]>(null);\n const [deviceId, setDeviceId] = useState<string | null>(null);\n const [cameras, setCameras] = useState<MediaDeviceInfo[]>([]);\n const [recording, setRecording] = useState(false);\n const [recordElapsedSeconds, setRecordElapsedSeconds] = useState(0);\n const [flash, setFlash] = useState(false);\n // Bumped to force one reacquire after a hard track end (OS interruption).\n const [acquireToken, setAcquireToken] = 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 micClones: MediaStreamTrack[];\n micHeld: boolean;\n timer: ReturnType<typeof setInterval>;\n } | null>(null);\n const captureBusyRef = useRef(false);\n const reacquiredOnceRef = useRef(false);\n const callbacksRef = useRef({ onPhoto, onVideo, onFiles, onError });\n callbacksRef.current = { onPhoto, onVideo, onFiles, onError };\n\n const reportError = useCallback((message: string, err: unknown) => {\n const sink = callbacksRef.current.onError;\n if (sink) sink(message, err);\n else console.error(`[capture:engine] ${message}`, err);\n }, []);\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\n void (async () => {\n // Pre-check: a known-denied permission renders the blocked state\n // without hammering getUserMedia (it would instantly reject anyway).\n const known = await queryCameraPermission();\n if (cancelled) return;\n if (known === \"denied\") {\n setBlocked({ reason: \"permission-denied\" });\n return;\n }\n\n // Combined prompt decision: fold the mic into the SAME getUserMedia\n // call only when a mic prompt would actually appear — \"granted\" needs\n // none, and \"denied\" audio would fail the whole combined call.\n const includeMic =\n withAudio && shouldCombineMicPrompt(await queryMicPermission());\n if (cancelled) return;\n\n const video: MediaTrackConstraints = {\n width: { ideal: 4096 },\n height: { ideal: 4096 },\n ...(deviceId ? { deviceId: { exact: deviceId } } : { facingMode: initialFacing }),\n };\n\n const acquire = async (mic: boolean): Promise<MediaStream> => {\n try {\n return await navigator.mediaDevices.getUserMedia(\n mic\n ? { video, audio: buildWarmMicConstraints() }\n : { video, audio: false },\n );\n } catch (err) {\n if (mic && classifyCameraBlockReason(err) === \"permission-denied\") {\n // Ambiguous denial of the combined call — retry ONCE video-only:\n // if the camera half succeeds, the denial was the mic's. A mic\n // denial must never take the camera down. (Bounded single retry\n // on the denial path, never a constraint-fallback loop.)\n return navigator.mediaDevices.getUserMedia({ video, audio: false });\n }\n throw err;\n }\n };\n\n try {\n const s = await acquire(includeMic);\n if (cancelled) {\n s.getTracks().forEach((t) => t.stop());\n return;\n }\n // Bank the mic half of a combined grant in the warm-mic manager so\n // the first recording reuses it with no second prompt (the mic light\n // clears on the manager's keepalive schedule).\n const audioTracks = s.getAudioTracks();\n if (audioTracks.length > 0) {\n for (const t of audioTracks) s.removeTrack(t);\n adoptWarmAudioStream(new MediaStream(audioTracks));\n }\n acquired = s;\n streamRef.current = s;\n setStream(s);\n setBlocked(null);\n reacquiredOnceRef.current = false;\n\n // Track health: a camera track that ENDS mid-session (OS\n // interruption, device pulled, another app grabbed it) must never\n // leave a frozen frame. Reacquire ONCE; a second death reports\n // blocked.\n for (const track of s.getVideoTracks()) {\n track.onended = () => {\n if (cancelled || streamRef.current !== s) return;\n console.error(\n \"[capture:engine] camera track ENDED (OS interruption / device change) — reacquiring.\",\n );\n if (reacquiredOnceRef.current) {\n setBlocked({ reason: \"not-supported\" });\n return;\n }\n reacquiredOnceRef.current = true;\n setAcquireToken((n) => n + 1);\n };\n }\n\n // Learn whether a flip target exists (labels need a granted state —\n // we just acquired, so this is the right moment).\n const devices = await navigator.mediaDevices.enumerateDevices();\n if (!cancelled) {\n setCameras(devices.filter((d) => d.kind === \"videoinput\"));\n }\n } catch (err: unknown) {\n if (cancelled) return;\n setBlocked({ reason: classifyCameraBlockReason(err) });\n }\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 }, [deviceId, initialFacing, withAudio, acquireToken]);\n\n const cameraBlocked = blocked !== null;\n\n // ── Video-mode mic warm hold (one prompt per medium on iOS Safari) ───────\n useEffect(() => {\n if (mode !== \"video\" || cameraBlocked || !withAudio) return;\n let cancelled = false;\n let held = false;\n void (async () => {\n if ((await queryMicPermission()) === \"denied\") return;\n if (cancelled) return;\n acquireWarmMic()\n .then(() => {\n if (cancelled) {\n releaseWarmMic();\n return;\n }\n held = true;\n })\n .catch(() => {\n // Denied/unavailable — the record path surfaces its own error.\n });\n })();\n return () => {\n cancelled = true;\n if (held) releaseWarmMic();\n };\n }, [mode, cameraBlocked, withAudio]);\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 }\n };\n }, []);\n\n // ── Photo ────────────────────────────────────────────────────────────────\n const capturePhotoWith = useCallback(\n (opts: { fileNamePrefix: string; aspect?: CaptureAspect }) => {\n const video = videoRef.current;\n if (!video || video.videoWidth === 0) return;\n // One capture at a time: a double-tap on the shutter must not produce\n // two near-identical frames.\n if (captureBusyRef.current) return;\n captureBusyRef.current = true;\n void (async () => {\n try {\n // Full-sensor shutter behind the cropped preview (§2 policy 6);\n // the aspect crop (when selected) is applied to that full frame.\n const canvas = document.createElement(\"canvas\");\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"2d canvas context unavailable\");\n ctx.drawImage(video, 0, 0);\n const full = await new Promise<Blob>((resolve, reject) =>\n canvas.toBlob(\n (b) => (b ? resolve(b) : reject(new Error(\"toBlob returned null\"))),\n \"image/jpeg\",\n photoQuality,\n ),\n );\n const blob = await cropBlobToAspect(\n full,\n opts.aspect ?? \"full\",\n photoQuality,\n );\n callbacksRef.current.onPhoto(\n new File(\n [blob],\n `${opts.fileNamePrefix}-${new Date().toISOString()}.jpg`,\n { type: \"image/jpeg\" },\n ),\n );\n setFlash(true);\n window.setTimeout(() => setFlash(false), 120);\n } catch (err) {\n reportError(\"The photo could not be captured — try again.\", err);\n } finally {\n captureBusyRef.current = false;\n }\n })();\n },\n [photoQuality, reportError],\n );\n\n const onCapturePhoto = useCallback(\n (opts?: { aspect?: CaptureAspect }) => {\n capturePhotoWith(\n opts?.aspect\n ? { fileNamePrefix, aspect: opts.aspect }\n : { fileNamePrefix },\n );\n },\n [capturePhotoWith, fileNamePrefix],\n );\n\n // ── Video ────────────────────────────────────────────────────────────────\n const onStartRecording = useCallback(() => {\n const base = streamRef.current;\n if (!base || recorderRef.current) return;\n void (async () => {\n // Mic discipline (the proven runtime contract): acquire the WARM mic\n // once, CLONE its track into the composed stream, stop only the clones\n // on exit, and release the hold exactly once. Stopping the shared\n // track would kill the warm grant for every other holder.\n let micClones: MediaStreamTrack[] = [];\n let micHeld = false;\n if (withAudio) {\n try {\n const mic = await acquireWarmMic();\n micHeld = true;\n micClones = mic.getAudioTracks().map((t) => t.clone());\n } catch {\n // Mic denied/unavailable — record video-only rather than failing\n // the capture.\n }\n }\n const composed = new MediaStream([...base.getVideoTracks(), ...micClones]);\n const mime = RECORDING_MIME_LADDER.find(\n (m) =>\n typeof MediaRecorder !== \"undefined\" && MediaRecorder.isTypeSupported(m),\n );\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(\n composed,\n mime ? { mimeType: mime } : undefined,\n );\n } catch (err) {\n micClones.forEach((t) => t.stop());\n if (micHeld) releaseWarmMic();\n reportError(\"Could not start the video recording.\", err);\n return;\n }\n const entry = {\n recorder,\n chunks: [] as Blob[],\n startedAt: performance.now(),\n micClones,\n micHeld,\n timer: setInterval(() => {\n // Monotonic elapsed clock — performance.now(), never chunk arrival.\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.micClones.forEach((t) => t.stop());\n if (entry.micHeld) releaseWarmMic();\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], `${fileNamePrefix}-video-${Date.now()}.${ext}`, {\n type,\n }),\n durationMs,\n );\n };\n recorder.start(1000);\n setRecordElapsedSeconds(0);\n setRecording(true);\n })();\n }, [withAudio, fileNamePrefix, reportError]);\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 // ── Flip: cycle real devices, never mid-recording ────────────────────────\n const onFlipCamera = useCallback(() => {\n // A flip reacquires the stream, which would kill a live recording. The\n // button is hidden while recording; this guard backstops it.\n if (recorderRef.current) return;\n const current =\n deviceId ??\n streamRef.current?.getVideoTracks()[0]?.getSettings().deviceId ??\n null;\n const next = nextCameraDevice(cameras, current);\n if (next) setDeviceId(next.deviceId);\n }, [cameras, deviceId]);\n\n const canFlip = cameras.length > 1 && !cameraBlocked && !recording;\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: canFlip ? onFlipCamera : null,\n flash,\n capturePhotoWith,\n }),\n [\n stream,\n blocked,\n onCapturePhoto,\n onStartRecording,\n onStopRecording,\n recording,\n recordElapsedSeconds,\n onUpload,\n onFlipCamera,\n canFlip,\n flash,\n capturePhotoWith,\n ],\n );\n}\n","/**\n * src/engine/crop.ts — the ONE aspect-crop implementation.\n *\n * Center-crops a captured JPEG to the requested output aspect. The crop is\n * applied to the FULL sensor frame (honest pixels, not a preview grab) —\n * §2 policy 6 of the chrome: the shutter always captures full-sensor and the\n * selected aspect is a REAL crop of that frame. Ported from the matrx-frontend\n * host adapter (C22 retrofit, 2026-08-30) so every host shares one geometry.\n */\n\nimport type { CaptureAspect } from \"../types\";\n\nexport const PHOTO_JPEG_QUALITY = 0.92;\n\nexport async function cropBlobToAspect(\n blob: Blob,\n aspect: CaptureAspect,\n quality: number = PHOTO_JPEG_QUALITY,\n): Promise<Blob> {\n if (aspect === \"full\") return blob;\n const [wRatio, hRatio] =\n aspect === \"1:1\" ? [1, 1] : aspect === \"4:3\" ? [4, 3] : [16, 9];\n const bitmap = await createImageBitmap(blob);\n try {\n const srcW = bitmap.width;\n const srcH = bitmap.height;\n // Orient the target ratio to the frame (portrait sensor → portrait crop).\n const target = srcW >= srcH ? wRatio / hRatio : hRatio / wRatio;\n let cropW = srcW;\n let cropH = Math.round(srcW / target);\n if (cropH > srcH) {\n cropH = srcH;\n cropW = Math.round(srcH * target);\n }\n if (cropW === srcW && cropH === srcH) return blob;\n const canvas = document.createElement(\"canvas\");\n canvas.width = cropW;\n canvas.height = cropH;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return blob;\n ctx.drawImage(\n bitmap,\n Math.round((srcW - cropW) / 2),\n Math.round((srcH - cropH) / 2),\n cropW,\n cropH,\n 0,\n 0,\n cropW,\n cropH,\n );\n return await new Promise<Blob>((resolve) =>\n canvas.toBlob((out) => resolve(out ?? blob), \"image/jpeg\", quality),\n );\n } finally {\n bitmap.close();\n }\n}\n\n/**\n * Cycle to the next camera in a device list — the flip algorithm the host\n * adapter and the default engine share. Returns null when there is nothing\n * to flip to (fewer than two cameras).\n */\nexport function nextCameraDevice<T extends { deviceId: string }>(\n cameras: readonly T[],\n currentDeviceId: string | null | undefined,\n): T | null {\n if (cameras.length < 2) return null;\n const currentIdx = currentDeviceId\n ? cameras.findIndex((c) => c.deviceId === currentDeviceId)\n : -1;\n return cameras[(Math.max(currentIdx, 0) + 1) % cameras.length] ?? null;\n}\n","/**\n * src/engine/permissions.ts — permission pre-checks + error classification\n * for the default engine (and for hosts, so they stop re-implementing the\n * NotAllowedError branch — the C22 rule: quirk branches live HERE).\n */\n\nexport type MediaPermissionState = \"granted\" | \"denied\" | \"prompt\" | \"unknown\";\n\nasync function queryPermission(name: string): Promise<MediaPermissionState> {\n if (typeof navigator === \"undefined\" || !navigator.permissions) {\n return \"unknown\";\n }\n try {\n const status = await navigator.permissions.query({\n name: name as PermissionName,\n });\n if (status.state === \"granted\") return \"granted\";\n if (status.state === \"denied\") return \"denied\";\n return \"prompt\";\n } catch {\n // Permissions API doesn't support this name here (Safari) — unknown, so\n // callers fall back to the real getUserMedia outcome.\n return \"unknown\";\n }\n}\n\n/**\n * Pre-check the camera permission WITHOUT prompting. A known-\"denied\" state\n * lets a surface render its how-to-re-enable explainer instead of hammering\n * getUserMedia with calls that instantly reject (the proven matrx-frontend\n * pre-check contract). \"unknown\" (Safari) means: just try — the gUM outcome\n * is the truth there.\n */\nexport function queryCameraPermission(): Promise<MediaPermissionState> {\n return queryPermission(\"camera\");\n}\n\n/** Same pre-check for the microphone — drives the combined-prompt decision. */\nexport function queryMicPermission(): Promise<MediaPermissionState> {\n return queryPermission(\"microphone\");\n}\n\n/**\n * Pure decision for the combined camera+mic prompt: only worth it when a mic\n * prompt would actually appear (\"prompt\"/\"unknown\"). \"granted\" needs no\n * prompt; \"denied\" would reject the WHOLE combined call and take the camera\n * with it.\n */\nexport function shouldCombineMicPrompt(\n micPermissionState: MediaPermissionState,\n): boolean {\n return micPermissionState === \"prompt\" || micPermissionState === \"unknown\";\n}\n\n/** True when a getUserMedia rejection means the USER/OS denied permission\n * (a missing device is not a denial). */\nexport function isMediaDenialError(err: unknown): boolean {\n const name =\n err && typeof err === \"object\" && \"name\" in err\n ? String((err as { name: unknown }).name)\n : \"\";\n return name === \"NotAllowedError\" || name === \"SecurityError\";\n}\n\n/**\n * Classify a camera-acquisition failure into the engine's `blocked` reason.\n * The ONE place the NotAllowedError/SecurityError branch lives — hosts adapt\n * their runtime errors through this instead of re-implementing it.\n */\nexport function classifyCameraBlockReason(\n err: unknown,\n): \"permission-denied\" | \"not-supported\" {\n return isMediaDenialError(err) ? \"permission-denied\" : \"not-supported\";\n}\n","/**\n * src/engine/warm-mic.ts — the package's warm microphone manager.\n *\n * Ported VERBATIM (2026-08-30, the C22/C23 retrofit) from matrx-frontend's\n * proven `features/audio/micStream.ts` — the module that encodes the real\n * iOS Safari device pain the default engine previously did not have:\n *\n * WHY THIS EXISTS\n * ---------------\n * Every recording surface used to call `navigator.mediaDevices.getUserMedia`\n * itself and fully `track.stop()` the stream when it finished. On mobile\n * (notably iOS Safari) that produces a permission/\"in use\" prompt on *every*\n * single recording, because the previous grant is torn down before the next\n * acquisition. Tapping record three times = three prompts.\n *\n * This manager keeps ONE stream warm and hands it to every caller. After the\n * last holder releases it, the stream is kept alive for a SHORT keepalive\n * window; if another recording starts within that window, the same live\n * stream (and the same OS grant) is reused — no second prompt. Only after the\n * window elapses with no holders is the stream actually stopped (mic light\n * off), so we don't hold the mic open forever.\n *\n * KEEPALIVE IS DELIBERATELY SHORT (a few seconds). Minutes keep the browser's\n * recording indicator lit long after the user stopped — alarming and abnormal\n * (\"why is my mic still on?\"). A few seconds still coalesces a quick\n * stop→re-record (the iOS re-prompt case) while clearing the mic light almost\n * immediately once the user is actually done. On desktop, re-acquiring after\n * release does NOT re-prompt (the origin's grant persists), so a short window\n * costs nothing there.\n *\n * THE FOUR iOS-SAFARI RECOVERY BRANCHES (`MicInterruptionReason`), each of\n * which used to be a silent audio drop:\n * 1. `ended` — the OS killed the track (lock screen, incoming\n * call, app switch, device unplug). The warm grant\n * is gone; the dead stream is dropped so the next\n * acquire re-acquires (and WILL re-prompt).\n * 2. `muted` — transient interruption (iOS mutes during a\n * call). The grant survives; no re-prompt.\n * 3. `unmuted` — the transient interruption ended.\n * 4. `permission-revoked` — the user/OS pulled the permission; the warm\n * stream is hard-stopped NOW.\n * All four are reported loudly on the interruption channel so a recording\n * surface can react instead of just dropping audio.\n *\n * Reference-counted: concurrent holders (an analyser tap + a recorder) are\n * fine; the stream is only eligible for release when the count hits zero.\n * Callers MUST NOT call `track.stop()` on the returned stream — call\n * `releaseWarmMic()` instead.\n *\n * State lives on `globalThis` under a `Symbol.for` slot (the media-cache\n * pattern) — a dual ESM/CJS module graph must still see ONE mic manager.\n */\n\ntype Listener = (state: MicStreamState) => void;\n\nexport type MicInterruptionReason =\n | \"ended\"\n | \"muted\"\n | \"unmuted\"\n | \"permission-revoked\";\n\ntype InterruptionListener = (reason: MicInterruptionReason) => void;\n\nexport type MicStreamState =\n | \"idle\"\n | \"acquiring\"\n | \"active\"\n | \"keepalive\"\n | \"error\";\n\nconst DEFAULT_KEEPALIVE_MS = 6_000; // a few seconds — clear the mic light promptly\n\ninterface ManagerInternal {\n stream: MediaStream | null;\n inFlight: Promise<MediaStream> | null;\n refCount: number;\n releaseTimer: ReturnType<typeof setTimeout> | null;\n state: MicStreamState;\n keepAliveMs: number;\n /** The host's chosen input device, applied as an `{ideal}` constraint on\n * the NEXT acquire. null = system default. */\n preferredInputDeviceId: string | null;\n listeners: Set<Listener>;\n interruptionListeners: Set<InterruptionListener>;\n pageLifecycleWatched: boolean;\n permissionWatched: boolean;\n}\n\nconst STATE_SLOT = Symbol.for(\"ai-matrx.capture.warm-mic-state\");\n\nfunction getManager(): ManagerInternal {\n const holder = globalThis as Record<symbol, ManagerInternal | undefined>;\n let m = holder[STATE_SLOT];\n if (!m) {\n m = {\n stream: null,\n inFlight: null,\n refCount: 0,\n releaseTimer: null,\n state: \"idle\",\n keepAliveMs: DEFAULT_KEEPALIVE_MS,\n preferredInputDeviceId: null,\n listeners: new Set(),\n interruptionListeners: new Set(),\n pageLifecycleWatched: false,\n permissionWatched: false,\n };\n holder[STATE_SLOT] = m;\n }\n return m;\n}\n\nfunction emitInterruption(reason: MicInterruptionReason): void {\n for (const l of getManager().interruptionListeners) {\n try {\n l(reason);\n } catch {\n // never let a listener break the manager\n }\n }\n}\n\n/**\n * Subscribe to mic interruptions (track end / mute / permission loss).\n * Returns an unsubscribe fn. Surfaces are expected to make these LOUD — an\n * interruption during a recording is a real event the user must see, not a\n * silent drop.\n */\nexport function subscribeWarmMicInterruption(\n listener: InterruptionListener,\n): () => void {\n const m = getManager();\n m.interruptionListeners.add(listener);\n return () => {\n m.interruptionListeners.delete(listener);\n };\n}\n\nfunction setState(next: MicStreamState): void {\n const m = getManager();\n m.state = next;\n for (const l of m.listeners) {\n try {\n l(next);\n } catch {\n // never let a listener break the manager\n }\n }\n}\n\nfunction clearReleaseTimer(): void {\n const m = getManager();\n if (m.releaseTimer) {\n clearTimeout(m.releaseTimer);\n m.releaseTimer = null;\n }\n}\n\n/**\n * Watch the live tracks for OS-level interruptions. iOS fires these on lock,\n * incoming calls, app switches, and device changes — previously unhandled, so\n * the warm stream silently died and the next recording re-prompted with no\n * explanation. Each is reported loudly; on a hard `ended` the dead warm\n * stream is dropped so the next `acquireWarmMic` re-acquires a live one.\n */\nfunction attachTrackHealth(stream: MediaStream): void {\n const m = getManager();\n for (const track of stream.getAudioTracks()) {\n track.onended = () => {\n console.error(\n \"[capture:warm-mic] mic track ENDED (OS interruption / device change). \" +\n \"The warm grant is gone; the next recording will re-prompt.\",\n );\n if (m.stream === stream) {\n m.stream = null;\n setState(\"error\");\n }\n emitInterruption(\"ended\");\n };\n track.onmute = () => {\n console.warn(\n \"[capture:warm-mic] mic track MUTED (transient interruption — e.g. a call). \" +\n \"The grant survives; it should unmute when the interruption ends.\",\n );\n emitInterruption(\"muted\");\n };\n track.onunmute = () => {\n emitInterruption(\"unmuted\");\n };\n }\n}\n\n/**\n * Mic-permission revocation → stop the warm stream NOW and emit a loud\n * interruption. In matrx-frontend the ONE canonical permission watcher\n * (audioDevices) calls this; the package, having no host device layer, wires\n * its own minimal Permissions API watcher lazily (`watchMicPermission`) —\n * best-effort only, since Safari's Permissions API may not answer for\n * \"microphone\" (there, `ended` is the branch that fires instead).\n */\nexport function notifyMicPermissionRevoked(): void {\n console.error(\"[capture:warm-mic] microphone permission REVOKED.\");\n hardStopWarmMic();\n emitInterruption(\"permission-revoked\");\n}\n\nfunction watchMicPermission(): void {\n const m = getManager();\n if (m.permissionWatched) return;\n if (typeof navigator === \"undefined\" || !navigator.permissions) return;\n m.permissionWatched = true;\n void (async () => {\n try {\n const status = await navigator.permissions.query({\n name: \"microphone\" as PermissionName,\n });\n status.onchange = () => {\n if (status.state === \"denied\") notifyMicPermissionRevoked();\n };\n } catch {\n // Permissions API doesn't support \"microphone\" here (Safari) — the\n // track-health `ended` branch covers revocation there.\n }\n })();\n}\n\n// Page-lifecycle backstop — set up once. The browser's \"in use\" mic indicator\n// must NEVER survive the page. `pagehide` fires on tab close / navigation /\n// bfcache evict, so the warm stream is hard-stopped there even if a holder\n// leaked its release. This is a SECOND layer under the per-holder\n// release/keepalive — it firing means a release was missed (a real bug), so\n// it screams.\nfunction watchPageLifecycle(): void {\n const m = getManager();\n if (m.pageLifecycleWatched) return;\n if (typeof window === \"undefined\") return;\n m.pageLifecycleWatched = true;\n window.addEventListener(\"pagehide\", () => {\n if (m.stream || m.refCount > 0) {\n if (m.refCount > 0) {\n console.error(\n `[capture:warm-mic] page hiding with ${m.refCount} unreleased mic holder(s) — ` +\n \"forcing release. A surface leaked acquireWarmMic without a \" +\n \"matching releaseWarmMic on unmount.\",\n );\n }\n hardStopWarmMic();\n }\n });\n}\n\n/**\n * Set the preferred microphone. Applied as an `{ideal}` deviceId constraint\n * on the NEXT acquire — it does NOT switch a live stream (call\n * `hardStopWarmMic()` then re-acquire to switch mid-session). Pass null to\n * fall back to the system default.\n */\nexport function setPreferredMicDeviceId(id: string | null): void {\n getManager().preferredInputDeviceId = id;\n}\n\n/** True if the warm stream still has at least one live audio track. */\nfunction streamIsLive(stream: MediaStream | null): stream is MediaStream {\n if (!stream) return false;\n const tracks = stream.getAudioTracks();\n return tracks.length > 0 && tracks.every((t) => t.readyState === \"live\");\n}\n\n/**\n * The default audio constraints this manager acquires with — exported so the\n * default engine's COMBINED `getUserMedia({video, audio})` prompt (one\n * browser prompt for both permissions instead of camera-then-mic) requests\n * the mic with exactly the same processing + preferred-device shape the\n * singleton itself would use.\n */\nexport function buildWarmMicConstraints(): MediaTrackConstraints {\n const m = getManager();\n const audio: MediaTrackConstraints = {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n };\n if (m.preferredInputDeviceId) {\n audio.deviceId = { ideal: m.preferredInputDeviceId };\n }\n return audio;\n}\n\n/**\n * Adopt an audio stream acquired ELSEWHERE (the default engine's combined\n * camera+mic prompt) as this manager's warm stream, so the grant that\n * combined prompt just earned is banked exactly like one of our own: the\n * next `acquireWarmMic()` inside the keepalive window reuses it with no\n * second prompt. If a warm stream already exists (or one is being acquired),\n * the incoming tracks are stopped immediately — never two live mic streams.\n * With zero holders the adopted stream is parked in the keepalive window, so\n * the mic light clears on the normal schedule.\n */\nexport function adoptWarmAudioStream(stream: MediaStream): void {\n const m = getManager();\n if (streamIsLive(m.stream) || m.inFlight) {\n for (const t of stream.getTracks()) {\n try {\n t.stop();\n } catch {\n // ignore\n }\n }\n return;\n }\n if (!streamIsLive(stream)) return; // dead on arrival — nothing to bank\n clearReleaseTimer();\n m.stream = stream;\n attachTrackHealth(stream);\n watchPageLifecycle();\n watchMicPermission();\n if (m.refCount > 0) {\n setState(\"active\");\n } else {\n armKeepaliveStop();\n }\n}\n\n/**\n * Acquire the shared warm mic stream, incrementing the holder count. The\n * returned stream is shared — DO NOT stop its tracks. Call `releaseWarmMic()`\n * when done. If a warm stream exists (active or in its keepalive window) it\n * is reused with no new permission prompt.\n */\nexport async function acquireWarmMic(\n constraints?: MediaTrackConstraints,\n): Promise<MediaStream> {\n const m = getManager();\n clearReleaseTimer();\n m.refCount += 1;\n\n // Reuse a still-live warm stream — the whole point of this manager.\n if (streamIsLive(m.stream)) {\n setState(\"active\");\n return m.stream;\n }\n\n // A previous warm stream died (device unplugged, OS revoke) — drop it.\n m.stream = null;\n\n if (m.inFlight) {\n // Coalesce concurrent acquisitions onto the same getUserMedia call.\n return m.inFlight;\n }\n\n setState(\"acquiring\");\n const audio: MediaTrackConstraints = constraints ?? buildWarmMicConstraints();\n if (m.preferredInputDeviceId && audio.deviceId == null) {\n audio.deviceId = { ideal: m.preferredInputDeviceId };\n }\n\n m.inFlight = (async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio });\n m.stream = stream;\n attachTrackHealth(stream);\n watchPageLifecycle();\n watchMicPermission();\n // Every holder released while gUM was in flight → nobody owns this\n // fresh stream. Without this, the manager would sit \"active\" with zero\n // holders and zero timers FOREVER (mic light on until refresh). Park it\n // in the keepalive window instead so it stops like any last release.\n if (m.refCount === 0) {\n armKeepaliveStop();\n return stream;\n }\n setState(\"active\");\n return stream;\n } catch (err) {\n // Acquisition failed — this holder doesn't actually hold anything.\n m.refCount = Math.max(0, m.refCount - 1);\n setState(\"error\");\n throw err;\n } finally {\n m.inFlight = null;\n }\n })();\n\n return m.inFlight;\n}\n\n/**\n * Release a previously-acquired hold. When the last holder releases, the\n * stream is kept warm for the keepalive window, then actually stopped.\n */\nexport function releaseWarmMic(): void {\n const m = getManager();\n if (m.refCount === 0) {\n // LOUD: an unbalanced release is never harmless — it either means a leak\n // elsewhere went uncounted, or (worse) it will steal the hold of the NEXT\n // acquirer, killing a live recording seconds in. Scream, don't floor.\n console.error(\n \"[capture:warm-mic] releaseWarmMic() with zero holders — unbalanced release. \" +\n \"Some caller released twice (or released a hold it never acquired).\",\n );\n return;\n }\n m.refCount -= 1;\n if (m.refCount > 0) return;\n\n if (!m.stream) {\n // Nothing live yet. If an acquisition is still in flight, its resolution\n // sees refCount === 0 and parks the fresh stream in keepalive → stop.\n setState(m.inFlight ? \"keepalive\" : \"idle\");\n return;\n }\n\n armKeepaliveStop();\n}\n\n/** Park the stream in the keepalive window; hard-stop when it elapses with\n * no holders. The single exit used by both the normal last-release path and\n * the released-while-acquiring path. */\nfunction armKeepaliveStop(): void {\n const m = getManager();\n clearReleaseTimer();\n setState(\"keepalive\");\n m.releaseTimer = setTimeout(() => {\n m.releaseTimer = null;\n // Re-check: a holder may have re-acquired during the window.\n if (m.refCount > 0) return;\n hardStopWarmMic();\n }, m.keepAliveMs);\n}\n\n/**\n * Immediately stop the warm stream regardless of keepalive. Use only when\n * the mic must be released NOW; normal teardown uses `releaseWarmMic()`.\n */\nexport function hardStopWarmMic(): void {\n const m = getManager();\n clearReleaseTimer();\n if (m.stream) {\n for (const t of m.stream.getTracks()) {\n try {\n t.stop();\n } catch {\n // ignore\n }\n }\n m.stream = null;\n }\n m.refCount = 0;\n setState(\"idle\");\n}\n\nexport function getWarmMicState(): MicStreamState {\n return getManager().state;\n}\n\nexport function subscribeWarmMic(listener: Listener): () => void {\n const m = getManager();\n m.listeners.add(listener);\n return () => {\n m.listeners.delete(listener);\n };\n}\n\n/** Diagnostics snapshot for debug panels. */\nexport function warmMicDebug(): {\n state: MicStreamState;\n refCount: number;\n live: boolean;\n keepAliveMs: number;\n} {\n const m = getManager();\n return {\n state: m.state,\n refCount: m.refCount,\n live: streamIsLive(m.stream),\n keepAliveMs: m.keepAliveMs,\n };\n}\n","\"use client\";\n\n/**\n * CloudLibrarySheet — the recents-thumb destination: where the iPhone opens\n * the camera roll, we open the user's CLOUD media in a full-screen tiled\n * gallery — dark chrome to match the camera, square tiles, newest first.\n *\n * Absorbed from the matrx-frontend host (C22/C23 retrofit, 2026-08-30):\n * the 206-line host sheet was pure chrome over three injectable facts — the\n * item list, the thumbnail renderer, and where a tile navigates. The chrome\n * now ships here; the host injects only data and identity:\n *\n * <CloudLibrarySheet\n * open={open} onClose={close}\n * loading={treeLoading}\n * items={files.map(f => ({ id, fileName, kind, thumbnail: <MyThumb/> }))}\n * onOpenItem={(id) => router.push(`/files/f/${id}`)}\n * onUpload={openPicker} // v3 hosts — upload lives INSIDE the library\n * />\n *\n * Every tile OPENS the real file (no dead ends), and an empty gallery whose\n * only affordance is \"close\" is a dead end too — with `onUpload` supplied,\n * the empty state offers the one thing a user can do from here.\n */\n\nimport React, { useEffect, useState } from \"react\";\nimport type { CaptureCloudLibraryItem } from \"../types\";\nimport { safeBottom, safeTop } from \"../safe-area\";\nimport {\n ImageOffIcon,\n Loader2Icon,\n PlayIcon,\n UploadIcon,\n XIcon,\n} from \"./icons\";\n\nexport interface CloudLibrarySheetProps {\n open: boolean;\n onClose: () => void;\n /** Newest-first media items. The host supplies the thumbnail node — the\n * sheet never fetches (law 1: no network in this package). */\n items: readonly CaptureCloudLibraryItem[];\n /** True while the host's library data is still loading. */\n loading: boolean;\n /** Opening a tile (host navigation) — a busy overlay renders while true. */\n busy?: boolean;\n /** A tile was tapped. The host opens the REAL file route (no dead ends). */\n onOpenItem: (id: string) => void;\n /**\n * Pick files from the device. Supplied by v3 hosts, where this drawer is\n * the ONLY door to existing media (Arman, 2026-08-30: \"we don't need both\n * upload and cloud because we can modify the drawer that has the cloud\n * images to just show an option for uploading\").\n *\n * Two controls that both mean \"media I already have\" is a choice the user\n * should never have been asked to make: whether a file happens to be in\n * the cloud yet is our bookkeeping, not their mental model. Omitted by v2\n * hosts, which still carry a separate UPLOAD lane in the mode row.\n */\n onUpload?: (() => void) | undefined;\n /** Sheet title. Default \"Your media\". */\n title?: string;\n}\n\n/** Tiles rendered per page — a whole cloud tree at once meant thousands of\n * mounted thumbnails and as many fetches. */\nconst PAGE_SIZE = 60;\n\nexport function CloudLibrarySheet({\n open,\n onClose,\n items,\n loading,\n busy = false,\n onOpenItem,\n onUpload,\n title = \"Your media\",\n}: CloudLibrarySheetProps) {\n const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);\n\n // Reopening starts back at the first page (fresh scroll position too).\n useEffect(() => {\n if (open) setVisibleCount(PAGE_SIZE);\n }, [open]);\n\n if (!open) return null;\n\n const uploadThenClose = onUpload\n ? () => {\n // Close first: the picker is a system sheet, and leaving the library\n // mounted behind it means the user lands back in a gallery that has\n // not yet heard about their new file.\n onClose();\n onUpload();\n }\n : null;\n\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col bg-black\">\n <div\n className=\"flex shrink-0 items-center justify-between bg-black/80 px-4\"\n style={safeTop}\n >\n <h2 className=\"py-4 text-[17px] font-semibold text-white\">{title}</h2>\n <div className=\"flex items-center gap-2\">\n {uploadThenClose ? (\n <button\n type=\"button\"\n onClick={uploadThenClose}\n className=\"flex h-10 touch-manipulation items-center gap-1.5 rounded-full bg-white/10 px-3.5 text-[13px] font-medium text-white transition-colors hover:bg-white/20\"\n >\n <UploadIcon className=\"h-4 w-4\" />\n Upload\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close library\"\n className=\"flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20\"\n >\n <XIcon className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n\n <div\n className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-1\"\n style={safeBottom}\n >\n {loading ? (\n <div className=\"flex h-full items-center justify-center\">\n <Loader2Icon className=\"h-6 w-6 animate-spin text-white/60\" />\n </div>\n ) : items.length === 0 ? (\n <div className=\"flex h-full flex-col items-center justify-center gap-3 text-white/60\">\n <ImageOffIcon className=\"h-8 w-8\" />\n <p className=\"text-sm\">No photos or videos in your cloud yet.</p>\n {/* An empty gallery whose only affordance is \"close\" is a dead\n end — the one thing a user can do from here is add something. */}\n {uploadThenClose ? (\n <button\n type=\"button\"\n onClick={uploadThenClose}\n className=\"flex h-11 touch-manipulation items-center gap-2 rounded-full bg-white/10 px-5 text-sm font-medium text-white transition-colors hover:bg-white/20\"\n >\n <UploadIcon className=\"h-4 w-4\" />\n Upload from this device\n </button>\n ) : null}\n </div>\n ) : (\n <div className=\"grid grid-cols-3 gap-0.5 sm:grid-cols-5 md:grid-cols-7\">\n {items.slice(0, visibleCount).map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => onOpenItem(item.id)}\n aria-label={`Open ${item.fileName}`}\n className=\"relative aspect-square overflow-hidden bg-white/5 transition-opacity active:opacity-70\"\n >\n {item.thumbnail}\n {item.kind === \"video\" && (\n <PlayIcon\n className=\"absolute bottom-1.5 left-1.5 h-4 w-4 text-white drop-shadow\"\n fill=\"currentColor\"\n />\n )}\n </button>\n ))}\n </div>\n )}\n {!loading && items.length > visibleCount && (\n <div className=\"flex justify-center py-4\">\n <button\n type=\"button\"\n onClick={() => setVisibleCount((n) => n + PAGE_SIZE)}\n className=\"h-11 touch-manipulation rounded-full bg-white/10 px-6 text-sm font-medium text-white\"\n >\n Show more ({items.length - visibleCount} left)\n </button>\n </div>\n )}\n {busy && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center bg-black/40\">\n <Loader2Icon className=\"h-6 w-6 animate-spin text-white\" />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","formatElapsed","import_jsx_runtime","ASPECT_CYCLE","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","STATE_SLOT","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/react.ts","../src/components/CameraCapture.tsx","../src/components/icons.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/CameraCaptureV3.tsx","../src/components/CaptureRail.tsx","../src/components/HoldShutter.tsx","../src/components/CaptureExpandingField.tsx","../src/components/CameraFeed.tsx","../src/engine/useDefaultEngine.ts","../src/engine/crop.ts","../src/engine/permissions.ts","../src/engine/warm-mic.ts","../src/engine/video-result.ts","../src/components/CloudLibrarySheet.tsx"],"sourcesContent":["/**\n * @ai-matrx/capture/react — the DOM chrome + the batteries-included engine\n * (all \"use client\").\n *\n * Drop-in (any React host, no media runtime required):\n *\n * const engine = useDefaultCaptureEngine({ onPhoto, onVideo, onFiles });\n * <CameraCapture\n * engine={engine}\n * preview={<CameraFeed engine={engine} />}\n * cloud={cloudPort}\n * media={{ items, onDelete, onReplacePhoto }}\n * mode={mode}\n * onModeChange={setMode}\n * />\n */\n\nexport { CameraCapture, type CameraCaptureProps, type CaptureMediaSession } from \"./components/CameraCapture\";\n// v3 — the vertical-rail chrome (one shutter, right rail, expanding entry).\nexport { CameraCaptureV3, type CameraCaptureV3Props } from \"./components/CameraCaptureV3\";\nexport { CaptureExpandingField, type CaptureExpandingFieldProps } from \"./components/CaptureExpandingField\";\nexport { CaptureRail, type CaptureRailProps } from \"./components/CaptureRail\";\nexport { HoldShutter, type HoldShutterProps } from \"./components/HoldShutter\";\nexport { CameraFeed } from \"./components/CameraFeed\";\nexport { CaptureFilmstrip, type CaptureFilmstripProps } from \"./components/CaptureFilmstrip\";\nexport { CaptureSheet, type CaptureSheetAction, type CaptureSheetProps } from \"./components/CaptureSheet\";\nexport { CountdownOverlay } from \"./components/CountdownOverlay\";\nexport { GridOverlay } from \"./components/GridOverlay\";\nexport { ImageEditSheet, type ImageEditSheetProps } from \"./components/ImageEditSheet\";\nexport { MediaViewer, type MediaViewerProps } from \"./components/MediaViewer\";\nexport { ModeSelector, type ModeSelectorProps } from \"./components/ModeSelector\";\nexport { OptionsGridPanel, type OptionsGridPanelProps } from \"./components/OptionsGridPanel\";\nexport { ShutterButton, type ShutterButtonProps } from \"./components/ShutterButton\";\nexport { ZoomRow, type ZoomRowProps } from \"./components/ZoomRow\";\nexport { useTrackControls, type TrackControls } from \"./hooks/useTrackControls\";\nexport {\n useDefaultCaptureEngine,\n type DefaultCaptureEngine,\n type DefaultEngineOptions,\n} from \"./engine/useDefaultEngine\";\n// The engine's hard parts, exported for host adapters too (C22: the quirk\n// branches live in the package; a host runtime adapter injects identity and\n// borrows THESE instead of re-implementing them).\nexport {\n cropBlobToAspect,\n nextCameraDevice,\n PHOTO_JPEG_QUALITY,\n} from \"./engine/crop\";\nexport {\n finalizeCapturedVideo,\n type CapturedVideoResult,\n type FinalizeCapturedVideoOptions,\n} from \"./engine/video-result\";\nexport {\n classifyCameraBlockReason,\n isMediaDenialError,\n queryCameraPermission,\n queryMicPermission,\n shouldCombineMicPrompt,\n type MediaPermissionState,\n} from \"./engine/permissions\";\nexport {\n acquireWarmMic,\n adoptWarmAudioStream,\n buildWarmMicConstraints,\n getWarmMicState,\n hardStopWarmMic,\n notifyMicPermissionRevoked,\n releaseWarmMic,\n setPreferredMicDeviceId,\n subscribeWarmMic,\n subscribeWarmMicInterruption,\n warmMicDebug,\n type MicInterruptionReason,\n type MicStreamState,\n} from \"./engine/warm-mic\";\nexport {\n CloudLibrarySheet,\n type CloudLibrarySheetProps,\n} from \"./components/CloudLibrarySheet\";\nexport {\n getMediaUrl,\n invalidateMedia,\n primeMedia,\n useMediaUrl,\n type CaptureMediaItem,\n type ResolvedMedia,\n} from \"./media/media-cache\";\nexport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCameraV3Slots,\n CaptureCloudLibraryItem,\n CaptureCloudPort,\n CaptureOptionTile,\n CaptureRailAction,\n CaptureTimerSetting,\n} from \"./types\";\n","\"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 Grid3x3Icon,\n GripIcon,\n ProportionsIcon,\n RefreshCwIcon,\n SunMediumIcon,\n TimerIcon,\n XIcon,\n ZapIcon,\n ZapOffIcon,\n} from \"./icons\";\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 invalidateMedia,\n prepareMediaForEdit,\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 revoke: boolean;\n } | null>(null);\n const [editPreparing, setEditPreparing] = useState(false);\n const [editPreparationError, setEditPreparationError] = useState<string | null>(\n null,\n );\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\n const openLibraryFromRecents = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n const target = event.target;\n if (\n target instanceof Element &&\n target.closest(\n \"button, a, input, select, textarea, [role='button'], [role='link']\",\n )\n ) {\n // The injected thumbnail can render an informative media fallback\n // with its own remedy controls. Let that control act without also\n // opening the library.\n return;\n }\n cloud.onOpenLibrary();\n },\n [cloud],\n );\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 useEffect(\n () => () => {\n if (editTarget?.revoke) URL.revokeObjectURL(editTarget.url);\n },\n [editTarget],\n );\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 <ZapIcon className=\"h-6 w-6\" fill=\"currentColor\" />\n ) : (\n <ZapOffIcon 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: <TimerIcon 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: <Grid3x3Icon 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: <ProportionsIcon 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: <SunMediumIcon 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 <XIcon 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 <ZapIcon\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 <GripIcon 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 <SunMediumIcon 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 <div\n onClick={openLibraryFromRecents}\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 </div>\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Open your media library\"\n className=\"sr-only\"\n >\n Open your media library\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 <RefreshCwIcon 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 if (editPreparing) return;\n setEditPreparing(true);\n setEditPreparationError(null);\n void prepareMediaForEdit(item)\n .then((prepared) =>\n setEditTarget({ key: item.key, ...prepared }),\n )\n .catch(() =>\n setEditPreparationError(\n \"This photo could not be prepared for editing. Try again.\",\n ),\n )\n .finally(() => setEditPreparing(false));\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 {editPreparationError && (\n <p\n role=\"alert\"\n className=\"fixed inset-x-4 bottom-24 z-[70] rounded-xl bg-black/85 px-4 py-3 text-center text-sm text-[#FF6961]\"\n >\n {editPreparationError}\n </p>\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","/**\n * src/components/icons.tsx — the package's OWN inlined SVG icons.\n *\n * Per campaign ruling C19 (2026-08-29): no icon-library dependency, ever.\n * Every glyph below is copied from lucide-react v0.545.0 (ISC license)\n * exactly — the version this package shipped with before the swap, so\n * pixels don't change — following the @ai-matrx/media precedent\n * (`media/src/react/icons.tsx`). Add a new icon by copying the lucide\n * `__iconNode` data from the version noted here — never by adding a\n * lucide dep back.\n *\n * Internal module: icons are exported for the package's own components\n * only; they are not part of the public entry surface.\n */\n\nimport type { SVGProps } from \"react\";\n\nexport type CaptureIconProps = SVGProps<SVGSVGElement>;\n\nfunction svgProps(props: CaptureIconProps): CaptureIconProps {\n return {\n xmlns: \"http://www.w3.org/2000/svg\",\n viewBox: \"0 0 24 24\",\n width: 24,\n height: 24,\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n \"aria-hidden\": true,\n ...props,\n };\n}\n\n// lucide: check\nexport function CheckIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\n// lucide: chevron-left\nexport function ChevronLeftIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n );\n}\n\n// lucide: chevron-right\nexport function ChevronRightIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n );\n}\n\n// lucide: flip-horizontal-2\nexport function FlipHorizontal2Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m3 7 5 5-5 5V7\" />\n <path d=\"m21 7-5 5 5 5V7\" />\n <path d=\"M12 20v2\" />\n <path d=\"M12 14v2\" />\n <path d=\"M12 8v2\" />\n <path d=\"M12 2v2\" />\n </svg>\n );\n}\n\n// lucide: grid-3x3\nexport function Grid3x3Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n <path d=\"M3 9h18\" />\n <path d=\"M3 15h18\" />\n <path d=\"M9 3v18\" />\n <path d=\"M15 3v18\" />\n </svg>\n );\n}\n\n// lucide: grip\nexport function GripIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\n <circle cx=\"19\" cy=\"5\" r=\"1\" />\n <circle cx=\"5\" cy=\"5\" r=\"1\" />\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\n <circle cx=\"19\" cy=\"12\" r=\"1\" />\n <circle cx=\"5\" cy=\"12\" r=\"1\" />\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\n <circle cx=\"19\" cy=\"19\" r=\"1\" />\n <circle cx=\"5\" cy=\"19\" r=\"1\" />\n </svg>\n );\n}\n\n// lucide: images\nexport function ImagesIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m22 11-1.296-1.296a2.4 2.4 0 0 0-3.408 0L11 16\" />\n <path d=\"M4 8a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2\" />\n <circle cx=\"13\" cy=\"7\" r=\"1\" fill=\"currentColor\" />\n <rect x=\"8\" y=\"2\" width=\"14\" height=\"14\" rx=\"2\" />\n </svg>\n );\n}\n\n// lucide: loader-circle (a.k.a. Loader2)\nexport function Loader2Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n );\n}\n\n// lucide: upload\nexport function UploadIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M12 3v12\" />\n <path d=\"m17 8-5-5-5 5\" />\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n </svg>\n );\n}\n\n// lucide: image-off\nexport function ImageOffIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" />\n <path d=\"M10.41 10.41a2 2 0 1 1-2.83-2.83\" />\n <line x1=\"13.5\" x2=\"6\" y1=\"13.5\" y2=\"21\" />\n <line x1=\"18\" x2=\"21\" y1=\"12\" y2=\"15\" />\n <path d=\"M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59\" />\n <path d=\"M21 15V5a2 2 0 0 0-2-2H9\" />\n </svg>\n );\n}\n\n// lucide: lock\nexport function LockIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n );\n}\n\n// lucide: pencil\nexport function PencilIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\" />\n <path d=\"m15 5 4 4\" />\n </svg>\n );\n}\n\n// lucide: play\nexport function PlayIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\" />\n </svg>\n );\n}\n\n// lucide: proportions\nexport function ProportionsIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"M12 9v11\" />\n <path d=\"M2 9h13a2 2 0 0 1 2 2v9\" />\n </svg>\n );\n}\n\n// lucide: refresh-cw\nexport function RefreshCwIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\n <path d=\"M21 3v5h-5\" />\n <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\n <path d=\"M8 16H3v5\" />\n </svg>\n );\n}\n\n// lucide: rotate-ccw\nexport function RotateCcwIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </svg>\n );\n}\n\n// lucide: sun-medium\nexport function SunMediumIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 3v1\" />\n <path d=\"M12 20v1\" />\n <path d=\"M3 12h1\" />\n <path d=\"M20 12h1\" />\n <path d=\"m18.364 5.636-.707.707\" />\n <path d=\"m6.343 17.657-.707.707\" />\n <path d=\"m5.636 5.636.707.707\" />\n <path d=\"m17.657 17.657.707.707\" />\n </svg>\n );\n}\n\n// lucide: timer\nexport function TimerIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <line x1=\"10\" x2=\"14\" y1=\"2\" y2=\"2\" />\n <line x1=\"12\" x2=\"15\" y1=\"14\" y2=\"11\" />\n <circle cx=\"12\" cy=\"14\" r=\"8\" />\n </svg>\n );\n}\n\n// lucide: trash-2\nexport function Trash2Icon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M10 11v6\" />\n <path d=\"M14 11v6\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" />\n <path d=\"M3 6h18\" />\n <path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </svg>\n );\n}\n\n// lucide: x\nexport function XIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\n// lucide: zap\nexport function ZapIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\" />\n </svg>\n );\n}\n\n// lucide: zap-off\nexport function ZapOffIcon(props: CaptureIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M10.513 4.856 13.12 2.17a.5.5 0 0 1 .86.46l-1.377 4.317\" />\n <path d=\"M15.656 10H20a1 1 0 0 1 .78 1.63l-1.72 1.773\" />\n <path d=\"M16.273 16.273 10.88 21.83a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14H4a1 1 0 0 1-.78-1.63l4.507-4.643\" />\n <path d=\"m2 2 20 20\" />\n </svg>\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 }[] | undefined;\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 {\n Loader2Icon,\n XIcon,\n} from \"./icons\";\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 <XIcon 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 <Loader2Icon 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 /** Fresh bytes for pixel editing. Persisted hosts should provide this\n * instead of asking the editor to borrow a cache-owned viewer URL. */\n resolveEditBlob?: () => Promise<Blob>;\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\nexport interface PreparedMediaEdit {\n /** URL handed to the editor. Revoke it only when `revoke` is true. */\n url: string;\n revoke: 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\ninterface MediaCacheState {\n cache: Map<string, CacheEntry>;\n listeners: Map<string, Set<() => void>>;\n}\n\n// The LRU survives viewer/filmstrip mount cycles by design — but it must\n// NOT live in module-level variables: this package builds `splitting: false`\n// in dual ESM/CJS format, so a host whose loaders pull both graphs (Next.js\n// RSC + Jest, for example) would instantiate TWO caches and silently split\n// resolutions from subscribers. State lives on `globalThis` under a\n// `Symbol.for` slot instead (the kit `confirm/opener.ts` exemplar). Behavior\n// is unchanged; never \"clean this up\" into module locals.\nconst STATE_SLOT = Symbol.for(\"ai-matrx.capture.media-cache-state\");\n\nfunction getState(): MediaCacheState {\n const holder = globalThis as Record<symbol, MediaCacheState | undefined>;\n let state = holder[STATE_SLOT];\n if (!state) {\n state = { cache: new Map(), listeners: new Map() };\n holder[STATE_SLOT] = state;\n }\n return state;\n}\n\nfunction notify(key: string): void {\n getState().listeners.get(key)?.forEach((fn) => fn());\n}\n\nfunction evictIfNeeded(): void {\n while (getState().cache.size > MAX_ENTRIES) {\n const oldest = getState().cache.keys().next().value;\n if (oldest === undefined) return;\n const entry = getState().cache.get(oldest);\n getState().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 getState().cache.delete(key);\n getState().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 = getState().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 getState().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 = getState().cache.get(key);\n getState().cache.delete(key);\n if (entry?.revoke && entry.url) URL.revokeObjectURL(entry.url);\n notify(key);\n}\n\n/**\n * Materialize an editor-private snapshot of a media item.\n *\n * Viewer URLs are deliberately long-lived cache entries. A host-owned blob URL\n * can nevertheless be revoked when the host's byte cache evicts it: an already\n * decoded viewer image remains visible, while mounting the same stale URL in a\n * second `<img>` fails. Editing therefore crosses an ownership boundary. It\n * re-resolves persisted media, copies the bytes, and gives the editor its own\n * object URL instead of borrowing the viewer's cached URL.\n */\nexport async function prepareMediaForEdit(\n item: CaptureMediaItem,\n): Promise<PreparedMediaEdit> {\n if (item.resolveEditBlob) {\n const blob = await item.resolveEditBlob();\n if (blob.size === 0) throw new Error(\"media snapshot was empty\");\n return { url: URL.createObjectURL(blob), revoke: true };\n }\n\n // Fresh captures already carry a host-owned object URL. Retain the old\n // direct path for those callers; persisted media must supply fresh bytes.\n const url = item.src ?? getMediaUrl(item);\n if (!url) throw new Error(\"media item has no editable source\");\n return { url, revoke: false };\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 = getState().listeners.get(key) ?? new Set();\n const fn = () => bump((n) => n + 1);\n set.add(fn);\n getState().listeners.set(key, set);\n return () => {\n set.delete(fn);\n if (set.size === 0) getState().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 // The #t media fragment forces iOS Safari to decode and PAINT the\n // first frame — without it a preload=\"metadata\" video tile renders\n // black (\"videos don't show a thumbnail\", real-phone 2026-08-30).\n src={`${url}#t=0.01`}\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 {\n ChevronLeftIcon,\n ChevronRightIcon,\n PencilIcon,\n PlayIcon,\n Trash2Icon,\n XIcon,\n} from \"./icons\";\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) | undefined;\n /** Offered on photo items with a renderable URL. */\n onEdit?: ((item: CaptureMediaItem) => void) | undefined;\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 // The ACTIVE slide's <video> element (tap-to-play toggle target).\n const activeVideoRef = useRef<HTMLVideoElement | null>(null);\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 // Audio keeps native controls, so it keeps its pointer events. Video does\n // NOT: the slide is a control-less tap-to-play surface precisely so the\n // stage owns every gesture and a video swipes exactly like a photo.\n if ((e.target as HTMLElement).closest(\"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 // No direction ever locked = a TAP. On a video slide that toggles\n // playback (the control-less player's one control).\n if (d.axis === null) {\n const video = activeVideoRef.current;\n if (video) {\n if (video.paused) void video.play().catch(() => {});\n else video.pause();\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 <XIcon 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 <PencilIcon 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 <Trash2Icon 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\n key={current.key}\n item={current}\n active\n videoRef={activeVideoRef}\n />\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 <ChevronLeftIcon 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 <ChevronRightIcon 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 videoRef,\n}: {\n item: CaptureMediaItem;\n active: boolean;\n /** Receives the ACTIVE slide's video element (the tap-to-play target). */\n videoRef?: React.MutableRefObject<HTMLVideoElement | null> | undefined;\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 <VideoSlide url={url} active={active} videoRef={videoRef} />;\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\n/**\n * The control-less video slide. Native `controls` made the whole slide a\n * pointer sink — a video could not be swiped like a photo, which is the one\n * behavior a pager must keep uniform. Instead: the STAGE owns every gesture\n * (this element is pointer-events-none), a tap toggles playback, a centre\n * glyph shows the paused state, and a thin bottom bar shows progress.\n */\nfunction VideoSlide({\n url,\n active,\n videoRef,\n}: {\n url: string;\n active: boolean;\n videoRef?: React.MutableRefObject<HTMLVideoElement | null> | undefined;\n}) {\n const [paused, setPaused] = useState(true);\n const [progress, setProgress] = useState(0);\n const localRef = useRef<HTMLVideoElement | null>(null);\n\n // Hand the ACTIVE element to the stage's tap handler; clear on unmount so\n // a swiped-away slide can never receive the next tap.\n useEffect(() => {\n if (!active || !videoRef) return;\n videoRef.current = localRef.current;\n return () => {\n if (videoRef.current === localRef.current) videoRef.current = null;\n };\n }, [active, videoRef]);\n\n return (\n <div className=\"pointer-events-none absolute inset-0\">\n <video\n ref={localRef}\n src={url}\n playsInline\n // Hidden neighbors buffer METADATA only — never whole videos.\n preload={active ? \"auto\" : \"metadata\"}\n muted={!active}\n loop={false}\n onPlay={() => setPaused(false)}\n onPause={() => setPaused(true)}\n onEnded={() => setPaused(true)}\n onTimeUpdate={(e) => {\n const v = e.currentTarget;\n setProgress(v.duration > 0 ? v.currentTime / v.duration : 0);\n }}\n className=\"absolute inset-0 h-full w-full object-contain\"\n />\n {active && paused && (\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"flex h-16 w-16 items-center justify-center rounded-full bg-black/45\">\n <PlayIcon className=\"ml-1 h-8 w-8 fill-white text-white\" />\n </span>\n </span>\n )}\n {active && !paused && (\n <span className=\"absolute inset-x-4 bottom-2 h-0.5 overflow-hidden rounded-full bg-white/25\">\n <span\n className=\"block h-full rounded-full bg-white\"\n style={{ width: `${Math.round(progress * 100)}%` }}\n />\n </span>\n )}\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 {\n CheckIcon,\n FlipHorizontal2Icon,\n RotateCcwIcon,\n XIcon,\n} from \"./icons\";\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 [imageReady, setImageReady] = useState(false);\n const [transforming, setTransforming] = useState(false);\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 setImageReady(false);\n setTransforming(false);\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 (\n !img ||\n !imageReady ||\n img.naturalWidth === 0 ||\n saving ||\n transforming\n ) {\n setEditError(\"The image is still loading — wait a moment and try again.\");\n return;\n }\n setTransforming(true);\n setEditError(null);\n try {\n const sourceX = Math.round(crop.x * img.naturalWidth);\n const sourceY = Math.round(crop.y * img.naturalHeight);\n const sourceWidth = Math.max(\n 1,\n Math.min(\n img.naturalWidth - sourceX,\n Math.round(crop.w * img.naturalWidth),\n ),\n );\n const sourceHeight = Math.max(\n 1,\n Math.min(\n img.naturalHeight - sourceY,\n Math.round(crop.h * img.naturalHeight),\n ),\n );\n const swap = op === \"rotate-left\";\n const canvas = document.createElement(\"canvas\");\n canvas.width = swap ? sourceHeight : sourceWidth;\n canvas.height = swap ? sourceWidth : sourceHeight;\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(\n img,\n sourceX,\n sourceY,\n sourceWidth,\n sourceHeight,\n -sourceWidth / 2,\n -sourceHeight / 2,\n sourceWidth,\n sourceHeight,\n );\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n setTransforming(false);\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 // The existing <img> element temporarily reports zero natural\n // dimensions while its new object URL decodes. Keep every pixel\n // operation unavailable until its onLoad proves the bitmap ready.\n setImageReady(false);\n setWorkingSrc(url);\n setCrop(FULL_CROP);\n setAspect(\"free\");\n setTransforming(false);\n setEditError(null);\n },\n \"image/jpeg\",\n 0.95,\n );\n } catch {\n setTransforming(false);\n setEditError(\"This image could not be edited on this device.\");\n }\n },\n [crop, imageReady, saving, transforming, revokeOwned],\n );\n\n const applyAspect = useCallback((preset: AspectPreset) => {\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) {\n setEditError(\"The image is still loading — wait a moment and try again.\");\n return;\n }\n setAspect(preset);\n const ratio = ASPECTS.find((a) => a.id === preset)?.ratio ?? null;\n if (ratio === null) 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 || !imageReady || img.naturalWidth === 0) {\n setEditError(\"The image is still loading — wait a moment and try again.\");\n return;\n }\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, imageReady, 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 <XIcon 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 || transforming || !imageReady}\n aria-label=\"Save edited image\"\n aria-busy={saving || transforming || !imageReady}\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 <CheckIcon className=\"h-5 w-5\" />\n {saving\n ? \"Saving…\"\n : transforming || !imageReady\n ? \"Loading…\"\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 onLoad={() => setImageReady(true)}\n onError={() => {\n setImageReady(false);\n setTransforming(false);\n setEditError(\"This image could not be loaded on this device.\");\n }}\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 disabled={saving || transforming || !imageReady}\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 disabled={saving || transforming || !imageReady}\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 <RotateCcwIcon className=\"h-5 w-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => bake(\"flip\")}\n disabled={saving || transforming || !imageReady}\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 <FlipHorizontal2Icon className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CameraCaptureV3 — the vertical-rail camera chrome.\n *\n * A SECOND chrome over the same ports as `CameraCapture`, not a replacement.\n * Same engine, same cloud port, same package-owned review loop (filmstrip →\n * viewer → editor). What differs is the layout and the input model, and every\n * difference answers something the v2 chrome got wrong on a real phone\n * (Arman, 2026-08-30):\n *\n * ONE SHUTTER. Tap = photo, press-and-hold = video. v2 spent a whole\n * row on a mode selector, so the user paid bar height AND\n * a decision before the moment they were trying to catch.\n * THE RIGHT EDGE. Options live on a rail over the feed instead of a\n * two-tap grid behind a button. No layout cost, one tap\n * to anything, and the viewfinder stays whole.\n * IT COLLAPSES. Extras hide behind a chevron so the idle rail is short.\n * TEXT IS A BUTTON. Entry expands from a pill (see CaptureExpandingField) —\n * no permanent field, no keyboard-bait over the frame.\n * ONE MEDIA DOOR. There is no UPLOAD mode. The library button is the only\n * way to existing media, and picking files is an option\n * INSIDE that drawer — the host renders it there.\n *\n * Deliberately NOT copied from the app this borrows its shape from: fixed\n * duration choices (they belong to that product's format, not to a camera),\n * and the oversized promo pill for a new feature (chrome is not a billboard).\n *\n * Package source (`@ai-matrx/capture`). UI state only — capture, streams and\n * persistence come from the injected engine.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n Grid3x3Icon,\n ImagesIcon,\n ProportionsIcon,\n RefreshCwIcon,\n SunMediumIcon,\n TimerIcon,\n XIcon,\n ZapIcon,\n ZapOffIcon,\n} from \"./icons\";\nimport { cn } from \"../cn\";\nimport { safeTop, safeBottom } from \"../safe-area\";\n\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraV3Slots,\n CaptureCloudPort,\n CaptureRailAction,\n CaptureTimerSetting,\n} from \"../types\";\nimport { CaptureRail } from \"./CaptureRail\";\nimport { CaptureSheet, type CaptureSheetAction } from \"./CaptureSheet\";\nimport { CaptureFilmstrip } from \"./CaptureFilmstrip\";\nimport { CountdownOverlay } from \"./CountdownOverlay\";\nimport { GridOverlay } from \"./GridOverlay\";\nimport { HoldShutter } from \"./HoldShutter\";\nimport { ImageEditSheet } from \"./ImageEditSheet\";\nimport { MediaViewer } from \"./MediaViewer\";\nimport { invalidateMedia, prepareMediaForEdit } from \"../media/media-cache\";\nimport { useTrackControls } from \"../hooks/useTrackControls\";\nimport type { CaptureMediaSession } from \"./CameraCapture\";\n\nconst ASPECT_CYCLE: CaptureAspect[] = [\"full\", \"4:3\", \"1:1\", \"16:9\"];\n\nexport interface CameraCaptureV3Props {\n engine: CaptureCameraEngine;\n /** THE CLOUD LAW: still required. v3 changes where the door is, not whether\n * there is one — and in v3 it is the ONLY door to existing media. */\n cloud: CaptureCloudPort;\n preview: React.ReactNode;\n media?: CaptureMediaSession;\n /**\n * Fired on pointer-DOWN, before the hold threshold decides photo vs video.\n *\n * 🚨 THIS EXISTS FOR iOS. Hosts warm the microphone when they believe a\n * recording is coming (one permission prompt per medium). With a mode row\n * that signal arrived when the user chose VIDEO — early. With a hold\n * shutter there is no such moment, so waiting for the recording to actually\n * start means warming DURING the take, which on iOS Safari costs the first\n * second of audio or throws a prompt over the viewfinder mid-recording.\n *\n * The host is expected to make this idempotent: it fires on every press,\n * including the taps that turn out to be photos.\n */\n onRecordIntent?: () => void;\n /** Ring completes here; presentational only — it never stops the recorder. */\n maxRecordSeconds?: number;\n onReviewOpenChange?: (open: boolean) => void;\n onClose?: () => void;\n blockedSheet?: { body: React.ReactNode; actions: CaptureSheetAction[] };\n /** Hide all chrome except status chips (host renders its own toggle). */\n controlsHidden?: boolean;\n shutterDisabled?: boolean;\n slots?: CaptureCameraV3Slots;\n}\n\nexport function CameraCaptureV3({\n engine,\n cloud,\n preview,\n media,\n onRecordIntent,\n maxRecordSeconds,\n onReviewOpenChange,\n onClose,\n blockedSheet,\n controlsHidden = false,\n shutterDisabled = false,\n slots = {},\n}: CameraCaptureV3Props) {\n const [viewerKey, setViewerKey] = useState<string | null>(null);\n const [editTarget, setEditTarget] = useState<{\n key: string;\n url: string;\n revoke: boolean;\n } | null>(null);\n const [editPreparing, setEditPreparing] = useState(false);\n const [editPreparationError, setEditPreparationError] = useState<string | null>(\n null,\n );\n const [gridOn, setGridOn] = useState(false);\n const [timerSetting, setTimerSetting] = useState<CaptureTimerSetting>(0);\n const [aspect, setAspect] = useState<CaptureAspect>(\"full\");\n const [countdown, setCountdown] = useState<number | null>(null);\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 useEffect(\n () => () => {\n if (editTarget?.revoke) URL.revokeObjectURL(editTarget.url);\n },\n [editTarget],\n );\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 countdown must never survive into a recording — it would fire a photo\n // capture mid-take (the v2 bug, kept fixed here).\n useEffect(() => {\n if (engine.recording) clearCountdown();\n }, [engine.recording, clearCountdown]);\n\n const onPhoto = useCallback(() => {\n if (countdown !== null) {\n clearCountdown(); // tapping mid-countdown cancels\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 }, [aspect, clearCountdown, countdown, engine, timerSetting]);\n\n const blocked = engine.blocked !== null;\n\n // ── The rail ───────────────────────────────────────────────────────────\n // Primary (always visible): flip and flash — the two a user reaches for\n // without thinking. Everything else collapses.\n const coreRail: CaptureRailAction[] = [\n ...(engine.onFlipCamera\n ? [\n {\n id: \"flip\",\n label: \"Switch camera\",\n icon: <RefreshCwIcon className=\"h-[22px] w-[22px]\" />,\n primary: true,\n onPress: engine.onFlipCamera,\n },\n ]\n : []),\n ...(controls.torchSupported\n ? [\n {\n id: \"flash\",\n label: \"Flash\",\n icon: controls.torchOn ? (\n <ZapIcon className=\"h-[22px] w-[22px]\" fill=\"currentColor\" />\n ) : (\n <ZapOffIcon className=\"h-[22px] w-[22px]\" />\n ),\n active: controls.torchOn,\n primary: true,\n onPress: controls.toggleTorch,\n },\n ]\n : []),\n {\n id: \"timer\",\n label: \"Timer\",\n icon: <TimerIcon className=\"h-[22px] w-[22px]\" />,\n active: timerSetting !== 0,\n valueLabel: timerSetting === 0 ? undefined : `${timerSetting}s`,\n onPress: () => setTimerSetting((t) => (t === 0 ? 3 : t === 3 ? 10 : 0)),\n },\n {\n id: \"grid\",\n label: \"Grid\",\n icon: <Grid3x3Icon className=\"h-[22px] w-[22px]\" />,\n active: gridOn,\n onPress: () => setGridOn((g) => !g),\n },\n {\n id: \"aspect\",\n label: \"Aspect\",\n icon: <ProportionsIcon className=\"h-[22px] w-[22px]\" />,\n active: aspect !== \"full\",\n valueLabel: aspect === \"full\" ? undefined : aspect,\n onPress: () =>\n setAspect(\n (a) =>\n ASPECT_CYCLE[(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: <SunMediumIcon className=\"h-[22px] w-[22px]\" />,\n active: controls.exposure !== 0,\n valueLabel:\n controls.exposure === 0\n ? undefined\n : `${controls.exposure > 0 ? \"+\" : \"\"}${controls.exposure}`,\n onPress: () => controls.setExposure(controls.exposure === 0 ? 1 : 0),\n },\n ]\n : []),\n ];\n // A blocked camera disables the CORE actions (they steer hardware that is\n // not there) but the rail itself stays: host actions keep working through\n // the upload lane.\n const railActions = [\n ...coreRail.map((a) => (blocked ? { ...a, disabled: true } : a)),\n ...(slots.railActions ?? []),\n ];\n\n return (\n <div className=\"absolute inset-0 select-none overflow-hidden bg-black\">\n <div className=\"absolute inset-0\">{preview}</div>\n <GridOverlay visible={gridOn && !blocked} />\n\n {/* Aspect framing hint — the dimmed bands approximate the region the\n photo will discard. Never shown while recording: video is not\n cropped, so the bands would be a lie about the take in progress. */}\n {aspect !== \"full\" && !blocked && !engine.recording && (\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 row: no bar. Controls float over the feed so the viewfinder\n keeps its full height — the single biggest v2 complaint. ── */}\n {!controlsHidden && (\n <div\n className=\"pointer-events-none absolute inset-x-0 top-0 z-20 px-2\"\n style={safeTop}\n >\n <div className=\"flex min-h-[44px] items-center gap-2 py-1.5\">\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close camera\"\n className=\"pointer-events-auto flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full text-white drop-shadow-[0_1px_3px_rgba(0,0,0,0.6)] transition-colors hover:bg-white/10\"\n >\n <XIcon className=\"h-6 w-6\" />\n </button>\n ) : (\n <span className=\"h-10 w-10 shrink-0\" />\n )}\n\n {/* The expanding entry sits centre-top, where the eye already is\n and where a keyboard rising will not cover it. */}\n <div className=\"pointer-events-auto flex min-w-0 flex-1 justify-center\">\n {slots.topEntry}\n </div>\n\n <span className=\"h-10 w-10 shrink-0\" />\n </div>\n\n {slots.topCenter ? (\n <div className=\"pointer-events-none pb-1 text-center\">\n {slots.topCenter}\n </div>\n ) : null}\n </div>\n )}\n\n {/* Honesty chips stay visible even with controls hidden — they are the\n camera telling the truth, not chrome. */}\n {slots.statusChips ? (\n <div\n className=\"pointer-events-none absolute inset-x-0 z-20 flex flex-col items-center gap-1.5 px-3\"\n style={{\n // Clears the top row AND the topCenter line beneath it — chips\n // rendered at the label's own height read as a collision.\n top: `calc(env(safe-area-inset-top, 0px) + ${\n controlsHidden ? 12 : slots.topCenter ? 92 : 64\n }px)`,\n }}\n >\n {slots.statusChips}\n </div>\n ) : null}\n\n {/* ── The right rail. Rendered even when the camera is BLOCKED: host\n actions (notes, QR, process) are not camera controls and keep\n working through the upload lane — only the core camera actions\n disable (they are built with `disabled: blocked` above). ── */}\n {!controlsHidden && (\n <div\n className=\"pointer-events-none absolute right-1 z-20 flex justify-end\"\n style={{ top: `calc(env(safe-area-inset-top, 0px) + 64px)` }}\n >\n <CaptureRail actions={railActions} />\n </div>\n )}\n\n {/* ── Bottom: filmstrip over the feed, then the shutter row ── */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 bottom-0 z-20\" style={safeBottom}>\n {/* The session's captures, over the feed — same package-owned strip\n v2 renders, so the review loop is identical across chromes. */}\n {media && media.items.length > 0 ? (\n <div className=\"px-2 pb-1.5\">\n <CaptureFilmstrip\n items={media.items}\n onOpen={(item) => setViewerKey(item.key)}\n />\n </div>\n ) : null}\n {slots.aboveShutter ? (\n <div className=\"px-2 pb-1.5\">{slots.aboveShutter}</div>\n ) : null}\n\n <div className=\"flex items-center justify-between px-3 pb-2\">\n {/* ONE door to existing media. There is no upload lane here by\n design — the host's library drawer carries the \"pick files\"\n option, so a user has one place to look instead of two\n controls that both mean \"media I already have\". */}\n <div className=\"flex w-[86px] justify-start\">\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Your media — library and upload\"\n className=\"relative h-12 w-12 touch-manipulation overflow-hidden rounded-xl bg-black/40 ring-1 ring-white/30 backdrop-blur-md transition-transform active:scale-95\"\n >\n {cloud.recentsThumb ?? (\n <span className=\"flex h-full w-full items-center justify-center\">\n <ImagesIcon className=\"h-5 w-5 text-white/80\" />\n </span>\n )}\n </button>\n </div>\n\n <HoldShutter\n recording={engine.recording}\n elapsedSeconds={engine.recordElapsedSeconds}\n {...(maxRecordSeconds !== undefined\n ? { maxSeconds: maxRecordSeconds }\n : {})}\n disabled={shutterDisabled || blocked}\n {...(onRecordIntent ? { onPressStart: onRecordIntent } : {})}\n onPhoto={onPhoto}\n onStartRecording={engine.onStartRecording}\n onStopRecording={engine.onStopRecording}\n />\n\n <div className=\"flex w-[86px] justify-end\">\n {slots.shutterTrailing}\n </div>\n </div>\n\n {/* The gesture, stated once. A single-button camera is only obvious\n to the person who built it. */}\n {!engine.recording && !blocked ? (\n <p className=\"pb-1.5 text-center text-[11px] text-white/60 drop-shadow-[0_1px_2px_rgba(0,0,0,0.8)]\">\n Tap for a photo · hold to record\n </p>\n ) : null}\n </div>\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 {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 if (editPreparing) return;\n setEditPreparing(true);\n setEditPreparationError(null);\n void prepareMediaForEdit(item)\n .then((prepared) =>\n setEditTarget({ key: item.key, ...prepared }),\n )\n .catch(() =>\n setEditPreparationError(\n \"This photo could not be prepared for editing. Try again.\",\n ),\n )\n .finally(() => setEditPreparing(false));\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 {editPreparationError && (\n <p\n role=\"alert\"\n className=\"fixed inset-x-4 bottom-24 z-[70] rounded-xl bg-black/85 px-4 py-3 text-center text-sm text-[#FF6961]\"\n >\n {editPreparationError}\n </p>\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CaptureRail — the vertical action rail down the RIGHT edge of the frame.\n *\n * Why the edge and not a bar: a bottom bar steals height from the viewfinder\n * on every phone, and the options grid it fed cost two taps to reach anything.\n * A rail sits over the feed, costs no layout, and puts every option one tap\n * away under the thumb that is already holding the phone.\n *\n * Why it collapses: a complete rail is a long rail, and a long rail is a wall\n * of icons nobody reads. Actions marked `primary` stay; the rest hide behind a\n * chevron. The chevron is only rendered when there is something to hide —\n * a control that toggles nothing is a lie about the interface.\n *\n * Presentational only.\n */\n\nimport React, { useState } from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureRailAction } from \"../types\";\n\nexport interface CaptureRailProps {\n actions: CaptureRailAction[];\n /** Start expanded. Default false — an idle camera shows the short rail. */\n defaultExpanded?: boolean;\n className?: string;\n}\n\nexport function CaptureRail({\n actions,\n defaultExpanded = false,\n className,\n}: CaptureRailProps) {\n const [expanded, setExpanded] = useState(defaultExpanded);\n\n const primary = actions.filter((a) => a.primary);\n const extra = actions.filter((a) => !a.primary);\n // With nothing marked primary, the whole rail is always-on rather than\n // collapsing to nothing — a rail that can hide everything reads as broken.\n const shown = primary.length === 0 ? actions : expanded ? actions : primary;\n const collapsible = primary.length > 0 && extra.length > 0;\n\n if (actions.length === 0) return null;\n\n return (\n <div\n className={cn(\n \"pointer-events-auto flex flex-col items-center gap-1\",\n className,\n )}\n >\n {shown.map((action) => (\n <RailButton key={action.id} action={action} />\n ))}\n\n {collapsible ? (\n <button\n type=\"button\"\n onClick={() => setExpanded((open) => !open)}\n aria-expanded={expanded}\n aria-label={expanded ? \"Fewer options\" : \"More options\"}\n className={cn(\n \"mt-0.5 flex h-9 w-9 touch-manipulation items-center justify-center\",\n \"rounded-full text-white/90 transition-colors hover:bg-white/10\",\n )}\n >\n <Chevron\n className={cn(\n \"h-5 w-5 transition-transform duration-200\",\n expanded && \"rotate-180\",\n )}\n />\n </button>\n ) : null}\n </div>\n );\n}\n\nfunction RailButton({ action }: { action: CaptureRailAction }) {\n return (\n <button\n type=\"button\"\n onClick={action.onPress}\n disabled={action.disabled}\n aria-label={action.label}\n aria-pressed={action.active ?? undefined}\n className={cn(\n \"flex w-12 shrink-0 touch-manipulation flex-col items-center justify-center gap-0.5\",\n \"rounded-2xl py-1.5 transition-colors\",\n action.active ? \"text-[#FFCC00]\" : \"text-white\",\n action.disabled ? \"opacity-40\" : \"hover:bg-white/10 active:bg-white/15\",\n )}\n >\n {/* The icon carries a shadow rather than a plate: a chip per action turns\n the rail into a stack of boxes over the picture the user is framing. */}\n <span className=\"drop-shadow-[0_1px_3px_rgba(0,0,0,0.6)]\">\n {action.icon}\n </span>\n {action.valueLabel ? (\n <span className=\"text-[10px] font-semibold leading-none tabular-nums drop-shadow-[0_1px_2px_rgba(0,0,0,0.7)]\">\n {action.valueLabel}\n </span>\n ) : null}\n </button>\n );\n}\n\n/** Inlined so the package keeps zero icon dependencies. */\nfunction Chevron({ className }: { className?: string }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n );\n}\n","\"use client\";\n\n/**\n * HoldShutter — ONE button for photo and video.\n *\n * Tap takes a photo. Press and hold records, and releasing stops. The mode is\n * therefore a gesture, not a control you must set before the moment you are\n * trying to catch — which is the whole reason the v2 mode row was wrong on a\n * phone: it cost bar height AND a decision, and the decision was usually made\n * after the thing had happened.\n *\n * The interaction, precisely:\n *\n * pointerdown → arm a hold timer (HOLD_MS)\n * timer fires → start recording, ring begins filling\n * pointerup < HOLD_MS → cancel the timer, take a photo\n * pointerup >= HOLD_MS → stop recording (recording follows the finger)\n * slide up ≥ LOCK_DRAG_PX while holding → LOCK: release keeps recording,\n * the next tap stops\n * pointercancel / leave → same as pointerup (a drag off the button must not\n * leave the camera recording with nothing to stop it)\n *\n * A LOCK is a GESTURE, never a timer (Arman, 2026-08-30: recording follows\n * the finger — releasing stops, full stop — unless the user deliberately\n * locks): while holding a recording, slide UP to the lock target that\n * appears above the button. Locked, release keeps recording and the next\n * tap stops. An automatic time-based latch shipped first and was wrong —\n * it silently converted \"I let go\" into \"still recording\".\n *\n * Presentational only — the host owns the recorder.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n LockIcon,\n} from \"./icons\";\nimport { cn } from \"../cn\";\n\n/** Hold this long before a press becomes a recording. */\nconst HOLD_MS = 320;\n/** Slide the held finger this far UP to lock the recording. */\nconst LOCK_DRAG_PX = 64;\n\nexport interface HoldShutterProps {\n recording: boolean;\n /** Elapsed recording seconds, for the ring + read-out. */\n elapsedSeconds: number;\n /**\n * Ring completes at this many seconds. Presentational only — it does not\n * stop the recorder, because a chrome that silently ends a take is worse\n * than one that shows a full ring. Omit for no progress arc.\n */\n maxSeconds?: number;\n disabled?: boolean;\n /**\n * Fired on pointer-DOWN, before the hold threshold decides photo vs video.\n * Hosts use it to warm the microphone — see `CameraCaptureV3Props`.\n */\n onPressStart?: () => void;\n onPhoto: () => void;\n onStartRecording: () => void;\n onStopRecording: () => void;\n}\n\nexport function HoldShutter({\n recording,\n elapsedSeconds,\n maxSeconds,\n disabled = false,\n onPressStart,\n onPhoto,\n onStartRecording,\n onStopRecording,\n}: HoldShutterProps) {\n const holdTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const startedRecording = useRef(false);\n const pressOriginY = useRef(0);\n const [pressed, setPressed] = useState(false);\n const [locked, setLocked] = useState(false);\n\n const clearHold = useCallback(() => {\n if (holdTimer.current !== null) {\n clearTimeout(holdTimer.current);\n holdTimer.current = null;\n }\n }, []);\n\n // An externally-stopped recording (mode change, error, host abort) must\n // always unlatch.\n useEffect(() => {\n if (!recording) setLocked(false);\n }, [recording]);\n\n useEffect(() => clearHold, [clearHold]);\n\n const onPointerDown = useCallback(\n (event: React.PointerEvent<HTMLButtonElement>) => {\n if (disabled) return;\n // Keep receiving move/up even if the finger slides off the button.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n pressOriginY.current = event.clientY;\n setPressed(true);\n onPressStart?.();\n\n // Locked: this press is a STOP, decided on release so a tap-and-hold\n // does not stop and immediately restart.\n if (recording && locked) return;\n\n startedRecording.current = false;\n holdTimer.current = setTimeout(() => {\n holdTimer.current = null;\n startedRecording.current = true;\n onStartRecording();\n }, HOLD_MS);\n },\n [disabled, locked, onPressStart, onStartRecording, recording],\n );\n\n // Slide-to-lock: while THIS press is recording, dragging up past the lock\n // target latches the take.\n const onPointerMove = useCallback(\n (event: React.PointerEvent<HTMLButtonElement>) => {\n if (!pressed || locked || !startedRecording.current) return;\n if (pressOriginY.current - event.clientY >= LOCK_DRAG_PX) {\n setLocked(true);\n navigator.vibrate?.(30);\n }\n },\n [pressed, locked],\n );\n\n const endPress = useCallback(() => {\n if (!pressed) return;\n setPressed(false);\n const wasHold = startedRecording.current;\n clearHold();\n startedRecording.current = false;\n\n if (recording && locked && !wasHold) {\n onStopRecording();\n return;\n }\n if (wasHold) {\n // Recording follows the finger: release stops — unless the user slid\n // up to the lock during this press.\n if (!locked) onStopRecording();\n return;\n }\n if (!recording) onPhoto();\n }, [clearHold, locked, onPhoto, onStopRecording, pressed, recording]);\n\n const progress =\n maxSeconds && maxSeconds > 0\n ? Math.min(1, elapsedSeconds / maxSeconds)\n : 0;\n\n // 74px button, 3.5px ring — the arc rides just outside the border.\n const R = 39;\n const C = 2 * Math.PI * R;\n\n return (\n <div className=\"relative flex h-[86px] w-[86px] shrink-0 items-center justify-center\">\n {recording && maxSeconds ? (\n <svg\n className=\"pointer-events-none absolute inset-0 -rotate-90\"\n viewBox=\"0 0 86 86\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"43\"\n cy=\"43\"\n r={R}\n fill=\"none\"\n stroke=\"rgba(255,255,255,0.25)\"\n strokeWidth=\"4\"\n />\n <circle\n cx=\"43\"\n cy=\"43\"\n r={R}\n fill=\"none\"\n stroke=\"#FF3B30\"\n strokeWidth=\"4\"\n strokeLinecap=\"round\"\n strokeDasharray={C}\n strokeDashoffset={C * (1 - progress)}\n />\n </svg>\n ) : null}\n\n {/* The lock target — appears above the button while an UNLOCKED\n recording is held; sliding the finger up to it latches the take. */}\n {recording && pressed && !locked && (\n <span\n className=\"pointer-events-none absolute left-1/2 flex -translate-x-1/2 flex-col items-center gap-0.5\"\n style={{ top: -LOCK_DRAG_PX - 22 }}\n aria-hidden\n >\n <span className=\"flex h-9 w-9 items-center justify-center rounded-full bg-black/60 ring-1 ring-white/30\">\n <LockIcon className=\"h-4 w-4 text-white/90\" />\n </span>\n <span className=\"text-[10px] font-medium text-white/80 drop-shadow\">\n Slide up to lock\n </span>\n </span>\n )}\n {recording && locked && (\n <span\n className=\"pointer-events-none absolute left-1/2 flex h-9 w-9 -translate-x-1/2 items-center justify-center rounded-full bg-[#FF3B30]\"\n style={{ top: -LOCK_DRAG_PX - 22 }}\n aria-hidden\n >\n <LockIcon className=\"h-4 w-4 text-white\" fill=\"currentColor\" />\n </span>\n )}\n\n <button\n type=\"button\"\n disabled={disabled}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endPress}\n onPointerCancel={endPress}\n onLostPointerCapture={endPress}\n // The press IS the gesture; a click would fire a second photo after\n // pointerup on desktop.\n onContextMenu={(e) => e.preventDefault()}\n aria-label={\n recording\n ? locked\n ? \"Stop recording\"\n : \"Recording — release to stop\"\n : \"Tap for a photo, hold to record\"\n }\n className={cn(\n \"group flex h-[74px] w-[74px] shrink-0 touch-none select-none items-center justify-center\",\n \"rounded-full 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\",\n recording\n ? \"h-7 w-7 rounded-[6px] bg-[#FF3B30]\"\n : cn(\n \"h-[62px] w-[62px] rounded-full bg-white\",\n pressed && \"scale-90\",\n ),\n )}\n />\n </button>\n\n {recording ? (\n <span\n className={cn(\n \"pointer-events-none absolute -bottom-1 left-1/2 -translate-x-1/2 translate-y-full\",\n \"rounded-full bg-black/60 px-2 py-0.5 text-[11px] font-medium tabular-nums text-white\",\n )}\n >\n {formatElapsed(elapsedSeconds)}\n {locked ? \" · tap to stop\" : \"\"}\n </span>\n ) : null}\n </div>\n );\n}\n\nfunction formatElapsed(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = String(Math.floor(totalSeconds % 60)).padStart(2, \"0\");\n return `${m}:${s}`;\n}\n","\"use client\";\n\n/**\n * CaptureExpandingField — a pill BUTTON that becomes a text field on press,\n * and goes back to being a button when it is done.\n *\n * 🚨 WHY IT IS NOT AN INPUT (Arman, 2026-08-30). v2 pinned a live `<input>` in\n * the bottom bar. On a camera that is wrong three ways: it occupies a row\n * permanently for something used occasionally, it invites the keyboard over\n * the viewfinder on an accidental tap, and it makes an idle camera look like a\n * form. The pattern that fits is the one Apple uses for search — a compact\n * control that admits it is a control, and grows only when you mean it.\n *\n * COMMIT ON UNMOUNT, always. The v2 input already learned this the hard way:\n * hiding the controls or switching item remounts the field, `onBlur` never\n * fires, and a typed value evaporates. Anything typed is committed on the way\n * out, whatever caused the exit.\n *\n * Presentational + local draft state only; the host owns what a value means.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface CaptureExpandingFieldProps {\n /** Collapsed label, e.g. \"Serial / tag\". Also the accessible name. */\n label: string;\n /** Placeholder once expanded. Defaults to `label`. */\n placeholder?: string;\n /** Icon shown in the collapsed pill and at the field's leading edge. */\n icon?: React.ReactNode;\n /** Commit a non-empty trimmed value. Called on Enter, blur, and unmount. */\n onCommit: (value: string) => void;\n /** Start expanded (a host that knows entry is imminent). */\n defaultExpanded?: boolean;\n disabled?: boolean;\n /** Shown in the collapsed pill instead of `label` — e.g. the current tag. */\n currentValue?: string | null;\n className?: string;\n}\n\nexport function CaptureExpandingField({\n label,\n placeholder,\n icon,\n onCommit,\n defaultExpanded = false,\n disabled = false,\n currentValue = null,\n className,\n}: CaptureExpandingFieldProps) {\n const [expanded, setExpanded] = useState(defaultExpanded);\n const [draft, setDraft] = useState(\"\");\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n // Refs so the unmount commit reads the LATEST draft without re-running the\n // effect (which would fire the cleanup on every keystroke).\n const draftRef = useRef(draft);\n draftRef.current = draft;\n const onCommitRef = useRef(onCommit);\n onCommitRef.current = onCommit;\n\n useEffect(() => {\n return () => {\n const trimmed = draftRef.current.trim();\n if (trimmed) onCommitRef.current(trimmed);\n };\n }, []);\n\n const commit = useCallback(() => {\n const trimmed = draftRef.current.trim();\n if (trimmed) onCommitRef.current(trimmed);\n setDraft(\"\");\n }, []);\n\n const open = useCallback(() => {\n setExpanded(true);\n // Focus after paint so the keyboard rises with the field, not before it.\n requestAnimationFrame(() => inputRef.current?.focus());\n }, []);\n\n const close = useCallback(() => {\n commit();\n setExpanded(false);\n }, [commit]);\n\n if (!expanded) {\n return (\n <button\n type=\"button\"\n onClick={open}\n disabled={disabled}\n aria-label={label}\n aria-expanded={false}\n className={cn(\n \"flex h-9 max-w-[62vw] items-center gap-1.5 rounded-full px-3.5\",\n \"bg-black/45 text-[13px] font-semibold text-white backdrop-blur-md\",\n \"transition-colors hover:bg-black/60 active:bg-black/70\",\n disabled && \"opacity-40\",\n className,\n )}\n >\n {icon ? <span className=\"shrink-0\">{icon}</span> : null}\n <span className=\"truncate\">{currentValue?.trim() || label}</span>\n </button>\n );\n }\n\n return (\n <div\n className={cn(\n \"flex h-9 w-[min(78vw,320px)] items-center gap-1.5 rounded-full px-3\",\n \"bg-black/60 backdrop-blur-md\",\n className,\n )}\n >\n {icon ? <span className=\"shrink-0 text-white/70\">{icon}</span> : null}\n <input\n ref={inputRef}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={close}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n commit();\n (e.target as HTMLInputElement).blur();\n }\n if (e.key === \"Escape\") {\n setDraft(\"\");\n setExpanded(false);\n }\n }}\n placeholder={placeholder ?? label}\n aria-label={label}\n enterKeyHint=\"done\"\n autoCapitalize=\"characters\"\n autoCorrect=\"off\"\n spellCheck={false}\n // 16px floor: anything smaller makes iOS zoom the whole page on focus\n // and it never zooms back out.\n className={cn(\n \"min-w-0 flex-1 bg-transparent text-base text-white\",\n \"placeholder:text-white/40 focus:outline-none\",\n )}\n />\n <button\n type=\"button\"\n // onMouseDown, not onClick: the input's blur would otherwise fire\n // first, collapse the field, and unmount this button mid-click.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setDraft(\"\");\n setExpanded(false);\n }}\n aria-label={`Close ${label}`}\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-white/70 hover:bg-white/10 hover:text-white\"\n >\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n className=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\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 production camera engine, so a\n * host with no media runtime gets a fully wired camera from a few lines:\n *\n * const engine = useDefaultCaptureEngine({ onPhoto, onVideo, onFiles, mode });\n * <CameraCapture engine={engine} preview={<CameraFeed engine={engine} />} … />\n *\n * Hosts with a cross-feature media runtime (AI Matrx injects its\n * lease-managed runtime) pass their own `CaptureCameraEngine` instead — the\n * injection seam remains the mechanism; this is the standard implementation\n * of it (THE ALL-INCLUSIVE LAW, C23).\n *\n * getUserMedia lives ONLY under `src/engine/` (the laws test bans it\n * everywhere else). What this engine now carries — the hard parts that used\n * to live host-side (C22 retrofit, 2026-08-30, ported from matrx-frontend's\n * proven camera runtime):\n *\n * - PERMISSION PRE-CHECK: a known-denied camera permission reports\n * `blocked: \"permission-denied\"` WITHOUT another getUserMedia call —\n * never hammer a rejection the browser already told us about.\n * - COMBINED CAMERA+MIC PROMPT: when audio is wanted and a mic prompt would\n * actually appear, ONE `getUserMedia({video, audio})` browser prompt covers\n * both permissions (instead of a camera prompt at open plus a mic prompt\n * mid-action — the classic double-prompt annoyance; on iOS Safari it recurs\n * every session). The audio tracks are banked as the warm-mic manager's\n * stream; a combined-call denial is retried ONCE video-only, so a mic\n * denial never takes the camera down (bounded — never a fallback loop).\n * - MIC WARM HOLD: while the chrome is in video mode, a warm-mic hold keeps\n * the OS grant alive so iOS Safari prompts at most once per medium per\n * session (`warm-mic.ts` carries the four recovery branches).\n * - REAL DEVICE FLIP: cycles actual `videoinput` deviceIds (not a blind\n * facingMode toggle), hidden while recording — a flip reacquires the\n * stream, which would kill the take (`onFlipCamera` goes null, and the\n * handler backstops it).\n * - TRACK HEALTH: a camera track that ENDS (OS interruption, device pulled)\n * is reported loudly and reacquired once instead of leaving a frozen frame.\n * - MONOTONIC RECORDING CLOCK, one-capture-at-a-time shutter guard, a 120 ms\n * `flash` flag, and loud shutter failures through the `onError` sink.\n *\n * Photo path: full-sensor canvas capture (JPEG q0.92) then the ONE\n * center-crop implementation (`crop.ts`) — never a screenshot of the cropped\n * preview. Upload lane: a package-created hidden <input type=\"file\"> (no\n * `capture` attribute — that is what opens the OS gallery, not the camera).\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n} from \"../types\";\nimport { cropBlobToAspect, nextCameraDevice } from \"./crop\";\nimport {\n classifyCameraBlockReason,\n queryCameraPermission,\n queryMicPermission,\n shouldCombineMicPrompt,\n} from \"./permissions\";\nimport {\n acquireWarmMic,\n adoptWarmAudioStream,\n buildWarmMicConstraints,\n releaseWarmMic,\n} from \"./warm-mic\";\nimport { finalizeCapturedVideo } from \"./video-result\";\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 (full sensor, aspect crop\n * already applied). */\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. Default true. With audio on, the\n * engine folds the mic permission into the CAMERA's browser prompt (one\n * combined prompt) whenever a mic prompt would actually appear. */\n withAudio?: boolean;\n /** The chrome's current mode. In \"video\" the engine warm-holds the mic so\n * iOS Safari prompts at most once per medium per session. Omitting it\n * skips the warm hold (the record path still acquires the mic). */\n mode?: CaptureCameraMode;\n facingMode?: \"environment\" | \"user\";\n photoQuality?: number;\n /** Filename prefix for captures (default \"capture\"). */\n fileNamePrefix?: string;\n /** Loud failure sink (a toast, a logger). Default: console.error. A tapped\n * shutter that produced nothing must say so — never a silent drop. */\n onError?: (message: string, err: unknown) => void;\n}\n\n/** The engine plus the extras the default path owns (white shutter flash). */\nexport interface DefaultCaptureEngine extends CaptureCameraEngine {\n /** White shutter-flash flag (120 ms), for the host's flash overlay. */\n flash: boolean;\n /** Take a photo with a non-default filename prefix (e.g. delineators). */\n capturePhotoWith: (opts: {\n fileNamePrefix: string;\n aspect?: CaptureAspect;\n }) => void;\n}\n\nexport function useDefaultCaptureEngine(\n options: DefaultEngineOptions,\n): DefaultCaptureEngine {\n const {\n onPhoto,\n onVideo,\n onFiles,\n withAudio = true,\n mode,\n facingMode: initialFacing = \"environment\",\n photoQuality = 0.92,\n fileNamePrefix = \"capture\",\n onError,\n } = options;\n\n const [stream, setStream] = useState<MediaStream | null>(null);\n const [blocked, setBlocked] = useState<CaptureCameraEngine[\"blocked\"]>(null);\n const [deviceId, setDeviceId] = useState<string | null>(null);\n const [cameras, setCameras] = useState<MediaDeviceInfo[]>([]);\n const [recording, setRecording] = useState(false);\n const [recordElapsedSeconds, setRecordElapsedSeconds] = useState(0);\n const [flash, setFlash] = useState(false);\n // Bumped to force one reacquire after a hard track end (OS interruption).\n const [acquireToken, setAcquireToken] = 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 micClones: MediaStreamTrack[];\n micHeld: boolean;\n timer: ReturnType<typeof setInterval>;\n } | null>(null);\n const captureBusyRef = useRef(false);\n const reacquiredOnceRef = useRef(false);\n const callbacksRef = useRef({ onPhoto, onVideo, onFiles, onError });\n callbacksRef.current = { onPhoto, onVideo, onFiles, onError };\n\n const reportError = useCallback((message: string, err: unknown) => {\n const sink = callbacksRef.current.onError;\n if (sink) sink(message, err);\n else console.error(`[capture:engine] ${message}`, err);\n }, []);\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\n void (async () => {\n // Pre-check: a known-denied permission renders the blocked state\n // without hammering getUserMedia (it would instantly reject anyway).\n const known = await queryCameraPermission();\n if (cancelled) return;\n if (known === \"denied\") {\n setBlocked({ reason: \"permission-denied\" });\n return;\n }\n\n // Combined prompt decision: fold the mic into the SAME getUserMedia\n // call only when a mic prompt would actually appear — \"granted\" needs\n // none, and \"denied\" audio would fail the whole combined call.\n const includeMic =\n withAudio && shouldCombineMicPrompt(await queryMicPermission());\n if (cancelled) return;\n\n const video: MediaTrackConstraints = {\n width: { ideal: 4096 },\n height: { ideal: 4096 },\n ...(deviceId ? { deviceId: { exact: deviceId } } : { facingMode: initialFacing }),\n };\n\n const acquire = async (mic: boolean): Promise<MediaStream> => {\n try {\n return await navigator.mediaDevices.getUserMedia(\n mic\n ? { video, audio: buildWarmMicConstraints() }\n : { video, audio: false },\n );\n } catch (err) {\n if (mic && classifyCameraBlockReason(err) === \"permission-denied\") {\n // Ambiguous denial of the combined call — retry ONCE video-only:\n // if the camera half succeeds, the denial was the mic's. A mic\n // denial must never take the camera down. (Bounded single retry\n // on the denial path, never a constraint-fallback loop.)\n return navigator.mediaDevices.getUserMedia({ video, audio: false });\n }\n throw err;\n }\n };\n\n try {\n const s = await acquire(includeMic);\n if (cancelled) {\n s.getTracks().forEach((t) => t.stop());\n return;\n }\n // Bank the mic half of a combined grant in the warm-mic manager so\n // the first recording reuses it with no second prompt (the mic light\n // clears on the manager's keepalive schedule).\n const audioTracks = s.getAudioTracks();\n if (audioTracks.length > 0) {\n for (const t of audioTracks) s.removeTrack(t);\n adoptWarmAudioStream(new MediaStream(audioTracks));\n }\n acquired = s;\n streamRef.current = s;\n setStream(s);\n setBlocked(null);\n reacquiredOnceRef.current = false;\n\n // Track health: a camera track that ENDS mid-session (OS\n // interruption, device pulled, another app grabbed it) must never\n // leave a frozen frame. Reacquire ONCE; a second death reports\n // blocked.\n for (const track of s.getVideoTracks()) {\n track.onended = () => {\n if (cancelled || streamRef.current !== s) return;\n console.error(\n \"[capture:engine] camera track ENDED (OS interruption / device change) — reacquiring.\",\n );\n if (reacquiredOnceRef.current) {\n setBlocked({ reason: \"not-supported\" });\n return;\n }\n reacquiredOnceRef.current = true;\n setAcquireToken((n) => n + 1);\n };\n }\n\n // Learn whether a flip target exists (labels need a granted state —\n // we just acquired, so this is the right moment).\n const devices = await navigator.mediaDevices.enumerateDevices();\n if (!cancelled) {\n setCameras(devices.filter((d) => d.kind === \"videoinput\"));\n }\n } catch (err: unknown) {\n if (cancelled) return;\n setBlocked({ reason: classifyCameraBlockReason(err) });\n }\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 }, [deviceId, initialFacing, withAudio, acquireToken]);\n\n const cameraBlocked = blocked !== null;\n\n // ── Video-mode mic warm hold (one prompt per medium on iOS Safari) ───────\n useEffect(() => {\n if (mode !== \"video\" || cameraBlocked || !withAudio) return;\n let cancelled = false;\n let held = false;\n void (async () => {\n if ((await queryMicPermission()) === \"denied\") return;\n if (cancelled) return;\n acquireWarmMic()\n .then(() => {\n if (cancelled) {\n releaseWarmMic();\n return;\n }\n held = true;\n })\n .catch(() => {\n // Denied/unavailable — the record path surfaces its own error.\n });\n })();\n return () => {\n cancelled = true;\n if (held) releaseWarmMic();\n };\n }, [mode, cameraBlocked, withAudio]);\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 }\n };\n }, []);\n\n // ── Photo ────────────────────────────────────────────────────────────────\n const capturePhotoWith = useCallback(\n (opts: { fileNamePrefix: string; aspect?: CaptureAspect }) => {\n const video = videoRef.current;\n if (!video || video.videoWidth === 0) return;\n // One capture at a time: a double-tap on the shutter must not produce\n // two near-identical frames.\n if (captureBusyRef.current) return;\n captureBusyRef.current = true;\n void (async () => {\n try {\n // Full-sensor shutter behind the cropped preview (§2 policy 6);\n // the aspect crop (when selected) is applied to that full frame.\n const canvas = document.createElement(\"canvas\");\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"2d canvas context unavailable\");\n ctx.drawImage(video, 0, 0);\n const full = await new Promise<Blob>((resolve, reject) =>\n canvas.toBlob(\n (b) => (b ? resolve(b) : reject(new Error(\"toBlob returned null\"))),\n \"image/jpeg\",\n photoQuality,\n ),\n );\n const blob = await cropBlobToAspect(\n full,\n opts.aspect ?? \"full\",\n photoQuality,\n );\n callbacksRef.current.onPhoto(\n new File(\n [blob],\n `${opts.fileNamePrefix}-${new Date().toISOString()}.jpg`,\n { type: \"image/jpeg\" },\n ),\n );\n setFlash(true);\n window.setTimeout(() => setFlash(false), 120);\n } catch (err) {\n reportError(\"The photo could not be captured — try again.\", err);\n } finally {\n captureBusyRef.current = false;\n }\n })();\n },\n [photoQuality, reportError],\n );\n\n const onCapturePhoto = useCallback(\n (opts?: { aspect?: CaptureAspect }) => {\n capturePhotoWith(\n opts?.aspect\n ? { fileNamePrefix, aspect: opts.aspect }\n : { fileNamePrefix },\n );\n },\n [capturePhotoWith, fileNamePrefix],\n );\n\n // ── Video ────────────────────────────────────────────────────────────────\n const onStartRecording = useCallback(() => {\n const base = streamRef.current;\n if (!base || recorderRef.current) return;\n void (async () => {\n // Mic discipline (the proven runtime contract): acquire the WARM mic\n // once, CLONE its track into the composed stream, stop only the clones\n // on exit, and release the hold exactly once. Stopping the shared\n // track would kill the warm grant for every other holder.\n let micClones: MediaStreamTrack[] = [];\n let micHeld = false;\n if (withAudio) {\n try {\n const mic = await acquireWarmMic();\n micHeld = true;\n micClones = mic.getAudioTracks().map((t) => t.clone());\n } catch {\n // Mic denied/unavailable — record video-only rather than failing\n // the capture.\n }\n }\n const composed = new MediaStream([...base.getVideoTracks(), ...micClones]);\n const mime = RECORDING_MIME_LADDER.find(\n (m) =>\n typeof MediaRecorder !== \"undefined\" && MediaRecorder.isTypeSupported(m),\n );\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(\n composed,\n mime ? { mimeType: mime } : undefined,\n );\n } catch (err) {\n micClones.forEach((t) => t.stop());\n if (micHeld) releaseWarmMic();\n reportError(\"Could not start the video recording.\", err);\n return;\n }\n const entry = {\n recorder,\n chunks: [] as Blob[],\n startedAt: performance.now(),\n micClones,\n micHeld,\n timer: setInterval(() => {\n // Monotonic elapsed clock — performance.now(), never chunk arrival.\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.micClones.forEach((t) => t.stop());\n if (entry.micHeld) releaseWarmMic();\n recorderRef.current = null;\n setRecording(false);\n const captured = finalizeCapturedVideo({\n parts: entry.chunks,\n // Emitted chunk MIME is stronger evidence than recorder.mimeType.\n emittedMime: entry.chunks.find((chunk) => chunk.type)?.type,\n recorderMime: recorder.mimeType,\n durationMs: performance.now() - entry.startedAt,\n fileNamePrefix,\n });\n callbacksRef.current.onVideo(captured.file, captured.durationMs);\n };\n recorder.start(1000);\n setRecordElapsedSeconds(0);\n setRecording(true);\n })();\n }, [withAudio, fileNamePrefix, reportError]);\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 // ── Flip: cycle real devices, never mid-recording ────────────────────────\n const onFlipCamera = useCallback(() => {\n // A flip reacquires the stream, which would kill a live recording. The\n // button is hidden while recording; this guard backstops it.\n if (recorderRef.current) return;\n const current =\n deviceId ??\n streamRef.current?.getVideoTracks()[0]?.getSettings().deviceId ??\n null;\n const next = nextCameraDevice(cameras, current);\n if (next) setDeviceId(next.deviceId);\n }, [cameras, deviceId]);\n\n const canFlip = cameras.length > 1 && !cameraBlocked && !recording;\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: canFlip ? onFlipCamera : null,\n flash,\n capturePhotoWith,\n }),\n [\n stream,\n blocked,\n onCapturePhoto,\n onStartRecording,\n onStopRecording,\n recording,\n recordElapsedSeconds,\n onUpload,\n onFlipCamera,\n canFlip,\n flash,\n capturePhotoWith,\n ],\n );\n}\n","/**\n * src/engine/crop.ts — the ONE aspect-crop implementation.\n *\n * Center-crops a captured JPEG to the requested output aspect. The crop is\n * applied to the FULL sensor frame (honest pixels, not a preview grab) —\n * §2 policy 6 of the chrome: the shutter always captures full-sensor and the\n * selected aspect is a REAL crop of that frame. Ported from the matrx-frontend\n * host adapter (C22 retrofit, 2026-08-30) so every host shares one geometry.\n */\n\nimport type { CaptureAspect } from \"../types\";\n\nexport const PHOTO_JPEG_QUALITY = 0.92;\n\nexport async function cropBlobToAspect(\n blob: Blob,\n aspect: CaptureAspect,\n quality: number = PHOTO_JPEG_QUALITY,\n): Promise<Blob> {\n if (aspect === \"full\") return blob;\n const [wRatio, hRatio] =\n aspect === \"1:1\" ? [1, 1] : aspect === \"4:3\" ? [4, 3] : [16, 9];\n const bitmap = await createImageBitmap(blob);\n try {\n const srcW = bitmap.width;\n const srcH = bitmap.height;\n // Orient the target ratio to the frame (portrait sensor → portrait crop).\n const target = srcW >= srcH ? wRatio / hRatio : hRatio / wRatio;\n let cropW = srcW;\n let cropH = Math.round(srcW / target);\n if (cropH > srcH) {\n cropH = srcH;\n cropW = Math.round(srcH * target);\n }\n if (cropW === srcW && cropH === srcH) return blob;\n const canvas = document.createElement(\"canvas\");\n canvas.width = cropW;\n canvas.height = cropH;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return blob;\n ctx.drawImage(\n bitmap,\n Math.round((srcW - cropW) / 2),\n Math.round((srcH - cropH) / 2),\n cropW,\n cropH,\n 0,\n 0,\n cropW,\n cropH,\n );\n return await new Promise<Blob>((resolve) =>\n canvas.toBlob((out) => resolve(out ?? blob), \"image/jpeg\", quality),\n );\n } finally {\n bitmap.close();\n }\n}\n\n/**\n * Cycle to the next camera in a device list — the flip algorithm the host\n * adapter and the default engine share. Returns null when there is nothing\n * to flip to (fewer than two cameras).\n */\nexport function nextCameraDevice<T extends { deviceId: string }>(\n cameras: readonly T[],\n currentDeviceId: string | null | undefined,\n): T | null {\n if (cameras.length < 2) return null;\n const currentIdx = currentDeviceId\n ? cameras.findIndex((c) => c.deviceId === currentDeviceId)\n : -1;\n return cameras[(Math.max(currentIdx, 0) + 1) % cameras.length] ?? null;\n}\n","/**\n * src/engine/permissions.ts — permission pre-checks + error classification\n * for the default engine (and for hosts, so they stop re-implementing the\n * NotAllowedError branch — the C22 rule: quirk branches live HERE).\n */\n\nexport type MediaPermissionState = \"granted\" | \"denied\" | \"prompt\" | \"unknown\";\n\nasync function queryPermission(name: string): Promise<MediaPermissionState> {\n if (typeof navigator === \"undefined\" || !navigator.permissions) {\n return \"unknown\";\n }\n try {\n const status = await navigator.permissions.query({\n name: name as PermissionName,\n });\n if (status.state === \"granted\") return \"granted\";\n if (status.state === \"denied\") return \"denied\";\n return \"prompt\";\n } catch {\n // Permissions API doesn't support this name here (Safari) — unknown, so\n // callers fall back to the real getUserMedia outcome.\n return \"unknown\";\n }\n}\n\n/**\n * Pre-check the camera permission WITHOUT prompting. A known-\"denied\" state\n * lets a surface render its how-to-re-enable explainer instead of hammering\n * getUserMedia with calls that instantly reject (the proven matrx-frontend\n * pre-check contract). \"unknown\" (Safari) means: just try — the gUM outcome\n * is the truth there.\n */\nexport function queryCameraPermission(): Promise<MediaPermissionState> {\n return queryPermission(\"camera\");\n}\n\n/** Same pre-check for the microphone — drives the combined-prompt decision. */\nexport function queryMicPermission(): Promise<MediaPermissionState> {\n return queryPermission(\"microphone\");\n}\n\n/**\n * Pure decision for the combined camera+mic prompt: only worth it when a mic\n * prompt would actually appear (\"prompt\"/\"unknown\"). \"granted\" needs no\n * prompt; \"denied\" would reject the WHOLE combined call and take the camera\n * with it.\n */\nexport function shouldCombineMicPrompt(\n micPermissionState: MediaPermissionState,\n): boolean {\n return micPermissionState === \"prompt\" || micPermissionState === \"unknown\";\n}\n\n/** True when a getUserMedia rejection means the USER/OS denied permission\n * (a missing device is not a denial). */\nexport function isMediaDenialError(err: unknown): boolean {\n const name =\n err && typeof err === \"object\" && \"name\" in err\n ? String((err as { name: unknown }).name)\n : \"\";\n return name === \"NotAllowedError\" || name === \"SecurityError\";\n}\n\n/**\n * Classify a camera-acquisition failure into the engine's `blocked` reason.\n * The ONE place the NotAllowedError/SecurityError branch lives — hosts adapt\n * their runtime errors through this instead of re-implementing it.\n */\nexport function classifyCameraBlockReason(\n err: unknown,\n): \"permission-denied\" | \"not-supported\" {\n return isMediaDenialError(err) ? \"permission-denied\" : \"not-supported\";\n}\n","/**\n * src/engine/warm-mic.ts — the package's warm microphone manager.\n *\n * Ported VERBATIM (2026-08-30, the C22/C23 retrofit) from matrx-frontend's\n * proven `features/audio/micStream.ts` — the module that encodes the real\n * iOS Safari device pain the default engine previously did not have:\n *\n * WHY THIS EXISTS\n * ---------------\n * Every recording surface used to call `navigator.mediaDevices.getUserMedia`\n * itself and fully `track.stop()` the stream when it finished. On mobile\n * (notably iOS Safari) that produces a permission/\"in use\" prompt on *every*\n * single recording, because the previous grant is torn down before the next\n * acquisition. Tapping record three times = three prompts.\n *\n * This manager keeps ONE stream warm and hands it to every caller. After the\n * last holder releases it, the stream is kept alive for a SHORT keepalive\n * window; if another recording starts within that window, the same live\n * stream (and the same OS grant) is reused — no second prompt. Only after the\n * window elapses with no holders is the stream actually stopped (mic light\n * off), so we don't hold the mic open forever.\n *\n * KEEPALIVE IS DELIBERATELY SHORT (a few seconds). Minutes keep the browser's\n * recording indicator lit long after the user stopped — alarming and abnormal\n * (\"why is my mic still on?\"). A few seconds still coalesces a quick\n * stop→re-record (the iOS re-prompt case) while clearing the mic light almost\n * immediately once the user is actually done. On desktop, re-acquiring after\n * release does NOT re-prompt (the origin's grant persists), so a short window\n * costs nothing there.\n *\n * THE FOUR iOS-SAFARI RECOVERY BRANCHES (`MicInterruptionReason`), each of\n * which used to be a silent audio drop:\n * 1. `ended` — the OS killed the track (lock screen, incoming\n * call, app switch, device unplug). The warm grant\n * is gone; the dead stream is dropped so the next\n * acquire re-acquires (and WILL re-prompt).\n * 2. `muted` — transient interruption (iOS mutes during a\n * call). The grant survives; no re-prompt.\n * 3. `unmuted` — the transient interruption ended.\n * 4. `permission-revoked` — the user/OS pulled the permission; the warm\n * stream is hard-stopped NOW.\n * All four are reported loudly on the interruption channel so a recording\n * surface can react instead of just dropping audio.\n *\n * Reference-counted: concurrent holders (an analyser tap + a recorder) are\n * fine; the stream is only eligible for release when the count hits zero.\n * Callers MUST NOT call `track.stop()` on the returned stream — call\n * `releaseWarmMic()` instead.\n *\n * State lives on `globalThis` under a `Symbol.for` slot (the media-cache\n * pattern) — a dual ESM/CJS module graph must still see ONE mic manager.\n */\n\ntype Listener = (state: MicStreamState) => void;\n\nexport type MicInterruptionReason =\n | \"ended\"\n | \"muted\"\n | \"unmuted\"\n | \"permission-revoked\";\n\ntype InterruptionListener = (reason: MicInterruptionReason) => void;\n\nexport type MicStreamState =\n | \"idle\"\n | \"acquiring\"\n | \"active\"\n | \"keepalive\"\n | \"error\";\n\nconst DEFAULT_KEEPALIVE_MS = 6_000; // a few seconds — clear the mic light promptly\n\ninterface ManagerInternal {\n stream: MediaStream | null;\n inFlight: Promise<MediaStream> | null;\n refCount: number;\n releaseTimer: ReturnType<typeof setTimeout> | null;\n state: MicStreamState;\n keepAliveMs: number;\n /** The host's chosen input device, applied as an `{ideal}` constraint on\n * the NEXT acquire. null = system default. */\n preferredInputDeviceId: string | null;\n listeners: Set<Listener>;\n interruptionListeners: Set<InterruptionListener>;\n pageLifecycleWatched: boolean;\n permissionWatched: boolean;\n}\n\nconst STATE_SLOT = Symbol.for(\"ai-matrx.capture.warm-mic-state\");\n\nfunction getManager(): ManagerInternal {\n const holder = globalThis as Record<symbol, ManagerInternal | undefined>;\n let m = holder[STATE_SLOT];\n if (!m) {\n m = {\n stream: null,\n inFlight: null,\n refCount: 0,\n releaseTimer: null,\n state: \"idle\",\n keepAliveMs: DEFAULT_KEEPALIVE_MS,\n preferredInputDeviceId: null,\n listeners: new Set(),\n interruptionListeners: new Set(),\n pageLifecycleWatched: false,\n permissionWatched: false,\n };\n holder[STATE_SLOT] = m;\n }\n return m;\n}\n\nfunction emitInterruption(reason: MicInterruptionReason): void {\n for (const l of getManager().interruptionListeners) {\n try {\n l(reason);\n } catch {\n // never let a listener break the manager\n }\n }\n}\n\n/**\n * Subscribe to mic interruptions (track end / mute / permission loss).\n * Returns an unsubscribe fn. Surfaces are expected to make these LOUD — an\n * interruption during a recording is a real event the user must see, not a\n * silent drop.\n */\nexport function subscribeWarmMicInterruption(\n listener: InterruptionListener,\n): () => void {\n const m = getManager();\n m.interruptionListeners.add(listener);\n return () => {\n m.interruptionListeners.delete(listener);\n };\n}\n\nfunction setState(next: MicStreamState): void {\n const m = getManager();\n m.state = next;\n for (const l of m.listeners) {\n try {\n l(next);\n } catch {\n // never let a listener break the manager\n }\n }\n}\n\nfunction clearReleaseTimer(): void {\n const m = getManager();\n if (m.releaseTimer) {\n clearTimeout(m.releaseTimer);\n m.releaseTimer = null;\n }\n}\n\n/**\n * Watch the live tracks for OS-level interruptions. iOS fires these on lock,\n * incoming calls, app switches, and device changes — previously unhandled, so\n * the warm stream silently died and the next recording re-prompted with no\n * explanation. Each is reported loudly; on a hard `ended` the dead warm\n * stream is dropped so the next `acquireWarmMic` re-acquires a live one.\n */\nfunction attachTrackHealth(stream: MediaStream): void {\n const m = getManager();\n for (const track of stream.getAudioTracks()) {\n track.onended = () => {\n console.error(\n \"[capture:warm-mic] mic track ENDED (OS interruption / device change). \" +\n \"The warm grant is gone; the next recording will re-prompt.\",\n );\n if (m.stream === stream) {\n m.stream = null;\n setState(\"error\");\n }\n emitInterruption(\"ended\");\n };\n track.onmute = () => {\n console.warn(\n \"[capture:warm-mic] mic track MUTED (transient interruption — e.g. a call). \" +\n \"The grant survives; it should unmute when the interruption ends.\",\n );\n emitInterruption(\"muted\");\n };\n track.onunmute = () => {\n emitInterruption(\"unmuted\");\n };\n }\n}\n\n/**\n * Mic-permission revocation → stop the warm stream NOW and emit a loud\n * interruption. In matrx-frontend the ONE canonical permission watcher\n * (audioDevices) calls this; the package, having no host device layer, wires\n * its own minimal Permissions API watcher lazily (`watchMicPermission`) —\n * best-effort only, since Safari's Permissions API may not answer for\n * \"microphone\" (there, `ended` is the branch that fires instead).\n */\nexport function notifyMicPermissionRevoked(): void {\n console.error(\"[capture:warm-mic] microphone permission REVOKED.\");\n hardStopWarmMic();\n emitInterruption(\"permission-revoked\");\n}\n\nfunction watchMicPermission(): void {\n const m = getManager();\n if (m.permissionWatched) return;\n if (typeof navigator === \"undefined\" || !navigator.permissions) return;\n m.permissionWatched = true;\n void (async () => {\n try {\n const status = await navigator.permissions.query({\n name: \"microphone\" as PermissionName,\n });\n status.onchange = () => {\n if (status.state === \"denied\") notifyMicPermissionRevoked();\n };\n } catch {\n // Permissions API doesn't support \"microphone\" here (Safari) — the\n // track-health `ended` branch covers revocation there.\n }\n })();\n}\n\n// Page-lifecycle backstop — set up once. The browser's \"in use\" mic indicator\n// must NEVER survive the page. `pagehide` fires on tab close / navigation /\n// bfcache evict, so the warm stream is hard-stopped there even if a holder\n// leaked its release. This is a SECOND layer under the per-holder\n// release/keepalive — it firing means a release was missed (a real bug), so\n// it screams.\nfunction watchPageLifecycle(): void {\n const m = getManager();\n if (m.pageLifecycleWatched) return;\n if (typeof window === \"undefined\") return;\n m.pageLifecycleWatched = true;\n window.addEventListener(\"pagehide\", () => {\n if (m.stream || m.refCount > 0) {\n if (m.refCount > 0) {\n console.error(\n `[capture:warm-mic] page hiding with ${m.refCount} unreleased mic holder(s) — ` +\n \"forcing release. A surface leaked acquireWarmMic without a \" +\n \"matching releaseWarmMic on unmount.\",\n );\n }\n hardStopWarmMic();\n }\n });\n}\n\n/**\n * Set the preferred microphone. Applied as an `{ideal}` deviceId constraint\n * on the NEXT acquire — it does NOT switch a live stream (call\n * `hardStopWarmMic()` then re-acquire to switch mid-session). Pass null to\n * fall back to the system default.\n */\nexport function setPreferredMicDeviceId(id: string | null): void {\n getManager().preferredInputDeviceId = id;\n}\n\n/** True if the warm stream still has at least one live audio track. */\nfunction streamIsLive(stream: MediaStream | null): stream is MediaStream {\n if (!stream) return false;\n const tracks = stream.getAudioTracks();\n return tracks.length > 0 && tracks.every((t) => t.readyState === \"live\");\n}\n\n/**\n * The default audio constraints this manager acquires with — exported so the\n * default engine's COMBINED `getUserMedia({video, audio})` prompt (one\n * browser prompt for both permissions instead of camera-then-mic) requests\n * the mic with exactly the same processing + preferred-device shape the\n * singleton itself would use.\n */\nexport function buildWarmMicConstraints(): MediaTrackConstraints {\n const m = getManager();\n const audio: MediaTrackConstraints = {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n };\n if (m.preferredInputDeviceId) {\n audio.deviceId = { ideal: m.preferredInputDeviceId };\n }\n return audio;\n}\n\n/**\n * Adopt an audio stream acquired ELSEWHERE (the default engine's combined\n * camera+mic prompt) as this manager's warm stream, so the grant that\n * combined prompt just earned is banked exactly like one of our own: the\n * next `acquireWarmMic()` inside the keepalive window reuses it with no\n * second prompt. If a warm stream already exists (or one is being acquired),\n * the incoming tracks are stopped immediately — never two live mic streams.\n * With zero holders the adopted stream is parked in the keepalive window, so\n * the mic light clears on the normal schedule.\n */\nexport function adoptWarmAudioStream(stream: MediaStream): void {\n const m = getManager();\n if (streamIsLive(m.stream) || m.inFlight) {\n for (const t of stream.getTracks()) {\n try {\n t.stop();\n } catch {\n // ignore\n }\n }\n return;\n }\n if (!streamIsLive(stream)) return; // dead on arrival — nothing to bank\n clearReleaseTimer();\n m.stream = stream;\n attachTrackHealth(stream);\n watchPageLifecycle();\n watchMicPermission();\n if (m.refCount > 0) {\n setState(\"active\");\n } else {\n armKeepaliveStop();\n }\n}\n\n/**\n * Acquire the shared warm mic stream, incrementing the holder count. The\n * returned stream is shared — DO NOT stop its tracks. Call `releaseWarmMic()`\n * when done. If a warm stream exists (active or in its keepalive window) it\n * is reused with no new permission prompt.\n */\nexport async function acquireWarmMic(\n constraints?: MediaTrackConstraints,\n): Promise<MediaStream> {\n const m = getManager();\n clearReleaseTimer();\n m.refCount += 1;\n\n // Reuse a still-live warm stream — the whole point of this manager.\n if (streamIsLive(m.stream)) {\n setState(\"active\");\n return m.stream;\n }\n\n // A previous warm stream died (device unplugged, OS revoke) — drop it.\n m.stream = null;\n\n if (m.inFlight) {\n // Coalesce concurrent acquisitions onto the same getUserMedia call.\n return m.inFlight;\n }\n\n setState(\"acquiring\");\n const audio: MediaTrackConstraints = constraints ?? buildWarmMicConstraints();\n if (m.preferredInputDeviceId && audio.deviceId == null) {\n audio.deviceId = { ideal: m.preferredInputDeviceId };\n }\n\n m.inFlight = (async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio });\n m.stream = stream;\n attachTrackHealth(stream);\n watchPageLifecycle();\n watchMicPermission();\n // Every holder released while gUM was in flight → nobody owns this\n // fresh stream. Without this, the manager would sit \"active\" with zero\n // holders and zero timers FOREVER (mic light on until refresh). Park it\n // in the keepalive window instead so it stops like any last release.\n if (m.refCount === 0) {\n armKeepaliveStop();\n return stream;\n }\n setState(\"active\");\n return stream;\n } catch (err) {\n // Acquisition failed — this holder doesn't actually hold anything.\n m.refCount = Math.max(0, m.refCount - 1);\n setState(\"error\");\n throw err;\n } finally {\n m.inFlight = null;\n }\n })();\n\n return m.inFlight;\n}\n\n/**\n * Release a previously-acquired hold. When the last holder releases, the\n * stream is kept warm for the keepalive window, then actually stopped.\n */\nexport function releaseWarmMic(): void {\n const m = getManager();\n if (m.refCount === 0) {\n // LOUD: an unbalanced release is never harmless — it either means a leak\n // elsewhere went uncounted, or (worse) it will steal the hold of the NEXT\n // acquirer, killing a live recording seconds in. Scream, don't floor.\n console.error(\n \"[capture:warm-mic] releaseWarmMic() with zero holders — unbalanced release. \" +\n \"Some caller released twice (or released a hold it never acquired).\",\n );\n return;\n }\n m.refCount -= 1;\n if (m.refCount > 0) return;\n\n if (!m.stream) {\n // Nothing live yet. If an acquisition is still in flight, its resolution\n // sees refCount === 0 and parks the fresh stream in keepalive → stop.\n setState(m.inFlight ? \"keepalive\" : \"idle\");\n return;\n }\n\n armKeepaliveStop();\n}\n\n/** Park the stream in the keepalive window; hard-stop when it elapses with\n * no holders. The single exit used by both the normal last-release path and\n * the released-while-acquiring path. */\nfunction armKeepaliveStop(): void {\n const m = getManager();\n clearReleaseTimer();\n setState(\"keepalive\");\n m.releaseTimer = setTimeout(() => {\n m.releaseTimer = null;\n // Re-check: a holder may have re-acquired during the window.\n if (m.refCount > 0) return;\n hardStopWarmMic();\n }, m.keepAliveMs);\n}\n\n/**\n * Immediately stop the warm stream regardless of keepalive. Use only when\n * the mic must be released NOW; normal teardown uses `releaseWarmMic()`.\n */\nexport function hardStopWarmMic(): void {\n const m = getManager();\n clearReleaseTimer();\n if (m.stream) {\n for (const t of m.stream.getTracks()) {\n try {\n t.stop();\n } catch {\n // ignore\n }\n }\n m.stream = null;\n }\n m.refCount = 0;\n setState(\"idle\");\n}\n\nexport function getWarmMicState(): MicStreamState {\n return getManager().state;\n}\n\nexport function subscribeWarmMic(listener: Listener): () => void {\n const m = getManager();\n m.listeners.add(listener);\n return () => {\n m.listeners.delete(listener);\n };\n}\n\n/** Diagnostics snapshot for debug panels. */\nexport function warmMicDebug(): {\n state: MicStreamState;\n refCount: number;\n live: boolean;\n keepAliveMs: number;\n} {\n const m = getManager();\n return {\n state: m.state,\n refCount: m.refCount,\n live: streamIsLive(m.stream),\n keepAliveMs: m.keepAliveMs,\n };\n}\n","/**\n * The one terminal video contract shared by the package-owned MediaRecorder\n * engine and host adapters. Emitted bytes are stronger MIME evidence than a\n * requested/recorder format, and a delivered recording always carries a\n * positive integer duration.\n */\n\nconst VIDEO_EXTENSIONS: Readonly<Record<string, string>> = {\n \"video/mp4\": \"mp4\",\n \"video/webm\": \"webm\",\n \"video/ogg\": \"ogv\",\n \"video/quicktime\": \"mov\",\n};\n\nexport interface FinalizeCapturedVideoOptions {\n parts: readonly BlobPart[];\n /** MIME observed on emitted/final Blob data — the strongest authority. */\n emittedMime?: string | null | undefined;\n /** MediaRecorder-reported MIME — fallback when emitted bytes have none. */\n recorderMime?: string | null | undefined;\n durationMs: number;\n fileNamePrefix: string;\n /** Injectable filename clock for deterministic tests. */\n timestampMs?: number;\n}\n\nexport interface CapturedVideoResult {\n file: File;\n mime: string;\n durationMs: number;\n}\n\nfunction firstNonEmpty(...values: Array<string | null | undefined>): string {\n for (const value of values) {\n const normalized = value?.trim();\n if (normalized) return normalized;\n }\n return \"video/webm\";\n}\n\n/**\n * Finalize recording bytes without letting requested MIME, filename, and\n * duration drift apart. Unknown/non-video formats fail loudly rather than\n * producing a mislabeled upload.\n */\nexport function finalizeCapturedVideo(\n options: FinalizeCapturedVideoOptions,\n): CapturedVideoResult {\n if (!Number.isFinite(options.durationMs)) {\n throw new Error(\"finalizeCapturedVideo requires a finite duration.\");\n }\n\n const mime = firstNonEmpty(options.emittedMime, options.recorderMime);\n const container = mime.split(\";\")[0]?.trim().toLowerCase() ?? \"\";\n const extension = VIDEO_EXTENSIONS[container];\n if (!extension) {\n throw new Error(\n `finalizeCapturedVideo requires a recognized video MIME; received \"${mime}\".`,\n );\n }\n\n const durationMs = Math.max(1, Math.round(options.durationMs));\n const blob = new Blob([...options.parts], { type: mime });\n const timestampMs = options.timestampMs ?? Date.now();\n const file = new File(\n [blob],\n `${options.fileNamePrefix}-video-${timestampMs}.${extension}`,\n { type: mime },\n );\n return { file, mime, durationMs };\n}\n","\"use client\";\n\n/**\n * CloudLibrarySheet — the recents-thumb destination: where the iPhone opens\n * the camera roll, we open the user's CLOUD media in a full-screen tiled\n * gallery — dark chrome to match the camera, square tiles, newest first.\n *\n * Absorbed from the matrx-frontend host (C22/C23 retrofit, 2026-08-30):\n * the 206-line host sheet was pure chrome over three injectable facts — the\n * item list, the thumbnail renderer, and where a tile navigates. The chrome\n * now ships here; the host injects only data and identity:\n *\n * <CloudLibrarySheet\n * open={open} onClose={close}\n * loading={treeLoading}\n * items={files.map(f => ({ id, fileName, kind, thumbnail: <MyThumb/> }))}\n * onOpenItem={(id) => router.push(`/files/f/${id}`)}\n * onUpload={openPicker} // v3 hosts — upload lives INSIDE the library\n * />\n *\n * Every tile OPENS the real file (no dead ends), and an empty gallery whose\n * only affordance is \"close\" is a dead end too — with `onUpload` supplied,\n * the empty state offers the one thing a user can do from here.\n */\n\nimport React, { useEffect, useState } from \"react\";\nimport type { CaptureCloudLibraryItem } from \"../types\";\nimport { safeBottom, safeTop } from \"../safe-area\";\nimport {\n ImageOffIcon,\n Loader2Icon,\n PlayIcon,\n UploadIcon,\n XIcon,\n} from \"./icons\";\n\nexport interface CloudLibrarySheetProps {\n open: boolean;\n onClose: () => void;\n /** Newest-first media items. The host supplies the thumbnail node — the\n * sheet never fetches (law 1: no network in this package). */\n items: readonly CaptureCloudLibraryItem[];\n /** True while the host's library data is still loading. */\n loading: boolean;\n /** Opening a tile (host navigation) — a busy overlay renders while true. */\n busy?: boolean;\n /** A tile was tapped. The host opens the REAL file route (no dead ends). */\n onOpenItem: (id: string) => void;\n /**\n * Pick files from the device. Supplied by v3 hosts, where this drawer is\n * the ONLY door to existing media (Arman, 2026-08-30: \"we don't need both\n * upload and cloud because we can modify the drawer that has the cloud\n * images to just show an option for uploading\").\n *\n * Two controls that both mean \"media I already have\" is a choice the user\n * should never have been asked to make: whether a file happens to be in\n * the cloud yet is our bookkeeping, not their mental model. Omitted by v2\n * hosts, which still carry a separate UPLOAD lane in the mode row.\n */\n onUpload?: (() => void) | undefined;\n /** Sheet title. Default \"Your media\". */\n title?: string;\n}\n\n/** Tiles rendered per page — a whole cloud tree at once meant thousands of\n * mounted thumbnails and as many fetches. */\nconst PAGE_SIZE = 60;\n\nexport function CloudLibrarySheet({\n open,\n onClose,\n items,\n loading,\n busy = false,\n onOpenItem,\n onUpload,\n title = \"Your media\",\n}: CloudLibrarySheetProps) {\n const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);\n\n // Reopening starts back at the first page (fresh scroll position too).\n useEffect(() => {\n if (open) setVisibleCount(PAGE_SIZE);\n }, [open]);\n\n if (!open) return null;\n\n const uploadThenClose = onUpload\n ? () => {\n // Close first: the picker is a system sheet, and leaving the library\n // mounted behind it means the user lands back in a gallery that has\n // not yet heard about their new file.\n onClose();\n onUpload();\n }\n : null;\n\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col bg-black\">\n <div\n className=\"flex shrink-0 items-center justify-between bg-black/80 px-4\"\n style={safeTop}\n >\n <h2 className=\"py-4 text-[17px] font-semibold text-white\">{title}</h2>\n <div className=\"flex items-center gap-2\">\n {uploadThenClose ? (\n <button\n type=\"button\"\n onClick={uploadThenClose}\n className=\"flex h-10 touch-manipulation items-center gap-1.5 rounded-full bg-white/10 px-3.5 text-[13px] font-medium text-white transition-colors hover:bg-white/20\"\n >\n <UploadIcon className=\"h-4 w-4\" />\n Upload\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close library\"\n className=\"flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20\"\n >\n <XIcon className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n\n <div\n className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-1\"\n style={safeBottom}\n >\n {loading ? (\n <div className=\"flex h-full items-center justify-center\">\n <Loader2Icon className=\"h-6 w-6 animate-spin text-white/60\" />\n </div>\n ) : items.length === 0 ? (\n <div className=\"flex h-full flex-col items-center justify-center gap-3 text-white/60\">\n <ImageOffIcon className=\"h-8 w-8\" />\n <p className=\"text-sm\">No photos or videos in your cloud yet.</p>\n {/* An empty gallery whose only affordance is \"close\" is a dead\n end — the one thing a user can do from here is add something. */}\n {uploadThenClose ? (\n <button\n type=\"button\"\n onClick={uploadThenClose}\n className=\"flex h-11 touch-manipulation items-center gap-2 rounded-full bg-white/10 px-5 text-sm font-medium text-white transition-colors hover:bg-white/20\"\n >\n <UploadIcon className=\"h-4 w-4\" />\n Upload from this device\n </button>\n ) : null}\n </div>\n ) : (\n <div className=\"grid grid-cols-3 gap-0.5 sm:grid-cols-5 md:grid-cols-7\">\n {items.slice(0, visibleCount).map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => onOpenItem(item.id)}\n aria-label={`Open ${item.fileName}`}\n className=\"relative aspect-square overflow-hidden bg-white/5 transition-opacity active:opacity-70\"\n >\n {item.thumbnail}\n {item.kind === \"video\" && (\n <PlayIcon\n className=\"absolute bottom-1.5 left-1.5 h-4 w-4 text-white drop-shadow\"\n fill=\"currentColor\"\n />\n )}\n </button>\n ))}\n </div>\n )}\n {!loading && items.length > visibleCount && (\n <div className=\"flex justify-center py-4\">\n <button\n type=\"button\"\n onClick={() => setVisibleCount((n) => n + PAGE_SIZE)}\n className=\"h-11 touch-manipulation rounded-full bg-white/10 px-6 text-sm font-medium text-white\"\n >\n Show more ({items.length - visibleCount} left)\n </button>\n </div>\n )}\n {busy && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center bg-black/40\">\n <Loader2Icon className=\"h-6 w-6 animate-spin text-white\" />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime","formatElapsed","import_jsx_runtime","ASPECT_CYCLE","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_react","STATE_SLOT","import_react","import_jsx_runtime"]}