pediatric-ai-scribe-v3/public/app/assets/Recorder-DdXTjnSC.js
Daniel aab8376fa1 chore(verify): drop vanilla-era test + linter; rebuild React bundle
The verify pipeline carried two scripts whose targets no longer
exist after the vanilla deletion at commit 25a9c4c:

  test/calc-math.test.js  — required public/js/calc-math.js (gone);
    every assertion is now covered by shared/clinical/
    calculators.test.ts (vitest).
  scripts/lint-references.js — scans public/js/*.js for DOM id
    references and checks each target exists in public/components/
    *.html. Those trees are gone; TypeScript + knip + vite build
    cover the equivalent dead-reference class for the React tree.

package.json:
  • `verify` drops `test:node` and `lint:refs`; now runs
    typecheck + vitest + knip + client vite build.
  • `test:node` script removed (test/ directory empty).

public/app/assets/ — fresh vite build so the bundle hashes match
the current source (new chunks for ByVisitAge, Cms, DxPicker,
EditableResult, LabsList, Milestones, OutputActions, Recorder,
Shadess, SlideEditor, VisitNote — all features landed in the
preceding commits).

Verified locally on node 20: tsc + 9 test files / 177 tests pass,
knip finds nothing, vite builds 35 chunks, client bundle is 343 kB
initial / 106 kB gzipped (Cms is the biggest lazy chunk at 402 kB
due to Tiptap/ProseMirror).
2026-04-24 05:46:59 +02:00

1 line
No EOL
7.6 KiB
JavaScript

import{i as e,n as t,t as n}from"./jsx-runtime-ByY1xr43.js";var r=e(t(),1),i=class{mediaRecorder=null;chunks=[];stream=null;start(){return this.chunks=[],navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:16e3,echoCancellation:!0,noiseSuppression:!0}}).then(e=>{this.stream=e;let t=MediaRecorder.isTypeSupported(`audio/webm;codecs=opus`)?`audio/webm;codecs=opus`:`audio/webm`;this.mediaRecorder=new MediaRecorder(e,{mimeType:t,audioBitsPerSecond:32e3}),this.mediaRecorder.ondataavailable=e=>{e.data.size>0&&this.chunks.push(e.data)},this.mediaRecorder.start(1e3)})}stop(){return new Promise(e=>{if(!this.mediaRecorder||this.mediaRecorder.state===`inactive`){e(null);return}this.mediaRecorder.onstop=()=>{let t=new Blob(this.chunks,{type:this.mediaRecorder.mimeType});this.stream&&this.stream.getTracks().forEach(e=>e.stop()),e(t)},this.mediaRecorder.stop()})}pause(){if(this.mediaRecorder&&typeof this.mediaRecorder.pause==`function`&&this.mediaRecorder.state===`recording`)try{this.mediaRecorder.pause()}catch{}}resume(){if(this.mediaRecorder)try{if(typeof this.mediaRecorder.resume==`function`&&this.mediaRecorder.state===`paused`)this.mediaRecorder.resume();else if(this.mediaRecorder.state===`inactive`&&this.stream&&this.stream.active){let e=MediaRecorder.isTypeSupported(`audio/webm;codecs=opus`)?`audio/webm;codecs=opus`:`audio/webm`;this.mediaRecorder=new MediaRecorder(this.stream,{mimeType:e,audioBitsPerSecond:32e3}),this.mediaRecorder.ondataavailable=e=>{e.data.size>0&&this.chunks.push(e.data)},this.mediaRecorder.start(1e3)}}catch{}}},a=null;async function o(){try{let e=await(await fetch(`/api/transcribe/status`,{credentials:`include`})).json();a=!!e.available,e.provider}catch{a=!1}}function s(){return a}async function c(e,t=`encounter`){if(a===null&&await o(),a===!1)return{success:!1,noProvider:!0,error:`No transcription API configured — using live transcript`};let n=new FormData;n.append(`audio`,e,`audio.webm`);try{let r=await(await fetch(`/api/transcribe`,{method:`POST`,credentials:`include`,body:n})).json();return!r.success&&e.size>0&&l(e,t+`-failed-transcription`).catch(()=>{}),r}catch(n){return l(e,t+`-failed-transcription`).catch(()=>{}),{success:!1,error:n.message}}}async function l(e,t){try{let n=new FormData;n.append(`audio`,e,`audio.webm`),n.append(`module`,t);let r=await(await fetch(`/api/audio-backups`,{method:`POST`,credentials:`include`,body:n})).json();if(r.success&&r.id)return r.id}catch{}return m(e,t)}var u=`PedScribeAudioBackup`,d=`recordings`,f=null;function p(){return f?Promise.resolve(f):new Promise((e,t)=>{let n=indexedDB.open(u,1);n.onupgradeneeded=e=>{let t=e.target.result;t.objectStoreNames.contains(d)||t.createObjectStore(d,{keyPath:`id`,autoIncrement:!0}).createIndex(`timestamp`,`timestamp`,{unique:!1})},n.onsuccess=()=>{f=n.result,e(f)},n.onerror=()=>t(Error(`IndexedDB open failed`))})}function m(e,t){return p().then(n=>new Promise((r,i)=>{let a=n.transaction(d,`readwrite`).objectStore(d).add({blob:e,module:t,timestamp:Date.now(),size:e.size,mimeType:e.type});a.onsuccess=()=>r(a.result),a.onerror=()=>i(Error(`Failed to save audio backup`))})).catch(()=>null)}var h=`ped_web_speech_enabled`;function g(){if(typeof window>`u`)return null;let e=window;return e.SpeechRecognition||e.webkitSpeechRecognition||null}function _(){try{return localStorage.getItem(h)===`1`||localStorage.getItem(h)===`true`}catch{return!1}}function v(e){let t=g();if(!t)return null;let n=null,r=!1;function i(){let n=new t;return n.continuous=!0,n.interimResults=!0,n.lang=`en-US`,n.maxAlternatives=1,n.onresult=t=>{let n=``;for(let r=t.resultIndex;r<t.results.length;r++){let i=t.results[r][0].transcript;t.results[r].isFinal?e.onFinal(i+` `):n=i}e.onInterim(n)},n.onerror=t=>{t.error===`no-speech`||t.error===`aborted`||e.onError?.(t.error)},n.onend=()=>{if(r)try{n.start()}catch{}},n}return{start:()=>{r=!0,n=i();try{n.start()}catch{}},stop:()=>{if(r=!1,n)try{n.stop()}catch{}}}}function y(e,t){if(!e||!t)return e;let n=e.trim();if(!n||t.trimEnd().endsWith(n))return``;let r=n.split(/\s+/);if(r.length>=3){if(t.trimEnd().split(/\s+/).slice(-r.length).join(` `)===n)return``;let e=Math.ceil(r.length/2),i=r.slice(0,e).join(` `);if(t.trimEnd().endsWith(i))return r.slice(e).join(` `)+` `}return e}var b=n(),x=`inline-flex items-center gap-2 px-4 py-2 rounded-md text-sm font-semibold border transition-colors`;function S({module:e,onTranscript:t,onInterim:n,onError:a,disabled:l}){let u=(0,r.useRef)(null),d=(0,r.useRef)(null),f=(0,r.useRef)(``),p=(0,r.useRef)(null),m=(0,r.useRef)(0),h=(0,r.useRef)(0),g=(0,r.useRef)(0),[S,C]=(0,r.useState)(`idle`),[w,T]=(0,r.useState)(0);(0,r.useEffect)(()=>{s()===null&&o()},[]),(0,r.useEffect)(()=>()=>{p.current&&window.clearInterval(p.current),d.current&&d.current.stop(),u.current&&u.current.stop().catch(()=>{})},[]);function E(){p.current&&window.clearInterval(p.current),p.current=window.setInterval(()=>{T(Math.floor((Date.now()-m.current-h.current)/1e3))},1e3)}function D(){p.current&&=(window.clearInterval(p.current),null)}async function O(){if(S===`idle`){f.current=``,h.current=0,T(0);try{let e=new i;if(await e.start(),u.current=e,m.current=Date.now(),E(),C(`recording`),_()){let e=v({onFinal:e=>{let t=y(e,f.current);f.current+=t,n?.(f.current)},onInterim:e=>n?.(f.current+e),onError:()=>{}});d.current=e,e?.start()}}catch{a?.(`Microphone permission denied`),C(`idle`)}}}function k(){S===`recording`&&(u.current?.pause(),d.current?.stop(),g.current=Date.now(),D(),C(`paused`))}function A(){S===`paused`&&(u.current?.resume(),h.current+=Date.now()-g.current,E(),_()&&!d.current&&(d.current=v({onFinal:e=>{let t=y(e,f.current);f.current+=t,n?.(f.current)},onInterim:e=>n?.(f.current+e)})),d.current?.start(),C(`recording`))}async function j(){if(S!==`recording`&&S!==`paused`)return;let n=f.current.trim();d.current?.stop(),d.current=null,D();let r=w;C(`transcribing`);try{let i=await u.current.stop();if(u.current=null,!i||i.size===0){n&&t(n,{durationSec:r,appended:!0}),C(`idle`);return}if(i.size>24*1024*1024){a?.(`Recording too large for AI transcription — using live transcript`),n&&t(n,{durationSec:r,appended:!0}),C(`idle`);return}let o=await c(i,e);o.success&&o.text?t(o.text,{provider:o.provider,durationSec:r,appended:!1}):o.noProvider?n?t(n,{durationSec:r,appended:!0}):a?.(`No transcription API configured`):n?t(n,{durationSec:r,appended:!0}):a?.(o.error||`Transcription failed`)}catch(e){a?.(e.message)}finally{C(`idle`)}}let M=String(Math.floor(w/60)).padStart(2,`0`),N=String(w%60).padStart(2,`0`);return(0,b.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,"data-testid":`recorder-`+e,children:[S===`idle`&&(0,b.jsx)(`button`,{type:`button`,onClick:O,disabled:l,className:x+` bg-destructive text-white border-destructive hover:bg-red-700`,"data-testid":`recorder-start`,children:`🎙 Start recording`}),(S===`recording`||S===`paused`)&&(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)(`button`,{type:`button`,onClick:S===`recording`?k:A,className:x+` bg-amber-500 text-white border-amber-500 hover:bg-amber-600`,"data-testid":`recorder-pause`,children:S===`recording`?`⏸ Pause`:`▶ Resume`}),(0,b.jsx)(`button`,{type:`button`,onClick:j,className:x+` bg-slate-800 text-white border-slate-800 hover:bg-slate-900`,"data-testid":`recorder-stop`,children:`⏹ Stop`}),(0,b.jsxs)(`span`,{className:`inline-flex items-center gap-1.5 text-sm text-destructive font-mono`,"data-testid":`recorder-timer`,children:[(0,b.jsx)(`span`,{className:S===`recording`?`animate-pulse`:`opacity-50`,children:``}),S===`paused`?`paused `:``,M,`:`,N]})]}),S===`transcribing`&&(0,b.jsx)(`span`,{className:`text-sm text-muted-foreground`,"data-testid":`recorder-transcribing`,children:`⌛ Transcribing…`})]})}export{S as t};