@ai-matrx/capture 0.2.1 → 0.3.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 +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 +224 -1
- package/dist/react.d.ts +224 -1
- 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,
|
|
@@ -1643,15 +1647,670 @@ function CameraCapture({
|
|
|
1643
1647
|
] });
|
|
1644
1648
|
}
|
|
1645
1649
|
|
|
1646
|
-
// 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
|
|
1647
1655
|
var import_react6 = require("react");
|
|
1648
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 = [
|
|
2017
|
+
...coreRail.map((a) => blocked ? { ...a, disabled: true } : a),
|
|
2018
|
+
...slots.railActions ?? []
|
|
2019
|
+
];
|
|
2020
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "absolute inset-0 select-none overflow-hidden bg-black", children: [
|
|
2021
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "absolute inset-0", children: preview }),
|
|
2022
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(GridOverlay, { visible: gridOn && !blocked }),
|
|
2023
|
+
aspect !== "full" && !blocked && !engine.recording && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2024
|
+
"div",
|
|
2025
|
+
{
|
|
2026
|
+
"aria-hidden": true,
|
|
2027
|
+
className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center",
|
|
2028
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2029
|
+
"div",
|
|
2030
|
+
{
|
|
2031
|
+
className: "shadow-[0_0_0_9999px_rgba(0,0,0,0.45)]",
|
|
2032
|
+
style: {
|
|
2033
|
+
aspectRatio: aspect === "1:1" ? "1 / 1" : aspect === "4:3" ? "3 / 4" : "9 / 16",
|
|
2034
|
+
width: aspect === "16:9" ? "100%" : void 0,
|
|
2035
|
+
height: aspect === "16:9" ? void 0 : "70%",
|
|
2036
|
+
maxWidth: "100%",
|
|
2037
|
+
maxHeight: "100%"
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
)
|
|
2041
|
+
}
|
|
2042
|
+
),
|
|
2043
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CountdownOverlay, { seconds: countdown }),
|
|
2044
|
+
!controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2045
|
+
"div",
|
|
2046
|
+
{
|
|
2047
|
+
className: "pointer-events-none absolute inset-x-0 top-0 z-20 px-2",
|
|
2048
|
+
style: safeTop,
|
|
2049
|
+
children: [
|
|
2050
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-h-[44px] items-center gap-2 py-1.5", children: [
|
|
2051
|
+
onClose ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2052
|
+
"button",
|
|
2053
|
+
{
|
|
2054
|
+
type: "button",
|
|
2055
|
+
onClick: onClose,
|
|
2056
|
+
"aria-label": "Close camera",
|
|
2057
|
+
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",
|
|
2058
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react5.X, { className: "h-6 w-6" })
|
|
2059
|
+
}
|
|
2060
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "h-10 w-10 shrink-0" }),
|
|
2061
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "pointer-events-auto flex min-w-0 flex-1 justify-center", children: slots.topEntry }),
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "h-10 w-10 shrink-0" })
|
|
2063
|
+
] }),
|
|
2064
|
+
slots.topCenter ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "pointer-events-none pb-1 text-center", children: slots.topCenter }) : null
|
|
2065
|
+
]
|
|
2066
|
+
}
|
|
2067
|
+
),
|
|
2068
|
+
slots.statusChips ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2069
|
+
"div",
|
|
2070
|
+
{
|
|
2071
|
+
className: "pointer-events-none absolute inset-x-0 z-20 flex flex-col items-center gap-1.5 px-3",
|
|
2072
|
+
style: {
|
|
2073
|
+
// Clears the top row AND the topCenter line beneath it — chips
|
|
2074
|
+
// rendered at the label's own height read as a collision.
|
|
2075
|
+
top: `calc(env(safe-area-inset-top, 0px) + ${controlsHidden ? 12 : slots.topCenter ? 92 : 64}px)`
|
|
2076
|
+
},
|
|
2077
|
+
children: slots.statusChips
|
|
2078
|
+
}
|
|
2079
|
+
) : null,
|
|
2080
|
+
!controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2081
|
+
"div",
|
|
2082
|
+
{
|
|
2083
|
+
className: "pointer-events-none absolute right-1 z-20 flex justify-end",
|
|
2084
|
+
style: { top: `calc(env(safe-area-inset-top, 0px) + 64px)` },
|
|
2085
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CaptureRail, { actions: railActions })
|
|
2086
|
+
}
|
|
2087
|
+
),
|
|
2088
|
+
!controlsHidden && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "absolute inset-x-0 bottom-0 z-20", style: safeBottom, children: [
|
|
2089
|
+
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)(
|
|
2090
|
+
CaptureFilmstrip,
|
|
2091
|
+
{
|
|
2092
|
+
items: media.items,
|
|
2093
|
+
onOpen: (item) => setViewerKey(item.key)
|
|
2094
|
+
}
|
|
2095
|
+
) }) : null,
|
|
2096
|
+
slots.aboveShutter ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "px-2 pb-1.5", children: slots.aboveShutter }) : null,
|
|
2097
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between px-3 pb-2", children: [
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex w-[86px] justify-start", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2099
|
+
"button",
|
|
2100
|
+
{
|
|
2101
|
+
type: "button",
|
|
2102
|
+
onClick: cloud.onOpenLibrary,
|
|
2103
|
+
"aria-label": "Your media \u2014 library and upload",
|
|
2104
|
+
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",
|
|
2105
|
+
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" }) })
|
|
2106
|
+
}
|
|
2107
|
+
) }),
|
|
2108
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2109
|
+
HoldShutter,
|
|
2110
|
+
{
|
|
2111
|
+
recording: engine.recording,
|
|
2112
|
+
elapsedSeconds: engine.recordElapsedSeconds,
|
|
2113
|
+
...maxRecordSeconds !== void 0 ? { maxSeconds: maxRecordSeconds } : {},
|
|
2114
|
+
disabled: shutterDisabled || blocked,
|
|
2115
|
+
...onRecordIntent ? { onPressStart: onRecordIntent } : {},
|
|
2116
|
+
onPhoto,
|
|
2117
|
+
onStartRecording: engine.onStartRecording,
|
|
2118
|
+
onStopRecording: engine.onStopRecording
|
|
2119
|
+
}
|
|
2120
|
+
),
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex w-[86px] justify-end", children: slots.shutterTrailing })
|
|
2122
|
+
] }),
|
|
2123
|
+
!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
|
|
2124
|
+
] }),
|
|
2125
|
+
blocked && blockedSheet && !blockedDismissed && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2126
|
+
CaptureSheet,
|
|
2127
|
+
{
|
|
2128
|
+
open: true,
|
|
2129
|
+
onClose: () => setBlockedDismissed(true),
|
|
2130
|
+
body: blockedSheet.body,
|
|
2131
|
+
title: "Camera unavailable",
|
|
2132
|
+
actions: blockedSheet.actions
|
|
2133
|
+
}
|
|
2134
|
+
),
|
|
2135
|
+
media && viewerKey !== null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2136
|
+
MediaViewer,
|
|
2137
|
+
{
|
|
2138
|
+
items: media.items,
|
|
2139
|
+
initialKey: viewerKey,
|
|
2140
|
+
keysDisabled: editTarget !== null,
|
|
2141
|
+
onClose: () => setViewerKey(null),
|
|
2142
|
+
onDelete: media.onDelete ? (item) => {
|
|
2143
|
+
media.onDelete?.(item.key);
|
|
2144
|
+
invalidateMedia(item.key);
|
|
2145
|
+
} : void 0,
|
|
2146
|
+
onEdit: media.onReplacePhoto ? (item) => {
|
|
2147
|
+
const url = getMediaUrl(item);
|
|
2148
|
+
if (url) setEditTarget({ key: item.key, url });
|
|
2149
|
+
} : void 0
|
|
2150
|
+
}
|
|
2151
|
+
),
|
|
2152
|
+
media?.onReplacePhoto && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2153
|
+
ImageEditSheet,
|
|
2154
|
+
{
|
|
2155
|
+
open: editTarget !== null,
|
|
2156
|
+
src: editTarget?.url ?? null,
|
|
2157
|
+
onClose: () => setEditTarget(null),
|
|
2158
|
+
onSave: (blob) => {
|
|
2159
|
+
const target = editTarget;
|
|
2160
|
+
setEditTarget(null);
|
|
2161
|
+
setViewerKey(null);
|
|
2162
|
+
if (target) {
|
|
2163
|
+
media.onReplacePhoto?.(target.key, blob);
|
|
2164
|
+
invalidateMedia(target.key);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
),
|
|
2169
|
+
slots.overlays
|
|
2170
|
+
] });
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
// src/components/CaptureExpandingField.tsx
|
|
2174
|
+
var import_react9 = require("react");
|
|
2175
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2176
|
+
function CaptureExpandingField({
|
|
2177
|
+
label,
|
|
2178
|
+
placeholder,
|
|
2179
|
+
icon,
|
|
2180
|
+
onCommit,
|
|
2181
|
+
defaultExpanded = false,
|
|
2182
|
+
disabled = false,
|
|
2183
|
+
currentValue = null,
|
|
2184
|
+
className
|
|
2185
|
+
}) {
|
|
2186
|
+
const [expanded, setExpanded] = (0, import_react9.useState)(defaultExpanded);
|
|
2187
|
+
const [draft, setDraft] = (0, import_react9.useState)("");
|
|
2188
|
+
const inputRef = (0, import_react9.useRef)(null);
|
|
2189
|
+
const draftRef = (0, import_react9.useRef)(draft);
|
|
2190
|
+
draftRef.current = draft;
|
|
2191
|
+
const onCommitRef = (0, import_react9.useRef)(onCommit);
|
|
2192
|
+
onCommitRef.current = onCommit;
|
|
2193
|
+
(0, import_react9.useEffect)(() => {
|
|
2194
|
+
return () => {
|
|
2195
|
+
const trimmed = draftRef.current.trim();
|
|
2196
|
+
if (trimmed) onCommitRef.current(trimmed);
|
|
2197
|
+
};
|
|
2198
|
+
}, []);
|
|
2199
|
+
const commit = (0, import_react9.useCallback)(() => {
|
|
2200
|
+
const trimmed = draftRef.current.trim();
|
|
2201
|
+
if (trimmed) onCommitRef.current(trimmed);
|
|
2202
|
+
setDraft("");
|
|
2203
|
+
}, []);
|
|
2204
|
+
const open = (0, import_react9.useCallback)(() => {
|
|
2205
|
+
setExpanded(true);
|
|
2206
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
2207
|
+
}, []);
|
|
2208
|
+
const close = (0, import_react9.useCallback)(() => {
|
|
2209
|
+
commit();
|
|
2210
|
+
setExpanded(false);
|
|
2211
|
+
}, [commit]);
|
|
2212
|
+
if (!expanded) {
|
|
2213
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2214
|
+
"button",
|
|
2215
|
+
{
|
|
2216
|
+
type: "button",
|
|
2217
|
+
onClick: open,
|
|
2218
|
+
disabled,
|
|
2219
|
+
"aria-label": label,
|
|
2220
|
+
"aria-expanded": false,
|
|
2221
|
+
className: cn(
|
|
2222
|
+
"flex h-9 max-w-[62vw] items-center gap-1.5 rounded-full px-3.5",
|
|
2223
|
+
"bg-black/45 text-[13px] font-semibold text-white backdrop-blur-md",
|
|
2224
|
+
"transition-colors hover:bg-black/60 active:bg-black/70",
|
|
2225
|
+
disabled && "opacity-40",
|
|
2226
|
+
className
|
|
2227
|
+
),
|
|
2228
|
+
children: [
|
|
2229
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "shrink-0", children: icon }) : null,
|
|
2230
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate", children: currentValue?.trim() || label })
|
|
2231
|
+
]
|
|
2232
|
+
}
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2236
|
+
"div",
|
|
2237
|
+
{
|
|
2238
|
+
className: cn(
|
|
2239
|
+
"flex h-9 w-[min(78vw,320px)] items-center gap-1.5 rounded-full px-3",
|
|
2240
|
+
"bg-black/60 backdrop-blur-md",
|
|
2241
|
+
className
|
|
2242
|
+
),
|
|
2243
|
+
children: [
|
|
2244
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "shrink-0 text-white/70", children: icon }) : null,
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2246
|
+
"input",
|
|
2247
|
+
{
|
|
2248
|
+
ref: inputRef,
|
|
2249
|
+
value: draft,
|
|
2250
|
+
onChange: (e) => setDraft(e.target.value),
|
|
2251
|
+
onBlur: close,
|
|
2252
|
+
onKeyDown: (e) => {
|
|
2253
|
+
if (e.key === "Enter") {
|
|
2254
|
+
commit();
|
|
2255
|
+
e.target.blur();
|
|
2256
|
+
}
|
|
2257
|
+
if (e.key === "Escape") {
|
|
2258
|
+
setDraft("");
|
|
2259
|
+
setExpanded(false);
|
|
2260
|
+
}
|
|
2261
|
+
},
|
|
2262
|
+
placeholder: placeholder ?? label,
|
|
2263
|
+
"aria-label": label,
|
|
2264
|
+
enterKeyHint: "done",
|
|
2265
|
+
autoCapitalize: "characters",
|
|
2266
|
+
autoCorrect: "off",
|
|
2267
|
+
spellCheck: false,
|
|
2268
|
+
className: cn(
|
|
2269
|
+
"min-w-0 flex-1 bg-transparent text-base text-white",
|
|
2270
|
+
"placeholder:text-white/40 focus:outline-none"
|
|
2271
|
+
)
|
|
2272
|
+
}
|
|
2273
|
+
),
|
|
2274
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2275
|
+
"button",
|
|
2276
|
+
{
|
|
2277
|
+
type: "button",
|
|
2278
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2279
|
+
onClick: () => {
|
|
2280
|
+
setDraft("");
|
|
2281
|
+
setExpanded(false);
|
|
2282
|
+
},
|
|
2283
|
+
"aria-label": `Close ${label}`,
|
|
2284
|
+
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-white/70 hover:bg-white/10 hover:text-white",
|
|
2285
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2286
|
+
"svg",
|
|
2287
|
+
{
|
|
2288
|
+
viewBox: "0 0 24 24",
|
|
2289
|
+
fill: "none",
|
|
2290
|
+
stroke: "currentColor",
|
|
2291
|
+
strokeWidth: "2.5",
|
|
2292
|
+
strokeLinecap: "round",
|
|
2293
|
+
className: "h-3.5 w-3.5",
|
|
2294
|
+
"aria-hidden": "true",
|
|
2295
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M18 6 6 18M6 6l12 12" })
|
|
2296
|
+
}
|
|
2297
|
+
)
|
|
2298
|
+
}
|
|
2299
|
+
)
|
|
2300
|
+
]
|
|
2301
|
+
}
|
|
2302
|
+
);
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
// src/components/CameraFeed.tsx
|
|
2306
|
+
var import_react10 = require("react");
|
|
2307
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1649
2308
|
function CameraFeed({
|
|
1650
2309
|
engine,
|
|
1651
2310
|
mirror = false
|
|
1652
2311
|
}) {
|
|
1653
2312
|
const { stream, videoRef } = engine;
|
|
1654
|
-
(0,
|
|
2313
|
+
(0, import_react10.useEffect)(() => {
|
|
1655
2314
|
const video = videoRef.current;
|
|
1656
2315
|
if (!video) return;
|
|
1657
2316
|
if (video.srcObject !== stream) video.srcObject = stream;
|
|
@@ -1659,7 +2318,7 @@ function CameraFeed({
|
|
|
1659
2318
|
video.srcObject = null;
|
|
1660
2319
|
};
|
|
1661
2320
|
}, [stream, videoRef]);
|
|
1662
|
-
return /* @__PURE__ */ (0,
|
|
2321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1663
2322
|
"video",
|
|
1664
2323
|
{
|
|
1665
2324
|
ref: videoRef,
|
|
@@ -1677,7 +2336,7 @@ function CameraFeed({
|
|
|
1677
2336
|
}
|
|
1678
2337
|
|
|
1679
2338
|
// src/engine/useDefaultEngine.ts
|
|
1680
|
-
var
|
|
2339
|
+
var import_react11 = require("react");
|
|
1681
2340
|
var RECORDING_MIME_LADDER = [
|
|
1682
2341
|
'video/mp4;codecs="avc1.42E01E,mp4a.40.2"',
|
|
1683
2342
|
"video/mp4",
|
|
@@ -1707,18 +2366,18 @@ function useDefaultCaptureEngine(options) {
|
|
|
1707
2366
|
facingMode: initialFacing = "environment",
|
|
1708
2367
|
photoQuality = 0.92
|
|
1709
2368
|
} = options;
|
|
1710
|
-
const [stream, setStream] = (0,
|
|
1711
|
-
const [blocked, setBlocked] = (0,
|
|
1712
|
-
const [facing, setFacing] = (0,
|
|
1713
|
-
const [multipleCameras, setMultipleCameras] = (0,
|
|
1714
|
-
const [recording, setRecording] = (0,
|
|
1715
|
-
const [recordElapsedSeconds, setRecordElapsedSeconds] = (0,
|
|
1716
|
-
const videoRef = (0,
|
|
1717
|
-
const streamRef = (0,
|
|
1718
|
-
const recorderRef = (0,
|
|
1719
|
-
const callbacksRef = (0,
|
|
2369
|
+
const [stream, setStream] = (0, import_react11.useState)(null);
|
|
2370
|
+
const [blocked, setBlocked] = (0, import_react11.useState)(null);
|
|
2371
|
+
const [facing, setFacing] = (0, import_react11.useState)(initialFacing);
|
|
2372
|
+
const [multipleCameras, setMultipleCameras] = (0, import_react11.useState)(false);
|
|
2373
|
+
const [recording, setRecording] = (0, import_react11.useState)(false);
|
|
2374
|
+
const [recordElapsedSeconds, setRecordElapsedSeconds] = (0, import_react11.useState)(0);
|
|
2375
|
+
const videoRef = (0, import_react11.useRef)(null);
|
|
2376
|
+
const streamRef = (0, import_react11.useRef)(null);
|
|
2377
|
+
const recorderRef = (0, import_react11.useRef)(null);
|
|
2378
|
+
const callbacksRef = (0, import_react11.useRef)({ onPhoto, onVideo, onFiles });
|
|
1720
2379
|
callbacksRef.current = { onPhoto, onVideo, onFiles };
|
|
1721
|
-
(0,
|
|
2380
|
+
(0, import_react11.useEffect)(() => {
|
|
1722
2381
|
let cancelled = false;
|
|
1723
2382
|
let acquired = null;
|
|
1724
2383
|
if (typeof navigator === "undefined" || !navigator.mediaDevices) {
|
|
@@ -1761,7 +2420,7 @@ function useDefaultCaptureEngine(options) {
|
|
|
1761
2420
|
setStream(null);
|
|
1762
2421
|
};
|
|
1763
2422
|
}, [facing]);
|
|
1764
|
-
(0,
|
|
2423
|
+
(0, import_react11.useEffect)(() => {
|
|
1765
2424
|
return () => {
|
|
1766
2425
|
const r = recorderRef.current;
|
|
1767
2426
|
if (r) {
|
|
@@ -1771,7 +2430,7 @@ function useDefaultCaptureEngine(options) {
|
|
|
1771
2430
|
}
|
|
1772
2431
|
};
|
|
1773
2432
|
}, []);
|
|
1774
|
-
const onCapturePhoto = (0,
|
|
2433
|
+
const onCapturePhoto = (0, import_react11.useCallback)(
|
|
1775
2434
|
(opts) => {
|
|
1776
2435
|
const video = videoRef.current;
|
|
1777
2436
|
if (!video || video.videoWidth === 0) return;
|
|
@@ -1811,7 +2470,7 @@ function useDefaultCaptureEngine(options) {
|
|
|
1811
2470
|
},
|
|
1812
2471
|
[photoQuality]
|
|
1813
2472
|
);
|
|
1814
|
-
const onStartRecording = (0,
|
|
2473
|
+
const onStartRecording = (0, import_react11.useCallback)(() => {
|
|
1815
2474
|
const base = streamRef.current;
|
|
1816
2475
|
if (!base || recorderRef.current) return;
|
|
1817
2476
|
void (async () => {
|
|
@@ -1878,11 +2537,11 @@ function useDefaultCaptureEngine(options) {
|
|
|
1878
2537
|
setRecording(true);
|
|
1879
2538
|
})();
|
|
1880
2539
|
}, [withAudio]);
|
|
1881
|
-
const onStopRecording = (0,
|
|
2540
|
+
const onStopRecording = (0, import_react11.useCallback)(() => {
|
|
1882
2541
|
const r = recorderRef.current;
|
|
1883
2542
|
if (r && r.recorder.state !== "inactive") r.recorder.stop();
|
|
1884
2543
|
}, []);
|
|
1885
|
-
const onUpload = (0,
|
|
2544
|
+
const onUpload = (0, import_react11.useCallback)(() => {
|
|
1886
2545
|
const input = document.createElement("input");
|
|
1887
2546
|
input.type = "file";
|
|
1888
2547
|
input.accept = "image/*,video/*";
|
|
@@ -1893,10 +2552,10 @@ function useDefaultCaptureEngine(options) {
|
|
|
1893
2552
|
};
|
|
1894
2553
|
input.click();
|
|
1895
2554
|
}, []);
|
|
1896
|
-
const onFlipCamera = (0,
|
|
2555
|
+
const onFlipCamera = (0, import_react11.useCallback)(() => {
|
|
1897
2556
|
setFacing((f) => f === "environment" ? "user" : "environment");
|
|
1898
2557
|
}, []);
|
|
1899
|
-
return (0,
|
|
2558
|
+
return (0, import_react11.useMemo)(
|
|
1900
2559
|
() => ({
|
|
1901
2560
|
stream,
|
|
1902
2561
|
videoRef,
|