UNPKG

@kortexa-ai/react-multimodal

Version:

A set of react components and hooks to help with multimodal input

1,005 lines (998 loc) 34.6 kB
var de = Object.defineProperty; var le = (l, n, r) => n in l ? de(l, n, { enumerable: !0, configurable: !0, writable: !0, value: r }) : l[n] = r; var oe = (l, n, r) => le(l, typeof n != "symbol" ? n + "" : n, r); import { createContext as q, useContext as ee, useState as F, useRef as R, useEffect as x, useCallback as d, useMemo as j } from "react"; import { jsx as J } from "react/jsx-runtime"; import { Hands as ue } from "@mediapipe/hands"; const re = q( void 0 ); re.displayName = "kortexa.ai:composite-media"; const Re = () => { const l = ee(re); if (!l) throw new Error( "useCompositeMedia must be used within a CompositeMediaProvider" ); return l; }, ne = q( void 0 ); ne.displayName = "kortexa.ai:microphone"; const ge = 16e3, ie = "kortexa-audio-processor", fe = ` class AudioProcessor extends AudioWorkletProcessor { constructor() { super(); this.bufferSize = 512; this.buffer = new Float32Array(this.bufferSize); this.offset = 0; } process(inputs, outputs) { const input = inputs[0]?.[0]; if (!input?.length) return true; for (let i = 0; i < input.length; i++) { this.buffer[this.offset++] = input[i]; if (this.offset === this.bufferSize) { this.port.postMessage(this.buffer); this.offset = 0; } } return true; } } registerProcessor('${ie}', AudioProcessor); `; function he() { const l = new Blob([fe], { type: "application/javascript; charset=utf-8" }); return URL.createObjectURL(l); } function me({ sampleRate: l = ge, onData: n, onError: r }) { const [e, t] = F(!1), h = R(!1), o = R(void 0), m = R(void 0), y = R(void 0), H = R(void 0), L = R(n), g = R(r); x(() => { L.current = n, g.current = r; }, [n, r]); const C = d(() => { var D, A, a; if (!h.current) { h.current = !0; try { m.current && (m.current.getTracks().forEach((S) => S.stop()), m.current = void 0), H.current && (H.current.disconnect(), H.current = void 0), y.current && (y.current.port.onmessage = null, y.current.disconnect(), y.current = void 0), ((D = o.current) == null ? void 0 : D.state) !== "closed" && ((A = o.current) == null || A.close(), o.current = void 0); } catch (u) { console.error("Error closing audio context:", u), (a = g.current) == null || a.call(g, "Failed to close audio context"); } finally { t(!1), h.current = !1; } } }, []), E = d(async () => { var D; e && C(); try { (!o.current || o.current.state === "closed") && (o.current = new AudioContext({ sampleRate: l })), o.current.state === "suspended" && await o.current.resume(), await o.current.audioWorklet.addModule( // `data:application/javascript;base64,${btoa(WORKLET_CODE)}` he() ), m.current = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: !0, noiseSuppression: !0 } }), H.current = o.current.createMediaStreamSource( m.current ), y.current = new AudioWorkletNode( o.current, ie ), y.current.port.onmessage = (A) => { var a; (a = L.current) == null || a.call(L, new Float32Array(A.data)); }, H.current.connect(y.current), y.current.connect(o.current.destination), t(!0); } catch (A) { console.error("Error starting audio:", A), (D = g.current) == null || D.call(g, "Failed to access microphone"), C(); } }, [l, C, e]); return x(() => C, [C]), { isRecording: e, start: E, stop: C }; } class ce { constructor() { oe(this, "listeners", /* @__PURE__ */ new Map()); } addListener(n, r) { this.listeners.has(n) || this.listeners.set(n, /* @__PURE__ */ new Map()), this.listeners.get(n).set(r.id, r); } removeListener(n, r) { const e = this.listeners.get(n); e && e.delete(r); } async dispatch(n, r) { const e = this.listeners.get(n); if (!e) return; const t = []; e.forEach((h) => { try { const m = h.listener(r); m instanceof Promise && t.push(m); } catch (o) { console.error(`Error in ${String(n)} listener ${h.id}:`, o); } }), t.length > 0 && await Promise.all(t); } clear(n) { n ? this.listeners.delete(n) : this.listeners.clear(); } getListenerCount(n) { var r; return ((r = this.listeners.get(n)) == null ? void 0 : r.size) ?? 0; } } const P = new ce(); function ve({ children: l, ...n }) { const { isRecording: r, start: e, stop: t } = me({ ...n, onData: (a) => { var u; (u = n.onData) == null || u.call(n, a), P.dispatch("data", a); }, onError: (a) => { var u; (u = n.onError) == null || u.call(n, a), P.dispatch("error", a); } }), h = d(async () => { try { r || (await e(), await P.dispatch("start")); } catch (a) { let u = "Failed to start microphone"; throw a instanceof Error ? u += ": " + a.message : u += ": " + String(a), P.dispatch("error", u), a; } }, [r, e]), o = d(() => { try { r && (P.dispatch("stop"), t()); } catch { P.dispatch("error", "Failed to stop microphone"); } }, [r, t]), m = d((a) => { const u = `kortexa-microphone-data-${Date.now()}`; return P.addListener("data", { id: u, listener: a }), u; }, []), y = d((a) => { P.removeListener("data", a); }, []), H = d((a) => { const u = `kortexa-microphone-start-${Date.now()}`; return P.addListener("start", { id: u, listener: a }), u; }, []), L = d((a) => { P.removeListener("start", a); }, []), g = d((a) => { const u = `kortexa-microphone-stop-${Date.now()}`; return P.addListener("stop", { id: u, listener: a }), u; }, []), C = d((a) => { P.removeListener("stop", a); }, []), E = d((a) => { const u = `kortexa-microphone-error-${Date.now()}`; return P.addListener("error", { id: u, listener: a }), u; }, []), D = d((a) => { P.removeListener("error", a); }, []); x(() => () => { P.clear(); }, []); const A = j(() => ({ isRecording: r, start: h, stop: o, addDataListener: m, removeDataListener: y, addStartListener: H, removeStartListener: L, addStopListener: g, removeStopListener: C, addErrorListener: E, removeErrorListener: D }), [ r, h, o, m, y, H, L, g, C, E, D ]); return /* @__PURE__ */ J(ne.Provider, { value: A, children: l }); } function Me() { const l = ee(ne); if (!l) throw new Error("useMicrophone must be used within MicrophoneProvider"); return l; } const se = q( void 0 ); se.displayName = "kortexa.ai:camera"; const pe = ({ defaultFacingMode: l = "user", defaultDeviceId: n, requestedWidth: r, requestedHeight: e, requestedAspectRatio: t, onStream: h, onError: o } = {}) => { const [m, y] = F(!1), [H, L] = F(null), [g, C] = F(l), [E, D] = F( [] ), [A, a] = F( n ), u = d(async () => { try { const f = (await navigator.mediaDevices.enumerateDevices()).filter( (M) => M.kind === "videoinput" ); if (D(f), f.length > 0 && !A) { const M = f.find((T) => (T.label.toLowerCase().includes("front") ? "user" : T.label.toLowerCase().includes("back") ? "environment" : null) === g); a( (M == null ? void 0 : M.deviceId) || f[0].deviceId ); } } catch (O) { console.error("Error enumerating camera devices:", O), o == null || o("Error enumerating camera devices."); } }, [A, g, o]); x(() => { u(); }, []), x(() => { typeof window < "u" && "ontouchstart" in window && l === "user" && !n && C("environment"); }, [l, n]); const S = d( async (O) => { const f = O || A; if (!f && E.length === 0 && (await u(), E.length === 0 || !f)) { console.error("No camera devices available or selected."), o == null || o("No camera devices available or selected."); return; } try { const M = { deviceId: f ? { exact: f } : void 0, facingMode: f ? void 0 : g // Only use facingMode if no specific deviceId }; r && (M.width = { ideal: r }), e && (M.height = { ideal: e }), t && (M.aspectRatio = { ideal: t }); const T = { video: Object.keys(M).length > 0 ? M : !0 }, s = await navigator.mediaDevices.getUserMedia( T ); L(s), y(!0), h == null || h(s), f && a(f); } catch (M) { if (console.error("Error accessing camera:", M), o == null || o(`Error accessing camera: ${M.message}`), f && E.length > 1) { const T = E.find( (s) => s.deviceId !== f ); T && (console.log( `Attempting to start with device: ${T.label || T.deviceId}` ), await S(T.deviceId)); } } }, [ A, g, h, o, E, u, r, e, t ] ), k = d(() => { H && (H.getTracks().forEach((O) => O.stop()), L(null), y(!1), h == null || h()); }, [H, h]), b = d(() => { m ? k() : S(); }, [m, k, S]), I = d(async () => { if (!m) return; const O = g === "user" ? "environment" : "user"; C(O), k(); const f = E.find((M) => { const T = M.label.toLowerCase(); return O === "user" ? T.includes("front") || !T.includes("back") : O === "environment" ? T.includes("back") : !1; }); f ? (a(f.deviceId), await S(f.deviceId)) : E.length > 0 ? (a(void 0), await S()) : o == null || o("No alternative camera found for flipping."); }, [m, g, k, S, E, o]), _ = d( async (O) => { O === A && m || (k(), a(O), await S(O)); }, [A, m, k, S] ); return x(() => () => { H && H.getTracks().forEach((O) => O.stop()); }, [H]), { stream: H, isRecording: m, facingMode: g, availableDevices: E, currentDeviceId: A, start: S, stop: k, toggle: b, flip: I, setDevice: _, getDevices: u }; }, $ = new ce(); function He({ children: l, ...n }) { const { stream: r, isRecording: e, facingMode: t, availableDevices: h, currentDeviceId: o, start: m, stop: y, flip: H, setDevice: L, getDevices: g } = pe({ ...n, onStream: (s) => { var v; (v = n.onStream) == null || v.call(n, s), $.dispatch("streamChanged", s); }, onError: (s) => { var v; (v = n.onError) == null || v.call(n, s), $.dispatch("error", s); } }), C = d(async () => { try { await m(), $.dispatch("started"); } catch (s) { let v = "Failed to start camera"; throw s instanceof Error ? v += ": " + s.message : v += ": " + String(s), $.dispatch("error", v), s; } }, [m]), E = d(() => { try { y(), $.dispatch("stopped"); } catch (s) { const v = `Failed to stop camera: ${s.message}`; $.dispatch("error", v); } }, [y]), D = d(async () => { try { await H(); } catch (s) { const v = `Failed to flip camera: ${s.message}`; $.dispatch("error", v); } }, [H]), A = d(async (s) => { try { await L(s); } catch (v) { const V = `Failed to set device: ${v.message}`; $.dispatch("error", V); } }, [L]); x(() => { $.dispatch("facingMode", t); }, [t]); const a = d((s) => { const v = `kortexa-camera-stream-${Date.now()}`, V = (N) => { s(N); }; return $.addListener("stream", { id: v, listener: V }), v; }, []), u = d((s) => { $.removeListener("stream", s); }, []), S = d((s) => { const v = `kortexa-camera-started-${Date.now()}`; return $.addListener("started", { id: v, listener: s }), v; }, []), k = d((s) => { $.removeListener("started", s); }, []), b = d((s) => { const v = `kortexa-camera-stopped-${Date.now()}`; return $.addListener("stopped", { id: v, listener: s }), v; }, []), I = d((s) => { $.removeListener("stopped", s); }, []), _ = d((s) => { const v = `kortexa-camera-facingMode-${Date.now()}`, V = (N) => { N !== void 0 && s(N); }; return $.addListener("facingMode", { id: v, listener: V }), v; }, []), O = d((s) => { $.removeListener("facingMode", s); }, []), f = d((s) => { const v = `kortexa-camera-error-${Date.now()}`, V = (N) => { N !== void 0 && s(N); }; return $.addListener("error", { id: v, listener: V }), v; }, []), M = d((s) => { $.removeListener("error", s); }, []); x(() => () => { $.clear(); }, []); const T = j(() => ({ isRecording: e, stream: r, facingMode: t, availableDevices: h, currentDeviceId: o, start: C, stop: E, toggle: () => e ? E() : C(), flip: D, setDevice: A, getDevices: g, addStreamListener: a, removeStreamListener: u, addStartListener: S, removeStartListener: k, addStopListener: b, removeStopListener: I, addFacingModeListener: _, removeFacingModeListener: O, addErrorListener: f, removeErrorListener: M }), [ e, r, t, h, o, C, E, D, A, g, a, u, S, k, b, I, _, O, f, M ]); return /* @__PURE__ */ J(se.Provider, { value: T, children: l }); } const Ee = () => { const l = ee(se); if (!l) throw new Error("useCamera must be used within a CameraProvider"); return l; }, ke = { staticImageMode: !1, maxNumHands: 2, modelComplexity: 1, minDetectionConfidence: 0.5, minTrackingConfidence: 0.5, selfieMode: !0 // Flip handedness for selfie view }; function we({ options: l, handsVersion: n = "0.4.1675469240", onInitialLoad: r, onHandsData: e, onError: t, onTrackingStarted: h, onTrackingStopped: o, onResults: m } = {}) { const y = R(null), H = R(null), L = R(null), g = R(!1), C = R(!1), [E, D] = F(!1), [A, a] = F(null), [u, S] = F(null), k = j( () => ({ ...ke, ...l }), [l] ), b = R(e); x(() => { b.current = e; }, [e]); const I = R(t); x(() => { I.current = t; }, [t]); const _ = R(h); x(() => { _.current = h; }, [h]); const O = R(o); x(() => { O.current = o; }, [o]); const f = R(m); x(() => { f.current = m; }, [m]); const M = R(r); x(() => { M.current = r; }, [r]); const T = R(/* @__PURE__ */ new Map()), s = R( /* @__PURE__ */ new Map() ), v = R(/* @__PURE__ */ new Map()), V = R(/* @__PURE__ */ new Map()), N = d( (c) => { const p = []; c.multiHandLandmarks && c.multiHandedness && c.multiHandLandmarks.forEach( (z, K) => { var i; const X = c.multiHandedness[K]; z && X && p.push({ landmarks: z, worldLandmarks: (i = c.multiHandWorldLandmarks) == null ? void 0 : i[K], handedness: [X] }); } ); const U = { detectedHands: p, imageDimensions: c.image ? { width: c.image.width, height: c.image.height } : void 0 }; if (a(U), b.current && b.current(U), T.current.forEach( (z, K) => z(U) ), f.current) try { f.current(p, c.image); } catch (z) { console.error( "[useHandsControl] Error in onResults callback:", z ); const K = z instanceof Error ? z.message : String(z); S(K), I.current && I.current(K), s.current.forEach( (X, i) => X(K) ); } !C.current && p.length > 0 && (C.current = !0, console.log( "[useHandsControl] processResults: Initial hand detection successful." ), M.current && M.current()); }, [ a, S, b, f, M, I, T, C ] ); x(() => { console.log( "[useHandsControl] useEffect[options, handsVersion]: Initializing MediaPipe Hands..." ); const c = new ue({ locateFile: (p) => (console.log( `[useHandsControl] locateFile: Attempting to locate ${p} using version ${n}` ), `https://cdn.jsdelivr.net/npm/@mediapipe/hands@${n}/${p}`) }); return c.setOptions(k), c.onResults(N), y.current = c, console.log( "[useHandsControl] MediaPipe Hands instance initialized with options:", k ), () => { y.current && (y.current.close().catch( (p) => console.error( "[useHandsControl] Error closing MediaPipe Hands:", p ) ), y.current = null, console.log( "[useHandsControl] MediaPipe Hands instance closed." )); }; }, [k, n, N]); const Q = d(async () => { if (!H.current || !y.current) { console.warn( "[useHandsControl] sendFrame: Video element or Hands instance not available. Cannot send frame." ); return; } if (H.current.paused || H.current.ended || H.current.readyState < HTMLMediaElement.HAVE_METADATA) { g.current && (L.current = requestAnimationFrame(Q)); return; } try { await y.current.send({ image: H.current }); } catch (c) { console.error( "[useHandsControl] sendFrame: Error sending frame to MediaPipe Hands:", c ); const p = c instanceof Error ? c.message : String(c); S(p), I.current && I.current(p), s.current.forEach( (U, z) => U(p) ); } g.current ? L.current = requestAnimationFrame(Q) : console.log( "[useHandsControl] sendFrame: Loop terminated because isTrackingRef.current is false." ); }, [ y, H, S, I, s ]), Z = d( async (c) => { if (!y.current) { console.error( "[useHandsControl] startTracking: Hands instance not initialized." ), S("Hands instance not initialized."), I.current && I.current("Hands instance not initialized."), s.current.forEach( (p, U) => p("Hands instance not initialized.") ); return; } if (g.current) { console.warn( "[useHandsControl] startTracking: Already tracking." ); return; } console.log( "[useHandsControl] startTracking: Video element set:", c ), H.current = c, C.current = !1, g.current = !0, console.log( "[useHandsControl] startTracking: Before setIsTracking(true). isTrackingRef.current:", g.current ), D(!0), L.current && cancelAnimationFrame(L.current), L.current = requestAnimationFrame(Q), console.log( "[useHandsControl] startTracking: Loop started. animationFrameIdRef:", L.current ), _.current && _.current(), v.current.forEach( (p, U) => p() ), S(null); }, [ Q, _, v, I, s, S ] ), B = d(() => { console.log( "[useHandsControl] stopTracking: Called. Current isTracking (state):", E, "isTrackingRef.current:", g.current ), !(!g.current && !E) && (g.current = !1, D(!1), console.log( "[useHandsControl] stopTracking: After updates. isTracking (state should be false soon):", E, "isTrackingRef.current:", g.current, "animationFrameIdRef:", L.current ), L.current && (cancelAnimationFrame(L.current), console.log( "[useHandsControl] stopTracking: Cleared animation frame", L.current ), L.current = null), O.current && O.current(), V.current.forEach( (c, p) => c() ), console.log("[useHandsControl] stopTracking: Completed.")); }, [E, O, V]); return { isTracking: E, handsData: A, error: u, startTracking: Z, stopTracking: B, getHandsInstance: () => y.current, addHandsDataListener: (c) => { const p = Date.now().toString() + Math.random().toString(); return T.current.set(p, c), p; }, removeHandsDataListener: (c) => { T.current.delete(c); }, addErrorListener: (c) => { const p = Date.now().toString() + Math.random().toString(); return s.current.set(p, c), p; }, removeErrorListener: (c) => { s.current.delete(c); }, addStartListener: (c) => { const p = Date.now().toString() + Math.random().toString(); return v.current.set(p, c), p; }, removeStartListener: (c) => { v.current.delete(c); }, addStopListener: (c) => { const p = Date.now().toString() + Math.random().toString(); return V.current.set(p, c), p; }, removeStopListener: (c) => { V.current.delete(c); } }; } const ae = q( {} ); ae.displayName = "kortexa.ai:hands-tracking"; function ye({ children: l, ...n }) { const r = we(n); return /* @__PURE__ */ J(ae.Provider, { value: r, children: l }); } const Le = () => { const l = ee(ae); if (l === null) throw new Error("useHands must be used within a HandsProvider"); return l; }, Ce = "proceed"; function Oe({ children: l, startBehavior: n }) { const r = Me(), e = Ee(), t = Le(), h = R(null), [o, m] = F(!1), [y, H] = F(!1), [L, g] = F(void 0), [C, E] = F(!1), [D, A] = F(void 0), [a, u] = F(void 0), [S, k] = F(void 0), [b, I] = F(!1), [_, O] = F(!1); x(() => { console.log("[MediaOrchestrator] Attaching mic error listener."); const i = r.addErrorListener((w) => { console.log("[MediaOrchestrator] Mic error received:", w), A(w); }); return () => { console.log("[MediaOrchestrator] Removing mic error listener."), r.removeErrorListener(i); }; }, [r]), x(() => { console.log("[MediaOrchestrator] Attaching cam error listener."); const i = e.addErrorListener((w) => { console.log("[MediaOrchestrator] Cam error received:", w), u(w); }); return () => { console.log("[MediaOrchestrator] Removing cam error listener."), e.removeErrorListener(i); }; }, [e]), x(() => { if (t) { console.log("[MediaOrchestrator] Attaching hands error listener."); const i = t.addErrorListener((w) => { console.log("[MediaOrchestrator] Hands error received:", w), k(w), I(!1); }); return () => { console.log("[MediaOrchestrator] Removing hands error listener."), t.removeErrorListener(i); }; } return () => { }; }, [t]); const f = j(() => r.isRecording, [r]), M = j(() => e.isRecording, [e]), T = j(() => (t == null ? void 0 : t.isTracking) ?? !1, [t]), s = j(() => f && M, [f, M]), v = j(() => { }, []), V = j(() => e.stream ?? void 0, [e]), N = j(() => e.facingMode, [e]), Q = j(() => (t == null ? void 0 : t.handsData) ?? void 0, [t]); console.log("[MediaProvider] InternalOrchestrator render. States: isAudioActive:", f, "isVideoActive:", M, "isHandTrackingActive:", T, "isStarting:", C, "isStartingHandsInternal:", b, "attemptedHandsStartInCycle:", o, "isVideoElementForHandsSet:", _); const Z = d((i) => { console.log("[MediaOrchestrator] setVideoElementForHands called with element:", i ? "VideoElement" : "null"), h.current = i, i ? O(!0) : (console.log("[MediaOrchestrator] Video element removed, resetting attemptedHandsStartInCycle."), m(!1), O(!1)); }, []), B = d(async () => { if (console.log(`[MediaOrchestrator] startHandsAsyncInternal: Entered. Pre-conditions: hands=${!!t}, videoElement=${!!h.current}, cam.isRecording=${e == null ? void 0 : e.isRecording}, stream=${!!(e != null && e.stream)}, !hands.isTracking=${!(t != null && t.isTracking)}`), !t || !h.current || !(e != null && e.isRecording) || !(e != null && e.stream)) { const w = "Pre-conditions not met for starting hand tracking (hands, videoElement, camera state)."; return console.warn("[MediaOrchestrator] startHandsAsyncInternal: Pre-conditions not met.", { hands: !!t, videoElement: !!h.current, camIsRecording: e == null ? void 0 : e.isRecording, stream: !!(e != null && e.stream) }), k(w), m(!0), Promise.reject(new Error(w)); } if (t.isTracking) return console.log("[MediaOrchestrator] startHandsAsyncInternal: Already tracking. Exiting."), Promise.resolve(); const i = h.current; console.log("[MediaOrchestrator] startHandsAsyncInternal: Setting attemptedHandsStartInCycle to true."), m(!0), k(void 0); try { console.log(`[MediaOrchestrator] startHandsAsyncInternal: Video element readyState: ${i.readyState}. Waiting for HAVE_METADATA if necessary.`), i.readyState < HTMLMediaElement.HAVE_METADATA && await new Promise((w, W) => { const G = () => { console.log("[MediaOrchestrator] startHandsAsyncInternal: videoEl loadedmetadata event triggered."), i.removeEventListener("loadedmetadata", G), i.removeEventListener("error", Y), w(); }, Y = (te) => { console.error("[MediaOrchestrator] startHandsAsyncInternal: videoEl error event triggered.", te), i.removeEventListener("loadedmetadata", G), i.removeEventListener("error", Y), W(new Error("Video element error during metadata load for hand tracking.")); }; i.addEventListener("loadedmetadata", G), i.addEventListener("error", Y); }), console.log("[MediaOrchestrator] startHandsAsyncInternal: Video ready. Calling hands.startTracking()."), await t.startTracking(i), console.log("[MediaOrchestrator] startHandsAsyncInternal: hands.startTracking() call completed."); } catch (w) { const W = w instanceof Error ? w.message : String(w); throw console.error("[MediaOrchestrator] startHandsAsyncInternal: Error during hand tracking start:", W), k(W), t != null && t.isTracking && console.warn("[MediaOrchestrator] startHandsAsyncInternal: hands.isTracking is true after a startTracking error. This might be unexpected."), w; } }, [t, e, k, m]), c = d(async () => { if (console.log(`[MediaOrchestrator] startMedia: Entered. isStarting: ${C}, isAudioActive: ${f}, isVideoActive: ${M}, hands.isTracking: ${t == null ? void 0 : t.isTracking}, videoElementSet: ${!!h.current}`), C) { console.log("[MediaOrchestrator] startMedia: Already starting. Exiting."); return; } E(!0), console.log("[MediaOrchestrator] startMedia: Set isStarting to true."), g(void 0), m(!1), H(!1), console.log("[MediaOrchestrator] startMedia: Reset attemptedHandsStartInCycle and userExplicitlyStoppedHands to false."), A(void 0), u(void 0), k(void 0); let i = f, w = M; if (r && !f) { console.log("[MediaOrchestrator] startMedia: Attempting to start Mic..."); try { await r.start(), i = !0, console.log("[MediaOrchestrator] startMedia: Mic start successful."); } catch { i = !1, console.error("[MediaOrchestrator] startMedia: Mic start failed."); } } if (e && !M) { console.log("[MediaOrchestrator] startMedia: Attempting to start Camera..."); try { await e.start(), w = !0, console.log("[MediaOrchestrator] startMedia: Camera start successful."); } catch { w = !1, console.error("[MediaOrchestrator] startMedia: Camera start failed."); } } if (n === "halt") { if (!i || !w) { const G = D || (i ? "" : "Failed to start"), Y = a || (w ? "" : "Failed to start"), te = `Media start halted: Mic ${i ? "OK" : `FAIL (${G})`}. Cam ${w ? "OK" : `FAIL (${Y})`}.`; console.warn("[MediaOrchestrator] startMedia: Halting due to mic/cam failure.", { localMicAttemptOk: i, localCamAttemptOk: w, micErrMsg: G, camErrMsg: Y }), g(te), i && r && r.stop(), w && e && e.stop(), E(!1), console.log("[MediaOrchestrator] startMedia: Set isStarting to false (halted)."); return; } } else { const W = []; if (i || W.push(`Mic: ${D || "Unknown"}`), w || W.push(`Cam: ${a || "Unknown"}`), W.length > 0) { const G = `Failed to start: ${W.join("; ")}. Others proceeded if successful.`; console.warn("[MediaOrchestrator] startMedia: Proceeding with errors.", { errorParts: W }), g(G); } } if (w && (e != null && e.isRecording) && e.stream && t && !t.isTracking && h.current && !o) { console.log("[MediaOrchestrator] startMedia: Conditions met to attempt starting hands via startHandsAsyncInternal."); try { await B(), console.log("[MediaOrchestrator] startMedia: startHandsAsyncInternal completed successfully."); } catch { console.error("[MediaOrchestrator] startMedia: startHandsAsyncInternal failed. Error should be in currentHandsError."), n === "halt" && (g(`Failed to start hand tracking. Halting media start. Error: ${S || "Unknown hands error"}`), console.warn("[MediaOrchestrator] startMedia: Hand tracking failed with startBehavior=halt. Media (mic/cam) might still be running.")); } } E(!1), console.log("[MediaOrchestrator] startMedia: Set isStarting to false (completed). Exiting startMedia."); }, [ r, e, t, n, f, M, C, h, o, B, D, a, S, A, u, k, g, E, m, H // Added setters for completeness though some might be stable ]); x(() => { e != null && e.isRecording && e.stream && t && !t.isTracking && h.current && !o && !y && (console.log("[MediaOrchestrator] useEffect[...]: Conditions met for auto-starting hands. Calling startHandsAsyncInternal."), B().catch(() => { console.error("[MediaOrchestrator] useEffect[...]: Error during automatic startHandsAsyncInternal. Error should be in currentHandsError."); })); }, [e == null ? void 0 : e.isRecording, e == null ? void 0 : e.stream, t, h, o, y, B]), x(() => { e != null && e.isRecording || (console.log("[MediaOrchestrator] useEffect[cam.isOn]: Camera is off, resetting attemptedHandsStartInCycle to false."), m(!1)); }, [e == null ? void 0 : e.isRecording, m]); const p = d(async () => { if (console.log(`[MediaOrchestrator] stopHands: Entered. hands.isTracking: ${t == null ? void 0 : t.isTracking}`), t && t.isTracking) try { await t.stopTracking(), console.log("[MediaOrchestrator] stopHands: hands.stopTracking() call completed."), H(!0); } catch (i) { const w = i instanceof Error ? i.message : String(i); console.error("[MediaOrchestrator] stopHands: Error during hand tracking stop:", w), k(w); } console.log("[MediaOrchestrator] stopHands: Resetting attemptedHandsStartInCycle to false."), m(!1), I(!1); }, [t, k, m]); x(() => { e && !e.isRecording && (t != null && t.isTracking && (console.log("[MediaOrchestrator] useEffect[...]: Camera turned off. Calling stopHands."), p()), console.log("[MediaOrchestrator] useEffect[...]: Camera off, resetting attemptedHandsStartInCycle."), m(!1)); }, [e, e == null ? void 0 : e.isRecording, t, p]); const U = d(() => { console.log(`[MediaOrchestrator] stopMedia: Entered. mic.isRecording: ${r == null ? void 0 : r.isRecording}, cam.isRecording: ${e == null ? void 0 : e.isRecording}`), r != null && r.isRecording && (console.log("[MediaOrchestrator] stopMedia: Stopping Mic."), r.stop()), e != null && e.isRecording && (console.log("[MediaOrchestrator] stopMedia: Stopping Camera. This should trigger useEffect to stop hands."), e.stop()), g(void 0), A(void 0), u(void 0), k(void 0), E(!1), console.log("[MediaOrchestrator] stopMedia: Exiting."); }, [r, e, g, A, u, k, E]), z = d(async () => { console.log(`[MediaOrchestrator] toggleMedia: Entered. isAudioActive: ${f}, isVideoActive: ${M}`), f && M ? (console.log("[MediaOrchestrator] toggleMedia: Media is active, calling stopMedia."), U()) : (console.log("[MediaOrchestrator] toggleMedia: Media is inactive, calling startMedia."), await c()), console.log("[MediaOrchestrator] toggleMedia: Exiting."); }, [f, M, c, U]), K = d(async () => { if (console.log(`[MediaOrchestrator] startHands: Entered. isStartingHandsInternal: ${b}, hands.isTracking: ${t == null ? void 0 : t.isTracking}`), b || t != null && t.isTracking) { console.log("[MediaOrchestrator] startHands: Already starting or tracking. Exiting."); return; } if (H(!1), console.log("[MediaOrchestrator] startHands: Setting isStartingHandsInternal to true."), I(!0), k(void 0), !t) { const i = "Hand tracking service not available."; console.warn("[MediaOrchestrator] startHands:", i), k(i), I(!1); return; } if (!(e != null && e.isRecording)) { const i = "Camera is not active. Cannot start hand tracking."; console.warn("[MediaOrchestrator] startHands:", i), k(i), I(!1); return; } if (!h.current) { const i = "Video element not set for hand tracking."; console.warn("[MediaOrchestrator] startHands:", i), k(i), I(!1), O(!1); return; } try { console.log("[MediaOrchestrator] startHands: Calling startHandsAsyncInternal."), await B(), console.log("[MediaOrchestrator] startHands: startHandsAsyncInternal completed successfully."); } catch { console.error("[MediaOrchestrator] startHands: startHandsAsyncInternal failed. Error should be in currentHandsError."); } finally { console.log("[MediaOrchestrator] startHands: Setting isStartingHandsInternal to false (completed attempt)."), I(!1); } console.log("[MediaOrchestrator] startHands: Exiting."); }, [t, e == null ? void 0 : e.isRecording, h, b, B, I, k]), X = j(() => ({ isAudioActive: f, isVideoActive: M, isHandTrackingActive: T, isMediaActive: s, audioStream: v, videoStream: V, videoFacingMode: N, currentHandsData: Q, audioError: D, videoError: a, handsError: S, mediaError: L, startMedia: c, stopMedia: U, toggleMedia: z, cam: e, mic: r, hands: t, setVideoElementForHands: Z, startHands: K, stopHands: p, isStartingMedia: C, isStartingHands: b, isVideoElementForHandsSet: _ }), [ f, M, T, s, v, V, N, Q, D, a, S, L, c, U, z, e, r, t, Z, K, p, C, b, _ ]); return /* @__PURE__ */ J(re.Provider, { value: X, children: l }); } function De({ children: l, microphoneProps: n, cameraProps: r, handsProps: e, startBehavior: t = Ce }) { return /* @__PURE__ */ J(ve, { ...n, children: /* @__PURE__ */ J(He, { ...r, children: /* @__PURE__ */ J(ye, { ...e, children: /* @__PURE__ */ J( Oe, { startBehavior: t, children: l } ) }) }) }); } export { He as CameraProvider, De as CompositeMediaProvider, ye as HandsProvider, ve as MicrophoneProvider, Ee as useCamera, pe as useCameraDevice, Re as useCompositeMedia, Le as useHands, we as useHandsControl, Me as useMicrophone, me as useMicrophoneDevice }; //# sourceMappingURL=react-multimodal.js.map