@octane-xplat/camera 0.12.0 → 0.14.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.
Files changed (83) hide show
  1. package/README.md +63 -3
  2. package/dist/linux/CameraView.web.tsrx.js +337 -0
  3. package/dist/linux/host-movie.web.js +100 -0
  4. package/dist/linux/index.js +5 -0
  5. package/dist/linux/layout-child.web.js +29 -0
  6. package/dist/linux/movie-metadata.web.js +65 -0
  7. package/dist/linux/octane-client-build.json +8 -0
  8. package/dist/linux/session-backend.linux.js +479 -0
  9. package/dist/linux/session-core.js +368 -0
  10. package/dist/linux/session.js +29 -0
  11. package/dist/linux/synthetic-session.js +280 -0
  12. package/dist/linux/types.js +19 -0
  13. package/dist/native/CameraView.android.js +25 -1
  14. package/dist/native/CameraView.ios.js +24 -1
  15. package/dist/native/CameraView.windows.js +173 -0
  16. package/dist/native/camera-permission.windows.js +43 -0
  17. package/dist/native/camera-session.windows.js +78 -0
  18. package/dist/native/cx.mobile.js +3 -2
  19. package/dist/native/cx.windows.js +9 -0
  20. package/dist/native/index.js +4 -1
  21. package/dist/native/layout-child.windows.js +53 -0
  22. package/dist/native/octane-client-build.json +1 -1
  23. package/dist/native/session-backend.android.js +234 -0
  24. package/dist/native/session-backend.ios.js +682 -0
  25. package/dist/native/session-backend.js +55 -0
  26. package/dist/native/session-backend.windows.js +905 -0
  27. package/dist/native/session-core.js +368 -0
  28. package/dist/native/session.js +29 -0
  29. package/dist/native/synthetic-session.js +280 -0
  30. package/dist/native/types.js +19 -0
  31. package/dist/web/CameraView.web.tsrx.js +28 -8
  32. package/dist/web/host-movie.web.js +100 -0
  33. package/dist/web/index.js +4 -1
  34. package/dist/web/movie-metadata.web.js +65 -0
  35. package/dist/web/movie-storage.web.js +119 -0
  36. package/dist/web/octane-client-build.json +1 -1
  37. package/dist/web/session-backend.web.js +490 -0
  38. package/dist/web/session-core.js +368 -0
  39. package/dist/web/session.js +29 -0
  40. package/dist/web/synthetic-session.js +280 -0
  41. package/dist/web/types.js +19 -0
  42. package/package.json +23 -5
  43. package/platforms/android/AndroidManifest.xml +2 -0
  44. package/platforms/android/include.gradle +1 -0
  45. package/platforms/android/java/com/octanexplat/camera/CameraPreviewView.java +4 -0
  46. package/platforms/android/java/com/octanexplat/camera/CameraSessionBridge.java +363 -0
  47. package/platforms/ios/Info.plist +2 -0
  48. package/platforms/macos/src/XplatCameraHost.swift +513 -0
  49. package/src/CameraView.android.tsrx +21 -1
  50. package/src/CameraView.ios.tsrx +28 -2
  51. package/src/CameraView.macos.tsrx +93 -0
  52. package/src/CameraView.web.tsrx +20 -2
  53. package/src/CameraView.windows.tsrx +124 -0
  54. package/src/camera-permission.windows.ts +76 -0
  55. package/src/camera-session.macos.ts +75 -0
  56. package/src/camera-session.windows.ts +123 -0
  57. package/src/cx.macos.ts +5 -0
  58. package/src/cx.mobile.ts +3 -2
  59. package/src/cx.windows.ts +6 -0
  60. package/src/host-movie.web.ts +180 -0
  61. package/src/index.ts +31 -0
  62. package/src/index.web.ts +31 -0
  63. package/src/layout-child.windows.ts +79 -0
  64. package/src/movie-metadata.web.ts +84 -0
  65. package/src/movie-storage.web.ts +164 -0
  66. package/src/props.ts +7 -1
  67. package/src/session-backend.android.ts +397 -0
  68. package/src/session-backend.ios.ts +1092 -0
  69. package/src/session-backend.linux.ts +869 -0
  70. package/src/session-backend.macos.ts +624 -0
  71. package/src/session-backend.ts +57 -0
  72. package/src/session-backend.web.ts +874 -0
  73. package/src/session-backend.windows.ts +1517 -0
  74. package/src/session-core.ts +659 -0
  75. package/src/session.ts +37 -0
  76. package/src/synthetic-session.ts +367 -0
  77. package/src/types.ts +329 -0
  78. package/types/index.d.ts +43 -0
  79. package/types/props.d.ts +7 -1
  80. package/types/session-core.d.ts +129 -0
  81. package/types/synthetic-session.d.ts +48 -0
  82. package/types/types.d.ts +263 -0
  83. package/src/CameraView.windows.ts +0 -5
package/types/index.d.ts CHANGED
@@ -1,5 +1,48 @@
1
1
  import type { UniversalComponent } from 'octane/universal'
2
2
  import type { CameraViewProps } from './props.js'
3
+ import type { CameraSession, CameraSessionConfig } from './types.js'
4
+ import type { SyntheticCameraScript, SyntheticCameraStorage } from './synthetic-session.js'
3
5
 
4
6
  export declare const CameraView: UniversalComponent<CameraViewProps>
5
7
  export type { CameraViewHandle, CameraViewProps, CameraAccessibilityRole } from './props.js'
8
+
9
+ export declare function createCameraSession(config?: CameraSessionConfig): CameraSession
10
+ export declare function createSyntheticCameraSession(
11
+ config?: CameraSessionConfig,
12
+ script?: Partial<SyntheticCameraScript>,
13
+ storage?: SyntheticCameraStorage,
14
+ ): CameraSession
15
+
16
+ export declare function createMemoryCameraStorage(): SyntheticCameraStorage & {
17
+ files: Map<string, Uint8Array>
18
+ }
19
+
20
+ export { CameraCaptureError } from './types.js'
21
+ export type {
22
+ CameraCaptureErrorKind,
23
+ CameraDeviceInfo,
24
+ CameraFacing,
25
+ CameraInterruptionReason,
26
+ CameraOrientation,
27
+ CameraPermissionKind,
28
+ CameraPermissionStatus,
29
+ CameraProfileCapability,
30
+ CameraRecorderState,
31
+ CameraSelection,
32
+ CameraSession,
33
+ CameraSessionCapabilities,
34
+ CameraSessionConfig,
35
+ CameraSessionEvent,
36
+ CameraSessionSnapshot,
37
+ CaptureIssue,
38
+ EffectiveCaptureConfig,
39
+ MovieClip,
40
+ MovieEndReason,
41
+ MovieOutcome,
42
+ MovieOutput,
43
+ MovieOutputHandle,
44
+ MovieTake,
45
+ StartRecordingOptions,
46
+ } from './types.js'
47
+
48
+ export type { SyntheticCameraScript, SyntheticCameraStorage } from './synthetic-session.js'
package/types/props.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Octane } from 'octane/jsx-runtime';
2
+ import type { CameraSession } from './types';
2
3
  export type CameraAccessibilityRole = 'button' | 'link' | 'search' | 'image' | 'heading' | 'adjustable' | 'summary' | 'text' | 'none' | 'progressbar' | 'checkbox' | 'switch' | 'radio' | 'spinbutton' | 'tab';
3
4
  export interface CameraViewHandle {
4
5
  /** The native preview surface (`HTMLVideoElement` on web). */
@@ -30,7 +31,11 @@ export interface CameraViewProps {
30
31
  checked?: boolean;
31
32
  };
32
33
  accessibilityLiveRegion?: 'none' | 'polite' | 'assertive';
33
- /** Lens — defaults to the rear camera. */
34
+ /** Shared camera owner enabling movie recording. When set, the preview
35
+ * attaches to the session's acquisition instead of owning one, and
36
+ * `facing` must be omitted — camera selection lives on the session. */
37
+ session?: CameraSession;
38
+ /** Lens — defaults to the rear camera. Invalid together with `session`. */
34
39
  facing?: 'front' | 'back';
35
40
  /** Default true. False releases the preview session. */
36
41
  active?: boolean;
@@ -45,4 +50,5 @@ export interface CameraViewProps {
45
50
  ios?: Record<string, any>;
46
51
  android?: Record<string, any>;
47
52
  web?: Record<string, any>;
53
+ windows?: Record<string, any>;
48
54
  }
@@ -0,0 +1,129 @@
1
+ import type { CameraInterruptionReason, CameraPermissionKind, CameraPermissionStatus, CameraSession, CameraSessionCapabilities, CameraSessionConfig, CameraSessionEvent, CameraSessionSnapshot, CaptureIssue, EffectiveCaptureConfig, MovieEndReason, MovieOutput, MovieOutputHandle, MovieTake, StartRecordingOptions } from './types';
2
+ /** Result an adapter reports when a capture attempt settles. Media
3
+ * metadata is already read from the finalized movie by the adapter. */
4
+ export type BackendSettle = {
5
+ kind: 'clip';
6
+ output: MovieOutput;
7
+ mimeType: string;
8
+ durationMs: number;
9
+ width: number;
10
+ height: number;
11
+ orientation: EffectiveCaptureConfig['orientation'];
12
+ hasAudio: boolean;
13
+ /** Native duration-limit enforcement ended the take. */
14
+ limitReached?: boolean;
15
+ /** Adapter-driven endings when no earlier app/lifecycle cause exists. */
16
+ endReason?: MovieEndReason;
17
+ warning?: CaptureIssue;
18
+ } | {
19
+ kind: 'failed';
20
+ stage: 'preparation' | 'capture' | 'finalization' | 'storage';
21
+ issue: CaptureIssue;
22
+ };
23
+ /** Per-attempt callbacks handed to `startCapture`. */
24
+ export interface BackendCaptureSink {
25
+ /** Native capture actually began; exactly once per settled attempt. */
26
+ started(config: EffectiveCaptureConfig): void;
27
+ /** Capture has ended; metadata verification and storage still own the attempt. */
28
+ finishing(reason?: MovieEndReason): void;
29
+ /** Terminal result; exactly once per admitted attempt. */
30
+ settled(result: BackendSettle): void;
31
+ }
32
+ export interface BackendCaptureRequest {
33
+ audio: boolean;
34
+ orientation: EffectiveCaptureConfig['orientation'] | undefined;
35
+ maximumDurationMs: number | undefined;
36
+ /** Resolved writable path for native adapters. */
37
+ destinationPath: string | undefined;
38
+ }
39
+ /** Adapter-owned preview attach. `listener` reports preview lifecycle;
40
+ * returns the detach function. */
41
+ export interface BackendPreviewListener {
42
+ onReady?: () => void;
43
+ onError?: (issue: CaptureIssue) => void;
44
+ }
45
+ /** Platform adapter surface driven by the shared session core. Adapters
46
+ * own permission policy, capability discovery, capture, and local output
47
+ * access; the core owns states, ordering, causes, and outcomes. */
48
+ export interface CameraSessionBackend {
49
+ readonly platform: string;
50
+ /** False when the adapter cannot capture on this host; the core
51
+ * rejects `startRecording` with `unsupportedPlatform`. */
52
+ readonly supported: boolean;
53
+ checkPermission(kind: CameraPermissionKind): Promise<CameraPermissionStatus>;
54
+ requestPermission(kind: CameraPermissionKind): Promise<CameraPermissionStatus>;
55
+ getCapabilities(): Promise<CameraSessionCapabilities>;
56
+ /** An active, ready preview is attached and the camera source is
57
+ * running — the admission requirement for `startRecording`. */
58
+ previewReady(): boolean;
59
+ /** Attach the shared-source preview to a platform view. */
60
+ attachPreview(host: unknown, listener?: BackendPreviewListener): () => void;
61
+ /** Replace configuration while idle; throws CameraCaptureError. */
62
+ configure?(config: CameraSessionConfig): void;
63
+ /** Synchronous platform-specific admission checks; creates no attempt on failure. */
64
+ validateStart?(options: StartRecordingOptions): void;
65
+ /** Begin capture for an admitted attempt. Async failures settle via
66
+ * `sink.settled`; a synchronous throw settles as a preparation failure. */
67
+ startCapture(request: BackendCaptureRequest, sink: BackendCaptureSink): void;
68
+ /** Request capture stop; settlement still arrives via the sink. */
69
+ stopCapture(): void;
70
+ /** Elapsed capture time in ms for the in-flight attempt. */
71
+ elapsedMs(): number;
72
+ openOutput(output: MovieOutput): Promise<MovieOutputHandle>;
73
+ /** Release camera ownership. The core calls this only after any
74
+ * in-flight attempt has settled. */
75
+ release(): Promise<void>;
76
+ /** Reattach/reconfigure bookkeeping when the preview view is detached
77
+ * while an attempt is settling — the adapter keeps hardware until
78
+ * settlement, then stops. */
79
+ onPreviewDetached?(): void;
80
+ /** Lifecycle observations the adapter emits at any time. */
81
+ setObserver(observer: BackendObserver | undefined): void;
82
+ }
83
+ export interface BackendObserver {
84
+ interruption(phase: 'began' | 'ended', reason?: CameraInterruptionReason): void;
85
+ availability(available: boolean): void;
86
+ /** Preview became ready/unready (session running state changed). */
87
+ previewReady(ready: boolean): void;
88
+ }
89
+ /** The concrete session object wrapping a platform backend. Internal to
90
+ * the package; `CameraSession` is the public shape. */
91
+ export declare class SharedCameraSession implements CameraSession {
92
+ private backend;
93
+ private config;
94
+ private listeners;
95
+ private state;
96
+ private permission;
97
+ private cameraAvailable;
98
+ private previewAttached;
99
+ private interrupted;
100
+ private disposeRequested;
101
+ private disposed;
102
+ private lastCapabilities;
103
+ private requestChains;
104
+ private attempt;
105
+ constructor(backend: CameraSessionBackend, config: CameraSessionConfig);
106
+ /** Package-internal: attach a preview host to this session. */
107
+ attach(host: unknown, listener?: BackendPreviewListener): () => void;
108
+ get attached(): boolean;
109
+ private detachInternal;
110
+ private emit;
111
+ private setState;
112
+ snapshot(): CameraSessionSnapshot;
113
+ subscribe(listener: (event: CameraSessionEvent) => void): () => void;
114
+ permissions(): Promise<{
115
+ camera: CameraPermissionStatus;
116
+ microphone: CameraPermissionStatus;
117
+ }>;
118
+ requestPermission(kind: CameraPermissionKind): Promise<CameraPermissionStatus>;
119
+ capabilities(): Promise<CameraSessionCapabilities>;
120
+ configure(next: Partial<CameraSessionConfig>): Promise<void>;
121
+ private validateCameraSelection;
122
+ private requestStop;
123
+ private handleInterruption;
124
+ private handleAvailability;
125
+ startRecording(options?: StartRecordingOptions): MovieTake;
126
+ openOutput(output: MovieOutput): Promise<MovieOutputHandle>;
127
+ dispose(): Promise<void>;
128
+ private finishDispose;
129
+ }
@@ -0,0 +1,48 @@
1
+ import type { CameraInterruptionReason, CameraPermissionStatus, CameraSession, CameraSessionConfig } from './types';
2
+ /** Development/testing seam — a deterministic camera source that drives
3
+ * the shared session state machine without hardware. NOT a production
4
+ * recording source: outcomes carry `synthetic: true` on their output so
5
+ * seam clips stay distinguishable from real capture.
6
+ *
7
+ * `script` is read live at each transition — mutate fields between
8
+ * operations to script permissions, delayed starts, interruptions,
9
+ * partial completions, and failures.
10
+ */
11
+ export interface SyntheticCameraScript {
12
+ permission: {
13
+ camera: CameraPermissionStatus;
14
+ microphone: CameraPermissionStatus;
15
+ };
16
+ /** Camera hardware availability observation. */
17
+ available: boolean;
18
+ /** Delay before `started` reports; models slow native activation. */
19
+ startDelayMs: number;
20
+ /** Finalized clip duration reported by the seam. */
21
+ clipDurationMs: number;
22
+ /** Produce a usable clip on stop; false settles `noMedia`. */
23
+ produceMedia: boolean;
24
+ /** When set, the attempt settles failed at this stage. */
25
+ failAt?: 'start' | 'capture' | 'finalize' | 'storage';
26
+ /** Emit an interruption after this many ms of recording; the take
27
+ * ends with the mapped end reason and a partial clip. */
28
+ interruption?: {
29
+ afterMs: number;
30
+ reason: CameraInterruptionReason;
31
+ endsAfterMs?: number;
32
+ };
33
+ }
34
+ export interface SyntheticCameraStorage {
35
+ write(path: string, bytes: Uint8Array): void | Promise<void>;
36
+ read(path: string): Uint8Array | undefined | Promise<Uint8Array | undefined>;
37
+ delete?(path: string): void | Promise<void>;
38
+ exists?(path: string): boolean | Promise<boolean>;
39
+ }
40
+ /** In-memory storage for the seam — a `fileUrl`-shaped map so outcomes
41
+ * exercise the same reopen path without touching a real filesystem. */
42
+ export declare function createMemoryCameraStorage(): SyntheticCameraStorage & {
43
+ files: Map<string, Uint8Array>;
44
+ };
45
+ /** Create a `CameraSession` backed by the deterministic synthetic source.
46
+ * Marked for development and testing — never present this as live
47
+ * capture to users. */
48
+ export declare function createSyntheticCameraSession(config?: CameraSessionConfig, script?: Partial<SyntheticCameraScript>, storage?: SyntheticCameraStorage): CameraSession;
@@ -0,0 +1,263 @@
1
+ /** Shared camera-session and movie-recording contract.
2
+ *
3
+ * A `CameraSession` owns one camera acquisition shared between a
4
+ * `CameraView` preview and at most one movie-recording attempt. Recording
5
+ * settles with an immutable `MovieOutcome` only after the local movie is
6
+ * finalized and its metadata is known — a returned clip is always
7
+ * reopenable through `openOutput`.
8
+ *
9
+ * This module is platform-pure: adapters implement
10
+ * `CameraSessionBackend` per host. Adapters that do not support capture
11
+ * yet report `supported: false` and reject `startRecording` with
12
+ * `unsupportedPlatform` instead of fabricating behavior.
13
+ */
14
+ /** Permission resource a `CameraSession` can inspect or request. */
15
+ export type CameraPermissionKind = 'camera' | 'microphone';
16
+ /** Portable authorization state. Checking never prompts. `blocked` and
17
+ * `unknown` exist so hosts that cannot distinguish user denial from
18
+ * policy refusal do not fabricate `restricted`. */
19
+ export type CameraPermissionStatus = 'notDetermined' | 'granted' | 'denied' | 'restricted' | 'blocked' | 'unknown' | 'unavailable';
20
+ /** Portable error categories. `unsupported*` covers missing host features,
21
+ * devices, or capture configurations — including adapters that are not
22
+ * implemented yet during staged delivery. */
23
+ export type CameraCaptureErrorKind = 'permissionDenied' | 'permissionRestricted' | 'permissionBlocked' | 'unsupportedPlatform' | 'unsupportedConfiguration' | 'unavailable' | 'busy' | 'invalidState' | 'invalidArgument' | 'destinationUnavailable' | 'insufficientStorage' | 'noMedia' | 'captureFailed' | 'finalizationFailed' | 'storageFailed' | 'configurationMissing';
24
+ /** Structured diagnostic carried by failures and non-fatal warnings. */
25
+ export interface CaptureIssue {
26
+ kind: CameraCaptureErrorKind;
27
+ /** Operation being attempted, e.g. 'startRecording', 'finalize'. */
28
+ operation: string;
29
+ message: string;
30
+ permission?: CameraPermissionKind;
31
+ /** Host-specific detail for logging; never required for control flow. */
32
+ cause?: unknown;
33
+ }
34
+ /** Error thrown or settled by the session API. Satisfies `CaptureIssue`
35
+ * so `error.kind` drives control flow without unwrapping. */
36
+ export declare class CameraCaptureError extends Error implements CaptureIssue {
37
+ kind: CameraCaptureErrorKind;
38
+ operation: string;
39
+ permission?: CameraPermissionKind;
40
+ cause?: unknown;
41
+ constructor(issue: Omit<CaptureIssue, 'cause'> & {
42
+ cause?: unknown;
43
+ });
44
+ }
45
+ export type CameraFacing = 'front' | 'back';
46
+ export type CameraOrientation = 'portrait' | 'portraitUpsideDown' | 'landscapeLeft' | 'landscapeRight' | 'unspecified';
47
+ /** Camera selection for `CameraSessionConfig.camera`. Desktop and browser
48
+ * hosts expose device identities instead of front/back semantics. */
49
+ export type CameraSelection = 'default' | 'front' | 'back' | {
50
+ deviceId: string;
51
+ };
52
+ export interface CameraSessionConfig {
53
+ /** Lens or device identity. Default `'default'`: rear when available. */
54
+ camera?: CameraSelection;
55
+ /** Include an audio track; requires microphone permission. Default
56
+ * false — silent capture is a deliberate app choice, never a
57
+ * fallback from denied microphone access. */
58
+ audio?: boolean;
59
+ /** Capture profile. `'standard'` targets 720p at 30 fps and may select
60
+ * a device-supported alternative; the started attempt discloses the
61
+ * actual configuration. */
62
+ profile?: 'standard';
63
+ }
64
+ /** Resolution/frame-rate combination advertised by capability discovery.
65
+ * Entries are supported combinations, not independent lists. */
66
+ export interface CameraProfileCapability {
67
+ profile: 'standard';
68
+ width: number;
69
+ height: number;
70
+ frameRate: number;
71
+ }
72
+ export interface CameraDeviceInfo {
73
+ id: string;
74
+ label: string;
75
+ facing?: CameraFacing;
76
+ }
77
+ /** What the session's host can do right now. `supported: false` means the
78
+ * adapter or host cannot record (reason explains); `available` reflects
79
+ * current hardware/permission state and changes over time. */
80
+ export interface CameraSessionCapabilities {
81
+ supported: boolean;
82
+ /** Why capture is unsupported or currently unavailable. */
83
+ reason?: string;
84
+ available: boolean;
85
+ cameras: CameraDeviceInfo[];
86
+ profiles: CameraProfileCapability[];
87
+ /** The session can add an audio track when permission allows. */
88
+ audio: boolean;
89
+ /** Fixed orientations the source can lock for a take. */
90
+ orientations: CameraOrientation[];
91
+ /** `native` enforcement can still overshoot at media boundaries;
92
+ * `bestEffort` limits are scheduled and may overshoot more. */
93
+ durationLimit: 'native' | 'bestEffort';
94
+ /** Where `elapsedMs` comes from while recording. */
95
+ elapsedTime: 'nativeMedia' | 'estimated';
96
+ output: {
97
+ mimeType: string;
98
+ container: string;
99
+ /** `appPrivateFile` output survives dispose and app restart;
100
+ * `originLocal` is browser storage subject to site-data clearing. */
101
+ storage: 'appPrivateFile' | 'originLocal';
102
+ /** Accepts an app-supplied `destinationFileUrl` on start. */
103
+ destinationFileUrl: boolean;
104
+ };
105
+ }
106
+ export type CameraRecorderState = 'idle' | 'starting' | 'recording' | 'finalizing' | 'disposed';
107
+ /** Frozen capture configuration reported once capture actually begins. */
108
+ export interface EffectiveCaptureConfig {
109
+ cameraId: string;
110
+ facing?: CameraFacing;
111
+ audio: boolean;
112
+ width: number;
113
+ height: number;
114
+ frameRate: number;
115
+ orientation: CameraOrientation;
116
+ mimeType: string;
117
+ container: string;
118
+ }
119
+ export interface CameraSessionSnapshot {
120
+ state: CameraRecorderState;
121
+ /** Non-prompting last-known authorization, refreshed by `permissions()`
122
+ * and `requestPermission()`. */
123
+ permission: Record<CameraPermissionKind, CameraPermissionStatus>;
124
+ /** Camera hardware/driver availability, separate from recorder state. */
125
+ cameraAvailable: boolean;
126
+ /** An active preview view is attached to this session. */
127
+ previewAttached: boolean;
128
+ interrupted: boolean;
129
+ attempt?: {
130
+ id: string;
131
+ state: 'starting' | 'recording' | 'finalizing';
132
+ elapsedMs: number;
133
+ configuration?: EffectiveCaptureConfig;
134
+ };
135
+ }
136
+ /** What ended a capture attempt. `interrupted` covers system suspension
137
+ * and preview teardown; `previewReleased` distinguishes an app-driven
138
+ * deactivation from an OS interruption. */
139
+ export type MovieEndReason = 'stopped' | 'maximumDuration' | 'interrupted' | 'backgrounded' | 'cameraUnavailable' | 'previewReleased' | 'disposed' | 'captureError';
140
+ /** Portable completed-output reference. `resourceId` reopens the clip
141
+ * through `openOutput`; `fileUrl` additionally guarantees file-backed
142
+ * native output. `synthetic` marks test-seam clips. */
143
+ export type MovieOutput = {
144
+ kind: 'nativeFile';
145
+ resourceId: string;
146
+ fileUrl: string;
147
+ synthetic?: boolean;
148
+ } | {
149
+ kind: 'browserStorage';
150
+ resourceId: string;
151
+ retention: 'persistent' | 'bestEffort';
152
+ synthetic?: boolean;
153
+ };
154
+ export interface MovieClip {
155
+ output: MovieOutput;
156
+ mimeType: string;
157
+ /** Authoritative duration read from the finalized movie. */
158
+ durationMs: number;
159
+ /** Presentation dimensions after the orientation transform. */
160
+ width: number;
161
+ height: number;
162
+ orientation: CameraOrientation;
163
+ /** Whether the finalized movie actually contains an audio track.
164
+ * Missing requested audio is a failure, not silent capture. */
165
+ hasAudio: boolean;
166
+ }
167
+ /** One immutable terminal result per admitted attempt. `partial` marks a
168
+ * usable clip ended early by an external condition — still a success;
169
+ * app review decides whether to keep it. */
170
+ export type MovieOutcome = {
171
+ kind: 'clip';
172
+ clip: MovieClip;
173
+ endReason: MovieEndReason;
174
+ partial: boolean;
175
+ warning?: CaptureIssue;
176
+ } | {
177
+ kind: 'failed';
178
+ endReason: MovieEndReason;
179
+ stage: 'preparation' | 'capture' | 'finalization' | 'storage';
180
+ error: CaptureIssue;
181
+ };
182
+ /** Ordered lifecycle observation, correlated to an attempt when relevant. */
183
+ export type CameraSessionEvent = {
184
+ type: 'stateChanged';
185
+ snapshot: CameraSessionSnapshot;
186
+ } | {
187
+ type: 'started';
188
+ attemptId: string;
189
+ configuration: EffectiveCaptureConfig;
190
+ } | {
191
+ type: 'finished';
192
+ attemptId: string;
193
+ outcome: MovieOutcome;
194
+ } | {
195
+ type: 'interruption';
196
+ phase: 'began' | 'ended';
197
+ reason?: CameraInterruptionReason;
198
+ } | {
199
+ type: 'availability';
200
+ available: boolean;
201
+ };
202
+ export type CameraInterruptionReason = 'background' | 'audioInUse' | 'cameraInUse' | 'multitasking' | 'systemPressure' | 'unknown';
203
+ export interface StartRecordingOptions {
204
+ /** Positive finite milliseconds. Enforcement precision comes from
205
+ * `capabilities().durationLimit`; the final duration is read from
206
+ * the movie and may overshoot. */
207
+ maximumDurationMs?: number;
208
+ /** Fixed orientation for the take. Must appear in
209
+ * `capabilities().orientations`; omit for the source default. */
210
+ orientation?: CameraOrientation;
211
+ /** App-supplied writable local `file://` URL (native hosts only,
212
+ * where `capabilities().output.destinationFileUrl` is true).
213
+ * Existing files are never overwritten. */
214
+ destinationFileUrl?: string;
215
+ }
216
+ /** One admitted capture attempt. `completion` is available immediately and
217
+ * settles exactly once with the terminal outcome; repeated `stop()`
218
+ * joins the same finalization. */
219
+ export interface MovieTake {
220
+ readonly id: string;
221
+ readonly state: 'starting' | 'recording' | 'finalizing' | 'settled';
222
+ /** Elapsed capture time in milliseconds; excludes preparation and
223
+ * finalization. Source identified by `capabilities().elapsedTime`. */
224
+ readonly elapsedMs: number;
225
+ readonly completion: Promise<MovieOutcome>;
226
+ /** Requests stop and joins finalization. Returns `completion`. */
227
+ stop(): Promise<MovieOutcome>;
228
+ }
229
+ /** Reopened access to a stored clip. `release()` frees temporary access;
230
+ * it never deletes the clip. */
231
+ export interface MovieOutputHandle {
232
+ /** Playable/readable location for the clip. */
233
+ readonly url: string;
234
+ readonly fileUrl?: string;
235
+ release(): void;
236
+ }
237
+ export interface CameraSession {
238
+ /** Non-prompting permission inspection; may return `unknown` when the
239
+ * host cannot observe authorization. */
240
+ permissions(): Promise<Record<CameraPermissionKind, CameraPermissionStatus>>;
241
+ /** Runs the system authorization flow for one kind. Calls are
242
+ * serialized; a status other than `notDetermined` returns without
243
+ * prompting. */
244
+ requestPermission(kind: CameraPermissionKind): Promise<CameraPermissionStatus>;
245
+ capabilities(): Promise<CameraSessionCapabilities>;
246
+ snapshot(): CameraSessionSnapshot;
247
+ /** Ordered lifecycle events. A caller never loses a result by
248
+ * subscribing late — `completion` is authoritative. Listener
249
+ * exceptions do not change capture. */
250
+ subscribe(listener: (event: CameraSessionEvent) => void): () => void;
251
+ /** Replaces configuration while idle. Validates against current
252
+ * capabilities and rejects with `invalidState` during an attempt. */
253
+ configure(config: Partial<CameraSessionConfig>): Promise<void>;
254
+ /** Admits one capture attempt. Throws `CameraCaptureError` before
255
+ * admission for invalid arguments, busy, invalid state, or
256
+ * unavailable camera — no attempt is created. */
257
+ startRecording(options?: StartRecordingOptions): MovieTake;
258
+ /** Reopens a stored output for playback or reading. */
259
+ openOutput(output: MovieOutput): Promise<MovieOutputHandle>;
260
+ /** Requests stop on any active attempt, awaits finalization, then
261
+ * releases camera ownership. */
262
+ dispose(): Promise<void>;
263
+ }
@@ -1,5 +0,0 @@
1
- import type { UniversalComponent } from 'octane/universal'
2
- import type { CameraViewProps } from './props'
3
-
4
- /** TypeScript fallback — no Windows CameraView implementation exists yet. */
5
- export declare const CameraView: UniversalComponent<CameraViewProps>