react-native-image-stitcher 0.2.1 → 0.4.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 +511 -1
- package/README.md +1 -1
- package/android/src/main/cpp/keyframe_gate_jni.cpp +138 -0
- package/android/src/main/java/io/imagestitcher/rn/IncrementalStitcher.kt +412 -40
- package/android/src/main/java/io/imagestitcher/rn/KeyframeGate.kt +128 -0
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +87 -45
- package/android/src/main/java/io/imagestitcher/rn/RNSARSession.kt +46 -4
- package/cpp/stitcher.cpp +101 -1
- package/cpp/stitcher.hpp +8 -0
- package/dist/camera/Camera.d.ts +9 -0
- package/dist/camera/Camera.js +165 -43
- package/dist/camera/CaptureDebugOverlay.d.ts +45 -0
- package/dist/camera/CaptureDebugOverlay.js +146 -0
- package/dist/camera/CaptureKeyframePill.d.ts +28 -0
- package/dist/camera/CaptureKeyframePill.js +60 -0
- package/dist/camera/CaptureMemoryPill.d.ts +28 -0
- package/dist/camera/CaptureMemoryPill.js +109 -0
- package/dist/camera/CaptureOrientationPill.d.ts +22 -0
- package/dist/camera/CaptureOrientationPill.js +44 -0
- package/dist/camera/CaptureStitchStatsToast.d.ts +45 -0
- package/dist/camera/CaptureStitchStatsToast.js +133 -0
- package/dist/camera/PanoramaSettings.d.ts +478 -0
- package/dist/camera/PanoramaSettings.js +120 -0
- package/dist/camera/PanoramaSettingsBridge.d.ts +84 -0
- package/dist/camera/PanoramaSettingsBridge.js +208 -0
- package/dist/camera/PanoramaSettingsModal.d.ts +50 -298
- package/dist/camera/PanoramaSettingsModal.js +189 -354
- package/dist/camera/buildPanoramaInitialSettings.d.ts +70 -0
- package/dist/camera/buildPanoramaInitialSettings.js +97 -0
- package/dist/camera/lowMemDevice.d.ts +24 -0
- package/dist/camera/lowMemDevice.js +69 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +37 -2
- package/dist/sensors/useIMUTranslationGate.d.ts +26 -0
- package/dist/sensors/useIMUTranslationGate.js +83 -1
- package/dist/stitching/incremental.d.ts +25 -0
- package/dist/stitching/useIncrementalStitcher.d.ts +12 -1
- package/dist/stitching/useIncrementalStitcher.js +7 -1
- package/ios/Sources/RNImageStitcher/IncrementalStitcher.swift +321 -7
- package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.swift +8 -0
- package/ios/Sources/RNImageStitcher/KeyframeGate.swift +12 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.h +13 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.mm +15 -0
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.h +1 -0
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.mm +17 -4
- package/ios/Sources/RNImageStitcher/Stitcher.swift +6 -1
- package/package.json +6 -2
- package/src/camera/Camera.tsx +220 -54
- package/src/camera/CaptureDebugOverlay.tsx +180 -0
- package/src/camera/CaptureKeyframePill.tsx +77 -0
- package/src/camera/CaptureMemoryPill.tsx +96 -0
- package/src/camera/CaptureOrientationPill.tsx +57 -0
- package/src/camera/CaptureStitchStatsToast.tsx +155 -0
- package/src/camera/PanoramaSettings.ts +605 -0
- package/src/camera/PanoramaSettingsBridge.ts +238 -0
- package/src/camera/PanoramaSettingsModal.tsx +296 -988
- package/src/camera/__tests__/PanoramaSettingsBridge.test.ts +375 -0
- package/src/camera/__tests__/buildPanoramaInitialSettings.test.ts +119 -0
- package/src/camera/__tests__/lowMemDevice.test.ts +52 -0
- package/src/camera/buildPanoramaInitialSettings.ts +139 -0
- package/src/camera/lowMemDevice.ts +71 -0
- package/src/index.ts +61 -3
- package/src/sensors/useIMUTranslationGate.ts +112 -1
- package/src/stitching/incremental.ts +25 -0
- package/src/stitching/useIncrementalStitcher.ts +18 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* CaptureMemoryPill — top-right diagnostic pill showing native
|
|
5
|
+
* process memory footprint in MB, polled at 500 ms.
|
|
6
|
+
*
|
|
7
|
+
* Color-coded against the iPhone 16 Pro per-process jetsam limit:
|
|
8
|
+
*
|
|
9
|
+
* - green <1500 MB (comfortable)
|
|
10
|
+
* - amber 1500–2200 (approaching pressure)
|
|
11
|
+
* - red >2200 (close to limit — capture may be killed)
|
|
12
|
+
*
|
|
13
|
+
* Backed by the existing `getMemoryFootprintMB()` native module
|
|
14
|
+
* (iOS: `task_info phys_footprint`, Android: `Debug.MemoryInfo
|
|
15
|
+
* getTotalPss * 1024`). Returns -1 if the native call fails.
|
|
16
|
+
*
|
|
17
|
+
* Mount this pill inside a `settings.debug`-gated branch — it
|
|
18
|
+
* polls native every 500 ms and is unwanted in production builds.
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.CaptureMemoryPill = CaptureMemoryPill;
|
|
55
|
+
const react_1 = __importStar(require("react"));
|
|
56
|
+
const react_native_1 = require("react-native");
|
|
57
|
+
const incremental_1 = require("../stitching/incremental");
|
|
58
|
+
function CaptureMemoryPill({ topInset = 0, pollIntervalMs = 500, }) {
|
|
59
|
+
const [memMB, setMemMB] = (0, react_1.useState)(null);
|
|
60
|
+
(0, react_1.useEffect)(() => {
|
|
61
|
+
const native = (0, incremental_1.getIncrementalNativeModule)();
|
|
62
|
+
if (!native?.getMemoryFootprintMB)
|
|
63
|
+
return undefined;
|
|
64
|
+
let cancelled = false;
|
|
65
|
+
const tick = async () => {
|
|
66
|
+
try {
|
|
67
|
+
const mb = await native.getMemoryFootprintMB();
|
|
68
|
+
if (!cancelled)
|
|
69
|
+
setMemMB(mb);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Bridge error — leave the previous reading visible.
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
tick();
|
|
76
|
+
const id = setInterval(tick, pollIntervalMs);
|
|
77
|
+
return () => {
|
|
78
|
+
cancelled = true;
|
|
79
|
+
clearInterval(id);
|
|
80
|
+
};
|
|
81
|
+
}, [pollIntervalMs]);
|
|
82
|
+
if (memMB === null || memMB < 0)
|
|
83
|
+
return null;
|
|
84
|
+
const bg = memMB > 2200 ? 'rgba(239, 68, 68, 0.92)' // red
|
|
85
|
+
: memMB > 1500 ? 'rgba(245, 158, 11, 0.92)' // amber
|
|
86
|
+
: 'rgba(34, 197, 94, 0.92)'; // green
|
|
87
|
+
return (react_1.default.createElement(react_native_1.View, { pointerEvents: "none", style: [
|
|
88
|
+
styles.container,
|
|
89
|
+
{ top: topInset + 56, backgroundColor: bg },
|
|
90
|
+
], accessibilityRole: "alert" },
|
|
91
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.text }, `${Math.round(memMB)} MB`)));
|
|
92
|
+
}
|
|
93
|
+
const styles = react_native_1.StyleSheet.create({
|
|
94
|
+
container: {
|
|
95
|
+
position: 'absolute',
|
|
96
|
+
right: 12,
|
|
97
|
+
paddingHorizontal: 10,
|
|
98
|
+
paddingVertical: 5,
|
|
99
|
+
borderRadius: 999,
|
|
100
|
+
zIndex: 100,
|
|
101
|
+
},
|
|
102
|
+
text: {
|
|
103
|
+
color: '#fff',
|
|
104
|
+
fontSize: 12,
|
|
105
|
+
fontWeight: '700',
|
|
106
|
+
fontFamily: 'Menlo',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=CaptureMemoryPill.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CaptureOrientationPill — diagnostic pill showing the operator's
|
|
3
|
+
* current hold orientation as detected by the pose-derived hook.
|
|
4
|
+
*
|
|
5
|
+
* Useful for diagnosing rotation issues — if the pill says
|
|
6
|
+
* `landscape-left` but the band overlay is rendering as if it's
|
|
7
|
+
* `portrait`, there's a mismatch between the JS orientation hook
|
|
8
|
+
* and the engine's pose-derived isLandscape signal.
|
|
9
|
+
*
|
|
10
|
+
* Pinned top-left below the status bar. Layer-2 hosts can mount
|
|
11
|
+
* this directly; Layer-1 `<Camera>` mounts it automatically when
|
|
12
|
+
* `settings.debug = true`.
|
|
13
|
+
*/
|
|
14
|
+
import React from 'react';
|
|
15
|
+
export interface CaptureOrientationPillProps {
|
|
16
|
+
/** Current device orientation (typically from useDeviceOrientation). */
|
|
17
|
+
orientation: string;
|
|
18
|
+
/** Top inset for safe-area placement. Pill pinned `topInset + 56`. */
|
|
19
|
+
topInset?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function CaptureOrientationPill({ orientation, topInset, }: CaptureOrientationPillProps): React.JSX.Element;
|
|
22
|
+
//# sourceMappingURL=CaptureOrientationPill.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* CaptureOrientationPill — diagnostic pill showing the operator's
|
|
5
|
+
* current hold orientation as detected by the pose-derived hook.
|
|
6
|
+
*
|
|
7
|
+
* Useful for diagnosing rotation issues — if the pill says
|
|
8
|
+
* `landscape-left` but the band overlay is rendering as if it's
|
|
9
|
+
* `portrait`, there's a mismatch between the JS orientation hook
|
|
10
|
+
* and the engine's pose-derived isLandscape signal.
|
|
11
|
+
*
|
|
12
|
+
* Pinned top-left below the status bar. Layer-2 hosts can mount
|
|
13
|
+
* this directly; Layer-1 `<Camera>` mounts it automatically when
|
|
14
|
+
* `settings.debug = true`.
|
|
15
|
+
*/
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.CaptureOrientationPill = CaptureOrientationPill;
|
|
21
|
+
const react_1 = __importDefault(require("react"));
|
|
22
|
+
const react_native_1 = require("react-native");
|
|
23
|
+
function CaptureOrientationPill({ orientation, topInset = 0, }) {
|
|
24
|
+
return (react_1.default.createElement(react_native_1.View, { pointerEvents: "none", style: [styles.container, { top: topInset + 56 }], accessibilityRole: "alert" },
|
|
25
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.text }, `orient: ${orientation}`)));
|
|
26
|
+
}
|
|
27
|
+
const styles = react_native_1.StyleSheet.create({
|
|
28
|
+
container: {
|
|
29
|
+
position: 'absolute',
|
|
30
|
+
left: 12,
|
|
31
|
+
paddingHorizontal: 10,
|
|
32
|
+
paddingVertical: 5,
|
|
33
|
+
borderRadius: 999,
|
|
34
|
+
backgroundColor: 'rgba(99, 102, 241, 0.92)',
|
|
35
|
+
zIndex: 100,
|
|
36
|
+
},
|
|
37
|
+
text: {
|
|
38
|
+
color: '#fff',
|
|
39
|
+
fontSize: 11,
|
|
40
|
+
fontWeight: '700',
|
|
41
|
+
fontFamily: 'Menlo',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=CaptureOrientationPill.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CaptureStitchStatsToast — auto-dismissing toast that shows the
|
|
3
|
+
* batch-stitcher's leaveBiggestComponent telemetry + the resolved
|
|
4
|
+
* cv::Stitcher mode after every successful finalize.
|
|
5
|
+
*
|
|
6
|
+
* Pattern: top-center capsule, dark translucent background, dismisses
|
|
7
|
+
* itself after `dismissAfterMs` (default 4500). Replaces the
|
|
8
|
+
* Alert.alert blocking modal that used to interrupt the next
|
|
9
|
+
* capture. See `useStitchStatsToast` hook for the matching
|
|
10
|
+
* imperative API.
|
|
11
|
+
*
|
|
12
|
+
* Layer-2 hosts can mount this directly + pass their own message;
|
|
13
|
+
* Layer-1 `<Camera>` mounts it under `settings.debug` and feeds
|
|
14
|
+
* the formatted message from the finalize result automatically.
|
|
15
|
+
*/
|
|
16
|
+
import React from 'react';
|
|
17
|
+
import type { IncrementalFinalizeResult } from '../stitching/incremental';
|
|
18
|
+
export interface CaptureStitchStatsToastProps {
|
|
19
|
+
/** Toast message to show. Pass null to hide. */
|
|
20
|
+
message: string | null;
|
|
21
|
+
/** Top inset for safe-area placement. Toast pinned `topInset + 12`. */
|
|
22
|
+
topInset?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function CaptureStitchStatsToast({ message, topInset, }: CaptureStitchStatsToastProps): React.JSX.Element | null;
|
|
25
|
+
/**
|
|
26
|
+
* Imperative API for showing transient stitch-stats toasts.
|
|
27
|
+
*
|
|
28
|
+
* Returns `{ message, showFor, showResult }`:
|
|
29
|
+
* - `message` — current toast text (pass to CaptureStitchStatsToast)
|
|
30
|
+
* - `showFor` — show an arbitrary string, auto-dismiss
|
|
31
|
+
* - `showResult` — format an `IncrementalFinalizeResult` into the
|
|
32
|
+
* standard "Stitch: N/M frames • thresh X.XX •
|
|
33
|
+
* N attempt(s) • mode" line and show it. Convenience
|
|
34
|
+
* for hosts that just want the canonical format.
|
|
35
|
+
*
|
|
36
|
+
* Auto-clears its setTimeout on unmount so callers don't have to
|
|
37
|
+
* worry about setState-on-unmounted warnings.
|
|
38
|
+
*/
|
|
39
|
+
export interface UseStitchStatsToastReturn {
|
|
40
|
+
message: string | null;
|
|
41
|
+
showFor: (msg: string, ms?: number) => void;
|
|
42
|
+
showResult: (result: IncrementalFinalizeResult, ms?: number) => void;
|
|
43
|
+
}
|
|
44
|
+
export declare function useStitchStatsToast(): UseStitchStatsToastReturn;
|
|
45
|
+
//# sourceMappingURL=CaptureStitchStatsToast.d.ts.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* CaptureStitchStatsToast — auto-dismissing toast that shows the
|
|
5
|
+
* batch-stitcher's leaveBiggestComponent telemetry + the resolved
|
|
6
|
+
* cv::Stitcher mode after every successful finalize.
|
|
7
|
+
*
|
|
8
|
+
* Pattern: top-center capsule, dark translucent background, dismisses
|
|
9
|
+
* itself after `dismissAfterMs` (default 4500). Replaces the
|
|
10
|
+
* Alert.alert blocking modal that used to interrupt the next
|
|
11
|
+
* capture. See `useStitchStatsToast` hook for the matching
|
|
12
|
+
* imperative API.
|
|
13
|
+
*
|
|
14
|
+
* Layer-2 hosts can mount this directly + pass their own message;
|
|
15
|
+
* Layer-1 `<Camera>` mounts it under `settings.debug` and feeds
|
|
16
|
+
* the formatted message from the finalize result automatically.
|
|
17
|
+
*/
|
|
18
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
21
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
22
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(o, k2, desc);
|
|
25
|
+
}) : (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
o[k2] = m[k];
|
|
28
|
+
}));
|
|
29
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
30
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
31
|
+
}) : function(o, v) {
|
|
32
|
+
o["default"] = v;
|
|
33
|
+
});
|
|
34
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
35
|
+
var ownKeys = function(o) {
|
|
36
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
37
|
+
var ar = [];
|
|
38
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
39
|
+
return ar;
|
|
40
|
+
};
|
|
41
|
+
return ownKeys(o);
|
|
42
|
+
};
|
|
43
|
+
return function (mod) {
|
|
44
|
+
if (mod && mod.__esModule) return mod;
|
|
45
|
+
var result = {};
|
|
46
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
47
|
+
__setModuleDefault(result, mod);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
})();
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.CaptureStitchStatsToast = CaptureStitchStatsToast;
|
|
53
|
+
exports.useStitchStatsToast = useStitchStatsToast;
|
|
54
|
+
const react_1 = __importStar(require("react"));
|
|
55
|
+
const react_native_1 = require("react-native");
|
|
56
|
+
function CaptureStitchStatsToast({ message, topInset = 0, }) {
|
|
57
|
+
if (message === null)
|
|
58
|
+
return null;
|
|
59
|
+
return (react_1.default.createElement(react_native_1.View, { pointerEvents: "none", style: [
|
|
60
|
+
styles.wrap,
|
|
61
|
+
{ top: topInset + 12 },
|
|
62
|
+
] },
|
|
63
|
+
react_1.default.createElement(react_native_1.View, { style: styles.capsule, accessibilityRole: "alert", accessibilityLiveRegion: "polite" },
|
|
64
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.text, numberOfLines: 3 }, message))));
|
|
65
|
+
}
|
|
66
|
+
const styles = react_native_1.StyleSheet.create({
|
|
67
|
+
wrap: {
|
|
68
|
+
position: 'absolute',
|
|
69
|
+
left: 24,
|
|
70
|
+
right: 24,
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
zIndex: 110,
|
|
73
|
+
},
|
|
74
|
+
capsule: {
|
|
75
|
+
paddingHorizontal: 16,
|
|
76
|
+
paddingVertical: 10,
|
|
77
|
+
borderRadius: 16,
|
|
78
|
+
backgroundColor: 'rgba(15, 23, 42, 0.92)',
|
|
79
|
+
maxWidth: '100%',
|
|
80
|
+
},
|
|
81
|
+
text: {
|
|
82
|
+
color: '#ffffff',
|
|
83
|
+
fontSize: 13,
|
|
84
|
+
fontWeight: '600',
|
|
85
|
+
textAlign: 'center',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
const DEFAULT_DISMISS_MS = 4500;
|
|
89
|
+
function useStitchStatsToast() {
|
|
90
|
+
const [message, setMessage] = (0, react_1.useState)(null);
|
|
91
|
+
const timerRef = (0, react_1.useRef)(null);
|
|
92
|
+
const showFor = (0, react_1.useCallback)((msg, ms = DEFAULT_DISMISS_MS) => {
|
|
93
|
+
if (timerRef.current)
|
|
94
|
+
clearTimeout(timerRef.current);
|
|
95
|
+
setMessage(msg);
|
|
96
|
+
timerRef.current = setTimeout(() => {
|
|
97
|
+
setMessage(null);
|
|
98
|
+
timerRef.current = null;
|
|
99
|
+
}, ms);
|
|
100
|
+
}, []);
|
|
101
|
+
const showResult = (0, react_1.useCallback)((result, ms = DEFAULT_DISMISS_MS) => {
|
|
102
|
+
// Format mirrors the RetaiLens debug toast that operators
|
|
103
|
+
// already recognise. Includes the new (audit F2g) resolved
|
|
104
|
+
// stitchMode as a fourth segment when present.
|
|
105
|
+
const requested = result.framesRequested;
|
|
106
|
+
const included = result.framesIncluded;
|
|
107
|
+
const thresh = result.finalConfidenceThresh;
|
|
108
|
+
const mode = result.stitchModeResolved;
|
|
109
|
+
// The retry-attempt count is derived deterministically from
|
|
110
|
+
// the threshold used on the successful attempt (1.0→1, 0.5→2,
|
|
111
|
+
// 0.3→3) per cpp/stitcher.cpp's retry loop.
|
|
112
|
+
const attempts = typeof thresh === 'number'
|
|
113
|
+
? thresh >= 0.99 ? 1
|
|
114
|
+
: thresh >= 0.49 ? 2
|
|
115
|
+
: thresh >= 0.29 ? 3
|
|
116
|
+
: null
|
|
117
|
+
: null;
|
|
118
|
+
const reqStr = typeof requested === 'number' ? requested : '?';
|
|
119
|
+
const incStr = typeof included === 'number' ? included : '?';
|
|
120
|
+
const threshStr = typeof thresh === 'number' && thresh >= 0
|
|
121
|
+
? thresh.toFixed(2)
|
|
122
|
+
: 'n/a';
|
|
123
|
+
const attStr = attempts !== null ? `${attempts} attempt${attempts > 1 ? 's' : ''}` : '? attempts';
|
|
124
|
+
const modeStr = mode ? ` • ${mode}` : '';
|
|
125
|
+
showFor(`Stitch: ${incStr}/${reqStr} frames • thresh ${threshStr} • ${attStr}${modeStr}`, ms);
|
|
126
|
+
}, [showFor]);
|
|
127
|
+
(0, react_1.useEffect)(() => () => {
|
|
128
|
+
if (timerRef.current)
|
|
129
|
+
clearTimeout(timerRef.current);
|
|
130
|
+
}, []);
|
|
131
|
+
return { message, showFor, showResult };
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=CaptureStitchStatsToast.js.map
|