@fayz-ai/plugin-scribe 0.11.2 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ScribeAssistantBridge.d.ts +5 -3
- package/dist/components/ScribeAssistantBridge.d.ts.map +1 -1
- package/dist/components/ScribeHeaderAction.d.ts +3 -2
- package/dist/components/ScribeHeaderAction.d.ts.map +1 -1
- package/dist/components/ScribeRecordingPill.d.ts +1 -1
- package/dist/components/ScribeRecordingPill.d.ts.map +1 -1
- package/dist/components/ScribeSessionPage.d.ts +5 -2
- package/dist/components/ScribeSessionPage.d.ts.map +1 -1
- package/dist/components/ScribeShellMount.d.ts +3 -10
- package/dist/components/ScribeShellMount.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +230 -317
- package/dist/index.js.map +1 -1
- package/dist/lib/start.d.ts +47 -0
- package/dist/lib/start.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -5
- package/dist/components/ScribeConsentDialog.d.ts +0 -12
- package/dist/components/ScribeConsentDialog.d.ts.map +0 -1
- package/dist/components/ScribeRecoveryBanner.d.ts +0 -8
- package/dist/components/ScribeRecoveryBanner.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import
|
|
1
|
+
import * as React5 from 'react';
|
|
2
|
+
import React5__default from 'react';
|
|
3
3
|
import { getActiveTenantId, getSupabaseClientOptional, registerTranslations } from '@fayz-ai/core';
|
|
4
4
|
import { create } from 'zustand';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
|
-
import {
|
|
6
|
+
import { Mic, FileText, Sparkles, Loader2, Pause, Play, Square, Settings2, AlertTriangle, RefreshCw, Trash2, Check } from 'lucide-react';
|
|
7
7
|
import { Button, toast, Skeleton, Badge, Tabs, TabsList, TabsTrigger, TabsContent, Card, CardContent, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Checkbox, ModalFooter, MarkdownEditor } from '@fayz-ai/ui/primitives';
|
|
8
|
-
import { Mic, FileText, Sparkles, Loader2, Pause, Play, Square, Settings2, AlertTriangle, RefreshCw, Trash2, Check, ShieldCheck } from 'lucide-react';
|
|
9
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import { openAssistantAs, useAgentSurface, registerTimelineSource, useAccessOptional, useHeaderAction, useAuth, usePublishAssistantActivity, useRegisterAssistantLauncher, resolveFayzAgentConnection } from '@fayz-ai/admin';
|
|
10
10
|
import { SubpageHeader } from '@fayz-ai/ui/layout';
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
@@ -1083,10 +1083,10 @@ function formatElapsed(ms) {
|
|
|
1083
1083
|
}
|
|
1084
1084
|
var MOBILE_BREAKPOINT = 768;
|
|
1085
1085
|
function useIsMobile() {
|
|
1086
|
-
const [isMobile, setIsMobile] =
|
|
1086
|
+
const [isMobile, setIsMobile] = React5.useState(
|
|
1087
1087
|
() => typeof window !== "undefined" && window.innerWidth < MOBILE_BREAKPOINT
|
|
1088
1088
|
);
|
|
1089
|
-
|
|
1089
|
+
React5.useEffect(() => {
|
|
1090
1090
|
const mq = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
1091
1091
|
const onChange = (e) => setIsMobile(e.matches);
|
|
1092
1092
|
setIsMobile(mq.matches);
|
|
@@ -1096,8 +1096,8 @@ function useIsMobile() {
|
|
|
1096
1096
|
return isMobile;
|
|
1097
1097
|
}
|
|
1098
1098
|
function useTicker(active) {
|
|
1099
|
-
const [, force] =
|
|
1100
|
-
|
|
1099
|
+
const [, force] = React5.useReducer((n) => n + 1, 0);
|
|
1100
|
+
React5.useEffect(() => {
|
|
1101
1101
|
if (!active) return;
|
|
1102
1102
|
let raf = 0;
|
|
1103
1103
|
let last = 0;
|
|
@@ -1121,13 +1121,14 @@ function useTicker(active) {
|
|
|
1121
1121
|
function ScribeRecordingPill({ labels, onOpenSession, mobileOnly }) {
|
|
1122
1122
|
const state = useScribeStore((s) => s.state);
|
|
1123
1123
|
const sessionId = useScribeStore((s) => s.sessionId);
|
|
1124
|
+
const subjectId = useScribeStore((s) => s.subjectId);
|
|
1124
1125
|
const subjectName = useScribeStore((s) => s.subjectName);
|
|
1125
1126
|
const isMobile = useIsMobile();
|
|
1126
|
-
const [busy, setBusy] =
|
|
1127
|
+
const [busy, setBusy] = React5.useState(false);
|
|
1127
1128
|
const active = state === "recording" || state === "paused" || state === "interrupted";
|
|
1128
1129
|
useTicker(state === "recording");
|
|
1129
1130
|
const elapsedMs2 = active ? getSnapshot().elapsedMs : 0;
|
|
1130
|
-
const handleToggle =
|
|
1131
|
+
const handleToggle = React5.useCallback(async () => {
|
|
1131
1132
|
setBusy(true);
|
|
1132
1133
|
try {
|
|
1133
1134
|
if (state === "recording") await pauseSession();
|
|
@@ -1136,15 +1137,15 @@ function ScribeRecordingPill({ labels, onOpenSession, mobileOnly }) {
|
|
|
1136
1137
|
setBusy(false);
|
|
1137
1138
|
}
|
|
1138
1139
|
}, [state]);
|
|
1139
|
-
const handleStop =
|
|
1140
|
+
const handleStop = React5.useCallback(async () => {
|
|
1140
1141
|
setBusy(true);
|
|
1141
1142
|
try {
|
|
1142
1143
|
const id = await endSession();
|
|
1143
|
-
if (id) onOpenSession?.(id);
|
|
1144
|
+
if (id) onOpenSession?.(id, subjectId);
|
|
1144
1145
|
} finally {
|
|
1145
1146
|
setBusy(false);
|
|
1146
1147
|
}
|
|
1147
|
-
}, [onOpenSession]);
|
|
1148
|
+
}, [onOpenSession, subjectId]);
|
|
1148
1149
|
if (!active || !sessionId) return null;
|
|
1149
1150
|
if (mobileOnly && !isMobile) return null;
|
|
1150
1151
|
const tone = state === "recording" ? "bg-destructive/10 text-destructive border-destructive/30" : state === "interrupted" ? "bg-amber-500/10 text-amber-700 border-amber-500/30 dark:text-amber-400" : "bg-muted text-muted-foreground border-border";
|
|
@@ -1160,7 +1161,7 @@ function ScribeRecordingPill({ labels, onOpenSession, mobileOnly }) {
|
|
|
1160
1161
|
"button",
|
|
1161
1162
|
{
|
|
1162
1163
|
type: "button",
|
|
1163
|
-
onClick: () => onOpenSession?.(sessionId),
|
|
1164
|
+
onClick: () => onOpenSession?.(sessionId, subjectId),
|
|
1164
1165
|
className: "flex items-center gap-1.5 text-sm font-medium tabular-nums",
|
|
1165
1166
|
title: subjectName ?? labels.sessionSingular,
|
|
1166
1167
|
children: [
|
|
@@ -1221,112 +1222,6 @@ function ScribeStartButton({ labels, disabled, onStart, variant = "default", siz
|
|
|
1221
1222
|
labels.start
|
|
1222
1223
|
] });
|
|
1223
1224
|
}
|
|
1224
|
-
var RESUMABLE_WINDOW_MS = 30 * 60 * 1e3;
|
|
1225
|
-
function ScribeRecoveryBanner({ labels, onOpenSession }) {
|
|
1226
|
-
const orphans2 = useScribeStore((s) => s.orphans);
|
|
1227
|
-
const state = useScribeStore((s) => s.state);
|
|
1228
|
-
const [busy, setBusy] = React7.useState(null);
|
|
1229
|
-
const live2 = state === "recording" || state === "paused" || state === "requesting";
|
|
1230
|
-
if (live2) return null;
|
|
1231
|
-
if (orphans2.length === 0) return null;
|
|
1232
|
-
const session = orphans2[0];
|
|
1233
|
-
const interruptedAgoMs = Date.now() - session.lastTickEpochMs;
|
|
1234
|
-
const canResume = interruptedAgoMs < RESUMABLE_WINDOW_MS;
|
|
1235
|
-
const durationLabel = formatElapsed(orphanDurationMs(session));
|
|
1236
|
-
const run = async (fn) => {
|
|
1237
|
-
setBusy(session.id);
|
|
1238
|
-
try {
|
|
1239
|
-
await fn();
|
|
1240
|
-
} finally {
|
|
1241
|
-
setBusy(null);
|
|
1242
|
-
}
|
|
1243
|
-
};
|
|
1244
|
-
return /* @__PURE__ */ jsxs("div", { className: "mx-3 mb-3 flex flex-col gap-2 rounded-lg border border-amber-500/40 bg-amber-500/10 p-3 md:mx-6 md:flex-row md:items-center md:justify-between", children: [
|
|
1245
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
1246
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600 dark:text-amber-400" }),
|
|
1247
|
-
/* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
|
|
1248
|
-
/* @__PURE__ */ jsxs("p", { className: "font-medium", children: [
|
|
1249
|
-
labels.sessionSingular,
|
|
1250
|
-
" de ",
|
|
1251
|
-
durationLabel,
|
|
1252
|
-
" foi interrompido",
|
|
1253
|
-
session.subjectName ? ` \u2014 ${session.subjectName}` : "",
|
|
1254
|
-
"."
|
|
1255
|
-
] }),
|
|
1256
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "O \xE1udio j\xE1 gravado est\xE1 seguro e continua sendo enviado." })
|
|
1257
|
-
] })
|
|
1258
|
-
] }),
|
|
1259
|
-
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap gap-1.5", children: [
|
|
1260
|
-
canResume && /* @__PURE__ */ jsx(Button, { size: "sm", disabled: busy === session.id, onClick: () => void run(resumeSession), children: labels.resume }),
|
|
1261
|
-
/* @__PURE__ */ jsx(
|
|
1262
|
-
Button,
|
|
1263
|
-
{
|
|
1264
|
-
size: "sm",
|
|
1265
|
-
variant: "outline",
|
|
1266
|
-
disabled: busy === session.id,
|
|
1267
|
-
onClick: () => void run(async () => {
|
|
1268
|
-
const id = await endSession();
|
|
1269
|
-
dismissOrphan(session.id);
|
|
1270
|
-
if (id) onOpenSession?.(id);
|
|
1271
|
-
}),
|
|
1272
|
-
children: labels.finish
|
|
1273
|
-
}
|
|
1274
|
-
),
|
|
1275
|
-
/* @__PURE__ */ jsx(
|
|
1276
|
-
Button,
|
|
1277
|
-
{
|
|
1278
|
-
size: "sm",
|
|
1279
|
-
variant: "ghost",
|
|
1280
|
-
disabled: busy === session.id,
|
|
1281
|
-
onClick: () => void run(() => discardSession(session.id)),
|
|
1282
|
-
children: labels.discard
|
|
1283
|
-
}
|
|
1284
|
-
)
|
|
1285
|
-
] })
|
|
1286
|
-
] });
|
|
1287
|
-
}
|
|
1288
|
-
function ScribeConsentDialog({
|
|
1289
|
-
open,
|
|
1290
|
-
labels,
|
|
1291
|
-
subjectName,
|
|
1292
|
-
defaultConsentMode,
|
|
1293
|
-
onCancel,
|
|
1294
|
-
onConfirm
|
|
1295
|
-
}) {
|
|
1296
|
-
const [acknowledged, setAcknowledged] = React7.useState(false);
|
|
1297
|
-
React7.useEffect(() => {
|
|
1298
|
-
if (open) setAcknowledged(false);
|
|
1299
|
-
}, [open]);
|
|
1300
|
-
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (next) => !next && onCancel(), children: /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
|
|
1301
|
-
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsxs(ModalTitle, { className: "flex items-center gap-2", children: [
|
|
1302
|
-
/* @__PURE__ */ jsx(Mic, { className: "h-4 w-4" }),
|
|
1303
|
-
"Iniciar grava\xE7\xE3o"
|
|
1304
|
-
] }) }),
|
|
1305
|
-
/* @__PURE__ */ jsxs(ModalBody, { className: "space-y-3 text-sm", children: [
|
|
1306
|
-
/* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
|
|
1307
|
-
"O \xE1udio ",
|
|
1308
|
-
subjectName ? `de ${subjectName}` : `deste ${labels.sessionSingular.toLowerCase()}`,
|
|
1309
|
-
" ser\xE1 gravado e transcrito para gerar um documento. A grava\xE7\xE3o fica vis\xEDvel na barra superior enquanto durar."
|
|
1310
|
-
] }),
|
|
1311
|
-
/* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-start gap-2 rounded-md border p-3", children: [
|
|
1312
|
-
/* @__PURE__ */ jsx(Checkbox, { checked: acknowledged, onChange: setAcknowledged, className: "mt-0.5" }),
|
|
1313
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
1314
|
-
"Informei ",
|
|
1315
|
-
subjectName ?? "o titular",
|
|
1316
|
-
" de que este atendimento ser\xE1 gravado e obtive o consentimento."
|
|
1317
|
-
] })
|
|
1318
|
-
] }),
|
|
1319
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-start gap-2 text-xs text-muted-foreground", children: [
|
|
1320
|
-
/* @__PURE__ */ jsx(ShieldCheck, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
1321
|
-
"O \xE1udio \xE9 armazenado de forma privada e pode ser apagado a qualquer momento \u2014 a transcri\xE7\xE3o e o documento assinado permanecem."
|
|
1322
|
-
] })
|
|
1323
|
-
] }),
|
|
1324
|
-
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
1325
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onCancel, children: "Cancelar" }),
|
|
1326
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", disabled: !acknowledged, onClick: () => onConfirm(defaultConsentMode), children: labels.start })
|
|
1327
|
-
] })
|
|
1328
|
-
] }) });
|
|
1329
|
-
}
|
|
1330
1225
|
function client() {
|
|
1331
1226
|
const supabase = getSupabaseClientOptional();
|
|
1332
1227
|
if (!supabase) throw new Error("Supabase n\xE3o inicializado");
|
|
@@ -1866,11 +1761,24 @@ function watchTranscript(sessionId, stt) {
|
|
|
1866
1761
|
function transcriptText() {
|
|
1867
1762
|
return useLiveTranscript.getState().segments.map((s) => s.gap ? "[trecho perdido]" : (s.text ?? "").trim()).filter(Boolean).join(" ");
|
|
1868
1763
|
}
|
|
1764
|
+
var SCRIBE_START_EVENT = "scribe:start";
|
|
1765
|
+
var SCRIBE_AGENT_ROLE = "encounter";
|
|
1766
|
+
function requestScribeStart(detail) {
|
|
1767
|
+
window.dispatchEvent(new CustomEvent(SCRIBE_START_EVENT, { detail }));
|
|
1768
|
+
}
|
|
1769
|
+
function scribeSessionPath(args) {
|
|
1770
|
+
const { sessionId, subjectId, subjectEntity } = args;
|
|
1771
|
+
return subjectId ? `/${subjectEntity}/${subjectId}/scribe/${sessionId}` : `/scribe/${sessionId}`;
|
|
1772
|
+
}
|
|
1773
|
+
function startScribeSession(detail = {}) {
|
|
1774
|
+
if (!openAssistantAs(SCRIBE_AGENT_ROLE)) return;
|
|
1775
|
+
requestScribeStart(detail);
|
|
1776
|
+
}
|
|
1869
1777
|
|
|
1870
1778
|
// src/components/ScribeAssistantBridge.tsx
|
|
1871
1779
|
function useSecondTicker(active) {
|
|
1872
|
-
const [, force] =
|
|
1873
|
-
|
|
1780
|
+
const [, force] = React5.useReducer((n) => n + 1, 0);
|
|
1781
|
+
React5.useEffect(() => {
|
|
1874
1782
|
if (!active) return;
|
|
1875
1783
|
const timer3 = setInterval(() => {
|
|
1876
1784
|
if (document.visibilityState === "visible") force();
|
|
@@ -1883,18 +1791,83 @@ function useSecondTicker(active) {
|
|
|
1883
1791
|
};
|
|
1884
1792
|
}, [active]);
|
|
1885
1793
|
}
|
|
1886
|
-
|
|
1794
|
+
var RESUMABLE_WINDOW_MS = 30 * 60 * 1e3;
|
|
1795
|
+
function ScribeAssistantBridge({
|
|
1796
|
+
labels,
|
|
1797
|
+
retention: retention2,
|
|
1798
|
+
stt,
|
|
1799
|
+
locale,
|
|
1800
|
+
onOpenSession
|
|
1801
|
+
}) {
|
|
1802
|
+
const { user } = useAuth();
|
|
1887
1803
|
const state = useScribeStore((s) => s.state);
|
|
1888
1804
|
const sessionId = useScribeStore((s) => s.sessionId);
|
|
1805
|
+
const subjectId = useScribeStore((s) => s.subjectId);
|
|
1889
1806
|
const subjectName = useScribeStore((s) => s.subjectName);
|
|
1807
|
+
const orphans2 = useScribeStore((s) => s.orphans);
|
|
1890
1808
|
const live2 = state === "recording" || state === "paused" || state === "interrupted";
|
|
1891
1809
|
useSecondTicker(state === "recording");
|
|
1892
|
-
|
|
1810
|
+
React5.useEffect(() => {
|
|
1893
1811
|
watchTranscript(live2 ? sessionId : null, stt);
|
|
1894
1812
|
}, [live2, sessionId, stt]);
|
|
1895
1813
|
const elapsedMs2 = live2 ? Math.floor(getSnapshot().elapsedMs / 1e3) * 1e3 : 0;
|
|
1896
|
-
const [
|
|
1897
|
-
const
|
|
1814
|
+
const [pending, setPending] = React5.useState(null);
|
|
1815
|
+
const start = React5.useCallback(
|
|
1816
|
+
async (detail, consentMode) => {
|
|
1817
|
+
setPending(null);
|
|
1818
|
+
if (!user?.id) {
|
|
1819
|
+
toast.error("Fa\xE7a login para gravar um atendimento");
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
const result = await beginSession({
|
|
1823
|
+
userId: user.id,
|
|
1824
|
+
subjectId: detail.subjectId,
|
|
1825
|
+
subjectName: detail.subjectName,
|
|
1826
|
+
appointmentId: detail.appointmentId,
|
|
1827
|
+
templateKeys: detail.templateKeys,
|
|
1828
|
+
locale,
|
|
1829
|
+
consentMode
|
|
1830
|
+
});
|
|
1831
|
+
if (!result.ok) toast.error(`N\xE3o foi poss\xEDvel iniciar: ${result.reason}`);
|
|
1832
|
+
},
|
|
1833
|
+
[user?.id, locale]
|
|
1834
|
+
);
|
|
1835
|
+
React5.useEffect(() => {
|
|
1836
|
+
const onStart = (ev) => {
|
|
1837
|
+
const detail = ev.detail ?? {};
|
|
1838
|
+
if (retention2.requireConsent) setPending(detail);
|
|
1839
|
+
else void start(detail, retention2.defaultConsentMode);
|
|
1840
|
+
};
|
|
1841
|
+
window.addEventListener(SCRIBE_START_EVENT, onStart);
|
|
1842
|
+
return () => window.removeEventListener(SCRIBE_START_EVENT, onStart);
|
|
1843
|
+
}, [retention2.requireConsent, retention2.defaultConsentMode, start]);
|
|
1844
|
+
const activity = React5.useMemo(() => {
|
|
1845
|
+
if (pending && !live2) {
|
|
1846
|
+
const who = pending.subjectName;
|
|
1847
|
+
return {
|
|
1848
|
+
id: "scribe.session",
|
|
1849
|
+
kind: "recording",
|
|
1850
|
+
state: "pending",
|
|
1851
|
+
label: who ? `${labels.sessionSingular} \xB7 ${who}` : labels.sessionSingular,
|
|
1852
|
+
// A ticked box consents for nobody — the holder does. What it records
|
|
1853
|
+
// is the professional ASSERTING they obtained it, which is the fact
|
|
1854
|
+
// that has to be provable later (LGPD art. 5º II), stamped on the same
|
|
1855
|
+
// row as the audio rather than reconstructed afterwards.
|
|
1856
|
+
confirm: {
|
|
1857
|
+
acknowledge: `Informei ${who ?? "o titular"} de que este atendimento ser\xE1 gravado e obtive o consentimento.`,
|
|
1858
|
+
note: "O \xE1udio fica guardado de forma privada e pode ser apagado quando voc\xEA quiser \u2014 a transcri\xE7\xE3o e o documento permanecem."
|
|
1859
|
+
},
|
|
1860
|
+
actions: [
|
|
1861
|
+
{ intent: "cancel", label: "Cancelar", run: () => setPending(null) },
|
|
1862
|
+
{
|
|
1863
|
+
intent: "start",
|
|
1864
|
+
label: labels.start,
|
|
1865
|
+
primary: true,
|
|
1866
|
+
run: () => void start(pending, retention2.defaultConsentMode)
|
|
1867
|
+
}
|
|
1868
|
+
]
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1898
1871
|
if (live2) {
|
|
1899
1872
|
const actions = [
|
|
1900
1873
|
state === "paused" ? { intent: "resume", label: labels.resume, run: () => void resumeSession() } : { intent: "pause", label: labels.pause, run: () => void pauseSession() },
|
|
@@ -1902,10 +1875,15 @@ function ScribeAssistantBridge({ labels, stt, onOpenSession }) {
|
|
|
1902
1875
|
intent: "stop",
|
|
1903
1876
|
label: labels.finish,
|
|
1904
1877
|
primary: true,
|
|
1878
|
+
// Stopping ends the assistant's part of this, and the app opens the
|
|
1879
|
+
// session. Upload and transcription were never waiting on a click —
|
|
1880
|
+
// the pump carries them — so an "encerrado, gerar?" step in between
|
|
1881
|
+
// was a screen asking permission for work already in progress, on the
|
|
1882
|
+
// surface least able to do the rest of it.
|
|
1905
1883
|
run: async () => {
|
|
1906
1884
|
const id = sessionId;
|
|
1907
1885
|
await endSession();
|
|
1908
|
-
if (id)
|
|
1886
|
+
if (id) onOpenSession?.(id, subjectId);
|
|
1909
1887
|
}
|
|
1910
1888
|
}
|
|
1911
1889
|
];
|
|
@@ -1920,32 +1898,58 @@ function ScribeAssistantBridge({ labels, stt, onOpenSession }) {
|
|
|
1920
1898
|
stream: getCaptureStream
|
|
1921
1899
|
};
|
|
1922
1900
|
}
|
|
1923
|
-
|
|
1901
|
+
const orphan = orphans2[0];
|
|
1902
|
+
if (orphan) {
|
|
1903
|
+
const canResume = Date.now() - orphan.lastTickEpochMs < RESUMABLE_WINDOW_MS;
|
|
1904
|
+
const actions = [];
|
|
1905
|
+
if (canResume) {
|
|
1906
|
+
actions.push({ intent: "resume", label: labels.resume, run: () => void resumeSession() });
|
|
1907
|
+
}
|
|
1908
|
+
actions.push({
|
|
1909
|
+
intent: "stop",
|
|
1910
|
+
label: labels.finish,
|
|
1911
|
+
primary: true,
|
|
1912
|
+
run: async () => {
|
|
1913
|
+
const id = await endSession();
|
|
1914
|
+
dismissOrphan(orphan.id);
|
|
1915
|
+
if (id) onOpenSession?.(id, orphan.subjectId);
|
|
1916
|
+
}
|
|
1917
|
+
});
|
|
1918
|
+
actions.push({
|
|
1919
|
+
intent: "discard",
|
|
1920
|
+
label: labels.discard,
|
|
1921
|
+
run: () => void discardSession(orphan.id)
|
|
1922
|
+
});
|
|
1924
1923
|
return {
|
|
1925
1924
|
id: "scribe.session",
|
|
1926
1925
|
kind: "recording",
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
primary: true,
|
|
1934
|
-
run: () => {
|
|
1935
|
-
onOpenSession?.(finished.id);
|
|
1936
|
-
setFinished(null);
|
|
1937
|
-
}
|
|
1938
|
-
},
|
|
1939
|
-
{ intent: "discard", label: "Depois", run: () => setFinished(null) }
|
|
1940
|
-
]
|
|
1926
|
+
// Not `orphan.accumulatedMs` — see orphanDurationMs: older sessions sit
|
|
1927
|
+
// at 0, and half an hour of audio announced itself as 00:00.
|
|
1928
|
+
elapsedMs: orphanDurationMs(orphan),
|
|
1929
|
+
label: orphan.subjectName ? `${labels.sessionSingular} interrompido \xB7 ${orphan.subjectName}` : `${labels.sessionSingular} interrompido`,
|
|
1930
|
+
state: "paused",
|
|
1931
|
+
actions
|
|
1941
1932
|
};
|
|
1942
1933
|
}
|
|
1943
1934
|
return null;
|
|
1944
|
-
}, [
|
|
1935
|
+
}, [
|
|
1936
|
+
live2,
|
|
1937
|
+
state,
|
|
1938
|
+
elapsedMs2,
|
|
1939
|
+
subjectId,
|
|
1940
|
+
subjectName,
|
|
1941
|
+
sessionId,
|
|
1942
|
+
orphans2,
|
|
1943
|
+
labels,
|
|
1944
|
+
onOpenSession,
|
|
1945
|
+
pending,
|
|
1946
|
+
start,
|
|
1947
|
+
retention2.defaultConsentMode
|
|
1948
|
+
]);
|
|
1945
1949
|
usePublishAssistantActivity(activity);
|
|
1946
|
-
const liveRef =
|
|
1950
|
+
const liveRef = React5.useRef({ live: live2, state, subjectName, sessionId });
|
|
1947
1951
|
liveRef.current = { live: live2, state, subjectName, sessionId };
|
|
1948
|
-
const surface =
|
|
1952
|
+
const surface = React5.useMemo(
|
|
1949
1953
|
() => ({
|
|
1950
1954
|
id: "scribe.session",
|
|
1951
1955
|
pluginId: "scribe",
|
|
@@ -1978,138 +1982,38 @@ function ScribeAssistantBridge({ labels, stt, onOpenSession }) {
|
|
|
1978
1982
|
useAgentSurface(surface);
|
|
1979
1983
|
const supported = isCaptureSupported();
|
|
1980
1984
|
useRegisterAssistantLauncher(
|
|
1981
|
-
live2 ? null : {
|
|
1985
|
+
live2 || pending ? null : {
|
|
1982
1986
|
id: "scribe.start",
|
|
1983
1987
|
kind: "recording",
|
|
1988
|
+
role: SCRIBE_AGENT_ROLE,
|
|
1984
1989
|
label: labels.start,
|
|
1985
1990
|
disabled: !supported,
|
|
1986
1991
|
disabledReason: "Este navegador n\xE3o suporta grava\xE7\xE3o de \xE1udio",
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
}
|
|
1992
|
+
// Already in her thread, so this is the plain request — no need to
|
|
1993
|
+
// re-open the panel it is being pressed inside.
|
|
1994
|
+
run: () => requestScribeStart({})
|
|
1991
1995
|
}
|
|
1992
1996
|
);
|
|
1993
1997
|
return null;
|
|
1994
1998
|
}
|
|
1995
|
-
var SCRIBE_START_EVENT = "scribe:start";
|
|
1996
1999
|
function ScribeShellMount({ labels, retention: retention2, stt, locale, onOpenSession }) {
|
|
1997
|
-
|
|
1998
|
-
const railOffset = useRightRailStore((s) => s.open ? s.width : 0);
|
|
1999
|
-
const contentTop = useContentTop();
|
|
2000
|
-
const bannerRef = React7.useRef(null);
|
|
2001
|
-
useReserveSpace(bannerRef);
|
|
2002
|
-
const [pending, setPending] = React7.useState(null);
|
|
2003
|
-
React7.useEffect(() => {
|
|
2000
|
+
React5.useEffect(() => {
|
|
2004
2001
|
wireScribeStore();
|
|
2005
2002
|
}, []);
|
|
2006
|
-
React7.useEffect(() => {
|
|
2007
|
-
const onStart = (ev) => {
|
|
2008
|
-
const detail = ev.detail ?? {};
|
|
2009
|
-
if (retention2.requireConsent) setPending(detail);
|
|
2010
|
-
else void start(detail, retention2.defaultConsentMode);
|
|
2011
|
-
};
|
|
2012
|
-
window.addEventListener(SCRIBE_START_EVENT, onStart);
|
|
2013
|
-
return () => window.removeEventListener(SCRIBE_START_EVENT, onStart);
|
|
2014
|
-
}, [retention2.requireConsent, retention2.defaultConsentMode, user?.id, locale]);
|
|
2015
|
-
const start = React7.useCallback(
|
|
2016
|
-
async (detail, consentMode) => {
|
|
2017
|
-
if (!user?.id) {
|
|
2018
|
-
toast.error("Fa\xE7a login para gravar um atendimento");
|
|
2019
|
-
return;
|
|
2020
|
-
}
|
|
2021
|
-
const result = await beginSession({
|
|
2022
|
-
userId: user.id,
|
|
2023
|
-
subjectId: detail.subjectId,
|
|
2024
|
-
subjectName: detail.subjectName,
|
|
2025
|
-
appointmentId: detail.appointmentId,
|
|
2026
|
-
templateKeys: detail.templateKeys,
|
|
2027
|
-
locale,
|
|
2028
|
-
consentMode
|
|
2029
|
-
});
|
|
2030
|
-
if (!result.ok) {
|
|
2031
|
-
toast.error(`N\xE3o foi poss\xEDvel iniciar: ${result.reason}`);
|
|
2032
|
-
return;
|
|
2033
|
-
}
|
|
2034
|
-
toast.success(`${labels.sessionSingular} iniciado`);
|
|
2035
|
-
},
|
|
2036
|
-
[user?.id, locale, labels.sessionSingular]
|
|
2037
|
-
);
|
|
2038
2003
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2039
|
-
/* @__PURE__ */ jsx(ScribeAssistantBridge, { labels, stt, onOpenSession }),
|
|
2040
|
-
/* @__PURE__ */ jsx(ScribeRecordingPill, { labels, onOpenSession, mobileOnly: true }),
|
|
2041
2004
|
/* @__PURE__ */ jsx(
|
|
2042
|
-
|
|
2005
|
+
ScribeAssistantBridge,
|
|
2043
2006
|
{
|
|
2044
|
-
open: pending !== null,
|
|
2045
2007
|
labels,
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
const detail = pending;
|
|
2051
|
-
setPending(null);
|
|
2052
|
-
if (detail) void start(detail, mode);
|
|
2053
|
-
}
|
|
2008
|
+
retention: retention2,
|
|
2009
|
+
stt,
|
|
2010
|
+
locale,
|
|
2011
|
+
onOpenSession
|
|
2054
2012
|
}
|
|
2055
2013
|
),
|
|
2056
|
-
|
|
2057
|
-
/* @__PURE__ */ jsx(
|
|
2058
|
-
"div",
|
|
2059
|
-
{
|
|
2060
|
-
ref: bannerRef,
|
|
2061
|
-
className: "pointer-events-auto fixed left-0 z-40",
|
|
2062
|
-
style: { top: contentTop, right: railOffset },
|
|
2063
|
-
children: /* @__PURE__ */ jsx(ScribeRecoveryBanner, { labels, onOpenSession })
|
|
2064
|
-
}
|
|
2065
|
-
),
|
|
2066
|
-
document.body
|
|
2067
|
-
)
|
|
2014
|
+
/* @__PURE__ */ jsx(ScribeRecordingPill, { labels, onOpenSession, mobileOnly: true })
|
|
2068
2015
|
] });
|
|
2069
2016
|
}
|
|
2070
|
-
function useReserveSpace(ref) {
|
|
2071
|
-
React7.useEffect(() => {
|
|
2072
|
-
if (typeof document === "undefined") return;
|
|
2073
|
-
const main = document.querySelector("main");
|
|
2074
|
-
const node = ref.current;
|
|
2075
|
-
if (!main || !node) return;
|
|
2076
|
-
const previous = main.style.paddingTop;
|
|
2077
|
-
const apply = () => {
|
|
2078
|
-
const height = node.getBoundingClientRect().height;
|
|
2079
|
-
main.style.paddingTop = height > 0 ? `${Math.ceil(height)}px` : previous;
|
|
2080
|
-
};
|
|
2081
|
-
apply();
|
|
2082
|
-
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(apply) : null;
|
|
2083
|
-
observer?.observe(node);
|
|
2084
|
-
return () => {
|
|
2085
|
-
observer?.disconnect();
|
|
2086
|
-
main.style.paddingTop = previous;
|
|
2087
|
-
};
|
|
2088
|
-
}, [ref]);
|
|
2089
|
-
}
|
|
2090
|
-
function useContentTop() {
|
|
2091
|
-
const [top, setTop] = React7.useState(56);
|
|
2092
|
-
React7.useEffect(() => {
|
|
2093
|
-
if (typeof document === "undefined") return;
|
|
2094
|
-
const measure = () => {
|
|
2095
|
-
const main2 = document.querySelector("main");
|
|
2096
|
-
if (main2) setTop(Math.round(main2.getBoundingClientRect().top));
|
|
2097
|
-
};
|
|
2098
|
-
measure();
|
|
2099
|
-
window.addEventListener("resize", measure);
|
|
2100
|
-
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
|
|
2101
|
-
const main = document.querySelector("main");
|
|
2102
|
-
if (observer && main) observer.observe(main);
|
|
2103
|
-
return () => {
|
|
2104
|
-
window.removeEventListener("resize", measure);
|
|
2105
|
-
observer?.disconnect();
|
|
2106
|
-
};
|
|
2107
|
-
}, []);
|
|
2108
|
-
return top;
|
|
2109
|
-
}
|
|
2110
|
-
function requestScribeStart(detail) {
|
|
2111
|
-
window.dispatchEvent(new CustomEvent(SCRIBE_START_EVENT, { detail }));
|
|
2112
|
-
}
|
|
2113
2017
|
|
|
2114
2018
|
// src/lib/prompt.ts
|
|
2115
2019
|
var PROMPT_VERSION = "scribe/narrative@1";
|
|
@@ -2277,15 +2181,15 @@ function DraftPanel({
|
|
|
2277
2181
|
onDiscarded
|
|
2278
2182
|
}) {
|
|
2279
2183
|
const original = generation.markdown ?? "";
|
|
2280
|
-
const [value, setValue] =
|
|
2281
|
-
const [saving, setSaving] =
|
|
2282
|
-
const [committing, setCommitting] =
|
|
2283
|
-
const timer3 =
|
|
2284
|
-
|
|
2184
|
+
const [value, setValue] = React5.useState(generation.markdownEdited ?? original);
|
|
2185
|
+
const [saving, setSaving] = React5.useState(false);
|
|
2186
|
+
const [committing, setCommitting] = React5.useState(false);
|
|
2187
|
+
const timer3 = React5.useRef(null);
|
|
2188
|
+
React5.useEffect(() => {
|
|
2285
2189
|
setValue(generation.markdownEdited ?? generation.markdown ?? "");
|
|
2286
2190
|
}, [generation.id, generation.markdown, generation.markdownEdited]);
|
|
2287
2191
|
const edited = value.trim() !== original.trim();
|
|
2288
|
-
const handleChange =
|
|
2192
|
+
const handleChange = React5.useCallback(
|
|
2289
2193
|
(next) => {
|
|
2290
2194
|
setValue(next);
|
|
2291
2195
|
if (timer3.current) clearTimeout(timer3.current);
|
|
@@ -2296,7 +2200,7 @@ function DraftPanel({
|
|
|
2296
2200
|
},
|
|
2297
2201
|
[generation.id]
|
|
2298
2202
|
);
|
|
2299
|
-
|
|
2203
|
+
React5.useEffect(() => {
|
|
2300
2204
|
return () => {
|
|
2301
2205
|
if (timer3.current) {
|
|
2302
2206
|
clearTimeout(timer3.current);
|
|
@@ -2304,7 +2208,7 @@ function DraftPanel({
|
|
|
2304
2208
|
}
|
|
2305
2209
|
};
|
|
2306
2210
|
}, [generation.id, value]);
|
|
2307
|
-
const handleCommit =
|
|
2211
|
+
const handleCommit = React5.useCallback(async () => {
|
|
2308
2212
|
setCommitting(true);
|
|
2309
2213
|
try {
|
|
2310
2214
|
if (timer3.current) clearTimeout(timer3.current);
|
|
@@ -2494,36 +2398,38 @@ function ScribeSessionPage({
|
|
|
2494
2398
|
stt,
|
|
2495
2399
|
generateEndpoint,
|
|
2496
2400
|
onOpenDocument,
|
|
2497
|
-
onBack
|
|
2401
|
+
onBack,
|
|
2402
|
+
onSessionLoaded
|
|
2498
2403
|
}) {
|
|
2499
|
-
const [session, setSession] =
|
|
2500
|
-
const [segments, setSegments] =
|
|
2501
|
-
const [generations, setGenerations] =
|
|
2502
|
-
const [loading, setLoading] =
|
|
2503
|
-
const [generating, setGenerating] =
|
|
2504
|
-
const [tab, setTab] =
|
|
2505
|
-
const draining2 =
|
|
2506
|
-
const refresh =
|
|
2404
|
+
const [session, setSession] = React5.useState(null);
|
|
2405
|
+
const [segments, setSegments] = React5.useState([]);
|
|
2406
|
+
const [generations, setGenerations] = React5.useState([]);
|
|
2407
|
+
const [loading, setLoading] = React5.useState(true);
|
|
2408
|
+
const [generating, setGenerating] = React5.useState(false);
|
|
2409
|
+
const [tab, setTab] = React5.useState("transcript");
|
|
2410
|
+
const draining2 = React5.useRef(false);
|
|
2411
|
+
const refresh = React5.useCallback(async () => {
|
|
2507
2412
|
const [s, segs, gens] = await Promise.all([
|
|
2508
2413
|
fetchSession(sessionId),
|
|
2509
2414
|
fetchSegments(sessionId),
|
|
2510
2415
|
fetchGenerations(sessionId)
|
|
2511
2416
|
]);
|
|
2512
2417
|
setSession(s);
|
|
2418
|
+
if (s) onSessionLoaded?.(s);
|
|
2513
2419
|
setSegments(segs);
|
|
2514
2420
|
setGenerations(gens);
|
|
2515
2421
|
return { session: s, segments: segs };
|
|
2516
|
-
}, [sessionId]);
|
|
2517
|
-
|
|
2422
|
+
}, [sessionId, onSessionLoaded]);
|
|
2423
|
+
React5.useEffect(() => {
|
|
2518
2424
|
setLoading(true);
|
|
2519
2425
|
void refresh().finally(() => setLoading(false));
|
|
2520
2426
|
}, [refresh]);
|
|
2521
|
-
const pendingStt =
|
|
2427
|
+
const pendingStt = React5.useMemo(
|
|
2522
2428
|
() => segments.filter((s) => s.uploadState === "uploaded" && ["pending", "failed"].includes(s.sttState) && s.sttAttempts < 4),
|
|
2523
2429
|
[segments]
|
|
2524
2430
|
);
|
|
2525
2431
|
const busy = pendingStt.length > 0 || segments.some((s) => s.sttState === "running");
|
|
2526
|
-
|
|
2432
|
+
React5.useEffect(() => {
|
|
2527
2433
|
if (!busy) return;
|
|
2528
2434
|
const id = setInterval(() => {
|
|
2529
2435
|
void (async () => {
|
|
@@ -2552,7 +2458,7 @@ function ScribeSessionPage({
|
|
|
2552
2458
|
return () => clearInterval(id);
|
|
2553
2459
|
}, [busy, pendingStt.length, sessionId, stt, refresh]);
|
|
2554
2460
|
const hasTranscript = segments.some((s) => !s.gap && s.text?.trim());
|
|
2555
|
-
const handleGenerate =
|
|
2461
|
+
const handleGenerate = React5.useCallback(
|
|
2556
2462
|
async (preset) => {
|
|
2557
2463
|
setGenerating(true);
|
|
2558
2464
|
try {
|
|
@@ -2583,7 +2489,7 @@ function ScribeSessionPage({
|
|
|
2583
2489
|
);
|
|
2584
2490
|
const activeGeneration = generations.find((g) => g.id === tab);
|
|
2585
2491
|
useAgentSurface(
|
|
2586
|
-
|
|
2492
|
+
React5.useMemo(
|
|
2587
2493
|
() => ({
|
|
2588
2494
|
id: "scribe.session",
|
|
2589
2495
|
describe: () => ({
|
|
@@ -2725,25 +2631,24 @@ function statusLabel(status) {
|
|
|
2725
2631
|
};
|
|
2726
2632
|
return map[status] ?? status;
|
|
2727
2633
|
}
|
|
2728
|
-
function ScribeHeaderAction({ labels, item,
|
|
2634
|
+
function ScribeHeaderAction({ labels, item, entityId }) {
|
|
2729
2635
|
const subjectId = item?.personId ?? item?.id;
|
|
2730
2636
|
const subjectName = item?.name ?? item?.full_name;
|
|
2731
2637
|
const { entitled } = useAccessOptional();
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
labels,
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
}
|
|
2745
|
-
}
|
|
2638
|
+
const busy = useScribeStore((s) => s.state === "recording" || s.state === "paused" || s.state === "interrupted" || s.state === "requesting");
|
|
2639
|
+
const available = !!subjectId && entitled("scribe") && isCaptureSupported();
|
|
2640
|
+
useHeaderAction(
|
|
2641
|
+
entityId ? `${entityId}.detail.header.actions` : "",
|
|
2642
|
+
available ? {
|
|
2643
|
+
id: "scribe-start",
|
|
2644
|
+
label: labels.start,
|
|
2645
|
+
icon: Mic,
|
|
2646
|
+
intent: "secondary",
|
|
2647
|
+
disabled: busy,
|
|
2648
|
+
onSelect: () => startScribeSession({ subjectId, subjectName })
|
|
2649
|
+
} : null
|
|
2746
2650
|
);
|
|
2651
|
+
return null;
|
|
2747
2652
|
}
|
|
2748
2653
|
|
|
2749
2654
|
// src/migrations/index.ts
|
|
@@ -3272,8 +3177,8 @@ function registerScribeTimelineSource() {
|
|
|
3272
3177
|
}
|
|
3273
3178
|
var BARS = 8;
|
|
3274
3179
|
function DeviceLevel({ deviceId, onSignal }) {
|
|
3275
|
-
const barsRef =
|
|
3276
|
-
|
|
3180
|
+
const barsRef = React5.useRef([]);
|
|
3181
|
+
React5.useEffect(() => {
|
|
3277
3182
|
let cancelled = false;
|
|
3278
3183
|
let stream = null;
|
|
3279
3184
|
let context = null;
|
|
@@ -3334,12 +3239,12 @@ function DeviceLevel({ deviceId, onSignal }) {
|
|
|
3334
3239
|
)) });
|
|
3335
3240
|
}
|
|
3336
3241
|
function ScribeDevicePicker({ open, onOpenChange }) {
|
|
3337
|
-
const [devices, setDevices] =
|
|
3338
|
-
const [selected, setSelected] =
|
|
3339
|
-
const [remember, setRemember] =
|
|
3340
|
-
const [signals, setSignals] =
|
|
3341
|
-
const [busy, setBusy] =
|
|
3342
|
-
|
|
3242
|
+
const [devices, setDevices] = React5.useState([]);
|
|
3243
|
+
const [selected, setSelected] = React5.useState(null);
|
|
3244
|
+
const [remember, setRemember] = React5.useState(true);
|
|
3245
|
+
const [signals, setSignals] = React5.useState({});
|
|
3246
|
+
const [busy, setBusy] = React5.useState(false);
|
|
3247
|
+
React5.useEffect(() => {
|
|
3343
3248
|
if (!open) return;
|
|
3344
3249
|
let cancelled = false;
|
|
3345
3250
|
const load2 = async () => {
|
|
@@ -3360,7 +3265,7 @@ function ScribeDevicePicker({ open, onOpenChange }) {
|
|
|
3360
3265
|
cancelled = true;
|
|
3361
3266
|
};
|
|
3362
3267
|
}, [open]);
|
|
3363
|
-
const markSignal =
|
|
3268
|
+
const markSignal = React5.useCallback((deviceId) => {
|
|
3364
3269
|
setSignals((prev) => prev[deviceId] ? prev : { ...prev, [deviceId]: true });
|
|
3365
3270
|
}, []);
|
|
3366
3271
|
const confirm = async () => {
|
|
@@ -3428,11 +3333,11 @@ function ScribeLivePanel({ labels, onOpenSession }) {
|
|
|
3428
3333
|
const sessionId = useScribeStore((s) => s.sessionId);
|
|
3429
3334
|
const subjectName = useScribeStore((s) => s.subjectName);
|
|
3430
3335
|
const segments = useLiveTranscript((s) => s.segments);
|
|
3431
|
-
const [busy, setBusy] =
|
|
3432
|
-
const [pickerOpen, setPickerOpen] =
|
|
3433
|
-
const [, force] =
|
|
3336
|
+
const [busy, setBusy] = React5.useState(false);
|
|
3337
|
+
const [pickerOpen, setPickerOpen] = React5.useState(false);
|
|
3338
|
+
const [, force] = React5.useReducer((n) => n + 1, 0);
|
|
3434
3339
|
const live2 = state === "recording" || state === "paused" || state === "interrupted";
|
|
3435
|
-
|
|
3340
|
+
React5.useEffect(() => {
|
|
3436
3341
|
if (state !== "recording") return;
|
|
3437
3342
|
const timer3 = setInterval(() => {
|
|
3438
3343
|
if (document.visibilityState === "visible") force();
|
|
@@ -3556,33 +3461,37 @@ function createScribePlugin(options) {
|
|
|
3556
3461
|
registerScribeTimelineSource();
|
|
3557
3462
|
initScribeRuntime({ retention: retention2 });
|
|
3558
3463
|
wireScribeStore();
|
|
3559
|
-
const
|
|
3560
|
-
|
|
3464
|
+
const subjectEntities = options.subjectEntities ?? ["clients", "patients", "people", "persons", "customers"];
|
|
3465
|
+
const sessionPath = options.sessionPath ?? ((args) => scribeSessionPath({ ...args, subjectEntity: subjectEntities[0] }));
|
|
3466
|
+
const openSession = (sessionId, subjectId) => {
|
|
3467
|
+
window.location.hash = sessionPath({ sessionId, subjectId });
|
|
3561
3468
|
};
|
|
3562
|
-
const ShellMount = () =>
|
|
3469
|
+
const ShellMount = () => React5__default.createElement(ScribeShellMount, { labels, retention: retention2, stt: options.stt, locale, onOpenSession: openSession });
|
|
3563
3470
|
ShellMount.displayName = "ScribeShellMount";
|
|
3564
3471
|
const SessionRoute = () => {
|
|
3565
3472
|
const hash = typeof window !== "undefined" ? window.location.hash : "";
|
|
3566
3473
|
const sessionId = hash.split("/scribe/")[1]?.split("/")[0] ?? "";
|
|
3567
3474
|
if (!sessionId) return null;
|
|
3568
|
-
return
|
|
3475
|
+
return React5__default.createElement(ScribeSessionPage, {
|
|
3569
3476
|
sessionId,
|
|
3570
3477
|
labels,
|
|
3571
3478
|
presets,
|
|
3572
3479
|
stt: options.stt,
|
|
3573
3480
|
generateEndpoint,
|
|
3481
|
+
onSessionLoaded: (session) => {
|
|
3482
|
+
if (session.subjectId) openSession(session.id, session.subjectId);
|
|
3483
|
+
},
|
|
3574
3484
|
onBack: () => {
|
|
3575
|
-
window.location.hash =
|
|
3485
|
+
window.location.hash = `/${subjectEntities[0]}`;
|
|
3576
3486
|
},
|
|
3577
3487
|
onOpenDocument: () => {
|
|
3578
|
-
window.location.hash =
|
|
3488
|
+
window.location.hash = `/${subjectEntities[0]}`;
|
|
3579
3489
|
}
|
|
3580
3490
|
});
|
|
3581
3491
|
};
|
|
3582
3492
|
SessionRoute.displayName = "ScribeSessionRoute";
|
|
3583
|
-
const HeaderAction = (props) =>
|
|
3493
|
+
const HeaderAction = (props) => React5__default.createElement(ScribeHeaderAction, { ...props, labels });
|
|
3584
3494
|
HeaderAction.displayName = "ScribeHeaderAction";
|
|
3585
|
-
const subjectEntities = options.subjectEntities ?? ["clients", "patients", "people", "persons", "customers"];
|
|
3586
3495
|
return {
|
|
3587
3496
|
id: "scribe",
|
|
3588
3497
|
name: labels.sessionPlural,
|
|
@@ -3597,6 +3506,10 @@ function createScribePlugin(options) {
|
|
|
3597
3506
|
// impressa passaria a depender de bucket, duas edge functions e chave de
|
|
3598
3507
|
// modelo.
|
|
3599
3508
|
dependencies: ["custom_forms"],
|
|
3509
|
+
// O trabalho deste plugin é de quem está NA SALA — grava o atendimento e o
|
|
3510
|
+
// transforma em documento. O plugin diz o papel; o app diz o nome (no salão,
|
|
3511
|
+
// a Auxiliar de Atendimento). Ver AgentRole em @fayz-ai/core.
|
|
3512
|
+
defaultAgentRole: SCRIBE_AGENT_ROLE,
|
|
3600
3513
|
navigation: [],
|
|
3601
3514
|
routes: [
|
|
3602
3515
|
{
|
|
@@ -3675,6 +3588,6 @@ function createScribePlugin(options) {
|
|
|
3675
3588
|
};
|
|
3676
3589
|
}
|
|
3677
3590
|
|
|
3678
|
-
export { PROMPT_VERSION, SCRIBE_START_EVENT, ScribeDevicePicker, ScribeLivePanel, ScribeSessionPage, ScribeStartButton, beginSession, createScribePlugin, deleteSessionAudio, discardSession, endSession, fetchGenerations, fetchOpenSessions, fetchSegments, fetchSession, fetchSessionsForSubject, formatElapsed, formatOffset, isCaptureSupported, isNarrativeSchema, parseSections, pauseSession, renderTranscript, requestScribeStart, resumeSession, useScribeStore };
|
|
3591
|
+
export { PROMPT_VERSION, SCRIBE_AGENT_ROLE, SCRIBE_START_EVENT, ScribeDevicePicker, ScribeLivePanel, ScribeSessionPage, ScribeStartButton, beginSession, createScribePlugin, deleteSessionAudio, discardSession, endSession, fetchGenerations, fetchOpenSessions, fetchSegments, fetchSession, fetchSessionsForSubject, formatElapsed, formatOffset, isCaptureSupported, isNarrativeSchema, parseSections, pauseSession, renderTranscript, requestScribeStart, resumeSession, scribeSessionPath, startScribeSession, useScribeStore };
|
|
3679
3592
|
//# sourceMappingURL=index.js.map
|
|
3680
3593
|
//# sourceMappingURL=index.js.map
|