@adminide-stack/yantra-mobile 12.0.136-alpha.1 → 12.0.136

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.
Files changed (37) hide show
  1. package/lib/compute.js +51 -1
  2. package/lib/compute.js.map +1 -1
  3. package/lib/features/apps/AppsCatalog.js +10 -0
  4. package/lib/features/apps/AppsCatalog.js.map +1 -1
  5. package/lib/features/apps/mobileAppCatalog.js +9 -0
  6. package/lib/features/apps/mobileAppCatalog.js.map +1 -1
  7. package/lib/features/apps/nativeSurfaceTwin.js +1 -0
  8. package/lib/features/apps/nativeSurfaceTwin.js.map +1 -1
  9. package/lib/features/devices/bodyComposition.js +116 -0
  10. package/lib/features/devices/bodyComposition.js.map +1 -0
  11. package/lib/features/devices/readings.js.map +1 -1
  12. package/lib/features/devices/session.js +22 -2
  13. package/lib/features/devices/session.js.map +1 -1
  14. package/lib/features/dictate/DictateExperience.js +387 -0
  15. package/lib/features/dictate/DictateExperience.js.map +1 -0
  16. package/lib/features/dictate/components/DictationList.js +110 -0
  17. package/lib/features/dictate/components/DictationList.js.map +1 -0
  18. package/lib/features/dictate/components/NoteView.js +330 -0
  19. package/lib/features/dictate/components/NoteView.js.map +1 -0
  20. package/lib/features/dictate/components/RecordPanel.js +218 -0
  21. package/lib/features/dictate/components/RecordPanel.js.map +1 -0
  22. package/lib/features/dictate/dictateClient.js +98 -0
  23. package/lib/features/dictate/dictateClient.js.map +1 -0
  24. package/lib/features/dictate/dictateTheme.js +46 -0
  25. package/lib/features/dictate/dictateTheme.js.map +1 -0
  26. package/lib/features/dictate/dictationStore.js +107 -0
  27. package/lib/features/dictate/dictationStore.js.map +1 -0
  28. package/lib/features/dictate/noteText.js +35 -0
  29. package/lib/features/dictate/noteText.js.map +1 -0
  30. package/lib/features/dictate/useDictationRecorder.js +121 -0
  31. package/lib/features/dictate/useDictationRecorder.js.map +1 -0
  32. package/lib/routes.json +50 -0
  33. package/lib/screens/Devices/index.js +205 -3
  34. package/lib/screens/Devices/index.js.map +1 -1
  35. package/lib/screens/DictateNative/index.js +36 -0
  36. package/lib/screens/DictateNative/index.js.map +1 -0
  37. package/package.json +4 -4
@@ -0,0 +1,98 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+ const DICTATE_CONNECTOR = "yantra-meetings";
18
+ const CLINICAL_NOTE_ACTION = "YANTRA_MEETINGS_CLINICAL_NOTE";
19
+ const NOTE_TIMEOUT_MS = 10 * 6e4;
20
+ class DictateError extends Error {
21
+ constructor(kind, message) {
22
+ super(message);
23
+ this.kind = kind;
24
+ this.name = "DictateError";
25
+ }
26
+ }
27
+ const asRecord = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
28
+ const str = (v) => typeof v === "string" ? v.trim() : "";
29
+ const strings = (v) => (Array.isArray(v) ? v : typeof v === "string" && v.trim() ? [v] : []).map(str).filter(Boolean);
30
+ const ACTIONS = ["start", "continue", "change", "stop", "mentioned"];
31
+ function unwrapNote(raw) {
32
+ var _a;
33
+ let layer = asRecord(raw);
34
+ const seen = [];
35
+ for (let depth = 0; layer && depth < 6; depth += 1) {
36
+ seen.push(layer);
37
+ const note = asRecord(layer.note);
38
+ if (note) {
39
+ const transcript = asRecord(layer.transcript);
40
+ const meds = Array.isArray(note.medications) ? note.medications : [];
41
+ return {
42
+ note: {
43
+ title: str(note.title) || "Clinical note",
44
+ subjective: strings(note.subjective),
45
+ objective: strings(note.objective),
46
+ assessment: strings(note.assessment),
47
+ plan: strings(note.plan),
48
+ medications: meds.map(asRecord).filter((m) => !!m && !!str(m.name)).map((m) => {
49
+ const action = str(m.action).toLowerCase();
50
+ return {
51
+ name: str(m.name),
52
+ dose: str(m.dose),
53
+ route: str(m.route),
54
+ frequency: str(m.frequency),
55
+ action: ACTIONS.includes(action) ? action : "mentioned"
56
+ };
57
+ }),
58
+ follow_up: str(note.follow_up),
59
+ to_verify: strings(note.to_verify)
60
+ },
61
+ transcript: str(transcript == null ? void 0 : transcript.text),
62
+ model: str(layer.model) || void 0
63
+ };
64
+ }
65
+ layer = (_a = asRecord(layer.data)) != null ? _a : asRecord(layer.result);
66
+ }
67
+ const reason = seen.flatMap((l) => [l.error, l.message, l.stderr]).find((v) => typeof v === "string" && v.trim().length > 0);
68
+ throw new DictateError("failed", reason != null ? reason : "The note came back empty.");
69
+ }
70
+ function dictateErrorText(err) {
71
+ const message = err instanceof Error ? err.message : String(err);
72
+ if (err instanceof DictateError && err.kind === "empty") return "No speech was heard in the recording. Try again closer to the phone.";
73
+ if (/network request failed|offline|not signed in/i.test(message)) return "You are offline or signed out. The recording is saved; try again when you are back online.";
74
+ if (/no speech|has no speech/i.test(message)) return "No speech was heard in the recording. Try again closer to the phone.";
75
+ if (/not found in registry|unknown action|CLINICAL_NOTE/i.test(message)) return "Dictate is not available on this workspace yet. The recording is saved.";
76
+ if (/timed? ?out|abort/i.test(message)) return "Writing the note took too long. The recording is saved; try again.";
77
+ return "The note could not be written. The recording is saved; try again.";
78
+ }
79
+ async function writeNoteFromRecording(audioUri, deps, opts = {}, onStage = () => void 0) {
80
+ var _a, _b;
81
+ onStage("uploading");
82
+ const inputUrl = await deps.upload(audioUri);
83
+ onStage("writing");
84
+ const raw = await deps.exec(DICTATE_CONNECTOR, CLINICAL_NOTE_ACTION, __spreadValues(__spreadValues({
85
+ input_url: inputUrl
86
+ }, ((_a = opts.keyterms) == null ? void 0 : _a.length) ? {
87
+ keyterms: opts.keyterms
88
+ } : {}), ((_b = opts.context) == null ? void 0 : _b.trim()) ? {
89
+ context: opts.context.trim()
90
+ } : {}), {
91
+ timeoutMs: NOTE_TIMEOUT_MS
92
+ });
93
+ const written = unwrapNote(raw);
94
+ const n = written.note;
95
+ const empty = !written.transcript && ![n.subjective, n.objective, n.assessment, n.plan].some((s) => s.length);
96
+ if (empty) throw new DictateError("empty", "The dictation has no speech.");
97
+ return written;
98
+ }export{CLINICAL_NOTE_ACTION,DICTATE_CONNECTOR,DictateError,NOTE_TIMEOUT_MS,dictateErrorText,unwrapNote,writeNoteFromRecording};//# sourceMappingURL=dictateClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictateClient.js","sources":["../../../src/features/dictate/dictateClient.ts"],"sourcesContent":["/**\n * Recording -> SOAP note. The recording is uploaded first and handed to the\n * connector as a URL (connector params travel as one CLI argument, capped at\n * 128 KB - audio never rides inline), then YANTRA_MEETINGS_CLINICAL_NOTE\n * transcribes it with a medical speech model and writes the note.\n */\nimport type { RemoteConnectorExec } from '../canvas/videoEditor/remoteMediaClient';\nimport type { ClinicalNote, MedicationAction } from './types';\n\nexport const DICTATE_CONNECTOR = 'yantra-meetings';\nexport const CLINICAL_NOTE_ACTION = 'YANTRA_MEETINGS_CLINICAL_NOTE';\n/** Transcription plus the note runs ~10 s for two minutes of speech; an hour's dictation stays well inside this. */\nexport const NOTE_TIMEOUT_MS = 10 * 60_000;\n\nexport type DictateStage = 'uploading' | 'writing';\n\nexport interface DictateDeps {\n exec: RemoteConnectorExec;\n upload: (localUri: string) => Promise<string>;\n}\n\nexport interface WrittenNote {\n note: ClinicalNote;\n transcript: string;\n model?: string;\n}\n\nexport class DictateError extends Error {\n constructor(\n readonly kind: 'offline' | 'unavailable' | 'empty' | 'failed',\n message: string,\n ) {\n super(message);\n this.name = 'DictateError';\n }\n}\n\ntype Json = Record<string, unknown>;\nconst asRecord = (v: unknown): Json | null => (v && typeof v === 'object' && !Array.isArray(v) ? (v as Json) : null);\nconst str = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');\nconst strings = (v: unknown): string[] =>\n (Array.isArray(v) ? v : typeof v === 'string' && v.trim() ? [v] : []).map(str).filter(Boolean);\nconst ACTIONS: MedicationAction[] = ['start', 'continue', 'change', 'stop', 'mentioned'];\n\n/** The note whatever wraps it ({ data: { note } }, { result: ... }), shaped defensively. */\nexport function unwrapNote(raw: unknown): WrittenNote {\n let layer = asRecord(raw);\n const seen: Json[] = [];\n for (let depth = 0; layer && depth < 6; depth += 1) {\n seen.push(layer);\n const note = asRecord(layer.note);\n if (note) {\n const transcript = asRecord(layer.transcript);\n const meds = Array.isArray(note.medications) ? note.medications : [];\n return {\n note: {\n title: str(note.title) || 'Clinical note',\n subjective: strings(note.subjective),\n objective: strings(note.objective),\n assessment: strings(note.assessment),\n plan: strings(note.plan),\n medications: meds\n .map(asRecord)\n .filter((m): m is Json => !!m && !!str(m.name))\n .map((m) => {\n const action = str(m.action).toLowerCase() as MedicationAction;\n return {\n name: str(m.name),\n dose: str(m.dose),\n route: str(m.route),\n frequency: str(m.frequency),\n action: ACTIONS.includes(action) ? action : 'mentioned',\n };\n }),\n follow_up: str(note.follow_up),\n to_verify: strings(note.to_verify),\n },\n transcript: str(transcript?.text),\n model: str(layer.model) || undefined,\n };\n }\n layer = asRecord(layer.data) ?? asRecord(layer.result);\n }\n const reason = seen\n .flatMap((l) => [l.error, l.message, l.stderr])\n .find((v): v is string => typeof v === 'string' && v.trim().length > 0);\n throw new DictateError('failed', reason ?? 'The note came back empty.');\n}\n\n/** A message a clinician can act on, never a stack trace. */\nexport function dictateErrorText(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n if (err instanceof DictateError && err.kind === 'empty') return 'No speech was heard in the recording. Try again closer to the phone.';\n if (/network request failed|offline|not signed in/i.test(message)) return 'You are offline or signed out. The recording is saved; try again when you are back online.';\n if (/no speech|has no speech/i.test(message)) return 'No speech was heard in the recording. Try again closer to the phone.';\n if (/not found in registry|unknown action|CLINICAL_NOTE/i.test(message)) return 'Dictate is not available on this workspace yet. The recording is saved.';\n if (/timed? ?out|abort/i.test(message)) return 'Writing the note took too long. The recording is saved; try again.';\n return 'The note could not be written. The recording is saved; try again.';\n}\n\nexport async function writeNoteFromRecording(\n audioUri: string,\n deps: DictateDeps,\n opts: { keyterms?: string[]; context?: string } = {},\n onStage: (stage: DictateStage) => void = () => undefined,\n): Promise<WrittenNote> {\n onStage('uploading');\n const inputUrl = await deps.upload(audioUri);\n onStage('writing');\n const raw = await deps.exec(\n DICTATE_CONNECTOR,\n CLINICAL_NOTE_ACTION,\n {\n input_url: inputUrl,\n ...(opts.keyterms?.length ? { keyterms: opts.keyterms } : {}),\n ...(opts.context?.trim() ? { context: opts.context.trim() } : {}),\n },\n { timeoutMs: NOTE_TIMEOUT_MS },\n );\n const written = unwrapNote(raw);\n const n = written.note;\n const empty = !written.transcript && ![n.subjective, n.objective, n.assessment, n.plan].some((s) => s.length);\n if (empty) throw new DictateError('empty', 'The dictation has no speech.');\n return written;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ function dictateTheme(isDark) {
2
+ if (isDark) {
3
+ return {
4
+ isDark: true,
5
+ screen: "#0b0f17",
6
+ card: "#141a24",
7
+ chip: "#1e2633",
8
+ border: "rgba(148,163,184,0.16)",
9
+ ink: "#e8edf5",
10
+ muted: "#9aa5b8",
11
+ faint: "#687489",
12
+ accent: "#5b9cf6",
13
+ accentInk: "#ffffff",
14
+ accentSoft: "rgba(91,156,246,0.16)",
15
+ rec: "#f87171",
16
+ recSoft: "rgba(248,113,113,0.16)",
17
+ warnInk: "#fcd34d",
18
+ warnSoft: "rgba(250,178,25,0.12)",
19
+ warnBorder: "rgba(250,178,25,0.32)",
20
+ good: "#4ade80",
21
+ goodSoft: "rgba(74,222,128,0.14)",
22
+ crit: "#f87171"
23
+ };
24
+ }
25
+ return {
26
+ isDark: false,
27
+ screen: "#f6f7f9",
28
+ card: "#ffffff",
29
+ chip: "#eef1f5",
30
+ border: "#e4e7ec",
31
+ ink: "#0f172a",
32
+ muted: "#5b6474",
33
+ faint: "#8e98a9",
34
+ accent: "#1a73e8",
35
+ accentInk: "#ffffff",
36
+ accentSoft: "#e8f0fe",
37
+ rec: "#dc2626",
38
+ recSoft: "#fdecec",
39
+ warnInk: "#92400e",
40
+ warnSoft: "#fffbeb",
41
+ warnBorder: "#fde68a",
42
+ good: "#15803d",
43
+ goodSoft: "#e8f6ee",
44
+ crit: "#b91c1c"
45
+ };
46
+ }export{dictateTheme};//# sourceMappingURL=dictateTheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictateTheme.js","sources":["../../../src/features/dictate/dictateTheme.ts"],"sourcesContent":["/**\n * Dictate's colours: the same roles and accent as the other native screens\n * (Expenses), so it reads as part of the app, with a recording red for the\n * live microphone and an amber for what the clinician must check.\n */\nexport type DictateTheme = {\n isDark: boolean;\n screen: string;\n card: string;\n chip: string;\n border: string;\n ink: string;\n muted: string;\n faint: string;\n accent: string;\n accentInk: string;\n accentSoft: string;\n rec: string;\n recSoft: string;\n warnInk: string;\n warnSoft: string;\n warnBorder: string;\n good: string;\n goodSoft: string;\n crit: string;\n};\n\nexport function dictateTheme(isDark: boolean): DictateTheme {\n if (isDark) {\n return {\n isDark: true,\n screen: '#0b0f17',\n card: '#141a24',\n chip: '#1e2633',\n border: 'rgba(148,163,184,0.16)',\n ink: '#e8edf5',\n muted: '#9aa5b8',\n faint: '#687489',\n accent: '#5b9cf6',\n accentInk: '#ffffff',\n accentSoft: 'rgba(91,156,246,0.16)',\n rec: '#f87171',\n recSoft: 'rgba(248,113,113,0.16)',\n warnInk: '#fcd34d',\n warnSoft: 'rgba(250,178,25,0.12)',\n warnBorder: 'rgba(250,178,25,0.32)',\n good: '#4ade80',\n goodSoft: 'rgba(74,222,128,0.14)',\n crit: '#f87171',\n };\n }\n return {\n isDark: false,\n screen: '#f6f7f9',\n card: '#ffffff',\n chip: '#eef1f5',\n border: '#e4e7ec',\n ink: '#0f172a',\n muted: '#5b6474',\n faint: '#8e98a9',\n accent: '#1a73e8',\n accentInk: '#ffffff',\n accentSoft: '#e8f0fe',\n rec: '#dc2626',\n recSoft: '#fdecec',\n warnInk: '#92400e',\n warnSoft: '#fffbeb',\n warnBorder: '#fde68a',\n good: '#15803d',\n goodSoft: '#e8f6ee',\n crit: '#b91c1c',\n };\n}\n"],"names":[],"mappings":"AA0BO,SAAS,aAAa,MAA+B,EAAA;AAC1D,EAAA,IAAI,MAAQ,EAAA;AACV,IAAO,OAAA;AAAA,MACL,MAAQ,EAAA,IAAA;AAAA,MACR,MAAQ,EAAA,SAAA;AAAA,MACR,IAAM,EAAA,SAAA;AAAA,MACN,IAAM,EAAA,SAAA;AAAA,MACN,MAAQ,EAAA,wBAAA;AAAA,MACR,GAAK,EAAA,SAAA;AAAA,MACL,KAAO,EAAA,SAAA;AAAA,MACP,KAAO,EAAA,SAAA;AAAA,MACP,MAAQ,EAAA,SAAA;AAAA,MACR,SAAW,EAAA,SAAA;AAAA,MACX,UAAY,EAAA,uBAAA;AAAA,MACZ,GAAK,EAAA,SAAA;AAAA,MACL,OAAS,EAAA,wBAAA;AAAA,MACT,OAAS,EAAA,SAAA;AAAA,MACT,QAAU,EAAA,uBAAA;AAAA,MACV,UAAY,EAAA,uBAAA;AAAA,MACZ,IAAM,EAAA,SAAA;AAAA,MACN,QAAU,EAAA,uBAAA;AAAA,MACV,IAAM,EAAA;AAAA,KACR;AAAA;AAEF,EAAO,OAAA;AAAA,IACL,MAAQ,EAAA,KAAA;AAAA,IACR,MAAQ,EAAA,SAAA;AAAA,IACR,IAAM,EAAA,SAAA;AAAA,IACN,IAAM,EAAA,SAAA;AAAA,IACN,MAAQ,EAAA,SAAA;AAAA,IACR,GAAK,EAAA,SAAA;AAAA,IACL,KAAO,EAAA,SAAA;AAAA,IACP,KAAO,EAAA,SAAA;AAAA,IACP,MAAQ,EAAA,SAAA;AAAA,IACR,SAAW,EAAA,SAAA;AAAA,IACX,UAAY,EAAA,SAAA;AAAA,IACZ,GAAK,EAAA,SAAA;AAAA,IACL,OAAS,EAAA,SAAA;AAAA,IACT,OAAS,EAAA,SAAA;AAAA,IACT,QAAU,EAAA,SAAA;AAAA,IACV,UAAY,EAAA,SAAA;AAAA,IACZ,IAAM,EAAA,SAAA;AAAA,IACN,QAAU,EAAA,SAAA;AAAA,IACV,IAAM,EAAA;AAAA,GACR;AACF"}
@@ -0,0 +1,107 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ function loadFs() {
21
+ return require("expo-file-system");
22
+ }
23
+ const MAX_RECORDS = 200;
24
+ function createDictationStore(fs = loadFs(), now = () => /* @__PURE__ */ new Date()) {
25
+ var _a;
26
+ const root = `${(_a = fs.documentDirectory) != null ? _a : ""}dictations/`;
27
+ const indexUri = `${root}index.json`;
28
+ let queue = Promise.resolve();
29
+ const serial = (task) => {
30
+ const next = queue.then(task, task);
31
+ queue = next.catch(() => void 0);
32
+ return next;
33
+ };
34
+ const ensureDir = async () => {
35
+ if (!(await fs.getInfoAsync(root)).exists) await fs.makeDirectoryAsync(root, {
36
+ intermediates: true
37
+ });
38
+ };
39
+ const read = async () => {
40
+ try {
41
+ if (!(await fs.getInfoAsync(indexUri)).exists) return [];
42
+ const parsed = JSON.parse(await fs.readAsStringAsync(indexUri));
43
+ return Array.isArray(parsed) ? parsed : [];
44
+ } catch (e) {
45
+ return [];
46
+ }
47
+ };
48
+ const write = async (records) => {
49
+ await ensureDir();
50
+ await fs.writeAsStringAsync(indexUri, JSON.stringify(records.slice(0, MAX_RECORDS)));
51
+ };
52
+ return {
53
+ list: () => serial(read),
54
+ keepRecording: (tempUri, durationMs) => serial(async () => {
55
+ var _a2, _b;
56
+ await ensureDir();
57
+ const at = now();
58
+ const id = `d${at.getTime().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
59
+ const ext = (_b = (_a2 = /\.[a-z0-9]{2,4}$/i.exec(tempUri)) == null ? void 0 : _a2[0]) != null ? _b : ".m4a";
60
+ const audioUri = `${root}${id}${ext}`;
61
+ await fs.moveAsync({
62
+ from: tempUri,
63
+ to: audioUri
64
+ });
65
+ const record = {
66
+ id,
67
+ createdAt: at.toISOString(),
68
+ durationMs,
69
+ status: "saved",
70
+ audioUri
71
+ };
72
+ await write([record, ...await read()]);
73
+ return record;
74
+ }),
75
+ update: (id, patch) => serial(async () => {
76
+ const records = await read();
77
+ const i = records.findIndex((r) => r.id === id);
78
+ if (i < 0) return null;
79
+ records[i] = __spreadProps(__spreadValues(__spreadValues({}, records[i]), patch), {
80
+ id
81
+ });
82
+ await write(records);
83
+ return records[i];
84
+ }),
85
+ dropAudio: (id) => serial(async () => {
86
+ const records = await read();
87
+ const record = records.find((r) => r.id === id);
88
+ if (!(record == null ? void 0 : record.audioUri)) return;
89
+ await fs.deleteAsync(record.audioUri, {
90
+ idempotent: true
91
+ }).catch(() => void 0);
92
+ delete record.audioUri;
93
+ await write(records);
94
+ }),
95
+ remove: (id) => serial(async () => {
96
+ const records = await read();
97
+ const record = records.find((r) => r.id === id);
98
+ if (record == null ? void 0 : record.audioUri) await fs.deleteAsync(record.audioUri, {
99
+ idempotent: true
100
+ }).catch(() => void 0);
101
+ await write(records.filter((r) => r.id !== id));
102
+ }),
103
+ discardRecording: (tempUri) => fs.deleteAsync(tempUri, {
104
+ idempotent: true
105
+ }).catch(() => void 0)
106
+ };
107
+ }export{createDictationStore};//# sourceMappingURL=dictationStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictationStore.js","sources":["../../../src/features/dictate/dictationStore.ts"],"sourcesContent":["/**\n * Dictations live on this device only: an index file plus, until its note is\n * written, the recording itself. A recording is moved out of the recorder's\n * cache the moment it stops, so a failed upload or a closed app never loses\n * it, and it is deleted as soon as the note exists - the less clinical audio\n * a phone keeps, the better.\n */\nimport type { DictationRecord } from './types';\n\n/** The slice of expo-file-system (legacy API) this needs; injectable for tests. */\nexport interface DictationFs {\n documentDirectory: string | null;\n getInfoAsync(uri: string): Promise<{ exists: boolean }>;\n makeDirectoryAsync(uri: string, options?: { intermediates?: boolean }): Promise<void>;\n readAsStringAsync(uri: string): Promise<string>;\n writeAsStringAsync(uri: string, contents: string): Promise<void>;\n moveAsync(options: { from: string; to: string }): Promise<void>;\n deleteAsync(uri: string, options?: { idempotent?: boolean }): Promise<void>;\n}\n\nfunction loadFs(): DictationFs {\n // Lazy: the module is native, and a test or a build without it must still load this file.\n return require('expo-file-system') as DictationFs;\n}\n\n/** Keeps the list from growing without bound; the oldest go first. */\nconst MAX_RECORDS = 200;\n\nexport interface DictationStore {\n list(): Promise<DictationRecord[]>;\n /** Moves a finished recording into the store and records it as saved. */\n keepRecording(tempUri: string, durationMs: number): Promise<DictationRecord>;\n update(id: string, patch: Partial<DictationRecord>): Promise<DictationRecord | null>;\n /** The note exists: the recording is no longer needed. */\n dropAudio(id: string): Promise<void>;\n remove(id: string): Promise<void>;\n /** A recording the clinician threw away before it was kept. */\n discardRecording(tempUri: string): Promise<void>;\n}\n\nexport function createDictationStore(fs: DictationFs = loadFs(), now: () => Date = () => new Date()): DictationStore {\n const root = `${fs.documentDirectory ?? ''}dictations/`;\n const indexUri = `${root}index.json`;\n // One write at a time: two quick updates must not race the index file.\n let queue: Promise<unknown> = Promise.resolve();\n const serial = <T>(task: () => Promise<T>): Promise<T> => {\n const next = queue.then(task, task);\n queue = next.catch(() => undefined);\n return next;\n };\n\n const ensureDir = async () => {\n if (!(await fs.getInfoAsync(root)).exists) await fs.makeDirectoryAsync(root, { intermediates: true });\n };\n const read = async (): Promise<DictationRecord[]> => {\n try {\n if (!(await fs.getInfoAsync(indexUri)).exists) return [];\n const parsed = JSON.parse(await fs.readAsStringAsync(indexUri));\n return Array.isArray(parsed) ? (parsed as DictationRecord[]) : [];\n } catch {\n return []; // a damaged index must not lock the clinician out of recording\n }\n };\n const write = async (records: DictationRecord[]) => {\n await ensureDir();\n await fs.writeAsStringAsync(indexUri, JSON.stringify(records.slice(0, MAX_RECORDS)));\n };\n\n return {\n list: () => serial(read),\n keepRecording: (tempUri, durationMs) =>\n serial(async () => {\n await ensureDir();\n const at = now();\n const id = `d${at.getTime().toString(36)}${Math.random().toString(36).slice(2, 6)}`;\n const ext = /\\.[a-z0-9]{2,4}$/i.exec(tempUri)?.[0] ?? '.m4a';\n const audioUri = `${root}${id}${ext}`;\n await fs.moveAsync({ from: tempUri, to: audioUri });\n const record: DictationRecord = { id, createdAt: at.toISOString(), durationMs, status: 'saved', audioUri };\n await write([record, ...(await read())]);\n return record;\n }),\n update: (id, patch) =>\n serial(async () => {\n const records = await read();\n const i = records.findIndex((r) => r.id === id);\n if (i < 0) return null;\n records[i] = { ...records[i], ...patch, id };\n await write(records);\n return records[i];\n }),\n dropAudio: (id) =>\n serial(async () => {\n const records = await read();\n const record = records.find((r) => r.id === id);\n if (!record?.audioUri) return;\n await fs.deleteAsync(record.audioUri, { idempotent: true }).catch(() => undefined);\n delete record.audioUri;\n await write(records);\n }),\n remove: (id) =>\n serial(async () => {\n const records = await read();\n const record = records.find((r) => r.id === id);\n if (record?.audioUri) await fs.deleteAsync(record.audioUri, { idempotent: true }).catch(() => undefined);\n await write(records.filter((r) => r.id !== id));\n }),\n discardRecording: (tempUri) => fs.deleteAsync(tempUri, { idempotent: true }).catch(() => undefined),\n };\n}\n"],"names":["_a"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ const NOTE_DISCLAIMER = "AI draft from a dictation. Review before use.";
2
+ const ACTION_LABEL = {
3
+ start: "start",
4
+ continue: "continue",
5
+ change: "change",
6
+ stop: "stop",
7
+ mentioned: "mentioned"
8
+ };
9
+ function medicationLine(med) {
10
+ const parts = [med.name, med.dose, med.route, med.frequency].map((p) => p.trim()).filter(Boolean);
11
+ return `${parts.join(" ")} (${ACTION_LABEL[med.action]})`;
12
+ }
13
+ function noteToText(note) {
14
+ const lines = [note.title, NOTE_DISCLAIMER];
15
+ const section = (heading, items) => {
16
+ if (!items.length) return;
17
+ lines.push("", heading, ...items.map((item) => `- ${item}`));
18
+ };
19
+ section("CHECK BEFORE USE", note.to_verify);
20
+ section("SUBJECTIVE", note.subjective);
21
+ section("OBJECTIVE", note.objective);
22
+ section("ASSESSMENT", note.assessment);
23
+ section("PLAN", note.plan);
24
+ section("MEDICATIONS", note.medications.map(medicationLine));
25
+ if (note.follow_up.trim()) lines.push("", "FOLLOW-UP", note.follow_up.trim());
26
+ return lines.join("\n");
27
+ }
28
+ function formatDuration(ms) {
29
+ const total = Math.max(0, Math.floor(ms / 1e3));
30
+ const h = Math.floor(total / 3600);
31
+ const m = Math.floor(total % 3600 / 60);
32
+ const s = total % 60;
33
+ const ss = String(s).padStart(2, "0");
34
+ return h ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
35
+ }export{NOTE_DISCLAIMER,formatDuration,medicationLine,noteToText};//# sourceMappingURL=noteText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noteText.js","sources":["../../../src/features/dictate/noteText.ts"],"sourcesContent":["/**\n * The note as plain text, for Copy and Share: section headings in capitals,\n * one item per line, the checks first so they cannot be missed once pasted.\n */\nimport type { ClinicalNote, NoteMedication } from './types';\n\nexport const NOTE_DISCLAIMER = 'AI draft from a dictation. Review before use.';\n\nconst ACTION_LABEL: Record<NoteMedication['action'], string> = {\n start: 'start',\n continue: 'continue',\n change: 'change',\n stop: 'stop',\n mentioned: 'mentioned',\n};\n\n/** \"lisinopril 20 mg PO once daily (change)\" - only the parts that were dictated. */\nexport function medicationLine(med: NoteMedication): string {\n const parts = [med.name, med.dose, med.route, med.frequency].map((p) => p.trim()).filter(Boolean);\n return `${parts.join(' ')} (${ACTION_LABEL[med.action]})`;\n}\n\nexport function noteToText(note: ClinicalNote): string {\n const lines: string[] = [note.title, NOTE_DISCLAIMER];\n const section = (heading: string, items: string[]) => {\n if (!items.length) return;\n lines.push('', heading, ...items.map((item) => `- ${item}`));\n };\n section('CHECK BEFORE USE', note.to_verify);\n section('SUBJECTIVE', note.subjective);\n section('OBJECTIVE', note.objective);\n section('ASSESSMENT', note.assessment);\n section('PLAN', note.plan);\n section('MEDICATIONS', note.medications.map(medicationLine));\n if (note.follow_up.trim()) lines.push('', 'FOLLOW-UP', note.follow_up.trim());\n return lines.join('\\n');\n}\n\n/** \"4:07\" or \"1:02:09\". */\nexport function formatDuration(ms: number): string {\n const total = Math.max(0, Math.floor(ms / 1000));\n const h = Math.floor(total / 3600);\n const m = Math.floor((total % 3600) / 60);\n const s = total % 60;\n const ss = String(s).padStart(2, '0');\n return h ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;\n}\n"],"names":[],"mappings":"AAKO,MAAM,eAAkB,GAAA;AAC/B,MAAM,YAAyD,GAAA;AAAA,EAC7D,KAAO,EAAA,OAAA;AAAA,EACP,QAAU,EAAA,UAAA;AAAA,EACV,MAAQ,EAAA,QAAA;AAAA,EACR,IAAM,EAAA,MAAA;AAAA,EACN,SAAW,EAAA;AACb,CAAA;AAGO,SAAS,eAAe,GAA6B,EAAA;AAC1D,EAAA,MAAM,QAAQ,CAAC,GAAA,CAAI,MAAM,GAAI,CAAA,IAAA,EAAM,IAAI,KAAO,EAAA,GAAA,CAAI,SAAS,CAAA,CAAE,IAAI,CAAK,CAAA,KAAA,CAAA,CAAE,MAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AAC9F,EAAO,OAAA,CAAA,EAAG,MAAM,IAAK,CAAA,GAAG,CAAC,CAAK,EAAA,EAAA,YAAA,CAAa,GAAI,CAAA,MAAM,CAAC,CAAA,CAAA,CAAA;AACxD;AACO,SAAS,WAAW,IAA4B,EAAA;AACrD,EAAA,MAAM,KAAkB,GAAA,CAAC,IAAK,CAAA,KAAA,EAAO,eAAe,CAAA;AACpD,EAAM,MAAA,OAAA,GAAU,CAAC,OAAA,EAAiB,KAAoB,KAAA;AACpD,IAAI,IAAA,CAAC,MAAM,MAAQ,EAAA;AACnB,IAAM,KAAA,CAAA,IAAA,CAAK,EAAI,EAAA,OAAA,EAAS,GAAG,KAAA,CAAM,IAAI,CAAQ,IAAA,KAAA,CAAA,EAAA,EAAK,IAAI,CAAA,CAAE,CAAC,CAAA;AAAA,GAC3D;AACA,EAAQ,OAAA,CAAA,kBAAA,EAAoB,KAAK,SAAS,CAAA;AAC1C,EAAQ,OAAA,CAAA,YAAA,EAAc,KAAK,UAAU,CAAA;AACrC,EAAQ,OAAA,CAAA,WAAA,EAAa,KAAK,SAAS,CAAA;AACnC,EAAQ,OAAA,CAAA,YAAA,EAAc,KAAK,UAAU,CAAA;AACrC,EAAQ,OAAA,CAAA,MAAA,EAAQ,KAAK,IAAI,CAAA;AACzB,EAAA,OAAA,CAAQ,aAAe,EAAA,IAAA,CAAK,WAAY,CAAA,GAAA,CAAI,cAAc,CAAC,CAAA;AAC3D,EAAI,IAAA,IAAA,CAAK,SAAU,CAAA,IAAA,EAAQ,EAAA,KAAA,CAAM,IAAK,CAAA,EAAA,EAAI,WAAa,EAAA,IAAA,CAAK,SAAU,CAAA,IAAA,EAAM,CAAA;AAC5E,EAAO,OAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAGO,SAAS,eAAe,EAAoB,EAAA;AACjD,EAAM,MAAA,KAAA,GAAQ,KAAK,GAAI,CAAA,CAAA,EAAG,KAAK,KAAM,CAAA,EAAA,GAAK,GAAI,CAAC,CAAA;AAC/C,EAAA,MAAM,CAAI,GAAA,IAAA,CAAK,KAAM,CAAA,KAAA,GAAQ,IAAI,CAAA;AACjC,EAAA,MAAM,CAAI,GAAA,IAAA,CAAK,KAAM,CAAA,KAAA,GAAQ,OAAO,EAAE,CAAA;AACtC,EAAA,MAAM,IAAI,KAAQ,GAAA,EAAA;AAClB,EAAA,MAAM,KAAK,MAAO,CAAA,CAAC,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AACpC,EAAA,OAAO,IAAI,CAAG,EAAA,CAAC,CAAI,CAAA,EAAA,MAAA,CAAO,CAAC,CAAE,CAAA,QAAA,CAAS,CAAG,EAAA,GAAG,CAAC,CAAI,CAAA,EAAA,EAAE,KAAK,CAAG,EAAA,CAAC,IAAI,EAAE,CAAA,CAAA;AACpE"}
@@ -0,0 +1,121 @@
1
+ import {useState,useRef,useCallback,useEffect}from'react';import {AppState}from'react-native';import {useAudioRecorder,useAudioRecorderState,setAudioModeAsync,requestRecordingPermissionsAsync,RecordingPresets,AudioQuality,IOSOutputFormat}from'expo-audio';import {deactivateKeepAwake,activateKeepAwakeAsync}from'expo-keep-awake';var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ const DICTATION_RECORDING = __spreadProps(__spreadValues({}, RecordingPresets.HIGH_QUALITY), {
21
+ extension: ".m4a",
22
+ sampleRate: 44100,
23
+ numberOfChannels: 1,
24
+ bitRate: 64e3,
25
+ isMeteringEnabled: true,
26
+ ios: __spreadProps(__spreadValues({}, RecordingPresets.HIGH_QUALITY.ios), {
27
+ outputFormat: IOSOutputFormat.MPEG4AAC,
28
+ audioQuality: AudioQuality.HIGH
29
+ }),
30
+ android: __spreadProps(__spreadValues({}, RecordingPresets.HIGH_QUALITY.android), {
31
+ outputFormat: "mpeg4",
32
+ audioEncoder: "aac"
33
+ })
34
+ });
35
+ const MAX_DICTATION_MS = 60 * 6e4;
36
+ const KEEP_AWAKE_TAG = "yantra-dictate";
37
+ function levelFromMetering(db) {
38
+ if (typeof db !== "number" || !Number.isFinite(db)) return 0;
39
+ return Math.min(1, Math.max(0, (db + 60) / 60));
40
+ }
41
+ class MicrophoneDeniedError extends Error {
42
+ constructor() {
43
+ super("Microphone access was declined. Allow it in Settings to dictate.");
44
+ this.name = "MicrophoneDeniedError";
45
+ }
46
+ }
47
+ function useDictationRecorder() {
48
+ const recorder = useAudioRecorder(DICTATION_RECORDING);
49
+ const state = useAudioRecorderState(recorder, 100);
50
+ const [phase, setPhase] = useState("idle");
51
+ const [pauseReason, setPauseReason] = useState(null);
52
+ const phaseRef = useRef("idle");
53
+ phaseRef.current = phase;
54
+ const releaseSession = useCallback(async () => {
55
+ await deactivateKeepAwake(KEEP_AWAKE_TAG).catch(() => void 0);
56
+ await setAudioModeAsync({
57
+ allowsRecording: false,
58
+ playsInSilentMode: true
59
+ }).catch(() => void 0);
60
+ }, []);
61
+ const pause = useCallback((reason = "user") => {
62
+ if (phaseRef.current !== "recording") return;
63
+ recorder.pause();
64
+ setPhase("paused");
65
+ setPauseReason(reason);
66
+ }, [recorder]);
67
+ const start = useCallback(async () => {
68
+ const permission = await requestRecordingPermissionsAsync();
69
+ if (!permission.granted) throw new MicrophoneDeniedError();
70
+ await setAudioModeAsync({
71
+ allowsRecording: true,
72
+ playsInSilentMode: true
73
+ });
74
+ await recorder.prepareToRecordAsync();
75
+ recorder.record();
76
+ await activateKeepAwakeAsync(KEEP_AWAKE_TAG).catch(() => void 0);
77
+ setPhase("recording");
78
+ setPauseReason(null);
79
+ }, [recorder]);
80
+ const resume = useCallback(() => {
81
+ if (phaseRef.current !== "paused") return;
82
+ recorder.record();
83
+ setPhase("recording");
84
+ setPauseReason(null);
85
+ }, [recorder]);
86
+ const finish = useCallback(async () => {
87
+ if (phaseRef.current === "idle") return null;
88
+ const durationMs = recorder.getStatus().durationMillis;
89
+ await recorder.stop();
90
+ setPhase("idle");
91
+ setPauseReason(null);
92
+ await releaseSession();
93
+ return recorder.uri ? {
94
+ uri: recorder.uri,
95
+ durationMs
96
+ } : null;
97
+ }, [recorder, releaseSession]);
98
+ useEffect(() => {
99
+ const sub = AppState.addEventListener("change", (next) => {
100
+ if (next !== "active") pause("background");
101
+ });
102
+ return () => sub.remove();
103
+ }, [pause]);
104
+ useEffect(() => {
105
+ if (phase === "recording" && state.durationMillis >= MAX_DICTATION_MS) pause("limit");
106
+ }, [phase, state.durationMillis, pause]);
107
+ useEffect(() => () => {
108
+ if (phaseRef.current !== "idle") void recorder.stop().catch(() => void 0);
109
+ void releaseSession();
110
+ }, [recorder, releaseSession]);
111
+ return {
112
+ phase,
113
+ pauseReason,
114
+ elapsedMs: state.durationMillis,
115
+ level: phase === "recording" ? levelFromMetering(state.metering) : 0,
116
+ start,
117
+ pause: () => pause("user"),
118
+ resume,
119
+ finish
120
+ };
121
+ }export{DICTATION_RECORDING,MAX_DICTATION_MS,MicrophoneDeniedError,levelFromMetering,useDictationRecorder};//# sourceMappingURL=useDictationRecorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDictationRecorder.js","sources":["../../../src/features/dictate/useDictationRecorder.ts"],"sourcesContent":["/**\n * The dictation recorder: mono AAC at 64 kbps (speech, about 0.5 MB a minute),\n * pause and resume, a live level, and the screen kept awake while a dictation\n * is open.\n *\n * This build has no background audio mode, so iOS stops the microphone when\n * the app leaves the screen. Rather than lose audio silently, the recorder\n * pauses itself when the app goes to the background and says so; the\n * clinician resumes when back. Recording with the screen locked needs a\n * native build with `UIBackgroundModes: audio` (and an Android microphone\n * foreground service).\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { AppState } from 'react-native';\nimport {\n AudioQuality,\n IOSOutputFormat,\n RecordingPresets,\n requestRecordingPermissionsAsync,\n setAudioModeAsync,\n useAudioRecorder,\n useAudioRecorderState,\n type RecordingOptions,\n} from 'expo-audio';\nimport { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake';\n\nexport const DICTATION_RECORDING: RecordingOptions = {\n ...RecordingPresets.HIGH_QUALITY,\n extension: '.m4a',\n sampleRate: 44100,\n numberOfChannels: 1,\n bitRate: 64000,\n isMeteringEnabled: true,\n ios: {\n ...RecordingPresets.HIGH_QUALITY.ios,\n outputFormat: IOSOutputFormat.MPEG4AAC,\n audioQuality: AudioQuality.HIGH,\n },\n android: {\n ...RecordingPresets.HIGH_QUALITY.android,\n outputFormat: 'mpeg4',\n audioEncoder: 'aac',\n },\n};\n\n/** One visit is well under this; past it the recording pauses and asks to be finished. */\nexport const MAX_DICTATION_MS = 60 * 60_000;\nconst KEEP_AWAKE_TAG = 'yantra-dictate';\n\nexport type RecorderPhase = 'idle' | 'recording' | 'paused';\nexport type PauseReason = 'user' | 'background' | 'limit' | null;\n\n/** -60 dB (silence) .. 0 dB (loud) mapped to 0..1. */\nexport function levelFromMetering(db: number | undefined): number {\n if (typeof db !== 'number' || !Number.isFinite(db)) return 0;\n return Math.min(1, Math.max(0, (db + 60) / 60));\n}\n\nexport class MicrophoneDeniedError extends Error {\n constructor() {\n super('Microphone access was declined. Allow it in Settings to dictate.');\n this.name = 'MicrophoneDeniedError';\n }\n}\n\nexport function useDictationRecorder() {\n const recorder = useAudioRecorder(DICTATION_RECORDING);\n const state = useAudioRecorderState(recorder, 100);\n const [phase, setPhase] = useState<RecorderPhase>('idle');\n const [pauseReason, setPauseReason] = useState<PauseReason>(null);\n const phaseRef = useRef<RecorderPhase>('idle');\n phaseRef.current = phase;\n\n const releaseSession = useCallback(async () => {\n await deactivateKeepAwake(KEEP_AWAKE_TAG).catch(() => undefined);\n await setAudioModeAsync({ allowsRecording: false, playsInSilentMode: true }).catch(() => undefined);\n }, []);\n\n const pause = useCallback(\n (reason: Exclude<PauseReason, null> = 'user') => {\n if (phaseRef.current !== 'recording') return;\n recorder.pause();\n setPhase('paused');\n setPauseReason(reason);\n },\n [recorder],\n );\n\n const start = useCallback(async () => {\n const permission = await requestRecordingPermissionsAsync();\n if (!permission.granted) throw new MicrophoneDeniedError();\n await setAudioModeAsync({ allowsRecording: true, playsInSilentMode: true });\n await recorder.prepareToRecordAsync();\n recorder.record();\n await activateKeepAwakeAsync(KEEP_AWAKE_TAG).catch(() => undefined);\n setPhase('recording');\n setPauseReason(null);\n }, [recorder]);\n\n const resume = useCallback(() => {\n if (phaseRef.current !== 'paused') return;\n recorder.record();\n setPhase('recording');\n setPauseReason(null);\n }, [recorder]);\n\n /** Ends the recording; resolves to its file (in the recorder's cache - move it before anything else). */\n const finish = useCallback(async (): Promise<{ uri: string; durationMs: number } | null> => {\n if (phaseRef.current === 'idle') return null;\n const durationMs = recorder.getStatus().durationMillis;\n await recorder.stop();\n setPhase('idle');\n setPauseReason(null);\n await releaseSession();\n return recorder.uri ? { uri: recorder.uri, durationMs } : null;\n }, [recorder, releaseSession]);\n\n // The microphone stops when the app leaves the screen: pause, and say so.\n useEffect(() => {\n const sub = AppState.addEventListener('change', (next) => {\n if (next !== 'active') pause('background');\n });\n return () => sub.remove();\n }, [pause]);\n\n useEffect(() => {\n if (phase === 'recording' && state.durationMillis >= MAX_DICTATION_MS) pause('limit');\n }, [phase, state.durationMillis, pause]);\n\n // Leaving the screen mid-dictation stops the microphone and lets the screen sleep.\n useEffect(\n () => () => {\n if (phaseRef.current !== 'idle') void recorder.stop().catch(() => undefined);\n void releaseSession();\n },\n [recorder, releaseSession],\n );\n\n return {\n phase,\n pauseReason,\n elapsedMs: state.durationMillis,\n level: phase === 'recording' ? levelFromMetering(state.metering) : 0,\n start,\n pause: () => pause('user'),\n resume,\n finish,\n };\n}\n"],"names":[],"mappings":"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"}
package/lib/routes.json CHANGED
@@ -1123,6 +1123,56 @@
1123
1123
  "hasComponent": true
1124
1124
  }
1125
1125
  },
1126
+ {
1127
+ "/:orgName/c/dictate-native": {
1128
+ "key": "dictate-native",
1129
+ "path": "/:orgName/c/dictate-native",
1130
+ "name": "DictateNative",
1131
+ "exact": true,
1132
+ "priority": 4,
1133
+ "hideInMenu": true,
1134
+ "auth": true,
1135
+ "menu_position": "side",
1136
+ "importPaths": {
1137
+ "navigationHeader": "@adminide-stack/yantra-mobile/lib/components/NavigationHeader/NavigationHeader.js"
1138
+ },
1139
+ "customHeader": {
1140
+ "name": "NavigationHeader",
1141
+ "component": "$.importPaths.navigationHeader",
1142
+ "props": {
1143
+ "showTitle": true,
1144
+ "showToggle": false,
1145
+ "showFilter": false,
1146
+ "isSearchBack": false,
1147
+ "showBack": true,
1148
+ "showGateway": false,
1149
+ "showHistory": false,
1150
+ "showNewChat": false
1151
+ }
1152
+ },
1153
+ "props": {
1154
+ "initialParams": {
1155
+ "orgName": null,
1156
+ "hideNavigationHeader": true
1157
+ },
1158
+ "options": {
1159
+ "title": "Dictate",
1160
+ "drawerItemStyle": {
1161
+ "display": "none"
1162
+ },
1163
+ "headerTitleAlign": "left",
1164
+ "gestureEnabled": false,
1165
+ "swipeEnabled": false,
1166
+ "animation": "fade",
1167
+ "animationTypeForReplace": "pop",
1168
+ "keyboardHandlingEnabled": false,
1169
+ "freezeOnBlur": true
1170
+ }
1171
+ },
1172
+ "componentPath": "@adminide-stack/yantra-mobile/lib/screens/DictateNative/index.js",
1173
+ "hasComponent": true
1174
+ }
1175
+ },
1126
1176
  {
1127
1177
  "/:orgName/c/webbuilder-native": {
1128
1178
  "key": "webbuilder-native",