@ai-matrx/capture 0.0.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 ADDED
@@ -0,0 +1,24 @@
1
+ # Changelog — @ai-matrx/capture
2
+
3
+ ## 0.1.0 — 2026-08-29
4
+
5
+ Initial extraction from matrx-frontend `features/capture-camera/` (the iPhone-style rebuild of the commerce intake camera, generalized):
6
+
7
+ - `CameraCapture` chrome (translucent bars, shutter, VIDEO·PHOTO·UPLOAD mode row, recents thumb, flip, honesty chips).
8
+ - Two-tap `OptionsGridPanel` (Flash/Timer/Grid/Aspect/Exposure + injected tiles).
9
+ - `ImageEditSheet` (crop/rotate/flip, canvas re-encode).
10
+ - `CaptureSheet` iOS-style system sheet (content + busy variants).
11
+ - `useTrackControls` (honest torch/zoom/exposure from track capabilities).
12
+ - THE CLOUD LAW: required `CaptureCloudPort`; laws test bans network, gUM, storage, and an optional cloud port.
13
+
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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AI Matrix Engine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,42 @@
1
1
  # @ai-matrx/capture
2
2
 
3
- Reserved for the AI Matrx opinionated camera and media-capture package. This `0.0.0` release reserves the canonical package name; the implementation is being built separately.
3
+ The AI Matrx capture kit the opinionated, iPhone-style camera experience as a reusable React package.
4
+
5
+ **THE CLOUD LAW:** cloud integration is WHAT this system does, not an add-on. `CameraCapture` requires a `CaptureCloudPort` (recents thumb, library opener, edited-image persistence). The host decides HOW the cloud is reached — never WHETHER.
6
+
7
+ ## What's inside
8
+
9
+ - `CameraCapture` — the assembled chrome: full-bleed feed under semi-transparent near-black bars; top bar (close · center slot · torch · extras · options); real zoom pills (track capabilities only — nothing faked); VIDEO · PHOTO · UPLOAD mode row (upload is first-class); recents thumb · shutter · flip; honesty chips (recording clock survives hidden chrome).
10
+ - `OptionsGridPanel` — the two-tap options surface (reveal, then act): Flash (torch), Timer (0/3/10s), Grid, Aspect (full/4:3/1:1/16:9), Exposure (when the hardware reports a range), plus host-injected tiles.
11
+ - `ImageEditSheet` — instant in-browser editing: crop (free/1:1/4:3/16:9), rotate, flip; canvas re-encode of the original pixels.
12
+ - `CaptureSheet` — the iOS-style system sheet (content + "busy" transient variant).
13
+ - `useTrackControls` — honest torch/zoom/exposure over `MediaTrackCapabilities`.
14
+ - Slot-based extensibility (`CaptureCameraSlots`) — typed slots for domain add-ons (status chips, option tiles, extra modes such as SCAN, rows above the mode selector, overlays). No plugin framework.
15
+
16
+ ## What's NOT inside (by design)
17
+
18
+ - **No getUserMedia** — the host injects a `CaptureCameraEngine` (stream, capture/record callbacks). In AI Matrx that host is the `features/media-capture` runtime (lease manager, recorder, chunk journal); those move here in a later major once extracted.
19
+ - **No network, no storage** — enforced by `src/laws.test.ts`.
20
+ - Persisted-media rendering (thumbnails, lightboxes, durable refs) — that's `@ai-matrx/media`.
21
+ - Audio-only recording UX — `@ai-matrx/browser-audio`.
22
+ - QR decoding — `@ai-matrx/kit/qr`.
23
+
24
+ ## Usage sketch
25
+
26
+ ```tsx
27
+ import { CameraCapture } from "@ai-matrx/capture/react";
28
+ import type { CaptureCameraEngine, CaptureCloudPort } from "@ai-matrx/capture";
29
+
30
+ <CameraCapture
31
+ engine={engine} // host camera runtime adapter
32
+ cloud={cloud} // REQUIRED — recents thumb, library, save-edited
33
+ mode={mode}
34
+ onModeChange={setMode}
35
+ preview={<YourLivePreview />}
36
+ onClose={close}
37
+ blockedSheet={{ body: <p>…</p>, actions: [...] }}
38
+ slots={{ optionTiles: [...], statusChips: <...>, extraModes: [{ id: "scan", label: "Scan", onSelect: openScanner }] }}
39
+ />
40
+ ```
41
+
42
+ The reference host integration is matrx-frontend `features/capture-camera/` (staging source) with the commerce intake surface at `/commerce/intake/v2` as the worked domain-extension example.
package/dist/index.cjs ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/index.ts
17
+ var src_exports = {};
18
+ module.exports = __toCommonJS(src_exports);
19
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @ai-matrx/capture — root entry: TYPES ONLY, RSC-safe (no \"use client\").\n *\n * THE CLOUD LAW: cloud integration is WHAT this system does, not an option.\n * `CameraCapture` (from `@ai-matrx/capture/react`) requires a\n * `CaptureCloudPort`; the host injects HOW it is fulfilled, never WHETHER.\n */\n\nexport type {\n CaptureAspect,\n CaptureCameraEngine,\n CaptureCameraMode,\n CaptureCameraSlots,\n CaptureCloudPort,\n CaptureOptionTile,\n CaptureTimerSetting,\n} from \"./types\";\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -0,0 +1,119 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * features/capture-camera/ — EXTRACTION SOURCE for the `@ai-matrx/capture`
5
+ * package (aidream/apps/shared/capture). Everything outside `host/` must stay
6
+ * free of app imports beyond React, lucide-react and `cn` — those are the
7
+ * documented substitution points when this directory is mirrored into the
8
+ * package (icons → inlined SVGs, cn → tailwind-merge), exactly like
9
+ * `@ai-matrx/media` was extracted from `features/files`.
10
+ *
11
+ * The package is the OPINIONATED iPhone-style camera chrome: translucent
12
+ * top/bottom bars over a full-bleed feed, the two-tap options grid, zoom
13
+ * pills, shutter, VIDEO·PHOTO·UPLOAD mode selector, recents thumb, flip
14
+ * button, rule-of-thirds grid, countdown timer, and the iOS-style sheet.
15
+ * It renders and orchestrates UI state ONLY — the host injects the engine
16
+ * (stream + capture callbacks) and everything persisted (upload, gallery,
17
+ * thumbnails). No fetch, no storage, no getUserMedia in this layer.
18
+ */
19
+
20
+ /** The two persistent capture modes; Upload is an immediate action. */
21
+ type CaptureCameraMode = "photo" | "video";
22
+ /** Timer options genuinely supported (a countdown before the shutter). */
23
+ type CaptureTimerSetting = 0 | 3 | 10;
24
+ /** Output aspect for photo capture (center-cropped from the full sensor). */
25
+ type CaptureAspect = "full" | "4:3" | "1:1" | "16:9";
26
+ /**
27
+ * THE CLOUD LAW of this package: cloud integration is WHAT the system does,
28
+ * not an optional add-on. Every `CameraCapture` REQUIRES this port — the
29
+ * type system refuses a camera with no cloud. How the host fulfills it
30
+ * (fileHandler, a client SDK, a domain uploader) is injected; that it is
31
+ * fulfilled is not negotiable.
32
+ */
33
+ interface CaptureCloudPort {
34
+ /** Content of the bottom-left recents thumbnail (latest cloud/session
35
+ * media). Null renders the placeholder — the button still opens the
36
+ * library. */
37
+ recentsThumb: React.ReactNode;
38
+ /** Opens the host's cloud media library (tiled gallery). */
39
+ onOpenLibrary: () => void;
40
+ /** Persists an edited image produced by the edit sheet. */
41
+ onSaveEdited: (blob: Blob, suggestedName: string) => void;
42
+ }
43
+ /**
44
+ * The engine port — everything the chrome needs from the host's camera
45
+ * runtime. The host owns lease acquisition/release, capture and recording;
46
+ * the chrome never touches getUserMedia.
47
+ */
48
+ interface CaptureCameraEngine {
49
+ /** Live stream for the preview, or null while connecting/blocked. */
50
+ stream: MediaStream | null;
51
+ /** The preview <video> element ref the host's capture path reads from. */
52
+ videoRef: React.RefObject<HTMLVideoElement | null>;
53
+ /** Camera unavailable: permission denied or unsupported. */
54
+ blocked: null | {
55
+ reason: "permission-denied" | "not-supported";
56
+ };
57
+ /** Take a photo NOW (any timer countdown already elapsed). The chrome's
58
+ * current aspect setting rides along; the host center-crops the full
59
+ * sensor frame to it ("full" = untouched). */
60
+ onCapturePhoto: (opts?: {
61
+ aspect?: CaptureAspect;
62
+ }) => void;
63
+ /** Start / stop video recording. */
64
+ onStartRecording: () => void;
65
+ onStopRecording: () => void;
66
+ recording: boolean;
67
+ /** Elapsed recording seconds (host-owned monotonic clock). */
68
+ recordElapsedSeconds: number;
69
+ /** Open the device files picker (the Upload lane). */
70
+ onUpload: () => void;
71
+ /** Flip to the next camera; null hides the flip button. */
72
+ onFlipCamera: (() => void) | null;
73
+ }
74
+ /** One tile in the two-tap options grid. */
75
+ interface CaptureOptionTile {
76
+ id: string;
77
+ label: string;
78
+ icon: React.ReactNode;
79
+ /** Highlight state (iPhone: yellow when engaged/auto). */
80
+ active?: boolean;
81
+ /** Small value read-out under the icon (e.g. "3s", "4:3"). */
82
+ valueLabel?: string;
83
+ disabled?: boolean;
84
+ onPress: () => void;
85
+ }
86
+ /**
87
+ * Slot-based extensibility — how domain layers (e.g. commerce intake) attach
88
+ * their own affordances without forking the chrome. Typed slots, not a plugin
89
+ * registry, on purpose.
90
+ */
91
+ interface CaptureCameraSlots {
92
+ /** Extra buttons in the top bar, before the options-grid button. */
93
+ topBarTrailing?: React.ReactNode;
94
+ /** Center of the top bar (e.g. current-item label + count). */
95
+ topBarCenter?: React.ReactNode;
96
+ /** Honesty chips under the top bar (QR confirmation, etc.). */
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;
101
+ /** Rows rendered inside the bottom bar ABOVE the mode selector
102
+ * (keep to ONE compact row — bar height is stolen from the feed). */
103
+ aboveModeSelector?: React.ReactNode;
104
+ /** A compact action pinned right of the mode selector (Next/Break). */
105
+ modeRowTrailing?: React.ReactNode;
106
+ /** Extra tiles appended to the options grid. */
107
+ optionTiles?: CaptureOptionTile[];
108
+ /** Extra entries in the mode row after UPLOAD (e.g. SCAN). Selecting one
109
+ * is an immediate host action — the chrome keeps its current mode. */
110
+ extraModes?: {
111
+ id: string;
112
+ label: string;
113
+ onSelect: () => void;
114
+ }[];
115
+ /** Free overlays rendered above everything (sheets, pagers). */
116
+ overlays?: React.ReactNode;
117
+ }
118
+
119
+ export type { CaptureAspect, CaptureCameraEngine, CaptureCameraMode, CaptureCameraSlots, CaptureCloudPort, CaptureOptionTile, CaptureTimerSetting };
@@ -0,0 +1,119 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * features/capture-camera/ — EXTRACTION SOURCE for the `@ai-matrx/capture`
5
+ * package (aidream/apps/shared/capture). Everything outside `host/` must stay
6
+ * free of app imports beyond React, lucide-react and `cn` — those are the
7
+ * documented substitution points when this directory is mirrored into the
8
+ * package (icons → inlined SVGs, cn → tailwind-merge), exactly like
9
+ * `@ai-matrx/media` was extracted from `features/files`.
10
+ *
11
+ * The package is the OPINIONATED iPhone-style camera chrome: translucent
12
+ * top/bottom bars over a full-bleed feed, the two-tap options grid, zoom
13
+ * pills, shutter, VIDEO·PHOTO·UPLOAD mode selector, recents thumb, flip
14
+ * button, rule-of-thirds grid, countdown timer, and the iOS-style sheet.
15
+ * It renders and orchestrates UI state ONLY — the host injects the engine
16
+ * (stream + capture callbacks) and everything persisted (upload, gallery,
17
+ * thumbnails). No fetch, no storage, no getUserMedia in this layer.
18
+ */
19
+
20
+ /** The two persistent capture modes; Upload is an immediate action. */
21
+ type CaptureCameraMode = "photo" | "video";
22
+ /** Timer options genuinely supported (a countdown before the shutter). */
23
+ type CaptureTimerSetting = 0 | 3 | 10;
24
+ /** Output aspect for photo capture (center-cropped from the full sensor). */
25
+ type CaptureAspect = "full" | "4:3" | "1:1" | "16:9";
26
+ /**
27
+ * THE CLOUD LAW of this package: cloud integration is WHAT the system does,
28
+ * not an optional add-on. Every `CameraCapture` REQUIRES this port — the
29
+ * type system refuses a camera with no cloud. How the host fulfills it
30
+ * (fileHandler, a client SDK, a domain uploader) is injected; that it is
31
+ * fulfilled is not negotiable.
32
+ */
33
+ interface CaptureCloudPort {
34
+ /** Content of the bottom-left recents thumbnail (latest cloud/session
35
+ * media). Null renders the placeholder — the button still opens the
36
+ * library. */
37
+ recentsThumb: React.ReactNode;
38
+ /** Opens the host's cloud media library (tiled gallery). */
39
+ onOpenLibrary: () => void;
40
+ /** Persists an edited image produced by the edit sheet. */
41
+ onSaveEdited: (blob: Blob, suggestedName: string) => void;
42
+ }
43
+ /**
44
+ * The engine port — everything the chrome needs from the host's camera
45
+ * runtime. The host owns lease acquisition/release, capture and recording;
46
+ * the chrome never touches getUserMedia.
47
+ */
48
+ interface CaptureCameraEngine {
49
+ /** Live stream for the preview, or null while connecting/blocked. */
50
+ stream: MediaStream | null;
51
+ /** The preview <video> element ref the host's capture path reads from. */
52
+ videoRef: React.RefObject<HTMLVideoElement | null>;
53
+ /** Camera unavailable: permission denied or unsupported. */
54
+ blocked: null | {
55
+ reason: "permission-denied" | "not-supported";
56
+ };
57
+ /** Take a photo NOW (any timer countdown already elapsed). The chrome's
58
+ * current aspect setting rides along; the host center-crops the full
59
+ * sensor frame to it ("full" = untouched). */
60
+ onCapturePhoto: (opts?: {
61
+ aspect?: CaptureAspect;
62
+ }) => void;
63
+ /** Start / stop video recording. */
64
+ onStartRecording: () => void;
65
+ onStopRecording: () => void;
66
+ recording: boolean;
67
+ /** Elapsed recording seconds (host-owned monotonic clock). */
68
+ recordElapsedSeconds: number;
69
+ /** Open the device files picker (the Upload lane). */
70
+ onUpload: () => void;
71
+ /** Flip to the next camera; null hides the flip button. */
72
+ onFlipCamera: (() => void) | null;
73
+ }
74
+ /** One tile in the two-tap options grid. */
75
+ interface CaptureOptionTile {
76
+ id: string;
77
+ label: string;
78
+ icon: React.ReactNode;
79
+ /** Highlight state (iPhone: yellow when engaged/auto). */
80
+ active?: boolean;
81
+ /** Small value read-out under the icon (e.g. "3s", "4:3"). */
82
+ valueLabel?: string;
83
+ disabled?: boolean;
84
+ onPress: () => void;
85
+ }
86
+ /**
87
+ * Slot-based extensibility — how domain layers (e.g. commerce intake) attach
88
+ * their own affordances without forking the chrome. Typed slots, not a plugin
89
+ * registry, on purpose.
90
+ */
91
+ interface CaptureCameraSlots {
92
+ /** Extra buttons in the top bar, before the options-grid button. */
93
+ topBarTrailing?: React.ReactNode;
94
+ /** Center of the top bar (e.g. current-item label + count). */
95
+ topBarCenter?: React.ReactNode;
96
+ /** Honesty chips under the top bar (QR confirmation, etc.). */
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;
101
+ /** Rows rendered inside the bottom bar ABOVE the mode selector
102
+ * (keep to ONE compact row — bar height is stolen from the feed). */
103
+ aboveModeSelector?: React.ReactNode;
104
+ /** A compact action pinned right of the mode selector (Next/Break). */
105
+ modeRowTrailing?: React.ReactNode;
106
+ /** Extra tiles appended to the options grid. */
107
+ optionTiles?: CaptureOptionTile[];
108
+ /** Extra entries in the mode row after UPLOAD (e.g. SCAN). Selecting one
109
+ * is an immediate host action — the chrome keeps its current mode. */
110
+ extraModes?: {
111
+ id: string;
112
+ label: string;
113
+ onSelect: () => void;
114
+ }[];
115
+ /** Free overlays rendered above everything (sheets, pagers). */
116
+ overlays?: React.ReactNode;
117
+ }
118
+
119
+ export type { CaptureAspect, CaptureCameraEngine, CaptureCameraMode, CaptureCameraSlots, CaptureCloudPort, CaptureOptionTile, CaptureTimerSetting };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}