@ai-matrx/capture 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,4 +11,14 @@ Initial extraction from matrx-frontend `features/capture-camera/` (the iPhone-st
11
11
  - `useTrackControls` (honest torch/zoom/exposure from track capabilities).
12
12
  - THE CLOUD LAW: required `CaptureCloudPort`; laws test bans network, gUM, storage, and an optional cloud port.
13
13
 
14
- Not yet published awaiting the one-time npm trusted-publisher bootstrap.
14
+ Published 2026-08-29 via tag `npm/capture/v0.1.0` (trusted publishing; the name was pre-reserved at 0.0.0).
15
+
16
+ ## 0.1.1 — 2026-08-29
17
+
18
+ Real-phone feedback pass (the bar was eating the viewfinder):
19
+
20
+ - Top bar compacted to ONE h-11 row (was h-16); smaller icons/hit targets.
21
+ - ModeSelector rebuilt as the CONNECTED sliding-thumb track (spring easing, uppercase yellow-active labels) — narrow on purpose so both sides of the row stay usable; extras join the track.
22
+ - New `slots.aboveBar`: host content rendered OVER the feed above the bottom bar (filmstrip home) — zero bar height.
23
+ - Domain action returned to the mode row (absolute right of the narrow track).
24
+ - Bottom paddings cut (mode row py-1.5, shutter row pb-1.5), thumbs 44px.
package/dist/index.d.cts CHANGED
@@ -95,8 +95,11 @@ interface CaptureCameraSlots {
95
95
  topBarCenter?: React.ReactNode;
96
96
  /** Honesty chips under the top bar (QR confirmation, etc.). */
97
97
  statusChips?: React.ReactNode;
98
+ /** Rendered OVER THE FEED just above the bottom bar (with the zoom
99
+ * pills) — costs the bar no height. The filmstrip belongs here. */
100
+ aboveBar?: React.ReactNode;
98
101
  /** Rows rendered inside the bottom bar ABOVE the mode selector
99
- * (filmstrip, notes/voice row, process button…). */
102
+ * (keep to ONE compact row bar height is stolen from the feed). */
100
103
  aboveModeSelector?: React.ReactNode;
101
104
  /** A compact action pinned right of the mode selector (Next/Break). */
102
105
  modeRowTrailing?: React.ReactNode;
package/dist/index.d.ts CHANGED
@@ -95,8 +95,11 @@ interface CaptureCameraSlots {
95
95
  topBarCenter?: React.ReactNode;
96
96
  /** Honesty chips under the top bar (QR confirmation, etc.). */
97
97
  statusChips?: React.ReactNode;
98
+ /** Rendered OVER THE FEED just above the bottom bar (with the zoom
99
+ * pills) — costs the bar no height. The filmstrip belongs here. */
100
+ aboveBar?: React.ReactNode;
98
101
  /** Rows rendered inside the bottom bar ABOVE the mode selector
99
- * (filmstrip, notes/voice row, process button…). */
102
+ * (keep to ONE compact row bar height is stolen from the feed). */
100
103
  aboveModeSelector?: React.ReactNode;
101
104
  /** A compact action pinned right of the mode selector (Next/Break). */
102
105
  modeRowTrailing?: React.ReactNode;
package/dist/react.cjs CHANGED
@@ -178,7 +178,8 @@ function ShutterButton({
178
178
 
179
179
  // src/components/ModeSelector.tsx
180
180
  var import_jsx_runtime2 = require("react/jsx-runtime");
181
- var LABEL_BASE = "touch-manipulation rounded-full px-4 py-1.5 text-[13px] font-semibold uppercase tracking-[0.12em] transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 disabled:opacity-40";
181
+ var SPRING_EASING = "linear(0, 0.0047 0.71%, 0.0189 1.44%, 0.0755 2.93%, 0.1692 4.49%, 0.3921 7.55%, 0.8121 12.94%, 0.9804 15.49%, 1.0946 18.14%, 1.1423 20.16%, 1.1568 21.62%, 1.1541 23.03%, 1.1113 26.4%, 1.0322 31.83%, 0.9902 36.25%, 0.9769 40.24%, 0.9844 45.87%, 1.0028 55.35%, 1.0075 63.42%, 1.0006 85.48%, 1)";
182
+ var LABEL_BASE = "relative z-10 flex h-8 min-w-0 touch-manipulation items-center justify-center rounded-full px-3 text-[11px] font-semibold uppercase tracking-[0.12em] transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 disabled:opacity-40";
182
183
  function ModeSelector({
183
184
  mode,
184
185
  onModeChange,
@@ -187,13 +188,28 @@ function ModeSelector({
187
188
  uploadDisabled = false,
188
189
  extraModes = []
189
190
  }) {
191
+ const segments = 3 + extraModes.length;
192
+ const activeIndex = mode === "video" ? 0 : 1;
190
193
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
191
194
  "div",
192
195
  {
193
196
  role: "tablist",
194
197
  "aria-label": "Capture mode",
195
- className: "flex items-center justify-center gap-1",
198
+ className: "relative inline-grid rounded-full bg-white/10 p-1",
199
+ style: { gridTemplateColumns: `repeat(${segments}, minmax(0, 1fr))` },
196
200
  children: [
201
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
202
+ "div",
203
+ {
204
+ "aria-hidden": true,
205
+ 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",
206
+ style: {
207
+ width: `calc((100% - 0.5rem) / ${segments})`,
208
+ transform: `translateX(${activeIndex * 100}%)`,
209
+ transitionTimingFunction: SPRING_EASING
210
+ }
211
+ }
212
+ ),
197
213
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
198
214
  "button",
199
215
  {
@@ -204,7 +220,7 @@ function ModeSelector({
204
220
  onClick: () => onModeChange("video"),
205
221
  className: cn(
206
222
  LABEL_BASE,
207
- mode === "video" ? "bg-white/15 text-[#FFCC00]" : "text-white"
223
+ mode === "video" ? "text-[#FFCC00]" : "text-white"
208
224
  ),
209
225
  children: "Video"
210
226
  }
@@ -219,7 +235,7 @@ function ModeSelector({
219
235
  onClick: () => onModeChange("photo"),
220
236
  className: cn(
221
237
  LABEL_BASE,
222
- mode === "photo" ? "bg-white/15 text-[#FFCC00]" : "text-white"
238
+ mode === "photo" ? "text-[#FFCC00]" : "text-white"
223
239
  ),
224
240
  children: "Photo"
225
241
  }
@@ -548,17 +564,17 @@ function CameraCapture({
548
564
  }
549
565
  ),
550
566
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CountdownOverlay, { seconds: countdown }),
551
- !controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute inset-x-0 top-0 z-20 bg-black/65 pt-safe backdrop-blur-[2px]", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex h-16 items-center gap-1 px-3", children: [
567
+ !controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute inset-x-0 top-0 z-20 bg-black/65 pt-safe backdrop-blur-[2px]", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex h-11 items-center gap-0.5 px-1.5", children: [
552
568
  onClose ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
553
569
  "button",
554
570
  {
555
571
  type: "button",
556
572
  onClick: onClose,
557
573
  "aria-label": "Close camera",
558
- className: "flex h-11 w-11 shrink-0 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10",
559
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.X, { className: "h-6 w-6" })
574
+ className: "flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10",
575
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.X, { className: "h-5 w-5" })
560
576
  }
561
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-11 shrink-0" }),
577
+ ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "w-10 shrink-0" }),
562
578
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "min-w-0 flex-1", children: slots.topBarCenter }),
563
579
  controls.torchSupported && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
564
580
  "button",
@@ -568,13 +584,13 @@ function CameraCapture({
568
584
  "aria-label": controls.torchOn ? "Turn flash off" : "Turn flash on",
569
585
  "aria-pressed": controls.torchOn,
570
586
  className: cn(
571
- "flex h-11 w-11 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors",
587
+ "flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors",
572
588
  controls.torchOn ? "text-[#FFCC00]" : "text-white hover:bg-white/10"
573
589
  ),
574
590
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
575
591
  import_lucide_react2.Zap,
576
592
  {
577
- className: "h-[22px] w-[22px]",
593
+ className: "h-5 w-5",
578
594
  fill: controls.torchOn ? "currentColor" : "none"
579
595
  }
580
596
  )
@@ -589,14 +605,14 @@ function CameraCapture({
589
605
  "aria-label": "More camera options",
590
606
  "aria-expanded": optionsOpen,
591
607
  className: cn(
592
- "flex h-11 w-11 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors",
608
+ "flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors",
593
609
  optionsOpen ? "bg-white/20 text-white" : "text-white hover:bg-white/10"
594
610
  ),
595
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.Grip, { className: "h-[22px] w-[22px]" })
611
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.Grip, { className: "h-5 w-5" })
596
612
  }
597
613
  )
598
614
  ] }) }),
599
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "pointer-events-none absolute inset-x-0 top-20 z-20 mt-safe flex flex-col items-center gap-2", children: [
615
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "pointer-events-none absolute inset-x-0 top-[50px] z-20 mt-safe flex flex-col items-center gap-1.5", children: [
600
616
  engine.recording && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex items-center gap-2 rounded-full bg-black/60 px-3 py-1.5 text-sm font-medium text-white", children: [
601
617
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "h-2.5 w-2.5 animate-pulse rounded-full bg-[#FF3B30]" }),
602
618
  formatElapsed(engine.recordElapsedSeconds)
@@ -604,7 +620,7 @@ function CameraCapture({
604
620
  slots.statusChips
605
621
  ] }),
606
622
  !controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "absolute inset-x-0 bottom-0 z-20", children: [
607
- !blocked && controls.zoomOptions.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
623
+ !blocked && controls.zoomOptions.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
608
624
  ZoomRow,
609
625
  {
610
626
  options: controls.zoomOptions,
@@ -612,6 +628,7 @@ function CameraCapture({
612
628
  onSelect: controls.setZoom
613
629
  }
614
630
  ) }),
631
+ slots.aboveBar && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-1.5 px-2", children: slots.aboveBar }),
615
632
  exposureOpen && controls.exposureSupported && controls.exposureRange && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mx-auto mb-3 flex w-64 items-center gap-3 rounded-full bg-black/55 px-4 py-2", children: [
616
633
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.SunMedium, { className: "h-4 w-4 shrink-0 text-[#FFCC00]" }),
617
634
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
@@ -634,26 +651,28 @@ function CameraCapture({
634
651
  ] }),
635
652
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "bg-black/65 px-3 pb-safe backdrop-blur-[2px]", children: [
636
653
  slots.aboveModeSelector,
637
- slots.modeRowTrailing && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex justify-end pb-1 pt-1.5", children: slots.modeRowTrailing }),
638
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center justify-center py-2.5", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
639
- ModeSelector,
640
- {
641
- mode,
642
- onModeChange,
643
- onUpload: engine.onUpload,
644
- modeDisabled: engine.recording,
645
- uploadDisabled: shutterDisabled && !blocked,
646
- extraModes: slots.extraModes
647
- }
648
- ) }),
649
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between px-2 pb-4 pt-1", children: [
650
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex w-16 justify-start", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
654
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative flex items-center justify-center py-1.5", children: [
655
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
656
+ ModeSelector,
657
+ {
658
+ mode,
659
+ onModeChange,
660
+ onUpload: engine.onUpload,
661
+ modeDisabled: engine.recording,
662
+ uploadDisabled: shutterDisabled && !blocked,
663
+ extraModes: slots.extraModes
664
+ }
665
+ ),
666
+ slots.modeRowTrailing && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute right-0", children: slots.modeRowTrailing })
667
+ ] }),
668
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between px-2 pb-1.5 pt-0.5", children: [
669
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex w-14 justify-start", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
651
670
  "button",
652
671
  {
653
672
  type: "button",
654
673
  onClick: cloud.onOpenLibrary,
655
674
  "aria-label": "Open your media library",
656
- className: "h-12 w-12 touch-manipulation overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/25 transition-transform active:scale-95",
675
+ className: "h-11 w-11 touch-manipulation overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/25 transition-transform active:scale-95",
657
676
  children: cloud.recentsThumb ?? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "block h-full w-full bg-white/5" })
658
677
  }
659
678
  ) }),
@@ -666,16 +685,16 @@ function CameraCapture({
666
685
  onPress: onShutter
667
686
  }
668
687
  ),
669
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex w-16 justify-end", children: engine.onFlipCamera ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
688
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex w-14 justify-end", children: engine.onFlipCamera ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
670
689
  "button",
671
690
  {
672
691
  type: "button",
673
692
  onClick: engine.onFlipCamera,
674
693
  "aria-label": "Switch camera",
675
- className: "flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/15 text-white transition-transform active:rotate-180 active:scale-95 duration-300",
694
+ 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",
676
695
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.RefreshCw, { className: "h-5 w-5" })
677
696
  }
678
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "h-12 w-12" }) })
697
+ ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "h-11 w-11" }) })
679
698
  ] })
680
699
  ] })
681
700
  ] }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts","../src/components/CameraCapture.tsx","../src/cn.ts","../src/hooks/useTrackControls.ts","../src/components/ShutterButton.tsx","../src/components/ModeSelector.tsx","../src/components/ZoomRow.tsx","../src/components/OptionsGridPanel.tsx","../src/components/CaptureSheet.tsx","../src/components/GridOverlay.tsx","../src/components/CountdownOverlay.tsx","../src/components/ImageEditSheet.tsx"],"sourcesContent":["/**\n * @ai-matrx/capture/react — the DOM chrome (all \"use client\").\n */\n\nexport { CameraCapture, type CameraCaptureProps } from \"./components/CameraCapture\";\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 { 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 type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCloudPort,\n CaptureOptionTile,\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 Grip,\n Grid3x3,\n Proportions,\n SunMedium,\n Timer,\n X,\n Zap,\n ZapOff,\n RefreshCw,\n} from \"lucide-react\";\nimport { cn } from \"../cn\";\n\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCloudPort,\n CaptureOptionTile,\n CaptureTimerSetting,\n} from \"../types\";\nimport { useTrackControls } from \"../hooks/useTrackControls\";\nimport { ShutterButton } from \"./ShutterButton\";\nimport { ModeSelector } from \"./ModeSelector\";\nimport { ZoomRow } from \"./ZoomRow\";\nimport { OptionsGridPanel } from \"./OptionsGridPanel\";\nimport { CaptureSheet, type CaptureSheetAction } from \"./CaptureSheet\";\nimport { GridOverlay } from \"./GridOverlay\";\nimport { CountdownOverlay } from \"./CountdownOverlay\";\n\nexport interface CameraCaptureProps {\n engine: CaptureCameraEngine;\n /** THE CLOUD LAW: the cloud port is REQUIRED — a camera with no cloud\n * library/persistence is not a valid instance of this system. */\n cloud: CaptureCloudPort;\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n /** The live preview element (host wires its runtime's `CameraPreview`). */\n preview: React.ReactNode;\n /** Close affordance top-left; omitted = no close button. */\n onClose?: () => void;\n /** Body + actions for the camera-blocked iOS-style sheet. */\n blockedSheet?: { body: React.ReactNode; actions: CaptureSheetAction[] };\n /** Hide all chrome except honesty chips (host renders its own toggle). */\n controlsHidden?: boolean;\n shutterDisabled?: boolean;\n slots?: CaptureCameraSlots;\n}\n\nfunction formatElapsed(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = String(totalSeconds % 60).padStart(2, \"0\");\n return `${m}:${s}`;\n}\n\nconst ASPECT_CYCLE: CaptureAspect[] = [\"full\", \"4:3\", \"1:1\", \"16:9\"];\n\nexport function CameraCapture({\n engine,\n cloud,\n mode,\n onModeChange,\n preview,\n onClose,\n blockedSheet,\n controlsHidden = false,\n shutterDisabled = false,\n slots = {},\n}: CameraCaptureProps) {\n const [optionsOpen, setOptionsOpen] = useState(false);\n const [gridOn, setGridOn] = useState(false);\n const [timerSetting, setTimerSetting] = useState<CaptureTimerSetting>(0);\n const [aspect, setAspect] = useState<CaptureAspect>(\"full\");\n const [exposureOpen, setExposureOpen] = useState(false);\n const [countdown, setCountdown] = useState<number | null>(null);\n // The blocked sheet is dismissible — uploads and the system camera keep\n // working, so closing it reveals the chrome rather than leaving the page.\n const [blockedDismissed, setBlockedDismissed] = useState(false);\n const countdownRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const controls = useTrackControls(engine.stream);\n\n const clearCountdown = useCallback(() => {\n if (countdownRef.current) clearInterval(countdownRef.current);\n countdownRef.current = null;\n setCountdown(null);\n }, []);\n useEffect(() => clearCountdown, [clearCountdown]);\n\n const onShutter = useCallback(() => {\n if (mode === \"video\") {\n if (engine.recording) engine.onStopRecording();\n else engine.onStartRecording();\n return;\n }\n if (countdown !== null) {\n // Tapping mid-countdown cancels — matching iOS.\n clearCountdown();\n return;\n }\n if (timerSetting === 0) {\n engine.onCapturePhoto({ aspect });\n return;\n }\n let remaining = timerSetting;\n setCountdown(remaining);\n countdownRef.current = setInterval(() => {\n remaining -= 1;\n if (remaining <= 0) {\n clearCountdown();\n engine.onCapturePhoto({ aspect });\n } else {\n setCountdown(remaining);\n }\n }, 1000);\n }, [mode, engine, countdown, timerSetting, aspect, clearCountdown]);\n\n const cycleTimer = useCallback(() => {\n setTimerSetting((t) => (t === 0 ? 3 : t === 3 ? 10 : 0));\n }, []);\n\n const coreTiles: CaptureOptionTile[] = [\n ...(controls.torchSupported\n ? [\n {\n id: \"flash\",\n label: \"Flash\",\n icon: controls.torchOn ? (\n <Zap className=\"h-6 w-6\" fill=\"currentColor\" />\n ) : (\n <ZapOff className=\"h-6 w-6\" />\n ),\n active: controls.torchOn,\n onPress: controls.toggleTorch,\n },\n ]\n : []),\n {\n id: \"timer\",\n label: \"Timer\",\n icon: <Timer className=\"h-6 w-6\" />,\n active: timerSetting !== 0,\n valueLabel: timerSetting === 0 ? undefined : `${timerSetting}s`,\n onPress: cycleTimer,\n },\n {\n id: \"grid\",\n label: \"Grid\",\n icon: <Grid3x3 className=\"h-6 w-6\" />,\n active: gridOn,\n onPress: () => setGridOn((g) => !g),\n },\n // Aspect applies to PHOTO output (center-cropped from the full sensor).\n {\n id: \"aspect\",\n label: \"Aspect\",\n icon: <Proportions className=\"h-6 w-6\" />,\n active: aspect !== \"full\",\n valueLabel: aspect === \"full\" ? undefined : aspect,\n onPress: () =>\n setAspect(\n (a) =>\n ASPECT_CYCLE[\n (ASPECT_CYCLE.indexOf(a) + 1) % ASPECT_CYCLE.length\n ] ?? \"full\",\n ),\n },\n ...(controls.exposureSupported\n ? [\n {\n id: \"exposure\",\n label: \"Exposure\",\n icon: <SunMedium className=\"h-6 w-6\" />,\n active: controls.exposure !== 0 || exposureOpen,\n valueLabel:\n controls.exposure === 0\n ? undefined\n : `${controls.exposure > 0 ? \"+\" : \"\"}${controls.exposure}`,\n onPress: () => setExposureOpen((o) => !o),\n },\n ]\n : []),\n ];\n const tiles = [...coreTiles, ...(slots.optionTiles ?? [])];\n\n const blocked = engine.blocked !== null;\n\n return (\n <div className=\"absolute inset-0 select-none overflow-hidden bg-black\">\n {/* Full-bleed feed */}\n <div className=\"absolute inset-0\">{preview}</div>\n <GridOverlay visible={gridOn && !blocked} />\n {/* Aspect framing hint — the photo output is center-cropped to the\n selected ratio; the dimmed bands approximate the discarded region\n of the VISIBLE frame (the capture itself crops the full sensor). */}\n {mode === \"photo\" && aspect !== \"full\" && !blocked && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center\"\n >\n <div\n className=\"shadow-[0_0_0_9999px_rgba(0,0,0,0.45)]\"\n style={{\n aspectRatio:\n aspect === \"1:1\" ? \"1 / 1\" : aspect === \"4:3\" ? \"3 / 4\" : \"9 / 16\",\n width: aspect === \"16:9\" ? \"100%\" : undefined,\n height: aspect === \"16:9\" ? undefined : \"70%\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n }}\n />\n </div>\n )}\n <CountdownOverlay seconds={countdown} />\n\n {/* Top bar — semi-transparent near-black, feed visible through it. */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 top-0 z-20 bg-black/65 pt-safe backdrop-blur-[2px]\">\n <div className=\"flex h-16 items-center gap-1 px-3\">\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close camera\"\n className=\"flex h-11 w-11 shrink-0 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10\"\n >\n <X className=\"h-6 w-6\" />\n </button>\n ) : (\n <span className=\"w-11 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-11 w-11 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n controls.torchOn\n ? \"text-[#FFCC00]\"\n : \"text-white hover:bg-white/10\",\n )}\n >\n <Zap\n className=\"h-[22px] w-[22px]\"\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-11 w-11 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n optionsOpen ? \"bg-white/20 text-white\" : \"text-white hover:bg-white/10\",\n )}\n >\n <Grip className=\"h-[22px] w-[22px]\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Honesty chips — visible even with controls hidden. */}\n <div className=\"pointer-events-none absolute inset-x-0 top-20 z-20 mt-safe flex flex-col items-center gap-2\">\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-3\">\n <ZoomRow\n options={controls.zoomOptions}\n value={controls.zoom}\n onSelect={controls.setZoom}\n />\n </div>\n )}\n {/* Exposure slider — revealed by the EXPOSURE tile, floats above\n the bottom bar like the iOS exposure control. */}\n {exposureOpen && controls.exposureSupported && controls.exposureRange && (\n <div className=\"mx-auto mb-3 flex w-64 items-center gap-3 rounded-full bg-black/55 px-4 py-2\">\n <SunMedium className=\"h-4 w-4 shrink-0 text-[#FFCC00]\" />\n <input\n type=\"range\"\n min={controls.exposureRange.min}\n max={controls.exposureRange.max}\n step={controls.exposureRange.step}\n value={controls.exposure}\n onChange={(e) => controls.setExposure(Number(e.target.value))}\n aria-label=\"Exposure compensation\"\n className=\"w-full accent-[#FFCC00]\"\n />\n <span className=\"w-8 shrink-0 text-right text-xs tabular-nums text-white\">\n {controls.exposure > 0 ? \"+\" : \"\"}\n {controls.exposure}\n </span>\n </div>\n )}\n <div className=\"bg-black/65 px-3 pb-safe backdrop-blur-[2px]\">\n {slots.aboveModeSelector}\n {/* The domain action (Next/Break) gets its own right-aligned row —\n overlaying it on the mode labels collides on narrow phones. */}\n {slots.modeRowTrailing && (\n <div className=\"flex justify-end pb-1 pt-1.5\">\n {slots.modeRowTrailing}\n </div>\n )}\n <div className=\"flex items-center justify-center py-2.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 </div>\n <div className=\"flex items-center justify-between px-2 pb-4 pt-1\">\n <div className=\"flex w-16 justify-start\">\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Open your media library\"\n className=\"h-12 w-12 touch-manipulation overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/25 transition-transform active:scale-95\"\n >\n {cloud.recentsThumb ?? (\n <span className=\"block h-full w-full bg-white/5\" />\n )}\n </button>\n </div>\n <ShutterButton\n mode={mode}\n recording={engine.recording}\n disabled={shutterDisabled || blocked}\n onPress={onShutter}\n />\n <div className=\"flex w-16 justify-end\">\n {engine.onFlipCamera ? (\n <button\n type=\"button\"\n onClick={engine.onFlipCamera}\n aria-label=\"Switch camera\"\n className=\"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/15 text-white transition-transform active:rotate-180 active:scale-95 duration-300\"\n >\n <RefreshCw className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"h-12 w-12\" />\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n\n <OptionsGridPanel\n open={optionsOpen && !controlsHidden}\n onClose={() => setOptionsOpen(false)}\n tiles={tiles}\n />\n\n {blocked && blockedSheet && !blockedDismissed && (\n <CaptureSheet\n open\n onClose={() => setBlockedDismissed(true)}\n body={blockedSheet.body}\n title=\"Camera unavailable\"\n actions={blockedSheet.actions}\n />\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","/**\n * The package-local `cn` — the documented substitution point for the app's\n * `@/lib/utils` cn during extraction (same pattern as `@ai-matrx/media`).\n */\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(\n ...inputs: Array<string | false | null | undefined>\n): string {\n return twMerge(inputs.filter(Boolean).join(\" \"));\n}\n","\"use client\";\n\n/**\n * useTrackControls — HONEST torch + zoom over a live video track's real\n * capabilities (`MediaTrackCapabilities`). If the hardware doesn't report\n * torch or a zoom range, the corresponding control simply doesn't exist —\n * we never render a fake toggle.\n *\n * Zoom pill options are derived from the reported [min, max] range using the\n * familiar phone ladder (.5 / 1 / 2 / 4 / 8) clipped to what the device can\n * actually do. Applied via `track.applyConstraints({ advanced: [...] })`.\n *\n * Package source (`@ai-matrx/capture`) — browser media APIs only, no app\n * imports.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\n\n// torch/zoom are spec'd in mediacapture-image but absent from lib.dom.\ninterface ExtendedCapabilities extends MediaTrackCapabilities {\n torch?: boolean;\n zoom?: { min: number; max: number; step?: number };\n exposureCompensation?: { min: number; max: number; step?: number };\n}\ninterface ExtendedSettings extends MediaTrackSettings {\n torch?: boolean;\n zoom?: number;\n exposureCompensation?: number;\n}\n\nconst ZOOM_LADDER = [0.5, 1, 2, 4, 8];\n\nexport interface TrackControls {\n torchSupported: boolean;\n torchOn: boolean;\n toggleTorch: () => void;\n /** ≥2 entries when the track reports a usable zoom range, else []. */\n zoomOptions: number[];\n zoom: number;\n setZoom: (factor: number) => void;\n /** Exposure compensation — only when the hardware reports a range. */\n exposureSupported: boolean;\n exposure: number;\n exposureRange: { min: number; max: number; step: number } | null;\n setExposure: (value: number) => void;\n}\n\nexport function useTrackControls(stream: MediaStream | null): TrackControls {\n const [torchOn, setTorchOn] = useState(false);\n const [zoom, setZoomState] = useState(1);\n const [exposure, setExposureState] = useState(0);\n // Capabilities can be empty until the track is fully live; re-read once on\n // stream change and again on a short delayed tick (iOS reports late).\n const [caps, setCaps] = useState<ExtendedCapabilities | null>(null);\n\n const track = stream?.getVideoTracks()[0] ?? null;\n\n useEffect(() => {\n setTorchOn(false);\n if (!track || typeof track.getCapabilities !== \"function\") {\n setCaps(null);\n return;\n }\n const read = () => {\n try {\n setCaps(track.getCapabilities() as ExtendedCapabilities);\n const settings = track.getSettings() as ExtendedSettings;\n if (typeof settings.zoom === \"number\") setZoomState(settings.zoom);\n if (typeof settings.exposureCompensation === \"number\")\n setExposureState(settings.exposureCompensation);\n } catch {\n setCaps(null);\n }\n };\n read();\n const timer = window.setTimeout(read, 750);\n return () => window.clearTimeout(timer);\n }, [track]);\n\n const torchSupported = caps?.torch === true;\n\n const toggleTorch = useCallback(() => {\n if (!track || !torchSupported) return;\n const next = !torchOn;\n track\n .applyConstraints({ advanced: [{ torch: next } as MediaTrackConstraintSet] })\n .then(() => setTorchOn(next))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] torch toggle failed\", err);\n });\n }, [track, torchSupported, torchOn]);\n\n const zoomOptions = useMemo(() => {\n const range = caps?.zoom;\n if (!range || !(range.max > range.min)) return [];\n const options = ZOOM_LADDER.filter(\n (f) => f >= range.min && f <= range.max,\n );\n if (!options.includes(1) && 1 >= range.min && 1 <= range.max) {\n options.push(1);\n options.sort((a, b) => a - b);\n }\n return options.length >= 2 ? options : [];\n }, [caps]);\n\n const setZoom = useCallback(\n (factor: number) => {\n if (!track || !caps?.zoom) return;\n const clamped = Math.min(caps.zoom.max, Math.max(caps.zoom.min, factor));\n track\n .applyConstraints({\n advanced: [{ zoom: clamped } as MediaTrackConstraintSet],\n })\n .then(() => setZoomState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] zoom failed\", err);\n });\n },\n [track, caps],\n );\n\n const exposureCaps = caps?.exposureCompensation;\n const exposureRange =\n exposureCaps && exposureCaps.max > exposureCaps.min\n ? {\n min: exposureCaps.min,\n max: exposureCaps.max,\n step: exposureCaps.step && exposureCaps.step > 0 ? exposureCaps.step : 0.5,\n }\n : null;\n\n const setExposure = useCallback(\n (value: number) => {\n if (!track || !exposureRange) return;\n const clamped = Math.min(\n exposureRange.max,\n Math.max(exposureRange.min, value),\n );\n track\n .applyConstraints({\n advanced: [\n { exposureCompensation: clamped } as MediaTrackConstraintSet,\n ],\n })\n .then(() => setExposureState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] exposure failed\", err);\n });\n },\n [track, exposureRange],\n );\n\n return {\n torchSupported,\n torchOn,\n toggleTorch,\n zoomOptions,\n zoom,\n setZoom,\n exposureSupported: exposureRange !== null,\n exposure,\n exposureRange,\n setExposure,\n };\n}\n","\"use client\";\n\n/**\n * ShutterButton — the iPhone shutter: a thin white ring with a filled inner\n * circle. Photo = white fill; video idle = red circle; video recording = the\n * inner shape morphs to a small red rounded square (the iOS stop affordance).\n * Press feedback is a scale-down of the INNER fill only, like iOS.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureCameraMode } from \"../types\";\n\nexport interface ShutterButtonProps {\n mode: CaptureCameraMode;\n recording: boolean;\n disabled?: boolean;\n onPress: () => void;\n}\n\nexport function ShutterButton({\n mode,\n recording,\n disabled = false,\n onPress,\n}: ShutterButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onPress}\n disabled={disabled}\n aria-label={\n mode === \"photo\"\n ? \"Take photo\"\n : recording\n ? \"Stop recording\"\n : \"Start recording\"\n }\n className={cn(\n \"group flex h-[74px] w-[74px] shrink-0 items-center justify-center rounded-full\",\n \"border-[3.5px] border-white transition-opacity\",\n disabled && \"opacity-30\",\n )}\n >\n <span\n className={cn(\n \"block transition-all duration-200 ease-out group-active:scale-90\",\n mode === \"photo\"\n ? \"h-[62px] w-[62px] rounded-full bg-white\"\n : recording\n ? \"h-8 w-8 rounded-md bg-[#FF3B30]\"\n : \"h-[62px] w-[62px] rounded-full bg-[#FF3B30]\",\n )}\n />\n </button>\n );\n}\n","\"use client\";\n\n/**\n * ModeSelector — the iPhone mode row: uppercase, letter-spaced labels in a\n * horizontal band; the ACTIVE mode sits in a dark pill with the iOS camera\n * yellow. VIDEO and PHOTO are persistent modes; UPLOAD is an immediate\n * action (opens the host's picker) and never takes the active state — it is\n * our third lane in the slot the iPhone layout leaves open next to PHOTO.\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 ModeSelectorProps {\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n onUpload: () => void;\n /** Locks mode switching (while recording). */\n modeDisabled?: boolean;\n uploadDisabled?: boolean;\n /** Host-injected extra entries after UPLOAD (e.g. SCAN) — immediate\n * actions, never the active mode. */\n extraModes?: { id: string; label: string; onSelect: () => void }[];\n}\n\nconst LABEL_BASE =\n \"touch-manipulation rounded-full px-4 py-1.5 text-[13px] font-semibold uppercase tracking-[0.12em] transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 disabled:opacity-40\";\n\nexport function ModeSelector({\n mode,\n onModeChange,\n onUpload,\n modeDisabled = false,\n uploadDisabled = false,\n extraModes = [],\n}: ModeSelectorProps) {\n return (\n <div\n role=\"tablist\"\n aria-label=\"Capture mode\"\n className=\"flex items-center justify-center gap-1\"\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\" ? \"bg-white/15 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\" ? \"bg-white/15 text-[#FFCC00]\" : \"text-white\",\n )}\n >\n Photo\n </button>\n <button\n type=\"button\"\n aria-label=\"Upload photos or videos from this device\"\n disabled={modeDisabled || uploadDisabled}\n onClick={onUpload}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n Upload\n </button>\n {extraModes.map((extra) => (\n <button\n key={extra.id}\n type=\"button\"\n aria-label={extra.label}\n disabled={modeDisabled}\n onClick={extra.onSelect}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n {extra.label}\n </button>\n ))}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * ZoomRow — the iPhone zoom pills floating over the bottom of the feed:\n * small translucent circles, the active factor in a slightly larger circle\n * with yellow text and an \"×\" suffix. Rendered ONLY when the host reports a\n * real zoom range (track capabilities) — we never fake unsupported zoom.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface ZoomRowProps {\n /** Available zoom factors in ascending order (e.g. [1, 2, 4]). */\n options: number[];\n /** The currently applied factor (nearest option is highlighted). */\n value: number;\n onSelect: (factor: number) => void;\n}\n\nfunction formatFactor(factor: number): string {\n return factor < 1\n ? `.${String(Math.round(factor * 10))}`\n : String(Math.round(factor * 10) / 10);\n}\n\nexport function ZoomRow({ options, value, onSelect }: ZoomRowProps) {\n if (options.length < 2) return null;\n const active = options.reduce((best, opt) =>\n Math.abs(opt - value) < Math.abs(best - value) ? opt : best,\n );\n return (\n <div className=\"flex items-center justify-center gap-3\">\n {options.map((opt) => {\n const isActive = opt === active;\n return (\n <button\n key={opt}\n type=\"button\"\n onClick={() => onSelect(opt)}\n aria-label={`Zoom ${formatFactor(opt)}x`}\n aria-pressed={isActive}\n className={cn(\n \"flex touch-manipulation items-center justify-center rounded-full font-semibold transition-all duration-200\",\n isActive\n ? \"h-10 w-10 bg-black/45 text-[13px] text-[#FFCC00]\"\n : \"h-8 w-8 bg-black/35 text-[12px] text-white\",\n )}\n >\n {formatFactor(opt)}\n {isActive && <span className=\"text-[10px]\">×</span>}\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * OptionsGridPanel — the iPhone two-tap options surface: tapping the grid\n * button dims the viewfinder and reveals a rounded dark panel pinned to the\n * bottom holding a grid of circular toggle buttons with uppercase labels\n * (one tap to reveal, one tap to act). This is NOT a drawer — no drag\n * handle, no route; tapping the scrim or the grid button again closes it.\n *\n * Tiles are injected (`CaptureOptionTile[]`) so hosts and domain extensions\n * add their own toggles without touching the panel.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureOptionTile } from \"../types\";\n\nexport interface OptionsGridPanelProps {\n open: boolean;\n onClose: () => void;\n tiles: CaptureOptionTile[];\n}\n\nexport function OptionsGridPanel({\n open,\n onClose,\n tiles,\n}: OptionsGridPanelProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-40\">\n {/* Dimming scrim — the viewfinder darkens like iOS; tap to dismiss. */}\n <button\n type=\"button\"\n aria-label=\"Close camera options\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/70\"\n />\n <div className=\"absolute inset-x-3 bottom-3 mb-safe rounded-[2.5rem] bg-[#3a3a3c]/95 px-6 py-8 shadow-2xl\">\n <div className=\"grid grid-cols-3 gap-x-4 gap-y-7\">\n {tiles.map((tile) => (\n <div key={tile.id} className=\"flex flex-col items-center gap-2.5\">\n <button\n type=\"button\"\n onClick={tile.onPress}\n disabled={tile.disabled}\n aria-label={tile.label}\n aria-pressed={tile.active === true}\n className={cn(\n \"flex h-16 w-16 touch-manipulation items-center justify-center rounded-full bg-[#2c2c2e] transition-colors\",\n tile.active ? \"text-[#FFCC00]\" : \"text-white\",\n tile.disabled && \"opacity-40\",\n )}\n >\n {tile.icon}\n </button>\n <span className=\"text-[13px] font-semibold uppercase tracking-[0.14em] text-white\">\n {tile.valueLabel ? `${tile.label} ${tile.valueLabel}` : tile.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CaptureSheet — the iOS-style system sheet used over the camera: a light,\n * heavily-rounded card sliding over the lower portion of the screen with a\n * circular ✕ close top-right; content is an icon + bold title + body text\n * with a filled primary action and a tinted secondary one. `variant=\"busy\"`\n * is the transient state (small spinner + label, like the OS \"Connecting…\"\n * sheet). Deliberately light-on-dark regardless of app theme — it mirrors\n * the OS presentation over camera chrome.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { Loader2, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\nexport interface CaptureSheetAction {\n label: string;\n onPress: () => void;\n kind?: \"primary\" | \"secondary\";\n}\n\nexport interface CaptureSheetProps {\n open: boolean;\n onClose: () => void;\n /** Standard content sheet by default; \"busy\" renders spinner + label. */\n variant?: \"content\" | \"busy\";\n icon?: React.ReactNode;\n title?: string;\n body?: React.ReactNode;\n actions?: CaptureSheetAction[];\n /** The busy variant's label (\"Connecting…\"). */\n busyLabel?: string;\n}\n\nexport function CaptureSheet({\n open,\n onClose,\n variant = \"content\",\n icon,\n title,\n body,\n actions = [],\n busyLabel = \"Working…\",\n}: CaptureSheetProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col justify-end\">\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/30\"\n />\n <div className=\"relative mx-2 mb-2 mb-safe rounded-[2rem] bg-[#f2f2f7] px-6 pb-6 pt-5 text-black shadow-2xl\">\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"absolute right-4 top-4 flex h-9 w-9 items-center justify-center rounded-full bg-black/5 text-black/70 transition-colors hover:bg-black/10\"\n >\n <X className=\"h-5 w-5\" strokeWidth={2.5} />\n </button>\n {variant === \"busy\" ? (\n <div className=\"flex min-h-[220px] items-center justify-center gap-2.5\">\n <Loader2 className=\"h-5 w-5 animate-spin text-black/50\" />\n <span className=\"text-[17px] font-medium text-black/80\">\n {busyLabel}\n </span>\n </div>\n ) : (\n <div className=\"pt-4\">\n {icon && <div className=\"mb-5 text-[#0a84ff]\">{icon}</div>}\n {title && (\n <h2 className=\"mb-2 text-[26px] font-bold leading-tight\">\n {title}\n </h2>\n )}\n {body && (\n <div className=\"text-[17px] leading-snug text-black/85\">\n {body}\n </div>\n )}\n {actions.length > 0 && (\n <div className=\"mt-7 flex flex-col gap-3\">\n {actions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={action.onPress}\n className={cn(\n \"h-[50px] w-full touch-manipulation rounded-full text-[17px] font-semibold transition-transform active:scale-[0.98]\",\n (action.kind ?? \"primary\") === \"primary\"\n ? \"bg-[#0a84ff] text-white\"\n : \"bg-black/[0.06] text-[#0a84ff]\",\n )}\n >\n {action.label}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * GridOverlay — the rule-of-thirds composition grid over the viewfinder.\n * Genuinely supported (pure CSS), toggled from the options grid.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function GridOverlay({ visible }: { visible: boolean }) {\n if (!visible) return null;\n return (\n <div aria-hidden className=\"pointer-events-none absolute inset-0 z-10\">\n <div className=\"absolute inset-y-0 left-1/3 w-px bg-white/40\" />\n <div className=\"absolute inset-y-0 left-2/3 w-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-1/3 h-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-2/3 h-px bg-white/40\" />\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CountdownOverlay — the big centered timer digits (iPhone timer capture):\n * one large white numeral per second, scaling in as it changes.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function CountdownOverlay({ seconds }: { seconds: number | null }) {\n if (seconds === null || seconds <= 0) return null;\n return (\n <div className=\"pointer-events-none absolute inset-0 z-30 flex items-center justify-center\">\n <span\n key={seconds}\n className=\"text-[120px] font-light text-white drop-shadow-lg [@starting-style]:scale-125 [@starting-style]:opacity-0 transition-all duration-300\"\n >\n {seconds}\n </span>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * ImageEditSheet — instant in-browser image editing, core to the package\n * (THE CLOUD LAW's sibling: capture without instant edit is half a system).\n * v1 covers the basics that belong in the browser — crop (free + 1:1, 4:3,\n * 16:9 presets), rotate 90°, flip — full-screen dark chrome in the iOS\n * editing style: Cancel/Save top bar, the image letterboxed center, a\n * draggable/resizable crop frame with corner handles, tool row bottom.\n * Heavier AI edits ride host-injected actions, not this sheet.\n *\n * Output is a JPEG re-encode of the ORIGINAL pixels (rotation/flip/crop\n * applied on canvas) — never a screenshot of the preview.\n *\n * Package source (`@ai-matrx/capture`) — browser canvas only, no app deps.\n */\n\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { Check, FlipHorizontal2, RotateCcw, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\ntype AspectPreset = \"free\" | \"1:1\" | \"4:3\" | \"16:9\";\nconst ASPECTS: { id: AspectPreset; label: string; ratio: number | null }[] = [\n { id: \"free\", label: \"Free\", ratio: null },\n { id: \"1:1\", label: \"Square\", ratio: 1 },\n { id: \"4:3\", label: \"4:3\", ratio: 4 / 3 },\n { id: \"16:9\", label: \"16:9\", ratio: 16 / 9 },\n];\n\n/** Normalized crop rect (0..1) relative to the ROTATED/FLIPPED image. */\ninterface CropRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface ImageEditSheetProps {\n open: boolean;\n /** Source image URL (object URL or resolvable src). */\n src: string | null;\n onClose: () => void;\n /** Receives the edited JPEG. The host persists it (cloud port). */\n onSave: (blob: Blob) => void;\n}\n\nconst FULL_CROP: CropRect = { x: 0, y: 0, w: 1, h: 1 };\nconst MIN_CROP = 0.08;\n\nexport function ImageEditSheet({\n open,\n src,\n onClose,\n onSave,\n}: ImageEditSheetProps) {\n const [rotation, setRotation] = useState(0); // 0|90|180|270, CCW steps\n const [flipped, setFlipped] = useState(false);\n const [aspect, setAspect] = useState<AspectPreset>(\"free\");\n const [crop, setCrop] = useState<CropRect>(FULL_CROP);\n const [saving, setSaving] = useState(false);\n const stageRef = useRef<HTMLDivElement | null>(null);\n const imgRef = useRef<HTMLImageElement | null>(null);\n const dragRef = useRef<{\n kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\";\n startX: number;\n startY: number;\n startCrop: CropRect;\n } | null>(null);\n\n useEffect(() => {\n if (open) {\n setRotation(0);\n setFlipped(false);\n setAspect(\"free\");\n setCrop(FULL_CROP);\n setSaving(false);\n }\n }, [open, src]);\n\n const applyAspect = useCallback((preset: AspectPreset) => {\n setAspect(preset);\n const ratio = ASPECTS.find((a) => a.id === preset)?.ratio ?? null;\n if (ratio === null) return;\n // Fit the largest centered rect of the target ratio inside the frame,\n // in DISPLAYED-image normalized space (needs the element's real aspect).\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n const rotated = rotationSwapsAxes();\n const iw = rotated ? img.naturalHeight : img.naturalWidth;\n const ih = rotated ? img.naturalWidth : img.naturalHeight;\n const imageRatio = iw / ih;\n let w = 1;\n let h = 1;\n if (ratio > imageRatio) h = imageRatio / ratio;\n else w = ratio / imageRatio;\n setCrop({ x: (1 - w) / 2, y: (1 - h) / 2, w, h });\n\n function rotationSwapsAxes() {\n return rotation % 180 !== 0;\n }\n }, [rotation]);\n\n const onPointerDown = useCallback(\n (kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\") =>\n (e: React.PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n dragRef.current = {\n kind,\n startX: e.clientX,\n startY: e.clientY,\n startCrop: crop,\n };\n },\n [crop],\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n const drag = dragRef.current;\n const img = imgRef.current;\n if (!drag || !img) return;\n const rect = img.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const dx = (e.clientX - drag.startX) / rect.width;\n const dy = (e.clientY - drag.startY) / rect.height;\n const c = { ...drag.startCrop };\n const ratio = ASPECTS.find((a) => a.id === aspect)?.ratio ?? null;\n const frameRatio = rect.width / rect.height;\n\n if (drag.kind === \"move\") {\n c.x = Math.min(1 - c.w, Math.max(0, c.x + dx));\n c.y = Math.min(1 - c.h, Math.max(0, c.y + dy));\n } else {\n const left = drag.kind === \"nw\" || drag.kind === \"sw\";\n const top = drag.kind === \"nw\" || drag.kind === \"ne\";\n let x2 = c.x + c.w;\n let y2 = c.y + c.h;\n if (left) c.x = Math.min(x2 - MIN_CROP, Math.max(0, c.x + dx));\n else x2 = Math.max(c.x + MIN_CROP, Math.min(1, x2 + dx));\n if (top) c.y = Math.min(y2 - MIN_CROP, Math.max(0, c.y + dy));\n else y2 = Math.max(c.y + MIN_CROP, Math.min(1, y2 + dy));\n c.w = x2 - c.x;\n c.h = y2 - c.y;\n if (ratio !== null) {\n // Lock the ratio by deriving height from width in SCREEN space.\n const targetH = (c.w * frameRatio) / ratio;\n if (top) c.y = y2 - Math.min(targetH, y2);\n c.h = Math.min(targetH, top ? y2 - c.y : 1 - c.y);\n c.w = (c.h * ratio) / frameRatio;\n if (left) c.x = x2 - c.w;\n }\n }\n setCrop(c);\n },\n [aspect],\n );\n\n const onPointerUp = useCallback(() => {\n dragRef.current = null;\n }, []);\n\n const save = useCallback(() => {\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n setSaving(true);\n try {\n const swap = rotation % 180 !== 0;\n const outW = Math.round((swap ? img.naturalHeight : img.naturalWidth) * crop.w);\n const outH = Math.round((swap ? img.naturalWidth : img.naturalHeight) * crop.h);\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, outW);\n canvas.height = Math.max(1, outH);\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"no 2d context\");\n // Move into crop space, then apply rotation/flip about the image center.\n const rw = swap ? img.naturalHeight : img.naturalWidth;\n const rh = swap ? img.naturalWidth : img.naturalHeight;\n ctx.translate(-crop.x * rw, -crop.y * rh);\n ctx.translate(rw / 2, rh / 2);\n ctx.rotate((rotation * Math.PI) / 180);\n if (flipped) ctx.scale(-1, 1);\n ctx.drawImage(img, -img.naturalWidth / 2, -img.naturalHeight / 2);\n canvas.toBlob(\n (blob) => {\n setSaving(false);\n if (blob) {\n onSave(blob);\n onClose();\n }\n },\n \"image/jpeg\",\n 0.92,\n );\n } catch (err) {\n console.error(\"[capture-camera] edit save failed\", err);\n setSaving(false);\n }\n }, [rotation, flipped, crop, onSave, onClose]);\n\n if (!open || !src) return null;\n\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col bg-black\">\n {/* Top bar */}\n <div className=\"flex shrink-0 items-center justify-between px-4 pt-safe\">\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Cancel editing\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-medium text-white\"\n >\n <X className=\"h-5 w-5\" />\n Cancel\n </button>\n <span className=\"text-[15px] font-semibold text-white/90\">Edit</span>\n <button\n type=\"button\"\n onClick={save}\n disabled={saving}\n aria-label=\"Save edited image\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-semibold text-[#FFCC00] disabled:opacity-50\"\n >\n <Check className=\"h-5 w-5\" />\n Save\n </button>\n </div>\n\n {/* Stage */}\n <div\n ref={stageRef}\n className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-hidden p-4\"\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"relative max-h-full max-w-full\">\n {/* eslint-disable-next-line @next/next/no-img-element -- local object URL being edited */}\n <img\n ref={imgRef}\n src={src}\n alt=\"Image being edited\"\n draggable={false}\n className=\"max-h-[62dvh] max-w-full select-none object-contain\"\n style={{\n transform: `rotate(${rotation}deg) ${flipped ? \"scaleX(-1)\" : \"\"}`,\n }}\n />\n {/* Crop frame in displayed-image space */}\n <div\n role=\"presentation\"\n onPointerDown={onPointerDown(\"move\")}\n className=\"absolute cursor-move touch-none border-2 border-white shadow-[0_0_0_9999px_rgba(0,0,0,0.55)]\"\n style={{\n left: `${crop.x * 100}%`,\n top: `${crop.y * 100}%`,\n width: `${crop.w * 100}%`,\n height: `${crop.h * 100}%`,\n }}\n >\n {([\"nw\", \"ne\", \"sw\", \"se\"] as const).map((corner) => (\n <span\n key={corner}\n role=\"presentation\"\n onPointerDown={onPointerDown(corner)}\n className={cn(\n \"absolute h-6 w-6 touch-none\",\n corner === \"nw\" &&\n \"-left-1.5 -top-1.5 border-l-4 border-t-4 cursor-nwse-resize\",\n corner === \"ne\" &&\n \"-right-1.5 -top-1.5 border-r-4 border-t-4 cursor-nesw-resize\",\n corner === \"sw\" &&\n \"-bottom-1.5 -left-1.5 border-b-4 border-l-4 cursor-nesw-resize\",\n corner === \"se\" &&\n \"-bottom-1.5 -right-1.5 border-b-4 border-r-4 cursor-nwse-resize\",\n \"border-white\",\n )}\n />\n ))}\n </div>\n </div>\n </div>\n\n {/* Tool row */}\n <div className=\"shrink-0 pb-safe\">\n <div className=\"flex items-center justify-center gap-2 pb-2\">\n {ASPECTS.map((a) => (\n <button\n key={a.id}\n type=\"button\"\n onClick={() => applyAspect(a.id)}\n aria-pressed={aspect === a.id}\n className={cn(\n \"touch-manipulation rounded-full px-3.5 py-1.5 text-[12px] font-semibold uppercase tracking-wide transition-colors\",\n aspect === a.id\n ? \"bg-white/20 text-[#FFCC00]\"\n : \"text-white/80\",\n )}\n >\n {a.label}\n </button>\n ))}\n </div>\n <div className=\"flex items-center justify-center gap-6 pb-4\">\n <button\n type=\"button\"\n onClick={() => {\n setRotation((r) => (r + 270) % 360);\n setCrop(FULL_CROP);\n setAspect(\"free\");\n }}\n aria-label=\"Rotate left\"\n className=\"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10 text-white\"\n >\n <RotateCcw className=\"h-5 w-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setFlipped((f) => !f)}\n aria-label=\"Flip horizontally\"\n aria-pressed={flipped}\n className={cn(\n \"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10\",\n flipped ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n <FlipHorizontal2 className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_lucide_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_lucide_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/react.ts","../src/components/CameraCapture.tsx","../src/cn.ts","../src/hooks/useTrackControls.ts","../src/components/ShutterButton.tsx","../src/components/ModeSelector.tsx","../src/components/ZoomRow.tsx","../src/components/OptionsGridPanel.tsx","../src/components/CaptureSheet.tsx","../src/components/GridOverlay.tsx","../src/components/CountdownOverlay.tsx","../src/components/ImageEditSheet.tsx"],"sourcesContent":["/**\n * @ai-matrx/capture/react — the DOM chrome (all \"use client\").\n */\n\nexport { CameraCapture, type CameraCaptureProps } from \"./components/CameraCapture\";\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 { 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 type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCloudPort,\n CaptureOptionTile,\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 Grip,\n Grid3x3,\n Proportions,\n SunMedium,\n Timer,\n X,\n Zap,\n ZapOff,\n RefreshCw,\n} from \"lucide-react\";\nimport { cn } from \"../cn\";\n\nimport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCloudPort,\n CaptureOptionTile,\n CaptureTimerSetting,\n} from \"../types\";\nimport { useTrackControls } from \"../hooks/useTrackControls\";\nimport { ShutterButton } from \"./ShutterButton\";\nimport { ModeSelector } from \"./ModeSelector\";\nimport { ZoomRow } from \"./ZoomRow\";\nimport { OptionsGridPanel } from \"./OptionsGridPanel\";\nimport { CaptureSheet, type CaptureSheetAction } from \"./CaptureSheet\";\nimport { GridOverlay } from \"./GridOverlay\";\nimport { CountdownOverlay } from \"./CountdownOverlay\";\n\nexport interface CameraCaptureProps {\n engine: CaptureCameraEngine;\n /** THE CLOUD LAW: the cloud port is REQUIRED — a camera with no cloud\n * library/persistence is not a valid instance of this system. */\n cloud: CaptureCloudPort;\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n /** The live preview element (host wires its runtime's `CameraPreview`). */\n preview: React.ReactNode;\n /** Close affordance top-left; omitted = no close button. */\n onClose?: () => void;\n /** Body + actions for the camera-blocked iOS-style sheet. */\n blockedSheet?: { body: React.ReactNode; actions: CaptureSheetAction[] };\n /** Hide all chrome except honesty chips (host renders its own toggle). */\n controlsHidden?: boolean;\n shutterDisabled?: boolean;\n slots?: CaptureCameraSlots;\n}\n\nfunction formatElapsed(totalSeconds: number): string {\n const m = Math.floor(totalSeconds / 60);\n const s = String(totalSeconds % 60).padStart(2, \"0\");\n return `${m}:${s}`;\n}\n\nconst ASPECT_CYCLE: CaptureAspect[] = [\"full\", \"4:3\", \"1:1\", \"16:9\"];\n\nexport function CameraCapture({\n engine,\n cloud,\n mode,\n onModeChange,\n preview,\n onClose,\n blockedSheet,\n controlsHidden = false,\n shutterDisabled = false,\n slots = {},\n}: CameraCaptureProps) {\n const [optionsOpen, setOptionsOpen] = useState(false);\n const [gridOn, setGridOn] = useState(false);\n const [timerSetting, setTimerSetting] = useState<CaptureTimerSetting>(0);\n const [aspect, setAspect] = useState<CaptureAspect>(\"full\");\n const [exposureOpen, setExposureOpen] = useState(false);\n const [countdown, setCountdown] = useState<number | null>(null);\n // The blocked sheet is dismissible — uploads and the system camera keep\n // working, so closing it reveals the chrome rather than leaving the page.\n const [blockedDismissed, setBlockedDismissed] = useState(false);\n const countdownRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const controls = useTrackControls(engine.stream);\n\n const clearCountdown = useCallback(() => {\n if (countdownRef.current) clearInterval(countdownRef.current);\n countdownRef.current = null;\n setCountdown(null);\n }, []);\n useEffect(() => clearCountdown, [clearCountdown]);\n\n const onShutter = useCallback(() => {\n if (mode === \"video\") {\n if (engine.recording) engine.onStopRecording();\n else engine.onStartRecording();\n return;\n }\n if (countdown !== null) {\n // Tapping mid-countdown cancels — matching iOS.\n clearCountdown();\n return;\n }\n if (timerSetting === 0) {\n engine.onCapturePhoto({ aspect });\n return;\n }\n let remaining = timerSetting;\n setCountdown(remaining);\n countdownRef.current = setInterval(() => {\n remaining -= 1;\n if (remaining <= 0) {\n clearCountdown();\n engine.onCapturePhoto({ aspect });\n } else {\n setCountdown(remaining);\n }\n }, 1000);\n }, [mode, engine, countdown, timerSetting, aspect, clearCountdown]);\n\n const cycleTimer = useCallback(() => {\n setTimerSetting((t) => (t === 0 ? 3 : t === 3 ? 10 : 0));\n }, []);\n\n const coreTiles: CaptureOptionTile[] = [\n ...(controls.torchSupported\n ? [\n {\n id: \"flash\",\n label: \"Flash\",\n icon: controls.torchOn ? (\n <Zap className=\"h-6 w-6\" fill=\"currentColor\" />\n ) : (\n <ZapOff className=\"h-6 w-6\" />\n ),\n active: controls.torchOn,\n onPress: controls.toggleTorch,\n },\n ]\n : []),\n {\n id: \"timer\",\n label: \"Timer\",\n icon: <Timer className=\"h-6 w-6\" />,\n active: timerSetting !== 0,\n valueLabel: timerSetting === 0 ? undefined : `${timerSetting}s`,\n onPress: cycleTimer,\n },\n {\n id: \"grid\",\n label: \"Grid\",\n icon: <Grid3x3 className=\"h-6 w-6\" />,\n active: gridOn,\n onPress: () => setGridOn((g) => !g),\n },\n // Aspect applies to PHOTO output (center-cropped from the full sensor).\n {\n id: \"aspect\",\n label: \"Aspect\",\n icon: <Proportions className=\"h-6 w-6\" />,\n active: aspect !== \"full\",\n valueLabel: aspect === \"full\" ? undefined : aspect,\n onPress: () =>\n setAspect(\n (a) =>\n ASPECT_CYCLE[\n (ASPECT_CYCLE.indexOf(a) + 1) % ASPECT_CYCLE.length\n ] ?? \"full\",\n ),\n },\n ...(controls.exposureSupported\n ? [\n {\n id: \"exposure\",\n label: \"Exposure\",\n icon: <SunMedium className=\"h-6 w-6\" />,\n active: controls.exposure !== 0 || exposureOpen,\n valueLabel:\n controls.exposure === 0\n ? undefined\n : `${controls.exposure > 0 ? \"+\" : \"\"}${controls.exposure}`,\n onPress: () => setExposureOpen((o) => !o),\n },\n ]\n : []),\n ];\n const tiles = [...coreTiles, ...(slots.optionTiles ?? [])];\n\n const blocked = engine.blocked !== null;\n\n return (\n <div className=\"absolute inset-0 select-none overflow-hidden bg-black\">\n {/* Full-bleed feed */}\n <div className=\"absolute inset-0\">{preview}</div>\n <GridOverlay visible={gridOn && !blocked} />\n {/* Aspect framing hint — the photo output is center-cropped to the\n selected ratio; the dimmed bands approximate the discarded region\n of the VISIBLE frame (the capture itself crops the full sensor). */}\n {mode === \"photo\" && aspect !== \"full\" && !blocked && (\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center\"\n >\n <div\n className=\"shadow-[0_0_0_9999px_rgba(0,0,0,0.45)]\"\n style={{\n aspectRatio:\n aspect === \"1:1\" ? \"1 / 1\" : aspect === \"4:3\" ? \"3 / 4\" : \"9 / 16\",\n width: aspect === \"16:9\" ? \"100%\" : undefined,\n height: aspect === \"16:9\" ? undefined : \"70%\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n }}\n />\n </div>\n )}\n <CountdownOverlay seconds={countdown} />\n\n {/* Top bar — ONE compact row; every pixel here is stolen from the\n feed, and a phone browser already spends chrome above us. */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 top-0 z-20 bg-black/65 pt-safe backdrop-blur-[2px]\">\n <div className=\"flex h-11 items-center gap-0.5 px-1.5\">\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close camera\"\n className=\"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full text-white transition-colors hover:bg-white/10\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"w-10 shrink-0\" />\n )}\n <div className=\"min-w-0 flex-1\">{slots.topBarCenter}</div>\n {controls.torchSupported && (\n <button\n type=\"button\"\n onClick={controls.toggleTorch}\n aria-label={\n controls.torchOn ? \"Turn flash off\" : \"Turn flash on\"\n }\n aria-pressed={controls.torchOn}\n className={cn(\n \"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n controls.torchOn\n ? \"text-[#FFCC00]\"\n : \"text-white hover:bg-white/10\",\n )}\n >\n <Zap\n className=\"h-5 w-5\"\n fill={controls.torchOn ? \"currentColor\" : \"none\"}\n />\n </button>\n )}\n {slots.topBarTrailing}\n <button\n type=\"button\"\n onClick={() => setOptionsOpen((o) => !o)}\n aria-label=\"More camera options\"\n aria-expanded={optionsOpen}\n className={cn(\n \"flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full transition-colors\",\n optionsOpen ? \"bg-white/20 text-white\" : \"text-white hover:bg-white/10\",\n )}\n >\n <Grip className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Honesty chips — visible even with controls hidden. */}\n <div className=\"pointer-events-none absolute inset-x-0 top-[50px] z-20 mt-safe flex flex-col items-center gap-1.5\">\n {engine.recording && (\n <span className=\"flex items-center gap-2 rounded-full bg-black/60 px-3 py-1.5 text-sm font-medium text-white\">\n <span className=\"h-2.5 w-2.5 animate-pulse rounded-full bg-[#FF3B30]\" />\n {formatElapsed(engine.recordElapsedSeconds)}\n </span>\n )}\n {slots.statusChips}\n </div>\n\n {/* Bottom stack */}\n {!controlsHidden && (\n <div className=\"absolute inset-x-0 bottom-0 z-20\">\n {/* Zoom pills float on the FEED, just above the bottom bar. */}\n {!blocked && controls.zoomOptions.length >= 2 && (\n <div className=\"mb-2\">\n <ZoomRow\n options={controls.zoomOptions}\n value={controls.zoom}\n onSelect={controls.setZoom}\n />\n </div>\n )}\n {/* Host content OVER the feed (filmstrip etc.) — zero bar height. */}\n {slots.aboveBar && <div className=\"mb-1.5 px-2\">{slots.aboveBar}</div>}\n {/* Exposure slider — revealed by the EXPOSURE tile, floats above\n the bottom bar like the iOS exposure control. */}\n {exposureOpen && controls.exposureSupported && controls.exposureRange && (\n <div className=\"mx-auto mb-3 flex w-64 items-center gap-3 rounded-full bg-black/55 px-4 py-2\">\n <SunMedium className=\"h-4 w-4 shrink-0 text-[#FFCC00]\" />\n <input\n type=\"range\"\n min={controls.exposureRange.min}\n max={controls.exposureRange.max}\n step={controls.exposureRange.step}\n value={controls.exposure}\n onChange={(e) => controls.setExposure(Number(e.target.value))}\n aria-label=\"Exposure compensation\"\n className=\"w-full accent-[#FFCC00]\"\n />\n <span className=\"w-8 shrink-0 text-right text-xs tabular-nums text-white\">\n {controls.exposure > 0 ? \"+\" : \"\"}\n {controls.exposure}\n </span>\n </div>\n )}\n <div className=\"bg-black/65 px-3 pb-safe backdrop-blur-[2px]\">\n {slots.aboveModeSelector}\n {/* Connected mode bar centered; the narrow track leaves BOTH\n sides usable — the domain action rides the same row. */}\n <div className=\"relative flex items-center justify-center 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=\"absolute right-0\">{slots.modeRowTrailing}</div>\n )}\n </div>\n <div className=\"flex items-center justify-between px-2 pb-1.5 pt-0.5\">\n <div className=\"flex w-14 justify-start\">\n <button\n type=\"button\"\n onClick={cloud.onOpenLibrary}\n aria-label=\"Open your media library\"\n className=\"h-11 w-11 touch-manipulation overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/25 transition-transform active:scale-95\"\n >\n {cloud.recentsThumb ?? (\n <span className=\"block h-full w-full bg-white/5\" />\n )}\n </button>\n </div>\n <ShutterButton\n mode={mode}\n recording={engine.recording}\n disabled={shutterDisabled || blocked}\n onPress={onShutter}\n />\n <div className=\"flex w-14 justify-end\">\n {engine.onFlipCamera ? (\n <button\n type=\"button\"\n onClick={engine.onFlipCamera}\n aria-label=\"Switch camera\"\n className=\"flex h-11 w-11 touch-manipulation items-center justify-center rounded-full bg-white/15 text-white transition-transform active:rotate-180 active:scale-95 duration-300\"\n >\n <RefreshCw className=\"h-5 w-5\" />\n </button>\n ) : (\n <span className=\"h-11 w-11\" />\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n\n <OptionsGridPanel\n open={optionsOpen && !controlsHidden}\n onClose={() => setOptionsOpen(false)}\n tiles={tiles}\n />\n\n {blocked && blockedSheet && !blockedDismissed && (\n <CaptureSheet\n open\n onClose={() => setBlockedDismissed(true)}\n body={blockedSheet.body}\n title=\"Camera unavailable\"\n actions={blockedSheet.actions}\n />\n )}\n\n {slots.overlays}\n </div>\n );\n}\n","/**\n * The package-local `cn` — the documented substitution point for the app's\n * `@/lib/utils` cn during extraction (same pattern as `@ai-matrx/media`).\n */\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(\n ...inputs: Array<string | false | null | undefined>\n): string {\n return twMerge(inputs.filter(Boolean).join(\" \"));\n}\n","\"use client\";\n\n/**\n * useTrackControls — HONEST torch + zoom over a live video track's real\n * capabilities (`MediaTrackCapabilities`). If the hardware doesn't report\n * torch or a zoom range, the corresponding control simply doesn't exist —\n * we never render a fake toggle.\n *\n * Zoom pill options are derived from the reported [min, max] range using the\n * familiar phone ladder (.5 / 1 / 2 / 4 / 8) clipped to what the device can\n * actually do. Applied via `track.applyConstraints({ advanced: [...] })`.\n *\n * Package source (`@ai-matrx/capture`) — browser media APIs only, no app\n * imports.\n */\n\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\n\n// torch/zoom are spec'd in mediacapture-image but absent from lib.dom.\ninterface ExtendedCapabilities extends MediaTrackCapabilities {\n torch?: boolean;\n zoom?: { min: number; max: number; step?: number };\n exposureCompensation?: { min: number; max: number; step?: number };\n}\ninterface ExtendedSettings extends MediaTrackSettings {\n torch?: boolean;\n zoom?: number;\n exposureCompensation?: number;\n}\n\nconst ZOOM_LADDER = [0.5, 1, 2, 4, 8];\n\nexport interface TrackControls {\n torchSupported: boolean;\n torchOn: boolean;\n toggleTorch: () => void;\n /** ≥2 entries when the track reports a usable zoom range, else []. */\n zoomOptions: number[];\n zoom: number;\n setZoom: (factor: number) => void;\n /** Exposure compensation — only when the hardware reports a range. */\n exposureSupported: boolean;\n exposure: number;\n exposureRange: { min: number; max: number; step: number } | null;\n setExposure: (value: number) => void;\n}\n\nexport function useTrackControls(stream: MediaStream | null): TrackControls {\n const [torchOn, setTorchOn] = useState(false);\n const [zoom, setZoomState] = useState(1);\n const [exposure, setExposureState] = useState(0);\n // Capabilities can be empty until the track is fully live; re-read once on\n // stream change and again on a short delayed tick (iOS reports late).\n const [caps, setCaps] = useState<ExtendedCapabilities | null>(null);\n\n const track = stream?.getVideoTracks()[0] ?? null;\n\n useEffect(() => {\n setTorchOn(false);\n if (!track || typeof track.getCapabilities !== \"function\") {\n setCaps(null);\n return;\n }\n const read = () => {\n try {\n setCaps(track.getCapabilities() as ExtendedCapabilities);\n const settings = track.getSettings() as ExtendedSettings;\n if (typeof settings.zoom === \"number\") setZoomState(settings.zoom);\n if (typeof settings.exposureCompensation === \"number\")\n setExposureState(settings.exposureCompensation);\n } catch {\n setCaps(null);\n }\n };\n read();\n const timer = window.setTimeout(read, 750);\n return () => window.clearTimeout(timer);\n }, [track]);\n\n const torchSupported = caps?.torch === true;\n\n const toggleTorch = useCallback(() => {\n if (!track || !torchSupported) return;\n const next = !torchOn;\n track\n .applyConstraints({ advanced: [{ torch: next } as MediaTrackConstraintSet] })\n .then(() => setTorchOn(next))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] torch toggle failed\", err);\n });\n }, [track, torchSupported, torchOn]);\n\n const zoomOptions = useMemo(() => {\n const range = caps?.zoom;\n if (!range || !(range.max > range.min)) return [];\n const options = ZOOM_LADDER.filter(\n (f) => f >= range.min && f <= range.max,\n );\n if (!options.includes(1) && 1 >= range.min && 1 <= range.max) {\n options.push(1);\n options.sort((a, b) => a - b);\n }\n return options.length >= 2 ? options : [];\n }, [caps]);\n\n const setZoom = useCallback(\n (factor: number) => {\n if (!track || !caps?.zoom) return;\n const clamped = Math.min(caps.zoom.max, Math.max(caps.zoom.min, factor));\n track\n .applyConstraints({\n advanced: [{ zoom: clamped } as MediaTrackConstraintSet],\n })\n .then(() => setZoomState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] zoom failed\", err);\n });\n },\n [track, caps],\n );\n\n const exposureCaps = caps?.exposureCompensation;\n const exposureRange =\n exposureCaps && exposureCaps.max > exposureCaps.min\n ? {\n min: exposureCaps.min,\n max: exposureCaps.max,\n step: exposureCaps.step && exposureCaps.step > 0 ? exposureCaps.step : 0.5,\n }\n : null;\n\n const setExposure = useCallback(\n (value: number) => {\n if (!track || !exposureRange) return;\n const clamped = Math.min(\n exposureRange.max,\n Math.max(exposureRange.min, value),\n );\n track\n .applyConstraints({\n advanced: [\n { exposureCompensation: clamped } as MediaTrackConstraintSet,\n ],\n })\n .then(() => setExposureState(clamped))\n .catch((err: unknown) => {\n console.error(\"[capture-camera] exposure failed\", err);\n });\n },\n [track, exposureRange],\n );\n\n return {\n torchSupported,\n torchOn,\n toggleTorch,\n zoomOptions,\n zoom,\n setZoom,\n exposureSupported: exposureRange !== null,\n exposure,\n exposureRange,\n setExposure,\n };\n}\n","\"use client\";\n\n/**\n * ShutterButton — the iPhone shutter: a thin white ring with a filled inner\n * circle. Photo = white fill; video idle = red circle; video recording = the\n * inner shape morphs to a small red rounded square (the iOS stop affordance).\n * Press feedback is a scale-down of the INNER fill only, like iOS.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureCameraMode } from \"../types\";\n\nexport interface ShutterButtonProps {\n mode: CaptureCameraMode;\n recording: boolean;\n disabled?: boolean;\n onPress: () => void;\n}\n\nexport function ShutterButton({\n mode,\n recording,\n disabled = false,\n onPress,\n}: ShutterButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onPress}\n disabled={disabled}\n aria-label={\n mode === \"photo\"\n ? \"Take photo\"\n : recording\n ? \"Stop recording\"\n : \"Start recording\"\n }\n className={cn(\n \"group flex h-[74px] w-[74px] shrink-0 items-center justify-center rounded-full\",\n \"border-[3.5px] border-white transition-opacity\",\n disabled && \"opacity-30\",\n )}\n >\n <span\n className={cn(\n \"block transition-all duration-200 ease-out group-active:scale-90\",\n mode === \"photo\"\n ? \"h-[62px] w-[62px] rounded-full bg-white\"\n : recording\n ? \"h-8 w-8 rounded-md bg-[#FF3B30]\"\n : \"h-[62px] w-[62px] rounded-full bg-[#FF3B30]\",\n )}\n />\n </button>\n );\n}\n","\"use client\";\n\n/**\n * ModeSelector — the CONNECTED capture-mode bar: one compact rounded track\n * holding VIDEO · PHOTO · UPLOAD (+ injected extras) with a spring-sliding\n * thumb under the active mode (the proven CaptureModeBar interaction, in\n * iPhone dress: uppercase letter-spaced labels, active in iOS camera\n * yellow). Deliberately narrow — the row keeps usable space on BOTH sides\n * for host affordances; screen height is the scarcest resource on a phone\n * browser, so the bar is one short row, never a spread of pills.\n *\n * VIDEO and PHOTO are persistent modes; UPLOAD and extras are immediate\n * actions and never take the thumb.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\n\nimport type { CaptureCameraMode } from \"../types\";\n\n/** Spring curve with a small overshoot (CSS linear() approximation);\n * class-level overshooting cubic-bezier is the fallback. */\nconst SPRING_EASING =\n \"linear(0, 0.0047 0.71%, 0.0189 1.44%, 0.0755 2.93%, 0.1692 4.49%, \" +\n \"0.3921 7.55%, 0.8121 12.94%, 0.9804 15.49%, 1.0946 18.14%, 1.1423 20.16%, \" +\n \"1.1568 21.62%, 1.1541 23.03%, 1.1113 26.4%, 1.0322 31.83%, 0.9902 36.25%, \" +\n \"0.9769 40.24%, 0.9844 45.87%, 1.0028 55.35%, 1.0075 63.42%, 1.0006 85.48%, 1)\";\n\nconst LABEL_BASE =\n \"relative z-10 flex h-8 min-w-0 touch-manipulation items-center justify-center rounded-full px-3 \" +\n \"text-[11px] font-semibold uppercase tracking-[0.12em] transition-colors duration-200 \" +\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 disabled:opacity-40\";\n\nexport interface ModeSelectorProps {\n mode: CaptureCameraMode;\n onModeChange: (mode: CaptureCameraMode) => void;\n onUpload: () => void;\n /** Locks mode switching (while recording). */\n modeDisabled?: boolean;\n uploadDisabled?: boolean;\n /** Host-injected extra entries after UPLOAD (e.g. SCAN) — immediate\n * actions, never the active mode. */\n extraModes?: { id: string; label: string; onSelect: () => void }[];\n}\n\nexport function ModeSelector({\n mode,\n onModeChange,\n onUpload,\n modeDisabled = false,\n uploadDisabled = false,\n extraModes = [],\n}: ModeSelectorProps) {\n const segments = 3 + extraModes.length;\n const activeIndex = mode === \"video\" ? 0 : 1;\n\n return (\n <div\n role=\"tablist\"\n aria-label=\"Capture mode\"\n className=\"relative inline-grid rounded-full bg-white/10 p-1\"\n style={{ gridTemplateColumns: `repeat(${segments}, minmax(0, 1fr))` }}\n >\n {/* Sliding thumb — transform-only, springs with overshoot, instant\n under prefers-reduced-motion. */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute bottom-1 top-1 left-1 rounded-full bg-white/20 transition-transform duration-500 ease-[cubic-bezier(0.34,1.56,0.64,1)] will-change-transform motion-reduce:transition-none\"\n style={{\n width: `calc((100% - 0.5rem) / ${segments})`,\n transform: `translateX(${activeIndex * 100}%)`,\n transitionTimingFunction: SPRING_EASING,\n }}\n />\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === \"video\"}\n disabled={modeDisabled}\n onClick={() => onModeChange(\"video\")}\n className={cn(\n LABEL_BASE,\n mode === \"video\" ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n Video\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={mode === \"photo\"}\n disabled={modeDisabled}\n onClick={() => onModeChange(\"photo\")}\n className={cn(\n LABEL_BASE,\n mode === \"photo\" ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n Photo\n </button>\n <button\n type=\"button\"\n aria-label=\"Upload photos or videos from this device\"\n disabled={modeDisabled || uploadDisabled}\n onClick={onUpload}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n Upload\n </button>\n {extraModes.map((extra) => (\n <button\n key={extra.id}\n type=\"button\"\n aria-label={extra.label}\n disabled={modeDisabled}\n onClick={extra.onSelect}\n className={cn(LABEL_BASE, \"text-white active:text-[#FFCC00]\")}\n >\n {extra.label}\n </button>\n ))}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * ZoomRow — the iPhone zoom pills floating over the bottom of the feed:\n * small translucent circles, the active factor in a slightly larger circle\n * with yellow text and an \"×\" suffix. Rendered ONLY when the host reports a\n * real zoom range (track capabilities) — we never fake unsupported zoom.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\n\nexport interface ZoomRowProps {\n /** Available zoom factors in ascending order (e.g. [1, 2, 4]). */\n options: number[];\n /** The currently applied factor (nearest option is highlighted). */\n value: number;\n onSelect: (factor: number) => void;\n}\n\nfunction formatFactor(factor: number): string {\n return factor < 1\n ? `.${String(Math.round(factor * 10))}`\n : String(Math.round(factor * 10) / 10);\n}\n\nexport function ZoomRow({ options, value, onSelect }: ZoomRowProps) {\n if (options.length < 2) return null;\n const active = options.reduce((best, opt) =>\n Math.abs(opt - value) < Math.abs(best - value) ? opt : best,\n );\n return (\n <div className=\"flex items-center justify-center gap-3\">\n {options.map((opt) => {\n const isActive = opt === active;\n return (\n <button\n key={opt}\n type=\"button\"\n onClick={() => onSelect(opt)}\n aria-label={`Zoom ${formatFactor(opt)}x`}\n aria-pressed={isActive}\n className={cn(\n \"flex touch-manipulation items-center justify-center rounded-full font-semibold transition-all duration-200\",\n isActive\n ? \"h-10 w-10 bg-black/45 text-[13px] text-[#FFCC00]\"\n : \"h-8 w-8 bg-black/35 text-[12px] text-white\",\n )}\n >\n {formatFactor(opt)}\n {isActive && <span className=\"text-[10px]\">×</span>}\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * OptionsGridPanel — the iPhone two-tap options surface: tapping the grid\n * button dims the viewfinder and reveals a rounded dark panel pinned to the\n * bottom holding a grid of circular toggle buttons with uppercase labels\n * (one tap to reveal, one tap to act). This is NOT a drawer — no drag\n * handle, no route; tapping the scrim or the grid button again closes it.\n *\n * Tiles are injected (`CaptureOptionTile[]`) so hosts and domain extensions\n * add their own toggles without touching the panel.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { cn } from \"../cn\";\nimport type { CaptureOptionTile } from \"../types\";\n\nexport interface OptionsGridPanelProps {\n open: boolean;\n onClose: () => void;\n tiles: CaptureOptionTile[];\n}\n\nexport function OptionsGridPanel({\n open,\n onClose,\n tiles,\n}: OptionsGridPanelProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-40\">\n {/* Dimming scrim — the viewfinder darkens like iOS; tap to dismiss. */}\n <button\n type=\"button\"\n aria-label=\"Close camera options\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/70\"\n />\n <div className=\"absolute inset-x-3 bottom-3 mb-safe rounded-[2.5rem] bg-[#3a3a3c]/95 px-6 py-8 shadow-2xl\">\n <div className=\"grid grid-cols-3 gap-x-4 gap-y-7\">\n {tiles.map((tile) => (\n <div key={tile.id} className=\"flex flex-col items-center gap-2.5\">\n <button\n type=\"button\"\n onClick={tile.onPress}\n disabled={tile.disabled}\n aria-label={tile.label}\n aria-pressed={tile.active === true}\n className={cn(\n \"flex h-16 w-16 touch-manipulation items-center justify-center rounded-full bg-[#2c2c2e] transition-colors\",\n tile.active ? \"text-[#FFCC00]\" : \"text-white\",\n tile.disabled && \"opacity-40\",\n )}\n >\n {tile.icon}\n </button>\n <span className=\"text-[13px] font-semibold uppercase tracking-[0.14em] text-white\">\n {tile.valueLabel ? `${tile.label} ${tile.valueLabel}` : tile.label}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CaptureSheet — the iOS-style system sheet used over the camera: a light,\n * heavily-rounded card sliding over the lower portion of the screen with a\n * circular ✕ close top-right; content is an icon + bold title + body text\n * with a filled primary action and a tinted secondary one. `variant=\"busy\"`\n * is the transient state (small spinner + label, like the OS \"Connecting…\"\n * sheet). Deliberately light-on-dark regardless of app theme — it mirrors\n * the OS presentation over camera chrome.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\nimport { Loader2, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\nexport interface CaptureSheetAction {\n label: string;\n onPress: () => void;\n kind?: \"primary\" | \"secondary\";\n}\n\nexport interface CaptureSheetProps {\n open: boolean;\n onClose: () => void;\n /** Standard content sheet by default; \"busy\" renders spinner + label. */\n variant?: \"content\" | \"busy\";\n icon?: React.ReactNode;\n title?: string;\n body?: React.ReactNode;\n actions?: CaptureSheetAction[];\n /** The busy variant's label (\"Connecting…\"). */\n busyLabel?: string;\n}\n\nexport function CaptureSheet({\n open,\n onClose,\n variant = \"content\",\n icon,\n title,\n body,\n actions = [],\n busyLabel = \"Working…\",\n}: CaptureSheetProps) {\n if (!open) return null;\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col justify-end\">\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n onClick={onClose}\n className=\"absolute inset-0 bg-black/30\"\n />\n <div className=\"relative mx-2 mb-2 mb-safe rounded-[2rem] bg-[#f2f2f7] px-6 pb-6 pt-5 text-black shadow-2xl\">\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"absolute right-4 top-4 flex h-9 w-9 items-center justify-center rounded-full bg-black/5 text-black/70 transition-colors hover:bg-black/10\"\n >\n <X className=\"h-5 w-5\" strokeWidth={2.5} />\n </button>\n {variant === \"busy\" ? (\n <div className=\"flex min-h-[220px] items-center justify-center gap-2.5\">\n <Loader2 className=\"h-5 w-5 animate-spin text-black/50\" />\n <span className=\"text-[17px] font-medium text-black/80\">\n {busyLabel}\n </span>\n </div>\n ) : (\n <div className=\"pt-4\">\n {icon && <div className=\"mb-5 text-[#0a84ff]\">{icon}</div>}\n {title && (\n <h2 className=\"mb-2 text-[26px] font-bold leading-tight\">\n {title}\n </h2>\n )}\n {body && (\n <div className=\"text-[17px] leading-snug text-black/85\">\n {body}\n </div>\n )}\n {actions.length > 0 && (\n <div className=\"mt-7 flex flex-col gap-3\">\n {actions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={action.onPress}\n className={cn(\n \"h-[50px] w-full touch-manipulation rounded-full text-[17px] font-semibold transition-transform active:scale-[0.98]\",\n (action.kind ?? \"primary\") === \"primary\"\n ? \"bg-[#0a84ff] text-white\"\n : \"bg-black/[0.06] text-[#0a84ff]\",\n )}\n >\n {action.label}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * GridOverlay — the rule-of-thirds composition grid over the viewfinder.\n * Genuinely supported (pure CSS), toggled from the options grid.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function GridOverlay({ visible }: { visible: boolean }) {\n if (!visible) return null;\n return (\n <div aria-hidden className=\"pointer-events-none absolute inset-0 z-10\">\n <div className=\"absolute inset-y-0 left-1/3 w-px bg-white/40\" />\n <div className=\"absolute inset-y-0 left-2/3 w-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-1/3 h-px bg-white/40\" />\n <div className=\"absolute inset-x-0 top-2/3 h-px bg-white/40\" />\n </div>\n );\n}\n","\"use client\";\n\n/**\n * CountdownOverlay — the big centered timer digits (iPhone timer capture):\n * one large white numeral per second, scaling in as it changes.\n *\n * Package source (`@ai-matrx/capture`) — presentational only.\n */\n\nimport React from \"react\";\n\nexport function CountdownOverlay({ seconds }: { seconds: number | null }) {\n if (seconds === null || seconds <= 0) return null;\n return (\n <div className=\"pointer-events-none absolute inset-0 z-30 flex items-center justify-center\">\n <span\n key={seconds}\n className=\"text-[120px] font-light text-white drop-shadow-lg [@starting-style]:scale-125 [@starting-style]:opacity-0 transition-all duration-300\"\n >\n {seconds}\n </span>\n </div>\n );\n}\n","\"use client\";\n\n/**\n * ImageEditSheet — instant in-browser image editing, core to the package\n * (THE CLOUD LAW's sibling: capture without instant edit is half a system).\n * v1 covers the basics that belong in the browser — crop (free + 1:1, 4:3,\n * 16:9 presets), rotate 90°, flip — full-screen dark chrome in the iOS\n * editing style: Cancel/Save top bar, the image letterboxed center, a\n * draggable/resizable crop frame with corner handles, tool row bottom.\n * Heavier AI edits ride host-injected actions, not this sheet.\n *\n * Output is a JPEG re-encode of the ORIGINAL pixels (rotation/flip/crop\n * applied on canvas) — never a screenshot of the preview.\n *\n * Package source (`@ai-matrx/capture`) — browser canvas only, no app deps.\n */\n\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { Check, FlipHorizontal2, RotateCcw, X } from \"lucide-react\";\nimport { cn } from \"../cn\";\n\ntype AspectPreset = \"free\" | \"1:1\" | \"4:3\" | \"16:9\";\nconst ASPECTS: { id: AspectPreset; label: string; ratio: number | null }[] = [\n { id: \"free\", label: \"Free\", ratio: null },\n { id: \"1:1\", label: \"Square\", ratio: 1 },\n { id: \"4:3\", label: \"4:3\", ratio: 4 / 3 },\n { id: \"16:9\", label: \"16:9\", ratio: 16 / 9 },\n];\n\n/** Normalized crop rect (0..1) relative to the ROTATED/FLIPPED image. */\ninterface CropRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface ImageEditSheetProps {\n open: boolean;\n /** Source image URL (object URL or resolvable src). */\n src: string | null;\n onClose: () => void;\n /** Receives the edited JPEG. The host persists it (cloud port). */\n onSave: (blob: Blob) => void;\n}\n\nconst FULL_CROP: CropRect = { x: 0, y: 0, w: 1, h: 1 };\nconst MIN_CROP = 0.08;\n\nexport function ImageEditSheet({\n open,\n src,\n onClose,\n onSave,\n}: ImageEditSheetProps) {\n const [rotation, setRotation] = useState(0); // 0|90|180|270, CCW steps\n const [flipped, setFlipped] = useState(false);\n const [aspect, setAspect] = useState<AspectPreset>(\"free\");\n const [crop, setCrop] = useState<CropRect>(FULL_CROP);\n const [saving, setSaving] = useState(false);\n const stageRef = useRef<HTMLDivElement | null>(null);\n const imgRef = useRef<HTMLImageElement | null>(null);\n const dragRef = useRef<{\n kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\";\n startX: number;\n startY: number;\n startCrop: CropRect;\n } | null>(null);\n\n useEffect(() => {\n if (open) {\n setRotation(0);\n setFlipped(false);\n setAspect(\"free\");\n setCrop(FULL_CROP);\n setSaving(false);\n }\n }, [open, src]);\n\n const applyAspect = useCallback((preset: AspectPreset) => {\n setAspect(preset);\n const ratio = ASPECTS.find((a) => a.id === preset)?.ratio ?? null;\n if (ratio === null) return;\n // Fit the largest centered rect of the target ratio inside the frame,\n // in DISPLAYED-image normalized space (needs the element's real aspect).\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n const rotated = rotationSwapsAxes();\n const iw = rotated ? img.naturalHeight : img.naturalWidth;\n const ih = rotated ? img.naturalWidth : img.naturalHeight;\n const imageRatio = iw / ih;\n let w = 1;\n let h = 1;\n if (ratio > imageRatio) h = imageRatio / ratio;\n else w = ratio / imageRatio;\n setCrop({ x: (1 - w) / 2, y: (1 - h) / 2, w, h });\n\n function rotationSwapsAxes() {\n return rotation % 180 !== 0;\n }\n }, [rotation]);\n\n const onPointerDown = useCallback(\n (kind: \"move\" | \"nw\" | \"ne\" | \"sw\" | \"se\") =>\n (e: React.PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n dragRef.current = {\n kind,\n startX: e.clientX,\n startY: e.clientY,\n startCrop: crop,\n };\n },\n [crop],\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent) => {\n const drag = dragRef.current;\n const img = imgRef.current;\n if (!drag || !img) return;\n const rect = img.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return;\n const dx = (e.clientX - drag.startX) / rect.width;\n const dy = (e.clientY - drag.startY) / rect.height;\n const c = { ...drag.startCrop };\n const ratio = ASPECTS.find((a) => a.id === aspect)?.ratio ?? null;\n const frameRatio = rect.width / rect.height;\n\n if (drag.kind === \"move\") {\n c.x = Math.min(1 - c.w, Math.max(0, c.x + dx));\n c.y = Math.min(1 - c.h, Math.max(0, c.y + dy));\n } else {\n const left = drag.kind === \"nw\" || drag.kind === \"sw\";\n const top = drag.kind === \"nw\" || drag.kind === \"ne\";\n let x2 = c.x + c.w;\n let y2 = c.y + c.h;\n if (left) c.x = Math.min(x2 - MIN_CROP, Math.max(0, c.x + dx));\n else x2 = Math.max(c.x + MIN_CROP, Math.min(1, x2 + dx));\n if (top) c.y = Math.min(y2 - MIN_CROP, Math.max(0, c.y + dy));\n else y2 = Math.max(c.y + MIN_CROP, Math.min(1, y2 + dy));\n c.w = x2 - c.x;\n c.h = y2 - c.y;\n if (ratio !== null) {\n // Lock the ratio by deriving height from width in SCREEN space.\n const targetH = (c.w * frameRatio) / ratio;\n if (top) c.y = y2 - Math.min(targetH, y2);\n c.h = Math.min(targetH, top ? y2 - c.y : 1 - c.y);\n c.w = (c.h * ratio) / frameRatio;\n if (left) c.x = x2 - c.w;\n }\n }\n setCrop(c);\n },\n [aspect],\n );\n\n const onPointerUp = useCallback(() => {\n dragRef.current = null;\n }, []);\n\n const save = useCallback(() => {\n const img = imgRef.current;\n if (!img || img.naturalWidth === 0) return;\n setSaving(true);\n try {\n const swap = rotation % 180 !== 0;\n const outW = Math.round((swap ? img.naturalHeight : img.naturalWidth) * crop.w);\n const outH = Math.round((swap ? img.naturalWidth : img.naturalHeight) * crop.h);\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.max(1, outW);\n canvas.height = Math.max(1, outH);\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"no 2d context\");\n // Move into crop space, then apply rotation/flip about the image center.\n const rw = swap ? img.naturalHeight : img.naturalWidth;\n const rh = swap ? img.naturalWidth : img.naturalHeight;\n ctx.translate(-crop.x * rw, -crop.y * rh);\n ctx.translate(rw / 2, rh / 2);\n ctx.rotate((rotation * Math.PI) / 180);\n if (flipped) ctx.scale(-1, 1);\n ctx.drawImage(img, -img.naturalWidth / 2, -img.naturalHeight / 2);\n canvas.toBlob(\n (blob) => {\n setSaving(false);\n if (blob) {\n onSave(blob);\n onClose();\n }\n },\n \"image/jpeg\",\n 0.92,\n );\n } catch (err) {\n console.error(\"[capture-camera] edit save failed\", err);\n setSaving(false);\n }\n }, [rotation, flipped, crop, onSave, onClose]);\n\n if (!open || !src) return null;\n\n return (\n <div className=\"absolute inset-0 z-50 flex flex-col bg-black\">\n {/* Top bar */}\n <div className=\"flex shrink-0 items-center justify-between px-4 pt-safe\">\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Cancel editing\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-medium text-white\"\n >\n <X className=\"h-5 w-5\" />\n Cancel\n </button>\n <span className=\"text-[15px] font-semibold text-white/90\">Edit</span>\n <button\n type=\"button\"\n onClick={save}\n disabled={saving}\n aria-label=\"Save edited image\"\n className=\"flex h-11 items-center gap-1.5 rounded-full px-3 text-[15px] font-semibold text-[#FFCC00] disabled:opacity-50\"\n >\n <Check className=\"h-5 w-5\" />\n Save\n </button>\n </div>\n\n {/* Stage */}\n <div\n ref={stageRef}\n className=\"relative flex min-h-0 flex-1 items-center justify-center overflow-hidden p-4\"\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <div className=\"relative max-h-full max-w-full\">\n {/* eslint-disable-next-line @next/next/no-img-element -- local object URL being edited */}\n <img\n ref={imgRef}\n src={src}\n alt=\"Image being edited\"\n draggable={false}\n className=\"max-h-[62dvh] max-w-full select-none object-contain\"\n style={{\n transform: `rotate(${rotation}deg) ${flipped ? \"scaleX(-1)\" : \"\"}`,\n }}\n />\n {/* Crop frame in displayed-image space */}\n <div\n role=\"presentation\"\n onPointerDown={onPointerDown(\"move\")}\n className=\"absolute cursor-move touch-none border-2 border-white shadow-[0_0_0_9999px_rgba(0,0,0,0.55)]\"\n style={{\n left: `${crop.x * 100}%`,\n top: `${crop.y * 100}%`,\n width: `${crop.w * 100}%`,\n height: `${crop.h * 100}%`,\n }}\n >\n {([\"nw\", \"ne\", \"sw\", \"se\"] as const).map((corner) => (\n <span\n key={corner}\n role=\"presentation\"\n onPointerDown={onPointerDown(corner)}\n className={cn(\n \"absolute h-6 w-6 touch-none\",\n corner === \"nw\" &&\n \"-left-1.5 -top-1.5 border-l-4 border-t-4 cursor-nwse-resize\",\n corner === \"ne\" &&\n \"-right-1.5 -top-1.5 border-r-4 border-t-4 cursor-nesw-resize\",\n corner === \"sw\" &&\n \"-bottom-1.5 -left-1.5 border-b-4 border-l-4 cursor-nesw-resize\",\n corner === \"se\" &&\n \"-bottom-1.5 -right-1.5 border-b-4 border-r-4 cursor-nwse-resize\",\n \"border-white\",\n )}\n />\n ))}\n </div>\n </div>\n </div>\n\n {/* Tool row */}\n <div className=\"shrink-0 pb-safe\">\n <div className=\"flex items-center justify-center gap-2 pb-2\">\n {ASPECTS.map((a) => (\n <button\n key={a.id}\n type=\"button\"\n onClick={() => applyAspect(a.id)}\n aria-pressed={aspect === a.id}\n className={cn(\n \"touch-manipulation rounded-full px-3.5 py-1.5 text-[12px] font-semibold uppercase tracking-wide transition-colors\",\n aspect === a.id\n ? \"bg-white/20 text-[#FFCC00]\"\n : \"text-white/80\",\n )}\n >\n {a.label}\n </button>\n ))}\n </div>\n <div className=\"flex items-center justify-center gap-6 pb-4\">\n <button\n type=\"button\"\n onClick={() => {\n setRotation((r) => (r + 270) % 360);\n setCrop(FULL_CROP);\n setAspect(\"free\");\n }}\n aria-label=\"Rotate left\"\n className=\"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10 text-white\"\n >\n <RotateCcw className=\"h-5 w-5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setFlipped((f) => !f)}\n aria-label=\"Flip horizontally\"\n aria-pressed={flipped}\n className={cn(\n \"flex h-12 w-12 touch-manipulation items-center justify-center rounded-full bg-white/10\",\n flipped ? \"text-[#FFCC00]\" : \"text-white\",\n )}\n >\n <FlipHorizontal2 className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_lucide_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_lucide_react","import_jsx_runtime"]}