@ohm_studio/sdk 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/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { OHMCoreClient } from "@ohm_studio/sdk-core";
2
- import type { OHMClientOptions } from "@ohm_studio/sdk-core";
2
+ import type { OHMInit } from "@ohm_studio/sdk-core";
3
3
  export * from "@ohm_studio/sdk-core";
4
- export { Recorder, isRecordingSupported } from "./recorder";
5
- export type { RecorderOptions, RecorderState } from "./recorder";
4
+ export { Recorder, RecorderError, isRecordingSupported } from "./recorder";
5
+ export type { RecorderOptions, RecorderState, RecorderErrorCode, MicrophoneInfo, } from "./recorder";
6
6
  /**
7
7
  * OHM Studio SDK for JavaScript / TypeScript.
8
8
  *
@@ -22,7 +22,7 @@ export type { RecorderOptions, RecorderState } from "./recorder";
22
22
  * });
23
23
  */
24
24
  export declare class OHM extends OHMCoreClient {
25
- constructor(opts: OHMClientOptions);
25
+ constructor(init: OHMInit);
26
26
  protected runMultipart<T>(opts: {
27
27
  path: string;
28
28
  file: any;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEjE;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,GAAI,SAAQ,aAAa;gBACxB,IAAI,EAAE,gBAAgB;cAIlB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE;QACpC,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,GAAG,CAAC;QACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,CAAC,CAAC;IAKd;;;;;;;;OAQG;cACa,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC;CAuBrB;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACV,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,GAAI,SAAQ,aAAa;gBACxB,IAAI,EAAE,OAAO;cAIT,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE;QACpC,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,GAAG,CAAC;QACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,CAAC,CAAC;IAKd;;;;;;;;OAQG;cACa,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,QAAQ,CAAC;CAuBrB;AAED,eAAe,GAAG,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { OHMCoreClient } from "@ohm_studio/sdk-core";
2
2
  export * from "@ohm_studio/sdk-core";
3
- export { Recorder, isRecordingSupported } from "./recorder";
3
+ export { Recorder, RecorderError, isRecordingSupported } from "./recorder";
4
4
  /**
5
5
  * OHM Studio SDK for JavaScript / TypeScript.
6
6
  *
@@ -20,8 +20,8 @@ export { Recorder, isRecordingSupported } from "./recorder";
20
20
  * });
21
21
  */
22
22
  export class OHM extends OHMCoreClient {
23
- constructor(opts) {
24
- super(opts);
23
+ constructor(init) {
24
+ super(init);
25
25
  }
26
26
  async runMultipart(opts) {
27
27
  const fd = await this.buildMultipartBody(opts.file, opts.fields);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAG5D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,GAAI,SAAQ,aAAa;IACpC,YAAY,IAAsB;QAChC,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAES,KAAK,CAAC,YAAY,CAAI,IAI/B;QACC,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAa,CAAC;QAC7E,OAAO,IAAI,CAAC,UAAU,CAAI,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;;;;OAQG;IACO,KAAK,CAAC,kBAAkB,CAChC,IAAa,EACb,MAA+B;QAE/B,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;YACxD,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;YAC/D,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,QAAQ,IAAK,IAAY,EAAE,CAAC;YACzE,sCAAsC;YACtC,MAAM,CAAC,GAAG,IAA6D,CAAC;YACxE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;gBAChC,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,0BAA0B;aAC3C,CAAC,CAAC;YACH,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,WAAW,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YAClD,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAQ3E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,GAAI,SAAQ,aAAa;IACpC,YAAY,IAAa;QACvB,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAES,KAAK,CAAC,YAAY,CAAI,IAI/B;QACC,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAa,CAAC;QAC7E,OAAO,IAAI,CAAC,UAAU,CAAI,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;;;;OAQG;IACO,KAAK,CAAC,kBAAkB,CAChC,IAAa,EACb,MAA+B;QAE/B,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;YACxD,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;YAC/D,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,QAAQ,IAAK,IAAY,EAAE,CAAC;YACzE,sCAAsC;YACtC,MAAM,CAAC,GAAG,IAA6D,CAAC;YACxE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;gBAChC,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,0BAA0B;aAC3C,CAAC,CAAC;YACH,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,WAAW,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YAClD,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAED,eAAe,GAAG,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { AudioExtractInput, AudioExtractResult, ExtractInput, ExtractResult, SummarizeInput, SummarizeResult } from "@ohm_studio/sdk-core";
3
3
  import { OHM } from "../index";
4
+ import { RecorderError, type RecorderOptions, type RecorderState } from "../recorder";
4
5
  export declare function OhmProvider({ client, children, }: {
5
6
  client: OHM;
6
7
  children: ReactNode;
@@ -33,4 +34,49 @@ export declare function useOhmSummarize(): {
33
34
  error: Error | null;
34
35
  isPending: boolean;
35
36
  };
37
+ export interface UseRecorderOptions extends Omit<RecorderOptions, "onStateChange" | "onLevel" | "onError"> {
38
+ /**
39
+ * If supplied, on stop the recorded blob is automatically sent to
40
+ * `ohm.audio.extract({ apiSlug, file })` and exposed on `data`/`transcript`.
41
+ * Requires <OhmProvider client={...}> above.
42
+ */
43
+ apiSlug?: string;
44
+ /** Pass-through inputs for auto-extract. */
45
+ extractInputs?: Record<string, unknown>;
46
+ /** Optional language hint. */
47
+ extractLanguage?: string;
48
+ }
49
+ export interface UseRecorderReturn<T> {
50
+ state: RecorderState;
51
+ isRecording: boolean;
52
+ isPaused: boolean;
53
+ /** Linear RMS 0–1 — wire to a VU meter. */
54
+ level: number;
55
+ /** Recorded duration in seconds (live, ticks every 250ms). */
56
+ durationSec: number;
57
+ /** Resulting blob after stop. */
58
+ blob: Blob | null;
59
+ /** Recorder error, if any. */
60
+ error: RecorderError | null;
61
+ start: () => Promise<void>;
62
+ stop: () => Promise<Blob | null>;
63
+ pause: () => void;
64
+ resume: () => void;
65
+ cancel: () => void;
66
+ reset: () => void;
67
+ extracting: boolean;
68
+ transcript: string | null;
69
+ data: T | null;
70
+ extractError: Error | null;
71
+ }
72
+ /**
73
+ * `useRecorder` — the one-call recorder hook for clinical apps.
74
+ *
75
+ * const r = useRecorder({ apiSlug: "opd-clinic", silenceAutoStop: { ms: 6000 } });
76
+ * <button onClick={r.isRecording ? r.stop : r.start}>
77
+ * {r.isRecording ? `Stop (${r.durationSec.toFixed(0)}s)` : "Record"}
78
+ * </button>
79
+ * {r.transcript && <pre>{r.transcript}</pre>}
80
+ */
81
+ export declare function useRecorder<T = Record<string, unknown>>(opts?: UseRecorderOptions): UseRecorderReturn<T>;
36
82
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAa/B,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,SAAS,CAAC;CACrB,uFAEA;AAoDD,wBAAgB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAxCQ,KAAK,GAAG,IAAI;eACR,OAAO;EA4CnB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAjDQ,KAAK,GAAG,IAAI;eACR,OAAO;EAqDnB;AAED,wBAAgB,eAAe;;;;;WAxDtB,KAAK,GAAG,IAAI;eACR,OAAO;EA2DnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAEL,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAarB,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,SAAS,CAAC;CACrB,uFAEA;AAoDD,wBAAgB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAxCQ,KAAK,GAAG,IAAI;eACR,OAAO;EA4CnB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;;;;;WAjDQ,KAAK,GAAG,IAAI;eACR,OAAO;EAqDnB;AAED,wBAAgB,eAAe;;;;;WAxDtB,KAAK,GAAG,IAAI;eACR,OAAO;EA2DnB;AAID,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;IACtE;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,8BAA8B;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,8BAA8B;IAC9B,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrD,IAAI,GAAE,kBAAuB,GAC5B,iBAAiB,CAAC,CAAC,CAAC,CAmJtB"}
@@ -1,4 +1,5 @@
1
- import { createContext, createElement, useCallback, useContext, useState, } from "react";
1
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
2
+ import { Recorder, } from "../recorder";
2
3
  /**
3
4
  * React hooks for @ohm_studio/sdk. Mirrors `@ohm_studio/sdk-react-native/react` so a
4
5
  * snippet copies cleanly between web and mobile codebases.
@@ -55,4 +56,157 @@ export function useOhmAudioExtract(opts) {
55
56
  export function useOhmSummarize() {
56
57
  return useOhmMutation((c, input) => c.summarize(input));
57
58
  }
59
+ /**
60
+ * `useRecorder` — the one-call recorder hook for clinical apps.
61
+ *
62
+ * const r = useRecorder({ apiSlug: "opd-clinic", silenceAutoStop: { ms: 6000 } });
63
+ * <button onClick={r.isRecording ? r.stop : r.start}>
64
+ * {r.isRecording ? `Stop (${r.durationSec.toFixed(0)}s)` : "Record"}
65
+ * </button>
66
+ * {r.transcript && <pre>{r.transcript}</pre>}
67
+ */
68
+ export function useRecorder(opts = {}) {
69
+ const client = useContext(OhmContext); // optional; only used for auto-extract
70
+ const recRef = useRef(null);
71
+ const [state, setState] = useState("idle");
72
+ const [level, setLevel] = useState(0);
73
+ const [durationSec, setDurationSec] = useState(0);
74
+ const [blob, setBlob] = useState(null);
75
+ const [error, setError] = useState(null);
76
+ const [extracting, setExtracting] = useState(false);
77
+ const [transcript, setTranscript] = useState(null);
78
+ const [data, setData] = useState(null);
79
+ const [extractError, setExtractError] = useState(null);
80
+ // Stable options ref so the recorder is built once.
81
+ const optsRef = useRef(opts);
82
+ optsRef.current = opts;
83
+ const ensureRecorder = useCallback(() => {
84
+ if (recRef.current)
85
+ return recRef.current;
86
+ const r = new Recorder({
87
+ ...optsRef.current,
88
+ onStateChange: (s) => setState(s),
89
+ onLevel: (rms) => setLevel(rms),
90
+ onError: (e) => setError(e),
91
+ });
92
+ recRef.current = r;
93
+ return r;
94
+ }, []);
95
+ // Tick duration while recording.
96
+ useEffect(() => {
97
+ if (state !== "recording")
98
+ return;
99
+ const id = setInterval(() => {
100
+ const r = recRef.current;
101
+ if (r)
102
+ setDurationSec(r.getDuration() / 1000);
103
+ }, 250);
104
+ return () => clearInterval(id);
105
+ }, [state]);
106
+ // Cleanup on unmount.
107
+ useEffect(() => {
108
+ return () => {
109
+ try {
110
+ recRef.current?.cancel();
111
+ }
112
+ catch {
113
+ /* ignore */
114
+ }
115
+ recRef.current = null;
116
+ };
117
+ }, []);
118
+ const start = useCallback(async () => {
119
+ setError(null);
120
+ setBlob(null);
121
+ setTranscript(null);
122
+ setData(null);
123
+ setExtractError(null);
124
+ setDurationSec(0);
125
+ const r = ensureRecorder();
126
+ await r.start();
127
+ }, [ensureRecorder]);
128
+ const stop = useCallback(async () => {
129
+ const r = recRef.current;
130
+ if (!r)
131
+ return null;
132
+ const out = await r.stop();
133
+ setBlob(out);
134
+ setDurationSec(r.getDuration() / 1000);
135
+ if (optsRef.current.apiSlug && client) {
136
+ setExtracting(true);
137
+ try {
138
+ const res = await client.audio.extract({
139
+ apiSlug: optsRef.current.apiSlug,
140
+ file: out,
141
+ inputs: optsRef.current.extractInputs,
142
+ language: optsRef.current.extractLanguage,
143
+ });
144
+ setTranscript(res.transcript ?? null);
145
+ setData(res.data);
146
+ }
147
+ catch (e) {
148
+ setExtractError(e);
149
+ }
150
+ finally {
151
+ setExtracting(false);
152
+ }
153
+ }
154
+ return out;
155
+ }, [client]);
156
+ const pause = useCallback(() => recRef.current?.pause(), []);
157
+ const resume = useCallback(() => recRef.current?.resume(), []);
158
+ const cancel = useCallback(() => recRef.current?.cancel(), []);
159
+ const reset = useCallback(() => {
160
+ try {
161
+ recRef.current?.cancel();
162
+ }
163
+ catch {
164
+ /* ignore */
165
+ }
166
+ recRef.current = null;
167
+ setState("idle");
168
+ setLevel(0);
169
+ setDurationSec(0);
170
+ setBlob(null);
171
+ setError(null);
172
+ setTranscript(null);
173
+ setData(null);
174
+ setExtractError(null);
175
+ }, []);
176
+ return useMemo(() => ({
177
+ state,
178
+ isRecording: state === "recording",
179
+ isPaused: state === "paused",
180
+ level,
181
+ durationSec,
182
+ blob,
183
+ error,
184
+ start,
185
+ stop,
186
+ pause,
187
+ resume,
188
+ cancel,
189
+ reset,
190
+ extracting,
191
+ transcript,
192
+ data,
193
+ extractError,
194
+ }), [
195
+ state,
196
+ level,
197
+ durationSec,
198
+ blob,
199
+ error,
200
+ start,
201
+ stop,
202
+ pause,
203
+ resume,
204
+ cancel,
205
+ reset,
206
+ extracting,
207
+ transcript,
208
+ data,
209
+ extractError,
210
+ ]);
211
+ }
58
212
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,UAAU,EACV,QAAQ,GAET,MAAM,OAAO,CAAC;AAWf;;;;;;;GAOG;AAEH,MAAM,UAAU,GAAG,aAAa,CAAa,IAAI,CAAC,CAAC;AAEnD,MAAM,UAAU,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GAIT;IACC,OAAO,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAQD,SAAS,cAAc,CACrB,EAAoD;IAEpD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAyB;QACzD,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,KAAK;KACjB,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,WAAW,CAC7B,KAAK,EAAE,KAAa,EAAE,EAAE;QACtB,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACrC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YACvD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,EAAE,CAAC,CACb,CAAC;IACF,OAAO;QACL,GAAG,KAAK;QACR,WAAW;QACX,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxB,KAAK,WAAW,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CACV,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAA8B,IAE1D;IACC,OAAO,cAAc,CAGnB,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAI,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAA8B,IAE/D;IACC,OAAO,cAAc,CAGnB,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAI,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,cAAc,CAAkC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAClE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CACnB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
@@ -1,30 +1,105 @@
1
1
  /**
2
- * Browser MediaRecorder wrapper. Hides the boilerplate of permissions,
3
- * stream tracks, chunk collection, and cleanup so customers can ship a
4
- * working recorder UI in three lines:
2
+ * Browser audio recorder for clinical consults.
5
3
  *
6
- * const rec = new Recorder();
4
+ * Wraps `MediaRecorder` + `getUserMedia` + `AudioContext` + `Wake Lock`
5
+ * with clinically-tuned defaults (16 kHz mono, echo-cancel, noise-suppress,
6
+ * auto-gain) and a codec cascade that works on iOS Safari, Firefox, and
7
+ * every Chromium browser.
8
+ *
9
+ * const rec = new Recorder({
10
+ * onLevel: (rms) => setVu(rms),
11
+ * onError: (e) => toast(e.message),
12
+ * maxDurationMs: 10 * 60_000, // hard cap at 10 min
13
+ * silenceAutoStop: { ms: 6000 }, // stop after 6 s of silence
14
+ * wakeLock: true,
15
+ * });
7
16
  * await rec.start();
8
- * const blob = await rec.stop(); // → ready to pass to ohm.audio.extract
17
+ * const blob = await rec.stop(); // → ohm.audio.extract({ file: blob, ... })
9
18
  *
10
- * Designed for Vite / Next.js / CRA / any modern browser. SSR-safe — the
11
- * `MediaRecorder` API is only touched inside method bodies, so importing
12
- * this module doesn't break server bundles.
19
+ * SSR-safe every browser-only API is touched inside method bodies, never
20
+ * at module scope. Importing this file from a Next.js server bundle is OK.
13
21
  */
22
+ export type RecorderState = "idle" | "starting" | "recording" | "paused" | "stopping";
23
+ export type RecorderErrorCode = "NotSupported" | "PermissionDenied" | "NoMicrophone" | "MicrophoneBusy" | "OverConstrained" | "DeviceLost" | "InvalidState" | "Unknown";
24
+ export declare class RecorderError extends Error {
25
+ code: RecorderErrorCode;
26
+ cause?: unknown;
27
+ constructor(code: RecorderErrorCode, message: string, cause?: unknown);
28
+ }
14
29
  export interface RecorderOptions {
15
30
  /**
16
- * MIME type to request from the browser. Default `audio/webm;codecs=opus`
17
- * which is universally supported and small. Pass another value if your
18
- * extraction pipeline needs a specific format.
31
+ * MIME type to request. Default: best supported from the cascade
32
+ * `audio/webm;codecs=opus` `audio/mp4;codecs=mp4a.40.2`
33
+ * `audio/ogg;codecs=opus` `audio/mp4` `audio/webm`.
34
+ * Pass an explicit value only if your pipeline needs a specific format.
19
35
  */
20
36
  mimeType?: string;
21
- /** Audio constraints passed to `getUserMedia`. */
37
+ /**
38
+ * Audio bitrate in bits-per-second. Default 32 000 (32 kbps Opus is
39
+ * plenty for clinical speech and keeps files tiny).
40
+ */
41
+ audioBitsPerSecond?: number;
42
+ /**
43
+ * Selected microphone deviceId. Use `Recorder.listMicrophones()` to enumerate.
44
+ */
45
+ deviceId?: string;
46
+ /**
47
+ * Override `getUserMedia` constraints entirely. If omitted, the SDK applies
48
+ * clinically-tuned defaults: 16 kHz mono, echo cancellation on,
49
+ * noise suppression on, auto-gain on.
50
+ */
22
51
  audioConstraints?: MediaTrackConstraints;
23
- /** Called whenever recording state changes (idle / recording / stopping). */
52
+ /**
53
+ * If true (default), sets `sampleRate: { ideal: 16000 }`, `channelCount: 1`,
54
+ * and the three voice-cleanup flags. STT models expect 16 kHz mono.
55
+ */
56
+ clinicalDefaults?: boolean;
57
+ /**
58
+ * Emit a `dataavailable` chunk every N ms. Required for streaming uploads.
59
+ * Default 0 (one final chunk on stop).
60
+ */
61
+ timesliceMs?: number;
62
+ /**
63
+ * Hard cap — auto-stop after this many ms of recording. Default off.
64
+ */
65
+ maxDurationMs?: number;
66
+ /**
67
+ * Auto-stop after sustained silence. RMS below `threshold` for `ms`
68
+ * continuous milliseconds triggers stop. Default off.
69
+ */
70
+ silenceAutoStop?: {
71
+ /** ms of continuous silence before auto-stop. Default 6000. */
72
+ ms?: number;
73
+ /** Linear-RMS threshold 0–1. Default 0.012 (≈ -38 dBFS). */
74
+ threshold?: number;
75
+ };
76
+ /**
77
+ * Hold a screen Wake Lock while recording, so phones / tablets don't dim.
78
+ * Default false. Falls back silently when the API isn't available.
79
+ */
80
+ wakeLock?: boolean;
81
+ /**
82
+ * Pause recording when the tab becomes hidden. Default false.
83
+ * (We never auto-resume — the user has to come back and tap.)
84
+ */
85
+ pauseOnHidden?: boolean;
86
+ /** Recording state changes. */
24
87
  onStateChange?: (state: RecorderState) => void;
88
+ /** Periodic linear RMS level 0–1 (≈ 60 Hz). Wire to a VU meter. */
89
+ onLevel?: (rms: number) => void;
90
+ /** Each `dataavailable` chunk. Useful for streaming uploads. */
91
+ onChunk?: (chunk: Blob) => void;
92
+ /** All recorder errors flow through here in addition to throwing on the awaited call. */
93
+ onError?: (err: RecorderError) => void;
94
+ /** Microphone disconnected (cable unplugged, OS revoked permission). */
95
+ onDeviceLost?: () => void;
96
+ }
97
+ export interface MicrophoneInfo {
98
+ deviceId: string;
99
+ label: string;
100
+ groupId: string;
25
101
  }
26
- export type RecorderState = "idle" | "recording" | "stopping";
27
- /** Returns true if the runtime supports recording (browser with MediaRecorder). */
102
+ /** True if the runtime supports recording (browser with MediaRecorder + getUserMedia). */
28
103
  export declare function isRecordingSupported(): boolean;
29
104
  export declare class Recorder {
30
105
  private rec;
@@ -32,23 +107,64 @@ export declare class Recorder {
32
107
  private stream;
33
108
  private state;
34
109
  private opts;
110
+ private mime;
111
+ private startedAt;
112
+ private pausedAccumMs;
113
+ private pauseStartedAt;
114
+ private maxStopTimer;
115
+ private audioCtx;
116
+ private analyser;
117
+ private levelSource;
118
+ private levelRafId;
119
+ private levelBuffer;
120
+ private silenceSinceMs;
121
+ private wakeLockSentinel;
122
+ private visibilityHandler;
35
123
  constructor(opts?: RecorderOptions);
36
- /** Current recorder state. */
124
+ /** Static support check. */
125
+ static isSupported(): boolean;
126
+ /** Pick the best supported MIME type for this browser. */
127
+ static getSupportedMimeType(prefer?: string): string | undefined;
128
+ /**
129
+ * Probe microphone permission *without* requesting it. Returns:
130
+ * "granted" — already allowed
131
+ * "denied" — user has blocked access; calling start() will reject
132
+ * "prompt" — browser will show the dialog on next start()
133
+ * "unknown" — browser doesn't support the Permissions API
134
+ */
135
+ static probePermission(): Promise<"granted" | "denied" | "prompt" | "unknown">;
136
+ /** Enumerate microphones. Empty `label` until permission is granted at least once. */
137
+ static listMicrophones(): Promise<MicrophoneInfo[]>;
138
+ /** Current state. */
37
139
  get currentState(): RecorderState;
140
+ /** Final MIME type the browser is producing (set after `start`). */
141
+ get mimeType(): string | undefined;
142
+ /** Recorded duration in ms (excluding paused time). 0 when idle. */
143
+ getDuration(): number;
38
144
  /**
39
145
  * Request microphone access and begin recording. Resolves once the
40
- * underlying MediaRecorder fires its `start` event. Throws on permission
41
- * denial or unsupported runtime.
146
+ * underlying MediaRecorder has fired `start`.
42
147
  */
43
148
  start(): Promise<void>;
149
+ /** Pause recording. Resumable via `resume()`. */
150
+ pause(): void;
151
+ /** Resume after `pause()`. */
152
+ resume(): void;
44
153
  /**
45
- * Stop recording and resolve with the assembled Blob. The caller can
46
- * pass this Blob directly to `ohm.audio.extract({ file: blob, ... })`.
47
- * Stops all media tracks so the browser's mic indicator clears.
154
+ * Stop recording and return the assembled Blob, ready to pass to
155
+ * `ohm.audio.extract({ file: blob, })`. Releases mic, wake lock, AudioContext.
48
156
  */
49
157
  stop(): Promise<Blob>;
50
- /** Abort the recording without surfacing the captured audio. */
158
+ /** Stop after `ms` from now. Returns the same promise as `stop()`. */
159
+ stopAfter(ms: number): Promise<Blob>;
160
+ /** Abort — releases everything, no Blob. */
51
161
  cancel(): void;
162
+ private startLevelMeter;
163
+ private acquireWakeLock;
164
+ private releaseWakeLock;
165
+ private attachVisibility;
166
+ private detachVisibility;
167
+ private cleanupStream;
52
168
  private cleanup;
53
169
  private setState;
54
170
  }
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;IACzC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;AAE9D,mFAAmF;AACnF,wBAAgB,oBAAoB,IAAI,OAAO,CAK9C;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAA8B;IACzC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,IAAI,CAAkB;gBAElB,IAAI,GAAE,eAAoB;IAItC,8BAA8B;IAC9B,IAAI,YAAY,IAAI,aAAa,CAEhC;IAED;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B5B;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B,gEAAgE;IAChE,MAAM,IAAI,IAAI;IAWd,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,QAAQ;CAKjB"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,MAAM,iBAAiB,GACzB,cAAc,GACd,kBAAkB,GAClB,cAAc,GACd,gBAAgB,GAChB,iBAAiB,GACjB,YAAY,GACZ,cAAc,GACd,SAAS,CAAC;AAEd,qBAAa,aAAc,SAAQ,KAAK;IACtC,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;gBACJ,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAMtE;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;IAEzC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,eAAe,CAAC,EAAE;QAChB,+DAA+D;QAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,4DAA4D;QAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,+BAA+B;IAC/B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,mEAAmE;IACnE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,gEAAgE;IAChE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAC;IAChC,yFAAyF;IACzF,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACvC,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAyED,0FAA0F;AAC1F,wBAAgB,oBAAoB,IAAI,OAAO,CAM9C;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAA8B;IACzC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAA8C;IAElE,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,WAAW,CAA2C;IAC9D,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,WAAW,CAA2B;IAE9C,OAAO,CAAC,cAAc,CAAK;IAE3B,OAAO,CAAC,gBAAgB,CAAa;IAErC,OAAO,CAAC,iBAAiB,CAA6B;gBAE1C,IAAI,GAAE,eAAoB;IAItC,4BAA4B;IAC5B,MAAM,CAAC,WAAW,IAAI,OAAO;IAI7B,0DAA0D;IAC1D,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhE;;;;;;OAMG;WACU,eAAe,IAAI,OAAO,CACrC,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAC5C;IAkBD,sFAAsF;WACzE,eAAe,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAczD,qBAAqB;IACrB,IAAI,YAAY,IAAI,aAAa,CAEhC;IAED,oEAAoE;IACpE,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAEjC;IAED,oEAAoE;IACpE,WAAW,IAAI,MAAM;IASrB;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiH5B,iDAAiD;IACjD,KAAK,IAAI,IAAI;IASb,8BAA8B;IAC9B,MAAM,IAAI,IAAI;IASd;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqC3B,sEAAsE;IACtE,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpC,4CAA4C;IAC5C,MAAM,IAAI,IAAI;IAad,OAAO,CAAC,eAAe;YAkDT,eAAe;IAW7B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,OAAO;IAgCf,OAAO,CAAC,QAAQ;CAKjB"}
package/dist/recorder.js CHANGED
@@ -1,22 +1,99 @@
1
1
  /**
2
- * Browser MediaRecorder wrapper. Hides the boilerplate of permissions,
3
- * stream tracks, chunk collection, and cleanup so customers can ship a
4
- * working recorder UI in three lines:
2
+ * Browser audio recorder for clinical consults.
5
3
  *
6
- * const rec = new Recorder();
4
+ * Wraps `MediaRecorder` + `getUserMedia` + `AudioContext` + `Wake Lock`
5
+ * with clinically-tuned defaults (16 kHz mono, echo-cancel, noise-suppress,
6
+ * auto-gain) and a codec cascade that works on iOS Safari, Firefox, and
7
+ * every Chromium browser.
8
+ *
9
+ * const rec = new Recorder({
10
+ * onLevel: (rms) => setVu(rms),
11
+ * onError: (e) => toast(e.message),
12
+ * maxDurationMs: 10 * 60_000, // hard cap at 10 min
13
+ * silenceAutoStop: { ms: 6000 }, // stop after 6 s of silence
14
+ * wakeLock: true,
15
+ * });
7
16
  * await rec.start();
8
- * const blob = await rec.stop(); // → ready to pass to ohm.audio.extract
17
+ * const blob = await rec.stop(); // → ohm.audio.extract({ file: blob, ... })
9
18
  *
10
- * Designed for Vite / Next.js / CRA / any modern browser. SSR-safe — the
11
- * `MediaRecorder` API is only touched inside method bodies, so importing
12
- * this module doesn't break server bundles.
19
+ * SSR-safe every browser-only API is touched inside method bodies, never
20
+ * at module scope. Importing this file from a Next.js server bundle is OK.
13
21
  */
14
- /** Returns true if the runtime supports recording (browser with MediaRecorder). */
22
+ export class RecorderError extends Error {
23
+ code;
24
+ cause;
25
+ constructor(code, message, cause) {
26
+ super(message);
27
+ this.name = "RecorderError";
28
+ this.code = code;
29
+ this.cause = cause;
30
+ }
31
+ }
32
+ const MIME_CASCADE = [
33
+ "audio/webm;codecs=opus",
34
+ "audio/mp4;codecs=mp4a.40.2",
35
+ "audio/ogg;codecs=opus",
36
+ "audio/mp4",
37
+ "audio/webm",
38
+ ];
39
+ function clinicalConstraints(deviceId) {
40
+ const base = {
41
+ echoCancellation: true,
42
+ noiseSuppression: true,
43
+ autoGainControl: true,
44
+ channelCount: { ideal: 1 },
45
+ sampleRate: { ideal: 16_000 },
46
+ sampleSize: { ideal: 16 },
47
+ };
48
+ if (deviceId)
49
+ base.deviceId = { exact: deviceId };
50
+ return base;
51
+ }
52
+ function pickMimeType(requested) {
53
+ if (typeof MediaRecorder === "undefined")
54
+ return undefined;
55
+ const probe = (m) => {
56
+ try {
57
+ return MediaRecorder.isTypeSupported(m);
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ };
63
+ if (requested && probe(requested))
64
+ return requested;
65
+ for (const m of MIME_CASCADE)
66
+ if (probe(m))
67
+ return m;
68
+ return undefined; // browser will pick its own fallback
69
+ }
70
+ function mapDomError(err) {
71
+ const name = (err && err.name) || "";
72
+ switch (name) {
73
+ case "NotAllowedError":
74
+ case "SecurityError":
75
+ return new RecorderError("PermissionDenied", "Microphone access was denied. Ask the user to allow it in browser settings.", err);
76
+ case "NotFoundError":
77
+ case "DevicesNotFoundError":
78
+ return new RecorderError("NoMicrophone", "No microphone was found.", err);
79
+ case "NotReadableError":
80
+ case "TrackStartError":
81
+ return new RecorderError("MicrophoneBusy", "The microphone is in use by another app.", err);
82
+ case "OverconstrainedError":
83
+ case "ConstraintNotSatisfiedError":
84
+ return new RecorderError("OverConstrained", "The requested audio constraints can't be satisfied by this device.", err);
85
+ default:
86
+ return new RecorderError("Unknown", (err && err.message) || "Recorder error", err);
87
+ }
88
+ }
89
+ /** True if the runtime supports recording (browser with MediaRecorder + getUserMedia). */
15
90
  export function isRecordingSupported() {
16
91
  if (typeof window === "undefined")
17
92
  return false;
18
93
  if (typeof navigator === "undefined" || !navigator.mediaDevices)
19
94
  return false;
95
+ if (typeof navigator.mediaDevices.getUserMedia !== "function")
96
+ return false;
20
97
  if (typeof globalThis.MediaRecorder === "undefined")
21
98
  return false;
22
99
  return true;
@@ -27,74 +104,255 @@ export class Recorder {
27
104
  stream = null;
28
105
  state = "idle";
29
106
  opts;
107
+ mime;
108
+ startedAt = 0;
109
+ pausedAccumMs = 0;
110
+ pauseStartedAt = 0;
111
+ maxStopTimer = null;
112
+ // Level metering
113
+ audioCtx = null;
114
+ analyser = null;
115
+ levelSource = null;
116
+ levelRafId = null;
117
+ levelBuffer = null;
118
+ // Silence
119
+ silenceSinceMs = 0;
120
+ // Wake lock
121
+ wakeLockSentinel = null;
122
+ // Visibility
123
+ visibilityHandler = null;
30
124
  constructor(opts = {}) {
31
125
  this.opts = opts;
32
126
  }
33
- /** Current recorder state. */
127
+ /** Static support check. */
128
+ static isSupported() {
129
+ return isRecordingSupported();
130
+ }
131
+ /** Pick the best supported MIME type for this browser. */
132
+ static getSupportedMimeType(prefer) {
133
+ return pickMimeType(prefer);
134
+ }
135
+ /**
136
+ * Probe microphone permission *without* requesting it. Returns:
137
+ * "granted" — already allowed
138
+ * "denied" — user has blocked access; calling start() will reject
139
+ * "prompt" — browser will show the dialog on next start()
140
+ * "unknown" — browser doesn't support the Permissions API
141
+ */
142
+ static async probePermission() {
143
+ if (typeof navigator === "undefined" ||
144
+ !navigator.permissions ||
145
+ typeof navigator.permissions.query !== "function") {
146
+ return "unknown";
147
+ }
148
+ try {
149
+ const status = await navigator.permissions.query({
150
+ name: "microphone",
151
+ });
152
+ return status.state;
153
+ }
154
+ catch {
155
+ return "unknown";
156
+ }
157
+ }
158
+ /** Enumerate microphones. Empty `label` until permission is granted at least once. */
159
+ static async listMicrophones() {
160
+ if (typeof navigator === "undefined" ||
161
+ !navigator.mediaDevices ||
162
+ typeof navigator.mediaDevices.enumerateDevices !== "function") {
163
+ return [];
164
+ }
165
+ const devices = await navigator.mediaDevices.enumerateDevices();
166
+ return devices
167
+ .filter((d) => d.kind === "audioinput")
168
+ .map((d) => ({ deviceId: d.deviceId, label: d.label, groupId: d.groupId }));
169
+ }
170
+ /** Current state. */
34
171
  get currentState() {
35
172
  return this.state;
36
173
  }
174
+ /** Final MIME type the browser is producing (set after `start`). */
175
+ get mimeType() {
176
+ return this.rec?.mimeType || this.mime;
177
+ }
178
+ /** Recorded duration in ms (excluding paused time). 0 when idle. */
179
+ getDuration() {
180
+ if (this.state === "idle")
181
+ return 0;
182
+ const now = Date.now();
183
+ if (this.state === "paused") {
184
+ return this.pauseStartedAt - this.startedAt - this.pausedAccumMs;
185
+ }
186
+ return now - this.startedAt - this.pausedAccumMs;
187
+ }
37
188
  /**
38
189
  * Request microphone access and begin recording. Resolves once the
39
- * underlying MediaRecorder fires its `start` event. Throws on permission
40
- * denial or unsupported runtime.
190
+ * underlying MediaRecorder has fired `start`.
41
191
  */
42
192
  async start() {
43
193
  if (!isRecordingSupported()) {
44
- throw new Error("Recording not supported — MediaRecorder is unavailable in this runtime");
194
+ throw new RecorderError("NotSupported", "Recording isn't supported in this runtime — MediaRecorder or getUserMedia is missing.");
45
195
  }
46
196
  if (this.state !== "idle") {
47
- throw new Error(`Cannot start recorder while ${this.state}`);
197
+ throw new RecorderError("InvalidState", `Cannot start while recorder is "${this.state}".`);
198
+ }
199
+ this.setState("starting");
200
+ const constraints = this.opts.audioConstraints ??
201
+ (this.opts.clinicalDefaults === false
202
+ ? true
203
+ : clinicalConstraints(this.opts.deviceId));
204
+ try {
205
+ this.stream = await navigator.mediaDevices.getUserMedia({
206
+ audio: constraints,
207
+ });
208
+ }
209
+ catch (e) {
210
+ this.setState("idle");
211
+ const err = mapDomError(e);
212
+ this.opts.onError?.(err);
213
+ throw err;
214
+ }
215
+ // Detect mic disconnect / OS-level revoke.
216
+ for (const track of this.stream.getAudioTracks()) {
217
+ track.addEventListener("ended", () => {
218
+ if (this.state === "recording" || this.state === "paused") {
219
+ this.opts.onDeviceLost?.();
220
+ this.opts.onError?.(new RecorderError("DeviceLost", "Microphone was disconnected."));
221
+ this.cancel();
222
+ }
223
+ });
224
+ }
225
+ this.mime = pickMimeType(this.opts.mimeType);
226
+ let rec;
227
+ try {
228
+ rec = new MediaRecorder(this.stream, {
229
+ mimeType: this.mime,
230
+ audioBitsPerSecond: this.opts.audioBitsPerSecond ?? 32_000,
231
+ });
232
+ }
233
+ catch (e) {
234
+ this.cleanupStream();
235
+ this.setState("idle");
236
+ const err = mapDomError(e);
237
+ this.opts.onError?.(err);
238
+ throw err;
48
239
  }
49
- const mimeType = this.opts.mimeType ?? "audio/webm;codecs=opus";
50
- this.stream = await navigator.mediaDevices.getUserMedia({
51
- audio: this.opts.audioConstraints ?? true,
52
- });
53
- const rec = new MediaRecorder(this.stream, { mimeType });
54
240
  this.chunks = [];
55
241
  rec.ondataavailable = (e) => {
56
- if (e.data && e.data.size > 0)
242
+ if (e.data && e.data.size > 0) {
57
243
  this.chunks.push(e.data);
244
+ this.opts.onChunk?.(e.data);
245
+ }
246
+ };
247
+ rec.onerror = (e) => {
248
+ const err = mapDomError(e?.error || e);
249
+ this.opts.onError?.(err);
58
250
  };
59
251
  this.rec = rec;
252
+ this.startLevelMeter();
253
+ if (this.opts.wakeLock)
254
+ await this.acquireWakeLock();
255
+ if (this.opts.pauseOnHidden)
256
+ this.attachVisibility();
60
257
  return new Promise((resolve, reject) => {
61
258
  rec.onstart = () => {
259
+ this.startedAt = Date.now();
260
+ this.pausedAccumMs = 0;
261
+ this.silenceSinceMs = 0;
62
262
  this.setState("recording");
263
+ if (this.opts.maxDurationMs && this.opts.maxDurationMs > 0) {
264
+ this.maxStopTimer = setTimeout(() => {
265
+ this.stop().catch(() => {
266
+ /* swallow — handled via onError */
267
+ });
268
+ }, this.opts.maxDurationMs);
269
+ }
63
270
  resolve();
64
271
  };
65
- rec.onerror = (e) => reject(e?.error || new Error("recorder error"));
66
- rec.start();
272
+ const onceErr = (e) => {
273
+ const err = mapDomError(e?.error || e);
274
+ this.opts.onError?.(err);
275
+ reject(err);
276
+ };
277
+ rec.addEventListener("error", onceErr, { once: true });
278
+ try {
279
+ if (this.opts.timesliceMs && this.opts.timesliceMs > 0) {
280
+ rec.start(this.opts.timesliceMs);
281
+ }
282
+ else {
283
+ rec.start();
284
+ }
285
+ }
286
+ catch (e) {
287
+ const err = mapDomError(e);
288
+ this.opts.onError?.(err);
289
+ reject(err);
290
+ }
67
291
  });
68
292
  }
293
+ /** Pause recording. Resumable via `resume()`. */
294
+ pause() {
295
+ if (this.state !== "recording" || !this.rec) {
296
+ throw new RecorderError("InvalidState", `Cannot pause while "${this.state}".`);
297
+ }
298
+ this.rec.pause();
299
+ this.pauseStartedAt = Date.now();
300
+ this.setState("paused");
301
+ }
302
+ /** Resume after `pause()`. */
303
+ resume() {
304
+ if (this.state !== "paused" || !this.rec) {
305
+ throw new RecorderError("InvalidState", `Cannot resume while "${this.state}".`);
306
+ }
307
+ this.pausedAccumMs += Date.now() - this.pauseStartedAt;
308
+ this.rec.resume();
309
+ this.setState("recording");
310
+ }
69
311
  /**
70
- * Stop recording and resolve with the assembled Blob. The caller can
71
- * pass this Blob directly to `ohm.audio.extract({ file: blob, ... })`.
72
- * Stops all media tracks so the browser's mic indicator clears.
312
+ * Stop recording and return the assembled Blob, ready to pass to
313
+ * `ohm.audio.extract({ file: blob, })`. Releases mic, wake lock, AudioContext.
73
314
  */
74
315
  async stop() {
75
- if (!this.rec || this.state !== "recording") {
76
- throw new Error("Recorder is not recording");
316
+ if (!this.rec || (this.state !== "recording" && this.state !== "paused")) {
317
+ throw new RecorderError("InvalidState", `Cannot stop while "${this.state}".`);
77
318
  }
78
319
  this.setState("stopping");
79
320
  return new Promise((resolve, reject) => {
80
321
  const rec = this.rec;
81
322
  rec.onstop = () => {
82
- const blob = new Blob(this.chunks, {
83
- type: rec.mimeType || "audio/webm",
84
- });
323
+ const type = rec.mimeType || this.mime || "audio/webm";
324
+ const blob = new Blob(this.chunks, { type });
85
325
  this.cleanup();
86
326
  resolve(blob);
87
327
  };
88
- rec.onerror = (e) => {
328
+ rec.addEventListener("error", (e) => {
89
329
  this.cleanup();
90
- reject(e?.error || new Error("recorder error"));
91
- };
92
- rec.stop();
330
+ const err = mapDomError(e?.error || e);
331
+ this.opts.onError?.(err);
332
+ reject(err);
333
+ }, { once: true });
334
+ try {
335
+ rec.stop();
336
+ }
337
+ catch (e) {
338
+ this.cleanup();
339
+ const err = mapDomError(e);
340
+ this.opts.onError?.(err);
341
+ reject(err);
342
+ }
343
+ });
344
+ }
345
+ /** Stop after `ms` from now. Returns the same promise as `stop()`. */
346
+ stopAfter(ms) {
347
+ return new Promise((resolve, reject) => {
348
+ setTimeout(() => {
349
+ this.stop().then(resolve, reject);
350
+ }, ms);
93
351
  });
94
352
  }
95
- /** Abort the recording without surfacing the captured audio. */
353
+ /** Abort releases everything, no Blob. */
96
354
  cancel() {
97
- if (this.rec && this.state === "recording") {
355
+ if (this.rec && (this.state === "recording" || this.state === "paused")) {
98
356
  try {
99
357
  this.rec.stop();
100
358
  }
@@ -104,11 +362,140 @@ export class Recorder {
104
362
  }
105
363
  this.cleanup();
106
364
  }
107
- cleanup() {
108
- this.stream?.getTracks().forEach((t) => t.stop());
365
+ // ─── internals ──────────────────────────────────────────────────────
366
+ startLevelMeter() {
367
+ if (!this.stream || !this.opts.onLevel)
368
+ return;
369
+ if (typeof AudioContext === "undefined")
370
+ return;
371
+ try {
372
+ const Ctx = globalThis.AudioContext ||
373
+ globalThis.webkitAudioContext;
374
+ this.audioCtx = new Ctx();
375
+ this.levelSource = this.audioCtx.createMediaStreamSource(this.stream);
376
+ this.analyser = this.audioCtx.createAnalyser();
377
+ this.analyser.fftSize = 1024;
378
+ this.analyser.smoothingTimeConstant = 0.6;
379
+ this.levelSource.connect(this.analyser);
380
+ this.levelBuffer = new Uint8Array(new ArrayBuffer(this.analyser.fftSize));
381
+ const silenceMs = this.opts.silenceAutoStop?.ms ?? 6000;
382
+ const silenceThr = this.opts.silenceAutoStop?.threshold ?? 0.012;
383
+ const tick = () => {
384
+ if (!this.analyser || !this.levelBuffer)
385
+ return;
386
+ this.analyser.getByteTimeDomainData(this.levelBuffer);
387
+ // RMS in 0..1
388
+ let sum = 0;
389
+ for (let i = 0; i < this.levelBuffer.length; i++) {
390
+ const v = (this.levelBuffer[i] - 128) / 128;
391
+ sum += v * v;
392
+ }
393
+ const rms = Math.sqrt(sum / this.levelBuffer.length);
394
+ this.opts.onLevel?.(rms);
395
+ if (this.opts.silenceAutoStop && this.state === "recording") {
396
+ if (rms < silenceThr) {
397
+ if (this.silenceSinceMs === 0)
398
+ this.silenceSinceMs = Date.now();
399
+ else if (Date.now() - this.silenceSinceMs >= silenceMs) {
400
+ this.silenceSinceMs = 0;
401
+ this.stop().catch(() => { });
402
+ return;
403
+ }
404
+ }
405
+ else {
406
+ this.silenceSinceMs = 0;
407
+ }
408
+ }
409
+ this.levelRafId = requestAnimationFrame(tick);
410
+ };
411
+ this.levelRafId = requestAnimationFrame(tick);
412
+ }
413
+ catch {
414
+ /* metering is best-effort; never block recording */
415
+ }
416
+ }
417
+ async acquireWakeLock() {
418
+ try {
419
+ const wl = navigator.wakeLock;
420
+ if (wl && typeof wl.request === "function") {
421
+ this.wakeLockSentinel = await wl.request("screen");
422
+ }
423
+ }
424
+ catch {
425
+ /* best-effort */
426
+ }
427
+ }
428
+ releaseWakeLock() {
429
+ try {
430
+ this.wakeLockSentinel?.release?.();
431
+ }
432
+ catch {
433
+ /* ignore */
434
+ }
435
+ this.wakeLockSentinel = null;
436
+ }
437
+ attachVisibility() {
438
+ if (typeof document === "undefined")
439
+ return;
440
+ this.visibilityHandler = () => {
441
+ if (document.visibilityState === "hidden" && this.state === "recording") {
442
+ try {
443
+ this.pause();
444
+ }
445
+ catch {
446
+ /* ignore */
447
+ }
448
+ }
449
+ };
450
+ document.addEventListener("visibilitychange", this.visibilityHandler);
451
+ }
452
+ detachVisibility() {
453
+ if (this.visibilityHandler && typeof document !== "undefined") {
454
+ document.removeEventListener("visibilitychange", this.visibilityHandler);
455
+ }
456
+ this.visibilityHandler = null;
457
+ }
458
+ cleanupStream() {
459
+ this.stream?.getTracks().forEach((t) => {
460
+ try {
461
+ t.stop();
462
+ }
463
+ catch {
464
+ /* ignore */
465
+ }
466
+ });
109
467
  this.stream = null;
468
+ }
469
+ cleanup() {
470
+ if (this.maxStopTimer) {
471
+ clearTimeout(this.maxStopTimer);
472
+ this.maxStopTimer = null;
473
+ }
474
+ if (this.levelRafId !== null) {
475
+ cancelAnimationFrame(this.levelRafId);
476
+ this.levelRafId = null;
477
+ }
478
+ try {
479
+ this.levelSource?.disconnect();
480
+ this.analyser?.disconnect();
481
+ this.audioCtx?.close().catch(() => { });
482
+ }
483
+ catch {
484
+ /* ignore */
485
+ }
486
+ this.levelSource = null;
487
+ this.analyser = null;
488
+ this.audioCtx = null;
489
+ this.levelBuffer = null;
490
+ this.releaseWakeLock();
491
+ this.detachVisibility();
492
+ this.cleanupStream();
110
493
  this.rec = null;
111
494
  this.chunks = [];
495
+ this.startedAt = 0;
496
+ this.pausedAccumMs = 0;
497
+ this.pauseStartedAt = 0;
498
+ this.silenceSinceMs = 0;
112
499
  this.setState("idle");
113
500
  }
114
501
  setState(next) {
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAiBH,mFAAmF;AACnF,MAAM,UAAU,oBAAoB;IAClC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAC9E,IAAI,OAAQ,UAAkB,CAAC,aAAa,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAC3E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,QAAQ;IACX,GAAG,GAAyB,IAAI,CAAC;IACjC,MAAM,GAAW,EAAE,CAAC;IACpB,MAAM,GAAuB,IAAI,CAAC;IAClC,KAAK,GAAkB,MAAM,CAAC;IAC9B,IAAI,CAAkB;IAE9B,YAAY,OAAwB,EAAE;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,8BAA8B;IAC9B,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CACb,wEAAwE,CACzE,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,wBAAwB,CAAC;QAChE,IAAI,CAAC,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC;YACtD,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI;SAC1C,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,GAAG,CAAC,eAAe,GAAG,CAAC,CAAC,EAAE,EAAE;YAC1B,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC;gBAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1D,CAAC,CAAC;QACF,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE;gBACjB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAC3B,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YACF,GAAG,CAAC,OAAO,GAAG,CAAC,CAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAC1E,GAAG,CAAC,KAAK,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC1B,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAI,CAAC;YACtB,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;gBAChB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;oBACjC,IAAI,EAAE,GAAG,CAAC,QAAQ,IAAI,YAAY;iBACnC,CAAC,CAAC;gBACH,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC;YACF,GAAG,CAAC,OAAO,GAAG,CAAC,CAAM,EAAE,EAAE;gBACvB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,MAAM,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAClD,CAAC,CAAC;YACF,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gEAAgE;IAChE,MAAM;QACJ,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC3C,IAAI,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;QAChB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAEO,QAAQ,CAAC,IAAmB;QAClC,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;CACF"}
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohm_studio/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "OHM Studio SDK for JavaScript / TypeScript / React. Voice-to-structured-JSON clinical extraction APIs.",
5
5
  "license": "MIT",
6
6
  "type": "module",