@ai-matrx/capture 0.2.0 → 0.3.0
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 +17 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/react.cjs +681 -22
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +228 -2
- package/dist/react.d.ts +228 -2
- package/dist/react.js +694 -25
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/dist/react.d.cts
CHANGED
|
@@ -114,6 +114,54 @@ interface CaptureCameraSlots {
|
|
|
114
114
|
/** Free overlays rendered above everything (sheets, pagers). */
|
|
115
115
|
overlays?: React.ReactNode;
|
|
116
116
|
}
|
|
117
|
+
/** One action on the v3 right-hand rail. */
|
|
118
|
+
interface CaptureRailAction {
|
|
119
|
+
id: string;
|
|
120
|
+
/** Accessible name; also the label shown when `showLabels` is on. */
|
|
121
|
+
label: string;
|
|
122
|
+
icon: React.ReactNode;
|
|
123
|
+
/** Engaged/on — rendered in the accent colour. */
|
|
124
|
+
active?: boolean;
|
|
125
|
+
/** A short read-out under the icon (e.g. "3s", "4:3", "Auto"). */
|
|
126
|
+
valueLabel?: string;
|
|
127
|
+
disabled?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Primary actions stay visible when the rail is collapsed. Everything else
|
|
130
|
+
* hides behind the chevron. Keep this to the two or three a user reaches for
|
|
131
|
+
* without thinking — a rail where nothing collapses is just a long rail.
|
|
132
|
+
*/
|
|
133
|
+
primary?: boolean;
|
|
134
|
+
onPress: () => void;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Slots for the v3 chrome. Deliberately fewer and differently shaped than
|
|
138
|
+
* `CaptureCameraSlots`: v3 has no mode row, so the slots that existed to sit
|
|
139
|
+
* around one are gone, and the rail replaces the options grid.
|
|
140
|
+
*/
|
|
141
|
+
interface CaptureCameraV3Slots {
|
|
142
|
+
/** Centre of the top row — the item label / count. Keep it to one line. */
|
|
143
|
+
topCenter?: React.ReactNode;
|
|
144
|
+
/** Honesty chips under the top row (QR confirmation, upload progress). */
|
|
145
|
+
statusChips?: React.ReactNode;
|
|
146
|
+
/** Rendered OVER the feed just above the shutter row — the filmstrip home. */
|
|
147
|
+
aboveShutter?: React.ReactNode;
|
|
148
|
+
/**
|
|
149
|
+
* The one action to the RIGHT of the shutter (Next / Break / Process).
|
|
150
|
+
* Exactly one: the shutter row is a thumb-reach zone and a second control
|
|
151
|
+
* there turns a one-hand camera into a two-hand one.
|
|
152
|
+
*/
|
|
153
|
+
shutterTrailing?: React.ReactNode;
|
|
154
|
+
/** Actions on the right rail, in order. */
|
|
155
|
+
railActions?: CaptureRailAction[];
|
|
156
|
+
/**
|
|
157
|
+
* The expanding text entry pinned to the top row (serial, tag, caption).
|
|
158
|
+
* Supply `<CaptureExpandingField/>` or any control — it is a slot so a host
|
|
159
|
+
* that needs none pays nothing for it.
|
|
160
|
+
*/
|
|
161
|
+
topEntry?: React.ReactNode;
|
|
162
|
+
/** Free overlays above everything (sheets, pagers). */
|
|
163
|
+
overlays?: React.ReactNode;
|
|
164
|
+
}
|
|
117
165
|
|
|
118
166
|
/**
|
|
119
167
|
* CaptureSheet — the iOS-style system sheet used over the camera: a light,
|
|
@@ -240,6 +288,9 @@ interface CameraCaptureProps {
|
|
|
240
288
|
preview: React__default.ReactNode;
|
|
241
289
|
/** Session media — enables the package-owned filmstrip + viewer + editor. */
|
|
242
290
|
media?: CaptureMediaSession;
|
|
291
|
+
/** Fires when the package review loop (viewer or editor) opens/closes over
|
|
292
|
+
* the live feed — hosts pause things that watch the feed (QR scanning). */
|
|
293
|
+
onReviewOpenChange?: (open: boolean) => void;
|
|
243
294
|
/** Close affordance top-left; omitted = no close button. */
|
|
244
295
|
onClose?: () => void;
|
|
245
296
|
/** Body + actions for the camera-blocked iOS-style sheet. */
|
|
@@ -252,7 +303,182 @@ interface CameraCaptureProps {
|
|
|
252
303
|
shutterDisabled?: boolean;
|
|
253
304
|
slots?: CaptureCameraSlots;
|
|
254
305
|
}
|
|
255
|
-
declare function CameraCapture({ engine, cloud, mode, onModeChange, preview, media, onClose, blockedSheet, controlsHidden, shutterDisabled, slots, }: CameraCaptureProps): React__default.JSX.Element;
|
|
306
|
+
declare function CameraCapture({ engine, cloud, mode, onModeChange, preview, media, onReviewOpenChange, onClose, blockedSheet, controlsHidden, shutterDisabled, slots, }: CameraCaptureProps): React__default.JSX.Element;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* CameraCaptureV3 — the vertical-rail camera chrome.
|
|
310
|
+
*
|
|
311
|
+
* A SECOND chrome over the same ports as `CameraCapture`, not a replacement.
|
|
312
|
+
* Same engine, same cloud port, same package-owned review loop (filmstrip →
|
|
313
|
+
* viewer → editor). What differs is the layout and the input model, and every
|
|
314
|
+
* difference answers something the v2 chrome got wrong on a real phone
|
|
315
|
+
* (Arman, 2026-08-30):
|
|
316
|
+
*
|
|
317
|
+
* ONE SHUTTER. Tap = photo, press-and-hold = video. v2 spent a whole
|
|
318
|
+
* row on a mode selector, so the user paid bar height AND
|
|
319
|
+
* a decision before the moment they were trying to catch.
|
|
320
|
+
* THE RIGHT EDGE. Options live on a rail over the feed instead of a
|
|
321
|
+
* two-tap grid behind a button. No layout cost, one tap
|
|
322
|
+
* to anything, and the viewfinder stays whole.
|
|
323
|
+
* IT COLLAPSES. Extras hide behind a chevron so the idle rail is short.
|
|
324
|
+
* TEXT IS A BUTTON. Entry expands from a pill (see CaptureExpandingField) —
|
|
325
|
+
* no permanent field, no keyboard-bait over the frame.
|
|
326
|
+
* ONE MEDIA DOOR. There is no UPLOAD mode. The library button is the only
|
|
327
|
+
* way to existing media, and picking files is an option
|
|
328
|
+
* INSIDE that drawer — the host renders it there.
|
|
329
|
+
*
|
|
330
|
+
* Deliberately NOT copied from the app this borrows its shape from: fixed
|
|
331
|
+
* duration choices (they belong to that product's format, not to a camera),
|
|
332
|
+
* and the oversized promo pill for a new feature (chrome is not a billboard).
|
|
333
|
+
*
|
|
334
|
+
* Package source (`@ai-matrx/capture`). UI state only — capture, streams and
|
|
335
|
+
* persistence come from the injected engine.
|
|
336
|
+
*/
|
|
337
|
+
|
|
338
|
+
interface CameraCaptureV3Props {
|
|
339
|
+
engine: CaptureCameraEngine;
|
|
340
|
+
/** THE CLOUD LAW: still required. v3 changes where the door is, not whether
|
|
341
|
+
* there is one — and in v3 it is the ONLY door to existing media. */
|
|
342
|
+
cloud: CaptureCloudPort;
|
|
343
|
+
preview: React__default.ReactNode;
|
|
344
|
+
media?: CaptureMediaSession;
|
|
345
|
+
/**
|
|
346
|
+
* Fired on pointer-DOWN, before the hold threshold decides photo vs video.
|
|
347
|
+
*
|
|
348
|
+
* 🚨 THIS EXISTS FOR iOS. Hosts warm the microphone when they believe a
|
|
349
|
+
* recording is coming (one permission prompt per medium). With a mode row
|
|
350
|
+
* that signal arrived when the user chose VIDEO — early. With a hold
|
|
351
|
+
* shutter there is no such moment, so waiting for the recording to actually
|
|
352
|
+
* start means warming DURING the take, which on iOS Safari costs the first
|
|
353
|
+
* second of audio or throws a prompt over the viewfinder mid-recording.
|
|
354
|
+
*
|
|
355
|
+
* The host is expected to make this idempotent: it fires on every press,
|
|
356
|
+
* including the taps that turn out to be photos.
|
|
357
|
+
*/
|
|
358
|
+
onRecordIntent?: () => void;
|
|
359
|
+
/** Ring completes here; presentational only — it never stops the recorder. */
|
|
360
|
+
maxRecordSeconds?: number;
|
|
361
|
+
onReviewOpenChange?: (open: boolean) => void;
|
|
362
|
+
onClose?: () => void;
|
|
363
|
+
blockedSheet?: {
|
|
364
|
+
body: React__default.ReactNode;
|
|
365
|
+
actions: CaptureSheetAction[];
|
|
366
|
+
};
|
|
367
|
+
/** Hide all chrome except status chips (host renders its own toggle). */
|
|
368
|
+
controlsHidden?: boolean;
|
|
369
|
+
shutterDisabled?: boolean;
|
|
370
|
+
slots?: CaptureCameraV3Slots;
|
|
371
|
+
}
|
|
372
|
+
declare function CameraCaptureV3({ engine, cloud, preview, media, onRecordIntent, maxRecordSeconds, onReviewOpenChange, onClose, blockedSheet, controlsHidden, shutterDisabled, slots, }: CameraCaptureV3Props): React__default.JSX.Element;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* CaptureExpandingField — a pill BUTTON that becomes a text field on press,
|
|
376
|
+
* and goes back to being a button when it is done.
|
|
377
|
+
*
|
|
378
|
+
* 🚨 WHY IT IS NOT AN INPUT (Arman, 2026-08-30). v2 pinned a live `<input>` in
|
|
379
|
+
* the bottom bar. On a camera that is wrong three ways: it occupies a row
|
|
380
|
+
* permanently for something used occasionally, it invites the keyboard over
|
|
381
|
+
* the viewfinder on an accidental tap, and it makes an idle camera look like a
|
|
382
|
+
* form. The pattern that fits is the one Apple uses for search — a compact
|
|
383
|
+
* control that admits it is a control, and grows only when you mean it.
|
|
384
|
+
*
|
|
385
|
+
* COMMIT ON UNMOUNT, always. The v2 input already learned this the hard way:
|
|
386
|
+
* hiding the controls or switching item remounts the field, `onBlur` never
|
|
387
|
+
* fires, and a typed value evaporates. Anything typed is committed on the way
|
|
388
|
+
* out, whatever caused the exit.
|
|
389
|
+
*
|
|
390
|
+
* Presentational + local draft state only; the host owns what a value means.
|
|
391
|
+
*/
|
|
392
|
+
|
|
393
|
+
interface CaptureExpandingFieldProps {
|
|
394
|
+
/** Collapsed label, e.g. "Serial / tag". Also the accessible name. */
|
|
395
|
+
label: string;
|
|
396
|
+
/** Placeholder once expanded. Defaults to `label`. */
|
|
397
|
+
placeholder?: string;
|
|
398
|
+
/** Icon shown in the collapsed pill and at the field's leading edge. */
|
|
399
|
+
icon?: React__default.ReactNode;
|
|
400
|
+
/** Commit a non-empty trimmed value. Called on Enter, blur, and unmount. */
|
|
401
|
+
onCommit: (value: string) => void;
|
|
402
|
+
/** Start expanded (a host that knows entry is imminent). */
|
|
403
|
+
defaultExpanded?: boolean;
|
|
404
|
+
disabled?: boolean;
|
|
405
|
+
/** Shown in the collapsed pill instead of `label` — e.g. the current tag. */
|
|
406
|
+
currentValue?: string | null;
|
|
407
|
+
className?: string;
|
|
408
|
+
}
|
|
409
|
+
declare function CaptureExpandingField({ label, placeholder, icon, onCommit, defaultExpanded, disabled, currentValue, className, }: CaptureExpandingFieldProps): React__default.JSX.Element;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* CaptureRail — the vertical action rail down the RIGHT edge of the frame.
|
|
413
|
+
*
|
|
414
|
+
* Why the edge and not a bar: a bottom bar steals height from the viewfinder
|
|
415
|
+
* on every phone, and the options grid it fed cost two taps to reach anything.
|
|
416
|
+
* A rail sits over the feed, costs no layout, and puts every option one tap
|
|
417
|
+
* away under the thumb that is already holding the phone.
|
|
418
|
+
*
|
|
419
|
+
* Why it collapses: a complete rail is a long rail, and a long rail is a wall
|
|
420
|
+
* of icons nobody reads. Actions marked `primary` stay; the rest hide behind a
|
|
421
|
+
* chevron. The chevron is only rendered when there is something to hide —
|
|
422
|
+
* a control that toggles nothing is a lie about the interface.
|
|
423
|
+
*
|
|
424
|
+
* Presentational only.
|
|
425
|
+
*/
|
|
426
|
+
|
|
427
|
+
interface CaptureRailProps {
|
|
428
|
+
actions: CaptureRailAction[];
|
|
429
|
+
/** Start expanded. Default false — an idle camera shows the short rail. */
|
|
430
|
+
defaultExpanded?: boolean;
|
|
431
|
+
className?: string;
|
|
432
|
+
}
|
|
433
|
+
declare function CaptureRail({ actions, defaultExpanded, className, }: CaptureRailProps): React__default.JSX.Element | null;
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* HoldShutter — ONE button for photo and video.
|
|
437
|
+
*
|
|
438
|
+
* Tap takes a photo. Press and hold records, and releasing stops. The mode is
|
|
439
|
+
* therefore a gesture, not a control you must set before the moment you are
|
|
440
|
+
* trying to catch — which is the whole reason the v2 mode row was wrong on a
|
|
441
|
+
* phone: it cost bar height AND a decision, and the decision was usually made
|
|
442
|
+
* after the thing had happened.
|
|
443
|
+
*
|
|
444
|
+
* The interaction, precisely:
|
|
445
|
+
*
|
|
446
|
+
* pointerdown → arm a hold timer (HOLD_MS)
|
|
447
|
+
* timer fires → start recording, ring begins filling
|
|
448
|
+
* pointerup < HOLD_MS → cancel the timer, take a photo
|
|
449
|
+
* pointerup >= HOLD_MS → stop recording
|
|
450
|
+
* pointercancel / leave → same as pointerup (a drag off the button must not
|
|
451
|
+
* leave the camera recording with nothing to stop it)
|
|
452
|
+
*
|
|
453
|
+
* A LOCK is deliberate: after ~1.2s of recording the button latches, so a user
|
|
454
|
+
* whose finger slips does not lose the take. Once latched, the next tap stops.
|
|
455
|
+
* Without it, the single-button model punishes exactly the long recordings it
|
|
456
|
+
* exists to make easy.
|
|
457
|
+
*
|
|
458
|
+
* Presentational only — the host owns the recorder.
|
|
459
|
+
*/
|
|
460
|
+
|
|
461
|
+
interface HoldShutterProps {
|
|
462
|
+
recording: boolean;
|
|
463
|
+
/** Elapsed recording seconds, for the ring + read-out. */
|
|
464
|
+
elapsedSeconds: number;
|
|
465
|
+
/**
|
|
466
|
+
* Ring completes at this many seconds. Presentational only — it does not
|
|
467
|
+
* stop the recorder, because a chrome that silently ends a take is worse
|
|
468
|
+
* than one that shows a full ring. Omit for no progress arc.
|
|
469
|
+
*/
|
|
470
|
+
maxSeconds?: number;
|
|
471
|
+
disabled?: boolean;
|
|
472
|
+
/**
|
|
473
|
+
* Fired on pointer-DOWN, before the hold threshold decides photo vs video.
|
|
474
|
+
* Hosts use it to warm the microphone — see `CameraCaptureV3Props`.
|
|
475
|
+
*/
|
|
476
|
+
onPressStart?: () => void;
|
|
477
|
+
onPhoto: () => void;
|
|
478
|
+
onStartRecording: () => void;
|
|
479
|
+
onStopRecording: () => void;
|
|
480
|
+
}
|
|
481
|
+
declare function HoldShutter({ recording, elapsedSeconds, maxSeconds, disabled, onPressStart, onPhoto, onStartRecording, onStopRecording, }: HoldShutterProps): React__default.JSX.Element;
|
|
256
482
|
|
|
257
483
|
/**
|
|
258
484
|
* CameraFeed — the package's live preview <video> for the default engine
|
|
@@ -491,4 +717,4 @@ interface DefaultEngineOptions {
|
|
|
491
717
|
}
|
|
492
718
|
declare function useDefaultCaptureEngine(options: DefaultEngineOptions): CaptureCameraEngine;
|
|
493
719
|
|
|
494
|
-
export { CameraCapture, type CameraCaptureProps, CameraFeed, type CaptureAspect, type CaptureCameraEngine, type CaptureCameraMode, type CaptureCameraSlots, type CaptureCloudPort, CaptureFilmstrip, type CaptureFilmstripProps, type CaptureMediaItem, type CaptureMediaSession, type CaptureOptionTile, CaptureSheet, type CaptureSheetAction, type CaptureSheetProps, type CaptureTimerSetting, CountdownOverlay, type DefaultEngineOptions, GridOverlay, ImageEditSheet, type ImageEditSheetProps, MediaViewer, type MediaViewerProps, ModeSelector, type ModeSelectorProps, OptionsGridPanel, type OptionsGridPanelProps, type ResolvedMedia, ShutterButton, type ShutterButtonProps, type TrackControls, ZoomRow, type ZoomRowProps, getMediaUrl, invalidateMedia, primeMedia, useDefaultCaptureEngine, useMediaUrl, useTrackControls };
|
|
720
|
+
export { CameraCapture, type CameraCaptureProps, CameraCaptureV3, type CameraCaptureV3Props, CameraFeed, type CaptureAspect, type CaptureCameraEngine, type CaptureCameraMode, type CaptureCameraSlots, type CaptureCameraV3Slots, type CaptureCloudPort, CaptureExpandingField, type CaptureExpandingFieldProps, CaptureFilmstrip, type CaptureFilmstripProps, type CaptureMediaItem, type CaptureMediaSession, type CaptureOptionTile, CaptureRail, type CaptureRailAction, type CaptureRailProps, CaptureSheet, type CaptureSheetAction, type CaptureSheetProps, type CaptureTimerSetting, CountdownOverlay, type DefaultEngineOptions, GridOverlay, HoldShutter, type HoldShutterProps, ImageEditSheet, type ImageEditSheetProps, MediaViewer, type MediaViewerProps, ModeSelector, type ModeSelectorProps, OptionsGridPanel, type OptionsGridPanelProps, type ResolvedMedia, ShutterButton, type ShutterButtonProps, type TrackControls, ZoomRow, type ZoomRowProps, getMediaUrl, invalidateMedia, primeMedia, useDefaultCaptureEngine, useMediaUrl, useTrackControls };
|
package/dist/react.d.ts
CHANGED
|
@@ -114,6 +114,54 @@ interface CaptureCameraSlots {
|
|
|
114
114
|
/** Free overlays rendered above everything (sheets, pagers). */
|
|
115
115
|
overlays?: React.ReactNode;
|
|
116
116
|
}
|
|
117
|
+
/** One action on the v3 right-hand rail. */
|
|
118
|
+
interface CaptureRailAction {
|
|
119
|
+
id: string;
|
|
120
|
+
/** Accessible name; also the label shown when `showLabels` is on. */
|
|
121
|
+
label: string;
|
|
122
|
+
icon: React.ReactNode;
|
|
123
|
+
/** Engaged/on — rendered in the accent colour. */
|
|
124
|
+
active?: boolean;
|
|
125
|
+
/** A short read-out under the icon (e.g. "3s", "4:3", "Auto"). */
|
|
126
|
+
valueLabel?: string;
|
|
127
|
+
disabled?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Primary actions stay visible when the rail is collapsed. Everything else
|
|
130
|
+
* hides behind the chevron. Keep this to the two or three a user reaches for
|
|
131
|
+
* without thinking — a rail where nothing collapses is just a long rail.
|
|
132
|
+
*/
|
|
133
|
+
primary?: boolean;
|
|
134
|
+
onPress: () => void;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Slots for the v3 chrome. Deliberately fewer and differently shaped than
|
|
138
|
+
* `CaptureCameraSlots`: v3 has no mode row, so the slots that existed to sit
|
|
139
|
+
* around one are gone, and the rail replaces the options grid.
|
|
140
|
+
*/
|
|
141
|
+
interface CaptureCameraV3Slots {
|
|
142
|
+
/** Centre of the top row — the item label / count. Keep it to one line. */
|
|
143
|
+
topCenter?: React.ReactNode;
|
|
144
|
+
/** Honesty chips under the top row (QR confirmation, upload progress). */
|
|
145
|
+
statusChips?: React.ReactNode;
|
|
146
|
+
/** Rendered OVER the feed just above the shutter row — the filmstrip home. */
|
|
147
|
+
aboveShutter?: React.ReactNode;
|
|
148
|
+
/**
|
|
149
|
+
* The one action to the RIGHT of the shutter (Next / Break / Process).
|
|
150
|
+
* Exactly one: the shutter row is a thumb-reach zone and a second control
|
|
151
|
+
* there turns a one-hand camera into a two-hand one.
|
|
152
|
+
*/
|
|
153
|
+
shutterTrailing?: React.ReactNode;
|
|
154
|
+
/** Actions on the right rail, in order. */
|
|
155
|
+
railActions?: CaptureRailAction[];
|
|
156
|
+
/**
|
|
157
|
+
* The expanding text entry pinned to the top row (serial, tag, caption).
|
|
158
|
+
* Supply `<CaptureExpandingField/>` or any control — it is a slot so a host
|
|
159
|
+
* that needs none pays nothing for it.
|
|
160
|
+
*/
|
|
161
|
+
topEntry?: React.ReactNode;
|
|
162
|
+
/** Free overlays above everything (sheets, pagers). */
|
|
163
|
+
overlays?: React.ReactNode;
|
|
164
|
+
}
|
|
117
165
|
|
|
118
166
|
/**
|
|
119
167
|
* CaptureSheet — the iOS-style system sheet used over the camera: a light,
|
|
@@ -240,6 +288,9 @@ interface CameraCaptureProps {
|
|
|
240
288
|
preview: React__default.ReactNode;
|
|
241
289
|
/** Session media — enables the package-owned filmstrip + viewer + editor. */
|
|
242
290
|
media?: CaptureMediaSession;
|
|
291
|
+
/** Fires when the package review loop (viewer or editor) opens/closes over
|
|
292
|
+
* the live feed — hosts pause things that watch the feed (QR scanning). */
|
|
293
|
+
onReviewOpenChange?: (open: boolean) => void;
|
|
243
294
|
/** Close affordance top-left; omitted = no close button. */
|
|
244
295
|
onClose?: () => void;
|
|
245
296
|
/** Body + actions for the camera-blocked iOS-style sheet. */
|
|
@@ -252,7 +303,182 @@ interface CameraCaptureProps {
|
|
|
252
303
|
shutterDisabled?: boolean;
|
|
253
304
|
slots?: CaptureCameraSlots;
|
|
254
305
|
}
|
|
255
|
-
declare function CameraCapture({ engine, cloud, mode, onModeChange, preview, media, onClose, blockedSheet, controlsHidden, shutterDisabled, slots, }: CameraCaptureProps): React__default.JSX.Element;
|
|
306
|
+
declare function CameraCapture({ engine, cloud, mode, onModeChange, preview, media, onReviewOpenChange, onClose, blockedSheet, controlsHidden, shutterDisabled, slots, }: CameraCaptureProps): React__default.JSX.Element;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* CameraCaptureV3 — the vertical-rail camera chrome.
|
|
310
|
+
*
|
|
311
|
+
* A SECOND chrome over the same ports as `CameraCapture`, not a replacement.
|
|
312
|
+
* Same engine, same cloud port, same package-owned review loop (filmstrip →
|
|
313
|
+
* viewer → editor). What differs is the layout and the input model, and every
|
|
314
|
+
* difference answers something the v2 chrome got wrong on a real phone
|
|
315
|
+
* (Arman, 2026-08-30):
|
|
316
|
+
*
|
|
317
|
+
* ONE SHUTTER. Tap = photo, press-and-hold = video. v2 spent a whole
|
|
318
|
+
* row on a mode selector, so the user paid bar height AND
|
|
319
|
+
* a decision before the moment they were trying to catch.
|
|
320
|
+
* THE RIGHT EDGE. Options live on a rail over the feed instead of a
|
|
321
|
+
* two-tap grid behind a button. No layout cost, one tap
|
|
322
|
+
* to anything, and the viewfinder stays whole.
|
|
323
|
+
* IT COLLAPSES. Extras hide behind a chevron so the idle rail is short.
|
|
324
|
+
* TEXT IS A BUTTON. Entry expands from a pill (see CaptureExpandingField) —
|
|
325
|
+
* no permanent field, no keyboard-bait over the frame.
|
|
326
|
+
* ONE MEDIA DOOR. There is no UPLOAD mode. The library button is the only
|
|
327
|
+
* way to existing media, and picking files is an option
|
|
328
|
+
* INSIDE that drawer — the host renders it there.
|
|
329
|
+
*
|
|
330
|
+
* Deliberately NOT copied from the app this borrows its shape from: fixed
|
|
331
|
+
* duration choices (they belong to that product's format, not to a camera),
|
|
332
|
+
* and the oversized promo pill for a new feature (chrome is not a billboard).
|
|
333
|
+
*
|
|
334
|
+
* Package source (`@ai-matrx/capture`). UI state only — capture, streams and
|
|
335
|
+
* persistence come from the injected engine.
|
|
336
|
+
*/
|
|
337
|
+
|
|
338
|
+
interface CameraCaptureV3Props {
|
|
339
|
+
engine: CaptureCameraEngine;
|
|
340
|
+
/** THE CLOUD LAW: still required. v3 changes where the door is, not whether
|
|
341
|
+
* there is one — and in v3 it is the ONLY door to existing media. */
|
|
342
|
+
cloud: CaptureCloudPort;
|
|
343
|
+
preview: React__default.ReactNode;
|
|
344
|
+
media?: CaptureMediaSession;
|
|
345
|
+
/**
|
|
346
|
+
* Fired on pointer-DOWN, before the hold threshold decides photo vs video.
|
|
347
|
+
*
|
|
348
|
+
* 🚨 THIS EXISTS FOR iOS. Hosts warm the microphone when they believe a
|
|
349
|
+
* recording is coming (one permission prompt per medium). With a mode row
|
|
350
|
+
* that signal arrived when the user chose VIDEO — early. With a hold
|
|
351
|
+
* shutter there is no such moment, so waiting for the recording to actually
|
|
352
|
+
* start means warming DURING the take, which on iOS Safari costs the first
|
|
353
|
+
* second of audio or throws a prompt over the viewfinder mid-recording.
|
|
354
|
+
*
|
|
355
|
+
* The host is expected to make this idempotent: it fires on every press,
|
|
356
|
+
* including the taps that turn out to be photos.
|
|
357
|
+
*/
|
|
358
|
+
onRecordIntent?: () => void;
|
|
359
|
+
/** Ring completes here; presentational only — it never stops the recorder. */
|
|
360
|
+
maxRecordSeconds?: number;
|
|
361
|
+
onReviewOpenChange?: (open: boolean) => void;
|
|
362
|
+
onClose?: () => void;
|
|
363
|
+
blockedSheet?: {
|
|
364
|
+
body: React__default.ReactNode;
|
|
365
|
+
actions: CaptureSheetAction[];
|
|
366
|
+
};
|
|
367
|
+
/** Hide all chrome except status chips (host renders its own toggle). */
|
|
368
|
+
controlsHidden?: boolean;
|
|
369
|
+
shutterDisabled?: boolean;
|
|
370
|
+
slots?: CaptureCameraV3Slots;
|
|
371
|
+
}
|
|
372
|
+
declare function CameraCaptureV3({ engine, cloud, preview, media, onRecordIntent, maxRecordSeconds, onReviewOpenChange, onClose, blockedSheet, controlsHidden, shutterDisabled, slots, }: CameraCaptureV3Props): React__default.JSX.Element;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* CaptureExpandingField — a pill BUTTON that becomes a text field on press,
|
|
376
|
+
* and goes back to being a button when it is done.
|
|
377
|
+
*
|
|
378
|
+
* 🚨 WHY IT IS NOT AN INPUT (Arman, 2026-08-30). v2 pinned a live `<input>` in
|
|
379
|
+
* the bottom bar. On a camera that is wrong three ways: it occupies a row
|
|
380
|
+
* permanently for something used occasionally, it invites the keyboard over
|
|
381
|
+
* the viewfinder on an accidental tap, and it makes an idle camera look like a
|
|
382
|
+
* form. The pattern that fits is the one Apple uses for search — a compact
|
|
383
|
+
* control that admits it is a control, and grows only when you mean it.
|
|
384
|
+
*
|
|
385
|
+
* COMMIT ON UNMOUNT, always. The v2 input already learned this the hard way:
|
|
386
|
+
* hiding the controls or switching item remounts the field, `onBlur` never
|
|
387
|
+
* fires, and a typed value evaporates. Anything typed is committed on the way
|
|
388
|
+
* out, whatever caused the exit.
|
|
389
|
+
*
|
|
390
|
+
* Presentational + local draft state only; the host owns what a value means.
|
|
391
|
+
*/
|
|
392
|
+
|
|
393
|
+
interface CaptureExpandingFieldProps {
|
|
394
|
+
/** Collapsed label, e.g. "Serial / tag". Also the accessible name. */
|
|
395
|
+
label: string;
|
|
396
|
+
/** Placeholder once expanded. Defaults to `label`. */
|
|
397
|
+
placeholder?: string;
|
|
398
|
+
/** Icon shown in the collapsed pill and at the field's leading edge. */
|
|
399
|
+
icon?: React__default.ReactNode;
|
|
400
|
+
/** Commit a non-empty trimmed value. Called on Enter, blur, and unmount. */
|
|
401
|
+
onCommit: (value: string) => void;
|
|
402
|
+
/** Start expanded (a host that knows entry is imminent). */
|
|
403
|
+
defaultExpanded?: boolean;
|
|
404
|
+
disabled?: boolean;
|
|
405
|
+
/** Shown in the collapsed pill instead of `label` — e.g. the current tag. */
|
|
406
|
+
currentValue?: string | null;
|
|
407
|
+
className?: string;
|
|
408
|
+
}
|
|
409
|
+
declare function CaptureExpandingField({ label, placeholder, icon, onCommit, defaultExpanded, disabled, currentValue, className, }: CaptureExpandingFieldProps): React__default.JSX.Element;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* CaptureRail — the vertical action rail down the RIGHT edge of the frame.
|
|
413
|
+
*
|
|
414
|
+
* Why the edge and not a bar: a bottom bar steals height from the viewfinder
|
|
415
|
+
* on every phone, and the options grid it fed cost two taps to reach anything.
|
|
416
|
+
* A rail sits over the feed, costs no layout, and puts every option one tap
|
|
417
|
+
* away under the thumb that is already holding the phone.
|
|
418
|
+
*
|
|
419
|
+
* Why it collapses: a complete rail is a long rail, and a long rail is a wall
|
|
420
|
+
* of icons nobody reads. Actions marked `primary` stay; the rest hide behind a
|
|
421
|
+
* chevron. The chevron is only rendered when there is something to hide —
|
|
422
|
+
* a control that toggles nothing is a lie about the interface.
|
|
423
|
+
*
|
|
424
|
+
* Presentational only.
|
|
425
|
+
*/
|
|
426
|
+
|
|
427
|
+
interface CaptureRailProps {
|
|
428
|
+
actions: CaptureRailAction[];
|
|
429
|
+
/** Start expanded. Default false — an idle camera shows the short rail. */
|
|
430
|
+
defaultExpanded?: boolean;
|
|
431
|
+
className?: string;
|
|
432
|
+
}
|
|
433
|
+
declare function CaptureRail({ actions, defaultExpanded, className, }: CaptureRailProps): React__default.JSX.Element | null;
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* HoldShutter — ONE button for photo and video.
|
|
437
|
+
*
|
|
438
|
+
* Tap takes a photo. Press and hold records, and releasing stops. The mode is
|
|
439
|
+
* therefore a gesture, not a control you must set before the moment you are
|
|
440
|
+
* trying to catch — which is the whole reason the v2 mode row was wrong on a
|
|
441
|
+
* phone: it cost bar height AND a decision, and the decision was usually made
|
|
442
|
+
* after the thing had happened.
|
|
443
|
+
*
|
|
444
|
+
* The interaction, precisely:
|
|
445
|
+
*
|
|
446
|
+
* pointerdown → arm a hold timer (HOLD_MS)
|
|
447
|
+
* timer fires → start recording, ring begins filling
|
|
448
|
+
* pointerup < HOLD_MS → cancel the timer, take a photo
|
|
449
|
+
* pointerup >= HOLD_MS → stop recording
|
|
450
|
+
* pointercancel / leave → same as pointerup (a drag off the button must not
|
|
451
|
+
* leave the camera recording with nothing to stop it)
|
|
452
|
+
*
|
|
453
|
+
* A LOCK is deliberate: after ~1.2s of recording the button latches, so a user
|
|
454
|
+
* whose finger slips does not lose the take. Once latched, the next tap stops.
|
|
455
|
+
* Without it, the single-button model punishes exactly the long recordings it
|
|
456
|
+
* exists to make easy.
|
|
457
|
+
*
|
|
458
|
+
* Presentational only — the host owns the recorder.
|
|
459
|
+
*/
|
|
460
|
+
|
|
461
|
+
interface HoldShutterProps {
|
|
462
|
+
recording: boolean;
|
|
463
|
+
/** Elapsed recording seconds, for the ring + read-out. */
|
|
464
|
+
elapsedSeconds: number;
|
|
465
|
+
/**
|
|
466
|
+
* Ring completes at this many seconds. Presentational only — it does not
|
|
467
|
+
* stop the recorder, because a chrome that silently ends a take is worse
|
|
468
|
+
* than one that shows a full ring. Omit for no progress arc.
|
|
469
|
+
*/
|
|
470
|
+
maxSeconds?: number;
|
|
471
|
+
disabled?: boolean;
|
|
472
|
+
/**
|
|
473
|
+
* Fired on pointer-DOWN, before the hold threshold decides photo vs video.
|
|
474
|
+
* Hosts use it to warm the microphone — see `CameraCaptureV3Props`.
|
|
475
|
+
*/
|
|
476
|
+
onPressStart?: () => void;
|
|
477
|
+
onPhoto: () => void;
|
|
478
|
+
onStartRecording: () => void;
|
|
479
|
+
onStopRecording: () => void;
|
|
480
|
+
}
|
|
481
|
+
declare function HoldShutter({ recording, elapsedSeconds, maxSeconds, disabled, onPressStart, onPhoto, onStartRecording, onStopRecording, }: HoldShutterProps): React__default.JSX.Element;
|
|
256
482
|
|
|
257
483
|
/**
|
|
258
484
|
* CameraFeed — the package's live preview <video> for the default engine
|
|
@@ -491,4 +717,4 @@ interface DefaultEngineOptions {
|
|
|
491
717
|
}
|
|
492
718
|
declare function useDefaultCaptureEngine(options: DefaultEngineOptions): CaptureCameraEngine;
|
|
493
719
|
|
|
494
|
-
export { CameraCapture, type CameraCaptureProps, CameraFeed, type CaptureAspect, type CaptureCameraEngine, type CaptureCameraMode, type CaptureCameraSlots, type CaptureCloudPort, CaptureFilmstrip, type CaptureFilmstripProps, type CaptureMediaItem, type CaptureMediaSession, type CaptureOptionTile, CaptureSheet, type CaptureSheetAction, type CaptureSheetProps, type CaptureTimerSetting, CountdownOverlay, type DefaultEngineOptions, GridOverlay, ImageEditSheet, type ImageEditSheetProps, MediaViewer, type MediaViewerProps, ModeSelector, type ModeSelectorProps, OptionsGridPanel, type OptionsGridPanelProps, type ResolvedMedia, ShutterButton, type ShutterButtonProps, type TrackControls, ZoomRow, type ZoomRowProps, getMediaUrl, invalidateMedia, primeMedia, useDefaultCaptureEngine, useMediaUrl, useTrackControls };
|
|
720
|
+
export { CameraCapture, type CameraCaptureProps, CameraCaptureV3, type CameraCaptureV3Props, CameraFeed, type CaptureAspect, type CaptureCameraEngine, type CaptureCameraMode, type CaptureCameraSlots, type CaptureCameraV3Slots, type CaptureCloudPort, CaptureExpandingField, type CaptureExpandingFieldProps, CaptureFilmstrip, type CaptureFilmstripProps, type CaptureMediaItem, type CaptureMediaSession, type CaptureOptionTile, CaptureRail, type CaptureRailAction, type CaptureRailProps, CaptureSheet, type CaptureSheetAction, type CaptureSheetProps, type CaptureTimerSetting, CountdownOverlay, type DefaultEngineOptions, GridOverlay, HoldShutter, type HoldShutterProps, ImageEditSheet, type ImageEditSheetProps, MediaViewer, type MediaViewerProps, ModeSelector, type ModeSelectorProps, OptionsGridPanel, type OptionsGridPanelProps, type ResolvedMedia, ShutterButton, type ShutterButtonProps, type TrackControls, ZoomRow, type ZoomRowProps, getMediaUrl, invalidateMedia, primeMedia, useDefaultCaptureEngine, useMediaUrl, useTrackControls };
|