@adminide-stack/yantra-mobile 12.0.141 → 12.0.142
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/lib/features/dictate/DictateExperience.js +59 -7
- package/lib/features/dictate/DictateExperience.js.map +1 -1
- package/lib/features/dictate/components/NoteView.js +227 -12
- package/lib/features/dictate/components/NoteView.js.map +1 -1
- package/lib/features/dictate/dictateClient.js +93 -23
- package/lib/features/dictate/dictateClient.js.map +1 -1
- package/lib/features/dictate/dictatePreference.js +19 -0
- package/lib/features/dictate/dictatePreference.js.map +1 -0
- package/lib/features/dictate/noteText.js +17 -2
- package/lib/features/dictate/noteText.js.map +1 -1
- package/lib/features/dictate/privacy.js +96 -0
- package/lib/features/dictate/privacy.js.map +1 -0
- package/lib/features/dictate/shownNote.js +24 -0
- package/lib/features/dictate/shownNote.js.map +1 -0
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {jsx,jsxs}from'react/jsx-runtime';import {useMemo,useState,
|
|
1
|
+
import {jsx,jsxs}from'react/jsx-runtime';import {useMemo,useState,useEffect,useCallback,useRef}from'react';import {Alert,BackHandler,Share,View,StyleSheet,ScrollView,Text,Linking}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {useSafeInsets}from'../canvas/withRemoteSafeArea.js';import {createRemoteUploadMedia,createRemoteConnectorExec}from'../canvas/videoEditor/remoteMediaClient.js';import {useAppTheme}from'../../theme/AppThemeProvider.js';import {Tap,DIM}from'../inbox/components/Tap.js';import {DictationList}from'./components/DictationList.js';import {NoteView}from'./components/NoteView.js';import {RecordingPanel,StartPanel}from'./components/RecordPanel.js';import {writeNoteFromRecording,dictateErrorText,suggestFromNote,suggestErrorText}from'./dictateClient.js';import {readShowIdentifiers,writeShowIdentifiers}from'./dictatePreference.js';import {dictateTheme}from'./dictateTheme.js';import {createDictationStore}from'./dictationStore.js';import {noteToText}from'./noteText.js';import {labelText}from'./privacy.js';import {shownNote}from'./shownNote.js';import {useDictationRecorder,MicrophoneDeniedError}from'./useDictationRecorder.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -41,7 +41,7 @@ function DictateExperience({
|
|
|
41
41
|
deps,
|
|
42
42
|
store: injectedStore
|
|
43
43
|
}) {
|
|
44
|
-
var _a, _b;
|
|
44
|
+
var _a, _b, _c;
|
|
45
45
|
const appTheme = useAppTheme();
|
|
46
46
|
const theme = useMemo(() => dictateTheme(hostDark != null ? hostDark : appTheme.isDark), [hostDark, appTheme.isDark]);
|
|
47
47
|
const insets = useSafeInsets();
|
|
@@ -55,6 +55,17 @@ function DictateExperience({
|
|
|
55
55
|
const [starting, setStarting] = useState(false);
|
|
56
56
|
const [error, setError] = useState(null);
|
|
57
57
|
const [micDenied, setMicDenied] = useState(false);
|
|
58
|
+
const [showIdentifiers, setShowIdentifiers] = useState(false);
|
|
59
|
+
const [suggestStates, setSuggestStates] = useState({});
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
void readShowIdentifiers().then(setShowIdentifiers);
|
|
62
|
+
}, []);
|
|
63
|
+
const toggleIdentifiers = useCallback(() => {
|
|
64
|
+
setShowIdentifiers((show) => {
|
|
65
|
+
writeShowIdentifiers(!show);
|
|
66
|
+
return !show;
|
|
67
|
+
});
|
|
68
|
+
}, []);
|
|
58
69
|
const tokenRef = useRef(token);
|
|
59
70
|
tokenRef.current = token;
|
|
60
71
|
const agent = useMemo(() => deps != null ? deps : {
|
|
@@ -103,6 +114,7 @@ function DictateExperience({
|
|
|
103
114
|
status: "ready",
|
|
104
115
|
note: written.note,
|
|
105
116
|
transcript: written.transcript,
|
|
117
|
+
redaction: written.redaction,
|
|
106
118
|
model: written.model,
|
|
107
119
|
error: void 0
|
|
108
120
|
});
|
|
@@ -114,9 +126,9 @@ function DictateExperience({
|
|
|
114
126
|
});
|
|
115
127
|
} finally {
|
|
116
128
|
setStages((_a2) => {
|
|
117
|
-
var
|
|
129
|
+
var _c2 = _a2, {
|
|
118
130
|
[_b2 = record.id]: _done
|
|
119
|
-
} =
|
|
131
|
+
} = _c2, rest = __objRest(_c2, [
|
|
120
132
|
__restKey(_b2)
|
|
121
133
|
]);
|
|
122
134
|
return rest;
|
|
@@ -124,6 +136,40 @@ function DictateExperience({
|
|
|
124
136
|
await refresh();
|
|
125
137
|
}
|
|
126
138
|
}, [agent, store, refresh]);
|
|
139
|
+
const suggest = useCallback(async (record) => {
|
|
140
|
+
if (!record.redaction) return;
|
|
141
|
+
setSuggestStates((s) => __spreadProps(__spreadValues({}, s), {
|
|
142
|
+
[record.id]: {
|
|
143
|
+
status: "loading"
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
try {
|
|
147
|
+
const {
|
|
148
|
+
items,
|
|
149
|
+
identifiers
|
|
150
|
+
} = await suggestFromNote(record.redaction, agent);
|
|
151
|
+
await store.update(record.id, {
|
|
152
|
+
suggestions: {
|
|
153
|
+
items,
|
|
154
|
+
identifiers,
|
|
155
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
await refresh();
|
|
159
|
+
setSuggestStates((s) => __spreadProps(__spreadValues({}, s), {
|
|
160
|
+
[record.id]: {
|
|
161
|
+
status: "idle"
|
|
162
|
+
}
|
|
163
|
+
}));
|
|
164
|
+
} catch (err) {
|
|
165
|
+
setSuggestStates((s) => __spreadProps(__spreadValues({}, s), {
|
|
166
|
+
[record.id]: {
|
|
167
|
+
status: "error",
|
|
168
|
+
message: suggestErrorText(err)
|
|
169
|
+
}
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
}, [agent, store, refresh]);
|
|
127
173
|
const start = useCallback(async () => {
|
|
128
174
|
setError(null);
|
|
129
175
|
setStarting(true);
|
|
@@ -217,11 +263,13 @@ function DictateExperience({
|
|
|
217
263
|
const open = screen.kind === "note" ? (_a = records.find((r) => r.id === screen.id)) != null ? _a : null : null;
|
|
218
264
|
const share = useCallback(() => {
|
|
219
265
|
if (!(open == null ? void 0 : open.note)) return;
|
|
266
|
+
const shown = shownNote(open, open.note, showIdentifiers);
|
|
267
|
+
const text = noteToText(shown.note, shown.transcript, shown.suggestions);
|
|
220
268
|
void Share.share({
|
|
221
|
-
title:
|
|
222
|
-
message:
|
|
269
|
+
title: labelText(shown.note.title),
|
|
270
|
+
message: labelText(text)
|
|
223
271
|
});
|
|
224
|
-
}, [open]);
|
|
272
|
+
}, [open, showIdentifiers]);
|
|
225
273
|
const remove = useCallback(() => {
|
|
226
274
|
if (!open) return;
|
|
227
275
|
const removed = open.note ? "The note and transcript are removed from this phone." : "The recording is removed from this phone.";
|
|
@@ -247,6 +295,10 @@ function DictateExperience({
|
|
|
247
295
|
if (screen.kind === "note" && open) {
|
|
248
296
|
body = /* @__PURE__ */ jsx(NoteView, { record: open, stage: (_b = stages[open.id]) != null ? _b : null, theme, bottomInset: insets.bottom, onRetry: () => {
|
|
249
297
|
void write(open);
|
|
298
|
+
}, showIdentifiers, onToggleIdentifiers: toggleIdentifiers, suggestState: (_c = suggestStates[open.id]) != null ? _c : {
|
|
299
|
+
status: "idle"
|
|
300
|
+
}, onSuggest: () => {
|
|
301
|
+
void suggest(open);
|
|
250
302
|
} });
|
|
251
303
|
} else if (screen.kind === "record" && recorder.phase !== "idle") {
|
|
252
304
|
body = /* @__PURE__ */ jsx(View, { style: styles.recordBody, children: /* @__PURE__ */ jsx(RecordingPanel, { theme, phase: recorder.phase, pauseReason: recorder.pauseReason, elapsedMs: recorder.elapsedMs, level: recorder.level, noInput: recorder.noInput, onPause: recorder.pause, onResume: recorder.resume, onDone: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DictateExperience.js","sources":["../../../src/features/dictate/DictateExperience.tsx"],"sourcesContent":["/**\n * Dictate: a clinician speaks a visit note; Yantra writes it up as a SOAP note\n * with the points to check before it is used.\n *\n * Record (screen kept awake, paused honestly if the app leaves the screen) ->\n * the recording is kept on the phone at once -> uploaded and handed to\n * YANTRA_MEETINGS_CLINICAL_NOTE (medical speech-to-text, then the note) ->\n * the note is saved on the phone and the recording deleted. A note that could\n * not be written keeps its recording for Retry, across restarts.\n *\n * The host screen passes the session (token + surface index URL); everything\n * else - the recorder, the store, the agent calls - lives here.\n */\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Alert, BackHandler, Linking, ScrollView, Share, StyleSheet, Text, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { useSafeInsets } from '../canvas/withRemoteSafeArea';\nimport { createRemoteConnectorExec, createRemoteUploadMedia } from '../canvas/videoEditor/remoteMediaClient';\nimport { useAppTheme } from '../../theme/AppThemeProvider';\nimport { DIM, Tap } from '../inbox/components/Tap';\nimport { DictationList } from './components/DictationList';\nimport { NoteView } from './components/NoteView';\nimport { RecordingPanel, StartPanel } from './components/RecordPanel';\nimport { dictateErrorText, writeNoteFromRecording, type DictateDeps, type DictateStage } from './dictateClient';\nimport { dictateTheme } from './dictateTheme';\nimport { createDictationStore, type DictationStore } from './dictationStore';\nimport { noteToText } from './noteText';\nimport type { DictationRecord } from './types';\nimport { MicrophoneDeniedError, useDictationRecorder } from './useDictationRecorder';\n\nexport interface DictateExperienceProps {\n isDark?: boolean;\n token?: string | null;\n /** The surface index URL; the agent (uploads, the note) is derived from it. */\n indexUrl: string;\n sessionError?: string | null;\n onRetrySession?: () => void;\n onExit: () => void;\n /** Tests and previews inject these; the app builds them from the session. */\n deps?: DictateDeps;\n store?: DictationStore;\n}\n\ntype Screen = { kind: 'home' } | { kind: 'record' } | { kind: 'note'; id: string };\n\nexport const PREVIEW_NOTICE =\n 'Preview: dictate made-up visits only. Real patient information needs your practice’s HIPAA agreement with Yantra first.';\n\nexport function DictateExperience({\n isDark: hostDark,\n token,\n indexUrl,\n sessionError,\n onRetrySession,\n onExit,\n deps,\n store: injectedStore,\n}: DictateExperienceProps) {\n const appTheme = useAppTheme();\n const theme = useMemo(() => dictateTheme(hostDark ?? appTheme.isDark), [hostDark, appTheme.isDark]);\n const insets = useSafeInsets();\n const store = useMemo(() => injectedStore ?? createDictationStore(), [injectedStore]);\n const recorder = useDictationRecorder();\n const [screen, setScreen] = useState<Screen>({ kind: 'home' });\n const [records, setRecords] = useState<DictationRecord[]>([]);\n const [stages, setStages] = useState<Record<string, DictateStage>>({});\n const [starting, setStarting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [micDenied, setMicDenied] = useState(false);\n\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const agent = useMemo<DictateDeps>(\n () =>\n deps ?? {\n exec: (connector, action, params, options) => {\n const { current } = tokenRef;\n if (!current) return Promise.reject(new Error('Network request failed: not signed in yet'));\n return createRemoteConnectorExec(current, indexUrl)(connector, action, params, options);\n },\n upload: (uri) => {\n const { current } = tokenRef;\n if (!current) return Promise.reject(new Error('Network request failed: not signed in yet'));\n return createRemoteUploadMedia(current, indexUrl)(uri);\n },\n },\n [deps, indexUrl],\n );\n\n const refresh = useCallback(async () => setRecords(await store.list()), [store]);\n\n // A note being written when the app last closed never finished: offer it again.\n useEffect(() => {\n void (async () => {\n for (const r of await store.list()) {\n if (r.status === 'writing') await store.update(r.id, { status: r.audioUri ? 'saved' : 'failed' });\n }\n await refresh();\n })();\n }, [store, refresh]);\n\n const write = useCallback(\n async (record: DictationRecord) => {\n if (!record.audioUri) return;\n setStages((s) => ({ ...s, [record.id]: 'uploading' }));\n await store.update(record.id, { status: 'writing', error: undefined });\n await refresh();\n try {\n const written = await writeNoteFromRecording(record.audioUri, agent, {}, (stage) =>\n setStages((s) => ({ ...s, [record.id]: stage })),\n );\n await store.update(record.id, {\n status: 'ready',\n note: written.note,\n transcript: written.transcript,\n model: written.model,\n error: undefined,\n });\n await store.dropAudio(record.id);\n } catch (err) {\n await store.update(record.id, { status: 'failed', error: dictateErrorText(err) });\n } finally {\n setStages(({ [record.id]: _done, ...rest }) => rest);\n await refresh();\n }\n },\n [agent, store, refresh],\n );\n\n const start = useCallback(async () => {\n setError(null);\n setStarting(true);\n try {\n await recorder.start();\n setMicDenied(false);\n setScreen({ kind: 'record' });\n } catch (err) {\n // A declined microphone is not an error to retry: it is a setting, so offer the way there.\n if (err instanceof MicrophoneDeniedError) setMicDenied(true);\n else setError('The microphone could not start. Try again.');\n } finally {\n setStarting(false);\n }\n }, [recorder]);\n\n const done = useCallback(async () => {\n const out = await recorder.finish();\n if (!out) {\n setScreen({ kind: 'home' });\n setError('The recording could not be saved. Try again.');\n return;\n }\n const record = await store.keepRecording(out.uri, out.durationMs);\n await refresh();\n setScreen({ kind: 'note', id: record.id });\n void write(record);\n }, [recorder, store, refresh, write]);\n\n const discard = useCallback(() => {\n Alert.alert('Discard this dictation?', 'The recording is deleted and no note is written.', [\n { text: 'Keep dictating', style: 'cancel' },\n {\n text: 'Discard',\n style: 'destructive',\n onPress: () => {\n void (async () => {\n const out = await recorder.finish();\n if (out) await store.discardRecording(out.uri);\n setScreen({ kind: 'home' });\n })();\n },\n },\n ]);\n }, [recorder, store]);\n\n const back = useCallback(() => {\n if (screen.kind === 'record') {\n if (recorder.phase === 'recording') recorder.pause();\n Alert.alert('Stop dictating?', 'Write the note from what you have recorded, or discard it.', [\n { text: 'Keep dictating', style: 'cancel' },\n {\n text: 'Write the note',\n onPress: () => {\n void done();\n },\n },\n {\n text: 'Discard',\n style: 'destructive',\n onPress: () => {\n void (async () => {\n const out = await recorder.finish();\n if (out) await store.discardRecording(out.uri);\n setScreen({ kind: 'home' });\n })();\n },\n },\n ]);\n return true;\n }\n if (screen.kind === 'note') {\n setScreen({ kind: 'home' });\n return true;\n }\n onExit();\n return true;\n }, [screen.kind, recorder, done, store, onExit]);\n\n useEffect(() => {\n const sub = BackHandler.addEventListener('hardwareBackPress', back);\n return () => sub.remove();\n }, [back]);\n\n const open = screen.kind === 'note' ? (records.find((r) => r.id === screen.id) ?? null) : null;\n\n const share = useCallback(() => {\n if (!open?.note) return;\n void Share.share({ title: open.note.title, message: noteToText(open.note) });\n }, [open]);\n\n const remove = useCallback(() => {\n if (!open) return;\n const removed = open.note\n ? 'The note and transcript are removed from this phone.'\n : 'The recording is removed from this phone.';\n Alert.alert('Delete this dictation?', removed, [\n { text: 'Cancel', style: 'cancel' },\n {\n text: 'Delete',\n style: 'destructive',\n onPress: () => {\n void (async () => {\n await store.remove(open.id);\n await refresh();\n setScreen({ kind: 'home' });\n })();\n },\n },\n ]);\n }, [open, store, refresh]);\n\n const title = screen.kind === 'note' ? 'Note' : 'Dictate';\n\n // The note, the live recording, or the home list - whichever screen is open.\n let body: React.ReactNode;\n if (screen.kind === 'note' && open) {\n body = (\n <NoteView\n record={open}\n stage={stages[open.id] ?? null}\n theme={theme}\n bottomInset={insets.bottom}\n onRetry={() => {\n void write(open);\n }}\n />\n );\n } else if (screen.kind === 'record' && recorder.phase !== 'idle') {\n body = (\n <View style={styles.recordBody}>\n <RecordingPanel\n theme={theme}\n phase={recorder.phase}\n pauseReason={recorder.pauseReason}\n elapsedMs={recorder.elapsedMs}\n level={recorder.level}\n noInput={recorder.noInput}\n onPause={recorder.pause}\n onResume={recorder.resume}\n onDone={() => {\n void done();\n }}\n onDiscard={discard}\n />\n </View>\n );\n } else {\n body = (\n <ScrollView contentContainerStyle={[styles.home, { paddingBottom: 24 + insets.bottom }]}>\n <Text style={[styles.lead, { color: theme.ink }]}>Dictate a visit</Text>\n <Text style={[styles.leadDetail, { color: theme.muted }]}>\n Speak your note. Yantra writes it up as a SOAP note and lists what to check before you use it.\n </Text>\n <StartPanel\n theme={theme}\n starting={starting}\n recordsInBackground={recorder.recordsInBackground}\n onStart={() => {\n void start();\n }}\n />\n {error ? <Text style={[styles.error, { color: theme.crit }]}>{error}</Text> : null}\n {micDenied ? (\n <View style={[styles.banner, { backgroundColor: theme.recSoft }]}>\n <MaterialCommunityIcons name=\"microphone-off\" size={18} color={theme.crit} />\n <Text style={[styles.bannerText, { color: theme.crit }]}>\n Microphone access is off for Yantra.\n </Text>\n <Tap\n onPress={() => {\n void Linking.openSettings();\n }}\n accessibilityRole=\"button\"\n style={[styles.bannerAction, { backgroundColor: theme.card, borderColor: theme.border }]}\n pressedStyle={DIM}\n >\n <Text style={[styles.bannerActionText, { color: theme.ink }]}>Open Settings</Text>\n </Tap>\n </View>\n ) : null}\n {sessionError ? (\n <Tap\n onPress={onRetrySession}\n accessibilityRole=\"button\"\n style={[styles.banner, { backgroundColor: theme.recSoft }]}\n pressedStyle={DIM}\n >\n <Text style={[styles.bannerText, { color: theme.crit }]}>\n Not connected, so notes cannot be written yet. Tap to retry.\n </Text>\n </Tap>\n ) : null}\n <View\n style={[\n styles.banner,\n {\n backgroundColor: theme.warnSoft,\n borderColor: theme.warnBorder,\n borderWidth: StyleSheet.hairlineWidth,\n },\n ]}\n >\n <MaterialCommunityIcons name=\"shield-alert-outline\" size={18} color={theme.warnInk} />\n <Text style={[styles.bannerText, { color: theme.warnInk }]}>{PREVIEW_NOTICE}</Text>\n </View>\n <Text style={[styles.sectionLabel, { color: theme.muted }]}>RECENT</Text>\n <DictationList records={records} theme={theme} onOpen={(r) => setScreen({ kind: 'note', id: r.id })} />\n </ScrollView>\n );\n }\n\n return (\n <View style={[styles.screen, { backgroundColor: theme.screen, paddingTop: insets.top }]}>\n <View style={[styles.header, { borderBottomColor: theme.border }]}>\n <Tap\n onPress={back}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Back\"\n style={styles.headerButton}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons name=\"chevron-left\" size={28} color={theme.ink} />\n </Tap>\n <Text style={[styles.headerTitle, { color: theme.ink }]}>{title}</Text>\n <View style={styles.headerActions}>\n {open?.note ? (\n <Tap\n onPress={share}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Share the note\"\n style={styles.headerButton}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons name=\"export-variant\" size={22} color={theme.ink} />\n </Tap>\n ) : null}\n {/* Any dictation can go, note or not; just not while its note is being written. */}\n {open && !stages[open.id] ? (\n <Tap\n onPress={remove}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Delete\"\n style={styles.headerButton}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons name=\"trash-can-outline\" size={22} color={theme.ink} />\n </Tap>\n ) : null}\n </View>\n </View>\n\n {body}\n </View>\n );\n}\n\nexport default DictateExperience;\n\nconst styles = StyleSheet.create({\n screen: { flex: 1 },\n header: {\n height: 52,\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 4,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n headerButton: { width: 44, height: 44, alignItems: 'center', justifyContent: 'center' },\n headerTitle: { flex: 1, fontSize: 17, fontWeight: '600' },\n headerActions: { flexDirection: 'row', alignItems: 'center' },\n home: { paddingHorizontal: 16, paddingTop: 20 },\n lead: { fontSize: 24, fontWeight: '700' },\n leadDetail: { marginTop: 6, fontSize: 15, lineHeight: 21 },\n error: { textAlign: 'center', fontSize: 13, marginBottom: 8 },\n banner: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 10,\n borderRadius: 12,\n paddingHorizontal: 12,\n paddingVertical: 10,\n marginBottom: 12,\n },\n bannerText: { flex: 1, fontSize: 13, lineHeight: 18 },\n bannerAction: {\n paddingHorizontal: 12,\n paddingVertical: 7,\n borderRadius: 999,\n borderWidth: StyleSheet.hairlineWidth,\n },\n bannerActionText: { fontSize: 13, fontWeight: '600' },\n sectionLabel: { marginTop: 12, marginBottom: 8, fontSize: 12, fontWeight: '700', letterSpacing: 0.8 },\n recordBody: { flex: 1, justifyContent: 'center', paddingHorizontal: 16 },\n});\n"],"names":["_b","_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DictateExperience.js","sources":["../../../src/features/dictate/DictateExperience.tsx"],"sourcesContent":["/**\n * Dictate: a clinician speaks a visit note; Yantra writes it up as a SOAP note\n * with the points to check before it is used.\n *\n * Record (screen kept awake, paused honestly if the app leaves the screen) ->\n * the recording is kept on the phone at once -> uploaded and handed to\n * YANTRA_MEETINGS_CLINICAL_NOTE (medical speech-to-text, then the note) ->\n * the note is saved on the phone and the recording deleted. A note that could\n * not be written keeps its recording for Retry, across restarts.\n *\n * The host screen passes the session (token + surface index URL); everything\n * else - the recorder, the store, the agent calls - lives here.\n */\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Alert, BackHandler, Linking, ScrollView, Share, StyleSheet, Text, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { useSafeInsets } from '../canvas/withRemoteSafeArea';\nimport { createRemoteConnectorExec, createRemoteUploadMedia } from '../canvas/videoEditor/remoteMediaClient';\nimport { useAppTheme } from '../../theme/AppThemeProvider';\nimport { DIM, Tap } from '../inbox/components/Tap';\nimport { DictationList } from './components/DictationList';\nimport { NoteView } from './components/NoteView';\nimport { RecordingPanel, StartPanel } from './components/RecordPanel';\nimport {\n dictateErrorText,\n suggestErrorText,\n suggestFromNote,\n writeNoteFromRecording,\n type DictateDeps,\n type DictateStage,\n} from './dictateClient';\nimport { readShowIdentifiers, writeShowIdentifiers } from './dictatePreference';\nimport { dictateTheme } from './dictateTheme';\nimport { createDictationStore, type DictationStore } from './dictationStore';\nimport { noteToText } from './noteText';\nimport { labelText } from './privacy';\nimport { shownNote, type SuggestState } from './shownNote';\nimport type { DictationRecord } from './types';\nimport { MicrophoneDeniedError, useDictationRecorder } from './useDictationRecorder';\n\nexport interface DictateExperienceProps {\n isDark?: boolean;\n token?: string | null;\n /** The surface index URL; the agent (uploads, the note) is derived from it. */\n indexUrl: string;\n sessionError?: string | null;\n onRetrySession?: () => void;\n onExit: () => void;\n /** Tests and previews inject these; the app builds them from the session. */\n deps?: DictateDeps;\n store?: DictationStore;\n}\n\ntype Screen = { kind: 'home' } | { kind: 'record' } | { kind: 'note'; id: string };\n\nexport const PREVIEW_NOTICE =\n 'Preview: dictate made-up visits only. Real patient information needs your practice’s HIPAA agreement with Yantra first.';\n\nexport function DictateExperience({\n isDark: hostDark,\n token,\n indexUrl,\n sessionError,\n onRetrySession,\n onExit,\n deps,\n store: injectedStore,\n}: DictateExperienceProps) {\n const appTheme = useAppTheme();\n const theme = useMemo(() => dictateTheme(hostDark ?? appTheme.isDark), [hostDark, appTheme.isDark]);\n const insets = useSafeInsets();\n const store = useMemo(() => injectedStore ?? createDictationStore(), [injectedStore]);\n const recorder = useDictationRecorder();\n const [screen, setScreen] = useState<Screen>({ kind: 'home' });\n const [records, setRecords] = useState<DictationRecord[]>([]);\n const [stages, setStages] = useState<Record<string, DictateStage>>({});\n const [starting, setStarting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [micDenied, setMicDenied] = useState(false);\n const [showIdentifiers, setShowIdentifiers] = useState(false);\n const [suggestStates, setSuggestStates] = useState<Record<string, SuggestState>>({});\n\n useEffect(() => {\n void readShowIdentifiers().then(setShowIdentifiers);\n }, []);\n const toggleIdentifiers = useCallback(() => {\n setShowIdentifiers((show) => {\n writeShowIdentifiers(!show);\n return !show;\n });\n }, []);\n\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const agent = useMemo<DictateDeps>(\n () =>\n deps ?? {\n exec: (connector, action, params, options) => {\n const { current } = tokenRef;\n if (!current) return Promise.reject(new Error('Network request failed: not signed in yet'));\n return createRemoteConnectorExec(current, indexUrl)(connector, action, params, options);\n },\n upload: (uri) => {\n const { current } = tokenRef;\n if (!current) return Promise.reject(new Error('Network request failed: not signed in yet'));\n return createRemoteUploadMedia(current, indexUrl)(uri);\n },\n },\n [deps, indexUrl],\n );\n\n const refresh = useCallback(async () => setRecords(await store.list()), [store]);\n\n // A note being written when the app last closed never finished: offer it again.\n useEffect(() => {\n void (async () => {\n for (const r of await store.list()) {\n if (r.status === 'writing') await store.update(r.id, { status: r.audioUri ? 'saved' : 'failed' });\n }\n await refresh();\n })();\n }, [store, refresh]);\n\n const write = useCallback(\n async (record: DictationRecord) => {\n if (!record.audioUri) return;\n setStages((s) => ({ ...s, [record.id]: 'uploading' }));\n await store.update(record.id, { status: 'writing', error: undefined });\n await refresh();\n try {\n const written = await writeNoteFromRecording(record.audioUri, agent, {}, (stage) =>\n setStages((s) => ({ ...s, [record.id]: stage })),\n );\n await store.update(record.id, {\n status: 'ready',\n note: written.note,\n transcript: written.transcript,\n redaction: written.redaction,\n model: written.model,\n error: undefined,\n });\n await store.dropAudio(record.id);\n } catch (err) {\n await store.update(record.id, { status: 'failed', error: dictateErrorText(err) });\n } finally {\n setStages(({ [record.id]: _done, ...rest }) => rest);\n await refresh();\n }\n },\n [agent, store, refresh],\n );\n\n const suggest = useCallback(\n async (record: DictationRecord) => {\n if (!record.redaction) return;\n setSuggestStates((s) => ({ ...s, [record.id]: { status: 'loading' } }));\n try {\n const { items, identifiers } = await suggestFromNote(record.redaction, agent);\n await store.update(record.id, {\n suggestions: { items, identifiers, createdAt: new Date().toISOString() },\n });\n await refresh();\n setSuggestStates((s) => ({ ...s, [record.id]: { status: 'idle' } }));\n } catch (err) {\n setSuggestStates((s) => ({ ...s, [record.id]: { status: 'error', message: suggestErrorText(err) } }));\n }\n },\n [agent, store, refresh],\n );\n\n const start = useCallback(async () => {\n setError(null);\n setStarting(true);\n try {\n await recorder.start();\n setMicDenied(false);\n setScreen({ kind: 'record' });\n } catch (err) {\n // A declined microphone is not an error to retry: it is a setting, so offer the way there.\n if (err instanceof MicrophoneDeniedError) setMicDenied(true);\n else setError('The microphone could not start. Try again.');\n } finally {\n setStarting(false);\n }\n }, [recorder]);\n\n const done = useCallback(async () => {\n const out = await recorder.finish();\n if (!out) {\n setScreen({ kind: 'home' });\n setError('The recording could not be saved. Try again.');\n return;\n }\n const record = await store.keepRecording(out.uri, out.durationMs);\n await refresh();\n setScreen({ kind: 'note', id: record.id });\n void write(record);\n }, [recorder, store, refresh, write]);\n\n const discard = useCallback(() => {\n Alert.alert('Discard this dictation?', 'The recording is deleted and no note is written.', [\n { text: 'Keep dictating', style: 'cancel' },\n {\n text: 'Discard',\n style: 'destructive',\n onPress: () => {\n void (async () => {\n const out = await recorder.finish();\n if (out) await store.discardRecording(out.uri);\n setScreen({ kind: 'home' });\n })();\n },\n },\n ]);\n }, [recorder, store]);\n\n const back = useCallback(() => {\n if (screen.kind === 'record') {\n if (recorder.phase === 'recording') recorder.pause();\n Alert.alert('Stop dictating?', 'Write the note from what you have recorded, or discard it.', [\n { text: 'Keep dictating', style: 'cancel' },\n {\n text: 'Write the note',\n onPress: () => {\n void done();\n },\n },\n {\n text: 'Discard',\n style: 'destructive',\n onPress: () => {\n void (async () => {\n const out = await recorder.finish();\n if (out) await store.discardRecording(out.uri);\n setScreen({ kind: 'home' });\n })();\n },\n },\n ]);\n return true;\n }\n if (screen.kind === 'note') {\n setScreen({ kind: 'home' });\n return true;\n }\n onExit();\n return true;\n }, [screen.kind, recorder, done, store, onExit]);\n\n useEffect(() => {\n const sub = BackHandler.addEventListener('hardwareBackPress', back);\n return () => sub.remove();\n }, [back]);\n\n const open = screen.kind === 'note' ? (records.find((r) => r.id === screen.id) ?? null) : null;\n\n // What is on screen goes: hidden identifiers stay hidden (\"[Name 1]\"), shown ones go as dictated.\n const share = useCallback(() => {\n if (!open?.note) return;\n const shown = shownNote(open, open.note, showIdentifiers);\n const text = noteToText(shown.note, shown.transcript, shown.suggestions);\n void Share.share({ title: labelText(shown.note.title), message: labelText(text) });\n }, [open, showIdentifiers]);\n\n const remove = useCallback(() => {\n if (!open) return;\n const removed = open.note\n ? 'The note and transcript are removed from this phone.'\n : 'The recording is removed from this phone.';\n Alert.alert('Delete this dictation?', removed, [\n { text: 'Cancel', style: 'cancel' },\n {\n text: 'Delete',\n style: 'destructive',\n onPress: () => {\n void (async () => {\n await store.remove(open.id);\n await refresh();\n setScreen({ kind: 'home' });\n })();\n },\n },\n ]);\n }, [open, store, refresh]);\n\n const title = screen.kind === 'note' ? 'Note' : 'Dictate';\n\n // The note, the live recording, or the home list - whichever screen is open.\n let body: React.ReactNode;\n if (screen.kind === 'note' && open) {\n body = (\n <NoteView\n record={open}\n stage={stages[open.id] ?? null}\n theme={theme}\n bottomInset={insets.bottom}\n onRetry={() => {\n void write(open);\n }}\n showIdentifiers={showIdentifiers}\n onToggleIdentifiers={toggleIdentifiers}\n suggestState={suggestStates[open.id] ?? { status: 'idle' }}\n onSuggest={() => {\n void suggest(open);\n }}\n />\n );\n } else if (screen.kind === 'record' && recorder.phase !== 'idle') {\n body = (\n <View style={styles.recordBody}>\n <RecordingPanel\n theme={theme}\n phase={recorder.phase}\n pauseReason={recorder.pauseReason}\n elapsedMs={recorder.elapsedMs}\n level={recorder.level}\n noInput={recorder.noInput}\n onPause={recorder.pause}\n onResume={recorder.resume}\n onDone={() => {\n void done();\n }}\n onDiscard={discard}\n />\n </View>\n );\n } else {\n body = (\n <ScrollView contentContainerStyle={[styles.home, { paddingBottom: 24 + insets.bottom }]}>\n <Text style={[styles.lead, { color: theme.ink }]}>Dictate a visit</Text>\n <Text style={[styles.leadDetail, { color: theme.muted }]}>\n Speak your note. Yantra writes it up as a SOAP note and lists what to check before you use it.\n </Text>\n <StartPanel\n theme={theme}\n starting={starting}\n recordsInBackground={recorder.recordsInBackground}\n onStart={() => {\n void start();\n }}\n />\n {error ? <Text style={[styles.error, { color: theme.crit }]}>{error}</Text> : null}\n {micDenied ? (\n <View style={[styles.banner, { backgroundColor: theme.recSoft }]}>\n <MaterialCommunityIcons name=\"microphone-off\" size={18} color={theme.crit} />\n <Text style={[styles.bannerText, { color: theme.crit }]}>\n Microphone access is off for Yantra.\n </Text>\n <Tap\n onPress={() => {\n void Linking.openSettings();\n }}\n accessibilityRole=\"button\"\n style={[styles.bannerAction, { backgroundColor: theme.card, borderColor: theme.border }]}\n pressedStyle={DIM}\n >\n <Text style={[styles.bannerActionText, { color: theme.ink }]}>Open Settings</Text>\n </Tap>\n </View>\n ) : null}\n {sessionError ? (\n <Tap\n onPress={onRetrySession}\n accessibilityRole=\"button\"\n style={[styles.banner, { backgroundColor: theme.recSoft }]}\n pressedStyle={DIM}\n >\n <Text style={[styles.bannerText, { color: theme.crit }]}>\n Not connected, so notes cannot be written yet. Tap to retry.\n </Text>\n </Tap>\n ) : null}\n <View\n style={[\n styles.banner,\n {\n backgroundColor: theme.warnSoft,\n borderColor: theme.warnBorder,\n borderWidth: StyleSheet.hairlineWidth,\n },\n ]}\n >\n <MaterialCommunityIcons name=\"shield-alert-outline\" size={18} color={theme.warnInk} />\n <Text style={[styles.bannerText, { color: theme.warnInk }]}>{PREVIEW_NOTICE}</Text>\n </View>\n <Text style={[styles.sectionLabel, { color: theme.muted }]}>RECENT</Text>\n <DictationList records={records} theme={theme} onOpen={(r) => setScreen({ kind: 'note', id: r.id })} />\n </ScrollView>\n );\n }\n\n return (\n <View style={[styles.screen, { backgroundColor: theme.screen, paddingTop: insets.top }]}>\n <View style={[styles.header, { borderBottomColor: theme.border }]}>\n <Tap\n onPress={back}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Back\"\n style={styles.headerButton}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons name=\"chevron-left\" size={28} color={theme.ink} />\n </Tap>\n <Text style={[styles.headerTitle, { color: theme.ink }]}>{title}</Text>\n <View style={styles.headerActions}>\n {open?.note ? (\n <Tap\n onPress={share}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Share the note\"\n style={styles.headerButton}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons name=\"export-variant\" size={22} color={theme.ink} />\n </Tap>\n ) : null}\n {/* Any dictation can go, note or not; just not while its note is being written. */}\n {open && !stages[open.id] ? (\n <Tap\n onPress={remove}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Delete\"\n style={styles.headerButton}\n pressedStyle={DIM}\n >\n <MaterialCommunityIcons name=\"trash-can-outline\" size={22} color={theme.ink} />\n </Tap>\n ) : null}\n </View>\n </View>\n\n {body}\n </View>\n );\n}\n\nexport default DictateExperience;\n\nconst styles = StyleSheet.create({\n screen: { flex: 1 },\n header: {\n height: 52,\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 4,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n headerButton: { width: 44, height: 44, alignItems: 'center', justifyContent: 'center' },\n headerTitle: { flex: 1, fontSize: 17, fontWeight: '600' },\n headerActions: { flexDirection: 'row', alignItems: 'center' },\n home: { paddingHorizontal: 16, paddingTop: 20 },\n lead: { fontSize: 24, fontWeight: '700' },\n leadDetail: { marginTop: 6, fontSize: 15, lineHeight: 21 },\n error: { textAlign: 'center', fontSize: 13, marginBottom: 8 },\n banner: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 10,\n borderRadius: 12,\n paddingHorizontal: 12,\n paddingVertical: 10,\n marginBottom: 12,\n },\n bannerText: { flex: 1, fontSize: 13, lineHeight: 18 },\n bannerAction: {\n paddingHorizontal: 12,\n paddingVertical: 7,\n borderRadius: 999,\n borderWidth: StyleSheet.hairlineWidth,\n },\n bannerActionText: { fontSize: 13, fontWeight: '600' },\n sectionLabel: { marginTop: 12, marginBottom: 8, fontSize: 12, fontWeight: '700', letterSpacing: 0.8 },\n recordBody: { flex: 1, justifyContent: 'center', paddingHorizontal: 16 },\n});\n"],"names":["_b","_a","_c"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {jsx,jsxs}from'react/jsx-runtime';import {useState,useEffect}from'react';import {View,StyleSheet,Text,ScrollView,ActivityIndicator}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {Tap,DIM}from'../../inbox/components/Tap.js';import {formatDuration,NOTE_DISCLAIMER}from'../noteText.js';const STAGE_TEXT = {
|
|
1
|
+
import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useState,useEffect}from'react';import {View,StyleSheet,Text,ScrollView,ActivityIndicator}from'react-native';import {MaterialCommunityIcons}from'@expo/vector-icons';import {Tap,DIM}from'../../inbox/components/Tap.js';import {formatDuration,NOTE_DISCLAIMER,SUGGESTION_HEADINGS,SUGGESTION_DISCLAIMER}from'../noteText.js';import {splitPlaceholders,placeholderLabel}from'../privacy.js';import {shownNote}from'../shownNote.js';const STAGE_TEXT = {
|
|
2
2
|
uploading: "Uploading the recording\u2026",
|
|
3
3
|
writing: "Transcribing and writing the note\u2026"
|
|
4
4
|
};
|
|
@@ -11,6 +11,17 @@ function dictationDate(iso) {
|
|
|
11
11
|
minute: "2-digit"
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
|
+
function Redacted({
|
|
15
|
+
text,
|
|
16
|
+
theme
|
|
17
|
+
}) {
|
|
18
|
+
const runs = splitPlaceholders(text);
|
|
19
|
+
if (runs.length === 1 && !runs[0].placeholder) return /* @__PURE__ */ jsx(Fragment, { children: text });
|
|
20
|
+
return /* @__PURE__ */ jsx(Fragment, { children: runs.map((run, i) => run.placeholder ? /* @__PURE__ */ jsx(Text, { style: [styles.placeholder, {
|
|
21
|
+
backgroundColor: theme.chip,
|
|
22
|
+
color: theme.muted
|
|
23
|
+
}], accessibilityLabel: `${placeholderLabel(run.text)}, hidden`, children: `\xA0${placeholderLabel(run.text).replace(/ /g, "\xA0")}\xA0` }, i) : run.text) });
|
|
24
|
+
}
|
|
14
25
|
function Section({
|
|
15
26
|
title,
|
|
16
27
|
items,
|
|
@@ -30,10 +41,115 @@ function Section({
|
|
|
30
41
|
}] }),
|
|
31
42
|
/* @__PURE__ */ jsx(Text, { style: [styles.itemText, {
|
|
32
43
|
color: theme.ink
|
|
33
|
-
}], children: item })
|
|
44
|
+
}], children: /* @__PURE__ */ jsx(Redacted, { text: item, theme }) })
|
|
34
45
|
] }, i))
|
|
35
46
|
] });
|
|
36
47
|
}
|
|
48
|
+
function PrivacyRow({
|
|
49
|
+
record,
|
|
50
|
+
showIdentifiers,
|
|
51
|
+
onToggle,
|
|
52
|
+
theme
|
|
53
|
+
}) {
|
|
54
|
+
const {
|
|
55
|
+
redaction
|
|
56
|
+
} = record;
|
|
57
|
+
if (!redaction) return null;
|
|
58
|
+
const count = redaction.identifiers.length;
|
|
59
|
+
const what = count === 1 ? "1 name or number" : `${count} names and numbers`;
|
|
60
|
+
const line = count ? `${what} ${showIdentifiers ? "shown here" : "hidden"}. The AI never saw them.` : "No names or numbers were heard. The AI saw the dictation as spoken.";
|
|
61
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.privacy, {
|
|
62
|
+
backgroundColor: theme.goodSoft
|
|
63
|
+
}], children: [
|
|
64
|
+
/* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "shield-lock-outline", size: 18, color: theme.good }),
|
|
65
|
+
/* @__PURE__ */ jsxs(View, { style: styles.privacyText, children: [
|
|
66
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.privacyLine, {
|
|
67
|
+
color: theme.ink
|
|
68
|
+
}], children: line }),
|
|
69
|
+
redaction.method === "patterns" && count ? /* @__PURE__ */ jsx(Text, { style: [styles.privacyNote, {
|
|
70
|
+
color: theme.warnInk
|
|
71
|
+
}], children: "Found without the medical transcriber; check that no name was missed." }) : null
|
|
72
|
+
] }),
|
|
73
|
+
count ? /* @__PURE__ */ jsx(Tap, { onPress: onToggle, accessibilityRole: "button", accessibilityLabel: showIdentifiers ? "Hide names and numbers" : "Show names and numbers", style: [styles.privacyToggle, {
|
|
74
|
+
borderColor: theme.border,
|
|
75
|
+
backgroundColor: theme.card
|
|
76
|
+
}], pressedStyle: DIM, children: /* @__PURE__ */ jsx(Text, { style: [styles.privacyToggleText, {
|
|
77
|
+
color: theme.ink
|
|
78
|
+
}], children: showIdentifiers ? "Hide" : "Show" }) }) : null
|
|
79
|
+
] });
|
|
80
|
+
}
|
|
81
|
+
const SUGGESTION_ICON = {
|
|
82
|
+
missing: "text-box-plus-outline",
|
|
83
|
+
safety: "alert-octagon-outline",
|
|
84
|
+
coding: "tag-outline",
|
|
85
|
+
follow_up: "calendar-check-outline"
|
|
86
|
+
};
|
|
87
|
+
function Suggestions({
|
|
88
|
+
items,
|
|
89
|
+
state,
|
|
90
|
+
onSuggest,
|
|
91
|
+
theme
|
|
92
|
+
}) {
|
|
93
|
+
const loading = state.status === "loading";
|
|
94
|
+
let content;
|
|
95
|
+
if (!items) {
|
|
96
|
+
content = /* @__PURE__ */ jsx(Text, { style: [styles.suggestWhy, {
|
|
97
|
+
color: theme.muted
|
|
98
|
+
}], children: "Ask the AI to read the note and point out what to add or check before you sign. It reads the note with names and numbers hidden." });
|
|
99
|
+
} else if (!items.length) {
|
|
100
|
+
content = /* @__PURE__ */ jsx(Text, { style: [styles.suggestWhy, {
|
|
101
|
+
color: theme.muted
|
|
102
|
+
}], children: "Nothing to add: the note covers what the AI would check." });
|
|
103
|
+
} else {
|
|
104
|
+
content = Object.keys(SUGGESTION_HEADINGS).map((kind) => {
|
|
105
|
+
const group = items.filter((s) => s.kind === kind);
|
|
106
|
+
if (!group.length) return null;
|
|
107
|
+
return /* @__PURE__ */ jsxs(View, { style: styles.suggestGroup, children: [
|
|
108
|
+
/* @__PURE__ */ jsxs(View, { style: styles.suggestHead, children: [
|
|
109
|
+
/* @__PURE__ */ jsx(MaterialCommunityIcons, { name: SUGGESTION_ICON[kind], size: 16, color: kind === "safety" ? theme.crit : theme.accent }),
|
|
110
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.suggestKind, {
|
|
111
|
+
color: theme.ink
|
|
112
|
+
}], children: SUGGESTION_HEADINGS[kind] })
|
|
113
|
+
] }),
|
|
114
|
+
group.map((s, i) => /* @__PURE__ */ jsxs(View, { style: styles.suggestItem, children: [
|
|
115
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.suggestText, {
|
|
116
|
+
color: theme.ink
|
|
117
|
+
}], children: /* @__PURE__ */ jsx(Redacted, { text: s.text, theme }) }),
|
|
118
|
+
s.why ? /* @__PURE__ */ jsx(Text, { style: [styles.suggestWhy, {
|
|
119
|
+
color: theme.muted
|
|
120
|
+
}], children: /* @__PURE__ */ jsx(Redacted, { text: s.why, theme }) }) : null
|
|
121
|
+
] }, i))
|
|
122
|
+
] }, kind);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
let buttonLabel = "Get suggestions";
|
|
126
|
+
if (loading) buttonLabel = "Reading the note\u2026";
|
|
127
|
+
else if (items) buttonLabel = "Ask again";
|
|
128
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.card, {
|
|
129
|
+
backgroundColor: theme.card,
|
|
130
|
+
borderColor: theme.border
|
|
131
|
+
}], children: [
|
|
132
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.heading, {
|
|
133
|
+
color: theme.muted
|
|
134
|
+
}], children: "SUGGESTIONS" }),
|
|
135
|
+
content,
|
|
136
|
+
state.status === "error" ? /* @__PURE__ */ jsx(Text, { style: [styles.suggestError, {
|
|
137
|
+
color: theme.crit
|
|
138
|
+
}], children: state.message }) : null,
|
|
139
|
+
/* @__PURE__ */ jsxs(Tap, { onPress: onSuggest, disabled: loading, accessibilityRole: "button", accessibilityLabel: items ? "Ask for suggestions again" : "Get suggestions", style: [styles.suggestButton, {
|
|
140
|
+
borderColor: theme.accent,
|
|
141
|
+
opacity: loading ? 0.6 : 1
|
|
142
|
+
}], pressedStyle: DIM, children: [
|
|
143
|
+
loading ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.accent, size: "small" }) : /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "lightbulb-on-outline", size: 18, color: theme.accent }),
|
|
144
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.suggestButtonText, {
|
|
145
|
+
color: theme.accent
|
|
146
|
+
}], children: buttonLabel })
|
|
147
|
+
] }),
|
|
148
|
+
(items == null ? void 0 : items.length) ? /* @__PURE__ */ jsx(Text, { style: [styles.suggestFoot, {
|
|
149
|
+
color: theme.faint
|
|
150
|
+
}], children: SUGGESTION_DISCLAIMER }) : null
|
|
151
|
+
] });
|
|
152
|
+
}
|
|
37
153
|
function actionTone(action, theme) {
|
|
38
154
|
if (action === "start") return {
|
|
39
155
|
bg: theme.goodSoft,
|
|
@@ -74,10 +190,10 @@ function Medications({
|
|
|
74
190
|
/* @__PURE__ */ jsxs(View, { style: styles.medText, children: [
|
|
75
191
|
/* @__PURE__ */ jsx(Text, { style: [styles.medName, {
|
|
76
192
|
color: theme.ink
|
|
77
|
-
}], children: med.name }),
|
|
193
|
+
}], children: /* @__PURE__ */ jsx(Redacted, { text: med.name, theme }) }),
|
|
78
194
|
/* @__PURE__ */ jsx(Text, { style: [styles.medDetail, {
|
|
79
195
|
color: detail ? theme.muted : theme.warnInk
|
|
80
|
-
}], children: detail
|
|
196
|
+
}], children: detail ? /* @__PURE__ */ jsx(Redacted, { text: detail, theme }) : "No dose or frequency dictated" })
|
|
81
197
|
] }),
|
|
82
198
|
/* @__PURE__ */ jsx(View, { style: [styles.pill, {
|
|
83
199
|
backgroundColor: tone.bg
|
|
@@ -115,7 +231,11 @@ function NoteView({
|
|
|
115
231
|
stage,
|
|
116
232
|
theme,
|
|
117
233
|
bottomInset,
|
|
118
|
-
onRetry
|
|
234
|
+
onRetry,
|
|
235
|
+
showIdentifiers,
|
|
236
|
+
onToggleIdentifiers,
|
|
237
|
+
suggestState,
|
|
238
|
+
onSuggest
|
|
119
239
|
}) {
|
|
120
240
|
var _a;
|
|
121
241
|
const [showTranscript, setShowTranscript] = useState(false);
|
|
@@ -140,17 +260,20 @@ function NoteView({
|
|
|
140
260
|
] });
|
|
141
261
|
}
|
|
142
262
|
const {
|
|
143
|
-
note
|
|
144
|
-
|
|
263
|
+
note,
|
|
264
|
+
transcript,
|
|
265
|
+
suggestions
|
|
266
|
+
} = shownNote(record, record.note, showIdentifiers);
|
|
145
267
|
return /* @__PURE__ */ jsxs(ScrollView, { contentContainerStyle: [styles.scroll, {
|
|
146
268
|
paddingBottom: 24 + bottomInset
|
|
147
269
|
}], children: [
|
|
148
270
|
/* @__PURE__ */ jsx(Text, { style: [styles.title, {
|
|
149
271
|
color: theme.ink
|
|
150
|
-
}], children: note.title }),
|
|
272
|
+
}], children: /* @__PURE__ */ jsx(Redacted, { text: note.title, theme }) }),
|
|
151
273
|
/* @__PURE__ */ jsx(Text, { style: [styles.meta, {
|
|
152
274
|
color: theme.faint
|
|
153
275
|
}], children: meta }),
|
|
276
|
+
/* @__PURE__ */ jsx(PrivacyRow, { record, showIdentifiers, onToggle: onToggleIdentifiers, theme }),
|
|
154
277
|
note.to_verify.length ? /* @__PURE__ */ jsxs(View, { style: [styles.verify, {
|
|
155
278
|
backgroundColor: theme.warnSoft,
|
|
156
279
|
borderColor: theme.warnBorder
|
|
@@ -161,9 +284,12 @@ function NoteView({
|
|
|
161
284
|
color: theme.warnInk
|
|
162
285
|
}], children: "Check before you use" })
|
|
163
286
|
] }),
|
|
164
|
-
note.to_verify.map((item, i) => /* @__PURE__ */
|
|
287
|
+
note.to_verify.map((item, i) => /* @__PURE__ */ jsxs(Text, { style: [styles.verifyItem, {
|
|
165
288
|
color: theme.warnInk
|
|
166
|
-
}], children:
|
|
289
|
+
}], children: [
|
|
290
|
+
"\u2022 ",
|
|
291
|
+
/* @__PURE__ */ jsx(Redacted, { text: item, theme })
|
|
292
|
+
] }, i))
|
|
167
293
|
] }) : null,
|
|
168
294
|
/* @__PURE__ */ jsx(Section, { title: "SUBJECTIVE", items: note.subjective, theme }),
|
|
169
295
|
/* @__PURE__ */ jsx(Section, { title: "OBJECTIVE", items: note.objective, theme }),
|
|
@@ -171,7 +297,8 @@ function NoteView({
|
|
|
171
297
|
/* @__PURE__ */ jsx(Section, { title: "PLAN", items: note.plan, theme }),
|
|
172
298
|
/* @__PURE__ */ jsx(Medications, { meds: note.medications, theme }),
|
|
173
299
|
note.follow_up ? /* @__PURE__ */ jsx(Section, { title: "FOLLOW-UP", items: [note.follow_up], theme }) : null,
|
|
174
|
-
record.
|
|
300
|
+
record.redaction ? /* @__PURE__ */ jsx(Suggestions, { items: suggestions, state: suggestState, onSuggest, theme }) : null,
|
|
301
|
+
transcript ? /* @__PURE__ */ jsxs(View, { style: [styles.card, {
|
|
175
302
|
backgroundColor: theme.card,
|
|
176
303
|
borderColor: theme.border
|
|
177
304
|
}], children: [
|
|
@@ -183,7 +310,7 @@ function NoteView({
|
|
|
183
310
|
] }),
|
|
184
311
|
showTranscript ? /* @__PURE__ */ jsx(Text, { style: [styles.transcript, {
|
|
185
312
|
color: theme.ink
|
|
186
|
-
}], children:
|
|
313
|
+
}], children: /* @__PURE__ */ jsx(Redacted, { text: transcript, theme }) }) : null
|
|
187
314
|
] }) : null,
|
|
188
315
|
/* @__PURE__ */ jsx(Text, { style: [styles.disclaimer, {
|
|
189
316
|
color: theme.faint
|
|
@@ -301,6 +428,94 @@ const styles = StyleSheet.create({
|
|
|
301
428
|
fontSize: 12,
|
|
302
429
|
textAlign: "center"
|
|
303
430
|
},
|
|
431
|
+
placeholder: {
|
|
432
|
+
fontSize: 13,
|
|
433
|
+
fontWeight: "600",
|
|
434
|
+
borderRadius: 4,
|
|
435
|
+
overflow: "hidden"
|
|
436
|
+
},
|
|
437
|
+
privacy: {
|
|
438
|
+
marginTop: 12,
|
|
439
|
+
borderRadius: 12,
|
|
440
|
+
paddingHorizontal: 12,
|
|
441
|
+
paddingVertical: 10,
|
|
442
|
+
flexDirection: "row",
|
|
443
|
+
alignItems: "center",
|
|
444
|
+
gap: 10
|
|
445
|
+
},
|
|
446
|
+
privacyText: {
|
|
447
|
+
flex: 1
|
|
448
|
+
},
|
|
449
|
+
privacyLine: {
|
|
450
|
+
fontSize: 13,
|
|
451
|
+
lineHeight: 18
|
|
452
|
+
},
|
|
453
|
+
privacyNote: {
|
|
454
|
+
marginTop: 2,
|
|
455
|
+
fontSize: 12,
|
|
456
|
+
lineHeight: 17
|
|
457
|
+
},
|
|
458
|
+
privacyToggle: {
|
|
459
|
+
paddingHorizontal: 14,
|
|
460
|
+
paddingVertical: 6,
|
|
461
|
+
borderRadius: 999,
|
|
462
|
+
borderWidth: StyleSheet.hairlineWidth
|
|
463
|
+
},
|
|
464
|
+
privacyToggleText: {
|
|
465
|
+
fontSize: 13,
|
|
466
|
+
fontWeight: "600"
|
|
467
|
+
},
|
|
468
|
+
suggestGroup: {
|
|
469
|
+
marginTop: 8
|
|
470
|
+
},
|
|
471
|
+
suggestHead: {
|
|
472
|
+
flexDirection: "row",
|
|
473
|
+
alignItems: "center",
|
|
474
|
+
gap: 6,
|
|
475
|
+
marginBottom: 2
|
|
476
|
+
},
|
|
477
|
+
suggestKind: {
|
|
478
|
+
fontSize: 14,
|
|
479
|
+
fontWeight: "700"
|
|
480
|
+
},
|
|
481
|
+
suggestItem: {
|
|
482
|
+
marginTop: 6,
|
|
483
|
+
paddingLeft: 22
|
|
484
|
+
},
|
|
485
|
+
suggestText: {
|
|
486
|
+
fontSize: 15,
|
|
487
|
+
lineHeight: 21
|
|
488
|
+
},
|
|
489
|
+
suggestWhy: {
|
|
490
|
+
marginTop: 2,
|
|
491
|
+
fontSize: 13,
|
|
492
|
+
lineHeight: 18
|
|
493
|
+
},
|
|
494
|
+
suggestError: {
|
|
495
|
+
marginTop: 10,
|
|
496
|
+
fontSize: 13,
|
|
497
|
+
lineHeight: 18
|
|
498
|
+
},
|
|
499
|
+
suggestButton: {
|
|
500
|
+
marginTop: 12,
|
|
501
|
+
flexDirection: "row",
|
|
502
|
+
alignItems: "center",
|
|
503
|
+
justifyContent: "center",
|
|
504
|
+
gap: 8,
|
|
505
|
+
paddingVertical: 10,
|
|
506
|
+
borderRadius: 999,
|
|
507
|
+
borderWidth: 1
|
|
508
|
+
},
|
|
509
|
+
suggestButtonText: {
|
|
510
|
+
fontSize: 15,
|
|
511
|
+
fontWeight: "600"
|
|
512
|
+
},
|
|
513
|
+
suggestFoot: {
|
|
514
|
+
marginTop: 10,
|
|
515
|
+
fontSize: 12,
|
|
516
|
+
lineHeight: 17,
|
|
517
|
+
textAlign: "center"
|
|
518
|
+
},
|
|
304
519
|
center: {
|
|
305
520
|
flex: 1,
|
|
306
521
|
alignItems: "center",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoteView.js","sources":["../../../../src/features/dictate/components/NoteView.tsx"],"sourcesContent":["/**\n * A dictation's note: what to check first (amber, above everything), the four\n * SOAP sections, the medications with what was dictated for each, follow-up,\n * and the transcript behind a toggle. While the note is being written this\n * shows the stage; when writing failed, why and a Retry that reuses the saved\n * recording.\n */\nimport React, { useEffect, useState } from 'react';\nimport { ActivityIndicator, ScrollView, StyleSheet, Text, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { DIM, Tap } from '../../inbox/components/Tap';\nimport { formatDuration, NOTE_DISCLAIMER } from '../noteText';\nimport type { DictateStage } from '../dictateClient';\nimport type { DictateTheme } from '../dictateTheme';\nimport type { DictationRecord, NoteMedication } from '../types';\n\nconst STAGE_TEXT: Record<DictateStage, string> = {\n uploading: 'Uploading the recording…',\n writing: 'Transcribing and writing the note…',\n};\n\nexport function dictationDate(iso: string): string {\n const d = new Date(iso);\n return Number.isNaN(d.getTime())\n ? ''\n : d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });\n}\n\nfunction Section({ title, items, theme }: { title: string; items: string[]; theme: DictateTheme }) {\n if (!items.length) return null;\n return (\n <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>\n <Text style={[styles.heading, { color: theme.muted }]}>{title}</Text>\n {items.map((item, i) => (\n <View key={i} style={styles.item}>\n <View style={[styles.bullet, { backgroundColor: theme.faint }]} />\n <Text style={[styles.itemText, { color: theme.ink }]}>{item}</Text>\n </View>\n ))}\n </View>\n );\n}\n\nfunction actionTone(action: NoteMedication['action'], theme: DictateTheme): { bg: string; ink: string } {\n if (action === 'start') return { bg: theme.goodSoft, ink: theme.good };\n if (action === 'change') return { bg: theme.accentSoft, ink: theme.accent };\n if (action === 'stop') return { bg: theme.recSoft, ink: theme.crit };\n return { bg: theme.chip, ink: theme.muted };\n}\n\nfunction Medications({ meds, theme }: { meds: NoteMedication[]; theme: DictateTheme }) {\n if (!meds.length) return null;\n return (\n <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>\n <Text style={[styles.heading, { color: theme.muted }]}>MEDICATIONS</Text>\n {meds.map((med, i) => {\n const tone = actionTone(med.action, theme);\n const detail = [med.dose, med.route, med.frequency].filter(Boolean).join(' · ');\n return (\n <View\n key={`${med.name}-${i}`}\n style={[\n styles.med,\n i > 0 && { borderTopColor: theme.border, borderTopWidth: StyleSheet.hairlineWidth },\n ]}\n >\n <View style={styles.medText}>\n <Text style={[styles.medName, { color: theme.ink }]}>{med.name}</Text>\n <Text style={[styles.medDetail, { color: detail ? theme.muted : theme.warnInk }]}>\n {detail || 'No dose or frequency dictated'}\n </Text>\n </View>\n <View style={[styles.pill, { backgroundColor: tone.bg }]}>\n <Text style={[styles.pillText, { color: tone.ink }]}>{med.action}</Text>\n </View>\n </View>\n );\n })}\n </View>\n );\n}\n\nfunction Writing({ stage, theme }: { stage: DictateStage; theme: DictateTheme }) {\n const [seconds, setSeconds] = useState(0);\n useEffect(() => {\n const id = setInterval(() => setSeconds((s) => s + 1), 1000);\n return () => clearInterval(id);\n }, []);\n return (\n <View style={styles.center}>\n <ActivityIndicator color={theme.accent} />\n <Text style={[styles.centerTitle, { color: theme.ink }]}>{STAGE_TEXT[stage]}</Text>\n <Text style={[styles.centerDetail, { color: theme.faint }]}>{seconds}s · usually under a minute</Text>\n </View>\n );\n}\n\nexport function NoteView({\n record,\n stage,\n theme,\n bottomInset,\n onRetry,\n}: {\n record: DictationRecord;\n stage: DictateStage | null;\n theme: DictateTheme;\n bottomInset: number;\n onRetry: () => void;\n}) {\n const [showTranscript, setShowTranscript] = useState(false);\n const meta = [dictationDate(record.createdAt), formatDuration(record.durationMs)].filter(Boolean).join(' · ');\n\n if (record.status === 'writing' || (record.status === 'saved' && stage)) {\n return <Writing stage={stage ?? 'writing'} theme={theme} />;\n }\n if (!record.note) {\n return (\n <View style={styles.center}>\n <MaterialCommunityIcons name=\"alert-circle-outline\" size={28} color={theme.crit} />\n <Text style={[styles.centerTitle, { color: theme.ink }]}>No note yet</Text>\n <Text style={[styles.centerDetail, { color: theme.muted }]}>\n {record.error ?? 'The recording is saved on this phone.'}\n </Text>\n {record.audioUri ? (\n <Tap\n onPress={onRetry}\n accessibilityRole=\"button\"\n style={[styles.retry, { backgroundColor: theme.accent }]}\n pressedStyle={DIM}\n >\n <Text style={[styles.retryText, { color: theme.accentInk }]}>Write the note</Text>\n </Tap>\n ) : null}\n </View>\n );\n }\n\n const { note } = record;\n return (\n <ScrollView contentContainerStyle={[styles.scroll, { paddingBottom: 24 + bottomInset }]}>\n <Text style={[styles.title, { color: theme.ink }]}>{note.title}</Text>\n <Text style={[styles.meta, { color: theme.faint }]}>{meta}</Text>\n\n {note.to_verify.length ? (\n <View style={[styles.verify, { backgroundColor: theme.warnSoft, borderColor: theme.warnBorder }]}>\n <View style={styles.verifyHead}>\n <MaterialCommunityIcons name=\"alert-outline\" size={18} color={theme.warnInk} />\n <Text style={[styles.verifyTitle, { color: theme.warnInk }]}>Check before you use</Text>\n </View>\n {note.to_verify.map((item, i) => (\n <Text key={i} style={[styles.verifyItem, { color: theme.warnInk }]}>\n {`• ${item}`}\n </Text>\n ))}\n </View>\n ) : null}\n\n <Section title=\"SUBJECTIVE\" items={note.subjective} theme={theme} />\n <Section title=\"OBJECTIVE\" items={note.objective} theme={theme} />\n <Section title=\"ASSESSMENT\" items={note.assessment} theme={theme} />\n <Section title=\"PLAN\" items={note.plan} theme={theme} />\n <Medications meds={note.medications} theme={theme} />\n {note.follow_up ? <Section title=\"FOLLOW-UP\" items={[note.follow_up]} theme={theme} /> : null}\n\n {record.transcript ? (\n <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>\n <Tap\n onPress={() => setShowTranscript((v) => !v)}\n accessibilityRole=\"button\"\n style={styles.transcriptToggle}\n pressedStyle={DIM}\n >\n <Text style={[styles.heading, styles.headingFlat, { color: theme.muted }]}>TRANSCRIPT</Text>\n <MaterialCommunityIcons\n name={showTranscript ? 'chevron-up' : 'chevron-down'}\n size={20}\n color={theme.muted}\n />\n </Tap>\n {showTranscript ? (\n <Text style={[styles.transcript, { color: theme.ink }]}>{record.transcript}</Text>\n ) : null}\n </View>\n ) : null}\n\n <Text style={[styles.disclaimer, { color: theme.faint }]}>{NOTE_DISCLAIMER}</Text>\n </ScrollView>\n );\n}\n\nconst styles = StyleSheet.create({\n scroll: { paddingHorizontal: 16, paddingTop: 8 },\n title: { fontSize: 21, fontWeight: '700', lineHeight: 27 },\n meta: { marginTop: 4, fontSize: 13 },\n verify: { marginTop: 16, borderRadius: 14, borderWidth: StyleSheet.hairlineWidth, padding: 14 },\n verifyHead: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 6 },\n verifyTitle: { fontSize: 14, fontWeight: '700' },\n verifyItem: { fontSize: 14, lineHeight: 20, marginTop: 4 },\n card: {\n marginTop: 12,\n borderRadius: 14,\n borderWidth: StyleSheet.hairlineWidth,\n paddingHorizontal: 14,\n paddingVertical: 12,\n },\n heading: { fontSize: 12, fontWeight: '700', letterSpacing: 0.8, marginBottom: 6 },\n headingFlat: { marginBottom: 0 },\n item: { flexDirection: 'row', alignItems: 'flex-start', marginTop: 6 },\n bullet: { width: 5, height: 5, borderRadius: 2.5, marginTop: 8, marginRight: 10 },\n itemText: { flex: 1, fontSize: 15, lineHeight: 21 },\n med: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10 },\n medText: { flex: 1, paddingRight: 10 },\n medName: { fontSize: 15, fontWeight: '600', textTransform: 'capitalize' },\n medDetail: { marginTop: 2, fontSize: 13 },\n pill: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 999 },\n pillText: { fontSize: 12, fontWeight: '600', textTransform: 'capitalize' },\n transcriptToggle: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },\n transcript: { marginTop: 10, fontSize: 14, lineHeight: 21 },\n disclaimer: { marginTop: 18, fontSize: 12, textAlign: 'center' },\n center: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 32 },\n centerTitle: { marginTop: 14, fontSize: 16, fontWeight: '600', textAlign: 'center' },\n centerDetail: { marginTop: 6, fontSize: 13, lineHeight: 19, textAlign: 'center' },\n retry: { marginTop: 18, paddingHorizontal: 20, paddingVertical: 12, borderRadius: 999 },\n retryText: { fontSize: 15, fontWeight: '600' },\n});\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NoteView.js","sources":["../../../../src/features/dictate/components/NoteView.tsx"],"sourcesContent":["/**\n * A dictation's note: what to check first (amber, above everything), the four\n * SOAP sections, the medications with what was dictated for each, follow-up,\n * the AI's suggestions on demand, and the transcript behind a toggle. While\n * the note is being written this shows the stage; when writing failed, why and\n * a Retry that reuses the saved recording.\n *\n * A note written with its identifiers hidden from the AI shows them hidden\n * (\"Name 1\") or put back, as the clinician chooses; the suggestions are asked\n * for with the hidden version only.\n */\nimport React, { useEffect, useState } from 'react';\nimport { ActivityIndicator, ScrollView, StyleSheet, Text, View } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { DIM, Tap } from '../../inbox/components/Tap';\nimport { formatDuration, NOTE_DISCLAIMER, SUGGESTION_DISCLAIMER, SUGGESTION_HEADINGS } from '../noteText';\nimport { placeholderLabel, splitPlaceholders } from '../privacy';\nimport type { DictateStage } from '../dictateClient';\nimport type { DictateTheme } from '../dictateTheme';\nimport type { DictationRecord, NoteMedication, NoteSuggestion, SuggestionKind } from '../types';\nimport { shownNote, type SuggestState } from '../shownNote';\n\nconst STAGE_TEXT: Record<DictateStage, string> = {\n uploading: 'Uploading the recording…',\n writing: 'Transcribing and writing the note…',\n};\n\nexport function dictationDate(iso: string): string {\n const d = new Date(iso);\n return Number.isNaN(d.getTime())\n ? ''\n : d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });\n}\n\n/** Text with each hidden identifier drawn as a small label (\"Name 1\"), so it reads as a gap, not a code. */\nfunction Redacted({ text, theme }: { text: string; theme: DictateTheme }) {\n const runs = splitPlaceholders(text);\n if (runs.length === 1 && !runs[0].placeholder) return <>{text}</>;\n return (\n <>\n {runs.map((run, i) =>\n run.placeholder ? (\n <Text\n key={i}\n style={[styles.placeholder, { backgroundColor: theme.chip, color: theme.muted }]}\n accessibilityLabel={`${placeholderLabel(run.text)}, hidden`}\n >\n {/* Non-breaking: a label wraps whole, never leaving a shaded gap at a line end. */}\n {`\\u00A0${placeholderLabel(run.text).replace(/ /g, '\\u00A0')}\\u00A0`}\n </Text>\n ) : (\n run.text\n ),\n )}\n </>\n );\n}\n\nfunction Section({ title, items, theme }: { title: string; items: string[]; theme: DictateTheme }) {\n if (!items.length) return null;\n return (\n <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>\n <Text style={[styles.heading, { color: theme.muted }]}>{title}</Text>\n {items.map((item, i) => (\n <View key={i} style={styles.item}>\n <View style={[styles.bullet, { backgroundColor: theme.faint }]} />\n <Text style={[styles.itemText, { color: theme.ink }]}>\n <Redacted text={item} theme={theme} />\n </Text>\n </View>\n ))}\n </View>\n );\n}\n\n/** What the AI saw, and the switch between hidden and put back. */\nfunction PrivacyRow({\n record,\n showIdentifiers,\n onToggle,\n theme,\n}: {\n record: DictationRecord;\n showIdentifiers: boolean;\n onToggle: () => void;\n theme: DictateTheme;\n}) {\n const { redaction } = record;\n if (!redaction) return null;\n const count = redaction.identifiers.length;\n const what = count === 1 ? '1 name or number' : `${count} names and numbers`;\n const line = count\n ? `${what} ${showIdentifiers ? 'shown here' : 'hidden'}. The AI never saw them.`\n : 'No names or numbers were heard. The AI saw the dictation as spoken.';\n return (\n <View style={[styles.privacy, { backgroundColor: theme.goodSoft }]}>\n <MaterialCommunityIcons name=\"shield-lock-outline\" size={18} color={theme.good} />\n <View style={styles.privacyText}>\n <Text style={[styles.privacyLine, { color: theme.ink }]}>{line}</Text>\n {redaction.method === 'patterns' && count ? (\n <Text style={[styles.privacyNote, { color: theme.warnInk }]}>\n Found without the medical transcriber; check that no name was missed.\n </Text>\n ) : null}\n </View>\n {count ? (\n <Tap\n onPress={onToggle}\n accessibilityRole=\"button\"\n accessibilityLabel={showIdentifiers ? 'Hide names and numbers' : 'Show names and numbers'}\n style={[styles.privacyToggle, { borderColor: theme.border, backgroundColor: theme.card }]}\n pressedStyle={DIM}\n >\n <Text style={[styles.privacyToggleText, { color: theme.ink }]}>\n {showIdentifiers ? 'Hide' : 'Show'}\n </Text>\n </Tap>\n ) : null}\n </View>\n );\n}\n\nconst SUGGESTION_ICON: Record<SuggestionKind, React.ComponentProps<typeof MaterialCommunityIcons>['name']> = {\n missing: 'text-box-plus-outline',\n safety: 'alert-octagon-outline',\n coding: 'tag-outline',\n follow_up: 'calendar-check-outline',\n};\n\n/** The AI's read of the note, on demand: grouped by kind, each with why. */\nfunction Suggestions({\n items,\n state,\n onSuggest,\n theme,\n}: {\n items: NoteSuggestion[] | null;\n state: SuggestState;\n onSuggest: () => void;\n theme: DictateTheme;\n}) {\n const loading = state.status === 'loading';\n let content: React.ReactNode;\n if (!items) {\n content = (\n <Text style={[styles.suggestWhy, { color: theme.muted }]}>\n Ask the AI to read the note and point out what to add or check before you sign. It reads the note with\n names and numbers hidden.\n </Text>\n );\n } else if (!items.length) {\n content = (\n <Text style={[styles.suggestWhy, { color: theme.muted }]}>\n Nothing to add: the note covers what the AI would check.\n </Text>\n );\n } else {\n content = (Object.keys(SUGGESTION_HEADINGS) as SuggestionKind[]).map((kind) => {\n const group = items.filter((s) => s.kind === kind);\n if (!group.length) return null;\n return (\n <View key={kind} style={styles.suggestGroup}>\n <View style={styles.suggestHead}>\n <MaterialCommunityIcons\n name={SUGGESTION_ICON[kind]}\n size={16}\n color={kind === 'safety' ? theme.crit : theme.accent}\n />\n <Text style={[styles.suggestKind, { color: theme.ink }]}>{SUGGESTION_HEADINGS[kind]}</Text>\n </View>\n {group.map((s, i) => (\n <View key={i} style={styles.suggestItem}>\n <Text style={[styles.suggestText, { color: theme.ink }]}>\n <Redacted text={s.text} theme={theme} />\n </Text>\n {s.why ? (\n <Text style={[styles.suggestWhy, { color: theme.muted }]}>\n <Redacted text={s.why} theme={theme} />\n </Text>\n ) : null}\n </View>\n ))}\n </View>\n );\n });\n }\n let buttonLabel = 'Get suggestions';\n if (loading) buttonLabel = 'Reading the note…';\n else if (items) buttonLabel = 'Ask again';\n return (\n <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>\n <Text style={[styles.heading, { color: theme.muted }]}>SUGGESTIONS</Text>\n {content}\n {state.status === 'error' ? (\n <Text style={[styles.suggestError, { color: theme.crit }]}>{state.message}</Text>\n ) : null}\n <Tap\n onPress={onSuggest}\n disabled={loading}\n accessibilityRole=\"button\"\n accessibilityLabel={items ? 'Ask for suggestions again' : 'Get suggestions'}\n style={[styles.suggestButton, { borderColor: theme.accent, opacity: loading ? 0.6 : 1 }]}\n pressedStyle={DIM}\n >\n {loading ? (\n <ActivityIndicator color={theme.accent} size=\"small\" />\n ) : (\n <MaterialCommunityIcons name=\"lightbulb-on-outline\" size={18} color={theme.accent} />\n )}\n <Text style={[styles.suggestButtonText, { color: theme.accent }]}>{buttonLabel}</Text>\n </Tap>\n {items?.length ? (\n <Text style={[styles.suggestFoot, { color: theme.faint }]}>{SUGGESTION_DISCLAIMER}</Text>\n ) : null}\n </View>\n );\n}\n\nfunction actionTone(action: NoteMedication['action'], theme: DictateTheme): { bg: string; ink: string } {\n if (action === 'start') return { bg: theme.goodSoft, ink: theme.good };\n if (action === 'change') return { bg: theme.accentSoft, ink: theme.accent };\n if (action === 'stop') return { bg: theme.recSoft, ink: theme.crit };\n return { bg: theme.chip, ink: theme.muted };\n}\n\nfunction Medications({ meds, theme }: { meds: NoteMedication[]; theme: DictateTheme }) {\n if (!meds.length) return null;\n return (\n <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>\n <Text style={[styles.heading, { color: theme.muted }]}>MEDICATIONS</Text>\n {meds.map((med, i) => {\n const tone = actionTone(med.action, theme);\n const detail = [med.dose, med.route, med.frequency].filter(Boolean).join(' · ');\n return (\n <View\n key={`${med.name}-${i}`}\n style={[\n styles.med,\n i > 0 && { borderTopColor: theme.border, borderTopWidth: StyleSheet.hairlineWidth },\n ]}\n >\n <View style={styles.medText}>\n <Text style={[styles.medName, { color: theme.ink }]}>\n <Redacted text={med.name} theme={theme} />\n </Text>\n <Text style={[styles.medDetail, { color: detail ? theme.muted : theme.warnInk }]}>\n {detail ? <Redacted text={detail} theme={theme} /> : 'No dose or frequency dictated'}\n </Text>\n </View>\n <View style={[styles.pill, { backgroundColor: tone.bg }]}>\n <Text style={[styles.pillText, { color: tone.ink }]}>{med.action}</Text>\n </View>\n </View>\n );\n })}\n </View>\n );\n}\n\nfunction Writing({ stage, theme }: { stage: DictateStage; theme: DictateTheme }) {\n const [seconds, setSeconds] = useState(0);\n useEffect(() => {\n const id = setInterval(() => setSeconds((s) => s + 1), 1000);\n return () => clearInterval(id);\n }, []);\n return (\n <View style={styles.center}>\n <ActivityIndicator color={theme.accent} />\n <Text style={[styles.centerTitle, { color: theme.ink }]}>{STAGE_TEXT[stage]}</Text>\n <Text style={[styles.centerDetail, { color: theme.faint }]}>{seconds}s · usually under a minute</Text>\n </View>\n );\n}\n\nexport function NoteView({\n record,\n stage,\n theme,\n bottomInset,\n onRetry,\n showIdentifiers,\n onToggleIdentifiers,\n suggestState,\n onSuggest,\n}: {\n record: DictationRecord;\n stage: DictateStage | null;\n theme: DictateTheme;\n bottomInset: number;\n onRetry: () => void;\n showIdentifiers: boolean;\n onToggleIdentifiers: () => void;\n suggestState: SuggestState;\n onSuggest: () => void;\n}) {\n const [showTranscript, setShowTranscript] = useState(false);\n const meta = [dictationDate(record.createdAt), formatDuration(record.durationMs)].filter(Boolean).join(' · ');\n\n if (record.status === 'writing' || (record.status === 'saved' && stage)) {\n return <Writing stage={stage ?? 'writing'} theme={theme} />;\n }\n if (!record.note) {\n return (\n <View style={styles.center}>\n <MaterialCommunityIcons name=\"alert-circle-outline\" size={28} color={theme.crit} />\n <Text style={[styles.centerTitle, { color: theme.ink }]}>No note yet</Text>\n <Text style={[styles.centerDetail, { color: theme.muted }]}>\n {record.error ?? 'The recording is saved on this phone.'}\n </Text>\n {record.audioUri ? (\n <Tap\n onPress={onRetry}\n accessibilityRole=\"button\"\n style={[styles.retry, { backgroundColor: theme.accent }]}\n pressedStyle={DIM}\n >\n <Text style={[styles.retryText, { color: theme.accentInk }]}>Write the note</Text>\n </Tap>\n ) : null}\n </View>\n );\n }\n\n const { note, transcript, suggestions } = shownNote(record, record.note, showIdentifiers);\n return (\n <ScrollView contentContainerStyle={[styles.scroll, { paddingBottom: 24 + bottomInset }]}>\n <Text style={[styles.title, { color: theme.ink }]}>\n <Redacted text={note.title} theme={theme} />\n </Text>\n <Text style={[styles.meta, { color: theme.faint }]}>{meta}</Text>\n <PrivacyRow\n record={record}\n showIdentifiers={showIdentifiers}\n onToggle={onToggleIdentifiers}\n theme={theme}\n />\n\n {note.to_verify.length ? (\n <View style={[styles.verify, { backgroundColor: theme.warnSoft, borderColor: theme.warnBorder }]}>\n <View style={styles.verifyHead}>\n <MaterialCommunityIcons name=\"alert-outline\" size={18} color={theme.warnInk} />\n <Text style={[styles.verifyTitle, { color: theme.warnInk }]}>Check before you use</Text>\n </View>\n {note.to_verify.map((item, i) => (\n <Text key={i} style={[styles.verifyItem, { color: theme.warnInk }]}>\n {'• '}\n <Redacted text={item} theme={theme} />\n </Text>\n ))}\n </View>\n ) : null}\n\n <Section title=\"SUBJECTIVE\" items={note.subjective} theme={theme} />\n <Section title=\"OBJECTIVE\" items={note.objective} theme={theme} />\n <Section title=\"ASSESSMENT\" items={note.assessment} theme={theme} />\n <Section title=\"PLAN\" items={note.plan} theme={theme} />\n <Medications meds={note.medications} theme={theme} />\n {note.follow_up ? <Section title=\"FOLLOW-UP\" items={[note.follow_up]} theme={theme} /> : null}\n\n {/* Only a note whose identifiers were hidden from the AI goes back to it. */}\n {record.redaction ? (\n <Suggestions items={suggestions} state={suggestState} onSuggest={onSuggest} theme={theme} />\n ) : null}\n\n {transcript ? (\n <View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }]}>\n <Tap\n onPress={() => setShowTranscript((v) => !v)}\n accessibilityRole=\"button\"\n style={styles.transcriptToggle}\n pressedStyle={DIM}\n >\n <Text style={[styles.heading, styles.headingFlat, { color: theme.muted }]}>TRANSCRIPT</Text>\n <MaterialCommunityIcons\n name={showTranscript ? 'chevron-up' : 'chevron-down'}\n size={20}\n color={theme.muted}\n />\n </Tap>\n {showTranscript ? (\n <Text style={[styles.transcript, { color: theme.ink }]}>\n <Redacted text={transcript} theme={theme} />\n </Text>\n ) : null}\n </View>\n ) : null}\n\n <Text style={[styles.disclaimer, { color: theme.faint }]}>{NOTE_DISCLAIMER}</Text>\n </ScrollView>\n );\n}\n\nconst styles = StyleSheet.create({\n scroll: { paddingHorizontal: 16, paddingTop: 8 },\n title: { fontSize: 21, fontWeight: '700', lineHeight: 27 },\n meta: { marginTop: 4, fontSize: 13 },\n verify: { marginTop: 16, borderRadius: 14, borderWidth: StyleSheet.hairlineWidth, padding: 14 },\n verifyHead: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 6 },\n verifyTitle: { fontSize: 14, fontWeight: '700' },\n verifyItem: { fontSize: 14, lineHeight: 20, marginTop: 4 },\n card: {\n marginTop: 12,\n borderRadius: 14,\n borderWidth: StyleSheet.hairlineWidth,\n paddingHorizontal: 14,\n paddingVertical: 12,\n },\n heading: { fontSize: 12, fontWeight: '700', letterSpacing: 0.8, marginBottom: 6 },\n headingFlat: { marginBottom: 0 },\n item: { flexDirection: 'row', alignItems: 'flex-start', marginTop: 6 },\n bullet: { width: 5, height: 5, borderRadius: 2.5, marginTop: 8, marginRight: 10 },\n itemText: { flex: 1, fontSize: 15, lineHeight: 21 },\n med: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10 },\n medText: { flex: 1, paddingRight: 10 },\n medName: { fontSize: 15, fontWeight: '600', textTransform: 'capitalize' },\n medDetail: { marginTop: 2, fontSize: 13 },\n pill: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 999 },\n pillText: { fontSize: 12, fontWeight: '600', textTransform: 'capitalize' },\n transcriptToggle: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },\n transcript: { marginTop: 10, fontSize: 14, lineHeight: 21 },\n disclaimer: { marginTop: 18, fontSize: 12, textAlign: 'center' },\n placeholder: { fontSize: 13, fontWeight: '600', borderRadius: 4, overflow: 'hidden' },\n privacy: {\n marginTop: 12,\n borderRadius: 12,\n paddingHorizontal: 12,\n paddingVertical: 10,\n flexDirection: 'row',\n alignItems: 'center',\n gap: 10,\n },\n privacyText: { flex: 1 },\n privacyLine: { fontSize: 13, lineHeight: 18 },\n privacyNote: { marginTop: 2, fontSize: 12, lineHeight: 17 },\n privacyToggle: {\n paddingHorizontal: 14,\n paddingVertical: 6,\n borderRadius: 999,\n borderWidth: StyleSheet.hairlineWidth,\n },\n privacyToggleText: { fontSize: 13, fontWeight: '600' },\n suggestGroup: { marginTop: 8 },\n suggestHead: { flexDirection: 'row', alignItems: 'center', gap: 6, marginBottom: 2 },\n suggestKind: { fontSize: 14, fontWeight: '700' },\n suggestItem: { marginTop: 6, paddingLeft: 22 },\n suggestText: { fontSize: 15, lineHeight: 21 },\n suggestWhy: { marginTop: 2, fontSize: 13, lineHeight: 18 },\n suggestError: { marginTop: 10, fontSize: 13, lineHeight: 18 },\n suggestButton: {\n marginTop: 12,\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n paddingVertical: 10,\n borderRadius: 999,\n borderWidth: 1,\n },\n suggestButtonText: { fontSize: 15, fontWeight: '600' },\n suggestFoot: { marginTop: 10, fontSize: 12, lineHeight: 17, textAlign: 'center' },\n center: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 32 },\n centerTitle: { marginTop: 14, fontSize: 16, fontWeight: '600', textAlign: 'center' },\n centerDetail: { marginTop: 6, fontSize: 13, lineHeight: 19, textAlign: 'center' },\n retry: { marginTop: 18, paddingHorizontal: 20, paddingVertical: 12, borderRadius: 999 },\n retryText: { fontSize: 15, fontWeight: '600' },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,8 @@ var __spreadValues = (a, b) => {
|
|
|
16
16
|
};
|
|
17
17
|
const DICTATE_CONNECTOR = "yantra-meetings";
|
|
18
18
|
const CLINICAL_NOTE_ACTION = "YANTRA_MEETINGS_CLINICAL_NOTE";
|
|
19
|
+
const SUGGEST_ACTION = "YANTRA_MEETINGS_CLINICAL_SUGGEST";
|
|
20
|
+
const SUGGEST_TIMEOUT_MS = 4 * 6e4;
|
|
19
21
|
const NOTE_TIMEOUT_MS = 10 * 6e4;
|
|
20
22
|
class DictateError extends Error {
|
|
21
23
|
constructor(kind, message) {
|
|
@@ -31,6 +33,47 @@ const strings = (v) => {
|
|
|
31
33
|
return str(v) ? [str(v)] : [];
|
|
32
34
|
};
|
|
33
35
|
const ACTIONS = ["start", "continue", "change", "stop", "mentioned"];
|
|
36
|
+
const KINDS = ["missing", "safety", "coding", "follow_up"];
|
|
37
|
+
function shapeNote(note) {
|
|
38
|
+
const meds = Array.isArray(note.medications) ? note.medications : [];
|
|
39
|
+
return {
|
|
40
|
+
title: str(note.title) || "Clinical note",
|
|
41
|
+
subjective: strings(note.subjective),
|
|
42
|
+
objective: strings(note.objective),
|
|
43
|
+
assessment: strings(note.assessment),
|
|
44
|
+
plan: strings(note.plan),
|
|
45
|
+
medications: meds.map(asRecord).filter((m) => !!m && !!str(m.name)).map((m) => {
|
|
46
|
+
const action = str(m.action).toLowerCase();
|
|
47
|
+
return {
|
|
48
|
+
name: str(m.name),
|
|
49
|
+
dose: str(m.dose),
|
|
50
|
+
route: str(m.route),
|
|
51
|
+
frequency: str(m.frequency),
|
|
52
|
+
action: ACTIONS.includes(action) ? action : "mentioned"
|
|
53
|
+
};
|
|
54
|
+
}),
|
|
55
|
+
follow_up: str(note.follow_up),
|
|
56
|
+
to_verify: strings(note.to_verify)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function identifiersOf(v) {
|
|
60
|
+
return (Array.isArray(v) ? v : []).map(asRecord).filter((i) => !!i && /^\[[A-Z]+_\d+\]$/.test(str(i.token)) && !!str(i.value)).map((i) => ({
|
|
61
|
+
token: str(i.token),
|
|
62
|
+
value: str(i.value),
|
|
63
|
+
kind: str(i.kind)
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
function redactionOf(v) {
|
|
67
|
+
const r = asRecord(v);
|
|
68
|
+
const note = asRecord(r == null ? void 0 : r.note);
|
|
69
|
+
if (!r || !note) return void 0;
|
|
70
|
+
return {
|
|
71
|
+
method: r.method === "patterns" ? "patterns" : "entities",
|
|
72
|
+
note: shapeNote(note),
|
|
73
|
+
transcript: str(r.transcript),
|
|
74
|
+
identifiers: identifiersOf(r.identifiers)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
34
77
|
function unwrapNote(raw) {
|
|
35
78
|
var _a;
|
|
36
79
|
let layer = asRecord(raw);
|
|
@@ -40,30 +83,14 @@ function unwrapNote(raw) {
|
|
|
40
83
|
const note = asRecord(layer.note);
|
|
41
84
|
if (note) {
|
|
42
85
|
const transcript = asRecord(layer.transcript);
|
|
43
|
-
const
|
|
44
|
-
return {
|
|
45
|
-
note:
|
|
46
|
-
title: str(note.title) || "Clinical note",
|
|
47
|
-
subjective: strings(note.subjective),
|
|
48
|
-
objective: strings(note.objective),
|
|
49
|
-
assessment: strings(note.assessment),
|
|
50
|
-
plan: strings(note.plan),
|
|
51
|
-
medications: meds.map(asRecord).filter((m) => !!m && !!str(m.name)).map((m) => {
|
|
52
|
-
const action = str(m.action).toLowerCase();
|
|
53
|
-
return {
|
|
54
|
-
name: str(m.name),
|
|
55
|
-
dose: str(m.dose),
|
|
56
|
-
route: str(m.route),
|
|
57
|
-
frequency: str(m.frequency),
|
|
58
|
-
action: ACTIONS.includes(action) ? action : "mentioned"
|
|
59
|
-
};
|
|
60
|
-
}),
|
|
61
|
-
follow_up: str(note.follow_up),
|
|
62
|
-
to_verify: strings(note.to_verify)
|
|
63
|
-
},
|
|
86
|
+
const redaction = redactionOf(layer.redaction);
|
|
87
|
+
return __spreadValues({
|
|
88
|
+
note: shapeNote(note),
|
|
64
89
|
transcript: str(transcript == null ? void 0 : transcript.text),
|
|
65
90
|
model: str(layer.model) || void 0
|
|
66
|
-
}
|
|
91
|
+
}, redaction ? {
|
|
92
|
+
redaction
|
|
93
|
+
} : {});
|
|
67
94
|
}
|
|
68
95
|
layer = (_a = asRecord(layer.data)) != null ? _a : asRecord(layer.result);
|
|
69
96
|
}
|
|
@@ -100,4 +127,47 @@ async function writeNoteFromRecording(audioUri, deps, opts = {}, onStage = () =>
|
|
|
100
127
|
const empty = !written.transcript && ![n.subjective, n.objective, n.assessment, n.plan].some((s) => s.length);
|
|
101
128
|
if (empty) throw new DictateError("empty", "The dictation has no speech.");
|
|
102
129
|
return written;
|
|
103
|
-
}
|
|
130
|
+
}
|
|
131
|
+
async function suggestFromNote(redaction, deps) {
|
|
132
|
+
var _a;
|
|
133
|
+
const raw = await deps.exec(DICTATE_CONNECTOR, SUGGEST_ACTION, {
|
|
134
|
+
note: redaction.note,
|
|
135
|
+
transcript: redaction.transcript,
|
|
136
|
+
// Which placeholders exist, so a new one numbers on; never what they stand for.
|
|
137
|
+
identifiers: redaction.identifiers.map(({
|
|
138
|
+
token,
|
|
139
|
+
kind
|
|
140
|
+
}) => ({
|
|
141
|
+
token,
|
|
142
|
+
kind
|
|
143
|
+
}))
|
|
144
|
+
}, {
|
|
145
|
+
timeoutMs: SUGGEST_TIMEOUT_MS
|
|
146
|
+
});
|
|
147
|
+
let layer = asRecord(raw);
|
|
148
|
+
for (let depth = 0; layer && depth < 6; depth += 1) {
|
|
149
|
+
if (Array.isArray(layer.suggestions)) {
|
|
150
|
+
const items = layer.suggestions.map(asRecord).filter((s) => !!s && KINDS.includes(str(s.kind)) && !!str(s.text)).map((s) => ({
|
|
151
|
+
kind: str(s.kind),
|
|
152
|
+
text: str(s.text),
|
|
153
|
+
why: str(s.why)
|
|
154
|
+
}));
|
|
155
|
+
return {
|
|
156
|
+
items,
|
|
157
|
+
identifiers: identifiersOf(layer.identifiers)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const reason = [layer.error, layer.message].find((v) => typeof v === "string" && !!v.trim());
|
|
161
|
+
if (layer.successful === false && reason) throw new DictateError("failed", reason);
|
|
162
|
+
layer = (_a = asRecord(layer.data)) != null ? _a : asRecord(layer.result);
|
|
163
|
+
}
|
|
164
|
+
throw new DictateError("failed", "The suggestions came back empty.");
|
|
165
|
+
}
|
|
166
|
+
function suggestErrorText(err) {
|
|
167
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
168
|
+
if (/network request failed|offline|not signed in/i.test(message)) return "You are offline or signed out. Try again when you are back online.";
|
|
169
|
+
if (/action '[^']*' not found in connector|not found in registry|is not installed/i.test(message)) return "Suggestions are not available on this workspace yet.";
|
|
170
|
+
if (/temporarily unavailable|problem on our side|all LLM providers failed|HTTP 5\d\d/i.test(message)) return "Yantra could not read the note right now. This is a problem on our side; try again in a few minutes.";
|
|
171
|
+
if (/timed? ?out|abort/i.test(message)) return "Reading the note took too long. Try again.";
|
|
172
|
+
return "The suggestions could not be written. Try again.";
|
|
173
|
+
}export{CLINICAL_NOTE_ACTION,DICTATE_CONNECTOR,DictateError,NOTE_TIMEOUT_MS,SUGGEST_ACTION,SUGGEST_TIMEOUT_MS,dictateErrorText,identifiersOf,suggestErrorText,suggestFromNote,unwrapNote,writeNoteFromRecording};//# sourceMappingURL=dictateClient.js.map
|
|
@@ -1 +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 if (Array.isArray(v)) return v.map(str).filter(Boolean);\n return str(v) ? [str(v)] : [];\n};\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')\n 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))\n 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))\n return 'No speech was heard in the recording. Try again closer to the phone.';\n // Every exec failure starts with the action's name (\"YANTRA_MEETINGS_CLINICAL_NOTE failed: ...\"), so\n // only cdecli's own words for a missing connector or action mean Dictate is not set up here.\n if (\n /action '[^']*' not found in connector|not found in registry|is not installed|auto-install (failed|did not complete)/i.test(\n message,\n )\n )\n return 'Dictate is not available on this workspace yet. The recording is saved.';\n // Ours, not theirs: the service behind the note (the connector's neutral \"temporarily unavailable\",\n // or, from an older connector, its raw \"all LLM providers failed\"), or the agent itself failing.\n if (\n /temporarily unavailable|problem on our side|all LLM providers failed|transcription failed|HTTP 5\\d\\d/i.test(\n message,\n )\n )\n return 'Yantra could not write the note right now. This is a problem on our side; the recording is saved, so try again in a few minutes.';\n if (/declined by the content safety rules/i.test(message))\n return 'The note could not be written from this dictation. 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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"}
|
|
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 from a\n * transcript whose identifiers are placeholders; it returns the note both ways.\n *\n * Suggestions: YANTRA_MEETINGS_CLINICAL_SUGGEST reads the hidden version only,\n * so nothing identifying goes back to the AI from this phone.\n */\nimport type { RemoteConnectorExec } from '../canvas/videoEditor/remoteMediaClient';\nimport type {\n ClinicalNote,\n MedicationAction,\n NoteIdentifier,\n NoteRedaction,\n NoteSuggestion,\n SuggestionKind,\n} from './types';\n\nexport const DICTATE_CONNECTOR = 'yantra-meetings';\nexport const CLINICAL_NOTE_ACTION = 'YANTRA_MEETINGS_CLINICAL_NOTE';\nexport const SUGGEST_ACTION = 'YANTRA_MEETINGS_CLINICAL_SUGGEST';\n/** A read of one note runs about ten seconds. */\nexport const SUGGEST_TIMEOUT_MS = 4 * 60_000;\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 /** Absent from a connector that does not hide identifiers yet. */\n redaction?: NoteRedaction;\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 if (Array.isArray(v)) return v.map(str).filter(Boolean);\n return str(v) ? [str(v)] : [];\n};\nconst ACTIONS: MedicationAction[] = ['start', 'continue', 'change', 'stop', 'mentioned'];\nconst KINDS: SuggestionKind[] = ['missing', 'safety', 'coding', 'follow_up'];\n\nfunction shapeNote(note: Json): ClinicalNote {\n const meds = Array.isArray(note.medications) ? note.medications : [];\n return {\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}\n\n/** Only well-formed placeholders: a malformed one could not be put back. */\nexport function identifiersOf(v: unknown): NoteIdentifier[] {\n return (Array.isArray(v) ? v : [])\n .map(asRecord)\n .filter((i): i is Json => !!i && /^\\[[A-Z]+_\\d+\\]$/.test(str(i.token)) && !!str(i.value))\n .map((i) => ({ token: str(i.token), value: str(i.value), kind: str(i.kind) }));\n}\n\nfunction redactionOf(v: unknown): NoteRedaction | undefined {\n const r = asRecord(v);\n const note = asRecord(r?.note);\n if (!r || !note) return undefined;\n return {\n method: r.method === 'patterns' ? 'patterns' : 'entities',\n note: shapeNote(note),\n transcript: str(r.transcript),\n identifiers: identifiersOf(r.identifiers),\n };\n}\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 redaction = redactionOf(layer.redaction);\n return {\n note: shapeNote(note),\n transcript: str(transcript?.text),\n model: str(layer.model) || undefined,\n ...(redaction ? { redaction } : {}),\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')\n 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))\n 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))\n return 'No speech was heard in the recording. Try again closer to the phone.';\n // Every exec failure starts with the action's name (\"YANTRA_MEETINGS_CLINICAL_NOTE failed: ...\"), so\n // only cdecli's own words for a missing connector or action mean Dictate is not set up here.\n if (\n /action '[^']*' not found in connector|not found in registry|is not installed|auto-install (failed|did not complete)/i.test(\n message,\n )\n )\n return 'Dictate is not available on this workspace yet. The recording is saved.';\n // Ours, not theirs: the service behind the note (the connector's neutral \"temporarily unavailable\",\n // or, from an older connector, its raw \"all LLM providers failed\"), or the agent itself failing.\n if (\n /temporarily unavailable|problem on our side|all LLM providers failed|transcription failed|HTTP 5\\d\\d/i.test(\n message,\n )\n )\n return 'Yantra could not write the note right now. This is a problem on our side; the recording is saved, so try again in a few minutes.';\n if (/declined by the content safety rules/i.test(message))\n return 'The note could not be written from this dictation. 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\n/** The AI's suggestions on a note, asked for with its hidden version only. */\nexport async function suggestFromNote(\n redaction: NoteRedaction,\n deps: Pick<DictateDeps, 'exec'>,\n): Promise<{ items: NoteSuggestion[]; identifiers: NoteIdentifier[] }> {\n const raw = await deps.exec(\n DICTATE_CONNECTOR,\n SUGGEST_ACTION,\n {\n note: redaction.note,\n transcript: redaction.transcript,\n // Which placeholders exist, so a new one numbers on; never what they stand for.\n identifiers: redaction.identifiers.map(({ token, kind }) => ({ token, kind })),\n },\n { timeoutMs: SUGGEST_TIMEOUT_MS },\n );\n let layer = asRecord(raw);\n for (let depth = 0; layer && depth < 6; depth += 1) {\n if (Array.isArray(layer.suggestions)) {\n const items = layer.suggestions\n .map(asRecord)\n .filter((s): s is Json => !!s && KINDS.includes(str(s.kind) as SuggestionKind) && !!str(s.text))\n .map((s) => ({ kind: str(s.kind) as SuggestionKind, text: str(s.text), why: str(s.why) }));\n return { items, identifiers: identifiersOf(layer.identifiers) };\n }\n const reason = [layer.error, layer.message].find((v): v is string => typeof v === 'string' && !!v.trim());\n if (layer.successful === false && reason) throw new DictateError('failed', reason);\n layer = asRecord(layer.data) ?? asRecord(layer.result);\n }\n throw new DictateError('failed', 'The suggestions came back empty.');\n}\n\n/** Why there are no suggestions, in words a clinician can act on. */\nexport function suggestErrorText(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n if (/network request failed|offline|not signed in/i.test(message))\n return 'You are offline or signed out. Try again when you are back online.';\n if (/action '[^']*' not found in connector|not found in registry|is not installed/i.test(message))\n return 'Suggestions are not available on this workspace yet.';\n if (/temporarily unavailable|problem on our side|all LLM providers failed|HTTP 5\\d\\d/i.test(message))\n return 'Yantra could not read the note right now. This is a problem on our side; try again in a few minutes.';\n if (/timed? ?out|abort/i.test(message)) return 'Reading the note took too long. Try again.';\n return 'The suggestions could not be written. Try again.';\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
let AsyncStorage = null;
|
|
3
|
+
try {
|
|
4
|
+
AsyncStorage = (_a = require("@react-native-async-storage/async-storage").default) != null ? _a : null;
|
|
5
|
+
} catch (e) {
|
|
6
|
+
AsyncStorage = null;
|
|
7
|
+
}
|
|
8
|
+
const KEY = "yantra.dictate.showIdentifiers.v1";
|
|
9
|
+
async function readShowIdentifiers() {
|
|
10
|
+
if (!AsyncStorage) return false;
|
|
11
|
+
try {
|
|
12
|
+
return await AsyncStorage.getItem(KEY) === "1";
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function writeShowIdentifiers(show) {
|
|
18
|
+
AsyncStorage == null ? void 0 : AsyncStorage.setItem(KEY, show ? "1" : "0").catch(() => void 0);
|
|
19
|
+
}export{readShowIdentifiers,writeShowIdentifiers};//# sourceMappingURL=dictatePreference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictatePreference.js","sources":["../../../src/features/dictate/dictatePreference.ts"],"sourcesContent":["/**\n * Whether Dictate shows a note with its names and numbers put back or hidden.\n * Hidden unless the clinician chose otherwise on this phone; either way the AI\n * only ever saw placeholders.\n */\ntype StorageLike = {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n};\n\nlet AsyncStorage: StorageLike | null = null;\ntry {\n // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require\n AsyncStorage = require('@react-native-async-storage/async-storage').default ?? null;\n} catch {\n AsyncStorage = null;\n}\n\nconst KEY = 'yantra.dictate.showIdentifiers.v1';\n\nexport async function readShowIdentifiers(): Promise<boolean> {\n if (!AsyncStorage) return false;\n try {\n return (await AsyncStorage.getItem(KEY)) === '1';\n } catch {\n return false;\n }\n}\n\nexport function writeShowIdentifiers(show: boolean): void {\n AsyncStorage?.setItem(KEY, show ? '1' : '0').catch(() => undefined);\n}\n"],"names":[],"mappings":"AAAA,IAAA,EAAA;AASA,IAAI,YAAmC,GAAA,IAAA;AACvC,IAAI;AAEF,EAAA,YAAA,GAAA,CAAe,EAAQ,GAAA,OAAA,CAAA,2CAA2C,CAAE,CAAA,OAAA,KAArD,IAAgE,GAAA,EAAA,GAAA,IAAA;AACjF,CAAQ,CAAA,OAAA,CAAA,EAAA;AACN,EAAe,YAAA,GAAA,IAAA;AACjB;AACA,MAAM,GAAM,GAAA,mCAAA;AACZ,eAAsB,mBAAwC,GAAA;AAC5D,EAAI,IAAA,CAAC,cAAqB,OAAA,KAAA;AAC1B,EAAI,IAAA;AACF,IAAA,OAAQ,MAAM,YAAA,CAAa,OAAQ,CAAA,GAAG,CAAO,KAAA,GAAA;AAAA,GACvC,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,KAAA;AAAA;AAEX;AACO,SAAS,qBAAqB,IAAqB,EAAA;AACxD,EAAA,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAAc,QAAQ,GAAK,EAAA,IAAA,GAAO,GAAM,GAAA,GAAA,CAAA,CAAK,MAAM,MAAM,MAAA,CAAA;AAC3D"}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
const NOTE_DISCLAIMER = "AI draft from a dictation. Review before use.";
|
|
2
|
+
const SUGGESTION_DISCLAIMER = "AI suggestions for you to consider, not medical advice.";
|
|
3
|
+
const SUGGESTION_HEADINGS = {
|
|
4
|
+
safety: "Check for safety",
|
|
5
|
+
missing: "Missing from the note",
|
|
6
|
+
coding: "Coding",
|
|
7
|
+
follow_up: "Follow-up"
|
|
8
|
+
};
|
|
2
9
|
const ACTION_LABEL = {
|
|
3
10
|
start: "start",
|
|
4
11
|
continue: "continue",
|
|
@@ -10,7 +17,7 @@ function medicationLine(med) {
|
|
|
10
17
|
const parts = [med.name, med.dose, med.route, med.frequency].map((p) => p.trim()).filter(Boolean);
|
|
11
18
|
return `${parts.join(" ")} (${ACTION_LABEL[med.action]})`;
|
|
12
19
|
}
|
|
13
|
-
function noteToText(note) {
|
|
20
|
+
function noteToText(note, transcript, suggestions) {
|
|
14
21
|
const lines = [note.title, NOTE_DISCLAIMER];
|
|
15
22
|
const section = (heading, items) => {
|
|
16
23
|
if (!items.length) return;
|
|
@@ -23,6 +30,14 @@ function noteToText(note) {
|
|
|
23
30
|
section("PLAN", note.plan);
|
|
24
31
|
section("MEDICATIONS", note.medications.map(medicationLine));
|
|
25
32
|
if (note.follow_up.trim()) lines.push("", "FOLLOW-UP", note.follow_up.trim());
|
|
33
|
+
if (suggestions == null ? void 0 : suggestions.length) {
|
|
34
|
+
lines.push("", "SUGGESTIONS", SUGGESTION_DISCLAIMER);
|
|
35
|
+
for (const kind of Object.keys(SUGGESTION_HEADINGS)) {
|
|
36
|
+
const group = suggestions.filter((s) => s.kind === kind);
|
|
37
|
+
if (group.length) lines.push(`${SUGGESTION_HEADINGS[kind]}:`, ...group.map((s) => `- ${s.text}${s.why ? ` (${s.why})` : ""}`));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (transcript == null ? void 0 : transcript.trim()) lines.push("", "TRANSCRIPT", transcript.trim());
|
|
26
41
|
return lines.join("\n");
|
|
27
42
|
}
|
|
28
43
|
function formatDuration(ms) {
|
|
@@ -32,4 +47,4 @@ function formatDuration(ms) {
|
|
|
32
47
|
const s = total % 60;
|
|
33
48
|
const ss = String(s).padStart(2, "0");
|
|
34
49
|
return h ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
35
|
-
}export{NOTE_DISCLAIMER,formatDuration,medicationLine,noteToText};//# sourceMappingURL=noteText.js.map
|
|
50
|
+
}export{NOTE_DISCLAIMER,SUGGESTION_DISCLAIMER,SUGGESTION_HEADINGS,formatDuration,medicationLine,noteToText};//# sourceMappingURL=noteText.js.map
|
|
@@ -1 +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":"
|
|
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 * and the transcript last, as the screen shows it under the note.\n */\nimport type { ClinicalNote, NoteMedication, NoteSuggestion, SuggestionKind } from './types';\n\nexport const NOTE_DISCLAIMER = 'AI draft from a dictation. Review before use.';\nexport const SUGGESTION_DISCLAIMER = 'AI suggestions for you to consider, not medical advice.';\n/** In the order the screen and the shared text show them. */\nexport const SUGGESTION_HEADINGS: Record<SuggestionKind, string> = {\n safety: 'Check for safety',\n missing: 'Missing from the note',\n coding: 'Coding',\n follow_up: 'Follow-up',\n};\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, transcript?: string, suggestions?: NoteSuggestion[] | null): 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 if (suggestions?.length) {\n lines.push('', 'SUGGESTIONS', SUGGESTION_DISCLAIMER);\n for (const kind of Object.keys(SUGGESTION_HEADINGS) as SuggestionKind[]) {\n const group = suggestions.filter((s) => s.kind === kind);\n if (group.length)\n lines.push(\n `${SUGGESTION_HEADINGS[kind]}:`,\n ...group.map((s) => `- ${s.text}${s.why ? ` (${s.why})` : ''}`),\n );\n }\n }\n if (transcript?.trim()) lines.push('', 'TRANSCRIPT', transcript.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":"AAMO,MAAM,eAAkB,GAAA;AACxB,MAAM,qBAAwB,GAAA;AAE9B,MAAM,mBAAsD,GAAA;AAAA,EACjE,MAAQ,EAAA,kBAAA;AAAA,EACR,OAAS,EAAA,uBAAA;AAAA,EACT,MAAQ,EAAA,QAAA;AAAA,EACR,SAAW,EAAA;AACb;AACA,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;AACgB,SAAA,UAAA,CAAW,IAAoB,EAAA,UAAA,EAAqB,WAA+C,EAAA;AACjH,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,EAAA,IAAI,2CAAa,MAAQ,EAAA;AACvB,IAAM,KAAA,CAAA,IAAA,CAAK,EAAI,EAAA,aAAA,EAAe,qBAAqB,CAAA;AACnD,IAAA,KAAA,MAAW,IAAQ,IAAA,MAAA,CAAO,IAAK,CAAA,mBAAmB,CAAuB,EAAA;AACvE,MAAA,MAAM,QAAQ,WAAY,CAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,CAAE,SAAS,IAAI,CAAA;AACrD,MAAI,IAAA,KAAA,CAAM,MAAQ,EAAA,KAAA,CAAM,IAAK,CAAA,CAAA,EAAG,oBAAoB,IAAI,CAAC,CAAK,CAAA,CAAA,EAAA,GAAG,KAAM,CAAA,GAAA,CAAI,OAAK,CAAK,EAAA,EAAA,CAAA,CAAE,IAAI,CAAA,EAAG,CAAE,CAAA,GAAA,GAAM,CAAK,EAAA,EAAA,CAAA,CAAE,GAAG,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAE,CAAC,CAAA;AAAA;AAC7H;AAEF,EAAI,IAAA,UAAA,IAAA,IAAA,GAAA,MAAA,GAAA,UAAA,CAAY,QAAc,KAAA,CAAA,IAAA,CAAK,IAAI,YAAc,EAAA,UAAA,CAAW,MAAM,CAAA;AACtE,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,96 @@
|
|
|
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
|
+
const PLACEHOLDER = /\[[A-Z]+_\d+\]/g;
|
|
21
|
+
const LABEL = {
|
|
22
|
+
NAME: "Name",
|
|
23
|
+
CLINICIAN: "Clinician",
|
|
24
|
+
FACILITY: "Facility",
|
|
25
|
+
ORG: "Organization",
|
|
26
|
+
DOB: "Birth date",
|
|
27
|
+
DATE: "Date",
|
|
28
|
+
AGE: "Age",
|
|
29
|
+
ID: "ID",
|
|
30
|
+
PHONE: "Phone",
|
|
31
|
+
EMAIL: "Email",
|
|
32
|
+
URL: "Link",
|
|
33
|
+
IP: "IP address",
|
|
34
|
+
ADDRESS: "Address"
|
|
35
|
+
};
|
|
36
|
+
function placeholderLabel(token) {
|
|
37
|
+
var _a;
|
|
38
|
+
const m = /^\[([A-Z]+)_(\d+)\]$/.exec(token);
|
|
39
|
+
if (!m) return token;
|
|
40
|
+
return `${(_a = LABEL[m[1]]) != null ? _a : m[1].charAt(0) + m[1].slice(1).toLowerCase()} ${m[2]}`;
|
|
41
|
+
}
|
|
42
|
+
function splitPlaceholders(text) {
|
|
43
|
+
var _a;
|
|
44
|
+
const runs = [];
|
|
45
|
+
let last = 0;
|
|
46
|
+
for (const m of text.matchAll(PLACEHOLDER)) {
|
|
47
|
+
const at = (_a = m.index) != null ? _a : 0;
|
|
48
|
+
if (at > last) runs.push({
|
|
49
|
+
text: text.slice(last, at),
|
|
50
|
+
placeholder: false
|
|
51
|
+
});
|
|
52
|
+
runs.push({
|
|
53
|
+
text: m[0],
|
|
54
|
+
placeholder: true
|
|
55
|
+
});
|
|
56
|
+
last = at + m[0].length;
|
|
57
|
+
}
|
|
58
|
+
if (last < text.length) runs.push({
|
|
59
|
+
text: text.slice(last),
|
|
60
|
+
placeholder: false
|
|
61
|
+
});
|
|
62
|
+
return runs;
|
|
63
|
+
}
|
|
64
|
+
function restoreText(text, identifiers) {
|
|
65
|
+
const byToken = new Map(identifiers.map((id) => [id.token, id.value]));
|
|
66
|
+
return text.replace(PLACEHOLDER, (token) => {
|
|
67
|
+
var _a;
|
|
68
|
+
return (_a = byToken.get(token)) != null ? _a : token;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function labelText(text) {
|
|
72
|
+
return text.replace(PLACEHOLDER, (token) => `[${placeholderLabel(token)}]`);
|
|
73
|
+
}
|
|
74
|
+
function mapNote(note, fn) {
|
|
75
|
+
return {
|
|
76
|
+
title: fn(note.title),
|
|
77
|
+
subjective: note.subjective.map(fn),
|
|
78
|
+
objective: note.objective.map(fn),
|
|
79
|
+
assessment: note.assessment.map(fn),
|
|
80
|
+
plan: note.plan.map(fn),
|
|
81
|
+
medications: note.medications.map((m) => __spreadProps(__spreadValues({}, m), {
|
|
82
|
+
name: fn(m.name),
|
|
83
|
+
dose: fn(m.dose),
|
|
84
|
+
route: fn(m.route),
|
|
85
|
+
frequency: fn(m.frequency)
|
|
86
|
+
})),
|
|
87
|
+
follow_up: fn(note.follow_up),
|
|
88
|
+
to_verify: note.to_verify.map(fn)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function mapSuggestions(items, fn) {
|
|
92
|
+
return items.map((s) => __spreadProps(__spreadValues({}, s), {
|
|
93
|
+
text: fn(s.text),
|
|
94
|
+
why: fn(s.why)
|
|
95
|
+
}));
|
|
96
|
+
}export{labelText,mapNote,mapSuggestions,placeholderLabel,restoreText,splitPlaceholders};//# sourceMappingURL=privacy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.js","sources":["../../../src/features/dictate/privacy.ts"],"sourcesContent":["/**\n * Placeholders in a dictation note. Before any AI reads a dictation, the\n * connector replaces each identifier (a name, a birth date, a phone number...)\n * with a placeholder such as \"[NAME_1]\" and hands back what each one stands\n * for. This phone keeps both: it shows the note with the identifiers hidden or\n * put back, and anything it sends to the AI later (suggestions) carries the\n * placeholders only.\n */\nimport type { ClinicalNote, NoteIdentifier, NoteSuggestion } from './types';\n\nconst PLACEHOLDER = /\\[[A-Z]+_\\d+\\]/g;\n\nconst LABEL: Record<string, string> = {\n NAME: 'Name',\n CLINICIAN: 'Clinician',\n FACILITY: 'Facility',\n ORG: 'Organization',\n DOB: 'Birth date',\n DATE: 'Date',\n AGE: 'Age',\n ID: 'ID',\n PHONE: 'Phone',\n EMAIL: 'Email',\n URL: 'Link',\n IP: 'IP address',\n ADDRESS: 'Address',\n};\n\n/** \"[NAME_1]\" -> \"Name 1\", how a hidden identifier reads on screen. */\nexport function placeholderLabel(token: string): string {\n const m = /^\\[([A-Z]+)_(\\d+)\\]$/.exec(token);\n if (!m) return token;\n return `${LABEL[m[1]] ?? m[1].charAt(0) + m[1].slice(1).toLowerCase()} ${m[2]}`;\n}\n\n/** The text in runs, each placeholder its own run, for drawing them apart. */\nexport function splitPlaceholders(text: string): Array<{ text: string; placeholder: boolean }> {\n const runs: Array<{ text: string; placeholder: boolean }> = [];\n let last = 0;\n for (const m of text.matchAll(PLACEHOLDER)) {\n const at = m.index ?? 0;\n if (at > last) runs.push({ text: text.slice(last, at), placeholder: false });\n runs.push({ text: m[0], placeholder: true });\n last = at + m[0].length;\n }\n if (last < text.length) runs.push({ text: text.slice(last), placeholder: false });\n return runs;\n}\n\n/** Each placeholder put back to what it stands for; one this phone does not know stays. */\nexport function restoreText(text: string, identifiers: NoteIdentifier[]): string {\n const byToken = new Map(identifiers.map((id) => [id.token, id.value]));\n return text.replace(PLACEHOLDER, (token) => byToken.get(token) ?? token);\n}\n\n/** Each placeholder as its label in brackets, for text that leaves the phone hidden (\"[Name 1]\"). */\nexport function labelText(text: string): string {\n return text.replace(PLACEHOLDER, (token) => `[${placeholderLabel(token)}]`);\n}\n\nexport function mapNote(note: ClinicalNote, fn: (text: string) => string): ClinicalNote {\n return {\n title: fn(note.title),\n subjective: note.subjective.map(fn),\n objective: note.objective.map(fn),\n assessment: note.assessment.map(fn),\n plan: note.plan.map(fn),\n medications: note.medications.map((m) => ({\n ...m,\n name: fn(m.name),\n dose: fn(m.dose),\n route: fn(m.route),\n frequency: fn(m.frequency),\n })),\n follow_up: fn(note.follow_up),\n to_verify: note.to_verify.map(fn),\n };\n}\n\nexport function mapSuggestions(items: NoteSuggestion[], fn: (text: string) => string): NoteSuggestion[] {\n return items.map((s) => ({ ...s, text: fn(s.text), why: fn(s.why) }));\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {mapSuggestions,restoreText,mapNote}from'./privacy.js';function shownNote(record, note, showIdentifiers) {
|
|
2
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3
|
+
const {
|
|
4
|
+
redaction
|
|
5
|
+
} = record;
|
|
6
|
+
if (!redaction) return {
|
|
7
|
+
note,
|
|
8
|
+
transcript: (_a = record.transcript) != null ? _a : "",
|
|
9
|
+
suggestions: null
|
|
10
|
+
};
|
|
11
|
+
const items = (_c = (_b = record.suggestions) == null ? void 0 : _b.items) != null ? _c : null;
|
|
12
|
+
if (!showIdentifiers) return {
|
|
13
|
+
note: redaction.note,
|
|
14
|
+
transcript: redaction.transcript,
|
|
15
|
+
suggestions: items
|
|
16
|
+
};
|
|
17
|
+
const identifiers = [...redaction.identifiers, ...(_e = (_d = record.suggestions) == null ? void 0 : _d.identifiers) != null ? _e : []];
|
|
18
|
+
const restore = (text) => restoreText(text, identifiers);
|
|
19
|
+
return {
|
|
20
|
+
note: mapNote(redaction.note, restore),
|
|
21
|
+
transcript: (_f = record.transcript) != null ? _f : restore(redaction.transcript),
|
|
22
|
+
suggestions: items ? mapSuggestions(items, restore) : null
|
|
23
|
+
};
|
|
24
|
+
}export{shownNote};//# sourceMappingURL=shownNote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shownNote.js","sources":["../../../src/features/dictate/shownNote.ts"],"sourcesContent":["/**\n * The note as the clinician chose to see it: identifiers hidden (what the AI\n * saw) or put back. A note written before identifiers were hidden has only\n * the one version.\n */\nimport { mapNote, mapSuggestions, restoreText } from './privacy';\nimport type { ClinicalNote, DictationRecord, NoteSuggestion } from './types';\n\nexport type SuggestState = { status: 'idle' } | { status: 'loading' } | { status: 'error'; message: string };\n\nexport interface ShownNote {\n note: ClinicalNote;\n transcript: string;\n /** null until suggestions were asked for (and always for a note written before hiding). */\n suggestions: NoteSuggestion[] | null;\n}\n\nexport function shownNote(record: DictationRecord, note: ClinicalNote, showIdentifiers: boolean): ShownNote {\n const { redaction } = record;\n if (!redaction) return { note, transcript: record.transcript ?? '', suggestions: null };\n const items = record.suggestions?.items ?? null;\n if (!showIdentifiers) return { note: redaction.note, transcript: redaction.transcript, suggestions: items };\n const identifiers = [...redaction.identifiers, ...(record.suggestions?.identifiers ?? [])];\n const restore = (text: string) => restoreText(text, identifiers);\n return {\n note: mapNote(redaction.note, restore),\n transcript: record.transcript ?? restore(redaction.transcript),\n suggestions: items ? mapSuggestions(items, restore) : null,\n };\n}\n"],"names":[],"mappings":"8DAqBgB,SAAA,SAAA,CAAU,MAAyB,EAAA,IAAA,EAAoB,eAAqC,EAAA;AArB5G,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAsBE,EAAM,MAAA;AAAA,IACJ;AAAA,GACE,GAAA,MAAA;AACJ,EAAI,IAAA,CAAC,WAAkB,OAAA;AAAA,IACrB,IAAA;AAAA,IACA,UAAA,EAAA,CAAY,EAAO,GAAA,MAAA,CAAA,UAAA,KAAP,IAAqB,GAAA,EAAA,GAAA,EAAA;AAAA,IACjC,WAAa,EAAA;AAAA,GACf;AACA,EAAA,MAAM,KAAQ,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,MAAA,CAAO,WAAP,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAoB,UAApB,IAA6B,GAAA,EAAA,GAAA,IAAA;AAC3C,EAAI,IAAA,CAAC,iBAAwB,OAAA;AAAA,IAC3B,MAAM,SAAU,CAAA,IAAA;AAAA,IAChB,YAAY,SAAU,CAAA,UAAA;AAAA,IACtB,WAAa,EAAA;AAAA,GACf;AACA,EAAA,MAAM,WAAc,GAAA,CAAC,GAAG,SAAA,CAAU,WAAa,EAAA,GAAA,CAAI,EAAO,GAAA,CAAA,EAAA,GAAA,MAAA,CAAA,WAAA,KAAP,IAAoB,GAAA,MAAA,GAAA,EAAA,CAAA,WAAA,KAApB,IAAmC,GAAA,EAAA,GAAA,EAAG,CAAA;AACzF,EAAA,MAAM,OAAU,GAAA,CAAC,IAAiB,KAAA,WAAA,CAAY,MAAM,WAAW,CAAA;AAC/D,EAAO,OAAA;AAAA,IACL,IAAM,EAAA,OAAA,CAAQ,SAAU,CAAA,IAAA,EAAM,OAAO,CAAA;AAAA,IACrC,aAAY,EAAO,GAAA,MAAA,CAAA,UAAA,KAAP,IAAqB,GAAA,EAAA,GAAA,OAAA,CAAQ,UAAU,UAAU,CAAA;AAAA,IAC7D,WAAa,EAAA,KAAA,GAAQ,cAAe,CAAA,KAAA,EAAO,OAAO,CAAI,GAAA;AAAA,GACxD;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminide-stack/yantra-mobile",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.142",
|
|
4
4
|
"description": "Sample core for higher packages to depend on",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"uuid": "^9.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"common": "
|
|
30
|
+
"common": "12.0.141",
|
|
31
31
|
"libphonenumber-js": "^1.13.3"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@messenger-box/platform-mobile": "*",
|
|
46
46
|
"@react-native-async-storage/async-storage": "*",
|
|
47
47
|
"@react-navigation/native": "*",
|
|
48
|
-
"common": "12.0.
|
|
48
|
+
"common": "12.0.141",
|
|
49
49
|
"expo-background-task": "*",
|
|
50
50
|
"expo-constants": "*",
|
|
51
51
|
"expo-secure-store": "*",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"typescript": {
|
|
68
68
|
"definition": "lib/index.d.ts"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "80980b8e928ffa364accf4aa00afa3a0d18b1ce1"
|
|
71
71
|
}
|