@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.cjs
CHANGED
|
@@ -22,11 +22,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var react_exports = {};
|
|
23
23
|
__export(react_exports, {
|
|
24
24
|
CameraCapture: () => CameraCapture,
|
|
25
|
+
CameraCaptureV3: () => CameraCaptureV3,
|
|
25
26
|
CameraFeed: () => CameraFeed,
|
|
27
|
+
CaptureExpandingField: () => CaptureExpandingField,
|
|
26
28
|
CaptureFilmstrip: () => CaptureFilmstrip,
|
|
29
|
+
CaptureRail: () => CaptureRail,
|
|
27
30
|
CaptureSheet: () => CaptureSheet,
|
|
28
31
|
CountdownOverlay: () => CountdownOverlay,
|
|
29
32
|
GridOverlay: () => GridOverlay,
|
|
33
|
+
HoldShutter: () => HoldShutter,
|
|
30
34
|
ImageEditSheet: () => ImageEditSheet,
|
|
31
35
|
MediaViewer: () => MediaViewer,
|
|
32
36
|
ModeSelector: () => ModeSelector,
|
|
@@ -1292,6 +1296,7 @@ function CameraCapture({
|
|
|
1292
1296
|
onModeChange,
|
|
1293
1297
|
preview,
|
|
1294
1298
|
media,
|
|
1299
|
+
onReviewOpenChange,
|
|
1295
1300
|
onClose,
|
|
1296
1301
|
blockedSheet,
|
|
1297
1302
|
controlsHidden = false,
|
|
@@ -1309,6 +1314,10 @@ function CameraCapture({
|
|
|
1309
1314
|
const [blockedDismissed, setBlockedDismissed] = (0, import_react5.useState)(false);
|
|
1310
1315
|
const countdownRef = (0, import_react5.useRef)(null);
|
|
1311
1316
|
const controls = useTrackControls(engine.stream);
|
|
1317
|
+
const reviewOpen = viewerKey !== null || editTarget !== null;
|
|
1318
|
+
(0, import_react5.useEffect)(() => {
|
|
1319
|
+
onReviewOpenChange?.(reviewOpen);
|
|
1320
|
+
}, [reviewOpen, onReviewOpenChange]);
|
|
1312
1321
|
const clearCountdown = (0, import_react5.useCallback)(() => {
|
|
1313
1322
|
if (countdownRef.current) clearInterval(countdownRef.current);
|
|
1314
1323
|
countdownRef.current = null;
|
|
@@ -1638,15 +1647,665 @@ function CameraCapture({
|
|
|
1638
1647
|
] });
|
|
1639
1648
|
}
|
|
1640
1649
|
|
|
1641
|
-
// src/components/
|
|
1650
|
+
// src/components/CameraCaptureV3.tsx
|
|
1651
|
+
var import_react8 = require("react");
|
|
1652
|
+
var import_lucide_react5 = require("lucide-react");
|
|
1653
|
+
|
|
1654
|
+
// src/components/CaptureRail.tsx
|
|
1642
1655
|
var import_react6 = require("react");
|
|
1643
1656
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1657
|
+
function CaptureRail({
|
|
1658
|
+
actions,
|
|
1659
|
+
defaultExpanded = false,
|
|
1660
|
+
className
|
|
1661
|
+
}) {
|
|
1662
|
+
const [expanded, setExpanded] = (0, import_react6.useState)(defaultExpanded);
|
|
1663
|
+
const primary = actions.filter((a) => a.primary);
|
|
1664
|
+
const extra = actions.filter((a) => !a.primary);
|
|
1665
|
+
const shown = primary.length === 0 ? actions : expanded ? actions : primary;
|
|
1666
|
+
const collapsible = primary.length > 0 && extra.length > 0;
|
|
1667
|
+
if (actions.length === 0) return null;
|
|
1668
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1669
|
+
"div",
|
|
1670
|
+
{
|
|
1671
|
+
className: cn(
|
|
1672
|
+
"pointer-events-auto flex flex-col items-center gap-1",
|
|
1673
|
+
className
|
|
1674
|
+
),
|
|
1675
|
+
children: [
|
|
1676
|
+
shown.map((action) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(RailButton, { action }, action.id)),
|
|
1677
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1678
|
+
"button",
|
|
1679
|
+
{
|
|
1680
|
+
type: "button",
|
|
1681
|
+
onClick: () => setExpanded((open) => !open),
|
|
1682
|
+
"aria-expanded": expanded,
|
|
1683
|
+
"aria-label": expanded ? "Fewer options" : "More options",
|
|
1684
|
+
className: cn(
|
|
1685
|
+
"mt-0.5 flex h-9 w-9 touch-manipulation items-center justify-center",
|
|
1686
|
+
"rounded-full text-white/90 transition-colors hover:bg-white/10"
|
|
1687
|
+
),
|
|
1688
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1689
|
+
Chevron,
|
|
1690
|
+
{
|
|
1691
|
+
className: cn(
|
|
1692
|
+
"h-5 w-5 transition-transform duration-200",
|
|
1693
|
+
expanded && "rotate-180"
|
|
1694
|
+
)
|
|
1695
|
+
}
|
|
1696
|
+
)
|
|
1697
|
+
}
|
|
1698
|
+
) : null
|
|
1699
|
+
]
|
|
1700
|
+
}
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
function RailButton({ action }) {
|
|
1704
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1705
|
+
"button",
|
|
1706
|
+
{
|
|
1707
|
+
type: "button",
|
|
1708
|
+
onClick: action.onPress,
|
|
1709
|
+
disabled: action.disabled,
|
|
1710
|
+
"aria-label": action.label,
|
|
1711
|
+
"aria-pressed": action.active ?? void 0,
|
|
1712
|
+
className: cn(
|
|
1713
|
+
"flex w-12 shrink-0 touch-manipulation flex-col items-center justify-center gap-0.5",
|
|
1714
|
+
"rounded-2xl py-1.5 transition-colors",
|
|
1715
|
+
action.active ? "text-[#FFCC00]" : "text-white",
|
|
1716
|
+
action.disabled ? "opacity-40" : "hover:bg-white/10 active:bg-white/15"
|
|
1717
|
+
),
|
|
1718
|
+
children: [
|
|
1719
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "drop-shadow-[0_1px_3px_rgba(0,0,0,0.6)]", children: action.icon }),
|
|
1720
|
+
action.valueLabel ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-[10px] font-semibold leading-none tabular-nums drop-shadow-[0_1px_2px_rgba(0,0,0,0.7)]", children: action.valueLabel }) : null
|
|
1721
|
+
]
|
|
1722
|
+
}
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
function Chevron({ className }) {
|
|
1726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1727
|
+
"svg",
|
|
1728
|
+
{
|
|
1729
|
+
viewBox: "0 0 24 24",
|
|
1730
|
+
fill: "none",
|
|
1731
|
+
stroke: "currentColor",
|
|
1732
|
+
strokeWidth: "2.5",
|
|
1733
|
+
strokeLinecap: "round",
|
|
1734
|
+
strokeLinejoin: "round",
|
|
1735
|
+
className,
|
|
1736
|
+
"aria-hidden": "true",
|
|
1737
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" })
|
|
1738
|
+
}
|
|
1739
|
+
);
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
// src/components/HoldShutter.tsx
|
|
1743
|
+
var import_react7 = require("react");
|
|
1744
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1745
|
+
var HOLD_MS = 320;
|
|
1746
|
+
var LOCK_AFTER_MS = 1200;
|
|
1747
|
+
function HoldShutter({
|
|
1748
|
+
recording,
|
|
1749
|
+
elapsedSeconds,
|
|
1750
|
+
maxSeconds,
|
|
1751
|
+
disabled = false,
|
|
1752
|
+
onPressStart,
|
|
1753
|
+
onPhoto,
|
|
1754
|
+
onStartRecording,
|
|
1755
|
+
onStopRecording
|
|
1756
|
+
}) {
|
|
1757
|
+
const holdTimer = (0, import_react7.useRef)(null);
|
|
1758
|
+
const startedRecording = (0, import_react7.useRef)(false);
|
|
1759
|
+
const [pressed, setPressed] = (0, import_react7.useState)(false);
|
|
1760
|
+
const [locked, setLocked] = (0, import_react7.useState)(false);
|
|
1761
|
+
const clearHold = (0, import_react7.useCallback)(() => {
|
|
1762
|
+
if (holdTimer.current !== null) {
|
|
1763
|
+
clearTimeout(holdTimer.current);
|
|
1764
|
+
holdTimer.current = null;
|
|
1765
|
+
}
|
|
1766
|
+
}, []);
|
|
1767
|
+
(0, import_react7.useEffect)(() => {
|
|
1768
|
+
if (!recording) {
|
|
1769
|
+
setLocked(false);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
if (elapsedSeconds * 1e3 >= LOCK_AFTER_MS) setLocked(true);
|
|
1773
|
+
}, [recording, elapsedSeconds]);
|
|
1774
|
+
(0, import_react7.useEffect)(() => clearHold, [clearHold]);
|
|
1775
|
+
const onPointerDown = (0, import_react7.useCallback)(
|
|
1776
|
+
(event) => {
|
|
1777
|
+
if (disabled) return;
|
|
1778
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
1779
|
+
setPressed(true);
|
|
1780
|
+
onPressStart?.();
|
|
1781
|
+
if (recording && locked) return;
|
|
1782
|
+
startedRecording.current = false;
|
|
1783
|
+
holdTimer.current = setTimeout(() => {
|
|
1784
|
+
holdTimer.current = null;
|
|
1785
|
+
startedRecording.current = true;
|
|
1786
|
+
onStartRecording();
|
|
1787
|
+
}, HOLD_MS);
|
|
1788
|
+
},
|
|
1789
|
+
[disabled, locked, onPressStart, onStartRecording, recording]
|
|
1790
|
+
);
|
|
1791
|
+
const endPress = (0, import_react7.useCallback)(() => {
|
|
1792
|
+
if (!pressed) return;
|
|
1793
|
+
setPressed(false);
|
|
1794
|
+
const wasHold = startedRecording.current;
|
|
1795
|
+
clearHold();
|
|
1796
|
+
startedRecording.current = false;
|
|
1797
|
+
if (recording && locked && !wasHold) {
|
|
1798
|
+
onStopRecording();
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
if (wasHold) {
|
|
1802
|
+
if (!locked) onStopRecording();
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
if (!recording) onPhoto();
|
|
1806
|
+
}, [clearHold, locked, onPhoto, onStopRecording, pressed, recording]);
|
|
1807
|
+
const progress = maxSeconds && maxSeconds > 0 ? Math.min(1, elapsedSeconds / maxSeconds) : 0;
|
|
1808
|
+
const R = 39;
|
|
1809
|
+
const C = 2 * Math.PI * R;
|
|
1810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative flex h-[86px] w-[86px] shrink-0 items-center justify-center", children: [
|
|
1811
|
+
recording && maxSeconds ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1812
|
+
"svg",
|
|
1813
|
+
{
|
|
1814
|
+
className: "pointer-events-none absolute inset-0 -rotate-90",
|
|
1815
|
+
viewBox: "0 0 86 86",
|
|
1816
|
+
"aria-hidden": "true",
|
|
1817
|
+
children: [
|
|
1818
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1819
|
+
"circle",
|
|
1820
|
+
{
|
|
1821
|
+
cx: "43",
|
|
1822
|
+
cy: "43",
|
|
1823
|
+
r: R,
|
|
1824
|
+
fill: "none",
|
|
1825
|
+
stroke: "rgba(255,255,255,0.25)",
|
|
1826
|
+
strokeWidth: "4"
|
|
1827
|
+
}
|
|
1828
|
+
),
|
|
1829
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1830
|
+
"circle",
|
|
1831
|
+
{
|
|
1832
|
+
cx: "43",
|
|
1833
|
+
cy: "43",
|
|
1834
|
+
r: R,
|
|
1835
|
+
fill: "none",
|
|
1836
|
+
stroke: "#FF3B30",
|
|
1837
|
+
strokeWidth: "4",
|
|
1838
|
+
strokeLinecap: "round",
|
|
1839
|
+
strokeDasharray: C,
|
|
1840
|
+
strokeDashoffset: C * (1 - progress)
|
|
1841
|
+
}
|
|
1842
|
+
)
|
|
1843
|
+
]
|
|
1844
|
+
}
|
|
1845
|
+
) : null,
|
|
1846
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1847
|
+
"button",
|
|
1848
|
+
{
|
|
1849
|
+
type: "button",
|
|
1850
|
+
disabled,
|
|
1851
|
+
onPointerDown,
|
|
1852
|
+
onPointerUp: endPress,
|
|
1853
|
+
onPointerCancel: endPress,
|
|
1854
|
+
onLostPointerCapture: endPress,
|
|
1855
|
+
onContextMenu: (e) => e.preventDefault(),
|
|
1856
|
+
"aria-label": recording ? locked ? "Stop recording" : "Recording \u2014 release to stop" : "Tap for a photo, hold to record",
|
|
1857
|
+
className: cn(
|
|
1858
|
+
"group flex h-[74px] w-[74px] shrink-0 touch-none select-none items-center justify-center",
|
|
1859
|
+
"rounded-full border-[3.5px] border-white transition-opacity",
|
|
1860
|
+
disabled && "opacity-30"
|
|
1861
|
+
),
|
|
1862
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1863
|
+
"span",
|
|
1864
|
+
{
|
|
1865
|
+
className: cn(
|
|
1866
|
+
"block transition-all duration-200 ease-out",
|
|
1867
|
+
recording ? "h-7 w-7 rounded-[6px] bg-[#FF3B30]" : cn(
|
|
1868
|
+
"h-[62px] w-[62px] rounded-full bg-white",
|
|
1869
|
+
pressed && "scale-90"
|
|
1870
|
+
)
|
|
1871
|
+
)
|
|
1872
|
+
}
|
|
1873
|
+
)
|
|
1874
|
+
}
|
|
1875
|
+
),
|
|
1876
|
+
recording ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1877
|
+
"span",
|
|
1878
|
+
{
|
|
1879
|
+
className: cn(
|
|
1880
|
+
"pointer-events-none absolute -bottom-1 left-1/2 -translate-x-1/2 translate-y-full",
|
|
1881
|
+
"rounded-full bg-black/60 px-2 py-0.5 text-[11px] font-medium tabular-nums text-white"
|
|
1882
|
+
),
|
|
1883
|
+
children: [
|
|
1884
|
+
formatElapsed2(elapsedSeconds),
|
|
1885
|
+
locked ? " \xB7 tap to stop" : ""
|
|
1886
|
+
]
|
|
1887
|
+
}
|
|
1888
|
+
) : null
|
|
1889
|
+
] });
|
|
1890
|
+
}
|
|
1891
|
+
function formatElapsed2(totalSeconds) {
|
|
1892
|
+
const m = Math.floor(totalSeconds / 60);
|
|
1893
|
+
const s = String(Math.floor(totalSeconds % 60)).padStart(2, "0");
|
|
1894
|
+
return `${m}:${s}`;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
// src/components/CameraCaptureV3.tsx
|
|
1898
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1899
|
+
var ASPECT_CYCLE2 = ["full", "4:3", "1:1", "16:9"];
|
|
1900
|
+
function CameraCaptureV3({
|
|
1901
|
+
engine,
|
|
1902
|
+
cloud,
|
|
1903
|
+
preview,
|
|
1904
|
+
media,
|
|
1905
|
+
onRecordIntent,
|
|
1906
|
+
maxRecordSeconds,
|
|
1907
|
+
onReviewOpenChange,
|
|
1908
|
+
onClose,
|
|
1909
|
+
blockedSheet,
|
|
1910
|
+
controlsHidden = false,
|
|
1911
|
+
shutterDisabled = false,
|
|
1912
|
+
slots = {}
|
|
1913
|
+
}) {
|
|
1914
|
+
const [viewerKey, setViewerKey] = (0, import_react8.useState)(null);
|
|
1915
|
+
const [editTarget, setEditTarget] = (0, import_react8.useState)(
|
|
1916
|
+
null
|
|
1917
|
+
);
|
|
1918
|
+
const [gridOn, setGridOn] = (0, import_react8.useState)(false);
|
|
1919
|
+
const [timerSetting, setTimerSetting] = (0, import_react8.useState)(0);
|
|
1920
|
+
const [aspect, setAspect] = (0, import_react8.useState)("full");
|
|
1921
|
+
const [countdown, setCountdown] = (0, import_react8.useState)(null);
|
|
1922
|
+
const [blockedDismissed, setBlockedDismissed] = (0, import_react8.useState)(false);
|
|
1923
|
+
const countdownRef = (0, import_react8.useRef)(null);
|
|
1924
|
+
const controls = useTrackControls(engine.stream);
|
|
1925
|
+
const reviewOpen = viewerKey !== null || editTarget !== null;
|
|
1926
|
+
(0, import_react8.useEffect)(() => {
|
|
1927
|
+
onReviewOpenChange?.(reviewOpen);
|
|
1928
|
+
}, [reviewOpen, onReviewOpenChange]);
|
|
1929
|
+
const clearCountdown = (0, import_react8.useCallback)(() => {
|
|
1930
|
+
if (countdownRef.current) clearInterval(countdownRef.current);
|
|
1931
|
+
countdownRef.current = null;
|
|
1932
|
+
setCountdown(null);
|
|
1933
|
+
}, []);
|
|
1934
|
+
(0, import_react8.useEffect)(() => clearCountdown, [clearCountdown]);
|
|
1935
|
+
(0, import_react8.useEffect)(() => {
|
|
1936
|
+
if (engine.recording) clearCountdown();
|
|
1937
|
+
}, [engine.recording, clearCountdown]);
|
|
1938
|
+
const onPhoto = (0, import_react8.useCallback)(() => {
|
|
1939
|
+
if (countdown !== null) {
|
|
1940
|
+
clearCountdown();
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
if (timerSetting === 0) {
|
|
1944
|
+
engine.onCapturePhoto({ aspect });
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
let remaining = timerSetting;
|
|
1948
|
+
setCountdown(remaining);
|
|
1949
|
+
countdownRef.current = setInterval(() => {
|
|
1950
|
+
remaining -= 1;
|
|
1951
|
+
if (remaining <= 0) {
|
|
1952
|
+
clearCountdown();
|
|
1953
|
+
engine.onCapturePhoto({ aspect });
|
|
1954
|
+
} else {
|
|
1955
|
+
setCountdown(remaining);
|
|
1956
|
+
}
|
|
1957
|
+
}, 1e3);
|
|
1958
|
+
}, [aspect, clearCountdown, countdown, engine, timerSetting]);
|
|
1959
|
+
const blocked = engine.blocked !== null;
|
|
1960
|
+
const coreRail = [
|
|
1961
|
+
...engine.onFlipCamera ? [
|
|
1962
|
+
{
|
|
1963
|
+
id: "flip",
|
|
1964
|
+
label: "Switch camera",
|
|
1965
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.RefreshCw, { className: "h-[22px] w-[22px]" }),
|
|
1966
|
+
primary: true,
|
|
1967
|
+
onPress: engine.onFlipCamera
|
|
1968
|
+
}
|
|
1969
|
+
] : [],
|
|
1970
|
+
...controls.torchSupported ? [
|
|
1971
|
+
{
|
|
1972
|
+
id: "flash",
|
|
1973
|
+
label: "Flash",
|
|
1974
|
+
icon: controls.torchOn ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.Zap, { className: "h-[22px] w-[22px]", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.ZapOff, { className: "h-[22px] w-[22px]" }),
|
|
1975
|
+
active: controls.torchOn,
|
|
1976
|
+
primary: true,
|
|
1977
|
+
onPress: controls.toggleTorch
|
|
1978
|
+
}
|
|
1979
|
+
] : [],
|
|
1980
|
+
{
|
|
1981
|
+
id: "timer",
|
|
1982
|
+
label: "Timer",
|
|
1983
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.Timer, { className: "h-[22px] w-[22px]" }),
|
|
1984
|
+
active: timerSetting !== 0,
|
|
1985
|
+
valueLabel: timerSetting === 0 ? void 0 : `${timerSetting}s`,
|
|
1986
|
+
onPress: () => setTimerSetting((t) => t === 0 ? 3 : t === 3 ? 10 : 0)
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
id: "grid",
|
|
1990
|
+
label: "Grid",
|
|
1991
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.Grid3x3, { className: "h-[22px] w-[22px]" }),
|
|
1992
|
+
active: gridOn,
|
|
1993
|
+
onPress: () => setGridOn((g) => !g)
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
id: "aspect",
|
|
1997
|
+
label: "Aspect",
|
|
1998
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.Proportions, { className: "h-[22px] w-[22px]" }),
|
|
1999
|
+
active: aspect !== "full",
|
|
2000
|
+
valueLabel: aspect === "full" ? void 0 : aspect,
|
|
2001
|
+
onPress: () => setAspect(
|
|
2002
|
+
(a) => ASPECT_CYCLE2[(ASPECT_CYCLE2.indexOf(a) + 1) % ASPECT_CYCLE2.length] ?? "full"
|
|
2003
|
+
)
|
|
2004
|
+
},
|
|
2005
|
+
...controls.exposureSupported ? [
|
|
2006
|
+
{
|
|
2007
|
+
id: "exposure",
|
|
2008
|
+
label: "Exposure",
|
|
2009
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.SunMedium, { className: "h-[22px] w-[22px]" }),
|
|
2010
|
+
active: controls.exposure !== 0,
|
|
2011
|
+
valueLabel: controls.exposure === 0 ? void 0 : `${controls.exposure > 0 ? "+" : ""}${controls.exposure}`,
|
|
2012
|
+
onPress: () => controls.setExposure(controls.exposure === 0 ? 1 : 0)
|
|
2013
|
+
}
|
|
2014
|
+
] : []
|
|
2015
|
+
];
|
|
2016
|
+
const railActions = [...coreRail, ...slots.railActions ?? []];
|
|
2017
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "absolute inset-0 select-none overflow-hidden bg-black", children: [
|
|
2018
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "absolute inset-0", children: preview }),
|
|
2019
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GridOverlay, { visible: gridOn && !blocked }),
|
|
2020
|
+
aspect !== "full" && !blocked && !engine.recording && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2021
|
+
"div",
|
|
2022
|
+
{
|
|
2023
|
+
"aria-hidden": true,
|
|
2024
|
+
className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center",
|
|
2025
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2026
|
+
"div",
|
|
2027
|
+
{
|
|
2028
|
+
className: "shadow-[0_0_0_9999px_rgba(0,0,0,0.45)]",
|
|
2029
|
+
style: {
|
|
2030
|
+
aspectRatio: aspect === "1:1" ? "1 / 1" : aspect === "4:3" ? "3 / 4" : "9 / 16",
|
|
2031
|
+
width: aspect === "16:9" ? "100%" : void 0,
|
|
2032
|
+
height: aspect === "16:9" ? void 0 : "70%",
|
|
2033
|
+
maxWidth: "100%",
|
|
2034
|
+
maxHeight: "100%"
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
)
|
|
2038
|
+
}
|
|
2039
|
+
),
|
|
2040
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CountdownOverlay, { seconds: countdown }),
|
|
2041
|
+
!controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2042
|
+
"div",
|
|
2043
|
+
{
|
|
2044
|
+
className: "pointer-events-none absolute inset-x-0 top-0 z-20 px-2",
|
|
2045
|
+
style: safeTop,
|
|
2046
|
+
children: [
|
|
2047
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-h-[44px] items-center gap-2 py-1.5", children: [
|
|
2048
|
+
onClose ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2049
|
+
"button",
|
|
2050
|
+
{
|
|
2051
|
+
type: "button",
|
|
2052
|
+
onClick: onClose,
|
|
2053
|
+
"aria-label": "Close camera",
|
|
2054
|
+
className: "pointer-events-auto flex h-10 w-10 shrink-0 touch-manipulation items-center justify-center rounded-full text-white drop-shadow-[0_1px_3px_rgba(0,0,0,0.6)] transition-colors hover:bg-white/10",
|
|
2055
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.X, { className: "h-6 w-6" })
|
|
2056
|
+
}
|
|
2057
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "h-10 w-10 shrink-0" }),
|
|
2058
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "pointer-events-auto flex min-w-0 flex-1 justify-center", children: slots.topEntry }),
|
|
2059
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "h-10 w-10 shrink-0" })
|
|
2060
|
+
] }),
|
|
2061
|
+
slots.topCenter ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "pointer-events-none pb-1 text-center", children: slots.topCenter }) : null
|
|
2062
|
+
]
|
|
2063
|
+
}
|
|
2064
|
+
),
|
|
2065
|
+
slots.statusChips ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2066
|
+
"div",
|
|
2067
|
+
{
|
|
2068
|
+
className: "pointer-events-none absolute inset-x-0 z-20 flex flex-col items-center gap-1.5 px-3",
|
|
2069
|
+
style: {
|
|
2070
|
+
top: `calc(env(safe-area-inset-top, 0px) + ${controlsHidden ? 12 : 64}px)`
|
|
2071
|
+
},
|
|
2072
|
+
children: slots.statusChips
|
|
2073
|
+
}
|
|
2074
|
+
) : null,
|
|
2075
|
+
!controlsHidden && !blocked && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2076
|
+
"div",
|
|
2077
|
+
{
|
|
2078
|
+
className: "pointer-events-none absolute right-1 z-20 flex justify-end",
|
|
2079
|
+
style: { top: `calc(env(safe-area-inset-top, 0px) + 64px)` },
|
|
2080
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CaptureRail, { actions: railActions })
|
|
2081
|
+
}
|
|
2082
|
+
),
|
|
2083
|
+
!controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "absolute inset-x-0 bottom-0 z-20", style: safeBottom, children: [
|
|
2084
|
+
media && media.items.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "px-2 pb-1.5", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2085
|
+
CaptureFilmstrip,
|
|
2086
|
+
{
|
|
2087
|
+
items: media.items,
|
|
2088
|
+
onOpen: (item) => setViewerKey(item.key)
|
|
2089
|
+
}
|
|
2090
|
+
) }) : null,
|
|
2091
|
+
slots.aboveShutter ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "px-2 pb-1.5", children: slots.aboveShutter }) : null,
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between px-3 pb-2", children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex w-[86px] justify-start", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2094
|
+
"button",
|
|
2095
|
+
{
|
|
2096
|
+
type: "button",
|
|
2097
|
+
onClick: cloud.onOpenLibrary,
|
|
2098
|
+
"aria-label": "Your media \u2014 library and upload",
|
|
2099
|
+
className: "relative h-12 w-12 touch-manipulation overflow-hidden rounded-xl bg-black/40 ring-1 ring-white/30 backdrop-blur-md transition-transform active:scale-95",
|
|
2100
|
+
children: cloud.recentsThumb ?? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.Images, { className: "h-5 w-5 text-white/80" }) })
|
|
2101
|
+
}
|
|
2102
|
+
) }),
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2104
|
+
HoldShutter,
|
|
2105
|
+
{
|
|
2106
|
+
recording: engine.recording,
|
|
2107
|
+
elapsedSeconds: engine.recordElapsedSeconds,
|
|
2108
|
+
...maxRecordSeconds !== void 0 ? { maxSeconds: maxRecordSeconds } : {},
|
|
2109
|
+
disabled: shutterDisabled || blocked,
|
|
2110
|
+
...onRecordIntent ? { onPressStart: onRecordIntent } : {},
|
|
2111
|
+
onPhoto,
|
|
2112
|
+
onStartRecording: engine.onStartRecording,
|
|
2113
|
+
onStopRecording: engine.onStopRecording
|
|
2114
|
+
}
|
|
2115
|
+
),
|
|
2116
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex w-[86px] justify-end", children: slots.shutterTrailing })
|
|
2117
|
+
] }),
|
|
2118
|
+
!engine.recording && !blocked ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "pb-1.5 text-center text-[11px] text-white/60 drop-shadow-[0_1px_2px_rgba(0,0,0,0.8)]", children: "Tap for a photo \xB7 hold to record" }) : null
|
|
2119
|
+
] }),
|
|
2120
|
+
blocked && blockedSheet && !blockedDismissed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2121
|
+
CaptureSheet,
|
|
2122
|
+
{
|
|
2123
|
+
open: true,
|
|
2124
|
+
onClose: () => setBlockedDismissed(true),
|
|
2125
|
+
body: blockedSheet.body,
|
|
2126
|
+
title: "Camera unavailable",
|
|
2127
|
+
actions: blockedSheet.actions
|
|
2128
|
+
}
|
|
2129
|
+
),
|
|
2130
|
+
media && viewerKey !== null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2131
|
+
MediaViewer,
|
|
2132
|
+
{
|
|
2133
|
+
items: media.items,
|
|
2134
|
+
initialKey: viewerKey,
|
|
2135
|
+
keysDisabled: editTarget !== null,
|
|
2136
|
+
onClose: () => setViewerKey(null),
|
|
2137
|
+
onDelete: media.onDelete ? (item) => {
|
|
2138
|
+
media.onDelete?.(item.key);
|
|
2139
|
+
invalidateMedia(item.key);
|
|
2140
|
+
} : void 0,
|
|
2141
|
+
onEdit: media.onReplacePhoto ? (item) => {
|
|
2142
|
+
const url = getMediaUrl(item);
|
|
2143
|
+
if (url) setEditTarget({ key: item.key, url });
|
|
2144
|
+
} : void 0
|
|
2145
|
+
}
|
|
2146
|
+
),
|
|
2147
|
+
media?.onReplacePhoto && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2148
|
+
ImageEditSheet,
|
|
2149
|
+
{
|
|
2150
|
+
open: editTarget !== null,
|
|
2151
|
+
src: editTarget?.url ?? null,
|
|
2152
|
+
onClose: () => setEditTarget(null),
|
|
2153
|
+
onSave: (blob) => {
|
|
2154
|
+
const target = editTarget;
|
|
2155
|
+
setEditTarget(null);
|
|
2156
|
+
setViewerKey(null);
|
|
2157
|
+
if (target) {
|
|
2158
|
+
media.onReplacePhoto?.(target.key, blob);
|
|
2159
|
+
invalidateMedia(target.key);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
),
|
|
2164
|
+
slots.overlays
|
|
2165
|
+
] });
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// src/components/CaptureExpandingField.tsx
|
|
2169
|
+
var import_react9 = require("react");
|
|
2170
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2171
|
+
function CaptureExpandingField({
|
|
2172
|
+
label,
|
|
2173
|
+
placeholder,
|
|
2174
|
+
icon,
|
|
2175
|
+
onCommit,
|
|
2176
|
+
defaultExpanded = false,
|
|
2177
|
+
disabled = false,
|
|
2178
|
+
currentValue = null,
|
|
2179
|
+
className
|
|
2180
|
+
}) {
|
|
2181
|
+
const [expanded, setExpanded] = (0, import_react9.useState)(defaultExpanded);
|
|
2182
|
+
const [draft, setDraft] = (0, import_react9.useState)("");
|
|
2183
|
+
const inputRef = (0, import_react9.useRef)(null);
|
|
2184
|
+
const draftRef = (0, import_react9.useRef)(draft);
|
|
2185
|
+
draftRef.current = draft;
|
|
2186
|
+
const onCommitRef = (0, import_react9.useRef)(onCommit);
|
|
2187
|
+
onCommitRef.current = onCommit;
|
|
2188
|
+
(0, import_react9.useEffect)(() => {
|
|
2189
|
+
return () => {
|
|
2190
|
+
const trimmed = draftRef.current.trim();
|
|
2191
|
+
if (trimmed) onCommitRef.current(trimmed);
|
|
2192
|
+
};
|
|
2193
|
+
}, []);
|
|
2194
|
+
const commit = (0, import_react9.useCallback)(() => {
|
|
2195
|
+
const trimmed = draftRef.current.trim();
|
|
2196
|
+
if (trimmed) onCommitRef.current(trimmed);
|
|
2197
|
+
setDraft("");
|
|
2198
|
+
}, []);
|
|
2199
|
+
const open = (0, import_react9.useCallback)(() => {
|
|
2200
|
+
setExpanded(true);
|
|
2201
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
2202
|
+
}, []);
|
|
2203
|
+
const close = (0, import_react9.useCallback)(() => {
|
|
2204
|
+
commit();
|
|
2205
|
+
setExpanded(false);
|
|
2206
|
+
}, [commit]);
|
|
2207
|
+
if (!expanded) {
|
|
2208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2209
|
+
"button",
|
|
2210
|
+
{
|
|
2211
|
+
type: "button",
|
|
2212
|
+
onClick: open,
|
|
2213
|
+
disabled,
|
|
2214
|
+
"aria-label": label,
|
|
2215
|
+
"aria-expanded": false,
|
|
2216
|
+
className: cn(
|
|
2217
|
+
"flex h-9 max-w-[62vw] items-center gap-1.5 rounded-full px-3.5",
|
|
2218
|
+
"bg-black/45 text-[13px] font-semibold text-white backdrop-blur-md",
|
|
2219
|
+
"transition-colors hover:bg-black/60 active:bg-black/70",
|
|
2220
|
+
disabled && "opacity-40",
|
|
2221
|
+
className
|
|
2222
|
+
),
|
|
2223
|
+
children: [
|
|
2224
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "shrink-0", children: icon }) : null,
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate", children: currentValue?.trim() || label })
|
|
2226
|
+
]
|
|
2227
|
+
}
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2231
|
+
"div",
|
|
2232
|
+
{
|
|
2233
|
+
className: cn(
|
|
2234
|
+
"flex h-9 w-[min(78vw,320px)] items-center gap-1.5 rounded-full px-3",
|
|
2235
|
+
"bg-black/60 backdrop-blur-md",
|
|
2236
|
+
className
|
|
2237
|
+
),
|
|
2238
|
+
children: [
|
|
2239
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "shrink-0 text-white/70", children: icon }) : null,
|
|
2240
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2241
|
+
"input",
|
|
2242
|
+
{
|
|
2243
|
+
ref: inputRef,
|
|
2244
|
+
value: draft,
|
|
2245
|
+
onChange: (e) => setDraft(e.target.value),
|
|
2246
|
+
onBlur: close,
|
|
2247
|
+
onKeyDown: (e) => {
|
|
2248
|
+
if (e.key === "Enter") {
|
|
2249
|
+
commit();
|
|
2250
|
+
e.target.blur();
|
|
2251
|
+
}
|
|
2252
|
+
if (e.key === "Escape") {
|
|
2253
|
+
setDraft("");
|
|
2254
|
+
setExpanded(false);
|
|
2255
|
+
}
|
|
2256
|
+
},
|
|
2257
|
+
placeholder: placeholder ?? label,
|
|
2258
|
+
"aria-label": label,
|
|
2259
|
+
enterKeyHint: "done",
|
|
2260
|
+
autoCapitalize: "characters",
|
|
2261
|
+
autoCorrect: "off",
|
|
2262
|
+
spellCheck: false,
|
|
2263
|
+
className: cn(
|
|
2264
|
+
"min-w-0 flex-1 bg-transparent text-base text-white",
|
|
2265
|
+
"placeholder:text-white/40 focus:outline-none"
|
|
2266
|
+
)
|
|
2267
|
+
}
|
|
2268
|
+
),
|
|
2269
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2270
|
+
"button",
|
|
2271
|
+
{
|
|
2272
|
+
type: "button",
|
|
2273
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2274
|
+
onClick: () => {
|
|
2275
|
+
setDraft("");
|
|
2276
|
+
setExpanded(false);
|
|
2277
|
+
},
|
|
2278
|
+
"aria-label": `Close ${label}`,
|
|
2279
|
+
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-white/70 hover:bg-white/10 hover:text-white",
|
|
2280
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2281
|
+
"svg",
|
|
2282
|
+
{
|
|
2283
|
+
viewBox: "0 0 24 24",
|
|
2284
|
+
fill: "none",
|
|
2285
|
+
stroke: "currentColor",
|
|
2286
|
+
strokeWidth: "2.5",
|
|
2287
|
+
strokeLinecap: "round",
|
|
2288
|
+
className: "h-3.5 w-3.5",
|
|
2289
|
+
"aria-hidden": "true",
|
|
2290
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M18 6 6 18M6 6l12 12" })
|
|
2291
|
+
}
|
|
2292
|
+
)
|
|
2293
|
+
}
|
|
2294
|
+
)
|
|
2295
|
+
]
|
|
2296
|
+
}
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
// src/components/CameraFeed.tsx
|
|
2301
|
+
var import_react10 = require("react");
|
|
2302
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1644
2303
|
function CameraFeed({
|
|
1645
2304
|
engine,
|
|
1646
2305
|
mirror = false
|
|
1647
2306
|
}) {
|
|
1648
2307
|
const { stream, videoRef } = engine;
|
|
1649
|
-
(0,
|
|
2308
|
+
(0, import_react10.useEffect)(() => {
|
|
1650
2309
|
const video = videoRef.current;
|
|
1651
2310
|
if (!video) return;
|
|
1652
2311
|
if (video.srcObject !== stream) video.srcObject = stream;
|
|
@@ -1654,7 +2313,7 @@ function CameraFeed({
|
|
|
1654
2313
|
video.srcObject = null;
|
|
1655
2314
|
};
|
|
1656
2315
|
}, [stream, videoRef]);
|
|
1657
|
-
return /* @__PURE__ */ (0,
|
|
2316
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1658
2317
|
"video",
|
|
1659
2318
|
{
|
|
1660
2319
|
ref: videoRef,
|
|
@@ -1672,7 +2331,7 @@ function CameraFeed({
|
|
|
1672
2331
|
}
|
|
1673
2332
|
|
|
1674
2333
|
// src/engine/useDefaultEngine.ts
|
|
1675
|
-
var
|
|
2334
|
+
var import_react11 = require("react");
|
|
1676
2335
|
var RECORDING_MIME_LADDER = [
|
|
1677
2336
|
'video/mp4;codecs="avc1.42E01E,mp4a.40.2"',
|
|
1678
2337
|
"video/mp4",
|
|
@@ -1702,18 +2361,18 @@ function useDefaultCaptureEngine(options) {
|
|
|
1702
2361
|
facingMode: initialFacing = "environment",
|
|
1703
2362
|
photoQuality = 0.92
|
|
1704
2363
|
} = options;
|
|
1705
|
-
const [stream, setStream] = (0,
|
|
1706
|
-
const [blocked, setBlocked] = (0,
|
|
1707
|
-
const [facing, setFacing] = (0,
|
|
1708
|
-
const [multipleCameras, setMultipleCameras] = (0,
|
|
1709
|
-
const [recording, setRecording] = (0,
|
|
1710
|
-
const [recordElapsedSeconds, setRecordElapsedSeconds] = (0,
|
|
1711
|
-
const videoRef = (0,
|
|
1712
|
-
const streamRef = (0,
|
|
1713
|
-
const recorderRef = (0,
|
|
1714
|
-
const callbacksRef = (0,
|
|
2364
|
+
const [stream, setStream] = (0, import_react11.useState)(null);
|
|
2365
|
+
const [blocked, setBlocked] = (0, import_react11.useState)(null);
|
|
2366
|
+
const [facing, setFacing] = (0, import_react11.useState)(initialFacing);
|
|
2367
|
+
const [multipleCameras, setMultipleCameras] = (0, import_react11.useState)(false);
|
|
2368
|
+
const [recording, setRecording] = (0, import_react11.useState)(false);
|
|
2369
|
+
const [recordElapsedSeconds, setRecordElapsedSeconds] = (0, import_react11.useState)(0);
|
|
2370
|
+
const videoRef = (0, import_react11.useRef)(null);
|
|
2371
|
+
const streamRef = (0, import_react11.useRef)(null);
|
|
2372
|
+
const recorderRef = (0, import_react11.useRef)(null);
|
|
2373
|
+
const callbacksRef = (0, import_react11.useRef)({ onPhoto, onVideo, onFiles });
|
|
1715
2374
|
callbacksRef.current = { onPhoto, onVideo, onFiles };
|
|
1716
|
-
(0,
|
|
2375
|
+
(0, import_react11.useEffect)(() => {
|
|
1717
2376
|
let cancelled = false;
|
|
1718
2377
|
let acquired = null;
|
|
1719
2378
|
if (typeof navigator === "undefined" || !navigator.mediaDevices) {
|
|
@@ -1756,7 +2415,7 @@ function useDefaultCaptureEngine(options) {
|
|
|
1756
2415
|
setStream(null);
|
|
1757
2416
|
};
|
|
1758
2417
|
}, [facing]);
|
|
1759
|
-
(0,
|
|
2418
|
+
(0, import_react11.useEffect)(() => {
|
|
1760
2419
|
return () => {
|
|
1761
2420
|
const r = recorderRef.current;
|
|
1762
2421
|
if (r) {
|
|
@@ -1766,7 +2425,7 @@ function useDefaultCaptureEngine(options) {
|
|
|
1766
2425
|
}
|
|
1767
2426
|
};
|
|
1768
2427
|
}, []);
|
|
1769
|
-
const onCapturePhoto = (0,
|
|
2428
|
+
const onCapturePhoto = (0, import_react11.useCallback)(
|
|
1770
2429
|
(opts) => {
|
|
1771
2430
|
const video = videoRef.current;
|
|
1772
2431
|
if (!video || video.videoWidth === 0) return;
|
|
@@ -1806,7 +2465,7 @@ function useDefaultCaptureEngine(options) {
|
|
|
1806
2465
|
},
|
|
1807
2466
|
[photoQuality]
|
|
1808
2467
|
);
|
|
1809
|
-
const onStartRecording = (0,
|
|
2468
|
+
const onStartRecording = (0, import_react11.useCallback)(() => {
|
|
1810
2469
|
const base = streamRef.current;
|
|
1811
2470
|
if (!base || recorderRef.current) return;
|
|
1812
2471
|
void (async () => {
|
|
@@ -1873,11 +2532,11 @@ function useDefaultCaptureEngine(options) {
|
|
|
1873
2532
|
setRecording(true);
|
|
1874
2533
|
})();
|
|
1875
2534
|
}, [withAudio]);
|
|
1876
|
-
const onStopRecording = (0,
|
|
2535
|
+
const onStopRecording = (0, import_react11.useCallback)(() => {
|
|
1877
2536
|
const r = recorderRef.current;
|
|
1878
2537
|
if (r && r.recorder.state !== "inactive") r.recorder.stop();
|
|
1879
2538
|
}, []);
|
|
1880
|
-
const onUpload = (0,
|
|
2539
|
+
const onUpload = (0, import_react11.useCallback)(() => {
|
|
1881
2540
|
const input = document.createElement("input");
|
|
1882
2541
|
input.type = "file";
|
|
1883
2542
|
input.accept = "image/*,video/*";
|
|
@@ -1888,10 +2547,10 @@ function useDefaultCaptureEngine(options) {
|
|
|
1888
2547
|
};
|
|
1889
2548
|
input.click();
|
|
1890
2549
|
}, []);
|
|
1891
|
-
const onFlipCamera = (0,
|
|
2550
|
+
const onFlipCamera = (0, import_react11.useCallback)(() => {
|
|
1892
2551
|
setFacing((f) => f === "environment" ? "user" : "environment");
|
|
1893
2552
|
}, []);
|
|
1894
|
-
return (0,
|
|
2553
|
+
return (0, import_react11.useMemo)(
|
|
1895
2554
|
() => ({
|
|
1896
2555
|
stream,
|
|
1897
2556
|
videoRef,
|