diff --git a/.gitea/workflows/backend-ecr.yaml b/.gitea/workflows/backend-ecr.yaml index 79f73dd..e6db949 100644 --- a/.gitea/workflows/backend-ecr.yaml +++ b/.gitea/workflows/backend-ecr.yaml @@ -37,6 +37,8 @@ env: jobs: build-and-push: runs-on: ubuntu-latest + container: + image: gitea-modal-runner:latest timeout-minutes: 30 steps: - name: Checkout repository diff --git a/.gitea/workflows/backend-modal.yaml b/.gitea/workflows/backend-modal.yaml index a278c49..64caa5b 100644 --- a/.gitea/workflows/backend-modal.yaml +++ b/.gitea/workflows/backend-modal.yaml @@ -6,9 +6,9 @@ # # Required Gitea Repository Secrets: # MODAL_TOKEN - Modal API token (from modal.com/settings) -# AWS_ACCESS_KEY_ID - For ECR push -# AWS_SECRET_ACCESS_KEY -# AWS_REGION - e.g., us-east-1 +# +# Required Modal Secrets (configured in Modal dashboard, NOT Gitea): +# aws-secrets - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION # # Required Gitea Repository Variables: # ECR_PUBLIC_REGISTRY_ALIAS - Your public.ecr.aws alias (e.g., vkist-project) @@ -61,35 +61,15 @@ env: jobs: build-via-modal: name: Build & Push via Modal - runs-on: ubuntu-latest + runs-on: [self-hosted, linux, amd64] + container: + image: gitea-modal-runner:latest timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install Modal CLI - run: pip install modal - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION || 'us-east-1' }} - - - name: Login to Amazon ECR Public - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - with: - registry-type: public - mask-password: 'true' - - name: Determine image tag id: tag run: | @@ -114,11 +94,6 @@ jobs: --platform linux/amd64 \ --push - - name: Print image URI - run: | - echo "Successfully pushed: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.tag }}" - echo "image=${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.tag }}" >> $GITHUB_OUTPUT - # Optional: Trigger dependent deployments # trigger-triton: # needs: build-via-modal diff --git a/.gitea/workflows/not_done_frontend.yaml b/.gitea/workflows/frontend.yaml similarity index 97% rename from .gitea/workflows/not_done_frontend.yaml rename to .gitea/workflows/frontend.yaml index 6f957f6..ebd77ff 100644 --- a/.gitea/workflows/not_done_frontend.yaml +++ b/.gitea/workflows/frontend.yaml @@ -17,7 +17,7 @@ permissions: env: FRONTEND_DIR: workspace/sprint_1_2/CODEBASE/frontend/implementation ECR_REGISTRY: public.ecr.aws - ECR_REPOSITORY: ${{ vars.ECR_PUBLIC_REGISTRY_ALIAS }}/lumina-frontend + ECR_REPOSITORY: ${{ vars.ECR_PUBLIC_REGISTRY_ALIAS }}/msk_lumina_frontend jobs: build-and-push: diff --git a/.gitignore b/.gitignore index b1adfe2..05cc2fb 100644 --- a/.gitignore +++ b/.gitignore @@ -239,4 +239,5 @@ workspace/sprint_1_2/CODEBASE/knowledge/implementation/model/ *.xz logs/ *.rdb -dist/ \ No newline at end of file +dist/ +distribute_asset/* \ No newline at end of file diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/.gitignore b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/.gitignore new file mode 100644 index 0000000..68b4f70 --- /dev/null +++ b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this .gitignore file itself, which forces Git to keep the folder +!.gitignore \ No newline at end of file diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/ClinicalWorkspacePage-BRH1XOmJ.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/ClinicalWorkspacePage-BRH1XOmJ.js deleted file mode 100644 index 5063fee..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/ClinicalWorkspacePage-BRH1XOmJ.js +++ /dev/null @@ -1,4714 +0,0 @@ -var wi=Object.defineProperty;var ki=(e,t,n)=>t in e?wi(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var be=(e,t,n)=>ki(e,typeof t!="symbol"?t+"":t,n);import{j as r,c as Br,G as qt,u as ji,M as Si}from"./index-BCa3bqPy.js";import{b as s,e as Xt,f as Ni,u as Mi,N as Ci}from"./vendor-react-BWW3uBCb.js";import{b as Ei,h as so,m as Ti,a as Ii,c as Li,d as Pi,A as Ai}from"./mlInferenceCacheStore-BwGclW_P.js";import{D as Jn,A as Ri,p as Di}from"./agent-runtime-CHsDDJds.js";const lo="lumina-workspace-zone-a-ratio",Ot=.6,hn=.48,gn=.78,Hr=10,Oi=1-Ot;function kr(e,t,n){return Math.min(Math.max(e,t),n)}function zi(){try{const e=localStorage.getItem(lo);if(!e)return Ot;const t=Number.parseFloat(e);return Number.isFinite(t)?kr(t,hn,gn):Ot}catch{return Ot}}function Fi(e){const t=1-e;return kr(t/Oi,.82,1.15)}function $i({zoneA:e,zoneB:t}){const n=s.useRef(null),[a,o]=s.useState(zi),[i,l]=s.useState(!1),[c,d]=s.useState(!1),[u,h]=s.useState(!0),p=Fi(a),f=c||i;s.useEffect(()=>{localStorage.setItem(lo,String(a))},[a]),s.useEffect(()=>{const L=window.matchMedia("(min-width: 1025px)"),z=()=>h(L.matches);return z(),L.addEventListener("change",z),()=>L.removeEventListener("change",z)},[]);const g=s.useCallback(L=>{const z=n.current;if(!z)return;const X=z.getBoundingClientRect(),R=16,U=X.width-R*2-Hr;if(U<=0)return;const v=L-X.left-R;o(kr(v/U,hn,gn))},[]),N=L=>{u&&(L.preventDefault(),L.stopPropagation(),L.currentTarget.setPointerCapture(L.pointerId),l(!0),g(L.clientX))},y=L=>{i&&g(L.clientX)},k=L=>{i&&(L.currentTarget.releasePointerCapture(L.pointerId),l(!1))},A=L=>{L.stopPropagation(),o(Ot)},I=()=>{i||d(!1)},w={"--workspace-zone-a-pct":`${(a*100).toFixed(2)}%`,"--workspace-panel-scale":String(p)};return r.jsxs("div",{ref:n,className:`workspace-shell ${i?"workspace-shell--dragging":""}`,style:w,children:[r.jsx("div",{className:"workspace-shell__zone-a",children:e}),u&&r.jsx("div",{className:"workspace-shell__divider",role:"separator","aria-orientation":"vertical","aria-valuenow":Math.round(a*100),"aria-valuemin":Math.round(hn*100),"aria-valuemax":Math.round(gn*100),"aria-label":"Vùng chia khung siêu âm và bảng phụ",onMouseEnter:()=>d(!0),onMouseLeave:I,children:f&&r.jsx("div",{className:"workspace-shell__divider-blob glass-elevated",role:"slider","aria-label":"Kéo để điều chỉnh độ rộng khung siêu âm và bảng phụ","aria-valuenow":Math.round(a*100),"aria-valuemin":Math.round(hn*100),"aria-valuemax":Math.round(gn*100),onPointerDown:N,onPointerMove:y,onPointerUp:k,onPointerCancel:k,onDoubleClick:A,children:r.jsxs("span",{className:"workspace-shell__divider-blob-grip","aria-hidden":!0,children:[r.jsx("span",{}),r.jsx("span",{}),r.jsx("span",{})]})})}),r.jsx("div",{className:"workspace-shell__zone-b",children:t}),r.jsx("style",{children:` - .workspace-shell { - display: flex; - flex: 0 0 calc(100dvh - var(--topbar-h) - var(--bottombar-h)); - height: calc(100dvh - var(--topbar-h) - var(--bottombar-h)); - min-height: 0; - padding: var(--space-md); - overflow: hidden; - user-select: ${i?"none":"auto"}; - } - .workspace-shell--dragging { - cursor: col-resize; - } - .workspace-shell__zone-a { - flex: 0 0 var(--workspace-zone-a-pct); - min-width: 0; - min-height: 0; - display: flex; - flex-direction: column; - overflow: hidden; - transition: flex-basis 0.05s linear; - } - .workspace-shell--dragging .workspace-shell__zone-a { - transition: none; - } - .workspace-shell__zone-b { - flex: 1 1 0; - min-width: 0; - min-height: 0; - display: flex; - flex-direction: column; - overflow: hidden; - font-size: calc(1rem * var(--workspace-panel-scale, 1)); - } - .workspace-shell__divider { - position: relative; - flex: 0 0 ${Hr}px; - margin: 0 2px; - cursor: default; - display: flex; - align-items: center; - justify-content: center; - z-index: 2; - } - .workspace-shell__divider::before { - content: ''; - position: absolute; - top: 8%; - bottom: 8%; - left: 50%; - width: 2px; - transform: translateX(-50%); - border-radius: 2px; - background: var(--color-outline-variant); - transition: background 0.15s, width 0.15s; - } - .workspace-shell__divider:hover::before, - .workspace-shell--dragging .workspace-shell__divider::before { - background: var(--color-secondary); - width: 3px; - } - .workspace-shell__divider-blob { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - display: flex; - align-items: center; - justify-content: center; - padding: 12px 8px; - border-radius: var(--radius-full); - cursor: col-resize; - touch-action: none; - z-index: 3; - animation: workspace-divider-blob-in 0.15s ease-out; - } - @keyframes workspace-divider-blob-in { - from { - opacity: 0; - transform: translate(-50%, -50%) scale(0.92); - } - to { - opacity: 1; - transform: translate(-50%, -50%) scale(1); - } - } - .workspace-shell__divider-blob-grip { - display: flex; - flex-direction: column; - gap: 3px; - align-items: center; - } - .workspace-shell__divider-blob-grip span { - display: block; - width: 16px; - height: 2px; - border-radius: 2px; - background: var(--color-secondary); - opacity: 0.85; - } - @media (max-width: 1024px) { - .workspace-shell { - flex: 1 1 auto; - height: auto; - flex-direction: column; - gap: var(--space-md); - overflow: visible; - } - .workspace-shell__zone-a, - .workspace-shell__zone-b { - flex: 1 1 auto; - font-size: 1rem; - overflow: visible; - } - } - `})]})}const Bi="Cảnh báo: ảnh có thể ngoài phạm vi mô hình đã học",Hi="Mô hình chưa được huấn luyện với loại ảnh tương tự, nên kết quả AI có thể không đáng tin. Hãy kiểm tra chất lượng ảnh và đối chiếu lâm sàng trước khi dựa vào nhãn tự động.";function co(e){return e>=.95?.05:e>=.9?.08:e>=.85?.12:e>=.75?.18:e>=.65?.25:.35}function uo(e,t,n="confident"){const a=co(t);return n==="ood_warning"?Hi:n==="ambiguous"?`Dự đoán chính: ${e}. Nhiều lớp có xác suất gần nhau — cần đối chiếu lâm sàng trước khi khóa kết quả.`:`Dự đoán: ${e}. Trong các ca có phân bố thống kê tương tự, tỷ lệ phân loại sai ước tính ~${(a*100).toFixed(0)}%.`}const zt=["med-lat","post-trans","sup-trans-flex","sup-up-long"],ho={"med-lat":{class:"med-lat",labelMedicalEn:"Medial Lat",labelVi:"Góc quét mặt trong mặt ngoài",legendPlaneVi:"Mặt cắt trong — ngoài",segmentLegendType:"sup",supportsThicknessMeasurement:!1},"post-trans":{class:"post-trans",labelMedicalEn:"Posterior Transverse",labelVi:"Góc quét mặt sau ngang",legendPlaneVi:"Mặt cắt ngang vùng khoeo / mặt sau",segmentLegendType:"post",supportsThicknessMeasurement:!1},"sup-trans-flex":{class:"sup-trans-flex",labelMedicalEn:"Suprapatellar Transverse Flexed",labelVi:"Góc quét cắt ngang trên xương bánh chè ở tư thế gấp gối",legendPlaneVi:"Mặt cắt ngang trên bánh chè — tư thế gấp gối",segmentLegendType:"sup",supportsThicknessMeasurement:!1},"sup-up-long":{class:"sup-up-long",labelMedicalEn:"Suprapatellar Upper Longitudinal",labelVi:"Góc quét cắt dọc phía trên xương bánh chè",legendPlaneVi:"Mặt cắt dọc phía trên bánh chè",segmentLegendType:"sup",supportsThicknessMeasurement:!0}};function Cn(e){return zt.includes(e)}function He(e){return ho[e]}function Qe(e){return`${e.labelMedicalEn} : ${e.labelVi}`}function jr(e){return He(e).segmentLegendType}function go(e){return He(e).supportsThicknessMeasurement}function Gi(e){if(e!=null&&e.class&&Cn(e.class)){const t=e.confidence!=null?e.confidence>1?e.confidence/100:e.confidence:0;return{class:e.class,confidence:t,calibration:e.calibration}}return null}const Wi="/assets/med-lat_1-Dto_0SyQ.png",Ui="/assets/med_lat_2-DbnwLN1s.png",Vi="/assets/trans_flex-8mO3g_5i.png",Gr=[{id:"med-lat-1",src:Wi,label:"Thành trong — góc 1"},{id:"med-lat-2",src:Ui,label:"Thành trong — góc 2"},{id:"trans-flex",src:Vi,label:"Ngang — gấp"}],Wr={"p-001":[{id:"p-001-sup-long-pos-0",src:"/assets/patient-profiles/p-001/sup-long-pos-0.png",label:"Sup dọc — viêm (+) · #1",stratum:"sup-up-long_positive",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_positive/58e7a7ef-de3e-11ee-97e2-0a580a5f5b60_11.png"},{id:"p-001-sup-long-neg-0",src:"/assets/patient-profiles/p-001/sup-long-neg-0.png",label:"Sup dọc — không viêm (−) · #1",stratum:"sup-up-long_negative",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_negative/58e7a90f-de3e-11ee-97e2-0a580a5f5b60_11.png"},{id:"p-001-post-trans-pos-0",src:"/assets/patient-profiles/p-001/post-trans-pos-0.png",label:"Sau ngang — viêm (+) · #1",stratum:"post_trans_positive",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_positive/72bb41ed-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-001-post-trans-neg-0",src:"/assets/patient-profiles/p-001/post-trans-neg-0.png",label:"Sau ngang — không viêm (−) · #1",stratum:"post_trans_negative",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_negative/72bb1476-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-001-other-0",src:"/assets/patient-profiles/p-001/other-0.png",label:"Góc khác (med-lat / sup-trans-flex) · #1",stratum:"other_angle",expectedAngleClass:"sup-trans-flex",sourcePath:"backend/tests/test_images/other_angle/trans_flex.png"}],"p-002":[{id:"p-002-sup-long-pos-0",src:"/assets/patient-profiles/p-002/sup-long-pos-0.png",label:"Sup dọc — viêm (+) · #1",stratum:"sup-up-long_positive",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_positive/72bb1ac0-f020-11ed-b527-0a580a5f736a_11.png"},{id:"p-002-sup-long-neg-0",src:"/assets/patient-profiles/p-002/sup-long-neg-0.png",label:"Sup dọc — không viêm (−) · #1",stratum:"sup-up-long_negative",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_negative/72bb0a8a-f020-11ed-b527-0a580a5f736a_11.png"},{id:"p-002-post-trans-pos-0",src:"/assets/patient-profiles/p-002/post-trans-pos-0.png",label:"Sau ngang — viêm (+) · #1",stratum:"post_trans_positive",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_positive/72bb4c47-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-002-post-trans-neg-0",src:"/assets/patient-profiles/p-002/post-trans-neg-0.png",label:"Sau ngang — không viêm (−) · #1",stratum:"post_trans_negative",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_negative/72bb322d-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-002-other-0",src:"/assets/patient-profiles/p-002/other-0.png",label:"Góc khác (med-lat / sup-trans-flex) · #1",stratum:"other_angle",expectedAngleClass:"sup-trans-flex",sourcePath:"backend/tests/test_images/other_angle/trans_flex.png"}],"p-003":[{id:"p-003-sup-long-pos-0",src:"/assets/patient-profiles/p-003/sup-long-pos-0.png",label:"Sup dọc — viêm (+) · #1",stratum:"sup-up-long_positive",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_positive/72bb1aaf-f020-11ed-b527-0a580a5f736a_11.png"},{id:"p-003-sup-long-neg-0",src:"/assets/patient-profiles/p-003/sup-long-neg-0.png",label:"Sup dọc — không viêm (−) · #1",stratum:"sup-up-long_negative",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_negative/53a31572-4380-11ee-9e9a-0a580a5f5f0e_11.png"},{id:"p-003-post-trans-pos-0",src:"/assets/patient-profiles/p-003/post-trans-pos-0.png",label:"Sau ngang — viêm (+) · #1",stratum:"post_trans_positive",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_positive/72bb41ed-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-003-post-trans-neg-0",src:"/assets/patient-profiles/p-003/post-trans-neg-0.png",label:"Sau ngang — không viêm (−) · #1",stratum:"post_trans_negative",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_negative/72bb1476-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-003-other-0",src:"/assets/patient-profiles/p-003/other-0.png",label:"Góc khác (med-lat / sup-trans-flex) · #1",stratum:"other_angle",expectedAngleClass:"med-lat",sourcePath:"backend/tests/test_images/other_angle/med-lat_1.png"}],"p-004":[{id:"p-004-sup-long-pos-0",src:"/assets/patient-profiles/p-004/sup-long-pos-0.png",label:"Sup dọc — viêm (+) · #1",stratum:"sup-up-long_positive",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_positive/72bb0b3a-f020-11ed-b527-0a580a5f736a_21.png"},{id:"p-004-sup-long-neg-0",src:"/assets/patient-profiles/p-004/sup-long-neg-0.png",label:"Sup dọc — không viêm (−) · #1",stratum:"sup-up-long_negative",expectedAngleClass:"sup-up-long",sourcePath:"backend/tests/test_images/sup-up-long_negative/53a31572-4380-11ee-9e9a-0a580a5f5f0e_11.png"},{id:"p-004-post-trans-pos-0",src:"/assets/patient-profiles/p-004/post-trans-pos-0.png",label:"Sau ngang — viêm (+) · #1",stratum:"post_trans_positive",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_positive/72bb4c47-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-004-post-trans-neg-0",src:"/assets/patient-profiles/p-004/post-trans-neg-0.png",label:"Sau ngang — không viêm (−) · #1",stratum:"post_trans_negative",expectedAngleClass:"post-trans",sourcePath:"backend/tests/test_images/post_trans_negative/72bb1476-f020-11ed-b527-0a580a5f736a_17.png"},{id:"p-004-other-0",src:"/assets/patient-profiles/p-004/other-0.png",label:"Góc khác (med-lat / sup-trans-flex) · #1",stratum:"other_angle",expectedAngleClass:"sup-trans-flex",sourcePath:"backend/tests/test_images/other_angle/trans_flex.png"}]};function po(e){return Wr[e]??Wr["p-001"]??[]}function zn(e,t,n,a){const o="naturalWidth"in t?t.naturalWidth:n,i="naturalHeight"in t?t.naturalHeight:a;if(!o||!i)return;const l=Math.min(n/o,a/i),c=o*l,d=i*l,u=(n-c)/2,h=(a-d)/2;e.drawImage(t,u,h,c,d)}function Fn(e){return new Promise((t,n)=>{const a=new Image;a.crossOrigin="anonymous",a.onload=()=>t(a),a.onerror=()=>n(new Error(`Failed to load ${e}`)),a.src=e})}async function Ki(e,t){const n=e.querySelector(".diagnostic-canvas__stage");if(!n)return null;const{width:a,height:o}=e.getBoundingClientRect(),i=Math.floor(a),l=Math.floor(o);if(i<1||l<1)return null;const c=document.createElement("canvas");c.width=i,c.height=l;const d=c.getContext("2d");if(!d)return null;d.fillStyle="#0a0f0e",d.fillRect(0,0,i,l);const{zoom:u,pan:h}=t;d.save(),d.translate(i/2+h.x,l/2+h.y),d.scale(u,u),d.translate(-i/2,-l/2);const p=n.querySelector(".diagnostic-canvas__frame");if(p!=null&&p.src)try{zn(d,await Fn(p.src),i,l)}catch{}const f=n.querySelector(".diagnostic-canvas__overlay--mask");if(f!=null&&f.src)try{zn(d,await Fn(f.src),i,l)}catch{}const g=n.querySelector(".diagnostic-canvas__overlay--gradcam");if(g!=null&&g.src)try{zn(d,await Fn(g.src),i,l)}catch{}const N=n.querySelector(".diagnostic-canvas__annotation-canvas");return N&&d.drawImage(N,0,0,i,l),d.restore(),c.toDataURL("image/png")}const qi=2.5,It=14,fo=[{id:"red",labelEn:"Red",labelVi:"Đỏ",hex:"#FF0000"},{id:"yellow",labelEn:"Yellow",labelVi:"Vàng",hex:"#FFFF00",contrastOnLight:!0,contrastOnDark:!0},{id:"cyan",labelEn:"Cyan",labelVi:"Xanh ngọc",hex:"#00FFFF",contrastOnLight:!0,contrastOnDark:!0},{id:"green",labelEn:"Green",labelVi:"Xanh lá",hex:"#00FF00"},{id:"magenta",labelEn:"Magenta",labelVi:"Hồng tím",hex:"#FF00FF"},{id:"blue",labelEn:"Blue",labelVi:"Xanh dương",hex:"#0000FF"},{id:"black",labelEn:"Black",labelVi:"Đen",hex:"#000000",contrastOnDark:!0}],Xi="#00FFFF",mo="msk-annotation-ink-color";function Yi(e){return fo.some(t=>t.hex.toLowerCase()===e.toLowerCase())}function Ji(){try{const e=localStorage.getItem(mo);if(e&&Yi(e))return e}catch{}return Xi}const rt=1,bo=4,Ur=.25;function Zi(e){return Math.min(bo,Math.max(rt,Math.round(e*1e3)/1e3))}function Qt(e,t,n,a,o){const i=Zi(o);if(i<=rt)return{zoom:rt,pan:{x:0,y:0}};const l=n.x-a.w/2,c=n.y-a.h/2;return{zoom:i,pan:{x:l-(l-t.x)/e*i,y:c-(c-t.y)/e*i}}}function Vr(e,t,n){return{x:e.x+t,y:e.y+n}}function Qi(e,t,n,a){const{zoom:o,pan:i}=a,l=n.width,c=n.height,d=e-n.left,u=t-n.top;return{x:(d-l/2-i.x)/o+l/2,y:(u-c/2-i.y)/o+c/2}}function es(e,t,n){const{zoom:a,pan:o}=n;return{x:(e.x-t.w/2)*a+t.w/2+o.x,y:(e.y-t.h/2)*a+t.h/2+o.y}}function Kr(e,t){const n=t.clientX-e.clientX,a=t.clientY-e.clientY;return Math.hypot(n,a)}function qr(e,t){return{x:(e.clientX+t.clientX)/2,y:(e.clientY+t.clientY)/2}}const _o=24,ts=12,ns=80,rs=.3;function as(e){let t=0;for(let n=1;nc+c).join(""):n,o=Number.parseInt(a.slice(0,2),16),i=Number.parseInt(a.slice(2,4),16),l=Number.parseInt(a.slice(4,6),16);return`rgba(${o}, ${i}, ${l}, ${t})`}function ls(e,t){if(t.length<3)return!1;let n=!1;for(let a=0,o=t.length-1;ae.y!=d>e.y&&e.x<(c-i)*(e.y-l)/(d-l+Number.EPSILON)+i&&(n=!n)}return n}function Zn(e,t,n){const a=n.x-t.x,o=n.y-t.y,i=a*a+o*o;if(i===0)return mn(e,t);const l=Math.max(0,Math.min(1,((e.x-t.x)*a+(e.y-t.y)*o)/i)),c={x:t.x+l*a,y:t.y+l*o};return mn(e,c)}function Xr(e,t,n){const{points:a}=t;if(a.length<3)return!1;if(ls(e,a))return!0;const o=n*n;for(const i of a){const l=i.x-e.x,c=i.y-e.y;if(l*l+c*c<=o)return!0}for(let i=0;i2){const o=t[0],i=t[t.length-1];if(mn(o,i)<=_o&&Zn(e,i,o)<=n)return!0}return!1}let et=null;function Yr(e){et=e}function xo(e){return et?et.exportForFrame(e):{frameId:e,strokes:[],regionMasks:[]}}function cs(e){et==null||et.clearForFrame(e)}function Jr(){et==null||et.clearAll()}let Ge=null,pn=null;function Zr(e){Ge=e}function Qr(e){pn=e}function ds(e,t){pn&&(pn.width=e,pn.height=t)}function pt(){return(Ge==null?void 0:Ge.isActive())??!1}function ea(e,t){Ge==null||Ge.onOverlayCreate(e,t)}function ta(e,t,n,a){Ge!=null&&Ge.isActive()&&Ge.onOverlayMutate(e,t,n,a)}function Lt(e,t){Ge==null||Ge.onOverlayDelete(e,t)}const Qn=1e3/30,us=2e3;function vo(e){return[...e].sort((t,n)=>t.relTime!==n.relTime?t.relTime-n.relTime:t.timestamp-n.timestamp)}function hs(e){return e.strokes.reduce((t,n)=>t+n.points.length,0)+e.regionMasks.reduce((t,n)=>t+n.points.length,0)}function na(e,t){return vo([...e,...t])}function yo(e){return e.length===0?0:e[e.length-1].relTime}function gs(e){return Math.max(.1,yo(e)/1e3)}function ps(e,t,n){return t<=0||n<=0?{x:0,y:0}:{x:Math.max(0,Math.min(1,e.x/t)),y:Math.max(0,Math.min(1,e.y/n))}}function fs(e,t,n){return{x:e.x*t,y:e.y*n}}function wo(e,t,n){return e.map(a=>ps(a,t,n))}function ko(e,t,n){return{overlayId:e.id,toolType:"FREEHAND_PEN",points:wo(e.points,t,n),color:e.color,strokeWidth:e.width}}function jo(e,t,n){return{overlayId:e.id,toolType:"FREEHAND_PEN",points:wo(e.points,t,n),color:e.color}}function ms(e,t,n,a){const o=[],i=[];for(const l of t.values()){const c=l.points.map(d=>fs(d,n,a));if(!(c.length<2)){if(l.toolType==="FREEHAND_PEN"&&c.length>=3){const d=c[0],u=c[c.length-1];if(Math.hypot(d.x-u.x,d.y-u.y)<12&&c.length>=4){i.push({id:l.overlayId,points:c,color:l.color??"#00FFFF"});continue}}o.push({id:l.overlayId,tool:"draw",points:c,color:l.color??"#00FFFF",width:l.strokeWidth??2.5})}}return{frameId:e,strokes:o,regionMasks:i}}function er(e,t){if(!(t.points.length<2)){e.strokeStyle=t.color,e.lineWidth=t.width,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(t.points[0].x,t.points[0].y);for(let n=1;n{d.current=null,v(!1),c.current=null,ie(null),N(b)},[]);h.current=g,V.current=B;const F=s.useCallback(b=>{V.current=b,re(b);try{localStorage.setItem(mo,b)}catch{}},[]),_=s.useCallback((b,W=!0)=>{u.current=b;const Q=a.current;Q&&(Q.style.transform=ra(b.pan,b.zoom)),W&&(k(b.zoom),I(b.pan))},[]),O=s.useCallback(b=>{u.current=b,!p.current&&(p.current=requestAnimationFrame(()=>{p.current=0;const W=a.current;W&&(W.style.transform=ra(b.pan,b.zoom)),k(b.zoom),I(b.pan)}))},[]),S=s.useCallback(()=>{const b=n.current;if(!b)return null;const W=b.getBoundingClientRect();return{rect:W,size:{w:W.width,h:W.height}}},[]),P=s.useRef(w),j=s.useRef(z);P.current=w,j.current=z;const ee=s.useRef(e),J=s.useCallback((b,W,Q=c.current)=>{const Y=t.current;if(!Y)return;const fe=Y.getContext("2d");if(!fe)return;const ce=window.devicePixelRatio||1;fe.setTransform(ce,0,0,ce,0,0),_s(fe,Y.width/ce,Y.height/ce,b,W,Q),ds(Y.width/ce,Y.height/ce),l.current&&er(fe,l.current)},[]);s.useEffect(()=>{const b=ee.current;b!==e&&(o.current.set(b,P.current),i.current.set(b,j.current));const W=o.current.get(e)??[],Q=i.current.get(e)??[];L(W),X(Q),c.current=null,ie(null),l.current=null,_({zoom:rt,pan:{x:0,y:0}}),J(W,Q,null),ee.current=e},[_,e,J]);const K=s.useCallback(()=>{c.current=null,ie(null),J(w,z,null)},[J,z,w]),Ee=s.useCallback(()=>{const b=c.current;if(!b){ie(null);return}const W={id:b.id,points:b.points,color:b.color};if(X(Q=>{const Y=[...Q,W];return i.current.set(e,Y),Y}),pt()){const Q=t.current?{width:t.current.width/(window.devicePixelRatio||1),height:t.current.height/(window.devicePixelRatio||1)}:{width:1024,height:768};ea(e,jo(W,Q.width,Q.height)),Lt(e,b.id)}c.current=null,ie(null)},[e]),pe=s.useCallback(b=>{L(Q=>{const Y=Q.filter(ce=>$n(b,ce.points,It)),fe=Q.filter(ce=>!$n(b,ce.points,It));if(o.current.set(e,fe),pt())for(const ce of Y)Lt(e,ce.id);return fe}),X(Q=>{const Y=Q.filter(ce=>Xr(b,ce,It)),fe=Q.filter(ce=>!Xr(b,ce,It));if(i.current.set(e,fe),pt())for(const ce of Y)Lt(e,ce.id);return fe});const W=c.current;W&&$n(b,W.points,It)&&K()},[K,e]),he=s.useCallback(()=>{const b=t.current,W=n.current;if(!b||!W)return;const Q=window.devicePixelRatio||1,{clientWidth:Y,clientHeight:fe}=W;b.width=Math.max(1,Math.floor(Y*Q)),b.height=Math.max(1,Math.floor(fe*Q)),b.style.width=`${Y}px`,b.style.height=`${fe}px`;const ce=b.getContext("2d");ce&&ce.setTransform(Q,0,0,Q,0,0),J(w,z)},[J,w,z]);s.useEffect(()=>{he();const b=n.current;if(!b)return;const W=new ResizeObserver(()=>he());return W.observe(b),()=>W.disconnect()},[he]),s.useEffect(()=>{J(w,z)},[w,z,J]),s.useEffect(()=>{const b=n.current;if(!b)return;const W=M=>{const Z=S();if(!Z)return;const te={x:M.clientX-Z.rect.left,y:M.clientY-Z.rect.top},me=u.current;if(M.ctrlKey){M.preventDefault();const Me=Math.exp(-M.deltaY*xs),Oe=Qt(me.zoom,me.pan,te,Z.size,me.zoom*Me);O(Oe);return}h.current==="pan"&&(M.preventDefault(),O({zoom:me.zoom,pan:Vr(me.pan,-M.deltaX,-M.deltaY)}))},Q=()=>{l.current&&(l.current=null,J(o.current.get(e)??[],i.current.get(e)??[]))},Y=M=>{if(M.touches.length===2){M.preventDefault(),Q();const Z=M.touches[0],te=M.touches[1],me=qr(Z,te),Me=S();if(!Me)return;f.current={lastDistance:Kr(Z,te),lastCenter:{x:me.x-Me.rect.left,y:me.y-Me.rect.top}}}},fe=M=>{if(M.touches.length!==2||!f.current)return;M.preventDefault();const Z=S();if(!Z)return;const te=M.touches[0],me=M.touches[1],Me=Kr(te,me),Oe=qr(te,me),Te={x:Oe.x-Z.rect.left,y:Oe.y-Z.rect.top},Ce=f.current,ze=u.current,$e=Me/Math.max(Ce.lastDistance,1),Ie=Qt(ze.zoom,ze.pan,Te,Z.size,ze.zoom*$e),Ae=h.current==="pan"?Vr(Ie.pan,Te.x-Ce.lastCenter.x,Te.y-Ce.lastCenter.y):Ie.pan;O({zoom:Ie.zoom,pan:Ae}),f.current={lastDistance:Me,lastCenter:Te}},ce=M=>{M.touches.length<2&&(f.current=null)};return b.addEventListener("wheel",W,{passive:!1}),b.addEventListener("touchstart",Y,{passive:!1}),b.addEventListener("touchmove",fe,{passive:!1}),b.addEventListener("touchend",ce),b.addEventListener("touchcancel",ce),()=>{b.removeEventListener("wheel",W),b.removeEventListener("touchstart",Y),b.removeEventListener("touchmove",fe),b.removeEventListener("touchend",ce),b.removeEventListener("touchcancel",ce)}},[e,S,J,O]);const Ne=s.useCallback((b,W)=>{const Q=n.current;if(!Q)return null;const Y=Q.getBoundingClientRect();return Qi(b,W,Y,u.current)},[]),ge=s.useCallback(()=>{const b=S();if(!b)return;const W=u.current,Q=Math.min(bo,Math.round((W.zoom+Ur)*100)/100),Y={x:b.size.w/2,y:b.size.h/2};_(Qt(W.zoom,W.pan,Y,b.size,Q))},[_,S]),ae=s.useCallback(()=>{const b=S();if(!b)return;const W=u.current,Q=Math.max(rt,Math.round((W.zoom-Ur)*100)/100),Y={x:b.size.w/2,y:b.size.h/2};_(Qt(W.zoom,W.pan,Y,b.size,Q))},[_,S]),E=s.useCallback(()=>{_({zoom:rt,pan:{x:0,y:0}})},[_]),$=s.useCallback(()=>{if(pt()){for(const b of w)Lt(e,b.id);for(const b of z)Lt(e,b.id)}l.current=null,c.current=null,ie(null),L([]),X([]),o.current.set(e,[]),i.current.set(e,[]),J([],[])},[e,J]),se=s.useCallback(b=>{var Q,Y;if(b.button!==0)return;if(g==="pan"){d.current={x:b.clientX,y:b.clientY,panX:u.current.pan.x,panY:u.current.pan.y},v(!0),b.currentTarget.setPointerCapture(b.pointerId);return}const W=Ne(b.clientX,b.clientY);if(W){if(g==="draw"){c.current&&K();const fe={id:crypto.randomUUID(),tool:"draw",points:[W],color:V.current,width:qi};l.current=fe,v(!0),(Q=t.current)==null||Q.setPointerCapture(b.pointerId),J(w,z);return}g==="eraser"&&(v(!0),(Y=t.current)==null||Y.setPointerCapture(b.pointerId),pe(W))}},[pe,Ne,K,J,z,w,g]),ke=s.useCallback(b=>{if(g==="pan"&&d.current){const Q=b.clientX-d.current.x,Y=b.clientY-d.current.y;O({zoom:u.current.zoom,pan:{x:d.current.panX+Q,y:d.current.panY+Y}});return}const W=Ne(b.clientX,b.clientY);if(W){if(g==="draw"&&l.current){l.current.points.push(W),pt()&&l.current.points.length>1&&ta(e,l.current.id,l.current.points,{color:l.current.color,strokeWidth:l.current.width}),J(w,z);return}g==="eraser"&&U&&pe(W)}},[pe,Ne,U,J,z,O,w,g]),Fe=s.useCallback(b=>{var W,Q;if(g==="pan"){d.current=null,v(!1),b.currentTarget.releasePointerCapture(b.pointerId);return}if(g==="draw"&&l.current){const Y=l.current;if(l.current=null,v(!1),(W=t.current)==null||W.releasePointerCapture(b.pointerId),Y.points.length>1&&os(Y)){c.current=Y;const fe=is(Y.points);ie({stroke:Y,anchor:fe}),J(w,z,Y);return}if(L(fe=>{const ce=[...fe,Y];return o.current.set(e,ce),ce}),pt()){const fe=t.current?{width:t.current.width/(window.devicePixelRatio||1),height:t.current.height/(window.devicePixelRatio||1)}:{width:1024,height:768};ta(e,Y.id,Y.points,{force:!0,color:Y.color,strokeWidth:Y.width}),ea(e,ko(Y,fe.width,fe.height))}return}g==="eraser"&&(v(!1),(Q=t.current)==null||Q.releasePointerCapture(b.pointerId))},[e,J,z,w,g]),Pe=g==="pan"?U?"grabbing":"grab":g==="draw"?"crosshair":"cell",q=s.useCallback(async()=>{const b=n.current;return b?Ki(b,u.current):null},[]),oe=s.useCallback(b=>({frameId:b,strokes:[...o.current.get(b)??[]],regionMasks:[...i.current.get(b)??[]]}),[]),we=s.useCallback(b=>{o.current.delete(b),i.current.delete(b),b===e&&(L([]),X([]),J([],[],null))},[e,J]),de=s.useCallback(()=>{l.current=null,c.current=null,ie(null),o.current.clear(),i.current.clear(),L([]),X([]),J([],[],null)},[J]),Se=s.useCallback(()=>{const b={};for(const[W,Q]of o.current.entries())b[W]={frameId:W,strokes:Q,regionMasks:i.current.get(W)??[]};return b},[]),je=s.useCallback(b=>{o.current.set(b.frameId,[...b.strokes]),i.current.set(b.frameId,[...b.regionMasks]),b.frameId===e&&(L(b.strokes),X(b.regionMasks),J(b.strokes,b.regionMasks,null))},[e,J]),G=s.useCallback(b=>{o.current.clear(),i.current.clear();for(const Y of Object.values(b))o.current.set(Y.frameId,Y.strokes),i.current.set(Y.frameId,Y.regionMasks);const W=o.current.get(e)??[],Q=i.current.get(e)??[];L(W),X(Q),J(W,Q,null)},[e,J]);s.useEffect(()=>(Yr({exportForFrame:oe,clearForFrame:we,clearAll:de}),()=>{Yr(null)}),[de,we,Se,oe]);const _e=s.useCallback(()=>{if(!R)return null;const b=n.current;if(!b)return R.anchor;const W={w:b.clientWidth,h:b.clientHeight};return es(R.anchor,W,u.current)},[R]);return{canvasRef:t,viewportRef:n,stageRef:a,tool:g,setTool:ne,inkColor:B,setInkColor:F,zoom:y,pan:A,zoomIn:ge,zoomOut:ae,resetView:E,clearAnnotations:$,closedLoopPrompt:R,closedLoopPromptViewportAnchor:_e(),confirmClosedLoopPrompt:Ee,dismissClosedLoopPrompt:K,onPointerDown:se,onPointerMove:ke,onPointerUp:Fe,viewportCursor:Pe,hasAnnotations:w.length>0||z.length>0,captureSnapshot:q,exportAllOverlays:Se,importOverlayForFrame:je,importAllOverlays:G,getViewportState:()=>({zoom:u.current.zoom,panX:u.current.pan.x,panY:u.current.pan.y})}}const ys=2.5,ws={file:"#22c55e",delete:"#ef4444",edit:"#38bdf8",view:"#a78bfa",ai:"#f59e0b",form:"#14b8a6"};function So(e){return ws[e]}function bn(e){const t=Math.floor(e/60),n=Math.floor(e%60);return`${String(t).padStart(2,"0")}:${String(n).padStart(2,"0")}`}function Sr(e,t){var n;return`${e}:${((n=t[0])==null?void 0:n.id)??"empty"}`}function ks(e){return{patientId:e,events:[],totalDurationSeconds:0,pointerTrace:[],segments:[],overlaysByFrameId:{},sessionKey:Sr(e,[])}}function js(e,t=.12){var o,i;const n=e.filter(l=>l.cursor);if(n.length===0)return[];const a=[];for(let l=0;l0?(k-u)/g:0,I=((o=c.frame)==null?void 0:o.frameId)??((i=d==null?void 0:d.frame)==null?void 0:i.frameId)??"";a.push({elapsedSeconds:k,x:p.x+(f.x-p.x)*A,y:p.y+(f.y-p.y)*A,frameId:I})}}return a}function Ss(e,t){if(e.length===0)return null;let n=e[0],a=e[e.length-1];for(const i of e)if(i.elapsedSeconds<=t&&(n=i),i.elapsedSeconds>=t){a=i;break}if(n.elapsedSeconds===a.elapsedSeconds)return{x:n.x,y:n.y};const o=(t-n.elapsedSeconds)/(a.elapsedSeconds-n.elapsedSeconds);return{x:n.x+(a.x-n.x)*o,y:n.y+(a.y-n.y)*o}}function Ns(e,t,n=ys,a){const o=Math.max(0,t-n);return e.filter(i=>!(i.elapsedSecondst||a&&i.frameId&&i.frameId!==a))}function aa(e,t){let n=e[0];for(const a of e)if(a.elapsedSeconds<=t)n=a;else break;return n}function oa(e){return`${e.title} — ${bn(e.elapsedSeconds)}`}function _n(e=new Date){return e.toLocaleTimeString("vi-VN",{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function Nr(e){return e.length===0?0:e[e.length-1].elapsedSeconds}function Ms(e,t){const n=Nr(e),a=t&&t.length>0?t[t.length-1].elapsedSeconds:0;return Math.max(n,a,1)}function Cs(e){return{id:crypto.randomUUID(),elapsedSeconds:0,recordedAt:_n(),title:"Bắt đầu phiên chẩn đoán",detail:`Mở workspace cho bệnh nhân ${e}.`,kind:"view",cursor:{x:12,y:18}}}function Es(e){return{id:crypto.randomUUID(),elapsedSeconds:Math.round(e*10)/10,recordedAt:_n(),title:"Kết thúc ghi phiên",detail:"Phiên ghi đã hoàn tất — xem lại danh sách hành động bên dưới.",kind:"view",cursor:{x:50,y:50}}}function ia(e,t,n){return{segmentId:crypto.randomUUID(),frame:e,startElapsed:t,endElapsed:null,viewport:n}}function Ts(e,t,n){return{...e,endElapsed:t,overlayCheckpoint:n}}function Is(e){const t={};for(const n of e)n.overlayCheckpoint&&(t[n.overlayCheckpoint.frameId]=n.overlayCheckpoint);return t}function Ls(e,t,n={},a){if(a!=null&&t.length>0){const o=No(t,a);if((o==null?void 0:o.frame.frameId)===e&&o.overlayCheckpoint)return o.overlayCheckpoint}for(let o=t.length-1;o>=0;o-=1){const i=t[o];if(i.frame.frameId===e&&i.overlayCheckpoint)return i.overlayCheckpoint}return n[e]??{frameId:e,strokes:[],regionMasks:[]}}function Ps(e,t={}){return e.overlayCheckpoint??t[e.frame.frameId]??{frameId:e.frame.frameId,strokes:[],regionMasks:[]}}function No(e,t){if(e.length===0)return null;let n=e[0];for(const a of e)if(a.startElapsed<=t)n=a;else break;return n}function sa(e){var t;return((t=e[0])==null?void 0:t.frame.frameId)??""}let Pt=null;const tr=new Map;function Mr(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`wal-${Date.now()}-${Math.random().toString(36).slice(2)}`}function Cr(){if(Pt)return Pt;if(typeof Worker>"u")return null;try{return Pt=new Worker(new URL("/assets/uixSessionRecorder.worker-IeiLyPFv.js",import.meta.url),{type:"module"}),Pt.addEventListener("message",e=>{const t=e.data;if(!(t!=null&&t.requestId))return;const n=tr.get(t.requestId);if(n){if(tr.delete(t.requestId),t.type==="wal_error"){n.reject(new Error(t.message));return}n.resolve(t)}}),Pt}catch{return null}}function Er(e){const t=Cr();return t?new Promise((n,a)=>{tr.set(e.requestId,{resolve:o=>n(o),reject:a}),t.postMessage(e)}):Promise.reject(new Error("UIX WAL worker unavailable"))}async function As(e){return(await Er({type:"wal_flush",requestId:Mr(),chunk:e})).events}async function Rs(e){if(!Cr())return{chunks:[],events:[]};try{const n=await Er({type:"wal_recover_patient",requestId:Mr(),patientId:e}),a=n.chunks.flatMap(o=>o.events);return{chunks:n.chunks,events:a}}catch{return{chunks:[],events:[]}}}async function Ds(){if(!Cr())return 0;try{return(await Er({type:"wal_purge_stale",requestId:Mr()})).removedCount}catch{return 0}}function la(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`chunk-${Date.now()}-${Math.random().toString(36).slice(2)}`}class Os{constructor(t){be(this,"buffer",[]);be(this,"recordingStartedAt",0);be(this,"lastMouseSampleAt",0);be(this,"lastMutateSampleAt",0);be(this,"flushTimer",null);be(this,"flushInFlight",!1);be(this,"patientId","");be(this,"sessionId","");be(this,"onFlush");be(this,"onTransmit");this.onFlush=t.onFlush,this.onTransmit=t.onTransmit}configure(t,n){this.patientId=t,this.sessionId=n}restoreTimelineAnchor(t){if(t.length===0)return;const n=t[t.length-1];this.recordingStartedAt=Date.now()-n.relTime}start(t=Date.now()){this.recordingStartedAt=t,this.buffer=[],this.lastMouseSampleAt=0,this.lastMutateSampleAt=0,this.scheduleFlush()}stop(){this.cancelFlushTimer(),this.flush(!0)}discard(){this.cancelFlushTimer(),this.buffer=[]}ensureWalActive(){this.flushTimer||this.scheduleFlush()}relTime(t=Date.now()){return Math.max(0,t-this.recordingStartedAt)}base(t,n,a=Date.now()){return{timestamp:a,relTime:this.relTime(a),frameId:t,type:n}}recordMouseMove(t,n,a,o=Date.now()){if(o-this.lastMouseSampleAt{this.flush()},us)}cancelFlushTimer(){this.flushTimer!=null&&(window.clearInterval(this.flushTimer),this.flushTimer=null)}}function xt(e){return e==="single_frame"||e==="multi_frame"?e:e==="encounter"||e==="per_frame"||e==="prompt"?"multi_frame":"single_frame"}const zs=1500,Fs="lumina-session-recording",$s=1,at="saved-bundles",xn="encounter-groups",Nt="settings";function Ct(){return new Promise((e,t)=>{const n=indexedDB.open(Fs,$s);n.onerror=()=>t(n.error??new Error("Failed to open session recording IndexedDB")),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;if(!a.objectStoreNames.contains(at)){const o=a.createObjectStore(at,{keyPath:"id"});o.createIndex("patientId","patientId",{unique:!1}),o.createIndex("encounterGroupId","encounterGroupId",{unique:!1})}a.objectStoreNames.contains(xn)||a.createObjectStore(xn,{keyPath:"id"}).createIndex("patientId","patientId",{unique:!1}),a.objectStoreNames.contains(Nt)||a.createObjectStore(Nt,{keyPath:"key"})}})}function Et(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error??new Error("IndexedDB request failed"))})}function Mo(e){return`recording-mode:${e}`}async function Bs(e){try{const t=await Ct(),n=await Et(t.transaction(Nt,"readonly").objectStore(Nt).get(Mo(e)));if(t.close(),(n==null?void 0:n.value)!=null)return xt(n.value)}catch{}return"single_frame"}async function Hs(e,t){const n=await Ct();await Et(n.transaction(Nt,"readwrite").objectStore(Nt).put({key:Mo(e),value:t})),n.close()}async function Gs(e){await Vs(e);const t=await Ct(),n=t.transaction(at,"readonly").objectStore(at).index("patientId"),a=await Et(n.getAll(e));return t.close(),a.filter(o=>!o.isDraft).sort((o,i)=>new Date(i.savedAt).getTime()-new Date(o.savedAt).getTime())}async function Co(e){const t=await Ct();await Et(t.transaction(at,"readwrite").objectStore(at).put(e)),t.close()}async function Ws(e){const t=await Ct();await Et(t.transaction(at,"readwrite").objectStore(at).delete(e)),t.close()}async function Us(e){const t=await Ct(),n=t.transaction(xn,"readonly").objectStore(xn).index("patientId"),a=await Et(n.getAll(e));return t.close(),a.sort((o,i)=>new Date(i.startedAt).getTime()-new Date(o.startedAt).getTime())}async function Vs(e){var t,n,a,o;try{const i=`lumina-saved-sessions:${e}`,l=localStorage.getItem(i);if(!l)return;const c=JSON.parse(l);if(!Array.isArray(c)||c.length===0)return;for(const d of c){const u=d.snapshot.capture,h=((n=(t=u.segments)==null?void 0:t[0])==null?void 0:n.frame.frameId)??((o=(a=u.events.find(f=>{var g;return(g=f.frame)==null?void 0:g.frameId}))==null?void 0:a.frame)==null?void 0:o.frameId)??"",p={id:d.id,patientId:e,name:d.name,savedAt:d.savedAt,recordingMode:xt(u.recordingMode),events:u.events,pointerTrace:u.pointerTrace??[],totalDurationSeconds:u.totalDurationSeconds??0,description:d.snapshot.description,stepNotes:d.snapshot.stepNotes,eventEdits:d.snapshot.eventEdits,segments:u.segments??[],overlaysByFrameId:u.overlaysByFrameId??{},uixEvents:u.uixEvents??[],primaryFrameId:h,capture:u};await Co(p)}localStorage.removeItem(i)}catch{}}function At(e){return JSON.stringify({events:e.events,pointerTrace:e.pointerTrace,description:e.description,stepNotes:e.stepNotes,eventEdits:e.eventEdits,segments:e.segments})}function Tr(e){try{return new Date(e).toLocaleString("vi-VN",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"})}catch{return e}}function Ks(e){var o;if(e.length===0)return"00:00";const t=((o=e[e.length-1])==null?void 0:o.elapsedSeconds)??0,n=Math.floor(t/60),a=Math.floor(t%60);return`${String(n).padStart(2,"0")}:${String(a).padStart(2,"0")}`}const qs="lumina-session-replay-notes",Xs="lumina-session-replay-desc",Ys="lumina-session-replay-event-edits",Js="lumina-session-replay-pointer-trace";function En(e){return`${qs}:${e}`}function vn(e){return`${Xs}:${e}`}function vt(e){try{const t=localStorage.getItem(En(e));return t?JSON.parse(t):{}}catch{return{}}}function Zs(e,t,n){const a=vt(e);n.trim()?a[t]=n.trim():delete a[t],localStorage.setItem(En(e),JSON.stringify(a))}function Ft(e){try{return localStorage.getItem(vn(e))??""}catch{return""}}function Eo(e,t){t.trim()?localStorage.setItem(vn(e),t.trim()):localStorage.removeItem(vn(e))}function Qs(e,t){localStorage.setItem(En(e),JSON.stringify(t))}function el(e,t){localStorage.setItem(Tn(e),JSON.stringify(t))}function Tn(e){return`${Ys}:${e}`}function yt(e){try{const t=localStorage.getItem(Tn(e));return t?JSON.parse(t):{}}catch{return{}}}function tl(e,t,n){const a=yt(e),i={...a[t]??{}};return n.title!==void 0&&(n.title.trim()?i.title=n.title.trim():delete i.title),n.detail!==void 0&&(n.detail.trim()?i.detail=n.detail.trim():delete i.detail),i.title||i.detail?a[t]=i:delete a[t],localStorage.setItem(Tn(e),JSON.stringify(a)),a}function nl(e,t){const n=t[e.id];return n?{...e,title:n.title??e.title,detail:n.detail??e.detail}:e}function en(e){localStorage.removeItem(En(e)),localStorage.removeItem(vn(e)),localStorage.removeItem(Tn(e)),localStorage.removeItem(yn(e))}function yn(e){return`${Js}:${e}`}function ca(e){try{return localStorage.getItem(yn(e))==="1"}catch{return!1}}function rl(e,t){t?localStorage.setItem(yn(e),"1"):localStorage.removeItem(yn(e))}const al="lumina-session-capture";function Ir(e){return`${al}:${e}`}function wt(){return{status:"idle",useCustomLog:!1,events:[],pointerTrace:[],segmentStartedAt:null,baseElapsedSeconds:0,totalDurationSeconds:0,segments:[],recordingMode:"single_frame",encounterGroupId:null,overlaysByFrameId:{},uixEvents:[],uixWalSessionId:null,uixWalAppliedChunkIds:[]}}function da(e){try{const t=localStorage.getItem(Ir(e));if(!t)return wt();const n=JSON.parse(t);return{...wt(),...n,segments:n.segments??[],recordingMode:n.recordingMode==="multi_frame"||n.recordingMode==="single_frame"?n.recordingMode:n.recordingMode==="encounter"||n.recordingMode==="per_frame"||n.recordingMode==="prompt"?"multi_frame":"single_frame",encounterGroupId:n.encounterGroupId??null,overlaysByFrameId:n.overlaysByFrameId??{},uixEvents:n.uixEvents??[],uixWalSessionId:n.uixWalSessionId??null,uixWalAppliedChunkIds:n.uixWalAppliedChunkIds??[],totalDurationSeconds:n.totalDurationSeconds??0,segmentStartedAt:null,status:n.status==="recording"?"paused":n.status}}catch{return wt()}}function ua(e,t){const n={...t,segmentStartedAt:null};localStorage.setItem(Ir(e),JSON.stringify(n))}function tn(e){localStorage.removeItem(Ir(e))}const To=s.createContext(null);function ha(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`uix-session-${Date.now()}-${Math.random().toString(36).slice(2)}`}function ga(e){return{...wt(),recordingMode:e}}function ol(e){return e.useCustomLog&&(e.events.length>0||e.uixEvents.length>0||e.segments.length>0)||e.status==="ended"||e.status==="paused"}function ft(e){var t;return e.status==="recording"&&e.segmentStartedAt!=null?e.baseElapsedSeconds+(Date.now()-e.segmentStartedAt)/1e3:((t=e.events[e.events.length-1])==null?void 0:t.elapsedSeconds)??e.baseElapsedSeconds}function pa(e){var o;const t=Nr(e.events);let n=[...e.segments],a={...e.overlaysByFrameId};for(let i=0;ida(e)),[l,c]=s.useState([]),[d,u]=s.useState([]),[h,p]=s.useState("single_frame"),[f,g]=s.useState(null),[N,y]=s.useState("inactive"),[k,A]=s.useState(""),[I,w]=s.useState(!1),[L,z]=s.useState(!1),[X,R]=s.useState(!1),[ie,U]=s.useState(!1),[v,B]=s.useState(null),[re,V]=s.useState(null),ne=s.useRef(null),F=s.useRef(o),_=s.useRef(h),O=s.useRef(null),S=s.useRef(null),P=s.useRef(null),j=s.useRef({width:1024,height:768});F.current=o,_.current=h;const ee=s.useMemo(()=>At({events:o.events,pointerTrace:o.pointerTrace,description:Ft(e),stepNotes:vt(e),eventEdits:yt(e),segments:o.segments}),[o.events,o.pointerTrace,o.segments,e]),J=s.useCallback(async()=>{const[m,x]=await Promise.all([Gs(e),Us(e)]);c(m),u(x)},[e]);s.useEffect(()=>{const m=da(e);i(m),J(),Bs(e).then(C=>{const T=m.status==="idle"&&!m.useCustomLog&&m.events.length===0&&m.segments.length===0,D=xt(T?C:m.recordingMode);p(D),i(H=>({...H,recordingMode:D}))}),g(null),y("inactive"),A(""),w(!1),z(!1),ne.current=null;let x=!1;return(async()=>{await Ds();const{chunks:C}=await Rs(e);x||C.length===0||i(T=>{const D=new Set(T.uixWalAppliedChunkIds),H=C.filter(xe=>!D.has(xe.chunkId));if(H.length===0)return T;const le={...T,uixEvents:na(T.uixEvents,H.flatMap(xe=>xe.events)),uixWalAppliedChunkIds:[...T.uixWalAppliedChunkIds,...H.map(xe=>xe.chunkId)],uixWalSessionId:T.uixWalSessionId??H[H.length-1].sessionId};return ua(e,le),le})})(),()=>{x=!0}},[e,J]),s.useEffect(()=>{if(N==="confirm")return;const m=o.status==="ended"&&o.useCustomLog&&o.events.length>=2&&ee!==ne.current;y(m?"savable":"inactive")},[o.status,o.useCustomLog,o.events.length,ee,N]),s.useEffect(()=>{if(!v)return;const m=window.setTimeout(()=>B(null),4e3);return()=>window.clearTimeout(m)},[v]);const K=s.useCallback(m=>{i(m),ua(e,m),At({events:m.events,pointerTrace:m.pointerTrace,description:"",stepNotes:{},eventEdits:{},segments:m.segments})!==ne.current&&g(null)},[e]),Ee=s.useCallback((m,x)=>{if(m.length===0)return;const C=F.current;C.uixWalAppliedChunkIds.includes(x)||K({...C,uixEvents:na(C.uixEvents,m),uixWalAppliedChunkIds:[...C.uixWalAppliedChunkIds,x]})},[K]);s.useEffect(()=>{var x;const m=F.current.uixWalSessionId;m&&((x=P.current)==null||x.configure(e,m))},[e,o.uixWalSessionId]),s.useEffect(()=>{const m=new Os({onFlush:Ee,onTransmit:()=>{}});return P.current=m,Qr(j.current),Zr({isActive:()=>F.current.status==="recording"&&_.current==="multi_frame",getContentSize:()=>j.current,onOverlayCreate:(x,C)=>{var T;(T=P.current)==null||T.recordOverlayCreate(x,C)},onOverlayMutate:(x,C,T,D)=>{var le;const H=j.current;(le=P.current)==null||le.recordOverlayMutate(x,{overlayId:C,points:T.map(xe=>({x:H.width>0?xe.x/H.width:xe.x,y:H.height>0?xe.y/H.height:xe.y})),color:D==null?void 0:D.color,strokeWidth:D==null?void 0:D.strokeWidth},D)},onOverlayDelete:(x,C)=>{var T;(T=P.current)==null||T.recordOverlayDelete(x,C)},onOverlayCheckpoint:(x,C)=>{var T;(T=P.current)==null||T.recordOverlayCheckpoint(x,C)}}),()=>{P.current=null,Zr(null),Qr(null)}},[Ee]);const pe=s.useCallback(m=>{const x=F.current;x.status==="recording"||x.status==="paused"||(p(m),_.current=m,Hs(e,m),K({...x,recordingMode:m}))},[e,K]),he=s.useCallback((m,x)=>{const C=F.current;if(C.status!=="recording")return;const T=Math.round(ft(C)*10)/10,D=C.segments[C.segments.length-1],H={id:crypto.randomUUID(),elapsedSeconds:T,recordedAt:_n(),frame:x??(D==null?void 0:D.frame),...m};K({...C,useCustomLog:!0,events:[...C.events,H]})},[K]),Ne=s.useCallback(m=>{const x=F.current;if(x.status!=="recording"&&x.status!=="paused")return;const C=x.segments.length-1;if(C<0)return;const T=x.segments[C];if(T.endElapsed!=null)return;const D=[...x.segments];D[C]={...T,viewport:{...T.viewport,...m}},K({...x,segments:D})},[K]),ge=s.useCallback((m,x,C)=>{const T=F.current;if(T.status!=="recording"||T.recordingMode==="multi_frame")return;const H={elapsedSeconds:Math.round(ft(T)*10)/10,x:Math.max(0,Math.min(100,m)),y:Math.max(0,Math.min(100,x)),frameId:C},le=T.pointerTrace[T.pointerTrace.length-1];le&&le.elapsedSeconds===H.elapsedSeconds&&Math.hypot(le.x-H.x,le.y-H.y)<.25&&le.frameId===C||K({...T,useCustomLog:!0,pointerTrace:[...T.pointerTrace,H]})},[K]),ae=s.useCallback((m,x,C)=>{var D;const T=F.current;T.status!=="recording"||T.recordingMode!=="multi_frame"||(D=P.current)==null||D.recordMouseMove(C,m,x)},[]),E=s.useCallback((m,x)=>{const C=F.current,T=Math.round(ft(C)*10)/10;let D=[...C.segments],H={...C.overlaysByFrameId};const le=D.length-1;if(le>=0&&D[le].endElapsed==null){const{segment:We,overlay:Ue}=nr(D[le],T);D[le]=We,H[Ue.frameId]=Ue}const xe=ia(m,T,x);K({...C,segments:[...D,xe],overlaysByFrameId:H})},[K]),$=s.useCallback(m=>{he({title:"Chuyển khung DICOM",detail:m.frameLabel,kind:"view",clickPulse:!0,cursor:{x:50,y:50}},m)},[he]),se=s.useCallback((m,x)=>{const C=F.current,T=C.segments[C.segments.length-1];if(_.current==="multi_frame"&&P.current&&T){const D=xo(T.frame.frameId),H=j.current,le=[...D.strokes.map(xe=>ko(xe,H.width,H.height)),...D.regionMasks.map(xe=>jo(xe,H.width,H.height))];le.length>0&&P.current.recordOverlayCheckpoint(T.frame.frameId,le),P.current.recordFrameSwitch(m.frameId,{fromFrameId:T.frame.frameId,toFrameId:m.frameId,dicomMetadataUrl:m.imageSrc??"",frameIndex:m.frameIndex,frameLabel:m.frameLabel})}E(m,x),$(m)},[$,E]),ke=s.useCallback((m,x)=>{const C=F.current;if(C.status!=="recording"&&C.status!=="paused"||xt(C.recordingMode)!=="multi_frame")return;const T=C.segments[C.segments.length-1];if((T==null?void 0:T.frame.frameId)===m.frameId)return;const D=Date.now(),H=O.current;if(H&&H.fromFrameId!==m.frameId&&D-H.switchedAt{S.current={frame:m,viewport:x}},[]),Pe=s.useCallback(()=>{var xe,We,Ue,ht;const m=S.current;if(!m)return;const{frame:x,viewport:C}=m,T=F.current;if(T.status==="recording"||T.status==="paused"||T.status==="ended")return;y("inactive"),A("");const D=Date.now(),H=Cs(t);H.frame=x;let le;if(T.useCustomLog&&T.events.length>0){const Xe=T.events[T.events.length-1].elapsedSeconds,gt=T.uixWalSessionId??ha();le={...T,status:"recording",segmentStartedAt:D,baseElapsedSeconds:Xe,recordingMode:_.current,uixWalSessionId:gt},_.current==="multi_frame"&&((xe=P.current)==null||xe.configure(e,gt),T.uixWalSessionId||(We=P.current)==null||We.start(D))}else{const Xe=ha();le={...T,status:"recording",useCustomLog:!0,recordingMode:_.current,events:[H],pointerTrace:[],uixEvents:[],uixWalSessionId:Xe,uixWalAppliedChunkIds:[],segmentStartedAt:D,baseElapsedSeconds:0,segments:[ia(x,0,C)],encounterGroupId:null},_.current==="multi_frame"&&((Ue=P.current)==null||Ue.configure(e,Xe),(ht=P.current)==null||ht.start(D))}K(le)},[t,e,K]),q=s.useCallback(()=>{var x;const m=F.current;m.status==="recording"&&((x=P.current)==null||x.flush(!0),K({...m,status:"paused",segmentStartedAt:null,baseElapsedSeconds:ft(m)}))},[K]),oe=s.useCallback(()=>{var D,H,le;const m=F.current;if(m.status!=="paused"&&m.status!=="ended")return;const x=m.events[m.events.length-1],C=(x==null?void 0:x.elapsedSeconds)??m.baseElapsedSeconds,T=m.status==="ended"&&(x==null?void 0:x.title)==="Kết thúc ghi phiên"?m.events.slice(0,-1):m.events;K({...m,status:"recording",recordingMode:_.current,events:T,segmentStartedAt:Date.now(),baseElapsedSeconds:C}),_.current==="multi_frame"&&m.uixWalSessionId&&((D=P.current)==null||D.configure(e,m.uixWalSessionId),(H=P.current)==null||H.restoreTimelineAnchor(m.uixEvents),(le=P.current)==null||le.ensureWalActive())},[e,K]),we=s.useCallback(()=>{var xe;const m=F.current;if(m.status!=="recording"&&m.status!=="paused")return;const x=ft(m),C=Es(x),T=m.events.filter(We=>We.title!=="Kết thúc ghi phiên");let D=[...m.segments],H={...m.overlaysByFrameId};const le=D.length-1;if(le>=0&&D[le].endElapsed==null){const{segment:We,overlay:Ue}=nr(D[le],x);D[le]=We,H[Ue.frameId]=Ue}(xe=P.current)==null||xe.stop(),K({...m,status:"ended",useCustomLog:!0,events:[...T,C],segmentStartedAt:null,baseElapsedSeconds:x,segments:D,overlaysByFrameId:H,totalDurationSeconds:x})},[K]),de=s.useCallback(()=>{if(N!=="savable")return;const m=F.current.segments.length,x=m>1?`Khảo sát ${Tr(new Date().toISOString()).split(",")[0]} — ${m} khung`:`Phiên ${_n().replace(/:/g,"-")}`;A(x),y("confirm")},[N]),Se=s.useCallback(()=>{A("");const m=F.current,x=At({events:m.events,pointerTrace:m.pointerTrace,description:Ft(e),stepNotes:vt(e),eventEdits:yt(e),segments:m.segments}),C=m.status==="ended"&&m.useCustomLog&&m.events.length>=2&&x!==ne.current;y(C?"savable":"inactive")},[e]),je=s.useCallback(()=>{const m=F.current,x=k.trim();if(!x||m.status!=="ended")return;const C=pa(m),T=Is(C.segments),D={id:crypto.randomUUID(),patientId:e,name:x,savedAt:new Date().toISOString(),encounterGroupId:m.encounterGroupId??void 0,recordingMode:m.recordingMode,events:C.events,pointerTrace:C.pointerTrace,totalDurationSeconds:C.totalDurationSeconds,description:Ft(e),stepNotes:{...vt(e)},eventEdits:{...yt(e)},segments:C.segments,overlaysByFrameId:T,primaryFrameId:sa(C.segments),uixEvents:C.uixEvents,capture:{...C,overlaysByFrameId:T}};Co(D).then(()=>J()),g(D.id),ne.current=At({events:D.events,pointerTrace:D.pointerTrace,description:D.description,stepNotes:D.stepNotes,eventEdits:D.eventEdits,segments:D.segments}),A(""),y("inactive")},[e,J,k]),G=s.useCallback(m=>{(async()=>{const x=l.find(D=>D.id===m);if(!x)return;const C=pa(x.capture);K({...C,overlaysByFrameId:x.overlaysByFrameId}),Eo(e,x.description),Qs(e,x.stepNotes),el(e,x.eventEdits);const T=x.segments[0];V({frameId:x.primaryFrameId||(T==null?void 0:T.frame.frameId)||"",frameIndex:(T==null?void 0:T.frame.frameIndex)??0,viewport:(T==null?void 0:T.viewport)??{zoom:1,panX:0,panY:0,showMask:!0,showGradcam:!1},overlaysByFrameId:x.overlaysByFrameId}),g(m),ne.current=At({events:x.events,pointerTrace:x.pointerTrace,description:x.description,stepNotes:x.stepNotes,eventEdits:x.eventEdits,segments:x.segments}),y("inactive"),A(""),w(!1)})()},[e,K,l]),_e=s.useCallback(m=>{Ws(m).then(()=>J()),f===m&&g(null)},[f,J]),b=he,W=o.status==="recording"||o.status==="paused"||o.status==="ended"&&N!=="inactive",Q=s.useCallback(()=>{R(!0)},[]),Y=s.useCallback(()=>{R(!1)},[]),fe=s.useCallback(()=>{var m;(m=P.current)==null||m.discard(),Jr(),tn(e),en(e),K(ga(_.current)),g(null),ne.current=null,y("inactive"),A(""),V(null),R(!1)},[e,K]),ce=o.status!=="recording"&&o.status!=="paused",M=s.useCallback(()=>{var m;(m=P.current)==null||m.discard(),Jr(),tn(e),en(e),K(ga(_.current)),g(null),ne.current=null,y("inactive"),A(""),V(null),U(!1)},[e,K]),Z=s.useCallback(()=>{if(ce){if(ol(F.current)){U(!0);return}M()}},[ce,M]),te=s.useCallback(()=>{U(!1)},[]),me=s.useCallback(()=>{M()},[M]),Me=s.useCallback(()=>{z(!0)},[]),Oe=s.useCallback(()=>{z(!1)},[]),Te=s.useCallback(async()=>{tn(e),en(e),await Br(e),K(wt()),g(null),ne.current=null,y("inactive"),A(""),z(!1)},[e,K]),Ce=s.useCallback(async()=>{tn(e),en(e),await Br(e),K(wt()),g(null),ne.current=null,y("inactive"),A(""),z(!1)},[e,K]),ze=s.useMemo(()=>{if(o.useCustomLog&&o.events.length>0){const m=o.events,x=o.status==="recording"?ft(o):Nr(m),C=Math.max(Ms(m,o.pointerTrace),x,1);return{patientId:e,events:m,totalDurationSeconds:C,pointerTrace:o.pointerTrace,segments:o.segments,overlaysByFrameId:o.overlaysByFrameId,primaryFrameId:sa(o.segments),sessionKey:Sr(e,m),uixEvents:o.uixEvents}}return ks(e)},[o,e]),$e=s.useMemo(()=>{if(o.status!=="recording")return null;const m=o.segments[o.segments.length-1];return m?`Đang ghi · ${m.frame.frameLabel}`:null},[o.segments,o.status]),Ie=s.useMemo(()=>o.status==="recording"||o.status==="paused"?xt(o.recordingMode):h,[o.recordingMode,o.status,h]),Ae=s.useMemo(()=>o.status!=="recording"||o.segments.length===0?!1:xt(o.recordingMode)==="single_frame",[o.recordingMode,o.segments.length,o.status]),Le=s.useMemo(()=>({status:o.status,isRecording:o.status==="recording",isSingleFrameNavLocked:Ae,sessionLog:ze,segments:o.segments,savedSessions:l,encounterGroups:d,activeSavedSessionId:f,saveUiState:N,saveNameDraft:k,sessionsDialogOpen:I,recordingMode:Ie,draftToastMessage:v,activeSegmentLabel:$e,replayFrameRestore:re,clearReplayFrameRestore:()=>V(null),setRecordingMode:pe,setRecordingContext:Fe,startRecording:Pe,pauseRecording:q,resumeRecording:oe,endRecording:we,recordDicomEvent:b,recordPointerSample:ge,recordUixMouseMove:ae,handleFrameSwitch:ke,updateActiveSegmentViewport:Ne,beginSaveSession:de,cancelSaveSession:Se,setSaveNameDraft:A,confirmSaveSession:je,openSessionsDialog:()=>w(!0),closeSessionsDialog:()=>w(!1),loadSavedSession:G,deleteSavedSession:_e,requestReset:Me,confirmReset:Te,confirmResetOnly:Ce,cancelReset:Oe,resetDialogOpen:L,requestCancelRecording:Q,dismissCancelRecording:Y,confirmCancelRecording:fe,cancelRecordDialogOpen:X,canCancelRecording:W,requestNewSession:Z,dismissNewSession:te,confirmNewSession:me,newSessionDialogOpen:ie,canStartNewSession:ce}),[o.status,o.segments,ze,Ae,l,d,f,N,k,I,Ie,v,$e,re,pe,Fe,Pe,q,oe,we,b,ge,ae,ke,Ne,de,Se,je,G,_e,Me,Te,Ce,Oe,L,Q,Y,fe,X,W,Z,te,me,ie,ce]);return r.jsx(To.Provider,{value:Le,children:a})}function Yt(){const e=s.useContext(To);if(!e)throw new Error("useSessionLifecycle must be used within SessionLifecycleProvider");return e}const sl={draw:"Vẽ chú thích",eraser:"Tẩy chú thích",pan:"Di chuyển khung hình"};function ll({viewportRef:e,frameRef:t,frameCount:n,patientId:a,tool:o,showMask:i,showGradcam:l=!1,zoom:c,onMaskToggle:d,onToolChange:u,onZoomIn:h,onZoomOut:p,onResetView:f,onClearAnnotations:g,onFrameSwitch:N,getViewportState:y}){const{isRecording:k,status:A,recordingMode:I,recordDicomEvent:w}=Yt(),L=s.useRef(t.frameId),z=s.useRef(!0),X=s.useRef(o),R=s.useRef(c);s.useEffect(()=>{z.current=!0,L.current=t.frameId},[a,n]);const ie=k||A==="paused"&&I==="multi_frame";return s.useEffect(()=>{if(z.current){z.current=!1,L.current=t.frameId;return}if(!ie||L.current===t.frameId)return;L.current=t.frameId;const F=y();N==null||N(t,{zoom:F.zoom,panX:F.panX,panY:F.panY,showMask:i,showGradcam:l})},[t,y,N,ie,l,i]),s.useEffect(()=>{!k||X.current===o||(X.current=o,w({title:"Đổi công cụ trên khung DICOM",detail:`${sl[o]??o} · ${t.frameLabel}`,kind:"edit"},t))},[o,t,k,w]),s.useEffect(()=>{!k||R.current===c||(R.current=c,w({title:"Thay đổi thu phóng khung DICOM",detail:`${Math.round(c*100)}% · ${t.frameLabel}`,kind:"view"},t))},[c,t,k,w]),s.useEffect(()=>{if(!k)return;const F=e.current;if(!F)return;const _=O=>{if(o!=="draw"&&o!=="eraser")return;const S=F.getBoundingClientRect();if(S.width<=0||S.height<=0)return;const P=Math.max(0,Math.min(100,(O.clientX-S.left)/S.width*100)),j=Math.max(0,Math.min(100,(O.clientY-S.top)/S.height*100));w({title:o==="eraser"?"Tẩy trên khung DICOM":"Vẽ trên khung DICOM",detail:`${t.frameLabel} · (${Math.round(P)}%, ${Math.round(j)}%)`,kind:"edit",cursor:{x:P,y:j},clickPulse:!0},t)};return F.addEventListener("pointerup",_),()=>F.removeEventListener("pointerup",_)},[k,o,t,w,e]),{handleMaskToggle:()=>{const F=!i;d(),k&&w({title:F?"Bật kết quả phân đoạn":"Tắt kết quả phân đoạn",detail:t.frameLabel,kind:"view",clickPulse:!0},t)},handleToolChange:F=>{u(F)},handleZoomIn:()=>{h()},handleZoomOut:()=>{p()},handleResetView:()=>{f(),k&&w({title:"Đặt lại khung nhìn DICOM",detail:t.frameLabel,kind:"view"},t)},handleClearAnnotations:()=>{g(),k&&w({title:"Xóa chú thích trên khung DICOM",detail:t.frameLabel,kind:"delete",clickPulse:!0},t)}}}const rr=["no_inflammation","inflammation"],ar={no_inflammation:"Không viêm",inflammation:"Có viêm"};function cl(e,t){if(e){const n=e.confidence!=null?e.confidence>1?e.confidence/100:e.confidence:.5;return{detected:e.detected??n>=.5,confidence:n,calibration:e.calibration}}return null}function dl(e){if(!e||typeof e!="object")return null;const t=e.level;if(typeof t!="number"||!Number.isFinite(t))return null;const n=typeof e.severity=="string"?e.severity:`Độ ${t}`,a=typeof e.description=="string"?e.description:void 0,o=typeof e.combined_score=="number"&&Number.isFinite(e.combined_score)?e.combined_score:void 0;return{level:Math.min(3,Math.max(0,Math.round(t))),label:n,description:a,combinedScore:o}}const ul="lumina-ml-inflight",Wt="lumina-ml-inflight:",$t=new Map;let nn=null;function In(){return typeof BroadcastChannel>"u"?null:(nn||(nn=new BroadcastChannel(ul),nn.onmessage=e=>{const t=e.data;if(!(t!=null&&t.batchKey))return;const n=$t.get(t.batchKey);if(n!=null&&n.length){if(t.type==="fetch-done")$t.delete(t.batchKey),n.forEach(({resolve:a})=>a());else if(t.type==="fetch-error"){$t.delete(t.batchKey);const a=new Error(t.message||"Cross-tab fetch failed");n.forEach(({reject:o})=>o(a))}}}),nn)}function hl(e){var t;try{localStorage.setItem(Wt+e,String(Date.now()))}catch{}(t=In())==null||t.postMessage({type:"fetch-start",batchKey:e})}function gl(e){var t;try{localStorage.removeItem(Wt+e)}catch{}(t=In())==null||t.postMessage({type:"fetch-done",batchKey:e})}function pl(e,t){var n;try{localStorage.removeItem(Wt+e)}catch{}(n=In())==null||n.postMessage({type:"fetch-error",batchKey:e,message:t})}function fl(e,t=12e4){try{const n=localStorage.getItem(Wt+e);if(!n)return!1;const a=Number(n);return!Number.isFinite(a)||Date.now()-a>t?(localStorage.removeItem(Wt+e),!1):!0}catch{return!1}}function ml(e,t=12e4){return In()?new Promise((a,o)=>{const i=window.setTimeout(()=>{o(new Error(`Timed out waiting for cross-tab ML fetch: ${e}`))},t),l=()=>{window.clearTimeout(i),a()},c=u=>{window.clearTimeout(i),o(u)},d=$t.get(e)??[];d.push({resolve:l,reject:c}),$t.set(e,d)}):Promise.resolve()}async function bl(e,t){var n;if(typeof navigator<"u"&&((n=navigator.locks)!=null&&n.request))return navigator.locks.request(`lumina-ml-${e}`,{mode:"exclusive"},t);if(fl(e))return await ml(e),t();hl(e);try{return await t()}catch(a){throw pl(e,a instanceof Error?a.message:"ML fetch failed"),a}finally{gl(e)}}function _l(){const e=Date.now().toString(36).toUpperCase().slice(-6),t=Math.random().toString(36).slice(2,5).toUpperCase();return`LUM-ML-${e}${t}`}function xl(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return"Unknown error"}}function vl(e,t){const n=e.toLowerCase();return/did not match the expected pattern|unexpected token|invalid json from ml api|empty response from ml api/i.test(e)||t instanceof SyntaxError?{code:"BAD_RESPONSE",title:"Không kết nối được dịch vụ phân tích",message:"Máy chủ phân tích AI chưa phản hồi đúng định dạng — thường do dịch vụ chưa chạy hoặc đang khởi động. Hình siêu âm vẫn hiển thị bình thường; chỉ lớp phân đoạn tạm thời không có.",ticketHints:["Ghi lại thời điểm xảy ra lỗi","Cho biết bạn vừa mở trang hay vừa tải lại","Đính kèm mã tham chiếu bên dưới"],developerHint:"Proxy ML có thể chưa chạy (test_fast_api_proxy.py :8001) hoặc Vite proxy trả về HTML thay vì JSON."}:/failed to fetch|networkerror|load failed|network request failed|econnrefused/i.test(n)?{code:"NETWORK",title:"Mất kết nối tới dịch vụ phân tích",message:"Không thể liên lạc với máy chủ phân tích. Kiểm tra kết nối mạng nội bộ; hình siêu âm gốc vẫn an toàn và có thể xem.",ticketHints:["Kiểm tra Wi‑Fi / mạng bệnh viện","Thử tải lại trang sau 30 giây","Gửi mã tham chiếu cho IT nếu lỗi lặp lại"],developerHint:"Browser could not reach the ML proxy — connection refused or CORS/proxy misconfig."}:/502|503|bad gateway|triton|unreachable|cold-start|overload/i.test(n)?{code:"SERVER_OVERLOAD",title:"Dịch vụ phân tích đang bận",message:"Máy chủ AI đang khởi động hoặc xử lý nhiều yêu cầu. Vui lòng đợi vài giây rồi tải lại trang.",ticketHints:["Đợi 10–30 giây rồi thử lại","Nếu nhiều tab cùng mở, đóng bớt tab","Gửi mã tham chiếu nếu lỗi kéo dài > 2 phút"],developerHint:"Modal Triton cold start or 502 — check proxy logs and TRITON_ENDPOINT."}:/\b4\d{2}\b|api error \(4/i.test(e)?{code:"CLIENT_ERROR",title:"Yêu cầu phân tích không hợp lệ",message:"Hệ thống từ chối yêu cầu phân tích cho khung này. Hình ảnh gốc không bị ảnh hưởng.",ticketHints:["Ghi khung siêu âm đang xem","Đính kèm mã tham chiếu"],developerHint:"HTTP 4xx from ML proxy — inspect request payload and frame_ids."}:/\b5\d{2}\b|api error \(5/i.test(e)?{code:"SERVER_UNAVAILABLE",title:"Dịch vụ phân tích tạm ngưng",message:"Máy chủ phân tích gặp sự cố nội bộ. Vui lòng thử lại sau hoặc liên hệ IT với mã tham chiếu.",ticketHints:["Thử tải lại trang","Gửi mã tham chiếu cho nhóm kỹ thuật"],developerHint:"HTTP 5xx from ML proxy — check server logs and Triton health."}:/không có kết quả phân đoạn/i.test(e)?{code:"NO_RESULT",title:"Chưa có kết quả phân đoạn",message:"Khung siêu âm này chưa có dữ liệu phân đoạn để hiển thị.",ticketHints:[]}:{code:"UNKNOWN",title:"Không tải được lớp phân đoạn",message:"Phân tích hình ảnh tạm thời không hoàn tất. Hình siêu âm gốc vẫn hiển thị; bạn có thể tiếp tục làm việc.",ticketHints:["Ghi thao tác vừa thực hiện (mở trang / đổi khung / bật mask)","Gửi mã tham chiếu cho IT"],developerHint:"Unclassified ML error — inspect browser Network tab and proxy logs."}}function wn(e,t){const n=xl(e),a=vl(n,e),i=[`Thao tác: ${t.operation==="segmentation"?"phân đoạn (segmentation)":t.operation==="angle"?"góc quét (angle)":"overlay"}`,t.frameId?`Khung: ${t.frameId}`:null,t.patientMrn?`MRN: ${t.patientMrn}`:null,`Chi tiết: ${n}`,a.developerHint?`Gợi ý: ${a.developerHint}`:null].filter(Boolean);return{...a,supportReference:_l(),technicalDetail:i.join(` -`)}}function yl(e,t){return["[Lumina MSK] Báo lỗi phân tích AI",`Mã tham chiếu: ${e.supportReference}`,`Mã lỗi: ${e.code}`,t?`Khung: ${t}`:null,`Mô tả: ${e.title} — ${e.message}`,"","--- Chi tiết kỹ thuật (cho IT) ---",e.technicalDetail].filter(a=>a!==null).join(` -`)}async function Lr(e){const t=await e.text();if(!t.trim())throw new Error(`Empty response from ML API (HTTP ${e.status})`);try{return JSON.parse(t)}catch{const n=t.slice(0,100).replace(/\s+/g," ");throw new Error(`Invalid JSON from ML API (HTTP ${e.status}): server returned non-JSON — ${n}`)}}const wl={effusion:{nameVi:"Dịch khớp",nameEn:"Joint effusion"},fat:{nameVi:"Mỡ",nameEn:"Fat"},"fat-pat":{nameVi:"Mỡ Hoffa",nameEn:"Hoffa fat pad"},femur:{nameVi:"Xương đùi",nameEn:"Femur"},synovium:{nameVi:"Màng hoạt dịch",nameEn:"Synovium"},tendon:{nameVi:"Gân",nameEn:"Tendon"},muscle:{nameVi:"Cơ bắp",nameEn:"Muscle"},artery:{nameVi:"Động mạch",nameEn:"Artery"},"baker's cyst":{nameVi:"Nang Baker",nameEn:"Baker's cyst"}},kl=[{key:"effusion",color:[255,0,0],nameEn:"Joint effusion",nameVi:"Dịch khớp",highlight:!0},{key:"fat",color:[255,255,0],nameEn:"Fat",nameVi:"Mỡ"},{key:"fat-pat",color:[0,255,255],nameEn:"Hoffa fat pad",nameVi:"Mỡ Hoffa"},{key:"femur",color:[0,255,0],nameEn:"Femur",nameVi:"Xương đùi"},{key:"synovium",color:[255,0,255],nameEn:"Synovium",nameVi:"Màng hoạt dịch",highlight:!0},{key:"tendon",color:[0,0,255],nameEn:"Tendon",nameVi:"Gân"}],jl=[{key:"baker's cyst",color:[255,0,0],nameEn:"Baker's cyst",nameVi:"Nang Baker",highlight:!0},{key:"fat",color:[255,255,0],nameEn:"Fat",nameVi:"Mỡ"},{key:"tendon",color:[0,0,255],nameEn:"Tendon",nameVi:"Gân"},{key:"muscle",color:[0,255,255],nameEn:"Muscle",nameVi:"Cơ bắp"},{key:"femur",color:[0,255,0],nameEn:"Femur",nameVi:"Xương đùi"},{key:"artery",color:[255,0,255],nameEn:"Artery",nameVi:"Động mạch"}],fa=[{id:"white-contours",textVi:"Viền trắng dày: vùng viêm — bao quanh dịch khớp và màng hoạt dịch (theo contour AI)",textEn:"Thick white outline: inflammatory region — joint effusion and synovium boundaries",indicator:"outline-white",supOnly:!0},{id:"contours",textVi:"Vùng màu bán trong suốt: lớp phân đoạn giải phẫu từ mô hình AI",textEn:"Semi-transparent color fills: AI anatomical segmentation classes"},{id:"bbox",textVi:"Khung xanh lá: bao quanh toàn bộ vùng giải phẫu được AI nhận diện (xương, mỡ, gân, dịch khớp, màng hoạt dịch…)",textEn:"Green box: bounds all AI-detected anatomy (bone, fat, tendon, effusion, synovium, etc.)",indicator:"bbox-green",supOnly:!0,measurementOnly:!0},{id:"roi-lines",textVi:"Hai đường dọc xanh ngọc: ranh giới trái/phải của vùng 1/3 giữa khung — khu vực hệ thống quét để đo",textEn:"Cyan vertical lines: left/right edges of the middle third of the box (measurement scan zone)",indicator:"vline-cyan",supOnly:!0,measurementOnly:!0},{id:"red-line",textVi:"Đường đỏ: độ dày dịch khớp + màng hoạt dịch — đoạn dọc liên tục dài nhất trong vùng 1/3 giữa",textEn:"Red line: effusion + synovium thickness — longest vertical segment in the middle third",indicator:"vline-red",supOnly:!0,measurementOnly:!0}];function Io(e){return e==="sup"?kl:jl}function Sl([e,t,n]){return`rgb(${e}, ${t}, ${n})`}function Lo(e,t){const n=t?jr(t):e.angle_type,a=Io(n),o=new Map(a.map(l=>[l.key,l])),i=e.classes_detected.filter(l=>l!=="background").map(l=>{const c=o.get(l),d=c??wl[l],u=e.color_legend[l]??(c==null?void 0:c.color)??[128,128,128];return{key:l,color:u,nameEn:(d==null?void 0:d.nameEn)??l,nameVi:(d==null?void 0:d.nameVi)??l,highlight:c==null?void 0:c.highlight}});return{angleType:n,legendEntries:i,measurement:t&&!go(t)?void 0:e.measurement}}function Ln(){return"".trim()!==""?"".replace(/\/$/,""):(console.warn("[segmentationApi] VITE_SEGMENT_API_BASE is not set. Segmentation API calls will fail. Set it in config/frontend.config.yaml for production builds."),"")}const Pn=new Map,Nl=new Map;function Ml(){Pn.clear(),Nl.clear()}function Cl(e,t){if(t.startsWith("image/"))return t;const n=e.toLowerCase();return n.endsWith(".jpg")||n.endsWith(".jpeg")?"image/jpeg":n.endsWith(".webp")?"image/webp":"image/png"}async function Po(e,t="frame.png"){const n=await fetch(e);if(!n.ok)throw new Error(`Failed to load frame image (${n.status})`);const a=await n.blob(),o=Cl(t,a.type);return new File([a],t,{type:o})}function El(e){var a,o;if(!((a=e.segmentation)!=null&&a.performed))return null;const t=(o=e.angle)!=null&&o.class&&Cn(e.angle.class)?e.angle.class:void 0,n={angle_type:e.segmentation.angle_type??(t?jr(t):"sup"),classes_detected:e.segmentation.classes_detected??[],color_legend:e.segmentation.color_legend??{},measurement:e.measurement};return Lo(n,t)}function Tl(e){var o,i;const t=((o=e.segmentation)==null?void 0:o.performed)===!0,n=t?((i=e.images)==null?void 0:i.segmented)??null:null,a=t?El(e):null;if(t&&!n)throw new Error("Segmentation performed but images.segmented is missing");return{overlaySrc:n,interpretation:a,raw:e}}const An=new Map,Il=new Map;function Ll(){An.clear(),Il.clear()}const qe=new Map,ct=new Map;function Ao(e){return[...e].sort().join("|")}function Pr(e){const t=Tl(e),n=Gi(e.angle);return{raw:e,segmentation:t,angle:n}}function Pl(e){return qe.get(e)}function Al(){qe.clear(),ct.clear()}async function ma(e,t){await Promise.all(e.map(async n=>{var a;if(!qe.has(n.id))try{const o=await so(n.src),i=Ii(t.patientMrn,n.id,o),l=await Li(i);if(!((a=l==null?void 0:l.segmentation)!=null&&a.raw))return;const c=l.segmentation.raw,d=Pr(c);qe.set(n.id,d),Pn.set(n.id,d.segmentation),d.angle&&An.set(n.id,d.angle)}catch{}}))}async function Ro(e,t,n){await Promise.all(e.map(async a=>{const o=t.get(a.id);if(o)try{const i=await so(a.src);await Ti(n.patientMrn,a.id,i,{segmentation:{overlaySrc:o.segmentation.overlaySrc,interpretation:o.segmentation.interpretation,raw:o.raw},angle:o.angle})}catch{}}))}async function Rl(e,t=Ln()){if(e.length===0)return new Map;const n=new FormData;(await Promise.all(e.map((c,d)=>Po(c.src,`${c.id||`frame-${d}`}.png`)))).forEach(c=>n.append("images",c)),n.append("frame_ids",JSON.stringify(e.map(c=>c.id)));const o=await fetch(`${t}/api/test/analyze/batch`,{method:"POST",body:n}),i=await Lr(o);if(!o.ok)throw new Error(i.detail??`CV analyze batch API error (${o.status})`);if(!i.success)throw new Error("CV analyze batch API returned success=false");const l=new Map;for(const c of i.results){const d=c.frame_id;d&&l.set(d,Pr(c))}return l}async function Dl(e,t){if(e.length===0)return new Map;t!=null&&t.patientMrn&&await ma(e,t);const n=e.filter(o=>!qe.has(o.id));if(n.length>0){const o=Ao(n.map(c=>c.id)),i=t!=null&&t.patientMrn?Ei(t.patientMrn,n.map(c=>c.id)):o;let l=ct.get(o);l||(l=bl(i,async()=>{t!=null&&t.patientMrn&&await ma(n,t);const c=n.filter(u=>!qe.has(u.id));if(c.length===0){const u=new Map;for(const h of n){const p=qe.get(h.id);p&&u.set(h.id,p)}return u}const d=await Rl(c);for(const[u,h]of d)qe.set(u,h),Pn.set(u,h.segmentation),h.angle&&An.set(u,h.angle);return t!=null&&t.patientMrn&&await Ro(c,d,t),d}).finally(()=>{ct.delete(o)}),ct.set(o,l)),await l}const a=new Map;for(const o of e){const i=qe.get(o.id);i&&a.set(o.id,i)}return a}async function Ol(e,t=Ln()){if(e.length===0)throw new Error("frames must not be empty");const n=new FormData;(await Promise.all(e.map((l,c)=>Po(l.src,`${l.id||`frame-${c}`}.png`)))).forEach(l=>n.append("images",l)),n.append("frame_ids",JSON.stringify(e.map(l=>l.id)));const o=await fetch(`${t}/api/test/analyze/batch/celery`,{method:"POST",body:n}),i=await Lr(o);if(!o.ok)throw new Error(`Celery batch submit error (${o.status})`);if(!i.success||!i.job_id)throw new Error("Celery batch submit returned success=false or missing job_id");return i.job_id}async function zl(e,t=Ln()){const n=await fetch(`${t}/api/test/analyze/batch/celery/${encodeURIComponent(e)}`),a=await Lr(n);if(!n.ok)throw new Error(a.detail??`Celery batch poll error (${n.status})`);return a}async function Fl(e,t,n=Ln(),a){if(e.length===0)return new Map;const o=Ao(e.map(d=>d.id)),i=new Map;for(const d of e){const u=qe.get(d.id);u&&i.set(d.id,u)}if(i.size===e.length)return i;const l=ct.get(o);if(l)return l;const c=(async()=>{var p;const d=await Ol(e,n),u=2e3,h=Date.now();for(;;){const f=await zl(d,n);if(f.status==="completed"){const g=new Map;if(f.results)for(const y of f.results){const k=y.frame_id;if(!k)continue;const A=Pr(y);g.set(k,A),qe.set(k,A),Pn.set(k,A.segmentation),A.angle&&An.set(k,A.angle)}t!=null&&t.patientMrn&&f.results&&await Ro(e,new Map([...g.entries()]),t);const N=Date.now()-h;return console.log(`[cvAnalyzeApi] Celery batch completed jobId=${d} frames=${e.length} totalMs=${N}`),g}if(f.status==="failed"){const g=Date.now()-h;throw console.error(`[cvAnalyzeApi] Celery batch failed jobId=${d} frames=${e.length} totalMs=${g}`),new Error(((p=f.errors)==null?void 0:p.join("; "))??"Celery batch job failed")}if(f.status==="unknown"){const g=Date.now()-h;throw console.error(`[cvAnalyzeApi] Celery batch unknown jobId=${d} frames=${e.length} totalMs=${g}`),new Error(f.detail??`Celery job ${d} not found`)}await new Promise(g=>setTimeout(g,u))}})();ct.set(o,c);try{return await c}finally{ct.delete(o)}}function $l(e,t,n,a){var i;if(!e)return n.setOverlaySrc(null),n.setInterpretation(null),n.setError(wn(new Error("Không có kết quả phân đoạn cho khung này"),{operation:"segmentation",frameId:a})),!1;if(!((i=e.raw.segmentation)!=null&&i.performed))return n.setOverlaySrc(null),n.setInterpretation(null),n.setError(null),!0;let o=e.interpretation;if(!o&&e.overlaySrc){const l={angle_type:e.raw.segmentation.angle_type??(t?jr(t):"sup"),classes_detected:e.raw.segmentation.classes_detected??[],color_legend:e.raw.segmentation.color_legend??{},measurement:e.raw.measurement};o=Lo(l,t)}return!o||!e.overlaySrc?(n.setOverlaySrc(null),n.setInterpretation(null),n.setError(wn(new Error("Phân đoạn không hoàn tất — thiếu overlay"),{operation:"segmentation",frameId:a})),!1):(n.setOverlaySrc(e.overlaySrc),n.setInterpretation(o),n.setError(null),!0)}function Bl(e,t,n,a,o){const[i,l]=s.useState(null),[c,d]=s.useState(null),[u,h]=s.useState(null),[p,f]=s.useState(null),[g,N]=s.useState(null),[y,k]=s.useState(!1),[A,I]=s.useState(!1),[w,L]=s.useState(null),[z,X]=s.useState(null),[R,ie]=s.useState(0),U=()=>{Al(),Ml(),Ll(),a&&Pi(a),ie(B=>B+1)},v=s.useRef(void 0);return s.useEffect(()=>{let B=!1;v.current=void 0;const re={setOverlaySrc:l,setInterpretation:d,setError:L};function V(S){var P;S&&(v.current=S.segmentation,h(S.angle),$l(S.segmentation,(P=S.angle)==null?void 0:P.class,re,e),ne(S.segmentation),F(S.segmentation))}function ne(S){const P=cl(S==null?void 0:S.raw.inflammation);f(P)}function F(S){N(dl(S==null?void 0:S.raw.severity))}async function _(S){k(!0),I(!0);try{const P=a?{patientMrn:a}:void 0;let j;if(o?j=await Fl(S,P):j=await Dl(S,P),B)return;const ee=j.get(e);if(!ee){l(null),d(null),h(null),f(null),N(null),L(wn(new Error("Không có kết quả phân tích cho khung này"),{operation:"segmentation",frameId:e,patientMrn:a}));return}V(ee)}catch(P){B||(l(null),d(null),h(null),f(null),N(null),L(wn(P,{operation:"segmentation",frameId:e,patientMrn:a})))}finally{B||(k(!1),I(!1))}}async function O(){const P=(n??[{id:e,src:t,label:""}]).map(ee=>({id:ee.id,src:ee.src})),j=Pl(e);if(j||(l(null),d(null),f(null),N(null),h(null)),L(null),X("backend"),j){V(j);return}await _(P)}return O(),()=>{B=!0}},[e,t,n,a,R,o]),{overlaySrc:i,interpretation:c,angleClassification:u,inflammationClassification:p,synovitisSeverity:g,isLoading:y,isSegmentationLoading:y,isAngleLoading:A,error:w,source:z,retry:U}}const mt=12,Do="msk-annotation-ribbon-position";function Hl(){try{const e=localStorage.getItem(Do);if(!e)return null;const t=JSON.parse(e);if(typeof t.x=="number"&&typeof t.y=="number")return t}catch{}return null}function or(e,t,n){const a=Math.max(mt,t.clientWidth-n.offsetWidth-mt),o=Math.max(mt,t.clientHeight-n.offsetHeight-mt);return{x:Math.min(a,Math.max(mt,e.x)),y:Math.min(o,Math.max(mt,e.y))}}function ba(e,t){return or({x:(e.clientWidth-t.offsetWidth)/2,y:e.clientHeight-t.offsetHeight-16},e,t)}function Gl(e){const t=s.useRef(null),[n,a]=s.useState(()=>Hl()),[o,i]=s.useState(!1),l=s.useRef(null),c=s.useCallback(()=>{const p=e.current,f=t.current;!p||!f||f.offsetWidth===0||a(g=>or(g??ba(p,f),p,f))},[e]);s.useLayoutEffect(()=>{c()},[c]),s.useEffect(()=>{const p=e.current;if(!p)return;const f=new ResizeObserver(()=>c());return f.observe(p),()=>f.disconnect()},[e,c]),s.useEffect(()=>{if(n)try{localStorage.setItem(Do,JSON.stringify(n))}catch{}},[n]);const d=s.useCallback(p=>{if(p.button!==0)return;p.stopPropagation(),p.preventDefault();const f=e.current,g=t.current;if(!f||!g)return;const N=n??ba(f,g);l.current={startX:p.clientX,startY:p.clientY,originX:N.x,originY:N.y},i(!0),p.currentTarget.setPointerCapture(p.pointerId)},[e,n]),u=s.useCallback(p=>{if(!l.current)return;const f=e.current,g=t.current;if(!f||!g)return;const N=p.clientX-l.current.startX,y=p.clientY-l.current.startY,k=or({x:l.current.originX+N,y:l.current.originY+y},f,g);a(k)},[e]),h=s.useCallback(p=>{l.current=null,i(!1),p.currentTarget.releasePointerCapture(p.pointerId)},[]);return{panelRef:t,position:n,isDragging:o,dragHandleProps:{onPointerDown:d,onPointerMove:u,onPointerUp:h,onPointerCancel:h}}}function Wl({size:e=18}){return r.jsxs("svg",{width:e,height:e,viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg","aria-hidden":!0,children:[r.jsx("path",{d:"M5.25 12.75C3.75 11.25 3.25 9.25 3.75 7.25C4.5 5 6.75 3.5 9.5 3.75C12.25 4 14.25 5.75 14.75 8.25C15.25 10.75 14.25 13 12.5 14.25M11.75 13.5C12.15 13 12.4 12.4 12.5 11.75",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"}),r.jsx("circle",{cx:"6.75",cy:"7.75",r:"1.05",fill:"currentColor"}),r.jsx("circle",{cx:"9.25",cy:"6.5",r:"1.05",fill:"currentColor"}),r.jsx("circle",{cx:"11.75",cy:"6.5",r:"1.05",fill:"currentColor"}),r.jsx("circle",{cx:"14.25",cy:"7.75",r:"1.05",fill:"currentColor"})]})}function Ul({inkColor:e,onInkColorChange:t,disabled:n}){const[a,o]=s.useState(!1),i=s.useRef(null),l=s.useId();s.useEffect(()=>{if(!a)return;const d=h=>{var p;(p=i.current)!=null&&p.contains(h.target)||o(!1)},u=h=>{h.key==="Escape"&&o(!1)};return document.addEventListener("pointerdown",d),document.addEventListener("keydown",u),()=>{document.removeEventListener("pointerdown",d),document.removeEventListener("keydown",u)}},[a]);const c=d=>{t(d),o(!1)};return r.jsxs("div",{ref:i,className:"annotation-color-picker",onPointerDown:d=>d.stopPropagation(),children:[r.jsxs("button",{type:"button",className:a?"annotation-color-picker__trigger annotation-color-picker__trigger--open":"annotation-color-picker__trigger",onClick:()=>o(d=>!d),disabled:n,"aria-haspopup":"listbox","aria-expanded":a,"aria-controls":l,title:"Bảng màu bút vẽ",children:[r.jsx("span",{className:"annotation-color-picker__palette-icon","aria-hidden":!0,children:r.jsx(Wl,{size:18})}),r.jsx("span",{className:"annotation-color-picker__ink-dot",style:{backgroundColor:e},"aria-hidden":!0}),r.jsx("span",{className:"annotation-color-picker__label",children:"Màu"})]}),a&&r.jsxs("div",{id:l,className:"annotation-color-picker__popover glass",role:"listbox","aria-label":"Chọn màu mực bút vẽ",children:[r.jsx("p",{className:"annotation-color-picker__heading",children:"Màu bút vẽ"}),r.jsx("div",{className:"annotation-color-picker__grid",children:fo.map(d=>r.jsx(Vl,{option:d,selected:e.toLowerCase()===d.hex.toLowerCase(),onSelect:()=>c(d.hex)},d.id))})]}),r.jsx("style",{children:Kl})]})}function Vl({option:e,selected:t,onSelect:n}){const a=e.contrastOnDark||e.contrastOnLight;return r.jsxs("button",{type:"button",role:"option","aria-selected":t,"aria-label":e.labelVi,title:`${e.labelVi} (${e.hex})`,className:t?"annotation-color-picker__swatch annotation-color-picker__swatch--selected":"annotation-color-picker__swatch",onClick:n,children:[r.jsx("span",{className:a?"annotation-color-picker__swatch-fill annotation-color-picker__swatch-fill--contrast":"annotation-color-picker__swatch-fill",style:{backgroundColor:e.hex}}),t&&r.jsx("span",{className:"annotation-color-picker__check","aria-hidden":!0,children:r.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3",children:r.jsx("path",{d:"M5 13l4 4L19 7"})})})]})}const Kl=` - .annotation-color-picker { - position: relative; - display: flex; - align-items: center; - } - .annotation-color-picker__trigger { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 8px 12px; - border: none; - border-radius: 999px; - background: transparent; - color: rgba(255, 255, 255, 0.88); - font-size: 12px; - font-weight: 600; - cursor: pointer; - transition: background 0.15s; - white-space: nowrap; - } - .annotation-color-picker__trigger:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.12); - } - .annotation-color-picker__trigger--open { - background: rgba(35, 104, 108, 0.85); - color: #fff; - } - .annotation-color-picker__trigger:disabled { - opacity: 0.4; - cursor: not-allowed; - } - .annotation-color-picker__palette-icon { - display: flex; - align-items: center; - justify-content: center; - } - .annotation-color-picker__ink-dot { - width: 14px; - height: 14px; - border-radius: 999px; - border: 2px solid rgba(255, 255, 255, 0.85); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35); - flex-shrink: 0; - } - .annotation-color-picker__label { - line-height: 1; - } - .annotation-color-picker__popover { - position: absolute; - left: 50%; - bottom: calc(100% + 10px); - transform: translateX(-50%); - z-index: 40; - min-width: 188px; - padding: 12px; - border-radius: 14px; - border: 1px solid rgba(173, 238, 242, 0.35); - box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45); - background: rgba(23, 29, 28, 0.94); - backdrop-filter: blur(12px); - } - .annotation-color-picker__heading { - margin: 0 0 10px; - font-size: 11px; - font-weight: 700; - letter-spacing: 0.04em; - text-transform: uppercase; - color: rgba(173, 238, 242, 0.9); - } - .annotation-color-picker__grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 8px; - } - .annotation-color-picker__swatch { - position: relative; - width: 36px; - height: 36px; - padding: 0; - border: none; - border-radius: 999px; - background: transparent; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: transform 0.12s; - } - .annotation-color-picker__swatch:hover { - transform: scale(1.08); - } - .annotation-color-picker__swatch--selected { - outline: 2px solid #adeef2; - outline-offset: 2px; - } - .annotation-color-picker__swatch-fill { - width: 28px; - height: 28px; - border-radius: 999px; - display: block; - } - .annotation-color-picker__swatch-fill--contrast { - border: 1.5px solid rgba(255, 255, 255, 0.55); - box-shadow: inset 0 0 0 1px rgba(23, 29, 28, 0.25); - } - .annotation-color-picker__check { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.65)); - pointer-events: none; - } - @media (max-width: 640px) { - .annotation-color-picker__label { - display: none; - } - .annotation-color-picker__trigger { - padding: 10px; - } - } -`;function bt({children:e}){return r.jsx("span",{className:"annotation-ribbon__icon","aria-hidden":!0,children:e})}function ql({containerRef:e,tool:t,onToolChange:n,inkColor:a,onInkColorChange:o,zoom:i,onZoomIn:l,onZoomOut:c,onResetView:d,onClearAnnotations:u,hasAnnotations:h,disabled:p}){const f=Math.round(i*100),{panelRef:g,position:N,isDragging:y,dragHandleProps:k}=Gl(e),A=N!=null?{left:N.x,top:N.y,transform:"none"}:{left:"50%",bottom:16,top:"auto",transform:"translateX(-50%)"};return r.jsxs("div",{ref:g,className:`annotation-ribbon glass ${p?"annotation-ribbon--disabled":""} ${y?"annotation-ribbon--dragging":""}`,style:A,role:"toolbar","aria-label":"Công cụ chú thích khung hình",onPointerDown:I=>I.stopPropagation(),children:[r.jsx("button",{type:"button",className:"annotation-ribbon__drag-handle","aria-label":"Kéo để di chuyển thanh công cụ",title:"Kéo để di chuyển",disabled:p,...k,children:r.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:[r.jsx("circle",{cx:"9",cy:"7",r:"1.5"}),r.jsx("circle",{cx:"15",cy:"7",r:"1.5"}),r.jsx("circle",{cx:"9",cy:"12",r:"1.5"}),r.jsx("circle",{cx:"15",cy:"12",r:"1.5"}),r.jsx("circle",{cx:"9",cy:"17",r:"1.5"}),r.jsx("circle",{cx:"15",cy:"17",r:"1.5"})]})}),r.jsx("span",{className:"annotation-ribbon__divider","aria-hidden":!0}),r.jsxs("div",{className:"annotation-ribbon__group",role:"group","aria-label":"Vẽ và tẩy",children:[r.jsxs("button",{type:"button",className:t==="draw"?"annotation-ribbon__btn annotation-ribbon__btn--active":"annotation-ribbon__btn",onClick:()=>n("draw"),disabled:p,"aria-pressed":t==="draw",title:"Vẽ — chuột hoặc cảm ứng",children:[r.jsx(bt,{children:r.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("path",{d:"M4 20h4l10.5-10.5a2.1 2.1 0 0 0-3-3L5 17v3z"}),r.jsx("path",{d:"m13.5 6.5 3 3"})]})}),r.jsx("span",{className:"annotation-ribbon__label",children:"Vẽ"})]}),r.jsxs("button",{type:"button",className:t==="eraser"?"annotation-ribbon__btn annotation-ribbon__btn--active":"annotation-ribbon__btn",onClick:()=>n("eraser"),disabled:p,"aria-pressed":t==="eraser",title:"Tẩy nét vẽ",children:[r.jsx(bt,{children:r.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("path",{d:"M20 20H8l-6.5-6.5a2.1 2.1 0 0 1 0-3L14 4.5a2.1 2.1 0 0 1 3 0L20 7.5"}),r.jsx("path",{d:"m6.5 13.5 6 6"})]})}),r.jsx("span",{className:"annotation-ribbon__label",children:"Tẩy"})]}),r.jsx(Ul,{inkColor:a,onInkColorChange:o,disabled:p})]}),r.jsx("span",{className:"annotation-ribbon__divider","aria-hidden":!0}),r.jsx("div",{className:"annotation-ribbon__group",role:"group","aria-label":"Di chuyển khung",children:r.jsxs("button",{type:"button",className:t==="pan"?"annotation-ribbon__btn annotation-ribbon__btn--active":"annotation-ribbon__btn",onClick:()=>n("pan"),disabled:p,"aria-pressed":t==="pan",title:"Di chuyển — bật để kéo hai ngón (trackpad/cảm ứng) hoặc kéo chuột",children:[r.jsx(bt,{children:r.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:r.jsx("path",{d:"M18 11V5a2 2 0 0 0-4 0v4M14 11V4a2 2 0 0 0-4 0v7M10 11V5a2 2 0 0 0-4 0v8a8 8 0 0 0 16 0v-5a2 2 0 0 0-4 0"})})}),r.jsx("span",{className:"annotation-ribbon__label",children:"Di chuyển"})]})}),r.jsx("span",{className:"annotation-ribbon__divider","aria-hidden":!0}),r.jsxs("div",{className:"annotation-ribbon__group",role:"group","aria-label":"Phóng to thu nhỏ",children:[r.jsx("button",{type:"button",className:"annotation-ribbon__btn annotation-ribbon__btn--compact",onClick:c,disabled:p||i<=1,title:"Thu nhỏ","aria-label":"Thu nhỏ",children:r.jsx(bt,{children:r.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("circle",{cx:"11",cy:"11",r:"7"}),r.jsx("path",{d:"M8 11h6M21 21l-4.35-4.35"})]})})}),r.jsxs("button",{type:"button",className:"annotation-ribbon__zoom-readout tnum",onClick:d,disabled:p,title:"Đặt lại vừa khung",children:[f,"%"]}),r.jsx("button",{type:"button",className:"annotation-ribbon__btn annotation-ribbon__btn--compact",onClick:l,disabled:p||i>=4,title:"Phóng to","aria-label":"Phóng to",children:r.jsx(bt,{children:r.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("circle",{cx:"11",cy:"11",r:"7"}),r.jsx("path",{d:"M11 8v6M8 11h6M21 21l-4.35-4.35"})]})})})]}),h&&r.jsxs(r.Fragment,{children:[r.jsx("span",{className:"annotation-ribbon__divider","aria-hidden":!0}),r.jsxs("button",{type:"button",className:"annotation-ribbon__btn annotation-ribbon__btn--ghost",onClick:u,disabled:p,title:"Xóa tất cả nét vẽ trên khung này",children:[r.jsx(bt,{children:r.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:r.jsx("path",{d:"M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14"})})}),r.jsx("span",{className:"annotation-ribbon__label",children:"Xóa nét"})]})]}),r.jsx("style",{children:Xl})]})}const Xl=` - .annotation-ribbon { - position: absolute; - z-index: 30; - pointer-events: auto; - display: flex; - align-items: center; - gap: 4px; - padding: 6px 8px; - border-radius: 999px; - box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35); - border: 1px solid rgba(173, 238, 242, 0.35); - max-width: calc(100% - 24px); - flex-wrap: wrap; - justify-content: center; - touch-action: none; - user-select: none; - } - .annotation-ribbon--dragging { - cursor: grabbing; - box-shadow: 0 12px 36px rgba(0, 0, 0, 0.5); - } - .annotation-ribbon--disabled { - opacity: 0.45; - pointer-events: none; - } - .annotation-ribbon__drag-handle { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - padding: 0; - border: none; - border-radius: 999px; - background: transparent; - color: rgba(255, 255, 255, 0.55); - cursor: grab; - flex-shrink: 0; - touch-action: none; - } - .annotation-ribbon__drag-handle:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.1); - color: rgba(255, 255, 255, 0.9); - } - .annotation-ribbon--dragging .annotation-ribbon__drag-handle { - cursor: grabbing; - color: #adeef2; - } - .annotation-ribbon__group { - display: flex; - align-items: center; - gap: 2px; - } - .annotation-ribbon__divider { - width: 1px; - height: 28px; - background: rgba(255, 255, 255, 0.2); - margin: 0 4px; - flex-shrink: 0; - } - .annotation-ribbon__btn { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 8px 12px; - border: none; - border-radius: 999px; - background: transparent; - color: rgba(255, 255, 255, 0.88); - font-size: 12px; - font-weight: 600; - cursor: pointer; - transition: background 0.15s, color 0.15s; - white-space: nowrap; - } - .annotation-ribbon__btn--compact { - padding: 8px 10px; - } - .annotation-ribbon__btn:hover:not(:disabled) { - background: rgba(255, 255, 255, 0.12); - } - .annotation-ribbon__btn--active { - background: rgba(35, 104, 108, 0.85); - color: #fff; - } - .annotation-ribbon__btn--ghost { - color: rgba(255, 200, 200, 0.95); - } - .annotation-ribbon__btn:disabled { - opacity: 0.4; - cursor: not-allowed; - } - .annotation-ribbon__icon { - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - } - .annotation-ribbon__label { - line-height: 1; - } - .annotation-ribbon__zoom-readout { - min-width: 3.25rem; - padding: 8px 6px; - border: none; - border-radius: 999px; - background: rgba(0, 0, 0, 0.35); - color: #adeef2; - font-size: 12px; - font-weight: 700; - cursor: pointer; - } - .annotation-ribbon__zoom-readout:hover:not(:disabled) { - background: rgba(0, 0, 0, 0.5); - } - @media (max-width: 640px) { - .annotation-ribbon__label { - display: none; - } - .annotation-ribbon__btn { - padding: 10px; - } - } -`;function Yl({anchor:e,onConfirm:t,onDismiss:n}){const a=s.useRef(null);return s.useEffect(()=>{const o=l=>{var c;(c=a.current)!=null&&c.contains(l.target)||n()},i=l=>{l.key==="Escape"&&n()};return document.addEventListener("pointerdown",o),document.addEventListener("keydown",i),()=>{document.removeEventListener("pointerdown",o),document.removeEventListener("keydown",i)}},[n]),r.jsxs("div",{ref:a,className:"closed-loop-prompt glass",style:{left:e.x,top:e.y-20},role:"dialog","aria-live":"polite","aria-label":"Xác nhận vùng đánh dấu",onPointerDown:o=>o.stopPropagation(),children:[r.jsx("p",{className:"closed-loop-prompt__label",children:"Bạn có muốn tạo vùng đánh dấu trên khu vực này không?"}),r.jsxs("div",{className:"closed-loop-prompt__actions",children:[r.jsx("button",{type:"button",className:"closed-loop-prompt__btn closed-loop-prompt__btn--confirm",onClick:t,"aria-label":"Xác nhận tạo vùng đánh dấu",title:"Xác nhận",children:r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3",children:r.jsx("path",{d:"M5 13l4 4L19 7"})})}),r.jsx("button",{type:"button",className:"closed-loop-prompt__btn closed-loop-prompt__btn--dismiss",onClick:n,"aria-label":"Hủy",title:"Hủy",children:r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"3",children:r.jsx("path",{d:"M6 6l12 12M18 6L6 18"})})})]}),r.jsx("style",{children:Jl})]})}const Jl=` - .closed-loop-prompt { - position: absolute; - z-index: 1000; - transform: translate(-50%, -100%); - max-width: min(280px, calc(100% - 24px)); - padding: 12px 14px; - border-radius: 12px; - border: 1px solid rgba(173, 238, 242, 0.4); - box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45); - background: rgba(23, 29, 28, 0.95); - backdrop-filter: blur(12px); - pointer-events: auto; - } - .closed-loop-prompt__label { - margin: 0 0 10px; - font-size: 13px; - line-height: 1.45; - font-weight: 600; - color: #fff; - } - .closed-loop-prompt__actions { - display: flex; - gap: 8px; - justify-content: flex-end; - } - .closed-loop-prompt__btn { - width: 34px; - height: 34px; - border: none; - border-radius: 999px; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: background 0.15s, transform 0.12s; - } - .closed-loop-prompt__btn:hover { - transform: scale(1.06); - } - .closed-loop-prompt__btn--confirm { - background: rgba(35, 104, 108, 0.9); - color: #fff; - } - .closed-loop-prompt__btn--confirm:hover { - background: rgba(35, 104, 108, 1); - } - .closed-loop-prompt__btn--dismiss { - background: rgba(255, 255, 255, 0.12); - color: rgba(255, 255, 255, 0.9); - } - .closed-loop-prompt__btn--dismiss:hover { - background: rgba(255, 255, 255, 0.2); - } -`,kn=12,Zl={width:240,height:340},rn={width:200,height:220},_a=14,Ql=["nw","ne","sw","se"];function Oo(e,t){const n=Math.max(rn.width,t.clientWidth-kn*2),a=Math.max(rn.height,t.clientHeight-kn*2);return{width:Math.min(Math.max(e.width,rn.width),n),height:Math.min(Math.max(e.height,rn.height),a)}}function fn(e,t){const n=Oo(e.size,t),a=Math.max(0,t.clientWidth-n.width),o=Math.max(0,t.clientHeight-n.height);return{size:n,position:{x:Math.min(Math.max(0,e.position.x),a),y:Math.min(Math.max(0,e.position.y),o)}}}function ec(e){const t=Oo(Zl,e);return fn({position:{x:e.clientWidth-t.width-kn,y:kn},size:t},e)}function tc({kind:e}){return r.jsx("span",{className:`seg-legend-bubble__note-indicator seg-legend-bubble__note-indicator--${e}`,"aria-hidden":!0})}function xa({note:e}){return r.jsxs("p",{className:"seg-legend-bubble__note",children:[e.indicator&&r.jsx(tc,{kind:e.indicator}),r.jsxs("span",{className:"seg-legend-bubble__note-copy",children:[r.jsx("span",{className:"seg-legend-bubble__note-vi",children:e.textVi}),r.jsx("span",{className:"seg-legend-bubble__note-en",children:e.textEn})]})]})}function nc({visible:e,containerRef:t,angleType:n="sup",entries:a,showMeasurementNote:o=!1}){const i=s.useRef(null),l=s.useRef({x:0,y:0}),c=s.useRef(null),[d,u]=s.useState(null),[h,p]=s.useState(!1),[f,g]=s.useState(!1),[N,y]=s.useState(!1),k=a??Io(n),A=v=>!v.supOnly||n==="sup",I=fa.filter(v=>A(v)&&!v.measurementOnly),w=o?fa.filter(v=>A(v)&&v.measurementOnly):[],L=I.length+w.length,z=s.useCallback(()=>{const v=t.current;v&&(u(B=>fn(B??ec(v),v)),y(!0))},[t]);s.useEffect(()=>{if(!e){y(!1);return}const v=requestAnimationFrame(()=>z());return()=>cancelAnimationFrame(v)},[e,z,k.length,L]),s.useEffect(()=>{if(!e)return;const v=()=>z();return window.addEventListener("resize",v),()=>window.removeEventListener("resize",v)},[e,z]);const X=v=>{const B=t.current,re=i.current;if(!B||!re||!d||v.button!==0||f)return;const V=re.getBoundingClientRect();l.current={x:v.clientX-V.left,y:v.clientY-V.top},p(!0),v.currentTarget.setPointerCapture(v.pointerId),v.preventDefault()},R=v=>{if(!h)return;const B=t.current;if(!B)return;const re=B.getBoundingClientRect();u(V=>V&&fn({...V,position:{x:v.clientX-re.left-l.current.x,y:v.clientY-re.top-l.current.y}},B))},ie=v=>{h&&(p(!1),v.currentTarget.hasPointerCapture(v.pointerId)&&v.currentTarget.releasePointerCapture(v.pointerId))},U=v=>B=>{!d||B.button!==0||(c.current={corner:v,pointerId:B.pointerId,startPointer:{x:B.clientX,y:B.clientY},startLayout:d},g(!0),B.currentTarget.setPointerCapture(B.pointerId),B.stopPropagation(),B.preventDefault())};return s.useEffect(()=>{if(!f)return;const v=re=>{const V=c.current,ne=t.current;if(!V||!ne||re.pointerId!==V.pointerId)return;const F=re.clientX-V.startPointer.x,_=re.clientY-V.startPointer.y,{startLayout:O}=V;let S={position:{...O.position},size:{...O.size}};switch(V.corner){case"se":S.size.width=O.size.width+F,S.size.height=O.size.height+_;break;case"sw":S.size.width=O.size.width-F,S.size.height=O.size.height+_,S.position.x=O.position.x+F;break;case"ne":S.size.width=O.size.width+F,S.size.height=O.size.height-_,S.position.y=O.position.y+_;break;case"nw":S.size.width=O.size.width-F,S.size.height=O.size.height-_,S.position.x=O.position.x+F,S.position.y=O.position.y+_;break}u(fn(S,ne))},B=re=>{const V=c.current;!V||re.pointerId!==V.pointerId||(c.current=null,g(!1))};return window.addEventListener("pointermove",v),window.addEventListener("pointerup",B),window.addEventListener("pointercancel",B),()=>{window.removeEventListener("pointermove",v),window.removeEventListener("pointerup",B),window.removeEventListener("pointercancel",B)}},[t,f]),e?r.jsxs("aside",{ref:i,className:`seg-legend-bubble glass-elevated${h?" seg-legend-bubble--dragging":""}${f?" seg-legend-bubble--resizing":""}${N?" seg-legend-bubble--ready":""}`,style:d?{left:`${d.position.x}px`,top:`${d.position.y}px`,width:`${d.size.width}px`,height:`${d.size.height}px`}:void 0,"aria-label":"Chú thích phân đoạn",children:[Ql.map(v=>r.jsx("button",{type:"button",className:`seg-legend-bubble__resize-handle seg-legend-bubble__resize-handle--${v}`,"aria-label":`Đổi kích thước góc ${v}`,onPointerDown:U(v)},v)),r.jsxs("header",{className:"seg-legend-bubble__header",onPointerDown:X,onPointerMove:R,onPointerUp:ie,onPointerCancel:ie,"aria-grabbed":h,children:[r.jsx("span",{className:"seg-legend-bubble__drag-handle","aria-hidden":!0,children:"⋮⋮"}),r.jsxs("div",{className:"seg-legend-bubble__header-copy",children:[r.jsx("span",{className:"seg-legend-bubble__eyebrow",children:"Phân đoạn AI"}),r.jsx("h3",{className:"seg-legend-bubble__title",children:"Chú thích màu"})]})]}),r.jsxs("div",{className:"seg-legend-bubble__body",children:[r.jsx("ul",{className:"seg-legend-bubble__list",role:"list",children:k.map(v=>r.jsxs("li",{className:"seg-legend-bubble__item",children:[r.jsx("span",{className:`seg-legend-bubble__swatch${v.highlight?" seg-legend-bubble__swatch--highlight":""}`,style:{backgroundColor:Sl(v.color)},"aria-hidden":!0}),r.jsxs("div",{className:"seg-legend-bubble__copy",children:[r.jsx("span",{className:"seg-legend-bubble__label-vi",children:v.nameVi}),r.jsx("span",{className:"seg-legend-bubble__label-en",children:v.nameEn})]})]},v.key))}),r.jsxs("div",{className:"seg-legend-bubble__notes",children:[I.map(v=>r.jsx(xa,{note:v},v.id)),w.length>0&&r.jsxs(r.Fragment,{children:[r.jsx("div",{className:"seg-legend-bubble__notes-divider",role:"separator","aria-hidden":!0}),w.map(v=>r.jsx(xa,{note:v},v.id))]})]})]}),r.jsx("style",{children:` - .seg-legend-bubble { - position: absolute; - display: flex; - flex-direction: column; - border-radius: var(--radius-lg); - overflow: hidden; - z-index: 20; - pointer-events: auto; - opacity: 0; - user-select: none; - } - - .seg-legend-bubble--ready { - opacity: 1; - } - - .seg-legend-bubble--dragging, - .seg-legend-bubble--resizing { - box-shadow: 0 20px 48px rgba(77, 141, 145, 0.2); - } - - .seg-legend-bubble--dragging { - cursor: grabbing; - } - - .seg-legend-bubble__resize-handle { - position: absolute; - width: ${_a}px; - height: ${_a}px; - padding: 0; - border: none; - background: transparent; - opacity: 0; - z-index: 2; - transition: opacity 0.15s ease; - } - - .seg-legend-bubble:hover .seg-legend-bubble__resize-handle, - .seg-legend-bubble--resizing .seg-legend-bubble__resize-handle, - .seg-legend-bubble__resize-handle:focus-visible { - opacity: 1; - } - - .seg-legend-bubble__resize-handle::after { - content: ''; - position: absolute; - width: 8px; - height: 8px; - border: 2px solid var(--color-secondary); - border-radius: 2px; - background: rgba(255, 255, 255, 0.92); - box-shadow: 0 1px 4px rgba(23, 29, 28, 0.18); - } - - .seg-legend-bubble__resize-handle--nw { - top: -4px; - left: -4px; - cursor: nwse-resize; - } - - .seg-legend-bubble__resize-handle--nw::after { - top: 3px; - left: 3px; - } - - .seg-legend-bubble__resize-handle--ne { - top: -4px; - right: -4px; - cursor: nesw-resize; - } - - .seg-legend-bubble__resize-handle--ne::after { - top: 3px; - right: 3px; - } - - .seg-legend-bubble__resize-handle--sw { - bottom: -4px; - left: -4px; - cursor: nesw-resize; - } - - .seg-legend-bubble__resize-handle--sw::after { - bottom: 3px; - left: 3px; - } - - .seg-legend-bubble__resize-handle--se { - bottom: -4px; - right: -4px; - cursor: nwse-resize; - } - - .seg-legend-bubble__resize-handle--se::after { - bottom: 3px; - right: 3px; - } - - .seg-legend-bubble__header { - display: flex; - align-items: flex-start; - gap: 8px; - padding: 10px 12px 8px; - border-bottom: 1px solid var(--color-outline-variant); - flex-shrink: 0; - cursor: grab; - touch-action: none; - } - - .seg-legend-bubble--dragging .seg-legend-bubble__header { - cursor: grabbing; - } - - .seg-legend-bubble__drag-handle { - font-size: 12px; - line-height: 1; - letter-spacing: -2px; - color: var(--color-outline); - padding-top: 2px; - flex-shrink: 0; - } - - .seg-legend-bubble__header-copy { - min-width: 0; - } - - .seg-legend-bubble__eyebrow { - display: block; - font-size: 10px; - font-weight: 600; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--color-secondary); - margin-bottom: 2px; - } - - .seg-legend-bubble__title { - font-size: 13px; - font-weight: 600; - color: var(--color-on-surface); - line-height: 1.3; - } - - .seg-legend-bubble__body { - flex: 1; - min-height: 0; - overflow-y: auto; - touch-action: pan-y; - scrollbar-width: thin; - scrollbar-color: rgba(35, 104, 108, 0.42) transparent; - } - - .seg-legend-bubble__body::-webkit-scrollbar { - width: 6px; - } - - .seg-legend-bubble__body::-webkit-scrollbar-track { - background: transparent; - } - - .seg-legend-bubble__body::-webkit-scrollbar-thumb { - background-color: rgba(35, 104, 108, 0.38); - border-radius: 999px; - border: 1px solid transparent; - background-clip: padding-box; - } - - .seg-legend-bubble__body::-webkit-scrollbar-thumb:hover { - background-color: rgba(35, 104, 108, 0.58); - } - - .seg-legend-bubble__list { - list-style: none; - margin: 0; - padding: 6px 4px 0 0; - } - - .seg-legend-bubble__item { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 7px 12px; - } - - .seg-legend-bubble__swatch { - width: 14px; - height: 14px; - border-radius: 4px; - flex-shrink: 0; - margin-top: 2px; - border: 1px solid rgba(23, 29, 28, 0.12); - box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35); - } - - .seg-legend-bubble__swatch--highlight { - box-shadow: - 0 0 0 2px #fff, - 0 0 0 3px rgba(23, 29, 28, 0.25); - } - - .seg-legend-bubble__copy { - display: flex; - flex-direction: column; - gap: 1px; - min-width: 0; - flex: 1; - } - - .seg-legend-bubble__label-vi, - .seg-legend-bubble__label-en, - .seg-legend-bubble__note, - .seg-legend-bubble__note-en { - overflow-wrap: anywhere; - word-break: normal; - } - - .seg-legend-bubble__label-vi { - font-size: 12px; - font-weight: 600; - color: var(--color-on-surface); - line-height: 1.25; - } - - .seg-legend-bubble__label-en { - font-size: 11px; - color: var(--color-on-surface-variant); - line-height: 1.25; - } - - .seg-legend-bubble__notes { - padding: 10px 12px 12px; - border-top: 1px solid rgba(23, 29, 28, 0.14); - background: rgba(255, 255, 255, 0.88); - display: flex; - flex-direction: column; - gap: 10px; - } - - .seg-legend-bubble__notes-divider { - height: 1px; - margin: 0; - background: rgba(23, 29, 28, 0.16); - flex-shrink: 0; - } - - .seg-legend-bubble__note { - margin: 0; - font-size: 11px; - line-height: 1.4; - color: var(--color-on-surface); - display: flex; - align-items: flex-start; - gap: 10px; - } - - .seg-legend-bubble__note-copy { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; - flex: 1; - } - - .seg-legend-bubble__note-indicator { - width: 14px; - height: 14px; - flex-shrink: 0; - margin-top: 2px; - border-radius: 2px; - position: relative; - } - - .seg-legend-bubble__note-indicator--bbox-green { - border: 2px solid rgb(0, 255, 0); - background: transparent; - box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35); - } - - .seg-legend-bubble__note-indicator--outline-white { - border: 3px solid rgb(255, 255, 255); - background: rgba(255, 0, 0, 0.35); - box-shadow: 0 0 0 1px rgba(23, 29, 28, 0.2); - } - - .seg-legend-bubble__note-indicator--vline-cyan, - .seg-legend-bubble__note-indicator--vline-red { - background: transparent; - } - - .seg-legend-bubble__note-indicator--vline-cyan::before, - .seg-legend-bubble__note-indicator--vline-cyan::after, - .seg-legend-bubble__note-indicator--vline-red::before { - content: ''; - position: absolute; - top: 1px; - bottom: 1px; - width: 2px; - border-radius: 1px; - } - - .seg-legend-bubble__note-indicator--vline-cyan::before { - left: 3px; - background: rgb(0, 255, 255); - } - - .seg-legend-bubble__note-indicator--vline-cyan::after { - right: 3px; - background: rgb(0, 255, 255); - } - - .seg-legend-bubble__note-indicator--vline-red::before { - left: 50%; - transform: translateX(-50%); - background: rgb(255, 0, 0); - width: 3px; - } - - .seg-legend-bubble__note-vi { - font-size: 12px; - font-weight: 600; - color: var(--color-on-surface); - line-height: 1.35; - } - - .seg-legend-bubble__note-en { - font-size: 10px; - font-style: italic; - color: var(--color-on-surface-variant); - line-height: 1.35; - } - `})]}):null}function rc({overlaySrc:e,isLoading:t}){return t&&!e?r.jsxs("div",{className:"segmentation-overlay segmentation-overlay--loading",role:"status","aria-live":"polite","aria-label":"Đang phân đoạn AI",children:[r.jsxs("div",{className:"segmentation-overlay__loading-card",children:[r.jsx("span",{className:"segmentation-overlay__spinner","aria-hidden":!0}),r.jsx("p",{className:"segmentation-overlay__loading-title",children:"Đang phân đoạn AI…"}),r.jsx("p",{className:"segmentation-overlay__loading-subtitle",children:"AI segmentation in progress…"})]}),r.jsx("style",{children:` - .segmentation-overlay--loading { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - padding: 24px; - background: rgba(10, 15, 14, 0.58); - pointer-events: none; - z-index: 12; - } - - .segmentation-overlay__loading-card { - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - max-width: min(92%, 360px); - padding: 20px 24px; - border-radius: var(--radius-lg); - border: 1px solid rgba(173, 238, 242, 0.35); - background: rgba(23, 29, 28, 0.88); - box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35); - text-align: center; - } - - .segmentation-overlay__spinner { - width: 28px; - height: 28px; - border: 3px solid rgba(173, 238, 242, 0.28); - border-top-color: var(--color-secondary-container); - border-radius: 50%; - animation: segmentation-overlay-spin 0.85s linear infinite; - } - - .segmentation-overlay__loading-title { - margin: 0; - font-size: 18px; - font-weight: 700; - line-height: 1.3; - letter-spacing: 0.01em; - color: #fff; - } - - .segmentation-overlay__loading-subtitle { - margin: 0; - font-size: 13px; - font-weight: 500; - line-height: 1.35; - color: rgba(255, 255, 255, 0.78); - } - - @keyframes segmentation-overlay-spin { - to { - transform: rotate(360deg); - } - } - `})]}):e?r.jsx("img",{src:e,alt:"","aria-hidden":!0,className:"diagnostic-canvas__overlay diagnostic-canvas__overlay--mask"}):null}async function ac(e){var t;if(!e)return!1;if(typeof navigator<"u"&&((t=navigator.clipboard)!=null&&t.writeText))try{return await navigator.clipboard.writeText(e),!0}catch{}if(typeof document>"u")return!1;try{const n=document.createElement("textarea");n.value=e,n.setAttribute("readonly",""),n.style.position="fixed",n.style.top="0",n.style.left="0",n.style.width="1px",n.style.height="1px",n.style.opacity="0",n.style.pointerEvents="none",document.body.appendChild(n),n.focus(),n.select(),n.setSelectionRange(0,e.length);const a=document.execCommand("copy");return document.body.removeChild(n),a}catch{return!1}}function oc({error:e,frameLabel:t,onRetry:n}){const[a,o]=s.useState(!1),[i,l]=s.useState(!1),c=e.code!=="NO_RESULT";async function d(){const u=yl(e,t);if(await ac(u)){l(!1),o(!0),window.setTimeout(()=>o(!1),2500);return}l(!0),window.setTimeout(()=>l(!1),4e3)}return r.jsxs("div",{className:"ml-svc-error",role:"alert","aria-live":"polite",children:[r.jsx("p",{className:"ml-svc-error__title",children:e.title}),r.jsx("p",{className:"ml-svc-error__message",children:e.message}),c&&r.jsxs("div",{className:"ml-svc-error__support",children:[r.jsx("p",{className:"ml-svc-error__ref-label",children:"Mã tham chiếu hỗ trợ"}),r.jsx("code",{className:"ml-svc-error__ref",tabIndex:0,children:e.supportReference}),r.jsx("p",{className:"ml-svc-error__ref-hint",children:"Cung cấp mã này khi gửi ticket cho IT — giúp đội kỹ thuật tra cứu nhanh."}),i&&r.jsx("p",{className:"ml-svc-error__copy-fail",children:"Không sao chép tự động được — hãy chọn và sao chép mã ở trên thủ công."})]}),e.ticketHints.length>0&&r.jsx("ul",{className:"ml-svc-error__hints",children:e.ticketHints.map(u=>r.jsx("li",{children:u},u))}),r.jsxs("div",{className:"ml-svc-error__actions",children:[n&&r.jsx("button",{type:"button",className:"ml-svc-error__btn",onClick:n,children:"Thử tải lại"}),c&&r.jsx("button",{type:"button",className:"ml-svc-error__btn ml-svc-error__btn--secondary",onClick:d,children:a?"Đã sao chép":"Sao chép cho ticket"})]}),c&&r.jsxs("details",{className:"ml-svc-error__details",children:[r.jsx("summary",{children:"Chi tiết kỹ thuật (cho IT)"}),r.jsx("pre",{className:"ml-svc-error__technical",children:e.technicalDetail})]}),r.jsx("style",{children:` - .ml-svc-error { - position: absolute; - inset: 0; - z-index: 14; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 10px; - padding: 24px 20px; - background: rgba(10, 15, 14, 0.78); - color: #f4f7f6; - text-align: center; - pointer-events: auto; - overflow-y: auto; - } - .ml-svc-error__title { - margin: 0; - font-size: 1rem; - font-weight: 600; - line-height: 1.35; - max-width: 22rem; - } - .ml-svc-error__message { - margin: 0; - font-size: 0.875rem; - line-height: 1.5; - max-width: 26rem; - opacity: 0.92; - } - .ml-svc-error__support { - margin-top: 4px; - padding: 10px 14px; - border-radius: var(--radius-md, 8px); - background: rgba(255, 255, 255, 0.08); - max-width: 24rem; - } - .ml-svc-error__ref-label { - margin: 0 0 4px; - font-size: 0.7rem; - text-transform: uppercase; - letter-spacing: 0.04em; - opacity: 0.75; - } - .ml-svc-error__ref { - display: block; - font-size: 1.05rem; - font-weight: 600; - letter-spacing: 0.06em; - user-select: all; - cursor: text; - } - .ml-svc-error__ref-hint { - margin: 6px 0 0; - font-size: 0.72rem; - line-height: 1.4; - opacity: 0.8; - } - .ml-svc-error__copy-fail { - margin: 8px 0 0; - font-size: 0.72rem; - line-height: 1.4; - color: #fcd34d; - } - .ml-svc-error__hints { - margin: 0; - padding-left: 1.1rem; - text-align: left; - font-size: 0.78rem; - line-height: 1.45; - max-width: 24rem; - opacity: 0.88; - } - .ml-svc-error__actions { - display: flex; - flex-wrap: wrap; - gap: 8px; - justify-content: center; - margin-top: 4px; - } - .ml-svc-error__btn { - border: 1px solid rgba(255, 255, 255, 0.35); - background: rgba(255, 255, 255, 0.14); - color: inherit; - border-radius: var(--radius-md, 8px); - padding: 7px 14px; - font-size: 0.8rem; - cursor: pointer; - } - .ml-svc-error__btn:hover { - background: rgba(255, 255, 255, 0.22); - } - .ml-svc-error__btn--secondary { - background: transparent; - } - .ml-svc-error__details { - margin-top: 6px; - max-width: 26rem; - width: 100%; - text-align: left; - font-size: 0.75rem; - } - .ml-svc-error__details summary { - cursor: pointer; - opacity: 0.8; - list-style: none; - } - .ml-svc-error__details summary::-webkit-details-marker { - display: none; - } - .ml-svc-error__technical { - margin: 8px 0 0; - padding: 10px; - border-radius: var(--radius-md, 8px); - background: rgba(0, 0, 0, 0.35); - font-size: 0.68rem; - line-height: 1.45; - white-space: pre-wrap; - word-break: break-word; - overflow-x: auto; - } - `})]})}function ic({showMask:e,onToggleMask:t,showGradcam:n=!1,onToggleGradcam:a,dimmed:o,frameIndex:i,onFrameIndexChange:l,onAngleClassificationChange:c,onInflammationClassificationChange:d,onSynovitisSeverityChange:u,onSegmentationLoadingChange:h,onRegisterSnapshotCapture:p,onReplayViewport:f,segmentOverlayReplay:g,patientMrn:N,patientId:y,scanFrames:k,useCelery:A}){var D;const I=k&&k.length>0?k:y?po(y):Gr,w=I.length>0?I:Gr,[L,z]=s.useState(0),X=i??L,{handleFrameSwitch:R,setRecordingContext:ie,isSingleFrameNavLocked:U,activeSegmentLabel:v,replayFrameRestore:B,clearReplayFrameRestore:re,draftToastMessage:V}=Yt(),[ne,F]=s.useState(null),_=s.useRef(null),O=s.useRef(X);O.current=X;const S=s.useCallback(H=>{l?l(H):z(H)},[l]),P=s.useCallback(H=>{const le=O.current,xe=typeof H=="function"?H(le):H;if(U&&_.current!=null&&xe!==_.current){F("Đang ghi một khung — chọn Nhiều khung để ghi khi đổi khung");return}S(xe)},[S,U]);s.useEffect(()=>{if(U){_.current===null&&(_.current=X);return}_.current=null},[X,U]),s.useEffect(()=>{!U||_.current===null||X!==_.current&&S(_.current)},[S,X,U]),s.useEffect(()=>{if(!ne)return;const H=window.setTimeout(()=>F(null),3500);return()=>window.clearTimeout(H)},[ne]);const j=w[X]??w[0],ee=`Khung ${X+1} / ${w.length} · ${j.label}`,J=s.useMemo(()=>({frameId:j.id,frameIndex:X,frameLabel:ee,imageSrc:j.src}),[j.id,X,ee,j.src]),K=U,{overlaySrc:Ee,interpretation:pe,angleClassification:he,inflammationClassification:Ne,synovitisSeverity:ge,isLoading:ae,isSegmentationLoading:E,error:$,source:se,retry:ke}=Bl(j.id,j.src,w,N,A);s.useEffect(()=>{S(0)},[y,w.length,S]),s.useEffect(()=>{c==null||c(he)},[he,c]),s.useEffect(()=>{d==null||d(Ne)},[Ne,d]),s.useEffect(()=>{u==null||u(ge)},[ge,u]),s.useEffect(()=>{h==null||h(E)},[E,h]);const Fe=()=>P(H=>H>0?H-1:w.length-1),Pe=()=>P(H=>H{const H=m();ie(J,{zoom:H.zoom,panX:H.panX,panY:H.panY,showMask:e,showGradcam:n})},[J,m,ie,n,e,Q]),s.useLayoutEffect(()=>{g&&g.frameId===j.id&&Ae(g)},[j.id,Ae,g]),s.useEffect(()=>{g&&g.frameId===j.id&&Ae(g)},[j.id,Ae,g]),s.useEffect(()=>{B&&(!U&&B.frameIndex!==X&&P(B.frameIndex),Le(B.overlaysByFrameId),f==null||f({showMask:B.viewport.showMask,showGradcam:B.viewport.showGradcam}),re())},[re,X,Le,U,f,B,P]),s.useEffect(()=>{p==null||p(Ie)},[Ie,p]);const x=ll({viewportRef:Se,frameRef:J,frameCount:w.length,patientId:y,tool:G,showMask:e,showGradcam:n,zoom:Q,onMaskToggle:t,onToolChange:_e,onZoomIn:Y,onZoomOut:fe,onResetView:ce,onClearAnnotations:M,onFrameSwitch:R,getViewportState:m}),C=G==="pan"?{onPointerDown:Oe,onPointerMove:Te,onPointerUp:Ce}:{onPointerDown:void 0,onPointerMove:void 0,onPointerUp:void 0},T=G==="draw"||G==="eraser"?{onPointerDown:Oe,onPointerMove:Te,onPointerUp:Ce}:{onPointerDown:void 0,onPointerMove:void 0,onPointerUp:void 0};return r.jsxs("div",{className:`diagnostic-canvas glass ${o?"diagnostic-canvas--dimmed":""}`,children:[r.jsxs("div",{className:"diagnostic-canvas__toolbar",children:[r.jsxs("div",{className:"diagnostic-canvas__frame-nav",children:[r.jsx("button",{type:"button",className:"diagnostic-canvas__nav-btn",onClick:Fe,disabled:K,"aria-label":"Khung trước",children:"‹"}),r.jsx("span",{className:"diagnostic-canvas__label",children:ee}),r.jsx("button",{type:"button",className:"diagnostic-canvas__nav-btn",onClick:Pe,disabled:K,"aria-label":"Khung sau",children:"›"})]}),r.jsxs("div",{className:"diagnostic-canvas__toggles",children:[r.jsxs("label",{className:"diagnostic-canvas__toggle",children:[r.jsx("input",{type:"checkbox",checked:e,onChange:x.handleMaskToggle}),"Kết quả phân đoạn"]}),a&&r.jsxs("label",{className:"diagnostic-canvas__toggle",children:[r.jsx("input",{type:"checkbox",checked:n,onChange:a}),"GradCAM"]})]})]}),r.jsxs("div",{ref:Se,className:"diagnostic-canvas__viewport",style:{cursor:ze},children:[v&&r.jsx("span",{className:"diagnostic-canvas__segment-chip",children:v}),V&&r.jsx("div",{className:"diagnostic-canvas__draft-toast",role:"status",children:V}),ne&&r.jsx("div",{className:"diagnostic-canvas__draft-toast",role:"status",children:ne}),r.jsxs("div",{ref:je,className:"diagnostic-canvas__stage",...C,children:[r.jsx("img",{src:j.src,alt:`Khung siêu âm — ${j.label}`,className:"diagnostic-canvas__frame",draggable:!1}),n&&r.jsx("img",{src:"/assets/gradcam-overlay.svg",alt:"","aria-hidden":!0,className:"diagnostic-canvas__overlay diagnostic-canvas__overlay--gradcam",draggable:!1}),e&&r.jsx(rc,{overlaySrc:Ee,isLoading:ae}),r.jsx("canvas",{ref:de,className:"diagnostic-canvas__annotation-canvas","aria-label":"Lớp chú thích lâm sàng",style:{pointerEvents:G==="draw"||G==="eraser"?"auto":"none"},...T})]}),e&&$&&!ae&&r.jsx(oc,{error:$,frameLabel:ee,onRetry:se==="backend"?ke:void 0}),Z&&te&&r.jsx(Yl,{anchor:te,onConfirm:me,onDismiss:Me}),r.jsx(ql,{containerRef:Se,tool:G,onToolChange:x.handleToolChange,inkColor:b,onInkColorChange:W,zoom:Q,onZoomIn:x.handleZoomIn,onZoomOut:x.handleZoomOut,onResetView:x.handleResetView,onClearAnnotations:x.handleClearAnnotations,hasAnnotations:$e,disabled:o}),r.jsx(nc,{visible:e&&!!pe,containerRef:Se,angleType:(pe==null?void 0:pe.angleType)??"sup",entries:pe==null?void 0:pe.legendEntries,showMeasurementNote:oe}),we&&r.jsxs("div",{className:"diagnostic-canvas__hud",children:[r.jsxs("span",{className:"tnum",children:[q.toFixed(2)," mm"]}),r.jsx("span",{children:"Độ dày (dịch + màng hoạt dịch)"})]})]}),r.jsx("style",{children:` - .diagnostic-canvas { - flex: 1; - border-radius: var(--radius-lg); - display: flex; - flex-direction: column; - min-height: 400px; - overflow: hidden; - } - .diagnostic-canvas--dimmed { - opacity: 0.45; - pointer-events: none; - } - .diagnostic-canvas__toolbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 10px var(--space-md); - border-bottom: 1px solid var(--glass-border); - flex-wrap: wrap; - gap: 8px; - } - .diagnostic-canvas__frame-nav { - display: flex; - align-items: center; - gap: 8px; - } - .diagnostic-canvas__nav-btn { - width: 28px; - height: 28px; - border: 1px solid var(--color-outline-variant); - border-radius: var(--radius-md); - background: rgba(255, 255, 255, 0.6); - color: var(--color-secondary); - font-size: 18px; - line-height: 1; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - padding: 0; - } - .diagnostic-canvas__nav-btn:hover { - background: rgba(255, 255, 255, 0.9); - } - .diagnostic-canvas__nav-btn:disabled { - opacity: 0.4; - cursor: not-allowed; - } - .diagnostic-canvas__nav-btn:disabled:hover { - background: rgba(255, 255, 255, 0.6); - } - .diagnostic-canvas__label { - font-size: 13px; - font-weight: 600; - color: var(--color-secondary); - } - .diagnostic-canvas__toggles { - display: flex; - gap: 14px; - flex-wrap: wrap; - } - .diagnostic-canvas__toggle { - display: flex; - align-items: center; - gap: 6px; - font-size: 13px; - cursor: pointer; - } - .diagnostic-canvas__viewport { - position: relative; - flex: 1; - background: #0a0f0e; - display: flex; - align-items: center; - justify-content: center; - min-height: 320px; - overflow: hidden; - touch-action: none; - user-select: none; - } - .diagnostic-canvas__segment-chip { - position: absolute; - top: 10px; - left: 10px; - z-index: 60; - padding: 4px 10px; - border-radius: 999px; - font-size: 11px; - font-weight: 600; - color: #ecfdf5; - background: rgba(220, 38, 38, 0.85); - border: 1px solid rgba(254, 202, 202, 0.35); - pointer-events: none; - } - .diagnostic-canvas__draft-toast { - position: absolute; - top: 44px; - left: 50%; - transform: translateX(-50%); - z-index: 60; - max-width: min(90%, 420px); - padding: 8px 12px; - border-radius: 8px; - font-size: 12px; - color: #e2e8f0; - background: rgba(15, 23, 42, 0.92); - border: 1px solid rgba(118, 200, 177, 0.35); - pointer-events: none; - } - .diagnostic-canvas__stage { - position: relative; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - transform-origin: center center; - will-change: transform; - backface-visibility: hidden; - } - .diagnostic-canvas__frame { - width: 100%; - height: 100%; - object-fit: contain; - pointer-events: none; - position: relative; - z-index: 1; - } - .diagnostic-canvas__annotation-canvas { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - touch-action: none; - z-index: 500; - } - .diagnostic-canvas__overlay { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - object-fit: contain; - pointer-events: none; - } - .diagnostic-canvas__overlay--gradcam { - mix-blend-mode: screen; - opacity: 0.65; - } - .diagnostic-canvas__overlay--mask { - opacity: 1; - mix-blend-mode: normal; - z-index: 10; - } - .diagnostic-canvas__hud { - position: absolute; - bottom: 72px; - left: 12px; - background: rgba(0, 0, 0, 0.65); - color: #fff; - padding: 8px 12px; - border-radius: var(--radius-md); - font-size: 12px; - display: flex; - flex-direction: column; - gap: 2px; - } - .diagnostic-canvas__hud span:first-child { - font-size: 18px; - font-weight: 600; - } - .diagnostic-canvas__hud span:last-child { - font-size: 11px; - line-height: 1.3; - max-width: 11rem; - } - `})]})}const jn={diseaseProgression:null,powerDopplerScore:null,synovialThicknessMm:"",synovialThicknessUnknown:!1,anatomyDescriptionRight:"",anatomyDescriptionLeft:"",diagnosisWorkImageDataUrl:null,nextProcedure:null,procedureReason:"",procedureResponsibleName:"",doctorConclusion:""},zo=[{value:"improved",label:"Tốt lên"},{value:"stable",label:"Ổn định"},{value:"worse",label:"Xấu đi"}],Fo=[0,1,2,3],$o=[{value:"joint_aspiration",label:"Chọc hút dịch khớp (Joint Aspiration)"},{value:"intra_articular_injection",label:"Tiêm nội khớp (Corticosteroid/PRP)"},{value:"synovial_biopsy",label:"Sinh thiết màng hoạt dịch"},{value:"mri_ct",label:"Chụp MRI/CT đối chiếu"},{value:"other",label:"Chỉ định khác"},{value:"none",label:"Không có"}],kt=6,Bo=["Tiến triển bệnh tình so với lần khám gần nhất","Số liệu đo lường","Mô tả giải phẫu","Ảnh siêu âm đã chú thích","Chỉ định thủ thuật tiếp theo","Kết luận của bác sĩ"];function sc({step:e,draft:t,jointLabel:n,onDraftChange:a,readOnlySnapshot:o=!1}){const i=d=>{a({...t,diseaseProgression:t.diseaseProgression===d?null:d})},l=d=>{a({...t,powerDopplerScore:t.powerDopplerScore===d?null:d})},c=d=>{a({...t,nextProcedure:t.nextProcedure===d?null:d})};return e===0?r.jsx("div",{className:"diag-form-fields__pill-group",role:"group","aria-label":"Tiến triển bệnh",children:zo.map(d=>r.jsx("button",{type:"button",className:t.diseaseProgression===d.value?"diag-form-fields__pill diag-form-fields__pill--active":"diag-form-fields__pill",onClick:()=>i(d.value),children:d.label},d.value))}):e===1?r.jsxs("div",{className:"diag-form-fields__measure-block",children:[r.jsx("p",{className:"diag-form-fields__measure-label",children:"Thang điểm Power Doppler"}),r.jsx("div",{className:"diag-form-fields__score-group",role:"group","aria-label":"Power Doppler",children:Fo.map(d=>r.jsx("button",{type:"button",className:t.powerDopplerScore===d?"diag-form-fields__score diag-form-fields__score--active":"diag-form-fields__score",onClick:()=>l(d),children:d},d))}),r.jsxs("label",{className:"diag-form-fields__field",children:[r.jsxs("span",{children:["Độ dày màng hoạt dịch ",n," ",r.jsx("span",{className:"diag-form-fields__field-note",children:"(không có hoặc không biết: −1)"})]}),r.jsx("input",{type:"text",inputMode:"decimal",className:"tnum",value:t.synovialThicknessUnknown?"−1":t.synovialThicknessMm,disabled:t.synovialThicknessUnknown,onChange:d=>a({...t,synovialThicknessMm:d.target.value}),placeholder:"mm"})]}),r.jsxs("label",{className:"diag-form-fields__checkbox",children:[r.jsx("input",{type:"checkbox",checked:t.synovialThicknessUnknown,onChange:d=>a({...t,synovialThicknessUnknown:d.target.checked,synovialThicknessMm:d.target.checked?"":t.synovialThicknessMm})}),r.jsx("span",{children:"Không có hoặc không đo được"})]})]}):e===2?r.jsxs("div",{className:"diag-form-fields__anatomy-block",children:[r.jsxs("label",{className:"diag-form-fields__field diag-form-fields__subfield",children:[r.jsx("span",{children:"Khớp gối phải"}),r.jsx("textarea",{className:"diag-form-fields__textarea--scroll diag-form-fields__textarea--sub",value:t.anatomyDescriptionRight,onChange:d=>a({...t,anatomyDescriptionRight:d.target.value}),placeholder:"Nhận định lâm sàng và ghi chú về giải phẫu khớp gối phải…"})]}),r.jsxs("label",{className:"diag-form-fields__field diag-form-fields__subfield",children:[r.jsx("span",{children:"Khớp gối trái"}),r.jsx("textarea",{className:"diag-form-fields__textarea--scroll diag-form-fields__textarea--sub",value:t.anatomyDescriptionLeft,onChange:d=>a({...t,anatomyDescriptionLeft:d.target.value}),placeholder:"Nhận định lâm sàng và ghi chú về giải phẫu khớp gối trái…"})]})]}):e===3?r.jsxs("div",{className:"diag-form-fields__snapshot",children:[t.diagnosisWorkImageDataUrl?r.jsx("img",{src:t.diagnosisWorkImageDataUrl,alt:"Ảnh siêu âm đã chú thích",className:"diag-form-fields__snapshot-img"}):r.jsx("div",{className:"diag-form-fields__snapshot-empty",children:"Chưa có ảnh chẩn đoán đã chú thích."}),o&&t.diagnosisWorkImageDataUrl&&r.jsx("p",{className:"diag-form-fields__snapshot-note",children:"Ảnh lưu từ khung chẩn đoán tại thời điểm lập phiếu."})]}):e===4?r.jsxs("div",{className:"diag-form-fields__procedure-block",children:[r.jsx("p",{className:"diag-form-fields__measure-label",children:"Chỉ định thủ thuật tiếp theo"}),r.jsx("div",{className:"diag-form-fields__procedure-list",role:"radiogroup","aria-label":"Chỉ định thủ thuật",children:$o.map(d=>r.jsxs("label",{className:t.nextProcedure===d.value?"diag-form-fields__procedure-option diag-form-fields__procedure-option--active":"diag-form-fields__procedure-option",children:[r.jsx("input",{type:"radio",name:"signOffProcedure",value:d.value,checked:t.nextProcedure===d.value,onChange:()=>c(d.value)}),r.jsx("span",{children:d.label})]},d.value))}),r.jsxs("label",{className:"diag-form-fields__field",children:[r.jsx("span",{children:"Tại sao cần thực hiện chỉ định"}),r.jsx("textarea",{className:"diag-form-fields__textarea--scroll diag-form-fields__textarea--compact",value:t.procedureReason,onChange:d=>a({...t,procedureReason:d.target.value}),placeholder:"Giải thích lý do lâm sàng…"})]}),r.jsxs("label",{className:"diag-form-fields__field",children:[r.jsx("span",{children:"Người phụ trách thực hiện"}),r.jsx("input",{type:"text",value:t.procedureResponsibleName,onChange:d=>a({...t,procedureResponsibleName:d.target.value}),placeholder:"Họ tên bác sĩ / kỹ thuật viên phụ trách"})]})]}):r.jsxs("label",{className:"diag-form-fields__field",children:[r.jsx("span",{className:"sr-only",children:"Kết luận bác sĩ"}),r.jsx("textarea",{className:"diag-form-fields__textarea--scroll",value:t.doctorConclusion,onChange:d=>a({...t,doctorConclusion:d.target.value}),placeholder:"Nhập kết luận / chẩn đoán / yêu cầu cần can thiệp đối với bệnh nhân."})]})}const lc=` - .diag-form-fields__pill-group { - display: flex; - flex-direction: column; - gap: 10px; - } - .diag-form-fields__pill { - width: 100%; - padding: 14px 16px; - border-radius: 999px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.9); - color: var(--color-on-surface); - font-size: 0.9375rem; - font-weight: 600; - cursor: pointer; - } - .diag-form-fields__pill--active { - background: rgba(35, 104, 108, 0.12); - border-color: var(--color-secondary); - color: var(--color-secondary); - } - .diag-form-fields__measure-block, - .diag-form-fields__procedure-block, - .diag-form-fields__anatomy-block { - display: flex; - flex-direction: column; - gap: 14px; - width: 100%; - } - .diag-form-fields__measure-label { - margin: 0; - font-size: 0.8125rem; - font-weight: 600; - color: var(--color-on-surface); - } - .diag-form-fields__score-group { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 8px; - } - .diag-form-fields__score { - aspect-ratio: 1; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - background: #fff; - font-size: 1.125rem; - font-weight: 700; - cursor: pointer; - } - .diag-form-fields__score--active { - background: var(--color-secondary); - border-color: var(--color-secondary); - color: #fff; - } - .diag-form-fields__field { - display: flex; - flex-direction: column; - gap: 6px; - font-size: 0.8125rem; - font-weight: 600; - color: var(--color-on-surface); - } - .diag-form-fields__subfield span { - font-size: 0.75rem; - font-weight: 700; - letter-spacing: 0.02em; - text-transform: uppercase; - color: var(--color-secondary); - } - .diag-form-fields__field-note { - font-weight: 400; - color: var(--color-on-surface-variant); - } - .diag-form-fields__field input, - .diag-form-fields__field textarea { - width: 100%; - box-sizing: border-box; - padding: 10px 12px; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - font: inherit; - font-weight: 400; - background: #fff; - } - .diag-form-fields__textarea--scroll { - resize: none; - height: 140px; - max-height: 140px; - overflow-y: auto; - } - .diag-form-fields__textarea--sub { - height: 100px; - max-height: 100px; - } - .diag-form-fields__textarea--compact { - height: 88px; - max-height: 88px; - } - .diag-form-fields__checkbox { - display: flex; - align-items: flex-start; - gap: 8px; - font-size: 0.8125rem; - font-weight: 400; - color: var(--color-on-surface-variant); - cursor: pointer; - } - .diag-form-fields__procedure-list { - display: flex; - flex-direction: column; - gap: 6px; - } - .diag-form-fields__procedure-option { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 10px 12px; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - background: #fff; - font-size: 0.8125rem; - cursor: pointer; - line-height: 1.35; - } - .diag-form-fields__procedure-option--active { - border-color: var(--color-secondary); - background: rgba(35, 104, 108, 0.08); - color: var(--color-secondary); - } - .diag-form-fields__snapshot-img { - width: 100%; - max-height: 180px; - object-fit: contain; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - background: #0a0f0e; - } - .diag-form-fields__snapshot-empty { - padding: 24px 12px; - text-align: center; - font-size: 0.8125rem; - color: var(--color-on-surface-variant); - border: 1px dashed var(--color-outline-variant); - border-radius: var(--radius-md); - } - .diag-form-fields__snapshot-note { - margin: 0; - font-size: 0.75rem; - color: var(--color-on-surface-variant); - font-style: italic; - } -`;function va(e){return e===0?"Nếu bệnh nhân lần đầu, có thể bỏ qua bước này.":null}function cc(){return r.jsxs("svg",{width:"28",height:"28",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("path",{d:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"}),r.jsx("path",{d:"M14 2v6h6M12 18v-6M9 15h6"})]})}function dc({open:e,draft:t,doctorName:n,jointLabel:a="khớp gối",facilityName:o="Phòng khám MSK — Hệ thống Ghi nhận Lâm sàng",onDraftChange:i,onConfirm:l,onCancel:c}){const d=s.useId(),[u,h]=s.useState(0);if(s.useEffect(()=>{e&&h(0)},[e]),s.useEffect(()=>{if(!e)return;const y=k=>{k.key==="Escape"&&c(),k.key==="ArrowRight"&&uA+1),k.key==="ArrowLeft"&&u>0&&h(A=>A-1)};return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[e,c,u]),!e)return null;const p=u===0,f=u===kt-1,g=Bo[u]??"",N=u===2||u===4;return r.jsxs("div",{className:"diag-signoff",role:"presentation",children:[r.jsx("button",{type:"button",className:"diag-signoff__backdrop","aria-label":"Đóng xác nhận chẩn đoán",onClick:c}),r.jsxs("div",{className:"diag-signoff__modal",role:"dialog","aria-modal":"true","aria-labelledby":d,children:[r.jsxs("header",{className:"diag-signoff__header",children:[r.jsx("span",{className:"diag-signoff__badge","aria-hidden":!0,children:r.jsx(cc,{})}),r.jsx("h2",{id:d,className:"diag-signoff__title",children:"Xác nhận kết quả chẩn đoán"}),r.jsx("p",{className:"diag-signoff__facility",children:o})]}),r.jsxs("p",{className:"diag-signoff__liability",children:["Bằng việc thực hiện thao tác này, bạn đồng ý là người"," ",r.jsx("strong",{className:"diag-signoff__liability-em",children:"chịu trách nhiệm về mặt pháp lý & y đức"})," ","cho kết quả chẩn đoán này!"]}),r.jsxs("article",{className:"diag-signoff__carousel-card",children:[r.jsxs("div",{className:"diag-signoff__carousel-head",children:[r.jsx("h3",{className:"diag-signoff__carousel-title",children:g}),r.jsxs("div",{className:"diag-signoff__carousel-nav",children:[r.jsx("button",{type:"button",className:"diag-signoff__nav-btn",disabled:p,onClick:()=>h(y=>y-1),"aria-label":"Bước trước",children:"←"}),r.jsx("button",{type:"button",className:"diag-signoff__nav-btn",disabled:f,onClick:()=>h(y=>y+1),"aria-label":"Bước sau",children:"→"})]})]}),va(u)&&r.jsx("p",{className:"diag-signoff__step-hint",children:va(u)}),r.jsx("div",{className:N?"diag-signoff__carousel-body diag-signoff__carousel-body--scroll":"diag-signoff__carousel-body",children:r.jsx(sc,{step:u,draft:t,jointLabel:a,onDraftChange:i,readOnlySnapshot:!0})}),r.jsxs("footer",{className:"diag-signoff__carousel-foot",children:[r.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[r.jsx("circle",{cx:"12",cy:"12",r:"9"}),r.jsx("path",{d:"M12 7v5l3 2"})]}),r.jsxs("span",{children:["Ký xác nhận bởi: ",r.jsx("strong",{children:n})]})]})]}),r.jsx("div",{className:"diag-signoff__dots",role:"tablist","aria-label":"Các bước xác nhận",children:Array.from({length:kt},(y,k)=>r.jsx("button",{type:"button",role:"tab","aria-selected":u===k,"aria-label":`Bước ${k+1}`,className:u===k?"diag-signoff__dot diag-signoff__dot--active":"diag-signoff__dot",onClick:()=>h(k)},k))}),r.jsxs("footer",{className:"diag-signoff__actions",children:[r.jsxs("button",{type:"button",className:"diag-signoff__confirm btn btn-primary",onClick:l,children:[r.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5","aria-hidden":!0,children:r.jsx("path",{d:"M20 6 9 17l-5-5"})}),"Xác nhận & Cập nhật"]}),r.jsx("button",{type:"button",className:"diag-signoff__cancel btn btn-ghost",onClick:c,children:"Hủy bỏ"})]})]}),r.jsx("style",{children:`${lc}${uc}`})]})}const uc=` - .diag-signoff { - position: fixed; - inset: 0; - z-index: 1200; - display: flex; - align-items: center; - justify-content: center; - padding: 20px; - } - .diag-signoff__backdrop { - position: absolute; - inset: 0; - border: none; - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); - cursor: pointer; - } - .diag-signoff__modal { - position: relative; - width: min(480px, 100%); - max-height: min(92vh, 860px); - overflow-y: auto; - background: #fff; - border-radius: 16px; - padding: 28px 28px 24px; - display: flex; - flex-direction: column; - gap: 18px; - box-shadow: 0 24px 48px rgba(10, 15, 14, 0.22); - } - .diag-signoff__header { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - gap: 8px; - } - .diag-signoff__badge { - display: flex; - align-items: center; - justify-content: center; - width: 56px; - height: 56px; - border-radius: 999px; - background: rgba(35, 104, 108, 0.12); - color: var(--color-secondary); - } - .diag-signoff__title { - margin: 0; - font-size: 1.25rem; - font-weight: 700; - color: #1a2423; - line-height: 1.3; - } - .diag-signoff__facility { - margin: 0; - font-size: 0.8125rem; - color: var(--color-on-surface-variant); - } - .diag-signoff__liability { - margin: 0; - text-align: center; - font-size: 0.8125rem; - line-height: 1.55; - color: var(--color-on-surface); - padding: 0 4px; - } - .diag-signoff__liability-em { - font-weight: 800; - text-decoration: underline; - text-underline-offset: 3px; - color: #0a0f0e; - } - .diag-signoff__carousel-card { - border: 1px solid var(--color-outline-variant); - border-radius: 12px; - background: rgba(245, 250, 249, 0.5); - overflow: hidden; - display: flex; - flex-direction: column; - } - .diag-signoff__carousel-head { - display: grid; - grid-template-columns: 1fr auto; - gap: 10px; - align-items: start; - padding: 14px 14px 8px; - border-bottom: 1px solid rgba(35, 104, 108, 0.1); - } - .diag-signoff__carousel-title { - margin: 0; - font-size: 0.875rem; - font-weight: 600; - line-height: 1.35; - color: var(--color-on-surface); - } - .diag-signoff__carousel-nav { - display: flex; - gap: 6px; - } - .diag-signoff__nav-btn { - width: 32px; - height: 32px; - border: 1px solid var(--color-outline-variant); - border-radius: 999px; - background: #fff; - color: var(--color-secondary); - font-size: 16px; - line-height: 1; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - padding: 0; - } - .diag-signoff__nav-btn:disabled { - opacity: 0.35; - cursor: not-allowed; - } - .diag-signoff__step-hint { - margin: 0; - padding: 0 14px 6px; - font-size: 0.75rem; - color: var(--color-on-surface-variant); - font-style: italic; - } - .diag-signoff__carousel-body { - padding: 14px; - min-height: 160px; - display: flex; - flex-direction: column; - justify-content: center; - } - .diag-signoff__carousel-body--scroll { - max-height: 280px; - overflow-y: auto; - justify-content: flex-start; - } - .diag-signoff__carousel-foot { - display: flex; - align-items: center; - gap: 6px; - padding: 10px 14px; - border-top: 1px solid rgba(35, 104, 108, 0.1); - font-size: 0.6875rem; - color: var(--color-on-surface-variant); - background: rgba(255, 255, 255, 0.85); - } - .diag-signoff__carousel-foot strong { - color: var(--color-on-surface); - font-weight: 600; - } - .diag-signoff__dots { - display: flex; - justify-content: center; - gap: 8px; - flex-wrap: wrap; - } - .diag-signoff__dot { - width: 8px; - height: 8px; - border-radius: 999px; - border: none; - padding: 0; - background: rgba(35, 104, 108, 0.25); - cursor: pointer; - } - .diag-signoff__dot--active { - width: 22px; - background: var(--color-secondary); - } - .diag-signoff__actions { - display: flex; - flex-direction: column; - gap: 10px; - } - .diag-signoff__confirm { - width: 100%; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 14px 16px; - font-weight: 600; - } - .diag-signoff__cancel { - width: 100%; - border: 1px solid var(--color-outline-variant); - background: #fff; - color: var(--color-on-surface); - padding: 12px 16px; - } -`,ir=[{id:"aggressive",tier:1,labelVi:"Nhạy cao / Sàng lọc",labelEn:"Aggressive / Screening",triggerVi:"Bác sĩ nghi ngờ mức độ viêm cao từ triệu chứng, chưa thấy trên ảnh.",ruleVi:"T = 0.7",recommendedT:.7,uiEffectVi:"Đẩy xác suất biên lên — hạ ngưỡng cảnh báo viêm để không bỏ sót ca hiếm gặp."},{id:"standard",tier:2,labelVi:"Chuẩn / Mặc định",labelEn:"Standard Baseline",triggerVi:"Vận hành mặc định — chưa có dự đoán từ trước từ người dùng.",ruleVi:"T = 1.4",recommendedT:1.4,uiEffectVi:"Giảm overconfidence của mạng — phân bố thực tế, cân bằng toán học."},{id:"conservative",tier:3,labelVi:"Bảo thủ / Hoài nghi",labelEn:"Conservative / Skeptical",triggerVi:"Bác sĩ tin bệnh nhân khỏe; dùng AI chỉ để kiểm tra lại.",ruleVi:"T = 2.2",recommendedT:2.2,uiEffectVi:"Làm phẳng phân bố — chỉ báo dương khi tín hiệu mô hình cực kỳ mạnh."}],ya={aggressiveMax:(.7+1.4)/2,standardMax:(1.4+2.2)/2},Ze={min:.5,max:2.5,step:.05,default:1.4};function Ho(e){return e<=ya.aggressiveMax?"aggressive":e<=ya.standardMax?"standard":"conservative"}function hc(e){return ir.find(t=>t.id===e)??ir[1]}function gc(e){return hc(e).recommendedT}const Bn={temperature:Ze.default,alpha_margin:.05,ood_entropy_threshold:.88},Go="lumina-calibration-user-config",wa={aggressive:.7,diagnostic:1.4,standard:1.4,screening:2.2,conservative:2.2};function pc(){try{const e=localStorage.getItem(Go);if(!e)return{...Bn};const t=JSON.parse(e);let n=t.temperature??Ze.default;return t.mode&&wa[t.mode]!=null&&t.temperature==null&&(n=wa[t.mode]),{...Bn,...t,temperature:Wo(n)}}catch{return{...Bn}}}function fc(e){localStorage.setItem(Go,JSON.stringify(e))}function Wo(e){return Math.min(Ze.max,Math.max(Ze.min,e))}function Uo(e){return Wo(e.temperature)}function mc(e){return Ho(Uo(e))}function bc(e,t){const n=Math.max(t,1e-6),a=e.map(c=>c/n),o=Math.max(...a),i=a.map(c=>Math.exp(c-o)),l=i.reduce((c,d)=>c+d,0);return i.map(c=>c/l)}function Vo(e){return-e.reduce((t,n)=>t+(n>0?n*Math.log(n):0),0)}function _c(e){return e.length<=1?0:Vo(e)/Math.log(e.length)}function Ko(e,t,n){var i;const a=e.map((l,c)=>({p:l,name:t[c]}));a.sort((l,c)=>c.p-l.p);const o=((i=a[0])==null?void 0:i.p)??0;return a.filter(l=>l.p>=o-n).map(l=>l.name)}function xc(e,t,n,a){return n>=a.ood_entropy_threshold?"ood_warning":Ko(e,t,a.alpha_margin).length>1?"ambiguous":"confident"}function qo(e,t){let n=t[0],a=-1;for(const o of t){const i=e[o]??0;i>a&&(a=i,n=o)}return n}function Xo(e,t,n,a){const o=[...t],i=Uo(n),l=mc(n),c=bc(e,i),d=c.reduce((A,I,w)=>I>c[A]?w:A,0),u=o[d],h=c[d],p=Vo(c),f=_c(c),g=Ko(c,o,n.alpha_margin),N=xc(c,o,f,n),y=co(h),k=Object.fromEntries(o.map((A,I)=>[A,Math.round(c[I]*1e4)/100]));return{raw_logits:e,temperature:Math.round(i*1e4)/1e4,base_temperature:n.temperature,mode:l,clinical_suspicion:0,alpha_margin:n.alpha_margin,class_probabilities:k,entropy:Math.round(p*1e4)/1e4,normalized_entropy:Math.round(f*1e4)/1e4,ambiguous_set:g,decision_state:N,predicted_error_rate:y,risk_framing_vi:uo(a(u),h,N)}}function lt({tone:e,children:t}){return r.jsx("span",{className:`angle-legend-color angle-legend-color--${e}`,children:t})}const vc=` - .angle-legend-color { - font-weight: 600; - padding: 0 4px; - border-radius: 3px; - } - .angle-legend-color--red { - color: rgb(185, 28, 28); - background: rgba(255, 0, 0, 0.12); - } - .angle-legend-color--purple { - color: rgb(156, 39, 176); - background: rgba(255, 0, 255, 0.12); - } - .angle-legend-color--cyan { - color: rgb(0, 131, 143); - background: rgba(0, 255, 255, 0.18); - } - .angle-legend-color--yellow { - color: rgb(161, 98, 7); - background: rgba(255, 255, 0, 0.22); - } - .angle-legend-color--green { - color: rgb(27, 122, 55); - background: rgba(0, 255, 0, 0.14); - } - .angle-legend-color--blue { - color: rgb(25, 78, 166); - background: rgba(0, 0, 255, 0.1); - } -`;function sr({includeHoffa:e=!1}){return r.jsxs("ul",{className:"angle-switch-guide__colors",children:[r.jsxs("li",{children:[r.jsx(lt,{tone:"red",children:"Đỏ"}),": Dịch khớp (Joint effusion)."]}),r.jsxs("li",{children:[r.jsx(lt,{tone:"purple",children:"Tím"}),": Màng hoạt dịch (Synovium)."]}),r.jsxs("li",{children:[r.jsx("strong",{children:"Viền trắng dày"})," trên overlay: bao quanh vùng viêm (dịch khớp và màng hoạt dịch)."]}),e&&r.jsxs("li",{children:[r.jsx(lt,{tone:"cyan",children:"Lam sáng"}),": Mỡ Hoffa (Hoffa fat pad)."]})]})}function Yo(){return r.jsxs("ul",{className:"angle-switch-guide__colors",children:[r.jsxs("li",{children:[r.jsx(lt,{tone:"red",children:"Đỏ"}),": Nang Baker (Baker's cyst) — thay vì dịch khớp như ở mặt trước."]}),r.jsxs("li",{children:[r.jsx(lt,{tone:"cyan",children:"Lam sáng"}),": Cơ vùng khoeo — thay vì mỡ Hoffa."]}),r.jsxs("li",{children:[r.jsx(lt,{tone:"yellow",children:"Vàng"}),": Mỡ."]}),r.jsxs("li",{children:[r.jsx(lt,{tone:"green",children:"Xanh lá"}),": Xương đùi."]})]})}function yc({angleClass:e,sectionNumber:t}){const n=He(e);if(e==="post-trans")return r.jsxs("section",{className:"angle-switch-guide__section",children:[r.jsxs("h4",{className:"angle-switch-guide__section-title",children:[t,". Góc ",n.labelMedicalEn," (",n.legendPlaneVi,")"]}),r.jsx("p",{className:"angle-switch-guide__catalog",children:"Mã màu (Theo Phụ lục A.2):"}),r.jsx(Yo,{})]});if(e==="sup-up-long")return r.jsxs("section",{className:"angle-switch-guide__section",children:[r.jsxs("h4",{className:"angle-switch-guide__section-title",children:[t,". Góc ",n.labelMedicalEn," (",n.legendPlaneVi,")"]}),r.jsx("p",{className:"angle-switch-guide__catalog",children:"Mã màu (Theo Phụ lục A.1):"}),r.jsx(sr,{includeHoffa:!0}),r.jsxs("p",{className:"angle-switch-guide__tech-note",children:[r.jsx("strong",{children:"Lưu ý kỹ thuật:"})," Góc này hỗ trợ thước đo độ dày tự động (khung xanh, đường ngọc, đường đỏ trên overlay)."]})]});const a=e==="sup-trans-flex"||e==="med-lat",o=He("sup-up-long");return r.jsxs("section",{className:"angle-switch-guide__section",children:[r.jsxs("h4",{className:"angle-switch-guide__section-title",children:[t,". Góc ",n.labelMedicalEn," (",n.legendPlaneVi,")"]}),r.jsx("p",{className:"angle-switch-guide__catalog",children:"Mã màu (Theo Phụ lục A.1):"}),r.jsx(sr,{includeHoffa:e==="med-lat"}),a&&r.jsxs("p",{className:"angle-switch-guide__tech-note",children:[r.jsx("strong",{children:"Lưu ý kỹ thuật:"})," Mặt cắt này không hiển thị thước đo độ dày tự động (Tính năng đo độ dày chỉ khả dụng trên mặt cắt dọc ",o.labelMedicalEn,")."]})]})}function wc({angleClass:e,sectionNumber:t}){const n=e==="post-trans",a=He(n?"sup-up-long":"post-trans");return r.jsxs("section",{className:"angle-switch-guide__section",children:[r.jsxs("h4",{className:"angle-switch-guide__section-title",children:[t,". Góc ",a.labelMedicalEn," (",a.legendPlaneVi,")"]}),r.jsx("p",{className:"angle-switch-guide__lead",children:n?"Khi chuyển sang góc quét phía trên bánh chè, định dạng màu quay về Phụ lục A.1:":"Khi chuyển sang góc quét này, định dạng màu sẽ tự động tái cấu trúc (Phụ lục A.2):"}),n?r.jsx(sr,{includeHoffa:!0}):r.jsx(Yo,{})]})}function kc({angleClass:e}){return r.jsxs("div",{className:"angle-switch-guide",children:[r.jsx("h3",{className:"angle-switch-guide__heading",children:"⚠️ Lưu ý quan trọng khi đổi góc quét"}),r.jsx("p",{className:"angle-switch-guide__intro",children:"Khi thay đổi góc quét siêu âm, ý nghĩa hiển thị của các vùng màu (overlay) và bong bóng thông tin (tooltip) sẽ thay đổi như sau:"}),r.jsx(yc,{angleClass:e,sectionNumber:1}),r.jsx(wc,{angleClass:e,sectionNumber:2}),r.jsx("style",{children:jc}),r.jsx("style",{children:vc})]})}const jc=` - .angle-switch-guide { - display: flex; - flex-direction: column; - gap: 10px; - font-size: 1em; - } - .angle-switch-guide__heading { - margin: 0; - font-size: 0.6875em; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--color-secondary); - line-height: 1.35; - } - .angle-switch-guide__intro { - margin: 0; - font-size: 0.8125em; - line-height: 1.45; - color: var(--color-on-surface); - } - .angle-switch-guide__section { - margin: 0; - padding-top: 4px; - } - .angle-switch-guide__section-title { - margin: 0 0 6px; - font-size: 0.8125em; - font-weight: 600; - line-height: 1.4; - color: var(--color-on-surface); - } - .angle-switch-guide__catalog, - .angle-switch-guide__lead { - margin: 0 0 6px; - font-size: 0.75em; - font-weight: 500; - color: var(--color-on-surface-variant); - } - .angle-switch-guide__colors { - margin: 0; - padding: 0 0 0 16px; - display: flex; - flex-direction: column; - gap: 5px; - font-size: 0.8125em; - line-height: 1.45; - color: var(--color-on-surface); - } - .angle-switch-guide__tech-note { - margin: 8px 0 0; - font-size: 0.75em; - line-height: 1.45; - color: var(--color-on-surface-variant); - } - .angle-switch-guide__tech-note strong { - color: var(--color-on-surface); - } -`;function Jo({calibration:e,entries:t,predictedKey:n}){const a=Math.max(...Object.values(e.class_probabilities),1);return r.jsxs("table",{className:"cal-dist",role:"table",children:[r.jsx("caption",{className:"cal-dist__caption",children:"Phân bố xác suất theo lớp"}),r.jsx("tbody",{children:t.map(({key:o,label:i})=>{const l=e.class_probabilities[o]??0,c=o===n,d=e.ambiguous_set.includes(o);return r.jsxs("tr",{className:c?"cal-dist__row--active":void 0,children:[r.jsxs("td",{className:"cal-dist__label-cell",children:[r.jsx("span",{className:"cal-dist__label",children:i}),d&&e.decision_state==="ambiguous"&&r.jsx("span",{className:"cal-dist__tag",children:"Mơ hồ"}),r.jsx("div",{className:"cal-dist__bar-track","aria-hidden":!0,children:r.jsx("div",{className:"cal-dist__bar-fill",style:{width:`${l/a*100}%`}})})]}),r.jsx("td",{className:"cal-dist__pct-cell",children:r.jsxs("span",{className:"cal-dist__pct tnum",children:[l.toFixed(1),"%"]})})]},o)})}),r.jsx("style",{children:Sc})]})}function Zo({calibration:e}){if(e.decision_state==="confident")return null;const t=e.decision_state==="ood_warning"?"cal-banner cal-banner--ood":"cal-banner cal-banner--ambiguous";return r.jsxs("p",{className:t,children:[e.decision_state==="ood_warning"?Bi:"Tập mơ hồ — cần đối chiếu thủ công",r.jsx("style",{children:Nc})]})}const Sc=` - .cal-dist { - width: 100%; - margin: 8px 0 0; - border-collapse: collapse; - border: none; - table-layout: fixed; - } - .cal-dist__caption { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; - } - .cal-dist td { - border: none; - padding: 0; - vertical-align: top; - } - .cal-dist tr + tr td { - padding-top: 10px; - } - .cal-dist__label-cell { - width: auto; - padding-right: 12px; - } - .cal-dist__pct-cell { - width: 3.75rem; - text-align: right; - white-space: nowrap; - } - .cal-dist__label { - display: inline; - font-size: 1em; - line-height: 1.4; - } - .cal-dist__row--active .cal-dist__label { - color: var(--color-secondary); - font-weight: 600; - } - .cal-dist__tag { - display: inline-block; - margin-left: 6px; - font-size: 9px; - padding: 1px 6px; - border-radius: 999px; - background: rgba(232, 168, 56, 0.2); - color: #9a6b12; - text-transform: uppercase; - letter-spacing: 0.04em; - vertical-align: middle; - } - .cal-dist__bar-track { - margin-top: 6px; - height: 6px; - border-radius: 999px; - background: rgba(0, 0, 0, 0.06); - overflow: hidden; - } - .cal-dist__bar-fill { - height: 100%; - border-radius: 999px; - background: var(--color-secondary); - opacity: 0.85; - } - .cal-dist__pct { - font-size: 0.9375em; - font-weight: 500; - color: var(--color-on-surface-variant); - line-height: 1.4; - } - .cal-dist__row--active .cal-dist__pct { - color: var(--color-secondary); - font-weight: 600; - } -`,Nc=` - .cal-banner { - margin: 0; - padding: 8px 10px; - border-radius: var(--radius-sm); - font-size: 13px; - line-height: 1.45; - font-weight: 500; - } - .cal-banner--ambiguous { - background: rgba(232, 168, 56, 0.12); - color: #8a6110; - border-left: 3px solid #e8a838; - } - .cal-banner--ood { - background: rgba(186, 26, 26, 0.08); - color: #8f1414; - border-left: 3px solid var(--color-error); - } -`,Mc="Giải thích cách tính",Cc="Câu chuyện ẩn dụ",Ec=["Hãy tưởng tượng mô hình AI giống như một vị bác sĩ nội trú trẻ tuổi, thiên tài nhưng vô cùng ngạo mạn. Vị bác sĩ này đọc sách rất nhiều, ghi nhớ chuẩn xác các triệu chứng viêm nhiễm (độ chính xác cao), nhưng có một nhược điểm lớn: luôn nói quá lên độ tự tin của mình. Khi thấy một ca bệnh hơi giống viêm nhiễm, thay vì bảo “Tôi nghĩ có 70% khả năng viêm”, anh ta luôn dõng dạc tuyên bố “Tôi chắc chắn 99,9%!”. Sự tự tin thái quá này cực kỳ nguy hiểm trong lâm sàng, vì nó khiến chúng ta mất cảnh giác ở những ca ranh giới.","Để kiểm soát sự kiêu ngạo này, bệnh viện cần một bác sĩ trưởng khoa trưởng thành và điềm đạm — chính là tham số Nhiệt độ (T). Bác sĩ trưởng khoa không thay đổi chẩn đoán hay kiến thức của bác sĩ trẻ, ông chỉ lắng nghe những “lập luận thô” (logits) của anh ta, rồi chủ động “làm dịu” (scale bằng T) sự hung hăng đó lại. Nhờ đó, con số phần trăm cuối cùng trả về cho bạn sẽ phản ánh đúng thực tế lâm sàng: ca nào rõ ràng thì vẫn là 99%, nhưng ca nào mập mờ sẽ bị kéo về đúng mức 70% hoặc rơi vào “Vùng lưỡng lự” để con người nhảy vào hội chẩn."],Tc="Năm điểm cốt lõi — con số trên màn hình đến từ đâu",Ic=[{title:"Điểm số thô không phải là phần trăm",body:"AI không trực tiếp nghĩ ra con số 90% hay 99%. Đầu tiên, nó chấm điểm toán học thô (logits) cho từng khả năng — ví dụ Viêm được 8 điểm, Không viêm được 2 điểm."},{title:"Cơ chế bóp méo của AI hiện đại",body:"Các mô hình AI càng phức tạp (kích thuớc lớn & sâu) và thông minh thì xu hướng phân tách các điểm thô càng xa (ví dụ 20 điểm và 1 điểm). Khi quy đổi, khoảng cách quá lớn này bị ép thành xác suất cực đoan như 99,9% — hiện tượng “quá tự tin” (over-confidence)."},{title:"Bác sĩ trưởng khoa hạ hỏa (tham số T)",body:"Trước khi chuyển điểm thô thành phần trăm, ta chia tất cả các điểm thô cho hệ số T (Nhiệt độ). Nếu T > 1, khoảng cách phóng đại giữa các điểm thô sẽ bị thu hẹp lại một cách toán học."},{title:"Quy đổi ra xác suất thực (Softmax)",body:"Sau khi được làm dịu bởi T, các điểm thô mới được chuẩn hóa bằng Softmax — quy đổi thành tỷ lệ phần trăm có tổng bằng 100%. Đây mới là con số hiển thị trên màn hình của bạn."},{title:"Điều chỉnh theo bối cảnh lâm sàng",body:"Thanh trượt T và ba bậc hiệu chuẩn phía trên cho phép bạn chọn mức “dịu” hay “sắc” phù hợp bối cảnh:",subPoints:["T cao (làm mịn): mô hình thận trọng hơn, kéo phần trăm ảo tưởng xuống gần thực tế — phù hợp khi kiểm tra chéo khắt khe.","T thấp (làm sắc): mô hình nhạy bén, quyết đoán hơn — hữu ích khi sàng lọc diện rộng để không bỏ sót dấu hiệu nghi ngờ."]}],Lc="Guo và cộng sự (2017), trong bài On Calibration of Modern Neural Networks (ICML), cho thấy mạng sâu hiện đại thường quá tự tin và đề xuất temperature scaling — hiệu chỉnh bằng một tham số T trên logits trước khi chuyển sang xác suất. Khung đánh giá mức khớp giữa xác suất báo cáo và tần suất đúng trên dữ liệu quan sát được trình bày trong tài liệu về phân loại và hiệu chuẩn độ tin cậy của Lei (Đại học Carnegie Mellon).",Pc="Đọc thêm",Ac=[{label:"Guo et al. (2017) — temperature scaling",href:"https://arxiv.org/abs/1706.04599"},{label:"Lei — confidence & classification calibration (CMU)",href:"https://www.stat.cmu.edu/~jinglei/conf_class_R2.pdf"}],Rc="Nền tảng khoa học & đọc thêm",Dc="Ẩn dụ để nắm bản chất toán học — cho bác sĩ cần trực giác trước khi đọc số",Oc="Lộ trình sinh ra % trên màn hình — cho người chỉnh T và đối chiếu kết quả hàng ngày",zc="Căn cứ khoa học để tra cứu — cho trưởng khoa / nghiên cứu cần trích dẫn";function Hn({title:e,teaser:t,defaultOpen:n,children:a}){return r.jsxs("details",{className:"cal-help-section",open:n,children:[r.jsxs("summary",{className:"cal-help-section__toggle",children:[r.jsx("span",{className:"cal-help-section__chevron","aria-hidden":!0}),r.jsxs("span",{className:"cal-help-section__labels",children:[r.jsx("span",{className:"cal-help-section__title",children:e}),r.jsx("span",{className:"cal-help-section__teaser",children:t})]})]}),r.jsx("div",{className:"cal-help-section__body",children:a})]})}function Ar({layout:e="block"}){const t=e==="block";return r.jsxs("div",{className:`cal-metric-help ${t?"cal-metric-help--block":"cal-metric-help--inline"}`,children:[r.jsxs("details",{className:"cal-metric-help__details",children:[r.jsxs("summary",{className:"cal-metric-help__trigger",children:[r.jsx("span",{className:"cal-metric-help__icon","aria-hidden":!0,children:"?"}),r.jsx("span",{className:"cal-metric-help__trigger-text",children:Mc})]}),r.jsxs("div",{className:"cal-metric-help__panel",role:"note",children:[r.jsx("p",{className:"cal-metric-help__intro",children:"Mở từng mục bạn quan tâm — không cần đọc hết một lúc."}),r.jsxs("div",{className:"cal-metric-help__sections",children:[r.jsx(Hn,{title:Cc,teaser:Dc,children:Ec.map(n=>r.jsx("p",{className:"cal-metric-help__prose",children:n},n.slice(0,32)))}),r.jsx(Hn,{title:Tc,teaser:Oc,defaultOpen:!0,children:r.jsx("ol",{className:"cal-metric-help__key-points",children:Ic.map(n=>r.jsxs("li",{className:"cal-metric-help__key-point",children:[r.jsxs("strong",{children:[n.title,"."]})," ",n.body,n.subPoints&&r.jsx("ul",{className:"cal-metric-help__sub-points",children:n.subPoints.map(a=>r.jsx("li",{children:a},a))})]},n.title))})}),r.jsxs(Hn,{title:Rc,teaser:zc,children:[r.jsx("p",{className:"cal-metric-help__foundation",children:Lc}),r.jsx("p",{className:"cal-metric-help__refs-title",children:Pc}),r.jsx("ul",{className:"cal-metric-help__refs",children:Ac.map(n=>r.jsx("li",{children:r.jsx("a",{href:n.href,target:"_blank",rel:"noopener noreferrer",children:n.label})},n.href))})]})]})]})]}),r.jsx("style",{children:$c})]})}const Fc=` - .cal-help-section { - border: 1px solid rgba(0, 0, 0, 0.08); - border-radius: var(--radius-sm); - background: rgba(255, 255, 255, 0.65); - overflow: hidden; - } - .cal-help-section__toggle { - list-style: none; - cursor: pointer; - display: flex; - align-items: flex-start; - gap: 10px; - padding: 12px 14px; - user-select: none; - } - .cal-help-section__toggle::-webkit-details-marker { - display: none; - } - .cal-help-section__toggle:hover { - background: rgba(35, 104, 108, 0.04); - } - .cal-help-section[open] .cal-help-section__toggle { - border-bottom: 1px solid rgba(0, 0, 0, 0.06); - background: rgba(35, 104, 108, 0.06); - } - .cal-help-section__chevron { - flex-shrink: 0; - width: 9px; - height: 9px; - margin-top: 6px; - border-right: 2px solid var(--color-secondary); - border-bottom: 2px solid var(--color-secondary); - transform: rotate(-45deg); - transition: transform 0.15s ease; - } - .cal-help-section[open] .cal-help-section__chevron { - transform: rotate(45deg); - margin-top: 4px; - } - .cal-help-section__labels { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 0; - } - .cal-help-section__title { - font-size: 14px; - font-weight: 600; - font-family: var(--font-headline); - color: var(--color-on-surface); - line-height: 1.35; - } - .cal-help-section__teaser { - font-size: 13px; - color: var(--color-on-surface-variant); - line-height: 1.45; - } - .cal-help-section__body { - padding: 12px 14px 14px; - font-size: 13px; - line-height: 1.55; - } -`,$c=` - ${Fc} - - .cal-metric-help { - text-transform: none; - letter-spacing: normal; - font-weight: normal; - /* rem base — avoids double-shrink when nested in small em containers */ - font-size: 13px; - } - .cal-metric-help--block { - display: block; - width: 100%; - } - .cal-metric-help--inline { - display: inline-block; - max-width: 100%; - } - .cal-metric-help__details { - display: block; - width: 100%; - } - .cal-metric-help__trigger { - list-style: none; - cursor: pointer; - display: inline-flex; - align-items: center; - gap: 8px; - padding: 4px 0; - color: var(--color-secondary); - font-size: 13px; - font-weight: 500; - user-select: none; - } - .cal-metric-help__trigger::-webkit-details-marker { - display: none; - } - .cal-metric-help__icon { - flex-shrink: 0; - width: 20px; - height: 20px; - border-radius: 999px; - border: 1px solid rgba(35, 104, 108, 0.35); - background: rgba(35, 104, 108, 0.08); - color: var(--color-secondary); - font-size: 12px; - font-weight: 700; - line-height: 18px; - text-align: center; - } - .cal-metric-help__trigger-text { - text-decoration: underline; - text-underline-offset: 2px; - text-decoration-color: rgba(35, 104, 108, 0.35); - } - .cal-metric-help__panel { - margin-top: 10px; - width: 100%; - box-sizing: border-box; - padding: 14px; - border-radius: var(--radius-sm); - background: rgba(35, 104, 108, 0.05); - border: 1px solid rgba(35, 104, 108, 0.12); - border-left: 3px solid var(--color-secondary); - font-size: 13px; - line-height: 1.55; - color: var(--color-on-surface-variant); - text-transform: none; - letter-spacing: normal; - } - .cal-metric-help__intro { - margin: 0 0 12px; - font-size: 13px; - line-height: 1.45; - color: var(--color-on-surface-variant); - } - .cal-metric-help__sections { - display: flex; - flex-direction: column; - gap: 10px; - } - .cal-metric-help__prose { - margin: 0 0 10px; - } - .cal-metric-help__prose:last-child { - margin-bottom: 0; - } - .cal-metric-help__key-points { - margin: 0; - padding-left: 1.2rem; - } - .cal-metric-help__key-point { - margin-bottom: 10px; - } - .cal-metric-help__key-point:last-child { - margin-bottom: 0; - } - .cal-metric-help__key-point strong { - color: var(--color-on-surface); - font-weight: 600; - } - .cal-metric-help__sub-points { - margin: 6px 0 0; - padding-left: 1rem; - list-style: disc; - } - .cal-metric-help__sub-points li { - margin-bottom: 4px; - } - .cal-metric-help__foundation { - margin: 0 0 10px; - font-style: italic; - font-size: 13px; - line-height: 1.5; - } - .cal-metric-help__refs-title { - margin: 0 0 6px; - font-size: 13px; - font-weight: 600; - color: var(--color-on-surface); - } - .cal-metric-help__refs { - margin: 0; - padding: 0; - list-style: none; - display: flex; - flex-direction: column; - gap: 6px; - } - .cal-metric-help__refs a { - color: var(--color-secondary); - font-size: 13px; - text-decoration: underline; - text-underline-offset: 2px; - word-break: break-word; - } -`,Bc=zt.map(e=>({key:e,label:Qe(He(e))}));function Hc(e){return Cn(e)?Qe(He(e)):e}function Gc({classification:e,userConfig:t,suppressed:n}){const a=s.useMemo(()=>{var h;const u=(h=e==null?void 0:e.calibration)==null?void 0:h.raw_logits;return u!=null&&u.length?Xo(u,zt,t,Hc):(e==null?void 0:e.calibration)??null},[e,t]),o=s.useMemo(()=>{if(!e)return null;if(!a)return e.class;const u=qo(a.class_probabilities,zt);return Cn(u)?u:e.class},[e,a]);if(n)return null;if(!e||!o)return r.jsxs("div",{className:"angle-blob glass",children:[r.jsx("span",{className:"angle-blob__eyebrow",children:"Phân loại góc chụp"}),r.jsx("p",{className:"angle-blob__pending",children:"Đang chờ kết quả phân loại góc…"}),r.jsx("style",{children:ka})]});const i=He(o),l=He(e.class),c=o!==e.class,d=(a==null?void 0:a.risk_framing_vi)??uo(Qe(i),e.confidence);return r.jsxs("div",{className:"angle-blob glass",children:[r.jsx("span",{className:"angle-blob__eyebrow",children:"Phân loại góc chụp"}),r.jsxs("div",{className:"angle-blob__result",children:[r.jsx("strong",{children:Qe(i)}),c&&r.jsxs("p",{className:"angle-blob__shift",children:["Mô hình gốc: ",Qe(l)," — đã điều chỉnh theo T / nghi ngờ lâm sàng."]}),a&&r.jsx(Zo,{calibration:a}),r.jsx("p",{className:"angle-blob__risk",children:d})]}),r.jsx("div",{className:"angle-blob__legend-block",children:r.jsx(kc,{angleClass:o})}),r.jsxs("details",{className:"angle-blob__classes",open:(a==null?void 0:a.decision_state)!=="confident",children:[r.jsx("summary",{children:"Phân bố xác suất theo lớp"}),r.jsx(Ar,{layout:"block"}),a?r.jsx(Jo,{calibration:a,entries:Bc,predictedKey:o}):r.jsx("ul",{children:zt.map(u=>{const h=He(u),p=u===o;return r.jsx("li",{className:p?"angle-blob__class--active":void 0,children:r.jsx("span",{children:Qe(h)})},u)})})]}),r.jsx("style",{children:ka})]})}const ka=` - .angle-blob { - margin-top: 4px; - padding: 14px; - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - gap: 10px; - font-size: 1em; - } - .angle-blob__eyebrow { - font-size: 0.6875em; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-on-surface-variant); - } - .angle-blob__pending { - margin: 0; - font-size: 0.8125em; - color: var(--color-on-surface-variant); - } - .angle-blob__result strong { - display: block; - font-size: 0.9375em; - font-family: var(--font-headline); - line-height: 1.4; - margin-bottom: 2px; - } - .angle-blob__shift { - margin: 4px 0 0; - font-size: 0.6875em; - color: var(--color-on-surface-variant); - font-style: italic; - line-height: 1.4; - } - .angle-blob__risk { - margin: 8px 0 0; - font-size: 0.8125em; - color: var(--color-on-surface-variant); - line-height: 1.45; - } - .angle-blob__legend-block { - padding: 12px; - border-radius: var(--radius-sm); - background: rgba(35, 104, 108, 0.06); - border-left: 3px solid var(--color-secondary); - } - .angle-blob__classes { - font-size: 0.875em; - color: var(--color-on-surface-variant); - } - .angle-blob__classes summary { - cursor: pointer; - font-weight: 600; - font-size: 1em; - color: var(--color-secondary); - } - .angle-blob__classes ul { - margin: 8px 0 0; - padding: 0; - list-style: none; - display: flex; - flex-direction: column; - gap: 6px; - } - .angle-blob__classes li { - display: flex; - flex-wrap: wrap; - align-items: baseline; - gap: 8px; - line-height: 1.4; - } - .angle-blob__class--active > span:first-child { - color: var(--color-secondary); - font-weight: 600; - } -`;function Wc({config:e,onChange:t}){const n=Ho(e.temperature),a=i=>{t({...e,temperature:i})},o=i=>{a(gc(i))};return r.jsxs("div",{className:"cal-ctrl",children:[r.jsxs("div",{className:"cal-ctrl__header",children:[r.jsx("span",{className:"cal-ctrl__title",children:"Điều chỉnh độ chắc chắn (T)"}),r.jsxs("span",{className:"cal-ctrl__hint tnum",children:["T = ",e.temperature.toFixed(2)]})]}),r.jsx(Ar,{layout:"block"}),r.jsx("div",{className:"cal-ctrl__tiers",role:"group","aria-label":"Bậc hiệu chuẩn lâm sàng",children:ir.map(i=>{const l=i.id===n;return r.jsxs("button",{type:"button",className:l?"cal-tier cal-tier--active":"cal-tier",onClick:()=>o(i.id),"aria-pressed":l,children:[r.jsxs("span",{className:"cal-tier__badge",children:["Bậc ",i.tier]}),r.jsx("strong",{className:"cal-tier__name",children:i.labelVi}),r.jsx("span",{className:"cal-tier__rule tnum",children:i.ruleVi}),r.jsx("span",{className:"cal-tier__trigger",children:i.triggerVi}),r.jsx("span",{className:"cal-tier__effect",children:i.uiEffectVi})]},i.id)})}),r.jsxs("label",{className:"cal-ctrl__slider-label",children:[r.jsx("span",{children:"Kéo T liên tục"}),r.jsx("span",{className:"tnum",children:e.temperature.toFixed(2)})]}),r.jsx("input",{type:"range",min:Math.round(Ze.min*100),max:Math.round(Ze.max*100),step:Math.round(Ze.step*100),value:Math.round(e.temperature*100),className:"cal-ctrl__slider","aria-valuemin":Ze.min,"aria-valuemax":Ze.max,"aria-valuenow":e.temperature,onChange:i=>a(Number(i.target.value)/100)}),r.jsxs("div",{className:"cal-ctrl__scale tnum","aria-hidden":!0,children:[r.jsx("span",{children:"0.5"}),r.jsx("span",{children:"1.05"}),r.jsx("span",{children:"1.8"}),r.jsx("span",{children:"2.5"})]}),r.jsx("style",{children:Uc})]})}const Uc=` - .cal-ctrl { - display: flex; - flex-direction: column; - gap: 10px; - padding: 10px 12px; - border-radius: var(--radius-sm); - background: rgba(0, 0, 0, 0.03); - border: 1px solid rgba(0, 0, 0, 0.06); - } - .cal-ctrl__header { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - gap: 4px; - align-items: baseline; - } - .cal-ctrl__title { - font-size: 0.6875em; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--color-on-surface-variant); - } - .cal-ctrl__hint { - font-size: 0.6875em; - color: var(--color-on-surface-variant); - opacity: 0.85; - } - .cal-ctrl__tiers { - display: flex; - flex-direction: column; - gap: 8px; - } - .cal-tier { - display: flex; - flex-direction: column; - gap: 4px; - padding: 10px 12px; - border-radius: var(--radius-sm); - border: 1px solid rgba(0, 0, 0, 0.08); - background: var(--color-surface, #fff); - text-align: left; - cursor: pointer; - transition: border-color 0.15s, background 0.15s, box-shadow 0.15s; - opacity: 0.72; - } - .cal-tier--active { - opacity: 1; - border-color: var(--color-secondary); - background: rgba(35, 104, 108, 0.1); - box-shadow: 0 0 0 1px rgba(35, 104, 108, 0.15); - } - .cal-tier__badge { - font-size: 0.625em; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--color-on-surface-variant); - } - .cal-tier--active .cal-tier__badge { - color: var(--color-secondary); - } - .cal-tier__name { - font-size: 0.8125em; - font-family: var(--font-headline); - line-height: 1.3; - color: var(--color-on-surface); - } - .cal-tier--active .cal-tier__name { - color: var(--color-secondary); - } - .cal-tier__rule { - font-size: 0.6875em; - font-weight: 600; - color: var(--color-on-surface-variant); - } - .cal-tier__trigger, - .cal-tier__effect { - font-size: 0.6875em; - line-height: 1.4; - color: var(--color-on-surface-variant); - } - .cal-ctrl__slider-label { - display: flex; - justify-content: space-between; - font-size: 0.75em; - color: var(--color-on-surface-variant); - margin-top: 2px; - } - .cal-ctrl__slider { - width: 100%; - accent-color: var(--color-secondary); - } - .cal-ctrl__scale { - display: flex; - justify-content: space-between; - font-size: 0.625em; - color: var(--color-on-surface-variant); - opacity: 0.8; - margin-top: -4px; - } -`,Qo="clinical_chat_reasoning_level",ei="clinical_chat_reasoning_bar_visible",dt="moderate",Vc="moderate",lr=[{id:"chill",emoji:"🧘",label:"Mức thấp",taskHint:"Chào hỏi · hỏi một ý",description:"Mức độ thấp — trả lời ngay, không hiện suy luận (512 token). Dùng khi chào hỏi, xác nhận nhanh, hoặc một câu hỏi đơn giản.",welcomeHint:"Mức độ thấp (🧘): trả lời nhanh, không hiện suy luận — phù hợp chào hỏi và câu hỏi ngắn.",chainOfThought:!1,maxTokens:512,maxHistoryTurns:2,temperature:.65},{id:"moderate",emoji:"🤔",label:"Mức vừa",taskHint:"Giải thích overlay & đề xuất AI",description:"Mức độ vừa — suy luận có hiện trace (1024 token). Chọn máy (Gemma 4 E2B) hoặc server (Gemma 4 E4B).",welcomeHint:"Mức độ vừa (🤔): suy luận có trace — phù hợp giải thích overlay và đề xuất AI.",chainOfThought:!0,maxTokens:1024,edgeMaxTokens:1024,maxHistoryTurns:4,temperature:.7},{id:"high",emoji:"🔥",label:"Mức cao",taskHint:"Thảo luận sâu nhiều lượt",description:"Mức độ cao — nhớ hội thoại dài (2048 token). Dùng khi hoài nghi đề xuất, chỉnh sửa phân loại, hoặc thảo luận phức tạp.",welcomeHint:"Mức độ cao (🔥): nhớ nhiều lượt hội thoại — phù hợp hoài nghi, chỉnh sửa, và thảo luận sâu.",beta:!0,chainOfThought:!1,maxTokens:2048,maxHistoryTurns:6,temperature:.7}];function ti(e){return!Ke(e).beta}function Kc(e){return ti(e)?e:Vc}function qc(e){return e.beta?`${e.description} — Beta: đang phát triển, chưa sẵn sàng.`:e.description}function Ke(e){return lr.find(t=>t.id===e)??lr[1]}function Xc(e){return Ke(e).chainOfThought}function ni(e,t){const n=Ke(e);return t==="edge"&&n.edgeMaxTokens!==void 0?n.edgeMaxTokens:n.maxTokens}function ja(e,t){const n=Ke(e);return{maxTokens:ni(e,t),topK:40,temperature:n.temperature,randomSeed:101}}function Sa(){if(typeof localStorage>"u")return dt;const e=localStorage.getItem(Qo);return e==="chill"||e==="moderate"||e==="high"?Kc(e):dt}function Yc(e){try{localStorage.setItem(Qo,e)}catch{}}function Jc(){if(typeof localStorage>"u")return!0;const e=localStorage.getItem(ei);return!(e==="0"||e==="false")}function Zc(e){try{localStorage.setItem(ei,e?"1":"0")}catch{}}const Qc="You are a clinical decision-support assistant for musculoskeletal ultrasound. Explain findings clearly for clinicians. Do not prescribe medications or give dosages.",Bt="<|channel>thought",Ht="",ed=2048;function td(e,t={},n=[]){var l;const a=((l=t.systemPrompt)==null?void 0:l.trim())??"",o=t.chainOfThought??!1,i=[];if(o||a){const c=o?`<|think|>${a}`:a;i.push(`<|turn>system -${c}`)}for(const c of n){const d=c.role==="user"?"user":"model";i.push(`<|turn>${d} -${c.text}`)}return i.push(`<|turn>user -${e}`),i.push(`<|turn>model -`),i.join(` -`)}function nd(e,t){return t?rd(e).answer:e.trim()}function rd(e){const t=e.indexOf(Bt);if(t===-1)return{thought:null,answer:e};const n=e.slice(t+Bt.length).replace(/^\n/,""),a=n.indexOf(Ht);return a===-1?{thought:n.trim(),answer:""}:{thought:n.slice(0,a).trim(),answer:n.slice(a+Ht.length).trim()}}const ad={maxTokens:1024,topK:40,temperature:.7,randomSeed:101},od={...ad,maxTokens:ed};function Na(){return!0}function Ma(){const t="".trim();return t||(console.warn("[clinicalChatConfig] VITE_API_BASE_URL is not set. Clinical chat BFF calls will fail. Set it in config/frontend.config.yaml for production builds."),"")}const id="gemma4:e4b";function ri(){const e="/api/ollama-chat/api/chat";return e.trim()!==""?e.trim():""}function sd(){return"gemma4:e4b".trim()||id}function Rn(){return ri().length>0}const Ca=9e4,Ea=4e3,ai="clinical_chat_reasoning_model_backend",Ta="server",cr=[{id:"edge",label:"Gemma 4 E2B · máy",shortLabel:"Máy",description:"Suy luận trên Gemma 4 E2B trong trình duyệt (OPFS + WebGPU)."},{id:"server",label:"Gemma 4 E4B · server",shortLabel:"Server",description:"Suy luận qua Gemma 4 E4B trên Modal Ollama (think:true)."}];function oi(e){return cr.find(t=>t.id===e)??cr[1]}function Dn(){if(typeof localStorage>"u")return Ta;const e=localStorage.getItem(ai);return e==="edge"||e==="server"?e:Ta}function ld(e){try{localStorage.setItem(ai,e)}catch{}}function Sn(e){return e==="server"&&!Rn()?"edge":e}function cd(e,t,n=Dn()){if(!Rn())return!1;if(e==="agent")return!0;if(e==="chat"){const a=Ke(t),o=Sn(n);return a.chainOfThought&&o==="server"}return!1}function Ia(e,t,n=Dn()){if(e==="agent")return"Gemma 4 E4B · server";if(!Ke(t).chainOfThought)return"Gemma 4 E2B";const o=Sn(n);return oi(o).label}const dr=[{id:"chat",label:"Trò chuyện",shortLabel:"Chat",description:"Hội thoại với AI — chọn mức độ (🧘 🤔 🔥), không công cụ.",llmCallsHint:"1 lần"},{id:"planning",label:"Lập kế hoạch",shortLabel:"Kế hoạch",description:"Sinh checklist các bước lâm sàng — một lần suy luận.",llmCallsHint:"1 lần",beta:!0},{id:"agent",label:"Agent",shortLabel:"Agent",description:"Tra cứu MOH/Exa và trả lời có trích dẫn — nhiều bước (Modal Ollama).",llmCallsHint:"2–6 lần",beta:!0}];function Jt(e){return!Ut(e).beta}function an(e){return Jt(e)?e:ur}function dd(e){const t=`${e.description} (${e.llmCallsHint})`;return e.beta?`${t} — Beta: đang phát triển, chưa sẵn sàng.`:t}function Ut(e){return dr.find(t=>t.id===e)??dr[0]}const ur="chat",ud={maxTokens:640,topK:30,temperature:.45,randomSeed:101},hd={maxTokens:1024,topK:40,temperature:.7,randomSeed:101};function ii(e,t=dt,n){switch(e){case"chat":return ja(t,n);case"planning":return ud;case"agent":return hd;default:return ja(t,n)}}function gd(e){return e!=="agent"?{}:{enablePlanPhase:!1,requireRetrievalBeforeFinal:!0,maxSteps:6}}function pd(e,t,n){return{...Jn,...gd(e),mockTools:t,bffBaseUrl:n}}const fd=` - -PLAN MODE (strict): -- Output a numbered checklist of clinical steps to address the user. -- One step per line (e.g. "1. …"). -- No greetings, no tool tags, no long prose paragraphs. -- Use Vietnamese when the clinician writes in Vietnamese. -`;function _t(e,t,n=dt,a=Dn()){const o=Ut(e);if(e==="chat"){const i=Ke(n),l=Ia(e,n,a);return`Sẵn sàng · ${o.shortLabel} · ${i.label} · ${l}`}return e==="agent"&&Rn()?`Sẵn sàng · ${o.shortLabel} · ${Ia(e,n)}`:`Sẵn sàng · ${o.shortLabel} · Gemma 4 E2B`}let La=0;function st(){return La+=1,`chat-${Date.now()}-${La}`}function on(e,t,n="chat",a=!1,o=dt){const i=Ut(n),l=n==="chat"?Ke(o):null,c=t?` Góc quét hiện tại: ${Qe(He(t))}.`:"";let d;if(a)d="AI đang phân tích mức viêm trên khung hình hiện tại — tôi chưa có đề xuất cụ thể để thảo luận.";else if(e!==null){const h=qt[e]??`Độ ${e}`;d=`Khi bạn hỏi về đề xuất AI, tôi có thể thảo luận viêm màng hoạt dịch (mức ${e} — ${h}).${c}`}else d=`Chưa có đề xuất mức viêm từ AI trên khung hình này.${c}`;const u=l!==null?` ${l.welcomeHint} Chạm biểu tượng ${l.emoji} trên thanh công cụ để đổi mức độ.`:"";return{id:st(),role:"assistant",content:`Tôi là trợ lý AI lâm sàng (${i.label} · ${i.llmCallsHint}).${u} ${d} Bạn có thể hỏi về overlay phân đoạn hoặc ghi nhận chỉnh sửa. Bạn muốn làm rõ điểm nào?`,timestamp:new Date}}function md(e,t,n){const a=e.toLowerCase(),o=t!==null?qt[t]??`Độ ${t}`:null;if(/^(xin chào|chào|hello|hi|hey)[\s!.?]*$/i.test(a.trim()))return"Chào bạn. Tôi sẵn sàng hỗ trợ khi bạn muốn thảo luận đề xuất AI, overlay phân đoạn, hoặc ghi nhận chỉnh sửa.";if(/hoài nghi|nghi ngờ|không tin|chưa thuyết phục|skeptic|doubt|lạ|odd|không đồng ý|không phù hợp/.test(a))return"Đã ghi nhận mối lo ngại của bạn. Hãy nêu cụ thể phần nào chưa phù hợp — ví dụ: mức viêm, vùng overlay, hoặc góc quét. Tôi sẽ giải thích căn cứ AI và hỗ trợ ghi nhận chỉnh sửa có kiểm soát, không cần tạm dừng quy trình khám.";if(/sửa|ghi đè|chỉnh|điều chỉnh|override|correct/.test(a))return"Đã ghi nhận yêu cầu chỉnh sửa. Mô tả mức độ hoặc vùng giải phẫu bạn muốn cập nhật — ví dụ: «Giảm độ viêm xuống 1» hoặc «Bỏ qua dịch ở hố suprapatellar». Trong bản triển khai đầy đủ, thay đổi sẽ được lưu vào audit trail và đồng bộ EMR.";if(/góc|angle|overlay|màu|legend|phân đoạn/.test(a)){if(n){const i=He(n),l=i.segmentLegendType==="post"?"A.2":"A.1";return`Ở ${i.labelMedicalEn}, đọc overlay theo Phụ lục ${l} (${i.legendPlaneVi}). Bạn có thể bật/tắt mask bằng checkbox «Kết quả phân đoạn».`}return"Chuyển khung siêu âm để tôi căn cứ góc quét cụ thể khi giải thích mã màu overlay."}return/viêm|độ|grade|mức|synovitis|severity/.test(a)?t===null||o===null?"Chưa có đề xuất mức viêm từ AI trên khung hình hiện tại. Hãy đợi phân tích hoàn tất hoặc chuyển khung siêu âm.":`Đề xuất hiện tại: mức ${t} (${o}), dựa trên diện tích dịch khớp và màng hoạt dịch. Nếu bạn không đồng ý, nêu mức độ lâm sàng mong muốn — tôi sẽ hỗ trợ ghi đè có kiểm soát.`:/đo|thickness|mm|độ dày/.test(a)?n==="sup-up-long"?"Độ dày (dịch + màng hoạt dịch) chỉ được đo tự động trên Suprapatellar Upper Longitudinal. Kiểm tra HUD góc dưới ảnh khi bật overlay.":"Thước đo tự động không áp dụng cho góc quét này. Chỉ Suprapatellar Upper Longitudinal có đường đo trên overlay.":"Cảm ơn bạn. Tôi có thể giải thích đề xuất AI, hướng dẫn đọc overlay, hoặc ghi nhận chỉnh sửa phân loại. Hãy nêu cụ thể vùng giải phẫu hoặc mức độ bạn muốn thảo luận."}const si=/^(xin chào|chào|hello|hi|hey|cảm ơn|thanks|ok|oke|được|rồi)[\s!.?]*$/i,bd=/\b(là gì|what is|giải thích|explain|tóm tắt|what does|nghĩa là|tại sao|why|hoài nghi|nghi ngờ|không đồng ý|chưa thuyết phục|sửa|chỉnh|ghi đè|overlay|góc quét|màu|legend|thảo luận|clarify)\b/i,Pa=/\b(kế hoạch|plan|planning|bước|steps|lộ trình|workflow|tiếp theo|approach|checklist|quy trình|làm sao tiếp|roadmap)\b/i,sn=/\b(guideline|moh|evidence|tra cứu|trích dẫn|citation|synovitis|chẩn đoán|diagnosis|grade|độ viêm|nghiên cứu|literature|exa|corpus|semantic|medgemma|escalate|cập nhật gần đây|recent|latest)\b/i,li=["chat","planning","agent"];function _d(e){let t="chat",n=-1/0;for(const a of li)Jt(a)&&e[a]>n&&(n=e[a],t=a);return t}function xd(e,t){const n=li.map(o=>e[o]).sort((o,i)=>i-o),a=n[0]-n[1];return a>=3?"high":a>=2?"medium":"low"}function vd(e,t){const n=t.trim();switch(e){case"chat":return si.test(n)?"Tin nhắn ngắn hoặc chào hỏi — dùng Chat mức độ 🧘 (thấp).":"Thảo luận hoặc làm rõ đề xuất AI — chế độ Trò chuyện phù hợp hơn.";case"planning":return"Yêu cầu lập bước/quy trình — dùng chế độ Lập kế hoạch.";case"agent":return"Cần tra cứu bằng chứng hoặc trích dẫn — dùng chế độ Agent.";default:return""}}function yd(e,t){const n=e.trim(),a=n.length,o={chat:1,planning:0,agent:0};(a<=24||si.test(n))&&(o.chat+=3),a<=80&&!Pa.test(n)&&!sn.test(n)&&(o.chat+=1),bd.test(n)&&(o.chat+=3),a>80&&!sn.test(n)&&(o.chat+=1),Pa.test(n)&&(o.planning+=4),sn.test(n)&&(o.agent+=4),/\?/.test(n)&&a>40&&sn.test(n)&&(o.agent+=1),o[t]+=1;const i=_d(o),l=xd(o);return{scores:o,recommended:i,reason:vd(i,n),confidence:l}}function wd(e,t){return!Jt(e.recommended)||e.recommended===t?!1:e.confidence!=="low"}const kd=`Case-context rules: -- Background workspace findings come from AI on the scan frame — they are NOT typed by the clinician in chat. -- Do NOT mention synovitis grade, severity, or scan angle unless the clinician asks about the AI proposal, overlay, grading, or current case. -- Never say "based on information you provided" for workspace or AI findings. -- For greetings or general messages, reply briefly and invite a clinical question. -- When the clinician asks about the case, you may reference the background findings below.`,jd="You are a friendly assistant in a musculoskeletal ultrasound clinical workspace. Be warm, concise, and helpful.",Sd=`Chill mode (casual / low effort): -- Reply naturally in Vietnamese when the user writes in Vietnamese. Prefer short answers (1–3 sentences). -- Casual questions, small talk, and general knowledge are welcome — answer briefly instead of refusing. -- When the user asks about the scan, overlay, synovitis grade, or AI findings, focus on clinical ultrasound support. -- Do not mention workspace or AI findings unless the user asks about the case, overlay, or grading. -- Do not prescribe medications or dosages.`;function Aa(e){if(e.severityLoading)return"Background: AI severity for the current frame is still loading — do not invent a grade.";if(e.grade===null)return"Background: No AI synovitis grade is available for the current frame yet.";const t=qt[e.grade]??`Độ ${e.grade}`;let n=`Background workspace finding (AI on current frame, not from chat): synovitis grade ${e.grade} (${t}).`;return e.angleClass&&(n+=` Scan angle: ${Qe(He(e.angleClass))}.`),n}function Nd(e){if(e.severityLoading)return"Ngữ cảnh ca: AI đang phân tích mức viêm…";if(e.grade===null)return"Ngữ cảnh ca: chưa có đề xuất mức viêm";const t=qt[e.grade]??`Độ ${e.grade}`,n=e.angleClass?` · ${Qe(He(e.angleClass))}`:"";return`Ngữ cảnh ca: viêm màng hoạt dịch mức ${e.grade} (${t})${n}`}function Ra(e,t="moderate"){return t==="chill"?[jd,Sd,Aa(e)].join(` - -`):[Qc,kd,"Respond in Vietnamese when the clinician writes in Vietnamese. Do not prescribe medications or dosages.",Aa(e)].join(` - -`)}async function Md(){const e=[],t=typeof navigator<"u"&&"gpu"in navigator&&!!navigator.gpu,n=typeof navigator<"u"&&!!navigator.storage&&typeof navigator.storage.getDirectory=="function",a=typeof Worker<"u",o=typeof window<"u"&&window.isSecureContext;return t||e.push("WebGPU is unavailable. Gemma 4 E2B browser inference requires WebGPU (Chrome/Edge desktop)."),n||e.push("OPFS is unavailable. Model caching on-device will not work in this browser."),o||e.push("Not a secure context. Use https:// or http://localhost."),n&&location.protocol!=="https:"&&location.hostname!=="localhost"&&e.push("OPFS is origin-scoped: caches are isolated per scheme+host+port. Models downloaded on one origin (e.g. localhost, 127.0.0.1, preview domains) are not available on another. Expect a full re-download when the origin changes."),{webgpu:t,opfs:n,worker:a,secureContext:o,ready:t&&n&&a&&o,notes:e}}function Gn(){return`req_${Date.now()}_${Math.random().toString(36).slice(2,8)}`}class Cd{constructor(){be(this,"worker");be(this,"initialized",!1);be(this,"activeGenerateRequestId",null);be(this,"pending",new Map);this.worker=new Worker(new URL("/assets/llm.worker-D-LkqiEp.js",import.meta.url),{type:"module"}),this.worker.onmessage=t=>{var o,i,l,c;const n=t.data,a=this.pending.get(n.requestId);if(a){if(n.type==="init_progress"){(o=a.onProgress)==null||o.call(a,n.message);return}if(n.type==="generate_progress"){(i=a.onProgress)==null||i.call(a,n.message);return}if(n.type==="segment_start"){(l=a.onSegmentStart)==null||l.call(a,n.segment);return}if(n.type==="token"){(c=a.onToken)==null||c.call(a,n.partial,n.done,n.segment);return}if(n.type==="generate_done"){this.pending.delete(n.requestId),this.activeGenerateRequestId===n.requestId&&(this.activeGenerateRequestId=null),a.resolve(n.stats);return}if(n.type==="init_done"){this.pending.delete(n.requestId),this.initialized=!0,a.resolve({init_ms:n.init_ms});return}if(n.type==="release_done"){this.pending.delete(n.requestId),this.initialized=!1,a.resolve(void 0);return}(n.type==="init_error"||n.type==="generate_error")&&(this.pending.delete(n.requestId),n.type==="init_error"&&(this.initialized=!1),this.activeGenerateRequestId===n.requestId&&(this.activeGenerateRequestId=null),a.reject(new Error(n.error)))}}}get isInitialized(){return this.initialized}init(t,n,a,o){const i=Gn();return new Promise((l,c)=>{this.pending.set(i,{resolve:l,reject:c,onProgress:o}),this.worker.postMessage({type:"init",requestId:i,modelFilename:t,decode:n,promptOptions:a})})}generateRawConversation(t,n,a,o,i){const l=Gn();return this.activeGenerateRequestId=l,new Promise((c,d)=>{this.pending.set(l,{resolve:u=>{const h=u;c({rawOutput:h.rawOutput??"",stats:h})},reject:d,onToken:u=>o==null?void 0:o(u),onSegmentStart:u=>i==null?void 0:i(u)}),this.worker.postMessage({type:"generate",requestId:l,userText:"",rawConversationPrompt:t,promptOptions:n,decode:a})})}release(){if(!this.initialized)return Promise.resolve();const t=Gn();return new Promise((n,a)=>{this.pending.set(t,{resolve:n,reject:a}),this.worker.postMessage({type:"release",requestId:t})})}cancelActiveGeneration(){this.activeGenerateRequestId&&this.worker.postMessage({type:"cancel",requestId:this.activeGenerateRequestId})}}const Ed="gemma-4-E2B-it-web",De="gemma-4-E2B-it-web.task",Rr="gemma-4-E2B-it-web.manifest.json",Zt="gemma-4-E2B-it-web.download.json",ot=2003697664,Vt="https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it-web.task",Td="qwen3-0.6b-reasoning",Id="qwen3_0.6b_q4_block32_ekv1280.litertlm",Ld="qwen3-0.6b-reasoning.manifest.json",Pd={checkpointFilename:`${Td}.download.json`},Ad=500*1024*1024;function Rd(e){return e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/(1024*1024)).toFixed(1)} MB`:`${(e/(1024*1024*1024)).toFixed(2)} GB`}const Da=new TextEncoder().encode("TFL3");function Dr(e){return e.length<6?!1:e.length>=4+Da.length&&new TextDecoder().decode(e.subarray(4,4+Da.length))==="TFL3"?!0:e[4]===80&&e[5]===75}function Or(e){const t=new TextDecoder().decode(e).trimStart();return t.startsWith("<")||t.startsWith("{")||t.startsWith("[")}function ci(e){if(e.endsWith(".litertlm"))throw new Error(`"${e}" is a LiteRT-LM checkpoint, not MediaPipe. Delete OPFS cache and download ${De} instead.`);if(!e.endsWith(".task"))throw new Error(`"${e}" is not a MediaPipe .task file. Use ${De} from the litert-community HuggingFace repo.`)}async function di(e,t=8){const n=await e.slice(0,t).arrayBuffer();return new Uint8Array(n)}async function ui(e){if(ci(e.name),e.size56&&(this.buffer.fill(0,this.bufferLength),this.hashBlock(this.buffer),this.bufferLength=0),this.buffer.fill(0,this.bufferLength,56);const n=new DataView(this.buffer.buffer,this.buffer.byteOffset,this.buffer.byteLength);return n.setUint32(56,Math.floor(t/4294967296),!1),n.setUint32(60,t>>>0,!1),this.hashBlock(this.buffer),[...this.state].map(a=>a.toString(16).padStart(8,"0")).join("")}hashBlock(t){const n=new Uint32Array(64),a=new DataView(t.buffer,t.byteOffset,t.byteLength);for(let f=0;f<16;f++)n[f]=a.getUint32(f*4,!1);for(let f=16;f<64;f++){const g=Ye(n[f-15],7)^Ye(n[f-15],18)^n[f-15]>>>3,N=Ye(n[f-2],17)^Ye(n[f-2],19)^n[f-2]>>>10;n[f]=n[f-16]+g+n[f-7]+N>>>0}let o=this.state[0],i=this.state[1],l=this.state[2],c=this.state[3],d=this.state[4],u=this.state[5],h=this.state[6],p=this.state[7];for(let f=0;f<64;f++){const g=Ye(d,6)^Ye(d,11)^Ye(d,25),N=d&u^~d&h,y=p+g+N+Dd[f]+n[f]>>>0,k=Ye(o,2)^Ye(o,13)^Ye(o,22),A=o&i^o&l^i&l,I=k+A>>>0;p=h,h=u,u=d,d=c+y>>>0,c=l,l=i,i=o,o=y+I>>>0}this.state[0]=this.state[0]+o>>>0,this.state[1]=this.state[1]+i>>>0,this.state[2]=this.state[2]+l>>>0,this.state[3]=this.state[3]+c>>>0,this.state[4]=this.state[4]+d>>>0,this.state[5]=this.state[5]+u>>>0,this.state[6]=this.state[6]+h>>>0,this.state[7]=this.state[7]+p>>>0}}function Ye(e,t){return e>>>t|e<<32-t}async function zd(e){const t=new Od,n=e.stream().getReader();for(;;){const{done:a,value:o}=await n.read();if(a)break;t.update(o)}return t.digestHex()}const Fd="models",$d=5*1024*1024,Oa=16,Bd=2e3,Hd=750;async function tt(){return(await navigator.storage.getDirectory()).getDirectoryHandle(Fd,{create:!0})}async function Gd(){try{const n=await(await(await tt()).getFileHandle(Zt)).getFile();return JSON.parse(await n.text())}catch{return null}}async function Wd(e){const a=await(await(await tt()).getFileHandle(Zt,{create:!0})).createWritable();await a.write(JSON.stringify(e,null,2)),await a.close()}async function Ud(){try{await(await tt()).removeEntry(Zt)}catch{}}const Vd=12e3;function za(){return typeof AbortSignal<"u"&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(Vd):void 0}async function Kd(e){let t=await fetch(e,{method:"HEAD",redirect:"follow",signal:za()});if(t.ok||(t=await fetch(e,{headers:{Range:"bytes=0-0"},redirect:"follow",signal:za()})),!t.ok&&t.status!==206)throw new Error(`Model download probe failed: HTTP ${t.status} from HuggingFace`);const n=(t.headers.get("accept-ranges")??"").toLowerCase();let a=Number(t.headers.get("content-length")??"0")||null;if(t.status===206){const o=t.headers.get("content-range"),i=o==null?void 0:o.match(/\/(\d+)$/);i&&(a=Number(i[1]))}return a||(a=ot),{bytesTotal:a,supportsRange:n==="bytes"||t.status===206}}function qd(e){const t=e==null?void 0:e.match(/\/(\d+)$/);return t?Number(t[1]):null}async function Xd(e,t,n){const a=(t==null?void 0:t.size)??0;if(n&&n.source_url===Vt&&n.filename===De&&n.bytes_written>a&&await jt(a,n.bytes_total),a===0)return 0;if(e.bytesTotal&&a>=e.bytesTotal)return e.bytesTotal;if(!e.supportsRange)return 0;const o=await di(t);return Or(o)||!Dr(o)?(await hi(),0):a}async function hi(){const e=await tt();for(const t of[De,Zt])try{await e.removeEntry(t)}catch{}}function Fa(e){return new Promise(t=>{window.setTimeout(t,e)})}function $a(e){if(e instanceof TypeError||e instanceof DOMException&&(e.name==="NetworkError"||e.name==="TimeoutError"))return!0;const t=(e instanceof Error?e.message:String(e)).toLowerCase();return t.includes("network")||t.includes("network_changed")||t.includes("err_network_changed")||t.includes("failed to fetch")||t.includes("load failed")||t.includes("interrupted")||t.includes("timed out")||t.includes("timeout")||t.includes("gián đoạn")||t.includes("http 502")||t.includes("http 503")||t.includes("http 429")||t.includes("http 504")}function Yd(e){if(e instanceof DOMException&&e.name==="NetworkError")return!0;const t=(e instanceof Error?e.message:String(e)).toLowerCase();return t.includes("network_changed")||t.includes("err_network_changed")||t.includes("network change")}function Ba(e,t){return Yd(e)?Hd:Bd*Math.min(t,4)}async function jt(e,t){await Wd({filename:De,source_url:Vt,bytes_total:t,bytes_written:e,updated_at:new Date().toISOString()})}async function Ha(e,t){const n=await ut(De),a=await Gd();return Xd(e,n,a)}function gi(e,t){return new Error(`Model download interrupted at ${Ve(e)} of ${Ve(t)} (network changed or connection lost). Retrying…`)}async function ut(e){try{return(await(await tt()).getFileHandle(e)).getFile()}catch{return null}}async function Ga(e){try{return await e.stream().getReader().cancel(),!0}catch{return!1}}async function hr(e){try{return await ui(e),null}catch(t){return t instanceof Error?t.message:String(t)}}function Rt(e,t,n){return{loadable:!1,reason:e,manifest:t,bytes:(n==null?void 0:n.size)??0,filename:(n==null?void 0:n.name)??null}}async function pi(){try{const n=await(await(await tt()).getFileHandle(Rr)).getFile(),a=JSON.parse(await n.text());return a.runtime="mediapipe",a}catch{return null}}async function Nn(){const e=await pi();if(e){try{ci(e.filename)}catch(a){return Rt(a instanceof Error?a.message:String(a),e,null)}const n=await ut(e.filename);if(n&&n.size>0&&n.size!==e.bytes)return Rt(`Partial download in OPFS (${Ve(n.size)} of ${Ve(e.bytes)}). Will resume on next install.`,e,n);if(n&&n.size>0&&await Ga(n)){const a=await hr(n);return a?Rt(a,e,n):{loadable:!0,reason:`Loadable from OPFS · MediaPipe (.task) · ${Ve(n.size)}`,manifest:e,bytes:n.size,filename:e.filename}}}const t=await ut(De);if(t&&t.size>0&&await Ga(t)){if(t.size{gr.delete(e)}}function Je(e){for(const t of gr)t(e)}async function Qd(){const e=await Nn();if(e.loadable)return e;Dt||(Dt=tu().catch(n=>{throw Dt=null,n})),await Dt;const t=await Nn();if(!t.loadable)throw new Error("Model install finished but OPFS checkpoint is still not loadable.");return t}function eu(e){const t=(e instanceof Error?e.message:String(e)).toLowerCase();return t.includes("no model format matched")||t.includes("too small")||t.includes("html/json")||t.includes("not a valid mediapipe")||t.includes("wrong file header")}async function tu(){for(let e=0;e<2;e+=1)try{return await ru()}catch(t){if(e===0&&eu(t)){await Jd();continue}throw t}throw new Error("Model install failed after clearing a corrupt OPFS cache.")}async function nu(e,t){var y,k;const n={};e>0&&(n.Range=`bytes=${e}-`);const a=await fetch(Vt,{headers:n,redirect:"follow"});if(!a.ok&&a.status!==206)throw new Error(`Model download failed: HTTP ${a.status} from HuggingFace`);if((a.headers.get("content-type")??"").includes("text/html"))throw new Error("HuggingFace returned HTML instead of the model file. Check network access and the model URL.");let i=t;const l=qd(a.headers.get("content-range"));if(l?i=l:a.status===200&&(i=Number(a.headers.get("content-length")??"0")||i||ot),!a.body)throw new Error("Model download failed: empty response body from HuggingFace");const d=await(await tt()).getFileHandle(De,{create:!0});let u=await d.createWritable({keepExistingData:e>0}),h=e;a.status===200&&e>0?(await u.close(),h=0,i=Number(a.headers.get("content-length")??"0")||i||ot,Je({phase:"downloading",bytesLoaded:0,bytesTotal:i}),u=await d.createWritable()):e>0&&await u.seek(e);const p=a.body.getReader();let f=h,g=0;try{for(;;){const{done:A,value:I}=await p.read();if(A)break;if(h===0&&f===0&&I.byteLength>0){const w=I.subarray(0,Math.min(8,I.byteLength));if(Or(w)||!Dr(w))throw new Error("Download stream is not a valid MediaPipe .task file (wrong file header from CDN).")}await u.write(I),f+=I.byteLength,g+=I.byteLength,Je({phase:"downloading",bytesLoaded:f,bytesTotal:i}),g>=$d&&(await jt(f,i),g=0)}}catch(A){try{await p.cancel()}catch{}try{await u.close()}catch{}const I=((y=await ut(De))==null?void 0:y.size)??f;throw await jt(I,i),A}await u.close();const N=((k=await ut(De))==null?void 0:k.size)??f;if(i&&N0&&p>=n){const f=await ut(De);if(f?await hr(f):"Model file missing"){await hi(),a=0;continue}break}p>0?Je({phase:"resuming",bytesLoaded:p,bytesTotal:n}):Je({phase:"downloading",bytesLoaded:0,bytesTotal:n});try{const f=await nu(p,n);if(n=n||f,n>0&&f>=n){a=0;break}if(a+=1,await jt(f,n),a>=Oa)throw gi(f,n||ot);Je({phase:"resuming",bytesLoaded:f,bytesTotal:n}),await Fa(Ba(new Error("interrupted"),a))}catch(f){const g=await Ha(e);if(await jt(g,n),!$a(f)||a>=Oa)throw $a(f)&&g>0&&n&&g(St=n,n)).catch(n=>{throw ln=null,n}));const t=e.onDownloadProgress?Zd(e.onDownloadProgress):void 0;try{return await ln}finally{t==null||t()}}async function ou(){const e=await Nn(),t=await Qd();if(!t.loadable||!t.filename)throw new Error("Gemma checkpoint is not available in OPFS after install.");return{modelFilename:t.filename,didInstallGemma:!e.loadable,didInstallQwen:!1}}async function fi(e){const t=()=>iu(e),n=Wa.then(t);return Wa=n.then(()=>{},()=>{}),n}async function iu(e){if(!St)throw new Error("OPFS checkpoints are not ready.");const t=e.onInitProgress;return pr==="gemma"&&(Be!=null&&Be.isInitialized)?{gemmaClient:Be}:(Be||(Be=new Cd),await Be.init(St.modelFilename,od,{systemPrompt:"",chainOfThought:!1},t),pr="gemma",{gemmaClient:Be})}function su(){return pr==="gemma"&&(Be==null?void 0:Be.isInitialized)===!0}function lu(){return Be!=null&&Be.isInitialized?Be:null}async function cu(e){if(!St)throw new Error("OPFS checkpoints are not ready.");return fi({onInitProgress:e==null?void 0:e.onInitProgress})}function du(e){const t={model:sd(),messages:e.messages,stream:!0,options:{num_predict:e.numPredict}};return e.think&&(t.think=!0),t}function uu(e){let t=e.trim();return!t||t==="[DONE]"||(t.startsWith("data:")&&(t=t.slice(5).trim()),t.startsWith("event:")||t.startsWith(":"))?"":t}function mi(e){const t=uu(e);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function hu(e){var t,n;return{thinkingDelta:((t=e.message)==null?void 0:t.thinking)??"",contentDelta:((n=e.message)==null?void 0:n.content)??""}}function Ua(e,t){return t?e?t===e?e:t.startsWith(e)?t:e.endsWith(t)?e:e+t:t:e}function bi(e,t,n){var d,u;let{thinkingText:a,answerText:o,segments:i}=e;const{thinkingDelta:l,contentDelta:c}=hu(t);if(l){const h=Ua(a,l),p=h.slice(a.length);a=h,p&&((d=n.onThinkingDelta)==null||d.call(n,p))}if(c){const h=Ua(o,c),p=h.slice(o.length);o=h,p&&((u=n.onContentDelta)==null||u.call(n,p),i+=1)}return{thinkingText:a,answerText:o,segments:i}}function gu(){return new Promise(e=>{window.setTimeout(e,0)})}const Wn=8;function pu(e){if(e.length<=Wn)return[e];const t=[];for(let n=0;nu.push({kind:"thinking",delta:h}),onContentDelta:h=>u.push({kind:"content",delta:h})});for(const h of u){const p=h.kind==="thinking"?n.onThinkingDelta:n.onContentDelta;if(p)for(const f of pu(h.delta))l&&await gu(),p(f),l=!0}}return{rest:o,state:i}}let Un=null;function mu(){return Un||(Un=new _i),Un}function bu(e){return e instanceof _i}function Va(e,t){return new Promise((n,a)=>{const o=window.setTimeout(()=>n(),e);t.addEventListener("abort",()=>{window.clearTimeout(o),a(new DOMException("Ollama request aborted","AbortError"))},{once:!0})})}function _u(e){return e===500||e===502||e===503||e===504||e===429}function xu(e){return e instanceof DOMException&&e.name==="AbortError"?!1:e instanceof TypeError||e instanceof DOMException&&e.name==="NetworkError"}class _i{constructor(){be(this,"activeAbort",null);be(this,"coldStartHandlers",null)}async runWithColdStartHandlers(t,n){this.coldStartHandlers=t;try{return await n()}finally{this.coldStartHandlers=null}}async generateRawConversation(t,n,a,o){return this.chat({messages:[{role:"user",content:t}],think:!1,decode:a},{onContentToken:o})}async generateChatTurn(t,n,a){const o=[{role:"system",content:t.systemPrompt},...t.history,{role:"user",content:t.userMessage}],{rawOutput:i,stats:l,answerText:c}=await this.chat({messages:o,think:t.think,decode:n},a);return{rawOutput:i,finalAnswer:c||i,stats:l}}cancelActiveGeneration(){var t;(t=this.activeAbort)==null||t.abort(),this.activeAbort=null}emitColdStartWait(t){var n,a;(a=(n=this.coldStartHandlers)==null?void 0:n.onWait)==null||a.call(n,t)}emitColdStartReady(){var t,n;(n=(t=this.coldStartHandlers)==null?void 0:t.onReady)==null||n.call(t)}async fetchChatWithColdStartRetry(t,n,a){const o=performance.now();let i=0;for(;;){i+=1;const l=Math.round(performance.now()-o);l>0&&i>1?this.emitColdStartWait({attempt:i,elapsedMs:l}):i===1&&this.emitColdStartWait({attempt:1,elapsedMs:0});try{const c=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),signal:a.signal});if(c.ok)return this.emitColdStartReady(),c;const d=await c.text(),u=_u(c.status),h=performance.now()-o{var z;(z=n==null?void 0:n.onThinkingToken)==null||z.call(n,L)},onContentDelta:L=>{var z;(z=n==null?void 0:n.onContentToken)==null||z.call(n,L)}});h=w.rest,l=w.state}if(h.trim()){const A=mi(h);A&&(l=bi(l,A,{onThinkingDelta:I=>{var w;return(w=n==null?void 0:n.onThinkingToken)==null?void 0:w.call(n,I)},onContentDelta:I=>{var w;return(w=n==null?void 0:n.onContentToken)==null?void 0:w.call(n,I)}}))}}finally{d.releaseLock(),this.activeAbort===o&&(this.activeAbort=null)}const{thinkingText:p,answerText:f,segments:g}=l,N=t.think&&p?`${p}${f}`:f,y={segments:Math.max(1,g),promptTokens:0,outputTokens:p.length+f.length,continued:!1,truncated:!1,rawOutput:N};return{rawOutput:N,thinkingText:p,answerText:f,stats:y}}}function Ka(e){if(e.phase==="hashing")return"Đang xác minh tệp…";if(e.phase==="writing")return"Đang lưu manifest…";const t=e.bytesTotal??ot;return`${Ve(e.bytesLoaded)} / ${Ve(t)}`}function vu(e){switch(e.phase){case"downloading":case"resuming":{if(!e.bytesTotal||e.bytesTotal<=0)return e.phase==="resuming"?6:4;const t=Math.min(1,e.bytesLoaded/e.bytesTotal);return 4+Math.round(t*92)}case"hashing":return 97;case"writing":return 98;case"done":return 99;default:return 4}}function qa(e,t){return e.includes("WASM")||e.includes("WebGPU")?Math.max(t,35):e.includes("Validating")?Math.max(t,55):e.includes("Initializing Gemma")||e.includes("OPFS")?Math.max(t,72):e.includes("Qwen3")||e.includes("LiteRT-LM")?Math.max(t,88):Math.max(t,45)}const Xa=200;function Ya(e,t={}){const n=t.start??12,a=t.cap??92,o=t.intervalMs??700,i=t.step??2;let l=n;e(l);const c=window.setInterval(()=>{l=Math.min(a,l+i),e(l)},o);return()=>window.clearInterval(c)}function Ja(e,t){const n=Math.min(e.length,t.length-1);for(let a=n;a>0;a-=1)if(t.startsWith(e.slice(-a)))return e.slice(-a);return""}function Za(){let e="preamble",t="",n="",a="",o=!1;function i(d){let u=t+d;for(t="";u.length>0;){if(e==="preamble"){const h=u.indexOf(Bt);if(h===-1){t=Ja(u,Bt),u=u.slice(0,u.length-t.length),u&&(a+=u);break}u=u.slice(h+Bt.length).replace(/^\n/,""),e="thought";continue}if(e==="thought"){const h=u.indexOf(Ht);if(h===-1){t=Ja(u,Ht),n+=u.slice(0,u.length-t.length);break}n+=u.slice(0,h),u=u.slice(h+Ht.length),e="answer",o=!0;continue}a+=u;break}return l()}function l(){return{thoughtContent:n,content:a,thoughtComplete:o}}function c(){return{thoughtContent:n.trim(),content:a.trim(),thoughtComplete:o}}return{append:i,snapshot:l,finalize:c}}function yu(e){let t=null,n=null,a=!0;function o(h){a&&e(h)}function i(){if(n=null,t===null||!a){t=null;return}const h=t;t=null,o(h)}function l(h){a&&(t=h,n===null&&(n=requestAnimationFrame(i)))}function c(h){a&&(n!==null&&(cancelAnimationFrame(n),n=null),t=null,o(h))}function d(){n!==null&&(cancelAnimationFrame(n),n=null),t=null}function u(){if(n!==null&&(cancelAnimationFrame(n),n=null),t!==null&&a){const h=t;t=null,o(h)}a=!1,t=null}return{schedule:l,flushNow:c,cancel:d,dispose:u}}const Kt=new Map;function Mt(e,t){return`${e}:${t}`}function Qa(e,t){if(t){Kt.set(e,t);return}Kt.delete(e)}function eo(e,t){const n=Kt.get(e);return n?(n.textContent!==t&&(n.textContent=t),!0):!1}function Vn(e){Kt.delete(Mt(e,"thought")),Kt.delete(Mt(e,"answer"))}function wu(e,t){return t?{maxTokens:t.maxTokens!==void 0?Math.max(e.maxTokens,t.maxTokens):e.maxTokens,topK:t.topK??e.topK,temperature:t.temperature??e.temperature,randomSeed:t.randomSeed??e.randomSeed}:e}async function ku(e,t,n,a){const o=new Ri({baseSystemPrompt:e.baseSystemPrompt,config:e.agentLoopConfig??{...Jn,mockTools:e.mockTools??!0,bffBaseUrl:e.bffBaseUrl??Jn.bffBaseUrl},onEvent:l=>{n==null||n(l)},generateStep:async({conversationPrompt:l,chainOfThought:c,expectedKind:d,decodeOverride:u})=>{if(a!=null&&a.aborted)throw new DOMException("Agent turn aborted","AbortError");const h={...e.readPromptOptions(),chainOfThought:c},p=wu(e.readDecodeParams(),u),f=d==="final"?N=>{n==null||n({type:"final_token",partial:N})}:void 0,{rawOutput:g}=await e.llmClient.generateRawConversation(l,h,p,f);if(a!=null&&a.aborted)throw new DOMException("Agent turn aborted","AbortError");return{rawOutput:g}}}),i=e.readPromptOptions();return o.runTurn({sessionId:t.sessionId,userMessage:t.userMessage,chainOfThought:i.chainOfThought},a)}function ju(e,t){const n=[];for(const a of e){if(a.streaming||!a.content.trim()||a.role!=="user"&&a.role!=="assistant")continue;const o=a.role==="user"?"user":"assistant",i=n[n.length-1];i&&i.role===o?i.text=`${i.text}${a.content}`:n.push({role:o,text:a.content})}return n.slice(-t*2)}function Su(e){const t=Di(e,!1);return t.kind==="plan"?t.checklist.map((n,a)=>`${a+1}. ${n}`).join(` -`):e.trim()}async function Nu(e,t,n){const a=t.chatReasoningLevel??dt,o=Ke(a),i=ii(t.inferenceMode,a,t.reasoningModelBackend),l={...e.readPromptOptions()};t.inferenceMode==="planning"?(l.systemPrompt=`${e.baseSystemPrompt}${fd}`,l.chainOfThought=!1):l.chainOfThought=o.chainOfThought;const c=ju(t.historyMessages,t.inferenceMode==="chat"?o.maxHistoryTurns:6);if(n!=null&&n.aborted)throw new DOMException("Chat turn aborted","AbortError");if(t.inferenceMode==="chat"&&o.chainOfThought&&bu(e.llmClient)){const f=c.map(N=>({role:N.role==="user"?"user":"assistant",content:N.text})),g=await e.llmClient.generateChatTurn({systemPrompt:e.baseSystemPrompt,userMessage:t.userMessage,history:f,think:!0},i,{onThinkingToken:t.onThoughtToken,onContentToken:t.onToken});if(n!=null&&n.aborted)throw new DOMException("Chat turn aborted","AbortError");return{finalAnswer:g.finalAnswer,rawOutput:g.rawOutput}}const d=td(t.userMessage,l,c),{rawOutput:u}=await e.llmClient.generateRawConversation(d,l,i,t.onToken);if(n!=null&&n.aborted)throw new DOMException("Chat turn aborted","AbortError");const h=t.inferenceMode==="chat"?o.chainOfThought:!1;return{finalAnswer:t.inferenceMode==="planning"?Su(u):nd(u,h),rawOutput:u}}async function Mu(e,t,n,a){if(t.inferenceMode==="agent")return ku({...e,mockTools:t.mockTools,bffBaseUrl:t.bffBaseUrl,agentLoopConfig:pd(t.inferenceMode,t.mockTools,t.bffBaseUrl)},{sessionId:t.sessionId,userMessage:t.userMessage},n,a);const o=await Nu(e,{inferenceMode:t.inferenceMode,chatReasoningLevel:t.chatReasoningLevel,reasoningModelBackend:t.reasoningModelBackend,userMessage:t.userMessage,historyMessages:t.historyMessages,onToken:i=>n==null?void 0:n({type:"final_token",partial:i}),onThoughtToken:i=>n==null?void 0:n({type:"thought_token",partial:i})},a);return{finalAnswer:o.finalAnswer,steps:1,toolCalls:[],toolResults:[],planChecklist:t.inferenceMode==="planning"?o.finalAnswer.split(` -`):[]}}function Cu(e,t=dt,n){return ii(e,t,n)}const Eu=15e3,Tu=3e3;function cn(e){return e==="installing-gemma"||e==="resuming-gemma"}function dn(){return`clinical-${Date.now()}-${Math.random().toString(36).slice(2,8)}`}function Kn(e,t,n){switch(e){case"chat":{const a=Ke(t);return a.chainOfThought?`${a.emoji} Đang suy luận…`:`${a.emoji} Đang trả lời…`}case"planning":return"Đang lập kế hoạch…";case"agent":return n==="tool"?"Agent · đang gọi công cụ…":"Agent · đang xử lý…";default:return"Đang trả lời…"}}function Iu(e,t){return e==="chat"&&Xc(t)}function Lu({grade:e,angleClass:t,severityLoading:n=!1,disabled:a=!1}){const o={grade:e,angleClass:t,severityLoading:n},i=Nd(o),[l,c]=s.useState(()=>an(ur)),[d,u]=s.useState(()=>Sa()),[h,p]=s.useState(()=>Sn(Dn())),[f,g]=s.useState(()=>Jc()),[N,y]=s.useState(()=>[on(e,t,ur,n,Sa())]),[k,A]=s.useState(""),[I,w]=s.useState(!1),[L,z]=s.useState("loading"),[X,R]=s.useState("Đang kiểm tra môi trường…"),[ie,U]=s.useState(!1),[v,B]=s.useState(null),[re,V]=s.useState(0),[ne,F]=s.useState(null),[_,O]=s.useState(!1),[S,P]=s.useState(!1),[j,ee]=s.useState(null),J=s.useRef(null),K=s.useRef(null),Ee=s.useRef(!1),pe=s.useRef(dn()),he=s.useRef(null),Ne=s.useRef(Ra(o,d)),ge=s.useRef(l),ae=s.useRef(d),E=s.useRef(h);s.useEffect(()=>{ge.current=l},[l]),s.useEffect(()=>{ae.current=d},[d]),s.useEffect(()=>{E.current=h},[h]);const $=s.useCallback((M,Z,te)=>_t(M,!0,Z,te),[]),se=s.useCallback(()=>Ra(o,d),[e,t,n,d]),ke=s.useCallback((M,Z)=>{pe.current=dn(),y([on(e,t,M,n,Z)]),ee(null),A(""),L==="llm"&&R($(M,Z,E.current))},[t,e,L,n,$]),Fe=s.useCallback(M=>{const Z=M??ge.current;c(Z),ke(Z,ae.current)},[ke]),Pe=s.useCallback(M=>{const Z=an(M);Z!==ge.current&&Fe(Z)},[Fe]);s.useEffect(()=>{const M=an(ge.current);M!==ge.current&&(ge.current=M,c(M),ke(M,ae.current))},[ke]);const q=s.useCallback(M=>{ti(M)&&M!==ae.current&&(ae.current=M,u(M),Yc(M),L==="llm"&&ge.current==="chat"&&R($("chat",M,E.current)))},[L,$]),oe=s.useCallback(M=>{const Z=Sn(M);Z!==E.current&&(E.current=Z,p(Z),ld(Z),L==="llm"&&ge.current==="chat"&&R($("chat",ae.current,Z)))},[L,$]),we=s.useCallback(()=>{g(M=>{const Z=!M;return Zc(Z),Z})},[]);s.useEffect(()=>{Ne.current=se(),pe.current=dn(),y([on(e,t,ge.current,n,ae.current)]),ee(null)},[e,t,n,se]),s.useEffect(()=>{Ne.current=se()},[se]),s.useEffect(()=>{let M=!1,Z,te,me,Me=Date.now(),Oe=-1;const Te=()=>{me!==void 0&&(window.clearInterval(me),me=void 0)},Ce=()=>{Te(),Me=Date.now(),Oe=-1,O(!1),me=window.setInterval(()=>{M||Date.now()-Me>Eu&&(O(!0),R("Tải Gemma bị gián đoạn — đang thử kết nối lại…"))},Tu)},ze=()=>{Me=Date.now(),O(!1)};async function $e(){M||(Te(),O(!1),V(100),P(!0),await new Promise(Le=>{Z=window.setTimeout(Le,Xa)}),!M&&(U(!1),P(!1),B(null),F(null)))}async function Ie(){if(!(M||su())){B("loading-gemma"),U(!0),V(12),R("Đang nạp Gemma 4 E2B vào bộ nhớ…"),te==null||te(),te=Ya(Le=>{M||V(m=>Math.max(m,Le))},{start:12,cap:94,intervalMs:650,step:3});try{const Le=await cu({onInitProgress:m=>{M||V(x=>qa(m,x))}});if(M)return;J.current=Le.gemmaClient,await $e(),M||R(_t(ge.current,!0,ae.current,E.current))}catch(Le){if(M)return;U(!1),P(!1),B(null);const m=Le instanceof Error?Le.message:"không nạp được Gemma";R(`Sẵn sàng · nạp Gemma thất bại (${m})`)}finally{te==null||te(),te=void 0}}}async function Ae(){var x;const Le=await Md();if(M||V(5),!Le.ready){M||(z("mock"),R("Chế độ demo · WebGPU/OPFS không khả dụng"));return}const m=await Nn();if(!M&&(V(8),!m.loadable)){const C=m.bytes>0;B(C?"resuming-gemma":"installing-gemma"),U(!0),R(C?"Đang tiếp tục tải Gemma 4 E2B…":"Đang cài đặt Gemma 4 E2B về máy…"),C&&F(Ka({phase:"resuming",bytesLoaded:m.bytes,bytesTotal:((x=m.manifest)==null?void 0:x.bytes)??null})),Ce()}try{if(await au({onDownloadProgress:C=>{if(M||(U(!0),F(Ka(C)),V(vu(C)),!(C.bytesLoaded>Oe)))return;Oe=C.bytesLoaded,ze();const D=C.phase==="resuming";B(D?"resuming-gemma":"installing-gemma"),R(D?"Đang tiếp tục tải Gemma 4 E2B…":"Đang cài đặt Gemma 4 E2B về máy…")}}),M)return;if(J.current=null,Ee.current=!0,m.loadable){z("llm"),Ie();return}if(await $e(),M)return;z("llm"),R(_t(ge.current,!0,ae.current,E.current)),Ie()}catch(C){if(Te(),!M){U(!1),P(!1),B(null),F(null),O(!1),z("mock");const T=C instanceof Error?C.message:"không tải được mô hình",D=C instanceof TypeError||C instanceof DOMException&&(C.name==="NetworkError"||C.name==="TimeoutError")||/network|failed to fetch|interrupted|timed out|timeout|gián đoạn|network_changed|err_network_changed/i.test(T);R(D?"Chế độ demo · Mạng gián đoạn — tải lại trang để tiếp tục cài đặt":`Chế độ demo · ${T}`)}}}return Ae(),()=>{M=!0,te==null||te(),Te(),Z!==void 0&&window.clearTimeout(Z)}},[]);const de=s.useCallback(()=>{const M=ge.current,Z=ae.current,te=M==="chat"&&Ke(Z).chainOfThought;return{systemPrompt:se(),chainOfThought:te}},[se]),Se=s.useCallback(()=>Cu(ge.current,ae.current,E.current),[]),je=s.useCallback((M,Z)=>{y(te=>te.map(me=>me.id===M?{...me,...Z}:me))},[]),G=s.useCallback(()=>{var M,Z,te;I&&((M=he.current)==null||M.abort(),(Z=J.current)==null||Z.cancelActiveGeneration(),(te=K.current)==null||te.cancelActiveGeneration(),y(me=>me.map(Me=>Me.streaming?{...Me,streaming:!1}:Me)),R(L==="llm"?_t(ge.current,!0,ae.current,E.current):"Chế độ demo (mock)"),w(!1),he.current=null)},[I,L]),_e=s.useCallback((M,Z,te,me)=>{const Me={id:st(),role:"user",content:M,timestamp:new Date};y(Ce=>[...Ce,Me]),A(""),ee(null),w(!0);const Oe=L==="llm"&&Ee.current,Te=new AbortController;he.current=Te,(async()=>{const Ce=an(Z);if(!Oe){window.setTimeout(()=>{const ue={id:st(),role:"assistant",content:md(M,e,t),timestamp:new Date};y(Re=>[...Re,ue]),w(!1),he.current=null},650);return}const ze=cd(Ce,te,E.current);let $e;if(ze)R(Kn(Ce,te)),$e=mu(),K.current=$e,J.current=null;else{const ue=lu();if(ue)$e=ue,J.current=ue,K.current=null;else{B("loading-gemma"),U(!0),V(12),R("Đang nạp Gemma 4 E2B vào bộ nhớ…");const Re=Ya(ye=>{V(nt=>Math.max(nt,ye))});let ve=null;try{const ye=await fi({inferenceMode:Ce,chatReasoningLevel:te,onInitProgress:nt=>{V(Tt=>qa(nt,Tt))}});if(Te.signal.aborted)return;ve=ye.gemmaClient,J.current=ve,K.current=null,V(100),P(!0),await new Promise(nt=>{window.setTimeout(nt,Xa)}),U(!1),P(!1),B(null)}catch(ye){U(!1),P(!1),B(null);const nt=ye instanceof Error?ye.message:"không tải được mô hình";y(Tt=>[...Tt,{id:st(),role:"assistant",content:`Không thể khởi tạo mô hình: ${nt}`,timestamp:new Date}]),R("Lỗi · Gemma 4 E2B"),w(!1),he.current=null;return}finally{Re()}if(!ve){y(ye=>[...ye,{id:st(),role:"assistant",content:"Gemma chưa sẵn sàng. Tải lại trang và thử lại.",timestamp:new Date}]),w(!1),he.current=null;return}$e=ve}}if(Te.signal.aborted){w(!1),he.current=null;return}const Ie=Iu(Ce,te),Ae=ze&&Ie;let Le="",m="",x=st(),C=1;const T=(ue,Re)=>({id:ue,role:"assistant",content:"",timestamp:new Date,streaming:!0,tracksThought:Ie,pondering:Re,ponderingVariant:Ce==="agent"?"agent":"chat"});y(ue=>[...ue,T(x,ze)]),R(Kn(Ce,te));let D="",H=Ie&&!Ae?Za():null,le=!1,xe=!1;const We=()=>{xe||(xe=!0,je(x,{pondering:!1}))},Ue=ze||Ie,ht=yu(ue=>{const Re=x;y(ve=>ve.map(ye=>ye.id===Re?{...ye,...ue}:ye))}),Xe=ue=>{const Re=x,ve=ue.thoughtContent!==void 0||ue.content!==void 0;if(Ue&&ve){let ye=!0;ue.thoughtContent!==void 0&&(ye=eo(Mt(Re,"thought"),ue.thoughtContent)&&ye),ue.content!==void 0&&(ye=eo(Mt(Re,"answer"),ue.content)&&ye),(!ye||ue.thoughtComplete!==void 0||ue.tracksThought!==void 0)&&Xt.flushSync(()=>{y(Tt=>Tt.map(On=>On.id===Re?{...On,...ue}:On))});return}ht.schedule({content:ue.content??"",thoughtContent:ue.thoughtContent,thoughtComplete:ue.thoughtComplete,tracksThought:ue.tracksThought})},gt=()=>{Ue||ht.dispose()},vi=()=>{if(H){const ue=H.finalize();je(x,{content:ue.content,thoughtContent:ue.thoughtContent||void 0,tracksThought:!0,thoughtComplete:!0,streaming:!1,pondering:!1})}else je(x,{content:D,streaming:!1,pondering:!1});Vn(x)},yi=()=>{vi(),Ue||ht.cancel();const ue=st();x=ue,C+=1,H=Ie&&!Ae?Za():null,D="",le=!1,y(Re=>[...Re,T(ue,!1)])};try{const Re=await Mu({llmClient:$e,baseSystemPrompt:se(),readPromptOptions:de,readDecodeParams:Se,mockTools:Na(),bffBaseUrl:Ma()},{inferenceMode:Ce,chatReasoningLevel:te,reasoningModelBackend:E.current,sessionId:pe.current,userMessage:M,historyMessages:me,mockTools:Na(),bffBaseUrl:Ma()},ve=>{try{if(ve.type==="segment_boundary"){ve.segment>=2&&!Ae&&yi();return}if(ve.type==="thought_token"){if(!ve.partial||!Ae)return;We(),Le+=ve.partial,Xe({tracksThought:!0,thoughtContent:Le,content:m,thoughtComplete:le});return}if(ve.type==="final_token"){if(!ve.partial)return;if(We(),Ae){!le&&(le=!0),m+=ve.partial,Xe({tracksThought:!0,thoughtContent:Le,content:m,thoughtComplete:le});return}if(Ie&&H){const ye=H.append(ve.partial);Xe({tracksThought:!0,...ye}),ye.thoughtComplete&&!le&&(le=!0,Xe({tracksThought:!0,thoughtContent:ye.thoughtContent,content:ye.content,thoughtComplete:!0}))}else D+=ve.partial,Xe({content:D})}else ve.type==="tool_running"?(We(),R(`Đang gọi công cụ · ${ve.call.name}…`)):ve.type==="llm_generating"?R(Kn(Ce,te,ve.expectedKind)):ve.type==="turn_complete"&&R(`Hoàn tất · ${ve.steps} bước`)}catch(ye){console.error("[clinical-chat] stream display error",ye)}},Te.signal);if(Te.signal.aborted){gt(),je(x,{streaming:!1});return}if(gt(),Vn(x),Ae)je(x,{content:m||Re.finalAnswer,thoughtContent:Le||void 0,tracksThought:!0,thoughtComplete:!0,streaming:!1,pondering:!1});else if(Ie&&H){const ve=H.finalize(),ye=ve.content||(C===1?Re.finalAnswer:"");je(x,{content:ye,thoughtContent:ve.thoughtContent||void 0,tracksThought:!0,thoughtComplete:!0,streaming:!1,pondering:!1})}else je(x,{content:D||(C===1?Re.finalAnswer:""),streaming:!1,pondering:!1});R(_t(Ce,!0,te,E.current))}catch(ue){if(gt(),Vn(x),ue instanceof DOMException&&ue.name==="AbortError"){je(x,{streaming:!1,pondering:!1});return}je(x,{content:ue instanceof Error?`Không thể trả lời: ${ue.message}`:"Không thể trả lời. Vui lòng thử lại.",streaming:!1,pondering:!1}),R(ze?"Lỗi · không trả lời được":"Lỗi · Gemma 4 E2B")}finally{w(!1),he.current=null}})()},[t,se,e,Se,de,L,je]),b=s.useCallback(()=>{const M=k.trim();if(!M||a||I)return;const Z=yd(M,ge.current);if(wd(Z,ge.current)){ee({analysis:Z,pendingText:M});return}_e(M,ge.current,ae.current,N)},[a,k,_e,I,N]),W=s.useCallback(()=>{ee(null)},[]),Q=s.useCallback(()=>{if(!j||I)return;const{pendingText:M}=j;ee(null),_e(M,ge.current,ae.current,N)},[_e,I,N,j]),Y=s.useCallback(()=>{if(!j||I)return;const{analysis:M,pendingText:Z}=j,te=M.recommended;if(!Jt(te))return;ee(null),c(te),pe.current=dn();const me=[on(e,t,te,n,ae.current)];y(me),L==="llm"&&R(_t(te,!0,ae.current,E.current)),_e(Z,te,ae.current,me)},[t,_e,e,I,j,L,n]),fe=s.useCallback(M=>{if(M.key==="Enter"&&!M.shiftKey){if(M.preventDefault(),I)return;b()}},[I,b]),ce=!a&&!I&&k.trim().length>0&&L!=="loading";return{messages:N,draft:k,setDraft:A,isGenerating:I,runtime:L,inferenceMode:l,setInferenceMode:Pe,chatReasoningLevel:d,setChatReasoningLevel:q,reasoningModelBackend:h,setReasoningModelBackend:oe,reasoningBarVisible:f,toggleReasoningBar:we,startNewConversation:Fe,modeSuggestion:j,dismissModeSuggestion:W,acceptModeSuggestion:Y,sendMessageAnyway:Q,statusLabel:X,caseContextLabel:i,canSend:ce,isModelLoading:ie,modelLoadPhase:v,modelLoadProgress:re,modelInstallTransferLabel:ne,modelLoadStalled:_,modelLoadFading:S,sendMessage:b,stopGeneration:G,onKeyDown:fe}}function Pu({text:e,className:t,streamTargetKey:n}){const a=s.useRef(null);return s.useLayoutEffect(()=>{if(!n)return;const o=a.current;return Qa(n,o),o&&e&&!o.textContent&&(o.textContent=e),()=>Qa(n,null)},[n,e]),s.useLayoutEffect(()=>{const o=a.current;!o||o.textContent===e||n||(o.textContent=e)},[e,n]),r.jsx("p",{ref:a,className:t})}const zr=s.memo(Pu),qn=/^(\d+)\.\s+(.*)$/,fr=/^(\s*)[\*\-]\s+(.*)$/;function mr(e,t){const n=[];let a=e,o=0;for(;a.length>0;){const i=a.match(/^\*\*(.+?)\*\*/);if(i){n.push(r.jsx("strong",{children:i[1]},`${t}-b-${o}`)),a=a.slice(i[0].length),o+=1;continue}const l=a.match(/^\*([^*]+?)\*/);if(l){n.push(r.jsx("em",{children:l[1]},`${t}-i-${o}`)),a=a.slice(l[0].length),o+=1;continue}const c=a.match(/^`([^`]+)`/);if(c){n.push(r.jsx("code",{className:"chat-md__code",children:c[1]},`${t}-c-${o}`)),a=a.slice(c[0].length),o+=1;continue}const d=a.search(/\*\*|\*|`/);if(d===-1){n.push(a);break}if(d>0){n.push(a.slice(0,d)),a=a.slice(d);continue}n.push(a[0]),a=a.slice(1),o+=1}return n}function br(e,t){const n=[];let a=t;for(;a0&&l>0){const c=n[n.length-1],d=br(e,a);c.children=d.items,a=d.next;continue}if(l>0)break;n.push({content:i[2],children:[]}),a+=1}return{items:n,next:a}}function _r(e,t,n){const a=t?"ol":"ul";return r.jsx(a,{className:t?"chat-md__ol":"chat-md__ul",children:e.map((o,i)=>r.jsxs("li",{children:[mr(o.content,`${n}-${i}`),o.children.length>0?_r(o.children,!1,`${n}-${i}-sub`):null]},`${n}-${i}`))},n)}function Au(e){const t=e.replace(/\r\n/g,` -`).split(` -`),n=[];let a=0,o=0;for(;acancelIdleCallback(n)}const t=window.setTimeout(e,0);return()=>clearTimeout(t)}function Du({text:e,className:t,streaming:n=!1}){const[a,o]=s.useState(null);return s.useEffect(()=>{if(n){o(null);return}let l=!1;const c=Ru(()=>{if(l)return;const d=Au(e);s.startTransition(()=>{l||o(d)})});return()=>{l=!0,c()}},[n,e]),e.trim()?n||a===null?r.jsx("div",{className:t?`chat-md chat-md--plain ${t}`:"chat-md chat-md--plain",children:n?r.jsx(zr,{text:e,className:"chat-md__plain"}):r.jsx("p",{className:"chat-md__plain",children:e})}):r.jsx("div",{className:t?`chat-md ${t}`:"chat-md",children:a}):null}const Ou=["Đang suy nghĩ","Đang phân tích","Đang cân nhắc","Đang tổng hợp"],zu=["Đang suy nghĩ","Đang lập kế hoạch","Đang chuẩn bị","Đang xử lý"];function Fu({variant:e="chat"}){const t=e==="agent"?zu:Ou,[n,a]=s.useState(0),[o,i]=s.useState(0);return s.useEffect(()=>{const l=window.setInterval(()=>{a(c=>(c+1)%t.length),i(c=>c+1)},2400);return()=>window.clearInterval(l)},[t.length]),r.jsxs("span",{className:"clinical-chat__pondering","aria-live":"polite","aria-label":`${t[n]}, vui lòng đợi`,children:[r.jsx("span",{className:"clinical-chat__pondering-text",children:t[n]}),r.jsxs("span",{className:"clinical-chat__pondering-dots","aria-hidden":!0,children:[r.jsx("span",{}),r.jsx("span",{}),r.jsx("span",{})]})]},o)}function $u({messageId:e,content:t,thoughtStreaming:n=!1}){const a=s.useId(),o=s.useRef(!1),[i,l]=s.useState(!1);if(s.useEffect(()=>{l(!1),o.current=!1},[e]),!t.trim()&&!n)return null;const c=()=>{o.current=!0,l(h=>!h)},d=n?"Đang suy luận":"Suy luận",u=!i&&!n?t.replace(/\s+/g," ").trim().slice(0,100):"";return r.jsxs("div",{className:`clinical-chat__thought ${n?"clinical-chat__thought--streaming":""} ${i?"clinical-chat__thought--open":"clinical-chat__thought--closed"}`,children:[r.jsxs("button",{type:"button",className:"clinical-chat__thought-toggle",onClick:c,"aria-expanded":i,"aria-controls":a,children:[r.jsx("span",{className:"clinical-chat__thought-chevron","aria-hidden":!0,children:i?"▾":"▸"}),r.jsxs("span",{className:"clinical-chat__thought-label",children:["🤔 ",d]}),n?r.jsxs("span",{className:"clinical-chat__thought-live","aria-label":"Đang cập nhật",children:[r.jsx("span",{}),r.jsx("span",{}),r.jsx("span",{})]}):null]}),!i&&u?r.jsxs("p",{className:"clinical-chat__thought-preview",children:[u,"…"]}):null,r.jsx("div",{id:a,className:"clinical-chat__thought-body",hidden:!i,children:r.jsx(zr,{text:t,streamTargetKey:Mt(e,"thought"),className:"clinical-chat__thought-md chat-md__plain"})})]})}const Bu=s.memo($u,(e,t)=>e.messageId===t.messageId&&e.content===t.content&&e.thoughtStreaming===t.thoughtStreaming&&e.answerStreaming===t.answerStreaming);function Hu(e){return e.toLocaleTimeString("vi-VN",{hour:"2-digit",minute:"2-digit"})}function Gu({message:e}){return r.jsxs("div",{className:`clinical-chat__bubble clinical-chat__bubble--${e.role}`,children:[r.jsxs("div",{className:"clinical-chat__bubble-meta",children:[r.jsx("span",{className:"clinical-chat__role",children:e.role==="user"?"Bạn":"AI lâm sàng"}),r.jsx("time",{className:"clinical-chat__time tnum",dateTime:e.timestamp.toISOString(),children:Hu(e.timestamp)})]}),e.pondering?r.jsx(Fu,{variant:e.ponderingVariant??"chat"}):e.streaming&&!e.content&&!e.tracksThought&&!e.thoughtContent?r.jsxs("span",{className:"clinical-chat__typing","aria-label":"AI đang trả lời",children:[r.jsx("span",{}),r.jsx("span",{}),r.jsx("span",{})]}):r.jsxs(r.Fragment,{children:[e.tracksThought||e.thoughtContent?r.jsx(Bu,{messageId:e.id,content:e.thoughtContent??"",thoughtStreaming:!!(e.streaming&&!e.thoughtComplete),answerStreaming:e.streaming}):null,e.content||e.tracksThought&&e.streaming?e.tracksThought?r.jsx(zr,{text:e.content,streamTargetKey:Mt(e.id,"answer"),className:"clinical-chat__answer-md chat-md__plain"}):r.jsx(Du,{text:e.content,streaming:e.streaming,className:"clinical-chat__answer-md"}):null]})]})}const Wu=s.memo(Gu,(e,t)=>e.message.id===t.message.id&&e.message.content===t.message.content&&e.message.thoughtContent===t.message.thoughtContent&&e.message.tracksThought===t.message.tracksThought&&e.message.thoughtComplete===t.message.thoughtComplete&&e.message.pondering===t.message.pondering&&e.message.ponderingVariant===t.message.ponderingVariant&&e.message.streaming===t.message.streaming&&e.message.role===t.message.role),Xn=380,Uu={"installing-gemma":{title:"Đang cài đặt Gemma 4 E2B về máy…",subtitle:"Mô hình trò chuyện chính (~1.87 GB). Nếu bị gián đoạn, lần mở sau sẽ tiếp tục từ chỗ đã tải.",ariaLabel:"Đang cài đặt Gemma 4 E2B",composerPlaceholder:"Đang cài đặt Gemma 4 E2B, vui lòng đợi…"},"resuming-gemma":{title:"Đang tiếp tục tải Gemma 4 E2B…",subtitle:"Lần tải trước bị gián đoạn — đang tiếp tục từ chỗ đã tải, không tải lại từ đầu.",ariaLabel:"Đang tiếp tục tải Gemma 4 E2B",composerPlaceholder:"Đang tiếp tục tải Gemma 4 E2B, vui lòng đợi…"},"loading-gemma":{title:"Đang nạp Gemma 4 E2B…",subtitle:"Mô hình đang được nạp vào bộ nhớ để sẵn sàng trò chuyện ngay.",ariaLabel:"Đang nạp Gemma 4 E2B",composerPlaceholder:"Đang nạp Gemma 4 E2B, vui lòng đợi…"}};function Vu({grade:e,angleClass:t,severityLoading:n=!1,disabled:a}){var Se,je;const{messages:o,draft:i,setDraft:l,isGenerating:c,runtime:d,inferenceMode:u,setInferenceMode:h,chatReasoningLevel:p,setChatReasoningLevel:f,reasoningModelBackend:g,setReasoningModelBackend:N,reasoningBarVisible:y,toggleReasoningBar:k,startNewConversation:A,modeSuggestion:I,dismissModeSuggestion:w,acceptModeSuggestion:L,sendMessageAnyway:z,statusLabel:X,caseContextLabel:R,canSend:ie,isModelLoading:U,modelLoadPhase:v,modelLoadProgress:B,modelInstallTransferLabel:re,modelLoadStalled:V,modelLoadFading:ne,sendMessage:F,stopGeneration:_,onKeyDown:O}=Lu({grade:e,angleClass:t,severityLoading:n,disabled:a}),S=s.useRef(null),P=s.useRef(null),j=s.useRef(!0),ee=s.useRef(0),J=72,K=s.useCallback(()=>{const G=S.current;if(!G)return;const _e=G.scrollHeight-G.scrollTop-G.clientHeight;j.current=_e<=J},[]);s.useEffect(()=>{if(!j.current)return;const G=performance.now();if(!(c&&G-ee.current<120))return ee.current=G,P.current!==null&&cancelAnimationFrame(P.current),P.current=requestAnimationFrame(()=>{P.current=null;const _e=S.current;_e&&j.current&&(_e.scrollTop=_e.scrollHeight)}),()=>{P.current!==null&&cancelAnimationFrame(P.current)}},[o.length,c,(Se=o[o.length-1])==null?void 0:Se.content,(je=o[o.length-1])==null?void 0:je.thoughtContent]);const Ee=G=>{if(G.preventDefault(),c){_();return}j.current=!0,F()},pe=()=>{if(c){_();return}j.current=!0,F()},he=a||d==="loading"||U,Ne=he||!c&&!ie,ge=U&&v!==null,ae=v?Uu[v]:null,E=Math.min(100,Math.max(0,Math.round(B))),$=V&&v!==null&&cn(v),se=$?"Tải Gemma bị gián đoạn":ae==null?void 0:ae.title,ke=$?`Mất kết nối — hiện không tải được. Đang thử lại tự động${re?` · đã lưu ${re}`:""}. Có thể tải lại trang để tiếp tục từ chỗ đã tải.`:ae==null?void 0:ae.subtitle,Fe=$?"Gián đoạn":v&&cn(v)&&re?re:`${E}%`,Pe=Ut(u),q=Ke(p),oe=ni(p,g),we=oi(g),de=I?Ut(I.analysis.recommended):null;return r.jsxs("div",{className:`clinical-chat glass ${a?"clinical-chat--disabled":""}`,children:[r.jsxs("div",{className:"clinical-chat__toolbar",children:[r.jsx("div",{className:"clinical-chat__mode-tabs",role:"tablist","aria-label":"Chế độ suy luận",children:dr.map(G=>{const _e=c||G.beta===!0;return r.jsxs("button",{type:"button",role:"tab","aria-selected":u===G.id,"aria-disabled":_e,className:`clinical-chat__mode-tab ${u===G.id?"clinical-chat__mode-tab--active":""} ${G.beta?"clinical-chat__mode-tab--beta":""}`,title:dd(G),disabled:_e,onClick:()=>h(G.id),children:[r.jsx("span",{className:"clinical-chat__mode-tab-label",children:G.shortLabel}),G.beta?r.jsx("span",{className:"clinical-chat__mode-beta","aria-hidden":!0,children:"Beta"}):null]},G.id)})}),u==="chat"&&r.jsx("button",{type:"button",className:`clinical-chat__reasoning-toggle ${y?"clinical-chat__reasoning-toggle--active":""}`,disabled:c,onClick:k,title:y?"Ẩn chọn mức độ":"Hiện chọn mức độ","aria-pressed":y,"aria-label":"Bật/tắt chọn mức độ",children:q.emoji}),r.jsx("button",{type:"button",className:"clinical-chat__new-session",disabled:c,onClick:()=>A(),title:"Bắt đầu cuộc trò chuyện mới với chế độ hiện tại",children:"Mới"})]}),u==="chat"&&y&&r.jsx("div",{className:"clinical-chat__reasoning-bar",role:"radiogroup","aria-label":"Mức độ suy luận",children:lr.map(G=>{const _e=c||G.beta===!0;return r.jsxs("button",{type:"button",role:"radio","aria-checked":p===G.id,"aria-disabled":_e,className:`clinical-chat__reasoning-btn ${p===G.id?"clinical-chat__reasoning-btn--active":""} ${G.beta?"clinical-chat__reasoning-btn--beta":""}`,title:qc(G),disabled:_e,onClick:()=>f(G.id),children:[r.jsx("span",{className:"clinical-chat__reasoning-emoji","aria-hidden":!0,children:G.emoji}),r.jsx("span",{className:"clinical-chat__reasoning-label",children:G.label}),G.beta?r.jsx("span",{className:"clinical-chat__reasoning-beta","aria-hidden":!0,children:"Beta"}):null,r.jsx("span",{className:"clinical-chat__reasoning-task",children:G.taskHint})]},G.id)})}),u==="chat"&&y&&p==="moderate"&&r.jsx("div",{className:"clinical-chat__model-backend",role:"radiogroup","aria-label":"Mô hình suy luận",children:cr.map(G=>{const _e=c||G.id==="server"&&!Rn();return r.jsx("button",{type:"button",role:"radio","aria-checked":g===G.id,"aria-disabled":_e,className:`clinical-chat__model-backend-btn ${g===G.id?"clinical-chat__model-backend-btn--active":""}`,title:G.description,disabled:_e,onClick:()=>N(G.id),children:G.shortLabel},G.id)})}),r.jsxs("div",{className:"clinical-chat__status","aria-live":"polite",children:[r.jsx("span",{className:`clinical-chat__runtime clinical-chat__runtime--${d}`,children:X}),r.jsx("span",{className:"clinical-chat__mode-hint",children:u==="chat"?p==="moderate"?`${q.label} · ${we.shortLabel} · ${oe} token`:`${q.label} · ${oe} token`:Pe.llmCallsHint})]}),r.jsx("p",{className:"clinical-chat__case-context",title:"Thông tin AI trên khung hình hiện tại — không phải tin nhắn của bạn",children:R}),I&&de&&Jt(I.analysis.recommended)&&r.jsxs("div",{className:"clinical-chat__mode-suggest",role:"status",children:[r.jsx("p",{className:"clinical-chat__mode-suggest-text",children:I.analysis.reason}),r.jsxs("p",{className:"clinical-chat__mode-suggest-sub",children:["Gợi ý: ",r.jsx("strong",{children:de.label})," thay vì ",Pe.label,"."]}),r.jsxs("div",{className:"clinical-chat__mode-suggest-actions",children:[r.jsxs("button",{type:"button",className:"clinical-chat__mode-suggest-primary",onClick:L,children:["Chuyển sang ",de.shortLabel," (cuộc trò chuyện mới)"]}),r.jsxs("button",{type:"button",className:"clinical-chat__mode-suggest-secondary",onClick:z,children:["Gửi với ",Pe.shortLabel]}),r.jsx("button",{type:"button",className:"clinical-chat__mode-suggest-dismiss",onClick:w,children:"Hủy"})]})]}),r.jsxs("div",{className:"clinical-chat__messages-wrap",children:[r.jsx("div",{ref:S,className:"clinical-chat__messages",role:"log","aria-live":"polite",onScroll:K,children:o.map(G=>r.jsx(Wu,{message:G},G.id))}),ge&&ae&&r.jsxs(r.Fragment,{children:[r.jsx("div",{className:`clinical-chat__overlay ${ne?"clinical-chat__overlay--fade-out":""}`,"aria-hidden":!0}),r.jsxs("div",{className:`clinical-chat__load-bubble clinical-chat__load-bubble--${v} ${$?"clinical-chat__load-bubble--stalled":""} ${ne?"clinical-chat__load-bubble--fade-out":""}`,role:"status","aria-live":"polite","aria-label":$?`${ae.ariaLabel} bị gián đoạn, đang thử lại`:v&&cn(v)&&re?`${ae.ariaLabel}, ${re}`:`${ae.ariaLabel}, ${E} phần trăm`,children:[r.jsx("div",{className:`clinical-chat__load-icon clinical-chat__load-icon--${v} ${$?"clinical-chat__load-icon--stalled":""}`,"aria-hidden":!0,children:$?r.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:[r.jsx("path",{d:"M12 8v5M12 16.5v.5",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round"}),r.jsx("path",{d:"M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z",stroke:"currentColor",strokeWidth:"1.6",strokeLinejoin:"round"})]}):v&&cn(v)?r.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:r.jsx("path",{d:"M12 3v3M12 18v3M4.22 4.22l2.12 2.12M17.66 17.66l2.12 2.12M3 12h3M18 12h3M4.22 19.78l2.12-2.12M17.66 6.34l2.12-2.12",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round"})}):r.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",children:r.jsx("path",{d:"M12 2v6M12 16v6M4.93 4.93l4.24 4.24M14.83 14.83l4.24 4.24M2 12h6M16 12h6M4.93 19.07l4.24-4.24M14.83 9.17l4.24-4.24",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round"})})}),r.jsx("p",{className:"clinical-chat__load-title",children:se}),r.jsx("p",{className:"clinical-chat__load-subtitle",children:ke}),r.jsxs("div",{className:"clinical-chat__load-progress-row",children:[r.jsx("div",{className:"clinical-chat__load-progress-track",children:r.jsx("div",{className:`clinical-chat__load-progress-fill clinical-chat__load-progress-fill--${v} ${$?"clinical-chat__load-progress-fill--stalled":""}`,style:{width:`${E}%`}})}),r.jsx("span",{className:`clinical-chat__load-progress-label tnum ${$?"clinical-chat__load-progress-label--stalled":""}`,children:Fe})]})]})]})]}),r.jsxs("form",{className:"clinical-chat__composer",onSubmit:Ee,children:[r.jsx("textarea",{className:`clinical-chat__input ${he?"clinical-chat__input--loading":""}`,rows:1,placeholder:he?(ae==null?void 0:ae.composerPlaceholder)??`${X}…`:"Hỏi khi nghi ngờ đề xuất AI, yêu cầu giải thích overlay, hoặc ghi nhận chỉnh sửa…",value:i,onChange:G=>l(G.target.value),onKeyDown:O,disabled:he||c,"aria-label":"Tin nhắn tới AI lâm sàng"}),r.jsx("button",{type:"button",className:`clinical-chat__send ${c?"clinical-chat__send--stop":""} ${he?"clinical-chat__send--loading":""}`,disabled:Ne,onClick:pe,"aria-label":c?"Dừng trả lời":"Gửi tin nhắn",title:c?"Dừng":"Gửi",children:c?r.jsx("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:r.jsx("rect",{x:"6",y:"6",width:"12",height:"12",rx:"1"})}):r.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none","aria-hidden":!0,children:r.jsx("path",{d:"M5 12h14M13 6l6 6-6 6",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})})]}),r.jsx("style",{children:Ku})]})}const Ku=` - .clinical-chat { - display: flex; - flex-direction: column; - border-radius: var(--radius-md); - overflow: hidden; - font-size: 1em; - height: calc(${Xn}px * var(--workspace-panel-scale, 1)); - min-height: calc(${Xn}px * 0.88); - max-height: calc(${Xn}px * 1.28); - } - .clinical-chat--disabled { - opacity: 0.5; - pointer-events: none; - } - .clinical-chat__toolbar { - flex-shrink: 0; - display: flex; - align-items: center; - gap: 6px; - padding: 8px 8px 0; - } - .clinical-chat__mode-tabs { - flex: 1; - display: flex; - gap: 4px; - min-width: 0; - } - .clinical-chat__mode-tab { - flex: 1; - min-width: 0; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 3px; - padding: 5px 4px; - border-radius: 999px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.75); - color: var(--color-on-surface-variant); - font-size: 10px; - font-weight: 600; - cursor: pointer; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .clinical-chat__mode-tab-label { - overflow: hidden; - text-overflow: ellipsis; - } - .clinical-chat__mode-beta { - flex-shrink: 0; - font-size: 7px; - font-weight: 700; - letter-spacing: 0.03em; - text-transform: uppercase; - padding: 1px 4px; - border-radius: 4px; - background: rgba(180, 120, 40, 0.15); - color: #9a6b1f; - line-height: 1.2; - } - .clinical-chat__mode-tab--beta { - opacity: 0.55; - cursor: not-allowed; - } - .clinical-chat__mode-tab--beta:disabled { - opacity: 0.5; - } - .clinical-chat__mode-tab--active { - background: var(--color-secondary-container); - border-color: rgba(35, 104, 108, 0.35); - color: var(--color-secondary); - } - .clinical-chat__mode-tab:disabled { - opacity: 0.45; - cursor: not-allowed; - } - .clinical-chat__reasoning-toggle { - flex-shrink: 0; - width: 28px; - height: 28px; - padding: 0; - border-radius: var(--radius-full); - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.85); - font-size: 14px; - line-height: 1; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - } - .clinical-chat__reasoning-toggle--active { - background: var(--color-secondary-container); - border-color: rgba(35, 104, 108, 0.35); - } - .clinical-chat__reasoning-toggle:disabled { - opacity: 0.45; - cursor: not-allowed; - } - .clinical-chat__reasoning-bar { - flex-shrink: 0; - display: flex; - gap: 4px; - padding: 4px 8px 0; - } - .clinical-chat__reasoning-btn { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 1px; - padding: 5px 4px; - border-radius: 10px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.75); - color: var(--color-on-surface-variant); - font-size: 9px; - font-weight: 600; - cursor: pointer; - } - .clinical-chat__reasoning-btn--active { - background: var(--color-secondary-container); - border-color: rgba(35, 104, 108, 0.35); - color: var(--color-secondary); - } - .clinical-chat__reasoning-btn--beta { - opacity: 0.55; - cursor: not-allowed; - } - .clinical-chat__reasoning-btn--beta:disabled { - opacity: 0.5; - } - .clinical-chat__reasoning-beta { - flex-shrink: 0; - font-size: 7px; - font-weight: 700; - letter-spacing: 0.03em; - text-transform: uppercase; - padding: 1px 4px; - border-radius: 4px; - background: rgba(180, 120, 40, 0.15); - color: #9a6b1f; - line-height: 1.2; - } - .clinical-chat__reasoning-btn:disabled { - opacity: 0.45; - cursor: not-allowed; - } - .clinical-chat__reasoning-emoji { - font-size: 16px; - line-height: 1; - } - .clinical-chat__reasoning-label { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - font-weight: 700; - } - .clinical-chat__reasoning-task { - font-size: 8px; - font-weight: 500; - line-height: 1.2; - opacity: 0.85; - text-align: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; - } - .clinical-chat__model-backend { - flex-shrink: 0; - display: flex; - gap: 6px; - padding: 4px 8px 0; - } - .clinical-chat__model-backend-btn { - flex: 1; - padding: 5px 8px; - border-radius: 999px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.75); - color: var(--color-on-surface-variant); - font-size: 10px; - font-weight: 600; - cursor: pointer; - } - .clinical-chat__model-backend-btn--active { - background: var(--color-secondary-container); - border-color: rgba(35, 104, 108, 0.35); - color: var(--color-secondary); - } - .clinical-chat__model-backend-btn:disabled { - opacity: 0.45; - cursor: not-allowed; - } - .clinical-chat__new-session { - flex-shrink: 0; - padding: 5px 8px; - border-radius: 999px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.85); - color: var(--color-on-surface-variant); - font-size: 10px; - font-weight: 600; - cursor: pointer; - } - .clinical-chat__new-session:disabled { - opacity: 0.45; - cursor: not-allowed; - } - .clinical-chat__status { - flex-shrink: 0; - padding: 6px 10px 0; - font-size: 10px; - color: var(--color-on-surface-variant); - display: flex; - justify-content: space-between; - gap: 8px; - } - .clinical-chat__runtime--llm { - color: var(--color-secondary); - } - .clinical-chat__runtime--mock { - color: var(--color-on-surface-variant); - } - .clinical-chat__runtime--loading { - opacity: 0.7; - } - .clinical-chat__mode-hint { - flex-shrink: 0; - opacity: 0.75; - } - .clinical-chat__case-context { - flex-shrink: 0; - margin: 4px 10px 0; - padding: 5px 8px; - border-radius: 6px; - background: rgba(35, 104, 108, 0.08); - border: 1px solid rgba(35, 104, 108, 0.14); - font-size: 10px; - line-height: 1.4; - color: var(--color-on-surface-variant); - } - .clinical-chat__mode-suggest { - flex-shrink: 0; - margin: 8px 8px 0; - padding: 10px; - border-radius: var(--radius-md); - border: 1px solid rgba(35, 104, 108, 0.22); - background: rgba(234, 239, 238, 0.65); - font-size: 0.6875em; - line-height: 1.45; - } - .clinical-chat__mode-suggest-text { - margin: 0 0 4px; - color: var(--color-on-surface); - } - .clinical-chat__mode-suggest-sub { - margin: 0 0 8px; - color: var(--color-on-surface-variant); - } - .clinical-chat__mode-suggest-actions { - display: flex; - flex-wrap: wrap; - gap: 6px; - } - .clinical-chat__mode-suggest-primary, - .clinical-chat__mode-suggest-secondary, - .clinical-chat__mode-suggest-dismiss { - border-radius: 999px; - padding: 5px 10px; - font-size: 10px; - font-weight: 600; - cursor: pointer; - border: 1px solid transparent; - } - .clinical-chat__mode-suggest-primary { - background: var(--color-secondary); - color: var(--color-on-secondary); - } - .clinical-chat__mode-suggest-secondary { - background: rgba(255, 255, 255, 0.9); - border-color: var(--color-outline-variant); - color: var(--color-on-surface); - } - .clinical-chat__mode-suggest-dismiss { - background: transparent; - color: var(--color-on-surface-variant); - } - .clinical-chat__messages-wrap { - position: relative; - flex: 1; - min-height: 0; - display: flex; - flex-direction: column; - } - .clinical-chat__overlay { - position: absolute; - inset: 0; - background: rgba(234, 239, 238, 0.55); - backdrop-filter: blur(2px); - -webkit-backdrop-filter: blur(2px); - opacity: 1; - transition: opacity 200ms ease; - pointer-events: none; - z-index: 1; - } - .clinical-chat__overlay--fade-out { - opacity: 0; - } - .clinical-chat__load-bubble { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - width: min(92%, 280px); - padding: 14px 16px; - border-radius: 12px; - background: #ffffff; - box-shadow: 0 8px 24px rgba(35, 104, 108, 0.12), 0 2px 8px rgba(23, 29, 28, 0.06); - border: 1px solid var(--glass-border); - z-index: 2; - opacity: 1; - transition: opacity 200ms ease; - pointer-events: none; - } - .clinical-chat__load-bubble--fade-out { - opacity: 0; - } - .clinical-chat__load-icon { - display: flex; - align-items: center; - justify-content: center; - width: 36px; - height: 36px; - margin: 0 auto 10px; - border-radius: var(--radius-full); - background: var(--color-secondary-container); - color: var(--color-secondary); - } - .clinical-chat__load-icon--installing-gemma, - .clinical-chat__load-icon--resuming-gemma, - .clinical-chat__load-icon--installing-qwen { - animation: clinical-chat-spin 2.4s linear infinite; - } - .clinical-chat__load-icon--installing { - animation: clinical-chat-spin 2.4s linear infinite; - } - /* Stalled: stop the spin so a frozen download never looks like active progress. */ - .clinical-chat__load-bubble--stalled { - border-color: rgba(180, 120, 40, 0.4); - } - .clinical-chat__load-icon--stalled { - animation: clinical-chat-stall-pulse 1.6s ease-in-out infinite; - background: rgba(180, 120, 40, 0.14); - color: #9a6b1f; - } - @keyframes clinical-chat-stall-pulse { - 0%, 100% { opacity: 0.55; } - 50% { opacity: 1; } - } - .clinical-chat__load-progress-fill--stalled { - background: repeating-linear-gradient( - 45deg, - rgba(180, 120, 40, 0.55), - rgba(180, 120, 40, 0.55) 6px, - rgba(180, 120, 40, 0.3) 6px, - rgba(180, 120, 40, 0.3) 12px - ); - animation: clinical-chat-stall-stripes 0.8s linear infinite; - } - @keyframes clinical-chat-stall-stripes { - from { background-position: 0 0; } - to { background-position: 17px 0; } - } - .clinical-chat__load-progress-label--stalled { - color: #9a6b1f; - } - @keyframes clinical-chat-pulse-icon { - 0%, 100% { opacity: 0.55; transform: scale(0.94); } - 50% { opacity: 1; transform: scale(1); } - } - .clinical-chat__load-bubble--loading-gemma, - .clinical-chat__load-bubble--loading-qwen { - border-color: rgba(35, 104, 108, 0.22); - } - .clinical-chat__load-progress-fill--loading-gemma, - .clinical-chat__load-progress-fill--loading-qwen { - background: linear-gradient(90deg, var(--color-secondary), #3d8b8f); - } - .clinical-chat__load-icon--loading-gemma, - .clinical-chat__load-icon--loading-qwen { - animation: clinical-chat-pulse-icon 1.4s ease-in-out infinite; - } - @keyframes clinical-chat-spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } - } - .clinical-chat__load-title { - margin: 0 0 6px; - font-family: var(--font-headline); - font-size: 0.8125em; - font-weight: 600; - line-height: 1.35; - color: var(--color-on-surface); - text-align: center; - } - .clinical-chat__load-subtitle { - margin: 0 0 12px; - font-size: 0.6875em; - line-height: 1.45; - color: var(--color-on-surface-variant); - text-align: center; - } - .clinical-chat__load-progress-row { - display: flex; - align-items: center; - gap: 8px; - } - .clinical-chat__load-progress-track { - flex: 1; - height: 6px; - border-radius: 999px; - background: #e0e0e0; - overflow: hidden; - } - .clinical-chat__load-progress-fill { - height: 100%; - border-radius: inherit; - background: var(--color-secondary); - transition: width 180ms ease; - } - .clinical-chat__load-progress-label { - flex-shrink: 0; - min-width: 7.5rem; - font-size: 0.6875em; - font-weight: 600; - color: var(--color-secondary); - text-align: right; - white-space: nowrap; - } - .clinical-chat__messages { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 12px 10px; - display: flex; - flex-direction: column; - gap: 10px; - scroll-behavior: smooth; - } - .clinical-chat__messages::-webkit-scrollbar { - width: 6px; - } - .clinical-chat__messages::-webkit-scrollbar-thumb { - background: rgba(35, 104, 108, 0.25); - border-radius: 999px; - } - .clinical-chat__bubble { - max-width: 92%; - padding: 8px 10px; - border-radius: var(--radius-md); - font-size: 0.75em; - line-height: 1.45; - contain: content; - } - .clinical-chat__bubble .chat-md__plain { - margin: 4px 0 0; - white-space: pre-wrap; - word-break: break-word; - line-height: 1.45; - } - .clinical-chat__bubble .chat-md { - margin-top: 4px; - word-break: break-word; - } - .clinical-chat__bubble .chat-md__p { - margin: 0 0 0.5em; - white-space: pre-wrap; - line-height: 1.45; - } - .clinical-chat__bubble .chat-md__p:last-child { - margin-bottom: 0; - } - .clinical-chat__bubble .chat-md__heading { - margin: 0.35em 0 0.25em; - font-size: 1em; - font-weight: 700; - line-height: 1.35; - color: var(--color-on-surface); - } - .clinical-chat__bubble .chat-md__ul, - .clinical-chat__bubble .chat-md__ol { - margin: 0.25em 0 0.5em; - padding-left: 1.25em; - } - .clinical-chat__bubble .chat-md__ul { - list-style: disc; - } - .clinical-chat__bubble .chat-md__ol { - list-style: decimal; - } - .clinical-chat__bubble .chat-md__ul li, - .clinical-chat__bubble .chat-md__ol li { - margin: 0.2em 0; - line-height: 1.4; - } - .clinical-chat__bubble .chat-md__ul .chat-md__ul { - list-style: circle; - margin-top: 0.15em; - } - .clinical-chat__bubble .chat-md__code { - font-family: var(--font-mono, ui-monospace, monospace); - font-size: 0.92em; - padding: 0.05em 0.3em; - border-radius: 4px; - background: rgba(35, 104, 108, 0.08); - } - .clinical-chat__thought { - margin-top: 6px; - border-radius: 8px; - background: rgba(35, 104, 108, 0.06); - border: 1px dashed rgba(35, 104, 108, 0.22); - font-size: 0.9em; - color: var(--color-on-surface-variant); - overflow: hidden; - } - .clinical-chat__thought--streaming { - border-color: rgba(35, 104, 108, 0.35); - } - .clinical-chat__thought-toggle { - display: flex; - align-items: center; - gap: 6px; - width: 100%; - padding: 6px 8px; - border: none; - background: transparent; - cursor: pointer; - text-align: left; - font: inherit; - color: inherit; - } - .clinical-chat__thought-toggle:hover { - background: rgba(35, 104, 108, 0.05); - } - .clinical-chat__thought-chevron { - flex-shrink: 0; - width: 12px; - font-size: 11px; - color: var(--color-secondary); - line-height: 1; - } - .clinical-chat__thought-label { - flex: 1; - min-width: 0; - font-size: 9px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--color-secondary); - } - .clinical-chat__thought-preview { - margin: 0; - padding: 0 8px 6px 26px; - font-size: 10px; - line-height: 1.35; - font-weight: 500; - opacity: 0.8; - color: var(--color-on-surface-variant); - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - .clinical-chat__thought-live { - flex-shrink: 0; - display: inline-flex; - gap: 3px; - align-items: center; - } - .clinical-chat__thought-live span { - width: 4px; - height: 4px; - border-radius: 50%; - background: var(--color-secondary); - opacity: 0.5; - animation: clinical-chat-pulse 1.2s ease-in-out infinite; - } - .clinical-chat__thought-live span:nth-child(2) { - animation-delay: 0.15s; - } - .clinical-chat__thought-live span:nth-child(3) { - animation-delay: 0.3s; - } - .clinical-chat__thought-body { - padding: 0 8px 8px; - border-top: 1px solid rgba(35, 104, 108, 0.1); - } - .clinical-chat__thought-md.chat-md { - font-size: 0.95em; - line-height: 1.4; - } - .clinical-chat__thought--closed .clinical-chat__thought-toggle { - padding-bottom: 6px; - } - .clinical-chat__bubble--assistant { - align-self: flex-start; - background: rgba(255, 255, 255, 0.85); - border: 1px solid var(--glass-border); - } - .clinical-chat__bubble--user { - align-self: flex-end; - background: var(--color-secondary-container); - border: 1px solid rgba(35, 104, 108, 0.2); - color: var(--color-on-surface); - } - .clinical-chat__bubble-meta { - display: flex; - justify-content: space-between; - align-items: center; - gap: 8px; - font-size: 10px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--color-on-surface-variant); - } - .clinical-chat__role { - font-weight: 600; - color: var(--color-secondary); - } - .clinical-chat__bubble--user .clinical-chat__role { - color: var(--color-primary); - } - .clinical-chat__typing { - display: inline-flex; - gap: 4px; - align-items: center; - margin-top: 4px; - } - .clinical-chat__typing span { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--color-secondary); - opacity: 0.45; - animation: clinical-chat-pulse 1.2s ease-in-out infinite; - } - .clinical-chat__typing span:nth-child(2) { - animation-delay: 0.15s; - } - .clinical-chat__typing span:nth-child(3) { - animation-delay: 0.3s; - } - @keyframes clinical-chat-pulse { - 0%, 80%, 100% { opacity: 0.35; transform: scale(0.9); } - 40% { opacity: 1; transform: scale(1); } - } - .clinical-chat__pondering { - display: inline-flex; - align-items: center; - gap: 6px; - margin-top: 2px; - font-size: 0.8125em; - font-weight: 500; - color: var(--color-on-surface-variant); - } - .clinical-chat__pondering-text { - animation: clinical-chat-ponder-flicker 1.6s ease-in-out infinite; - } - @keyframes clinical-chat-ponder-flicker { - 0%, 100% { opacity: 0.42; } - 45% { opacity: 1; } - 55% { opacity: 0.78; } - } - .clinical-chat__pondering-dots { - display: inline-flex; - gap: 3px; - align-items: center; - } - .clinical-chat__pondering-dots span { - width: 4px; - height: 4px; - border-radius: 50%; - background: currentColor; - opacity: 0.35; - animation: clinical-chat-ponder-dot 1.1s ease-in-out infinite; - } - .clinical-chat__pondering-dots span:nth-child(2) { - animation-delay: 0.18s; - } - .clinical-chat__pondering-dots span:nth-child(3) { - animation-delay: 0.36s; - } - @keyframes clinical-chat-ponder-dot { - 0%, 70%, 100% { opacity: 0.25; transform: translateY(0); } - 35% { opacity: 0.95; transform: translateY(-2px); } - } - .clinical-chat__composer { - flex-shrink: 0; - display: flex; - align-items: center; - gap: 8px; - padding: 8px; - border-top: 1px solid var(--glass-border); - background: rgba(255, 255, 255, 0.5); - } - .clinical-chat__input { - flex: 1; - min-height: 0; - max-height: 88px; - resize: none; - border: 1px solid var(--color-outline-variant); - border-radius: var(--radius-md); - padding: 8px 12px; - font-size: 0.875em; - line-height: 1.45; - background: rgba(255, 255, 255, 0.9); - color: var(--color-on-surface); - } - .clinical-chat__input:focus { - outline: 2px solid rgba(35, 104, 108, 0.35); - outline-offset: 1px; - border-color: var(--color-secondary); - } - .clinical-chat__input--loading { - background: #f5f5f5; - color: var(--color-on-surface-variant); - pointer-events: none; - } - .clinical-chat__input--loading::placeholder { - color: var(--color-on-surface-variant); - opacity: 0.75; - } - .clinical-chat__input::placeholder { - color: var(--color-on-surface-variant); - font-size: 0.875em; - font-weight: 500; - opacity: 0.85; - } - .clinical-chat__send { - flex-shrink: 0; - width: 36px; - height: 36px; - border: none; - border-radius: var(--radius-full); - background: var(--color-secondary); - color: var(--color-on-secondary); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.15s, transform 0.15s; - } - .clinical-chat__send--stop { - background: #c0392b; - } - .clinical-chat__send--stop:hover:not(:disabled) { - background: #a93226; - } - .clinical-chat__send:hover:not(:disabled) { - transform: scale(1.04); - } - .clinical-chat__send--loading { - opacity: 0.35; - cursor: not-allowed; - } - .clinical-chat__send:disabled { - opacity: 0.4; - cursor: not-allowed; - } -`,un={0:"#76c8b1",1:"#5fb89e",2:"#e8a838",3:"#ba1a1a"},qu=rr.map(e=>({key:e,label:ar[e]}));function Xu({severity:e,severityLoading:t=!1,inflammation:n,inflammationLoading:a=!1,userConfig:o,suppressed:i}){const l=s.useMemo(()=>{var p;const h=(p=n==null?void 0:n.calibration)==null?void 0:p.raw_logits;return h!=null&&h.length?Xo(h,rr,o,f=>ar[f]??f):(n==null?void 0:n.calibration)??null},[n,o]),c=s.useMemo(()=>l?qo(l.class_probabilities,rr):n!=null&&n.detected?"inflammation":"no_inflammation",[l,n]);if(i)return r.jsxs("div",{className:"severity-badge severity-badge--suppressed glass",children:[r.jsx("span",{className:"severity-badge__label",children:"Đề xuất AI bị ẩn"}),r.jsx("p",{children:"Chế độ an toàn — đánh giá thủ công bắt buộc"}),r.jsx("style",{children:to})]});const d=(e==null?void 0:e.level)??null,u=(e==null?void 0:e.label)??(d!=null?qt[d]:null);return r.jsxs("div",{className:"severity-panel",children:[t?r.jsxs("div",{className:"severity-panel__block glass",children:[r.jsx("span",{className:"severity-panel__eyebrow",children:"Viêm màng hoạt dịch — độ nặng (từ ảnh phân đoạn)"}),r.jsx("p",{className:"severity-panel__pending",children:"Đang chờ kết quả độ nặng từ phân đoạn…"})]}):d!=null&&u?r.jsxs("div",{className:"severity-badge glass",style:{borderColor:un[d]??un[0]},children:[r.jsx("span",{className:"severity-badge__grade tnum",style:{color:un[d]??un[0]},children:d}),r.jsxs("div",{children:[r.jsx("span",{className:"severity-badge__label",children:"Viêm màng hoạt dịch — độ nặng (từ ảnh phân đoạn)"}),r.jsx("strong",{children:u}),(e==null?void 0:e.description)&&r.jsx("p",{className:"severity-panel__desc",children:e.description})]})]}):null,a?r.jsxs("div",{className:"severity-panel__block glass",children:[r.jsx("span",{className:"severity-panel__eyebrow",children:"Phát hiện viêm"}),r.jsx("p",{className:"severity-panel__pending",children:"Đang chờ kết quả phát hiện viêm…"})]}):n&&l?r.jsxs("div",{className:"severity-panel__inflam glass",children:[r.jsx("span",{className:"severity-panel__eyebrow",children:"Phát hiện viêm"}),r.jsx(Ar,{layout:"block"}),r.jsx("strong",{className:"severity-panel__pred",children:ar[c]}),r.jsx(Zo,{calibration:l}),r.jsx("p",{className:"severity-panel__risk",children:l.risk_framing_vi}),r.jsx(Jo,{calibration:l,entries:qu,predictedKey:c})]}):null,r.jsx("style",{children:to})]})}const to=` - .severity-panel { - display: flex; - flex-direction: column; - gap: 10px; - } - .severity-panel__block { - padding: 14px; - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - gap: 8px; - } - .severity-panel__pending { - margin: 0; - font-size: 0.8125em; - color: var(--color-on-surface-variant); - line-height: 1.45; - } - .severity-panel__desc { - margin: 4px 0 0; - font-size: 0.75rem; - color: var(--color-on-surface-variant); - line-height: 1.4; - font-weight: 400; - } - .severity-badge { - display: flex; - align-items: center; - gap: 14px; - padding: var(--space-md); - border-radius: var(--radius-lg); - border-width: 2px; - border-style: solid; - } - .severity-badge--suppressed { - border-color: var(--color-error); - flex-direction: column; - align-items: flex-start; - gap: 4px; - } - .severity-badge--suppressed p { - margin: 0; - font-size: 13px; - color: var(--color-on-surface-variant); - } - .severity-badge__grade { - font-family: var(--font-headline); - font-size: 40px; - font-weight: 700; - line-height: 1; - width: 52px; - text-align: center; - } - .severity-badge__label { - display: block; - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-on-surface-variant); - margin-bottom: 2px; - } - .severity-badge strong { - font-size: 18px; - font-family: var(--font-headline); - } - .severity-panel__inflam { - padding: 12px 14px; - border-radius: var(--radius-md); - display: flex; - flex-direction: column; - gap: 8px; - } - .severity-panel__eyebrow { - display: block; - font-size: 0.6875em; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--color-on-surface-variant); - } - .severity-panel__pred { - font-size: 0.9375em; - font-family: var(--font-headline); - } - .severity-panel__risk { - margin: 0; - font-size: 0.8125em; - color: var(--color-on-surface-variant); - line-height: 1.45; - } -`,xr="msk-diagnostic-form-width",Gt=420,vr=320,Yu=720,Ju=.85;function yr(e,t,n){return Math.min(Math.max(e,t),n)}function wr(){return Math.min(Yu,window.innerWidth*Ju)}function Zu(){try{const e=localStorage.getItem(xr);if(!e)return Gt;const t=Number.parseFloat(e);return Number.isFinite(t)?yr(t,vr,wr()):Gt}catch{return Gt}}function Qu(){const[e,t]=s.useState(Zu),[n,a]=s.useState(!1),o=s.useRef(!1),i=s.useRef(e);i.current=e,s.useEffect(()=>{const p=()=>{t(f=>yr(f,vr,wr()))};return window.addEventListener("resize",p),()=>window.removeEventListener("resize",p)},[]);const l=s.useCallback(p=>{t(yr(window.innerWidth-p,vr,wr()))},[]),c=s.useCallback(p=>{p.preventDefault(),p.stopPropagation(),p.currentTarget.setPointerCapture(p.pointerId),o.current=!0,a(!0),l(p.clientX)},[l]),d=s.useCallback(p=>{o.current&&l(p.clientX)},[l]),u=s.useCallback(p=>{o.current&&(p.currentTarget.releasePointerCapture(p.pointerId),o.current=!1,a(!1),localStorage.setItem(xr,String(i.current)))},[]),h=s.useCallback(()=>{t(Gt),localStorage.setItem(xr,String(Gt))},[]);return s.useEffect(()=>{if(n)return document.body.style.cursor="col-resize",document.body.style.userSelect="none",()=>{document.body.style.cursor="",document.body.style.userSelect=""}},[n]),{width:e,isResizing:n,onHandlePointerDown:c,onHandlePointerMove:d,onHandlePointerUp:u,onHandlePointerCancel:u,resetWidth:h}}function eh({step:e}){return e===0?r.jsxs("svg",{width:"22",height:"22",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("path",{d:"M3 17l6-6 4 4 8-10"}),r.jsx("path",{d:"M14 5h7v7"})]}):e===1?r.jsx("svg",{width:"22",height:"22",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:r.jsx("path",{d:"M4 20V10M10 20V4M16 20v-8M22 20V8"})}):e===3?r.jsxs("svg",{width:"22",height:"22",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("rect",{x:"3",y:"5",width:"18",height:"14",rx:"2"}),r.jsx("circle",{cx:"8.5",cy:"10.5",r:"1.5"}),r.jsx("path",{d:"M21 16l-5.5-5.5L5 21"})]}):e===4?r.jsx("svg",{width:"22",height:"22",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:r.jsx("path",{d:"M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"})}):r.jsxs("svg",{width:"22",height:"22",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[r.jsx("path",{d:"M12 20h9"}),r.jsx("path",{d:"M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"})]})}function no(e){return e===0?"Nếu bệnh nhân lần đầu, có thể bỏ qua bước này.":e===2?"Ghi nhận tình trạng giải phẫu từng bên khớp. Cuộn trong từng ô nếu nội dung dài.":e===3?"Ảnh lưu từ khung chẩn đoán hiện tại, gồm lớp phân đoạn và chú thích của bác sĩ.":null}function th({open:e,patientName:t,patientMrn:n,jointLabel:a="khớp gối",aiGrade:o,angleLabel:i,draft:l,onDraftChange:c,onClose:d,onSave:u,onRefreshSnapshot:h}){const p=s.useId(),[f,g]=s.useState(0),[N,y]=s.useState(!1),k=s.useRef(null),{width:A,isResizing:I,onHandlePointerDown:w,onHandlePointerMove:L,onHandlePointerUp:z,onHandlePointerCancel:X,resetWidth:R}=Qu();s.useEffect(()=>{e&&g(0)},[e]),s.useEffect(()=>()=>{k.current&&clearTimeout(k.current)},[]),s.useEffect(()=>{(!e||f!==3)&&(y(!1),k.current&&(clearTimeout(k.current),k.current=null))},[e,f]);const ie=async()=>{!h||!await h()||(y(!0),k.current&&clearTimeout(k.current),k.current=setTimeout(()=>{y(!1),k.current=null},2e3))};if(s.useEffect(()=>{if(!e)return;const _=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=_}},[e]),s.useEffect(()=>{if(!e)return;const _=O=>{O.key==="Escape"&&d(),O.key==="ArrowRight"&&fS+1),O.key==="ArrowLeft"&&f>0&&g(S=>S-1)};return document.addEventListener("keydown",_),()=>document.removeEventListener("keydown",_)},[e,d,f]),!e)return null;const U=f===0,v=f===kt-1,B=Bo[f]??"",re=f===2||f===4,V=_=>{c({...l,diseaseProgression:l.diseaseProgression===_?null:_})},ne=_=>{c({...l,powerDopplerScore:l.powerDopplerScore===_?null:_})},F=_=>{c({...l,nextProcedure:l.nextProcedure===_?null:_})};return Xt.createPortal(r.jsxs("div",{className:`diagnostic-form-drawer${I?" diagnostic-form-drawer--resizing":""}`,role:"presentation",children:[r.jsx("button",{type:"button",className:"diagnostic-form-drawer__backdrop","aria-label":"Đóng phiếu chẩn đoán",onClick:d}),r.jsxs("aside",{className:"diagnostic-form-drawer__panel glass-elevated",role:"dialog","aria-modal":"true","aria-labelledby":p,style:{width:Math.min(A,window.innerWidth)},children:[r.jsx("div",{className:"diagnostic-form-drawer__resize-handle",role:"separator","aria-orientation":"vertical","aria-label":"Kéo để thay đổi chiều rộng phiếu chẩn đoán","aria-valuenow":A,"aria-valuemin":320,"aria-valuemax":720,onPointerDown:w,onPointerMove:L,onPointerUp:z,onPointerCancel:X,onDoubleClick:R}),r.jsxs("header",{className:"diagnostic-form-drawer__header",children:[r.jsxs("div",{children:[r.jsx("h2",{id:p,children:"Phiếu chẩn đoán lâm sàng"}),r.jsxs("p",{className:"diagnostic-form-drawer__meta",children:[t," · ",r.jsx("span",{className:"tnum",children:n})]})]}),r.jsx("button",{type:"button",className:"diagnostic-form-drawer__close",onClick:d,"aria-label":"Đóng",children:"×"})]}),(o!=null||i)&&f===0&&r.jsxs("div",{className:"diagnostic-form-drawer__ai-ref",children:[r.jsx("span",{className:"diagnostic-form-drawer__ai-ref-label",children:"Tham chiếu AI"}),r.jsxs("p",{children:[i&&r.jsx("strong",{children:i}),o!=null&&r.jsxs(r.Fragment,{children:[i?" · ":"","Mức viêm đề xuất: ",r.jsxs("span",{className:"tnum",children:["Grade ",o]})]})]})]}),r.jsxs("div",{className:"diagnostic-form-drawer__carousel",children:[r.jsxs("article",{className:"diagnostic-form-drawer__card",children:[r.jsxs("div",{className:"diagnostic-form-drawer__card-head",children:[r.jsx("span",{className:"diagnostic-form-drawer__card-icon","aria-hidden":!0,children:r.jsx(eh,{step:f})}),r.jsx("h3",{className:"diagnostic-form-drawer__card-title",children:B}),r.jsxs("div",{className:"diagnostic-form-drawer__card-nav",children:[r.jsx("button",{type:"button",className:"diagnostic-form-drawer__nav-btn",disabled:U,onClick:()=>g(_=>_-1),"aria-label":"Bước trước",children:"‹"}),r.jsx("button",{type:"button",className:"diagnostic-form-drawer__nav-btn",disabled:v,onClick:()=>g(_=>_+1),"aria-label":"Bước sau",children:"›"})]})]}),no(f)&&r.jsx("p",{className:"diagnostic-form-drawer__step-hint",children:no(f)}),r.jsxs("div",{className:re?"diagnostic-form-drawer__card-body diagnostic-form-drawer__card-body--scroll":"diagnostic-form-drawer__card-body",children:[f===0&&r.jsx("div",{className:"diagnostic-form-drawer__pill-group",role:"group","aria-label":"Tiến triển bệnh",children:zo.map(_=>r.jsx("button",{type:"button",className:l.diseaseProgression===_.value?"diagnostic-form-drawer__pill diagnostic-form-drawer__pill--active":"diagnostic-form-drawer__pill",onClick:()=>V(_.value),children:_.label},_.value))}),f===1&&r.jsxs("div",{className:"diagnostic-form-drawer__measure-block",children:[r.jsx("p",{className:"diagnostic-form-drawer__measure-label",children:"Thang điểm Power Doppler"}),r.jsx("div",{className:"diagnostic-form-drawer__score-group",role:"group","aria-label":"Power Doppler",children:Fo.map(_=>r.jsx("button",{type:"button",className:l.powerDopplerScore===_?"diagnostic-form-drawer__score diagnostic-form-drawer__score--active":"diagnostic-form-drawer__score",onClick:()=>ne(_),children:_},_))}),r.jsxs("label",{className:"diagnostic-form-drawer__field diagnostic-form-drawer__field--inline",children:[r.jsxs("span",{children:["Độ dày màng hoạt dịch ",a," ",r.jsx("span",{className:"diagnostic-form-drawer__field-note",children:"(không có hoặc không biết: −1)"})]}),r.jsx("input",{type:"text",inputMode:"decimal",className:"tnum",value:l.synovialThicknessUnknown?"−1":l.synovialThicknessMm,disabled:l.synovialThicknessUnknown,onChange:_=>c({...l,synovialThicknessMm:_.target.value}),placeholder:"mm"})]}),r.jsxs("label",{className:"diagnostic-form-drawer__checkbox",children:[r.jsx("input",{type:"checkbox",checked:l.synovialThicknessUnknown,onChange:_=>c({...l,synovialThicknessUnknown:_.target.checked,synovialThicknessMm:_.target.checked?"":l.synovialThicknessMm})}),r.jsx("span",{children:"Không có hoặc không đo được"})]})]}),f===2&&r.jsxs("div",{className:"diagnostic-form-drawer__anatomy-block",children:[r.jsxs("label",{className:"diagnostic-form-drawer__field diagnostic-form-drawer__subfield",children:[r.jsx("span",{children:"Khớp gối phải"}),r.jsx("textarea",{className:"diagnostic-form-drawer__textarea--scroll diagnostic-form-drawer__textarea--sub",value:l.anatomyDescriptionRight,onChange:_=>c({...l,anatomyDescriptionRight:_.target.value}),placeholder:"Nhận định lâm sàng và ghi chú về giải phẫu khớp gối phải…"})]}),r.jsxs("label",{className:"diagnostic-form-drawer__field diagnostic-form-drawer__subfield",children:[r.jsx("span",{children:"Khớp gối trái"}),r.jsx("textarea",{className:"diagnostic-form-drawer__textarea--scroll diagnostic-form-drawer__textarea--sub",value:l.anatomyDescriptionLeft,onChange:_=>c({...l,anatomyDescriptionLeft:_.target.value}),placeholder:"Nhận định lâm sàng và ghi chú về giải phẫu khớp gối trái…"})]})]}),f===3&&r.jsxs("div",{className:"diagnostic-form-drawer__snapshot",children:[l.diagnosisWorkImageDataUrl?r.jsx("img",{src:l.diagnosisWorkImageDataUrl,alt:"Ảnh siêu âm đã chú thích từ khung chẩn đoán",className:"diagnostic-form-drawer__snapshot-img"}):r.jsx("div",{className:"diagnostic-form-drawer__snapshot-empty",children:"Chưa có ảnh. Hãy chụp lại từ khung chẩn đoán."}),h&&r.jsx("button",{type:"button",className:N?"btn btn-secondary diagnostic-form-drawer__snapshot-btn diagnostic-form-drawer__snapshot-btn--ack":"btn btn-secondary diagnostic-form-drawer__snapshot-btn",onClick:()=>void ie(),"aria-live":"polite",children:N?"Đã chụp":"Chụp lại ảnh từ khung chẩn đoán"})]}),f===4&&r.jsxs("div",{className:"diagnostic-form-drawer__procedure-block",children:[r.jsx("p",{className:"diagnostic-form-drawer__measure-label",children:"Chỉ định thủ thuật tiếp theo"}),r.jsx("div",{className:"diagnostic-form-drawer__procedure-list",role:"radiogroup","aria-label":"Chỉ định thủ thuật",children:$o.map(_=>r.jsxs("label",{className:l.nextProcedure===_.value?"diagnostic-form-drawer__procedure-option diagnostic-form-drawer__procedure-option--active":"diagnostic-form-drawer__procedure-option",children:[r.jsx("input",{type:"radio",name:"nextProcedure",value:_.value,checked:l.nextProcedure===_.value,onChange:()=>F(_.value)}),r.jsx("span",{children:_.label})]},_.value))}),r.jsxs("label",{className:"diagnostic-form-drawer__field",children:[r.jsx("span",{children:"Tại sao cần thực hiện chỉ định"}),r.jsx("textarea",{className:"diagnostic-form-drawer__textarea--scroll diagnostic-form-drawer__textarea--compact",value:l.procedureReason,onChange:_=>c({...l,procedureReason:_.target.value}),placeholder:"Giải thích lý do lâm sàng cho chỉ định thủ thuật…"})]}),r.jsxs("label",{className:"diagnostic-form-drawer__field",children:[r.jsx("span",{children:"Người phụ trách thực hiện"}),r.jsx("input",{type:"text",value:l.procedureResponsibleName,onChange:_=>c({...l,procedureResponsibleName:_.target.value}),placeholder:"Họ tên bác sĩ / kỹ thuật viên phụ trách"})]})]}),f===5&&r.jsxs("label",{className:"diagnostic-form-drawer__field",children:[r.jsx("span",{className:"sr-only",children:"Kết luận bác sĩ"}),r.jsx("textarea",{className:"diagnostic-form-drawer__textarea--scroll",value:l.doctorConclusion,onChange:_=>c({...l,doctorConclusion:_.target.value}),placeholder:"Nhập kết luận / chẩn đoán / yêu cầu cần can thiệp đối với bệnh nhân."})]})]}),r.jsxs("footer",{className:"diagnostic-form-drawer__card-foot",children:[r.jsxs("svg",{className:"diagnostic-form-drawer__foot-icon",width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[r.jsx("circle",{cx:"12",cy:"12",r:"9"}),r.jsx("path",{d:"M12 7v5l3 2"})]}),r.jsxs("span",{children:["Lần chỉnh sửa cuối: vừa tạo · ",t]})]})]}),r.jsx("div",{className:"diagnostic-form-drawer__dots",role:"tablist","aria-label":"Các bước phiếu chẩn đoán",children:Array.from({length:kt},(_,O)=>r.jsx("button",{type:"button",role:"tab","aria-selected":f===O,"aria-label":`Bước ${O+1}`,className:f===O?"diagnostic-form-drawer__dot diagnostic-form-drawer__dot--active":"diagnostic-form-drawer__dot",onClick:()=>g(O)},O))})]}),r.jsxs("div",{className:"diagnostic-form-drawer__actions",children:[f===0&&r.jsx("button",{type:"button",className:"btn btn-ghost",onClick:()=>g(1),children:"Bỏ qua bước này"}),r.jsxs("div",{className:"diagnostic-form-drawer__actions-end",children:[r.jsx("button",{type:"button",className:"btn btn-ghost",onClick:d,children:"Đóng"}),v?r.jsx("button",{type:"button",className:"btn btn-primary",onClick:u,children:"Lưu Phiếu"}):r.jsx("button",{type:"button",className:"btn btn-primary",onClick:()=>g(_=>_+1),children:"Tiếp theo"})]})]})]}),r.jsx("style",{children:nh})]}),document.body)}const nh=` - .diagnostic-form-drawer { - position: fixed; - inset: 0; - z-index: 1200; - display: flex; - justify-content: flex-end; - isolation: isolate; - } - .diagnostic-form-drawer--resizing { - cursor: col-resize; - } - .diagnostic-form-drawer--resizing .diagnostic-form-drawer__backdrop { - cursor: col-resize; - } - .diagnostic-form-drawer__backdrop { - position: absolute; - inset: 0; - border: none; - background: rgba(10, 15, 14, 0.45); - cursor: pointer; - } - .diagnostic-form-drawer__panel { - position: relative; - min-width: 320px; - max-width: 100vw; - height: 100%; - padding: 20px; - display: flex; - flex-direction: column; - gap: 14px; - overflow-y: auto; - overflow-x: hidden; - border-radius: 0; - border-left: 1px solid var(--glass-border); - flex-shrink: 0; - } - .diagnostic-form-drawer__resize-handle { - position: absolute; - top: 0; - left: 0; - width: 12px; - height: 100%; - cursor: col-resize; - touch-action: none; - z-index: 3; - } - .diagnostic-form-drawer__resize-handle::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 4px; - height: 52px; - border-radius: 4px; - background: rgba(35, 104, 108, 0.28); - transition: background 0.15s, height 0.15s; - } - .diagnostic-form-drawer__resize-handle:hover::after, - .diagnostic-form-drawer--resizing .diagnostic-form-drawer__resize-handle::after { - background: rgba(35, 104, 108, 0.6); - height: 72px; - } - .diagnostic-form-drawer__header { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 12px; - flex-shrink: 0; - } - .diagnostic-form-drawer__header h2 { - margin: 0; - font-size: 1.125rem; - color: var(--color-on-surface); - } - .diagnostic-form-drawer__meta { - margin: 4px 0 0; - font-size: 0.8125rem; - color: var(--color-on-surface-variant); - } - .diagnostic-form-drawer__close { - width: 32px; - height: 32px; - border: none; - border-radius: 999px; - background: rgba(0, 0, 0, 0.06); - font-size: 22px; - line-height: 1; - cursor: pointer; - color: var(--color-on-surface); - } - .diagnostic-form-drawer__ai-ref { - padding: 10px 12px; - border-radius: var(--radius-md); - background: rgba(35, 104, 108, 0.08); - border: 1px solid rgba(35, 104, 108, 0.18); - font-size: 0.8125rem; - color: var(--color-on-surface-variant); - flex-shrink: 0; - } - .diagnostic-form-drawer__ai-ref-label { - display: block; - font-size: 0.6875rem; - font-weight: 700; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--color-secondary); - margin-bottom: 4px; - } - .diagnostic-form-drawer__carousel { - flex: 1; - display: flex; - flex-direction: column; - gap: 12px; - min-height: 0; - width: 100%; - } - .diagnostic-form-drawer__card { - flex: 1; - display: flex; - flex-direction: column; - border-radius: var(--radius-lg); - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.72); - overflow: hidden; - min-height: 320px; - width: 100%; - } - .diagnostic-form-drawer__card-head { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 10px; - align-items: start; - padding: 14px 14px 10px; - border-bottom: 1px solid rgba(35, 104, 108, 0.12); - } - .diagnostic-form-drawer__card-icon { - display: flex; - align-items: center; - justify-content: center; - width: 36px; - height: 36px; - border-radius: 10px; - background: rgba(35, 104, 108, 0.1); - color: var(--color-secondary); - flex-shrink: 0; - } - .diagnostic-form-drawer__card-title { - margin: 0; - font-size: 0.875rem; - font-weight: 600; - line-height: 1.35; - color: var(--color-on-surface); - } - .diagnostic-form-drawer__card-nav { - display: flex; - gap: 4px; - } - .diagnostic-form-drawer__nav-btn { - width: 28px; - height: 28px; - border: none; - border-radius: 999px; - background: rgba(35, 104, 108, 0.12); - color: var(--color-secondary); - font-size: 18px; - line-height: 1; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - padding: 0; - } - .diagnostic-form-drawer__nav-btn:disabled { - opacity: 0.35; - cursor: not-allowed; - } - .diagnostic-form-drawer__nav-btn:not(:disabled):hover { - background: rgba(35, 104, 108, 0.22); - } - .diagnostic-form-drawer__step-hint { - margin: 0; - padding: 0 14px 8px; - font-size: 0.75rem; - color: var(--color-on-surface-variant); - font-style: italic; - } - .diagnostic-form-drawer__card-body { - flex: 1; - padding: 14px; - display: flex; - flex-direction: column; - justify-content: center; - min-height: 0; - } - .diagnostic-form-drawer__card-body--scroll { - justify-content: flex-start; - overflow-y: auto; - } - .diagnostic-form-drawer__pill-group { - display: flex; - flex-direction: column; - gap: 10px; - } - .diagnostic-form-drawer__pill { - width: 100%; - padding: 14px 16px; - border-radius: 999px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.9); - color: var(--color-on-surface); - font-size: 0.9375rem; - font-weight: 600; - cursor: pointer; - transition: background 0.15s, border-color 0.15s; - } - .diagnostic-form-drawer__pill:hover { - border-color: var(--color-secondary); - } - .diagnostic-form-drawer__pill--active { - background: rgba(35, 104, 108, 0.12); - border-color: var(--color-secondary); - color: var(--color-secondary); - } - .diagnostic-form-drawer__measure-block { - display: flex; - flex-direction: column; - gap: 14px; - } - .diagnostic-form-drawer__anatomy-block { - display: flex; - flex-direction: column; - gap: 14px; - width: 100%; - } - .diagnostic-form-drawer__subfield span { - font-size: 0.75rem; - font-weight: 700; - letter-spacing: 0.02em; - text-transform: uppercase; - color: var(--color-secondary); - } - .diagnostic-form-drawer__procedure-block { - display: flex; - flex-direction: column; - gap: 14px; - } - .diagnostic-form-drawer__measure-label { - margin: 0; - font-size: 0.8125rem; - font-weight: 600; - color: var(--color-on-surface); - } - .diagnostic-form-drawer__score-group { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 8px; - } - .diagnostic-form-drawer__score { - aspect-ratio: 1; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.9); - font-size: 1.125rem; - font-weight: 700; - color: var(--color-on-surface); - cursor: pointer; - } - .diagnostic-form-drawer__score--active { - background: var(--color-secondary); - border-color: var(--color-secondary); - color: #fff; - } - .diagnostic-form-drawer__procedure-list { - display: flex; - flex-direction: column; - gap: 6px; - } - .diagnostic-form-drawer__procedure-option { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 10px 12px; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.9); - font-size: 0.8125rem; - font-weight: 500; - color: var(--color-on-surface); - cursor: pointer; - line-height: 1.35; - } - .diagnostic-form-drawer__procedure-option input { - margin-top: 2px; - flex-shrink: 0; - accent-color: var(--color-secondary); - } - .diagnostic-form-drawer__procedure-option--active { - border-color: var(--color-secondary); - background: rgba(35, 104, 108, 0.08); - color: var(--color-secondary); - } - .diagnostic-form-drawer__field { - display: flex; - flex-direction: column; - gap: 6px; - font-size: 0.8125rem; - font-weight: 600; - color: var(--color-on-surface); - } - .diagnostic-form-drawer__field-note { - font-weight: 400; - color: var(--color-on-surface-variant); - } - .diagnostic-form-drawer__field input, - .diagnostic-form-drawer__field textarea { - resize: vertical; - min-height: 44px; - width: 100%; - box-sizing: border-box; - padding: 10px 12px; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - font: inherit; - font-weight: 400; - background: rgba(255, 255, 255, 0.9); - } - .diagnostic-form-drawer__textarea--scroll { - resize: none; - height: 160px; - max-height: 160px; - min-height: 160px; - overflow-y: auto; - } - .diagnostic-form-drawer__textarea--compact { - height: 100px; - max-height: 100px; - min-height: 100px; - } - .diagnostic-form-drawer__textarea--sub { - height: 120px; - max-height: 120px; - min-height: 120px; - } - .diagnostic-form-drawer__snapshot { - display: flex; - flex-direction: column; - gap: 10px; - align-items: stretch; - } - .diagnostic-form-drawer__snapshot-img { - width: 100%; - max-height: min(280px, 40vh); - object-fit: contain; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - background: #0a0f0e; - } - .diagnostic-form-drawer__snapshot-empty { - padding: 32px 16px; - text-align: center; - font-size: 0.8125rem; - color: var(--color-on-surface-variant); - border-radius: var(--radius-md); - border: 1px dashed var(--color-outline-variant); - background: rgba(245, 250, 249, 0.6); - } - .diagnostic-form-drawer__snapshot-btn { - width: 100%; - font-size: 0.8125rem; - transition: background 0.2s, border-color 0.2s, color 0.2s; - } - .diagnostic-form-drawer__snapshot-btn--ack { - border-color: var(--color-secondary); - background: rgba(35, 104, 108, 0.14); - color: var(--color-secondary); - font-weight: 600; - } - .diagnostic-form-drawer__checkbox { - display: flex; - align-items: flex-start; - gap: 8px; - font-size: 0.8125rem; - font-weight: 400; - color: var(--color-on-surface-variant); - cursor: pointer; - } - .diagnostic-form-drawer__checkbox input { - margin-top: 2px; - } - .diagnostic-form-drawer__card-foot { - display: flex; - align-items: center; - gap: 6px; - padding: 10px 14px; - border-top: 1px solid rgba(35, 104, 108, 0.1); - font-size: 0.6875rem; - color: var(--color-on-surface-variant); - background: rgba(245, 250, 249, 0.8); - } - .diagnostic-form-drawer__foot-icon { - flex-shrink: 0; - color: var(--color-on-surface-variant); - } - .diagnostic-form-drawer__dots { - display: flex; - justify-content: center; - gap: 8px; - flex-wrap: wrap; - max-width: 100%; - } - .diagnostic-form-drawer__dot { - width: 8px; - height: 8px; - border-radius: 999px; - border: none; - padding: 0; - background: rgba(35, 104, 108, 0.25); - cursor: pointer; - flex-shrink: 0; - } - .diagnostic-form-drawer__dot--active { - width: 22px; - background: var(--color-secondary); - } - .diagnostic-form-drawer__actions { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 8px; - flex-shrink: 0; - padding-top: 4px; - border-top: 1px solid var(--color-outline-variant); - } - .diagnostic-form-drawer__actions-end { - display: flex; - gap: 8px; - margin-left: auto; - } - .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; - } -`,rh="lumina-msk",ah=1,it="diagnostic-form-drafts";function Fr(){return new Promise((e,t)=>{const n=indexedDB.open(rh,ah);n.onerror=()=>t(n.error??new Error("Failed to open IndexedDB")),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(it)||a.createObjectStore(it,{keyPath:"patientMrn"})}})}function Mn(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error??new Error("IndexedDB request failed"))})}function xi(e){return e.diseaseProgression!=null||e.powerDopplerScore!=null||e.synovialThicknessMm.trim()!==""||e.synovialThicknessUnknown||e.anatomyDescriptionRight.trim()!==""||e.anatomyDescriptionLeft.trim()!==""||e.diagnosisWorkImageDataUrl!=null||e.nextProcedure!=null||e.procedureReason.trim()!==""||e.procedureResponsibleName.trim()!==""||e.doctorConclusion.trim()!==""}async function $r(e){try{const t=await Fr(),n=await Mn(t.transaction(it,"readonly").objectStore(it).get(e));return t.close(),n!=null&&n.draft?{...jn,...n.draft}:null}catch{return null}}async function oh(e,t){try{const n=await Fr(),a=n.transaction(it,"readwrite").objectStore(it);if(!xi(t))return await Mn(a.delete(e)),n.close(),!1;const o={patientMrn:e,draft:t,updatedAt:Date.now()};return await Mn(a.put(o)),n.close(),!0}catch{return!1}}async function ih(e){try{const t=await Fr();await Mn(t.transaction(it,"readwrite").objectStore(it).delete(e)),t.close()}catch{}}async function sh(e){const t=await $r(e);return t!=null&&xi(t)}const ro=jn;function lh({severity:e,severityLoading:t=!1,inflammation:n,inflammationLoading:a=!1,angleClassification:o,patientName:i,patientMrn:l,patientJoint:c,chatGrade:d=null,onCaptureDiagnosisSnapshot:u,draftRevision:h=0}){var V,ne,F,_;const[p,f]=s.useState(pc),[g,N]=s.useState(!1),[y,k]=s.useState(ro),[A,I]=s.useState(!1),w=s.useRef(null);s.useEffect(()=>{fc(p)},[p]),s.useEffect(()=>{let O=!1;return sh(l).then(S=>{O||I(S)}),()=>{O=!0}},[l,h]);const L=s.useCallback(async O=>{const S=await oh(l,O);I(S)},[l]),z=s.useCallback(O=>{w.current&&clearTimeout(w.current),w.current=setTimeout(()=>{w.current=null,L(O)},400)},[L]);s.useEffect(()=>()=>{w.current&&clearTimeout(w.current)},[]);const X=s.useCallback(O=>{k(O),g&&z(O)},[g,z]),R=!!((ne=(V=n==null?void 0:n.calibration)==null?void 0:V.raw_logits)!=null&&ne.length||(_=(F=o==null?void 0:o.calibration)==null?void 0:F.raw_logits)!=null&&_.length),ie=o?ho[o.class].labelVi:null,U=async()=>{const S=await $r(l)??ro;let P=S.diagnosisWorkImageDataUrl;!P&&u&&(P=await u()),k({...S,diagnosisWorkImageDataUrl:P}),N(!0)},v=()=>{w.current&&(clearTimeout(w.current),w.current=null),L(y),N(!1)},B=()=>{w.current&&(clearTimeout(w.current),w.current=null),L(y),N(!1)},re=async()=>{const O=u?await u():null;return O?(k(S=>({...S,diagnosisWorkImageDataUrl:O})),!0):!1};return r.jsxs(r.Fragment,{children:[r.jsxs("aside",{className:"side-nav glass-elevated",children:[r.jsxs("section",{className:"side-nav__section",children:[r.jsx("h3",{children:"Đề xuất của AI"}),R&&r.jsx(Wc,{config:p,onChange:f}),r.jsx(Xu,{severity:e,severityLoading:t,inflammation:n,inflammationLoading:a,userConfig:p}),r.jsx(Gc,{classification:o??null,userConfig:p})]}),r.jsxs("section",{className:"side-nav__section side-nav__section--chat",children:[r.jsx("h3",{children:"Thảo luận kết quả"}),r.jsx("p",{className:"side-nav__intro",children:"Nếu đề xuất AI chưa thuyết phục hoặc cần làm rõ, trao đổi trực tiếp tại đây — không cần dừng quy trình."}),r.jsx(Vu,{grade:d,angleClass:(o==null?void 0:o.class)??null,severityLoading:t},l)]}),r.jsxs("section",{className:"side-nav__section",children:[r.jsx("h3",{children:"Thao tác nhanh"}),r.jsx("div",{className:"side-nav__actions",children:r.jsx("button",{type:"button",className:"btn btn-secondary",onClick:()=>void U(),children:A?"Sửa phiếu":"Lập phiếu"})}),r.jsx("p",{className:"side-nav__canvas-hint",children:"Để đánh dấu hoặc bao vùng trên ảnh siêu âm, dùng thanh công cụ nổi trên khung hình (Vẽ, vùng khép kín, Tẩy)."})]}),r.jsx("style",{children:` - .side-nav { - flex: 1; - min-height: 100%; - border-radius: var(--radius-lg); - padding: var(--space-md); - display: flex; - flex-direction: column; - gap: var(--space-lg); - overflow-y: auto; - font-size: 1em; - background: rgba(255, 255, 255, 0.96); - } - .side-nav__section h3 { - font-size: 0.875em; - font-weight: 600; - color: var(--color-secondary); - margin-bottom: 10px; - text-transform: uppercase; - letter-spacing: 0.04em; - } - .side-nav__intro { - font-size: 0.8125em; - color: var(--color-on-surface-variant); - margin: 0 0 10px; - } - .side-nav__section--chat { - flex-shrink: 0; - } - .side-nav__actions { - display: flex; - flex-direction: column; - gap: 8px; - } - .side-nav__actions .btn { - width: 100%; - } - .side-nav__actions .btn:disabled { - opacity: 0.45; - cursor: not-allowed; - } - .side-nav__canvas-hint { - margin: 10px 0 0; - font-size: 0.75rem; - line-height: 1.45; - color: var(--color-on-surface-variant); - } - `})]}),r.jsx(th,{open:g,patientName:i,patientMrn:l,jointLabel:(c==null?void 0:c.toLowerCase())??"khớp gối",aiGrade:e==null?void 0:e.level,angleLabel:ie,draft:y,onDraftChange:X,onClose:v,onSave:B,onRefreshSnapshot:re})]})}class ch{constructor(t,n={}){be(this,"events");be(this,"frameIndexById");be(this,"contentSize");be(this,"defaultPenColor");be(this,"frameCache",new Map);be(this,"currentFrameId",null);be(this,"currentFrameIndex",0);be(this,"currentDicomUrl","");be(this,"eventIndex",0);be(this,"editingOverlayId",null);be(this,"penColor");this.events=vo(t),this.frameIndexById=n.frameIndexById??{},this.contentSize=n.contentSize??{width:1024,height:768},this.defaultPenColor=n.defaultPenColor??"#00FFFF",this.penColor=this.defaultPenColor}static durationMs(t){return yo(t)}reset(){this.frameCache.clear(),this.currentFrameId=null,this.currentFrameIndex=0,this.currentDicomUrl="",this.eventIndex=0,this.editingOverlayId=null,this.penColor=this.defaultPenColor}seek(t){this.reset();let n=!1;for(;this.eventIndexc.type==="MOUSE_MOVE"&&c.frameId===this.currentFrameId);if(n.length===0)return null;let a=null,o=null;for(const c of n)if(c.relTime<=t&&(a=c),c.relTime>=t){o=c;break}const i=this.penColor;if(!a){const c=n[0];return{x:c.payload.x,y:c.payload.y,frameId:c.frameId,color:i}}if(!o||a.relTime===o.relTime)return{x:a.payload.x,y:a.payload.y,frameId:a.frameId,color:i};const l=(t-a.relTime)/(o.relTime-a.relTime);return{x:a.payload.x*(1-l)+o.payload.x*l,y:a.payload.y*(1-l)+o.payload.y*l,frameId:a.frameId,color:i}}}const dh=[.5,1,1.5,2];function uh(e,t){return Math.max(0,Math.min(t,e))}function hh({log:e,showPointerTrace:t=!1,onViewportChange:n,onFrameRestore:a,onReplaySegmentViewport:o}){var Pe;const[i,l]=s.useState("continuous"),[c,d]=s.useState(0),[u,h]=s.useState(0),[p,f]=s.useState(!1),[g,N]=s.useState(1),y=s.useRef(null),k=s.useRef(null),[A,I]=s.useState(!1),w=s.useRef(0),L=s.useRef(e.totalDurationSeconds),z=s.useRef(g),X=s.useRef(""),R=e.events,ie=e.uixEvents??[],U=ie.length>0,v=U?Math.max(e.totalDurationSeconds,gs(ie)):e.totalDurationSeconds,B=s.useMemo(()=>{const q={};for(const oe of e.segments??[])q[oe.frame.frameId]=oe.frame.frameIndex;for(const oe of ie)oe.type==="FRAME_SWITCH"&&(q[oe.payload.toFrameId]=oe.payload.frameIndex);return q},[e.segments,ie]),re=s.useMemo(()=>U?new ch(ie,{frameIndexById:B}):null,[U,ie,B]),V=e.sessionKey??Sr(e.patientId,R);L.current=v,z.current=g;const ne=s.useCallback(q=>{const oe=uh(q,L.current);w.current=oe,d(oe)},[]),F=s.useCallback(()=>{w.current=0,d(0),f(!1),h(0),X.current=""},[]),_=s.useCallback(()=>{F(),l("continuous")},[F]);s.useEffect(()=>{F()},[F,V]),s.useEffect(()=>{w.current>v&&ne(0)},[v,ne]);const O=s.useMemo(()=>e.pointerTrace&&e.pointerTrace.length>0?e.pointerTrace:js(R),[R,e.pointerTrace]),S=s.useMemo(()=>i==="carousel"?R[u]??R[0]??null:aa(R,c),[i,u,R,c]),P=i==="carousel"?(S==null?void 0:S.elapsedSeconds)??0:c,j=s.useMemo(()=>!U||!re?null:re.seek(P*1e3),[P,re,U]),ee=s.useMemo(()=>!e.segments||e.segments.length===0?null:No(e.segments,P),[e.segments,P]),J=(ee==null?void 0:ee.frame.frameId)??((Pe=S==null?void 0:S.frame)==null?void 0:Pe.frameId),K=s.useMemo(()=>J?O.filter(q=>!q.frameId||q.frameId===J):O,[J,O]),Ee=s.useMemo(()=>{if(!t)return null;if(U&&(j!=null&&j.cursor))return{x:j.cursor.x*100,y:j.cursor.y*100,color:j.cursor.color};if(K.length===0)return null;const q=Ss(K,P);return q?{...q,color:"#ff007f"}:null},[K,P,t,j,U]),pe=s.useMemo(()=>!t||K.length===0?[]:Ns(K,P,void 0,J),[J,K,P,t]),he=e.overlaysByFrameId??{},Ne=s.useCallback((q,oe)=>{if(!a&&!o||q===X.current&&!oe)return;if(X.current=q,i==="carousel"&&(S!=null&&S.frame)){const de=S.frame;if(U&&(j!=null&&j.frameRestore))a==null||a(j.frameRestore.frameId,j.frameRestore.frameIndex,j.frameRestore.overlay);else{const Se=Ls(de.frameId,e.segments??[],he,S.elapsedSeconds);a==null||a(de.frameId,de.frameIndex,Se)}oe&&(o==null||o({zoom:1,panX:0,panY:0,showMask:!0,showGradcam:!1}));return}if(U&&(j!=null&&j.frameRestore)){a==null||a(j.frameRestore.frameId,j.frameRestore.frameIndex,j.frameRestore.overlay),oe&&ee&&(o==null||o(ee.viewport));return}if(!ee)return;const we=Ps(ee,he);a==null||a(ee.frame.frameId,ee.frame.frameIndex,we),oe&&(o==null||o(ee.viewport))},[S,ee,e.segments,i,a,o,he,P,j,U]);s.useEffect(()=>{var de,Se;if(!a)return;const q=j!=null&&j.frameRestore?hs(j.frameRestore.overlay):0,oe=((de=j==null?void 0:j.editingOverlay)==null?void 0:de.pointCount)??0,we=i==="carousel"?`carousel:${u}`:U?`uix:${Math.floor(P*30)}:${((Se=j==null?void 0:j.frameRestore)==null?void 0:Se.frameId)??"none"}:${q}:${oe}:${j!=null&&j.frameChanged?"sw":""}`:`continuous:${(ee==null?void 0:ee.segmentId)??"none"}:${Math.floor(P*10)}`;Ne(we,!1)},[ee==null?void 0:ee.segmentId,u,i,a,P,Ne,j,U]);const ge=s.useCallback((q,oe,we=!1)=>{var je,G;const de=Ee??(q==null?void 0:q.cursor),Se=(Ee==null?void 0:Ee.color)??((je=j==null?void 0:j.cursor)==null?void 0:je.color)??((G=j==null?void 0:j.editingOverlay)==null?void 0:G.color);n==null||n({active:i==="continuous"&&p,ghostX:(de==null?void 0:de.x)??50,ghostY:(de==null?void 0:de.y)??50,ripple:we,event:q,elapsedSeconds:oe,showPointerTrace:t,trailPoints:pe,penColor:Se,isOverlayEditing:!!(j!=null&&j.editingOverlay)})},[Ee,p,i,n,t,pe,j]);s.useEffect(()=>{if(!S)return;const q=!!S.clickPulse&&(i==="carousel"||p);ge(S,P,q),q&&(I(!0),k.current&&window.clearTimeout(k.current),k.current=window.setTimeout(()=>I(!1),650))},[S,ge,p,i,P]),s.useEffect(()=>{if(!p||i!=="continuous"){y.current=null;return}let q=0,oe=!1;const we=de=>{if(oe)return;y.current==null&&(y.current=de);const Se=(de-y.current)/1e3;y.current=de;const je=L.current,G=Math.min(je,w.current+Se*z.current);if(w.current=G,d(G),G>=je-.01){oe=!0,f(!1);return}q=window.requestAnimationFrame(we)};return y.current=null,q=window.requestAnimationFrame(we),()=>{oe=!0,window.cancelAnimationFrame(q),y.current=null}},[p,i]),s.useEffect(()=>()=>{k.current&&window.clearTimeout(k.current)},[]);const ae=()=>{w.current>=L.current-.05&&ne(0),f(q=>!q)},E=q=>{f(!1),ne(w.current+q)},$=q=>{f(!1),ne(q),X.current=""},se=q=>{h(oe=>{const we=Math.max(0,Math.min(R.length-1,oe+q));return X.current="",we})},ke=q=>{f(!1),l("carousel"),h(Math.max(0,Math.min(R.length-1,q))),X.current=""};return{mode:i,switchMode:q=>{if(l(q),f(!1),q==="carousel"){const oe=aa(R,w.current),we=R.findIndex(de=>de.id===oe.id);h(we>=0?we:0)}else{const oe=R[u]??R[0];oe&&ne(oe.elapsedSeconds)}},elapsedSeconds:c,totalDuration:v,carouselIndex:u,totalSteps:R.length,activeEvent:S,isPlaying:p,speed:g,speeds:dh,setSpeed:N,togglePlay:ae,skip:E,seek:$,goCarousel:se,goToStep:ke,resetPlayback:_,resetPlaybackPosition:F,events:R,ripple:A}}function gh({pulsing:e}){return r.jsx("span",{className:`lifecycle-toolbar__rec-dot${e?" lifecycle-toolbar__rec-dot--pulse":""}`,"aria-hidden":!0})}function ph(){return r.jsx("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:r.jsx("path",{d:"M6 5h4v14H6zM14 5h4v14h-4z"})})}function fh(){return r.jsx("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:r.jsx("path",{d:"M8 5v14l11-7z"})})}function mh(){return r.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:r.jsx("rect",{x:"6",y:"6",width:"12",height:"12",rx:"1"})})}function bh(){return r.jsx("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:r.jsx("path",{d:"M12 5v14M5 12h14"})})}function _h(){return r.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2","aria-hidden":!0,children:[r.jsx("path",{d:"M3 12a9 9 0 1 0 3-6.7"}),r.jsx("path",{d:"M3 4v5h5"})]})}function ao(){return r.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[r.jsx("path",{d:"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"}),r.jsx("path",{d:"M17 21v-8H7v8M7 3v5h8"})]})}function xh(){return r.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[r.jsx("circle",{cx:"12",cy:"12",r:"9"}),r.jsx("path",{d:"M8 8l8 8M16 8l-8 8"})]})}function vh(){return r.jsx("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:r.jsx("path",{d:"M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"})})}function yh({status:e,saveState:t,saveName:n,savedSessionCount:a,onStart:o,onPause:i,onResume:l,onEnd:c,onReset:d,onNewSession:u,canStartNewSession:h,onSaveClick:p,onSaveNameChange:f,onConfirmSave:g,onCancelSave:N,onCancelRecord:y,canCancelRecord:k,onOpenSessionsList:A}){const I=e==="recording",w=e==="paused",L=e==="ended",z=I||w,X=n.trim().length>0,R=()=>{if(w||L){l();return}o()};return r.jsxs("div",{className:"lifecycle-toolbar-wrap",children:[r.jsxs("div",{className:"lifecycle-toolbar",role:"toolbar","aria-label":"Điều khiển vòng đời phiên ghi",children:[w?r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--resume",onClick:l,children:[r.jsx(fh,{}),r.jsx("span",{children:"Tiếp tục ghi"})]}):r.jsxs("button",{type:"button",className:`lifecycle-toolbar__btn lifecycle-toolbar__btn--record${I?" lifecycle-toolbar__btn--recording":""}`,onClick:R,disabled:I,"aria-pressed":I,children:[r.jsx(gh,{pulsing:I}),r.jsx("span",{children:I?"Đang ghi...":"Ghi"})]}),r.jsx("span",{className:"lifecycle-toolbar__divider","aria-hidden":!0}),r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--pause",onClick:i,disabled:!I,children:[r.jsx(ph,{}),r.jsx("span",{children:"Tạm dừng"})]}),r.jsx("span",{className:"lifecycle-toolbar__divider","aria-hidden":!0}),r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--end",onClick:c,disabled:!z,children:[r.jsx(mh,{}),r.jsx("span",{children:"Kết thúc ghi"})]}),r.jsx("span",{className:"lifecycle-toolbar__divider","aria-hidden":!0}),r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--new",onClick:u,disabled:!h,title:"Tạo phiên ghi trống mới — nhấn Ghi khi sẵn sàng",children:[r.jsx(bh,{}),r.jsx("span",{children:"Phiên mới"})]}),r.jsx("span",{className:"lifecycle-toolbar__divider","aria-hidden":!0}),r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--reset",onClick:d,title:"Xóa phiên hiện tại và ghi lại từ đầu",children:[r.jsx(_h,{}),r.jsx("span",{children:"Làm mới từ đầu"})]})]}),r.jsxs("div",{className:"lifecycle-toolbar__save-row",children:[r.jsx("div",{className:"lifecycle-toolbar__save-group",children:t==="confirm"?r.jsxs(r.Fragment,{children:[r.jsxs("label",{className:"lifecycle-toolbar__save-label",children:[r.jsx("span",{className:"sr-only",children:"Tên phiên ghi"}),r.jsx("input",{type:"text",className:"lifecycle-toolbar__save-input",value:n,placeholder:"Đặt tên phiên ghi…",onChange:ie=>f(ie.target.value),autoFocus:!0})]}),r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--save lifecycle-toolbar__btn--save-confirm",onClick:g,disabled:!X,children:[r.jsx(ao,{}),r.jsx("span",{children:"Xác nhận lưu"})]}),r.jsx("button",{type:"button",className:"lifecycle-toolbar__save-cancel",onClick:N,children:"Hủy"})]}):r.jsxs(r.Fragment,{children:[r.jsxs("button",{type:"button",className:`lifecycle-toolbar__btn lifecycle-toolbar__btn--save lifecycle-toolbar__btn--save-${t}`,onClick:p,disabled:t!=="savable","aria-disabled":t!=="savable",children:[r.jsx(ao,{}),r.jsx("span",{children:"Lưu phiên"})]}),r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--cancel-record",onClick:y,disabled:!k,title:"Hủy phiên ghi hiện tại, không lưu",children:[r.jsx(xh,{}),r.jsx("span",{children:"Hủy ghi"})]})]})}),r.jsxs("button",{type:"button",className:"lifecycle-toolbar__btn lifecycle-toolbar__btn--sessions",onClick:A,children:[r.jsx(vh,{}),r.jsxs("span",{children:["Danh sách phiên (",a,")"]})]})]}),r.jsx("style",{children:` - .lifecycle-toolbar-wrap { - flex-shrink: 0; - display: flex; - flex-direction: column; - gap: 8px; - border-bottom: 1px solid #334155; - padding-bottom: 12px; - margin-bottom: 4px; - } - .lifecycle-toolbar { - display: flex; - align-items: stretch; - gap: 0; - } - .lifecycle-toolbar__save-row { - display: flex; - align-items: center; - gap: 8px; - } - .lifecycle-toolbar__save-group { - flex: 1; - min-width: 0; - display: flex; - align-items: center; - gap: 6px; - } - .lifecycle-toolbar__save-label { - flex: 1; - min-width: 0; - } - .lifecycle-toolbar__save-input { - width: 100%; - padding: 8px 10px; - border-radius: 8px; - border: 1px solid rgba(118, 200, 177, 0.45); - background: rgba(15, 23, 42, 0.92); - color: #f8fafc; - font-size: 12px; - } - .lifecycle-toolbar__save-input::placeholder { - color: #64748b; - } - .lifecycle-toolbar__save-cancel { - border: none; - background: transparent; - color: #94a3b8; - font-size: 11px; - font-weight: 600; - cursor: pointer; - padding: 6px 4px; - white-space: nowrap; - } - .lifecycle-toolbar__divider { - width: 1px; - background: #334155; - margin: 4px 4px; - flex-shrink: 0; - } - .lifecycle-toolbar__btn { - flex: 1; - min-width: 0; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 5px; - padding: 8px 4px; - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.1); - background: rgba(15, 23, 42, 0.72); - color: #e2e8f0; - font-size: 10px; - font-weight: 600; - cursor: pointer; - white-space: nowrap; - } - .lifecycle-toolbar__btn:disabled { - opacity: 0.45; - cursor: not-allowed; - } - .lifecycle-toolbar__btn--record { - background: rgba(15, 23, 42, 0.92); - } - .lifecycle-toolbar__btn--recording { - border-color: rgba(220, 38, 38, 0.45); - background: rgba(30, 23, 23, 0.85); - } - .lifecycle-toolbar__btn--resume { - border-color: rgba(118, 200, 177, 0.35); - color: #76c8b1; - } - .lifecycle-toolbar__btn--pause:not(:disabled):hover { - border-color: rgba(148, 163, 184, 0.45); - } - .lifecycle-toolbar__btn--end:not(:disabled) { - border-color: rgba(248, 113, 113, 0.35); - color: #fecaca; - } - .lifecycle-toolbar__btn--end:not(:disabled):hover { - border-color: rgba(248, 113, 113, 0.55); - background: rgba(69, 10, 10, 0.35); - } - .lifecycle-toolbar__btn--new { - border-color: rgba(118, 200, 177, 0.4); - color: #a7f3d0; - background: rgba(15, 45, 48, 0.35); - } - .lifecycle-toolbar__btn--new:not(:disabled):hover { - border-color: rgba(118, 200, 177, 0.65); - background: rgba(26, 95, 96, 0.45); - color: #ecfdf5; - } - .lifecycle-toolbar__btn--reset { - border-color: rgba(251, 191, 36, 0.5); - color: #fcd34d; - background: rgba(146, 64, 14, 0.22); - box-shadow: inset 0 0 0 1px rgba(251, 191, 36, 0.12); - } - .lifecycle-toolbar__btn--reset:hover { - border-color: rgba(251, 191, 36, 0.75); - background: rgba(146, 64, 14, 0.34); - color: #fef08a; - } - .lifecycle-toolbar__btn--reset svg { - color: #fbbf24; - } - .lifecycle-toolbar__btn--save { - flex: 0 1 auto; - min-width: 108px; - border-color: rgba(148, 163, 184, 0.2); - color: #94a3b8; - } - .lifecycle-toolbar__btn--save-savable { - border-color: rgba(118, 200, 177, 0.55); - color: #76c8b1; - background: rgba(26, 95, 96, 0.35); - } - .lifecycle-toolbar__btn--save-savable:not(:disabled):hover { - background: rgba(26, 95, 96, 0.55); - color: #ecfdf5; - } - .lifecycle-toolbar__btn--save-confirm { - flex: 0 0 auto; - border-color: rgba(118, 200, 177, 0.65); - color: #ecfdf5; - background: rgba(26, 95, 96, 0.9); - } - .lifecycle-toolbar__btn--cancel-record { - flex: 0 1 auto; - min-width: 96px; - border-color: rgba(248, 113, 113, 0.35); - color: #fecaca; - background: rgba(69, 10, 10, 0.22); - } - .lifecycle-toolbar__btn--cancel-record:not(:disabled):hover { - border-color: rgba(248, 113, 113, 0.55); - background: rgba(127, 29, 29, 0.38); - color: #fee2e2; - } - .lifecycle-toolbar__btn--sessions { - flex: 0 1 auto; - min-width: 0; - border-color: rgba(148, 163, 184, 0.25); - color: #cbd5e1; - } - .lifecycle-toolbar__btn--sessions:hover { - border-color: rgba(148, 163, 184, 0.45); - background: rgba(30, 41, 59, 0.85); - } - .lifecycle-toolbar__rec-dot { - width: 9px; - height: 9px; - border-radius: 50%; - background: #dc2626; - flex-shrink: 0; - } - .lifecycle-toolbar__rec-dot--pulse { - animation: lifecycle-rec-pulse 1s ease-in-out infinite; - } - @keyframes lifecycle-rec-pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.3; } - } - .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; - } - `})]})}function wh({open:e,onDismiss:t,onConfirm:n}){return e?Xt.createPortal(r.jsxs("div",{className:"session-cancel-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"session-cancel-title",onClick:t,children:[r.jsxs("div",{className:"session-cancel-dialog__card",onClick:a=>a.stopPropagation(),children:[r.jsx("h3",{id:"session-cancel-title",className:"session-cancel-dialog__title",children:"Hủy phiên ghi?"}),r.jsx("p",{className:"session-cancel-dialog__caption",children:"Phiên ghi hiện tại chưa được lưu sẽ bị xóa. Ảnh DICOM đã tải lên vẫn được giữ lại."}),r.jsxs("div",{className:"session-cancel-dialog__actions",children:[r.jsx("button",{type:"button",className:"session-cancel-dialog__btn session-cancel-dialog__btn--dismiss",onClick:t,children:"Quay lại"}),r.jsx("button",{type:"button",className:"session-cancel-dialog__btn session-cancel-dialog__btn--confirm",onClick:n,children:"Hủy ghi"})]})]}),r.jsx("style",{children:` - .session-cancel-dialog { - position: fixed; - inset: 0; - z-index: 1000; - display: flex; - align-items: center; - justify-content: center; - padding: 20px; - background: rgba(15, 23, 42, 0.62); - cursor: pointer; - } - .session-cancel-dialog__card { - width: min(420px, 100%); - border-radius: 14px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: #1e293b; - padding: 22px 20px 18px; - box-shadow: 0 24px 48px rgba(0, 0, 0, 0.35); - cursor: default; - } - .session-cancel-dialog__title { - margin: 0 0 10px; - font-size: 17px; - font-weight: 700; - color: #f8fafc; - } - .session-cancel-dialog__caption { - margin: 0 0 18px; - font-size: 13px; - line-height: 1.5; - color: #94a3b8; - } - .session-cancel-dialog__actions { - display: flex; - justify-content: flex-end; - gap: 10px; - } - .session-cancel-dialog__btn { - border-radius: 10px; - padding: 9px 14px; - font-size: 13px; - font-weight: 600; - cursor: pointer; - } - .session-cancel-dialog__btn--dismiss { - border: 1px solid rgba(148, 163, 184, 0.35); - background: #1e293b; - color: #cbd5e1; - } - .session-cancel-dialog__btn--dismiss:hover { - background: rgba(51, 65, 85, 0.65); - } - .session-cancel-dialog__btn--confirm { - border: 1px solid #b91c1c; - background: #dc2626; - color: #fff; - } - .session-cancel-dialog__btn--confirm:hover { - background: #b91c1c; - } - `})]}),document.body):null}function kh({open:e,onDismiss:t,onConfirm:n}){return e?Xt.createPortal(r.jsxs("div",{className:"session-new-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"session-new-title",onClick:t,children:[r.jsxs("div",{className:"session-new-dialog__card",onClick:a=>a.stopPropagation(),children:[r.jsx("h3",{id:"session-new-title",className:"session-new-dialog__title",children:"Tạo phiên ghi mới?"}),r.jsxs("p",{className:"session-new-dialog__caption",children:["Canvas và nhật ký phiên hiện tại sẽ được xóa. Phiên mới ở trạng thái chờ — nhấn"," ",r.jsx("strong",{children:"Ghi"})," khi sẵn sàng bắt đầu. Ảnh DICOM vẫn được giữ lại."]}),r.jsxs("div",{className:"session-new-dialog__actions",children:[r.jsx("button",{type:"button",className:"session-new-dialog__btn session-new-dialog__btn--dismiss",onClick:t,children:"Quay lại"}),r.jsx("button",{type:"button",className:"session-new-dialog__btn session-new-dialog__btn--confirm",onClick:n,children:"Phiên mới"})]})]}),r.jsx("style",{children:` - .session-new-dialog { - position: fixed; - inset: 0; - z-index: 1000; - display: flex; - align-items: center; - justify-content: center; - padding: 20px; - background: rgba(15, 23, 42, 0.62); - cursor: pointer; - } - .session-new-dialog__card { - width: min(440px, 100%); - border-radius: 14px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: #1e293b; - padding: 22px 20px 18px; - box-shadow: 0 24px 48px rgba(0, 0, 0, 0.35); - cursor: default; - } - .session-new-dialog__title { - margin: 0 0 10px; - font-size: 17px; - font-weight: 700; - color: #f8fafc; - } - .session-new-dialog__caption { - margin: 0 0 18px; - font-size: 13px; - line-height: 1.5; - color: #94a3b8; - } - .session-new-dialog__caption strong { - color: #cbd5e1; - } - .session-new-dialog__actions { - display: flex; - justify-content: flex-end; - gap: 10px; - } - .session-new-dialog__btn { - border-radius: 10px; - padding: 9px 14px; - font-size: 13px; - font-weight: 600; - cursor: pointer; - } - .session-new-dialog__btn--dismiss { - border: 1px solid rgba(148, 163, 184, 0.35); - background: #1e293b; - color: #cbd5e1; - } - .session-new-dialog__btn--dismiss:hover { - background: rgba(51, 65, 85, 0.65); - } - .session-new-dialog__btn--confirm { - border: 1px solid rgba(118, 200, 177, 0.55); - background: rgba(26, 95, 96, 0.85); - color: #ecfdf5; - } - .session-new-dialog__btn--confirm:hover { - background: rgba(26, 95, 96, 1); - } - `})]}),document.body):null}function jh({open:e,onCancel:t,onConfirmAndRecord:n,onConfirmDeleteOnly:a}){return e?Xt.createPortal(r.jsxs("div",{className:"session-reset-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"session-reset-title",onClick:t,children:[r.jsxs("div",{className:"session-reset-dialog__card",onClick:o=>o.stopPropagation(),children:[r.jsx("h3",{id:"session-reset-title",className:"session-reset-dialog__title",children:"Xóa toàn bộ tiến trình lịch sử?"}),r.jsxs("p",{className:"session-reset-dialog__caption",children:["Hành động này sẽ xóa vĩnh viễn tất cả nhật ký thao tác và danh sách tệp ảnh DICOM đã tải lên trong phiên làm việc này. Chọn ",r.jsx("strong",{children:"Xóa & Ghi lại"})," để bắt đầu ghi ngay, hoặc"," ",r.jsx("strong",{children:"Chỉ xóa & Dừng"})," để xóa và dừng ghi (không tự ghi lại)."]}),r.jsxs("div",{className:"session-reset-dialog__actions",children:[r.jsx("button",{type:"button",className:"session-reset-dialog__btn session-reset-dialog__btn--cancel",onClick:t,children:"Hủy"}),r.jsx("button",{type:"button",className:"session-reset-dialog__btn session-reset-dialog__btn--delete-only",onClick:a,children:"Chỉ xóa & Dừng"}),r.jsx("button",{type:"button",className:"session-reset-dialog__btn session-reset-dialog__btn--confirm",onClick:n,children:"Xóa & Ghi lại"})]})]}),r.jsx("style",{children:` - .session-reset-dialog { - position: fixed; - inset: 0; - z-index: 1000; - display: flex; - align-items: center; - justify-content: center; - padding: 20px; - background: rgba(15, 23, 42, 0.62); - cursor: pointer; - } - .session-reset-dialog__card { - position: relative; - z-index: 1; - width: min(460px, 100%); - border-radius: 14px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: #1e293b; - padding: 22px 20px 18px; - box-shadow: 0 24px 48px rgba(0, 0, 0, 0.35); - cursor: default; - pointer-events: auto; - } - .session-reset-dialog__title { - margin: 0 0 10px; - font-size: 17px; - font-weight: 700; - color: #f8fafc; - } - .session-reset-dialog__caption { - margin: 0 0 18px; - font-size: 13px; - line-height: 1.5; - color: #94a3b8; - } - .session-reset-dialog__caption strong { - color: #cbd5e1; - font-weight: 600; - } - .session-reset-dialog__actions { - display: flex; - flex-wrap: wrap; - justify-content: flex-end; - gap: 10px; - position: relative; - z-index: 2; - } - .session-reset-dialog__btn { - position: relative; - z-index: 1; - border-radius: 10px; - padding: 9px 14px; - font-size: 13px; - font-weight: 600; - cursor: pointer; - pointer-events: auto; - } - .session-reset-dialog__btn--cancel { - border: 1px solid rgba(148, 163, 184, 0.35); - background: #1e293b; - color: #cbd5e1; - } - .session-reset-dialog__btn--cancel:hover { - background: rgba(51, 65, 85, 0.65); - } - .session-reset-dialog__btn--delete-only { - border: 1px solid rgba(251, 191, 36, 0.55); - background: rgba(146, 64, 14, 0.18); - color: #fcd34d; - } - .session-reset-dialog__btn--delete-only:hover { - background: rgba(146, 64, 14, 0.3); - border-color: rgba(251, 191, 36, 0.75); - } - .session-reset-dialog__btn--confirm { - border: 1px solid #b91c1c; - background: #dc2626; - color: #fff; - } - .session-reset-dialog__btn--confirm:hover { - background: #b91c1c; - } - `})]}),document.body):null}function oo({session:e,isActive:t,onLoad:n,onDelete:a,nested:o}){const i=e.events.length,l=Ks(e.events);return r.jsxs("li",{className:`saved-sessions-dialog__item${t?" saved-sessions-dialog__item--active":""}${o?" saved-sessions-dialog__item--nested":""}`,children:[r.jsxs("div",{className:"saved-sessions-dialog__item-main",children:[r.jsx("strong",{children:e.name}),r.jsxs("span",{className:"saved-sessions-dialog__meta tnum",children:[Tr(e.savedAt)," · ",l," · ",i," hành động",e.segments.length>1?` · ${e.segments.length} khung`:""]}),e.description&&r.jsx("p",{className:"saved-sessions-dialog__desc",children:e.description})]}),r.jsxs("div",{className:"saved-sessions-dialog__actions",children:[r.jsx("button",{type:"button",className:"saved-sessions-dialog__load",onClick:()=>n(e.id),children:t?"Đang xem":"Mở"}),r.jsx("button",{type:"button",className:"saved-sessions-dialog__delete",onClick:()=>a(e.id),"aria-label":`Xóa phiên ${e.name}`,children:"Xóa"})]})]})}function Sh({open:e,sessions:t,encounterGroups:n,activeSessionId:a,onClose:o,onLoad:i,onDelete:l}){if(!e)return null;const c=new Set(n.flatMap(u=>u.childRecordIds)),d=t.filter(u=>!c.has(u.id));return r.jsxs("div",{className:"saved-sessions-dialog",role:"dialog","aria-modal":!0,"aria-labelledby":"saved-sessions-title",children:[r.jsx("button",{type:"button",className:"saved-sessions-dialog__backdrop","aria-label":"Đóng",onClick:o}),r.jsxs("div",{className:"saved-sessions-dialog__panel",children:[r.jsxs("header",{className:"saved-sessions-dialog__header",children:[r.jsx("h3",{id:"saved-sessions-title",children:"Danh sách phiên đã lưu"}),r.jsx("button",{type:"button",className:"saved-sessions-dialog__close",onClick:o,"aria-label":"Đóng",children:"×"})]}),t.length===0?r.jsx("p",{className:"saved-sessions-dialog__empty",children:"Chưa có phiên ghi nào được lưu cho bệnh nhân này."}):r.jsxs("ul",{className:"saved-sessions-dialog__list",children:[n.map(u=>{const h=u.childRecordIds.map(p=>t.find(f=>f.id===p)).filter(p=>!!p);return h.length===0?null:r.jsxs("li",{className:"saved-sessions-dialog__group",children:[r.jsxs("div",{className:"saved-sessions-dialog__group-header",children:[r.jsx("strong",{children:u.displayName??`Phiên ${Tr(u.startedAt)}`}),r.jsxs("span",{className:"saved-sessions-dialog__meta tnum",children:[h.length," khung"]})]}),r.jsx("ul",{className:"saved-sessions-dialog__group-children",children:h.map(p=>r.jsx(oo,{session:p,isActive:p.id===a,onLoad:i,onDelete:l,nested:!0},p.id))})]},u.id)}),d.map(u=>r.jsx(oo,{session:u,isActive:u.id===a,onLoad:i,onDelete:l},u.id))]}),r.jsx("footer",{className:"saved-sessions-dialog__footer",children:r.jsx("button",{type:"button",className:"saved-sessions-dialog__footer-btn",onClick:o,children:"Đóng"})})]}),r.jsx("style",{children:` - .saved-sessions-dialog { - position: fixed; - inset: 0; - z-index: 120; - display: flex; - align-items: center; - justify-content: center; - padding: 16px; - } - .saved-sessions-dialog__backdrop { - position: absolute; - inset: 0; - border: none; - background: rgba(15, 23, 42, 0.55); - cursor: pointer; - } - .saved-sessions-dialog__panel { - position: relative; - width: min(480px, 100%); - max-height: min(70vh, 520px); - display: flex; - flex-direction: column; - border-radius: 12px; - background: #1e293b; - border: 1px solid #334155; - box-shadow: 0 20px 48px rgba(0, 0, 0, 0.35); - color: #e2e8f0; - } - .saved-sessions-dialog__header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 14px 16px; - border-bottom: 1px solid #334155; - } - .saved-sessions-dialog__header h3 { - margin: 0; - font-size: 15px; - font-weight: 700; - } - .saved-sessions-dialog__close { - border: none; - background: transparent; - color: #94a3b8; - font-size: 22px; - line-height: 1; - cursor: pointer; - padding: 0 4px; - } - .saved-sessions-dialog__empty { - margin: 0; - padding: 24px 16px; - font-size: 13px; - color: #94a3b8; - } - .saved-sessions-dialog__list { - list-style: none; - margin: 0; - padding: 8px; - overflow: auto; - flex: 1; - min-height: 0; - } - .saved-sessions-dialog__group { - list-style: none; - margin-bottom: 8px; - border: 1px solid #334155; - border-radius: 8px; - overflow: hidden; - } - .saved-sessions-dialog__group-header { - display: flex; - justify-content: space-between; - gap: 8px; - padding: 8px 12px; - background: rgba(15, 23, 42, 0.65); - border-bottom: 1px solid #334155; - } - .saved-sessions-dialog__group-header strong { - font-size: 12px; - } - .saved-sessions-dialog__group-children { - list-style: none; - margin: 0; - padding: 0; - } - .saved-sessions-dialog__item { - display: flex; - gap: 10px; - align-items: flex-start; - padding: 10px 12px; - border-radius: 8px; - border: 1px solid transparent; - } - .saved-sessions-dialog__item--nested { - border-radius: 0; - border-bottom: 1px solid rgba(51, 65, 85, 0.65); - } - .saved-sessions-dialog__item--active { - border-color: rgba(118, 200, 177, 0.45); - background: rgba(26, 95, 96, 0.25); - } - .saved-sessions-dialog__item-main { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 4px; - } - .saved-sessions-dialog__item-main strong { - font-size: 13px; - color: #f8fafc; - } - .saved-sessions-dialog__meta { - font-size: 11px; - color: #94a3b8; - } - .saved-sessions-dialog__desc { - margin: 0; - font-size: 12px; - color: #cbd5e1; - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - .saved-sessions-dialog__actions { - display: flex; - flex-direction: column; - gap: 4px; - flex-shrink: 0; - } - .saved-sessions-dialog__load, - .saved-sessions-dialog__delete { - border-radius: 6px; - border: 1px solid rgba(255, 255, 255, 0.12); - padding: 5px 10px; - font-size: 11px; - font-weight: 600; - cursor: pointer; - } - .saved-sessions-dialog__load { - background: rgba(26, 95, 96, 0.85); - color: #ecfdf5; - border-color: rgba(118, 200, 177, 0.35); - } - .saved-sessions-dialog__delete { - background: transparent; - color: #fca5a5; - border-color: rgba(248, 113, 113, 0.25); - } - .saved-sessions-dialog__footer { - padding: 10px 16px 14px; - border-top: 1px solid #334155; - display: flex; - justify-content: flex-end; - } - .saved-sessions-dialog__footer-btn { - border-radius: 8px; - border: 1px solid #475569; - background: rgba(15, 23, 42, 0.8); - color: #e2e8f0; - padding: 8px 14px; - font-size: 12px; - font-weight: 600; - cursor: pointer; - } - `})]})}const io=[{id:"single_frame",label:"Một khung",hint:"Ghi trên khung hiện tại — không đổi khung khi đang ghi"},{id:"multi_frame",label:"Nhiều khung",hint:"Một phiên liên tục — mỗi lần đổi khung tạo đoạn mới trên cùng dòng thời gian"}];function Nh({value:e,onChange:t,disabled:n}){var o;const a=((o=io.find(i=>i.id===e))==null?void 0:o.hint)??"";return r.jsxs("fieldset",{className:"recording-mode-selector",disabled:n,children:[r.jsx("legend",{children:"Phạm vi ghi phiên"}),r.jsx("div",{className:"recording-mode-selector__options",children:io.map(i=>r.jsxs("label",{className:"recording-mode-selector__option",children:[r.jsx("input",{type:"radio",name:"recording-mode",value:i.id,checked:e===i.id,onChange:()=>t(i.id)}),i.label]},i.id))}),a&&r.jsx("p",{className:"recording-mode-selector__hint",children:a}),r.jsx("style",{children:` - .recording-mode-selector { - border: none; - margin: 0; - padding: 0; - } - .recording-mode-selector legend { - font-size: 13px; - font-weight: 700; - color: #e2e8f0; - margin-bottom: 8px; - } - .recording-mode-selector__options { - display: flex; - flex-direction: column; - gap: 8px; - } - .recording-mode-selector__option { - display: flex; - align-items: center; - gap: 10px; - font-size: 14px; - font-weight: 500; - color: #f1f5f9; - cursor: pointer; - } - .recording-mode-selector__option input { - width: 16px; - height: 16px; - accent-color: #76c8b1; - } - .recording-mode-selector__hint { - margin: 8px 0 0; - font-size: 12.5px; - line-height: 1.5; - color: #cbd5e1; - } - .recording-mode-selector:disabled .recording-mode-selector__option { - opacity: 0.55; - cursor: not-allowed; - } - `})]})}function Mh({events:e,activeEventId:t,onSelectEvent:n}){return e.length===0?null:r.jsxs("section",{className:"recorded-actions","aria-label":"Danh sách hành động đã ghi",children:[r.jsxs("header",{className:"recorded-actions__header",children:[r.jsx("h3",{className:"recorded-actions__title",children:"Danh sách hành động đã ghi"}),r.jsxs("span",{className:"recorded-actions__count",children:[e.length," bước"]})]}),r.jsx("ol",{className:"recorded-actions__list",children:e.map((a,o)=>r.jsx("li",{children:r.jsxs("button",{type:"button",className:`recorded-actions__item${t===a.id?" recorded-actions__item--active":""}`,onClick:()=>n==null?void 0:n(a.id,o),children:[r.jsx("span",{className:"recorded-actions__kind",style:{background:So(a.kind)},"aria-hidden":!0}),r.jsxs("span",{className:"recorded-actions__body",children:[r.jsxs("span",{className:"recorded-actions__item-title",children:[o+1,". ",a.title]}),r.jsx("span",{className:"recorded-actions__item-detail",children:a.detail}),r.jsxs("span",{className:"recorded-actions__item-meta tnum",children:[bn(a.elapsedSeconds)," · Ghi nhận lúc ",a.recordedAt]})]})]})},a.id))}),r.jsx("style",{children:` - .recorded-actions { - flex-shrink: 0; - display: flex; - flex-direction: column; - gap: 8px; - max-height: 220px; - } - .recorded-actions__header { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 8px; - } - .recorded-actions__title { - margin: 0; - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.04em; - color: #76c8b1; - } - .recorded-actions__count { - font-size: 11px; - font-weight: 600; - color: #94a3b8; - } - .recorded-actions__list { - list-style: none; - margin: 0; - padding: 0; - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 6px; - } - .recorded-actions__item { - width: 100%; - display: flex; - align-items: flex-start; - gap: 10px; - padding: 10px; - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.1); - background: rgba(15, 23, 42, 0.55); - color: #e2e8f0; - text-align: left; - cursor: pointer; - } - .recorded-actions__item:hover { - border-color: rgba(118, 200, 177, 0.35); - } - .recorded-actions__item--active { - border-color: rgba(118, 200, 177, 0.55); - background: rgba(26, 95, 96, 0.22); - } - .recorded-actions__kind { - flex-shrink: 0; - width: 4px; - align-self: stretch; - border-radius: 999px; - min-height: 36px; - } - .recorded-actions__body { - display: flex; - flex-direction: column; - gap: 3px; - min-width: 0; - } - .recorded-actions__item-title { - font-size: 12px; - font-weight: 700; - color: #f1f5f9; - line-height: 1.35; - } - .recorded-actions__item-detail { - font-size: 11px; - color: #cbd5e1; - line-height: 1.4; - } - .recorded-actions__item-meta { - font-size: 10px; - color: #94a3b8; - } - `})]})}function Ch(){return r.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:r.jsx("path",{d:"M8 5v14l11-7z"})})}function Eh(){return r.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":!0,children:r.jsx("path",{d:"M6 5h4v14H6zM14 5h4v14h-4z"})})}function Th(){return r.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[r.jsx("path",{d:"M11 19V5l-7 7 7 7z"}),r.jsx("path",{d:"M18 19V5l-7 7 7 7z"})]})}function Ih(){return r.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[r.jsx("path",{d:"M13 5v14l7-7-7-7z"}),r.jsx("path",{d:"M6 5v14l7-7-7-7z"})]})}function Lh({patientId:e,onViewportChange:t,onFrameRestore:n,onReplaySegmentViewport:a}){var Ne,ge,ae;const o=Yt(),i=o.sessionLog,l=o.status==="ended",c=o.status==="recording"||o.status==="paused",[d,u]=s.useState(()=>yt(e)),[h,p]=s.useState(()=>ca(e)),f=s.useMemo(()=>({...i,events:i.events.map(E=>nl(E,d))}),[i,d]),g=hh({log:f,showPointerTrace:h,onViewportChange:c?void 0:t,onFrameRestore:c||o.status!=="ended"?void 0:(E,$,se)=>n==null?void 0:n(E,$,se),onReplaySegmentViewport:c?void 0:a}),N=s.useRef(null),y=s.useRef(null),[k,A]=s.useState(!1),[I,w]=s.useState(null),[L,z]=s.useState(!1),[X,R]=s.useState(()=>Ft(e)),[ie,U]=s.useState(()=>vt(e));s.useEffect(()=>{R(Ft(e)),U(vt(e)),u(yt(e)),p(ca(e))},[e,i.events.length,o.status,o.activeSavedSessionId]);const v=s.useRef(g);v.current=g,s.useEffect(()=>{const E=y.current;y.current=o.status,o.status==="ended"&&E!=="ended"&&(v.current.switchMode("carousel"),v.current.goToStep(0),v.current.resetPlaybackPosition()),o.status==="recording"&&E==="idle"&&v.current.resetPlayback()},[o.status]);const B=s.useCallback(E=>{R(E),Eo(e,E)},[e]),re=s.useCallback((E,$)=>{U(se=>{const ke={...se};return $.trim()?ke[E]=$.trim():delete ke[E],ke}),Zs(e,E,$)},[e]),V=s.useCallback((E,$,se)=>{u(tl(e,E,$==="title"?{title:se}:{detail:se}))},[e]),ne=Math.min(100,Math.max(0,g.elapsedSeconds/Math.max(g.totalDuration,.001)*100)),F=g.events.length>0,_=((Ne=g.activeEvent)==null?void 0:Ne.id)??"",O=ie[_]??"",S=((ge=g.activeEvent)==null?void 0:ge.title)??"",P=((ae=g.activeEvent)==null?void 0:ae.detail)??"",j=I?g.events.find(E=>E.id===I)??null:null,ee=j?Math.min(98,Math.max(2,j.elapsedSeconds/g.totalDuration*100)):50,J=s.useCallback(E=>{if(k){w(null);return}const $=N.current;if(!$)return;const se=$.getBoundingClientRect();if(se.width<=0)return;const ke=E-se.left,Fe=14;let Pe=null,q=Fe;for(const oe of g.events){const we=oe.elapsedSeconds/g.totalDuration*se.width,de=Math.abs(ke-we);de<=q&&(q=de,Pe=oe.id)}w(Pe)},[k,g.events,g.totalDuration]),K=s.useCallback(E=>{const $=N.current;if(!$)return;const se=$.getBoundingClientRect();if(se.width<=0)return;const ke=Math.max(0,Math.min(1,(E-se.left)/se.width));g.seek(ke*g.totalDuration)},[g]),Ee=E=>{E.target.closest(".review-session__timeline-tick")||(A(!0),E.currentTarget.setPointerCapture(E.pointerId),K(E.clientX))},pe=E=>{k&&K(E.clientX)},he=E=>{k&&(A(!1),E.currentTarget.hasPointerCapture(E.pointerId)&&E.currentTarget.releasePointerCapture(E.pointerId))};return r.jsxs("div",{className:"review-session",children:[r.jsxs("header",{className:"review-session__header",children:[r.jsx("h2",{className:"review-session__title",children:"XEM LẠI PHIÊN CHUẨN ĐOÁN"}),r.jsx(Nh,{value:o.recordingMode,onChange:o.setRecordingMode,disabled:o.status==="recording"||o.status==="paused"})]}),r.jsx(yh,{status:o.status,saveState:o.saveUiState,saveName:o.saveNameDraft,savedSessionCount:o.savedSessions.length,onStart:o.startRecording,onPause:o.pauseRecording,onResume:o.resumeRecording,onEnd:o.endRecording,onReset:o.requestReset,onNewSession:o.requestNewSession,canStartNewSession:o.canStartNewSession,onSaveClick:o.beginSaveSession,onSaveNameChange:o.setSaveNameDraft,onConfirmSave:o.confirmSaveSession,onCancelSave:o.cancelSaveSession,onCancelRecord:o.requestCancelRecording,canCancelRecord:o.canCancelRecording,onOpenSessionsList:o.openSessionsDialog}),r.jsx(wh,{open:o.cancelRecordDialogOpen,onDismiss:o.dismissCancelRecording,onConfirm:o.confirmCancelRecording}),r.jsx(kh,{open:o.newSessionDialogOpen,onDismiss:o.dismissNewSession,onConfirm:o.confirmNewSession}),r.jsx(Sh,{open:o.sessionsDialogOpen,sessions:o.savedSessions,encounterGroups:o.encounterGroups,activeSessionId:o.activeSavedSessionId,onClose:o.closeSessionsDialog,onLoad:o.loadSavedSession,onDelete:o.deleteSavedSession}),r.jsx(jh,{open:o.resetDialogOpen,onCancel:o.cancelReset,onConfirmAndRecord:()=>void o.confirmReset(),onConfirmDeleteOnly:()=>void o.confirmResetOnly()}),r.jsxs("div",{className:"review-session__tabs",role:"tablist","aria-label":"Chế độ xem lại phiên",children:[r.jsx("div",{className:"review-session__tab-indicator",style:{transform:g.mode==="continuous"?"translateX(0%)":"translateX(100%)"},"aria-hidden":!0}),r.jsxs("button",{type:"button",role:"tab","aria-selected":g.mode==="continuous",className:`review-session__tab ${g.mode==="continuous"?"review-session__tab--active":""}`,onClick:()=>g.switchMode("continuous"),children:[r.jsx("span",{className:"review-session__tab-icon","aria-hidden":!0,children:"▶"}),"Phát liên tục"]}),r.jsxs("button",{type:"button",role:"tab","aria-selected":g.mode==="carousel",className:`review-session__tab ${g.mode==="carousel"?"review-session__tab--active":""}`,onClick:()=>g.switchMode("carousel"),children:[r.jsx("span",{className:"review-session__tab-icon","aria-hidden":!0,children:"☰"}),"Từng bước hành động"]})]}),r.jsxs("label",{className:"review-session__session-desc",children:[r.jsx("span",{className:"review-session__session-desc-label",children:"Mô tả phiên chẩn đoán"}),r.jsx("textarea",{rows:2,value:X,placeholder:"Ghi chú tổng quan: mục tiêu phiên, bối cảnh lâm sàng, điểm cần rà soát…",onChange:E=>B(E.target.value)})]}),l&&r.jsx(Mh,{events:g.events,activeEventId:_,onSelectEvent:(E,$)=>g.goToStep($)}),r.jsx("div",{className:"review-session__stage",children:F?r.jsxs(r.Fragment,{children:[r.jsxs("label",{className:"review-session__trace-toggle",children:[r.jsx("input",{type:"checkbox",checked:h,onChange:E=>{const $=E.target.checked;p($),rl(e,$)}}),r.jsx("span",{children:"Hiển thị vết chuột (comet)"})]}),g.mode==="continuous"?r.jsxs("div",{className:"review-session__continuous",children:[r.jsxs("div",{className:"review-session__timeline-wrap",onMouseMove:E=>J(E.clientX),onMouseLeave:()=>w(null),children:[r.jsx("div",{className:"review-session__timeline-tooltip-slot","aria-hidden":!j,children:j&&r.jsx("div",{className:"review-session__timeline-tooltip",role:"tooltip",style:{left:`${ee}%`},children:oa(j)})}),r.jsxs("div",{ref:N,className:`review-session__timeline ${k?"review-session__timeline--scrubbing":""} ${g.isPlaying?"review-session__timeline--playing":""}`,role:"slider","aria-valuemin":0,"aria-valuemax":g.totalDuration,"aria-valuenow":Math.round(g.elapsedSeconds),"aria-label":"Thanh thời gian phát lại — kéo để chọn vị trí xem",onPointerDown:Ee,onPointerMove:pe,onPointerUp:he,onPointerCancel:he,children:[r.jsx("div",{className:"review-session__timeline-fill",style:{width:`${ne}%`}}),r.jsx("div",{className:"review-session__timeline-thumb",style:{left:`${ne}%`},"aria-hidden":!0}),g.events.map(E=>{const $=Math.min(98,Math.max(2,E.elapsedSeconds/g.totalDuration*100));return r.jsx("button",{type:"button",className:"review-session__timeline-tick",style:{left:`${$}%`,background:So(E.kind)},"aria-label":oa(E),onPointerDown:se=>se.stopPropagation(),onClick:()=>g.seek(E.elapsedSeconds)},E.id)})]}),r.jsxs("div",{className:"review-session__timeline-times",children:[r.jsx("span",{className:"tnum",children:bn(g.elapsedSeconds)}),r.jsx("span",{className:"tnum",children:bn(g.totalDuration)})]})]}),r.jsxs("div",{className:"review-session__deck",children:[r.jsx("button",{type:"button",className:"review-session__deck-btn","aria-label":"Lùi 10 giây",onClick:()=>g.skip(-10),children:r.jsx(Th,{})}),r.jsx("button",{type:"button",className:"review-session__deck-btn review-session__deck-btn--primary","aria-label":g.isPlaying?"Tạm dừng":"Phát",onClick:g.togglePlay,children:g.isPlaying?r.jsx(Eh,{}):r.jsx(Ch,{})}),r.jsx("button",{type:"button",className:"review-session__deck-btn","aria-label":"Tiến 10 giây",onClick:()=>g.skip(10),children:r.jsx(Ih,{})}),r.jsxs("div",{className:"review-session__speed",children:[r.jsxs("button",{type:"button",className:"review-session__speed-btn tnum","aria-expanded":L,onClick:()=>z(E=>!E),children:[g.speed,"x"]}),L&&r.jsx("ul",{className:"review-session__speed-menu",role:"menu",children:g.speeds.map(E=>r.jsx("li",{children:r.jsxs("button",{type:"button",role:"menuitem",className:E===g.speed?"review-session__speed-option--active":"",onClick:()=>{g.setSpeed(E),z(!1)},children:[E,"x"]})},E))})]})]})]}):r.jsxs("div",{className:"review-session__carousel-block",children:[r.jsxs("div",{className:"review-session__carousel",children:[r.jsx("button",{type:"button",className:"review-session__carousel-nav","aria-label":"Bước trước",disabled:g.carouselIndex<=0,onClick:()=>g.goCarousel(-1),children:"←"}),r.jsx("article",{className:"review-session__event-card",children:g.activeEvent&&r.jsxs(r.Fragment,{children:[r.jsxs("p",{className:"review-session__event-step-label",children:["Hành động ",g.carouselIndex+1," / ",g.totalSteps]}),r.jsxs("label",{className:"review-session__event-field",children:[r.jsx("span",{className:"review-session__event-field-label",children:"Tên hành động"}),r.jsx("input",{type:"text",className:"review-session__event-title-input",value:S,onChange:E=>V(_,"title",E.target.value)},`${_}-title`)]}),r.jsxs("label",{className:"review-session__event-field",children:[r.jsx("span",{className:"review-session__event-field-label",children:"Chi tiết"}),r.jsx("textarea",{rows:2,className:"review-session__event-detail-input",value:P,onChange:E=>V(_,"detail",E.target.value)},`${_}-detail`)]}),r.jsxs("p",{className:"review-session__event-time tnum",children:["Ghi nhận lúc: ",g.activeEvent.recordedAt]})]})}),r.jsx("button",{type:"button",className:"review-session__carousel-nav","aria-label":"Bước sau",disabled:g.carouselIndex>=g.totalSteps-1,onClick:()=>g.goCarousel(1),children:"→"})]}),r.jsxs("label",{className:"review-session__step-note",children:[r.jsx("span",{className:"review-session__step-note-label",children:"Mô tả bước này (tự ghi trong phiên)"}),r.jsx("textarea",{rows:3,value:O,placeholder:"Ví dụ: Bác sĩ xác nhận vùng tràn dịch suprapatellar và so sánh với ca trước…",onChange:E=>re(_,E.target.value)},_)]})]})]}):r.jsxs("div",{className:"review-session__empty",role:"status",children:[r.jsx("p",{className:"review-session__empty-title",children:"Chưa có phiên ghi"}),r.jsxs("p",{className:"review-session__empty-detail",children:["Nhấn ",r.jsx("strong",{children:"Ghi"})," trên thanh công cụ để bắt đầu ghi lại các thao tác chẩn đoán. Sau khi kết thúc phiên, thanh thời gian và phát lại sẽ hiển thị tại đây."]})]})}),r.jsx("style",{children:` - .review-session { - height: 100%; - min-height: 100%; - display: flex; - flex-direction: column; - gap: 12px; - padding: 16px; - background: rgba(30, 41, 59, 0.98); - color: #e2e8f0; - } - .review-session__header { - flex-shrink: 0; - padding-bottom: 8px; - } - .review-session__title { - margin: 0 0 16px; - font-size: 15px; - font-weight: 700; - letter-spacing: 0.02em; - color: #ffffff; - } - .review-session__tabs { - flex-shrink: 0; - position: relative; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 4px; - padding: 4px; - border-radius: 10px; - background: rgba(15, 23, 42, 0.65); - } - .review-session__tab-indicator { - position: absolute; - top: 4px; - left: 4px; - width: calc(50% - 4px); - height: calc(100% - 8px); - border-radius: 8px; - background: #1a5f60; - transition: transform 0.25s ease; - z-index: 0; - } - .review-session__tab { - position: relative; - z-index: 1; - border: none; - background: transparent; - color: #94a3b8; - font-size: 12px; - font-weight: 600; - padding: 10px 8px; - border-radius: 8px; - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - } - .review-session__tab--active { - color: #ffffff; - } - .review-session__tab-icon { - font-size: 11px; - opacity: 0.85; - } - .review-session__session-desc { - flex-shrink: 0; - display: flex; - flex-direction: column; - gap: 6px; - } - .review-session__session-desc-label, - .review-session__step-note-label { - font-size: 11px; - font-weight: 600; - color: #94a3b8; - text-transform: uppercase; - letter-spacing: 0.04em; - } - .review-session__session-desc textarea, - .review-session__step-note textarea { - width: 100%; - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(15, 23, 42, 0.72); - color: #f8fafc; - padding: 8px 10px; - font-size: 13px; - line-height: 1.45; - resize: vertical; - } - .review-session__session-desc textarea:focus, - .review-session__step-note textarea:focus { - outline: none; - border-color: #76c8b1; - box-shadow: 0 0 0 2px rgba(118, 200, 177, 0.25); - } - .review-session__stage { - flex: 1; - min-height: 0; - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: center; - gap: 10px; - padding: 8px 0; - } - .review-session__empty { - text-align: center; - padding: 24px 16px; - border-radius: 10px; - border: 1px dashed rgba(148, 163, 184, 0.35); - background: rgba(15, 23, 42, 0.45); - } - .review-session__empty-title { - margin: 0 0 8px; - font-size: 14px; - font-weight: 600; - color: #e2e8f0; - } - .review-session__empty-detail { - margin: 0; - font-size: 12px; - line-height: 1.5; - color: #94a3b8; - } - .review-session__empty-detail strong { - color: #76c8b1; - font-weight: 600; - } - .review-session__trace-toggle { - flex-shrink: 0; - display: inline-flex; - align-items: center; - gap: 8px; - align-self: flex-start; - font-size: 12px; - color: #cbd5e1; - cursor: pointer; - user-select: none; - } - .review-session__trace-toggle input { - width: 14px; - height: 14px; - accent-color: #76c8b1; - cursor: pointer; - } - .review-session__continuous { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - gap: 20px; - } - .review-session__timeline-wrap { - width: 100%; - position: relative; - padding: 4px 0 14px; - } - .review-session__timeline-tooltip-slot { - position: relative; - height: 20px; - margin-bottom: 6px; - } - .review-session__timeline { - position: relative; - height: 8px; - border-radius: 999px; - background: rgba(148, 163, 184, 0.25); - overflow: visible; - cursor: pointer; - touch-action: none; - } - .review-session__timeline--scrubbing { - cursor: grabbing; - } - .review-session__timeline-fill { - position: absolute; - inset: 0 auto 0 0; - height: 100%; - border-radius: inherit; - background: linear-gradient(90deg, #1a5f60, #76c8b1); - pointer-events: none; - transition: width 0.08s linear; - } - .review-session__timeline--scrubbing .review-session__timeline-fill { - transition: none; - } - .review-session__timeline--playing .review-session__timeline-fill, - .review-session__timeline--playing .review-session__timeline-thumb { - transition: none; - } - .review-session__timeline-thumb { - position: absolute; - top: 50%; - width: 16px; - height: 16px; - border-radius: 50%; - background: #ffffff; - border: 2px solid #1a5f60; - box-shadow: 0 0 0 4px rgba(26, 95, 96, 0.25); - transform: translate(-50%, -50%); - pointer-events: none; - z-index: 3; - transition: left 0.08s linear, transform 0.12s ease; - } - .review-session__timeline--scrubbing .review-session__timeline-thumb { - transition: none; - transform: translate(-50%, -50%) scale(1.12); - } - .review-session__timeline-tick { - position: absolute; - top: 50%; - width: 3px; - height: 14px; - transform: translate(-50%, -50%); - border: none; - border-radius: 2px; - cursor: pointer; - padding: 0; - z-index: 2; - } - .review-session__timeline-tooltip { - position: absolute; - top: 0; - transform: translateX(-50%); - font-size: 11px; - color: #cbd5e1; - text-align: center; - white-space: nowrap; - pointer-events: none; - max-width: min(280px, 90vw); - overflow: hidden; - text-overflow: ellipsis; - } - .review-session__timeline-times { - display: flex; - justify-content: space-between; - margin-top: 8px; - font-size: 12px; - color: #94a3b8; - } - .review-session__deck { - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - } - .review-session__deck-btn { - width: 40px; - height: 40px; - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(15, 23, 42, 0.55); - color: #e2e8f0; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - } - .review-session__deck-btn--primary { - width: 48px; - height: 48px; - background: #1a5f60; - border-color: #1a5f60; - color: #fff; - } - .review-session__speed { - position: relative; - margin-left: 4px; - } - .review-session__speed-btn { - min-width: 52px; - height: 40px; - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(15, 23, 42, 0.55); - color: #e2e8f0; - font-weight: 700; - cursor: pointer; - } - .review-session__speed-menu { - position: absolute; - bottom: calc(100% + 6px); - right: 0; - list-style: none; - margin: 0; - padding: 6px; - border-radius: 10px; - background: #0f172a; - border: 1px solid rgba(255, 255, 255, 0.1); - min-width: 72px; - z-index: 5; - } - .review-session__speed-menu button { - width: 100%; - border: none; - background: transparent; - color: #cbd5e1; - padding: 8px 10px; - border-radius: 6px; - cursor: pointer; - font-weight: 600; - } - .review-session__speed-menu button:hover, - .review-session__speed-option--active { - background: rgba(26, 95, 96, 0.35); - color: #fff; - } - .review-session__carousel-block { - width: 100%; - display: flex; - flex-direction: column; - gap: 14px; - } - .review-session__carousel { - display: grid; - grid-template-columns: 40px 1fr 40px; - gap: 8px; - align-items: stretch; - } - .review-session__carousel-nav { - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 10px; - background: rgba(15, 23, 42, 0.55); - color: #e2e8f0; - font-size: 18px; - cursor: pointer; - } - .review-session__carousel-nav:disabled { - opacity: 0.3; - cursor: not-allowed; - } - .review-session__event-card { - border-radius: 12px; - border: 1px solid rgba(118, 200, 177, 0.25); - background: rgba(15, 23, 42, 0.72); - padding: 14px; - display: flex; - flex-direction: column; - gap: 8px; - } - .review-session__event-step-label { - margin: 0; - font-size: 11px; - font-weight: 700; - color: #76c8b1; - text-transform: uppercase; - letter-spacing: 0.04em; - } - .review-session__event-field { - display: flex; - flex-direction: column; - gap: 4px; - } - .review-session__event-field-label { - font-size: 10px; - font-weight: 600; - color: #94a3b8; - text-transform: uppercase; - letter-spacing: 0.04em; - } - .review-session__event-title-input, - .review-session__event-detail-input { - width: 100%; - border-radius: 8px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(15, 23, 42, 0.72); - color: #f8fafc; - padding: 8px 10px; - font-size: 13px; - line-height: 1.45; - } - .review-session__event-title-input { - font-weight: 600; - color: #76c8b1; - } - .review-session__event-detail-input { - resize: vertical; - min-height: 52px; - } - .review-session__event-title-input:focus, - .review-session__event-detail-input:focus { - outline: none; - border-color: #76c8b1; - box-shadow: 0 0 0 2px rgba(118, 200, 177, 0.25); - } - .review-session__event-time { - margin: 0; - font-size: 12px; - color: #94a3b8; - } - .review-session__step-note { - display: flex; - flex-direction: column; - gap: 6px; - } - `})]})}function Ph(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2","aria-hidden":!0,children:r.jsx("path",{d:"m18 15-6-6-6 6"})})}function Ah(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2","aria-hidden":!0,children:r.jsx("path",{d:"m6 9 6 6 6-6"})})}function Rh({reviewLayer:e,diagnosisLayer:t,activeLayer:n,onLayerChange:a}){const[o,i]=s.useState("diagnosis"),l=n??o,c=p=>{a?a(p):i(p)},d=()=>c(l==="review"?"diagnosis":"review"),u=l==="review"?"diagnosis":"review",h=u==="review"?"Xem lại phiên chẩn đoán":"Thông tin chẩn đoán";return r.jsxs("div",{className:"sidebar-carousel",children:[r.jsxs("div",{className:"sidebar-carousel__nav",children:[r.jsx("button",{type:"button",className:"sidebar-carousel__nav-btn","aria-label":"Xoay lớp lên",onClick:d,children:r.jsx(Ph,{})}),r.jsx("button",{type:"button",className:"sidebar-carousel__nav-btn","aria-label":"Xoay lớp xuống",onClick:d,children:r.jsx(Ah,{})})]}),r.jsxs("div",{className:"sidebar-carousel__stack",children:[r.jsx("button",{type:"button",className:`sidebar-carousel__peek sidebar-carousel__peek--${u}`,"aria-label":`Chuyển sang ${h}`,onClick:()=>c(u),children:r.jsx("span",{className:"sidebar-carousel__peek-label",children:h})}),r.jsx("div",{className:`sidebar-carousel__foreground sidebar-carousel__foreground--${l}`,children:r.jsxs("div",{className:"sidebar-carousel__foreground-inner",children:[r.jsx("div",{className:`sidebar-carousel__layer${l==="review"?" sidebar-carousel__layer--active":""}`,"aria-hidden":l!=="review",children:e}),r.jsx("div",{className:`sidebar-carousel__layer${l==="diagnosis"?" sidebar-carousel__layer--active":""}`,"aria-hidden":l!=="diagnosis",children:t})]})})]}),r.jsx("style",{children:` - .sidebar-carousel { - position: relative; - flex: 1; - min-height: 0; - display: flex; - flex-direction: column; - } - .sidebar-carousel__nav { - position: absolute; - top: 36px; - right: 8px; - z-index: 30; - display: flex; - gap: 4px; - } - .sidebar-carousel__nav-btn { - width: 30px; - height: 30px; - border-radius: 8px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.92); - color: var(--color-secondary); - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - backdrop-filter: blur(8px); - } - .sidebar-carousel__nav-btn:hover { - border-color: var(--color-primary); - } - .sidebar-carousel__stack { - position: relative; - flex: 1; - min-height: 420px; - display: flex; - flex-direction: column; - overflow: hidden; - border-radius: var(--radius-lg); - border: 1px solid rgba(5, 107, 88, 0.18); - } - .sidebar-carousel__peek { - flex: 0 0 28px; - width: 100%; - border: none; - padding: 0 12px; - display: flex; - align-items: center; - cursor: pointer; - z-index: 5; - } - .sidebar-carousel__peek--review { - background: rgba(30, 41, 59, 0.55); - color: #cbd5e1; - } - .sidebar-carousel__peek--diagnosis { - background: rgba(255, 255, 255, 0.45); - color: var(--color-on-surface-variant); - border-bottom: 1px solid rgba(5, 107, 88, 0.12); - } - .sidebar-carousel__peek-label { - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.06em; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .sidebar-carousel__foreground { - flex: 1; - min-height: 0; - position: relative; - z-index: 20; - overflow: hidden; - box-shadow: 0 -4px 20px rgba(15, 23, 42, 0.12); - } - .sidebar-carousel__foreground--review { - background: rgba(30, 41, 59, 0.98); - } - .sidebar-carousel__foreground--diagnosis { - background: rgba(255, 255, 255, 0.96); - } - .sidebar-carousel__foreground-inner { - position: relative; - height: 100%; - min-height: 100%; - overflow: hidden; - } - .sidebar-carousel__layer { - display: none; - height: 100%; - min-height: 100%; - overflow: auto; - } - .sidebar-carousel__layer--active { - display: block; - } - .sidebar-carousel__foreground--diagnosis .side-nav { - border: none; - box-shadow: none; - border-radius: 0; - background: transparent; - backdrop-filter: none; - -webkit-backdrop-filter: none; - } - `})]})}function Dh({points:e}){if(e.length<2)return null;const t=e.length-1;return r.jsx(r.Fragment,{children:e.slice(1).map((n,a)=>{const o=e[a],i=t<=1?1:(a+1)/t,l=.06+i*.74,c=.12+i*.5;return r.jsx("line",{x1:o.x,y1:o.y,x2:n.x,y2:n.y,stroke:"#ff007f",strokeOpacity:l,strokeWidth:c,strokeLinecap:"round"},`${o.elapsedSeconds}-${n.elapsedSeconds}-${a}`)})})}function Oh({viewport:e}){if(!e.active)return null;const t=e.showPointerTrace&&e.trailPoints.length>0,n=e.penColor??"#ff007f",a=e.isOverlayEditing?16:14;return r.jsxs("div",{className:"replay-sandbox","aria-hidden":!1,children:[r.jsx("div",{className:"replay-sandbox__shield","aria-label":"Chế độ xem lại — tương tác bị khóa"}),e.showPointerTrace&&r.jsx("svg",{className:"replay-sandbox__trail",viewBox:"0 0 100 100",preserveAspectRatio:"none","aria-hidden":!0,children:r.jsx(Dh,{points:e.trailPoints})}),r.jsxs("div",{className:`replay-sandbox__ghost ${e.ripple?"replay-sandbox__ghost--ripple":""}${e.isOverlayEditing?" replay-sandbox__ghost--drawing":""}`,style:{left:`${e.ghostX}%`,top:`${e.ghostY}%`,transition:t?"left 0.08s linear, top 0.08s linear":"left 0.45s ease, top 0.45s ease","--replay-pen-color":n},children:[r.jsx("span",{className:"replay-sandbox__ghost-core",style:{width:a,height:a,background:n,boxShadow:`0 0 0 4px ${n}40, 0 0 18px ${n}a8`}}),e.ripple&&r.jsx("span",{className:"replay-sandbox__ghost-ring",style:{borderColor:`${n}bf`}})]}),e.event&&r.jsxs("div",{className:"replay-sandbox__event-chip",children:[r.jsx("strong",{children:e.event.title}),r.jsx("span",{children:e.event.detail})]}),r.jsx("style",{children:` - .replay-sandbox { - position: absolute; - inset: 0; - z-index: 40; - pointer-events: none; - } - .replay-sandbox__shield { - position: absolute; - inset: 0; - pointer-events: auto; - background: rgba(15, 23, 42, 0.04); - cursor: not-allowed; - } - .replay-sandbox__trail { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - z-index: 1; - overflow: visible; - pointer-events: none; - } - .replay-sandbox__ghost { - position: absolute; - transform: translate(-50%, -50%); - z-index: 2; - } - .replay-sandbox__ghost--drawing .replay-sandbox__ghost-core { - animation: replay-pen-pulse 0.6s ease-in-out infinite; - } - @keyframes replay-pen-pulse { - 0%, 100% { transform: scale(1); } - 50% { transform: scale(1.15); } - } - .replay-sandbox__ghost-core { - display: block; - width: 14px; - height: 14px; - border-radius: 50%; - background: #ff007f; - box-shadow: 0 0 0 4px rgba(255, 0, 127, 0.25), 0 0 18px rgba(255, 0, 127, 0.65); - } - .replay-sandbox__ghost-ring { - position: absolute; - inset: -10px; - border-radius: 50%; - border: 2px solid rgba(255, 0, 127, 0.75); - animation: replay-ghost-ring 0.65s ease-out forwards; - } - @keyframes replay-ghost-ring { - from { - transform: scale(0.6); - opacity: 0.95; - } - to { - transform: scale(2.2); - opacity: 0; - } - } - .replay-sandbox__event-chip { - position: absolute; - left: 12px; - bottom: 12px; - max-width: min(360px, calc(100% - 24px)); - padding: 10px 12px; - border-radius: 10px; - background: rgba(30, 41, 59, 0.92); - color: #f8fafc; - font-size: 12px; - line-height: 1.4; - box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25); - display: flex; - flex-direction: column; - gap: 4px; - pointer-events: none; - z-index: 3; - } - .replay-sandbox__event-chip strong { - color: #76c8b1; - font-size: 12px; - } - .replay-sandbox__event-chip span { - color: #e2e8f0; - } - `})]})}const zh=.002;function Fh(e,t){const{isRecording:n,recordingMode:a,recordPointerSample:o,recordUixMouseMove:i}=Yt(),l=s.useRef(null),c=s.useRef(t);c.current=t,s.useEffect(()=>{if(!n){l.current=null;return}const d=e.current;if(!d)return;const u=h=>{const p=d.getBoundingClientRect();if(p.width<=0||p.height<=0)return;const f=Math.max(0,Math.min(1,(h.clientX-p.left)/p.width)),g=Math.max(0,Math.min(1,(h.clientY-p.top)/p.height)),N=Date.now(),y=l.current;if(a==="multi_frame"){if(y){const I=Math.hypot(f-y.x,g-y.y);if(N-y.td.removeEventListener("pointermove",u)},[e,n,a,o,i])}const Yn={active:!1,ghostX:50,ghostY:50,ripple:!1,event:null,elapsedSeconds:0,showPointerTrace:!1,trailPoints:[]};function Vh(){const{patientId:e}=Ni(),t=Mi(),{patients:n}=ji(),a=s.useMemo(()=>n.find(o=>o.id===e)??n[0],[e,n]);return a?r.jsx(il,{patientId:a.id,patientName:a.name,patientMrn:a.mrn,children:r.jsx($h,{patient:a,onBack:()=>t("/")})}):r.jsx(Ci,{to:"/",replace:!0})}function $h({patient:e,onBack:t}){var E;const{status:n,updateActiveSegmentViewport:a,isSingleFrameNavLocked:o}=Yt(),i=n==="recording"||n==="paused",[l,c]=s.useState(!0),[d,u]=s.useState(0),h=s.useRef(null),[p,f]=s.useState(null),[g,N]=s.useState(null),[y,k]=s.useState(null),[A,I]=s.useState(!1),[w,L]=s.useState(!1),[z,X]=s.useState(jn),[R,ie]=s.useState(0),[U,v]=s.useState("diagnosis"),[B,re]=s.useState(Yn),[V,ne]=s.useState(null),F=g,_=y,O=A&&!y,S=A&&!g,P=(_==null?void 0:_.level)??null,j=s.useRef(null),ee=s.useRef(null),K=((E=s.useMemo(()=>po(e.id),[e.id])[d])==null?void 0:E.id)??"";s.useEffect(()=>{if(o){h.current===null&&(h.current=d);return}h.current=null},[d,o]);const Ee=s.useCallback($=>{o&&h.current!=null&&$!==h.current||(ne(null),u($))},[o]);s.useEffect(()=>{!o||h.current===null||d!==h.current&&u(h.current)},[d,o]),Fh(ee,K);const pe=s.useCallback(($,se,ke)=>{o||u(se),ne(ke)},[o]),he=$=>{c($.showMask)},Ne=async()=>{const $=await $r(e.mrn);X($??{...jn}),L(!0)},ge=async()=>{await ih(e.mrn),L(!1),ie($=>$+1)},ae=$=>{i||(re($),$.active&&U!=="review"&&v("review"))};return s.useEffect(()=>{i&&(re(Yn),ne(null))},[i]),s.useEffect(()=>{n==="ended"&&re(Yn)},[n]),r.jsxs(Ai,{topBar:{title:e.name,subtitle:`${e.mrn} · ${e.joint}`,showBack:!0,onBack:t,actions:r.jsxs(r.Fragment,{children:[r.jsx("button",{type:"button",className:"btn btn-secondary workspace-review-btn",onClick:()=>v("review"),children:"Xem lại phiên chẩn đoán"}),r.jsx("button",{type:"button",className:"btn btn-primary",onClick:()=>void Ne(),children:"Ký & Hoàn tất"})]})},children:[r.jsx($i,{zoneA:r.jsxs("div",{ref:ee,className:"workspace-canvas-stack",children:[r.jsx(ic,{showMask:l,onToggleMask:()=>{c($=>{const se=!$;return i&&a({showMask:se}),se})},frameIndex:d,onFrameIndexChange:Ee,onReplayViewport:({showMask:$})=>c($),segmentOverlayReplay:V,onAngleClassificationChange:f,onInflammationClassificationChange:N,onSynovitisSeverityChange:k,onSegmentationLoadingChange:I,patientMrn:e.mrn,patientId:e.id,useCelery:!1,onRegisterSnapshotCapture:$=>{j.current=$}}),r.jsx(Oh,{viewport:{...B,active:B.active&&U==="review"&&!i}})]}),zoneB:r.jsx(Rh,{activeLayer:U,onLayerChange:v,reviewLayer:r.jsx(Lh,{patientId:e.id,patientName:e.name,patientMrn:e.mrn,onViewportChange:ae,onFrameRestore:pe,onReplaySegmentViewport:he}),diagnosisLayer:r.jsx(lh,{severity:_,severityLoading:O,inflammation:F,inflammationLoading:S,angleClassification:p,patientName:e.name,patientMrn:e.mrn,patientJoint:e.joint,draftRevision:R,chatGrade:P,onCaptureDiagnosisSnapshot:()=>{var $;return(($=j.current)==null?void 0:$.call(j))??Promise.resolve(null)}})})}),r.jsx(dc,{open:w,draft:z,doctorName:Si,jointLabel:e.joint.toLowerCase(),onDraftChange:X,onConfirm:()=>void ge(),onCancel:()=>L(!1)}),r.jsx("style",{children:` - .workspace-canvas-stack { - position: relative; - flex: 1; - min-height: 0; - display: flex; - flex-direction: column; - } - .workspace-review-btn { - white-space: nowrap; - font-size: 13px; - } - `})]})}export{Vh as default}; diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/PatientWorklistPage-D1i7UsXA.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/PatientWorklistPage-D1i7UsXA.js deleted file mode 100644 index 0de67ef..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/PatientWorklistPage-D1i7UsXA.js +++ /dev/null @@ -1,522 +0,0 @@ -import{u as U,J as P,j as e,P as te,A as ie,i as ne}from"./index-BCa3bqPy.js";import{b as l,u as re}from"./vendor-react-BWW3uBCb.js";import{l as le,A as oe}from"./mlInferenceCacheStore-BwGclW_P.js";const se=` - .patient-profile-modal { - position: fixed; - inset: 0; - z-index: 1300; - display: flex; - align-items: center; - justify-content: center; - padding: 24px; - } - .patient-profile-modal__backdrop { - position: absolute; - inset: 0; - border: none; - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); - cursor: pointer; - } - .patient-profile-modal__sheet { - position: relative; - width: min(640px, 100%); - max-height: min(90vh, 880px); - overflow: hidden; - display: flex; - flex-direction: column; - background: #ffffff; - border-radius: 16px; - box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.08); - } - .patient-profile-modal__header { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 16px; - padding: 32px 32px 0; - } - .patient-profile-modal__title { - font-size: 20px; - font-weight: 600; - color: var(--color-on-surface); - line-height: 1.3; - } - .patient-profile-modal__close { - flex-shrink: 0; - width: 36px; - height: 36px; - } - .patient-profile-modal__body { - padding: 24px 32px; - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 20px; - } - .patient-profile-modal__grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; - } - .patient-profile-modal__field { - display: flex; - flex-direction: column; - gap: 6px; - min-width: 0; - } - .patient-profile-modal__field--full { - grid-column: 1 / -1; - } - .patient-profile-modal__label { - font-size: 13px; - font-weight: 600; - color: var(--color-on-surface-variant); - } - .patient-profile-modal__field input, - .patient-profile-modal__field select { - width: 100%; - padding: 10px 12px; - border-radius: 8px; - border: 1px solid var(--color-outline-variant); - background: #fff; - color: var(--color-on-surface); - } - .patient-profile-modal__field input:focus, - .patient-profile-modal__field select:focus { - outline: none; - border-color: var(--color-primary); - box-shadow: 0 0 0 3px rgba(5, 107, 88, 0.15); - } - .patient-profile-modal__status-pills { - display: flex; - flex-wrap: wrap; - gap: 8px; - } - .patient-profile-modal__status-pill { - cursor: pointer; - border: 2px solid transparent; - opacity: 0.72; - transition: opacity 0.15s, border-color 0.15s, transform 0.15s; - } - .patient-profile-modal__status-pill:hover:not(:disabled) { - opacity: 1; - } - .patient-profile-modal__status-pill--active { - opacity: 1; - border-color: currentColor; - transform: translateY(-1px); - } - .patient-profile-modal__dropzone { - border: 2px dashed #cbd5e1; - border-radius: 12px; - background: #f8fafc; - padding: 28px 20px; - text-align: center; - color: #64748b; - transition: border-color 0.15s, background 0.15s; - } - .patient-profile-modal__dropzone--active { - border-color: var(--color-primary); - background: rgba(5, 107, 88, 0.06); - color: var(--color-secondary); - } - .patient-profile-modal__dropzone-primary { - margin: 12px 0 4px; - font-weight: 600; - color: var(--color-on-surface); - } - .patient-profile-modal__dropzone-secondary { - margin: 0; - font-size: 14px; - } - .patient-profile-modal__browse-link { - border: none; - background: none; - padding: 0; - color: var(--color-primary); - font-weight: 600; - text-decoration: underline; - cursor: pointer; - } - .patient-profile-modal__browse-link:disabled { - opacity: 0.5; - cursor: not-allowed; - } - .patient-profile-modal__dropzone-caption { - margin: 10px 0 0; - font-size: 12px; - color: var(--color-on-surface-variant); - max-width: 420px; - margin-inline: auto; - line-height: 1.45; - } - .patient-profile-modal__file-error { - margin: -8px 0 0; - font-size: 13px; - color: var(--color-error); - } - .patient-profile-modal__inventory-title { - margin: 0 0 10px; - font-size: 14px; - font-weight: 700; - color: var(--color-on-surface); - } - .patient-profile-modal__inventory-loading, - .patient-profile-modal__inventory-empty { - margin: 0; - font-size: 13px; - color: var(--color-on-surface-variant); - font-style: italic; - } - .patient-profile-modal__file-list { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - gap: 10px; - } - .patient-profile-modal__file-item { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 10px 12px; - border-radius: 10px; - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.8); - transition: opacity 0.15s; - } - .patient-profile-modal__file-item--deleted { - opacity: 0.4; - } - .patient-profile-modal__file-item--deleted .patient-profile-modal__file-name, - .patient-profile-modal__file-item--deleted .patient-profile-modal__file-size { - text-decoration: line-through; - } - .patient-profile-modal__file-item--new { - border-color: rgba(5, 107, 88, 0.35); - background: rgba(118, 200, 177, 0.12); - } - .patient-profile-modal__file-icon { - color: var(--color-secondary); - margin-top: 2px; - } - .patient-profile-modal__file-meta { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 4px; - } - .patient-profile-modal__file-name { - font-size: 14px; - font-weight: 600; - word-break: break-all; - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; - } - .patient-profile-modal__file-size { - font-size: 12px; - color: var(--color-on-surface-variant); - } - .patient-profile-modal__new-badge { - display: inline-flex; - padding: 2px 8px; - border-radius: var(--radius-full); - font-size: 11px; - font-weight: 700; - background: var(--color-primary-container); - color: var(--color-on-primary-container); - text-decoration: none; - } - .patient-profile-modal__delete-badge { - color: var(--color-error); - font-weight: 600; - text-decoration: none; - } - .patient-profile-modal__progress { - height: 4px; - border-radius: 999px; - background: var(--color-surface-container-high); - overflow: hidden; - margin-top: 4px; - } - .patient-profile-modal__progress-bar { - height: 100%; - border-radius: inherit; - background: linear-gradient(90deg, var(--color-primary), var(--color-primary-container)); - transition: width 0.12s ease-out; - } - .patient-profile-modal__file-remove, - .patient-profile-modal__trash-btn { - width: 32px; - height: 32px; - flex-shrink: 0; - } - .patient-profile-modal__trash-btn { - color: var(--color-error); - border-color: rgba(186, 26, 26, 0.25); - } - .patient-profile-modal__trash-btn:hover:not(:disabled) { - background: var(--color-error-container); - border-color: var(--color-error); - } - .patient-profile-modal__undo-link { - border: none; - background: none; - padding: 4px 8px; - color: var(--color-primary); - font-size: 13px; - font-weight: 600; - cursor: pointer; - flex-shrink: 0; - } - .patient-profile-modal__footer { - display: flex; - justify-content: flex-end; - gap: 12px; - padding: 0 32px 32px; - border-top: 1px solid var(--color-outline-variant); - padding-top: 20px; - margin-top: auto; - } - .patient-profile-modal__spinner { - width: 16px; - height: 16px; - border: 2px solid rgba(255, 255, 255, 0.35); - border-top-color: #fff; - border-radius: 50%; - animation: patient-profile-modal-spin 0.7s linear infinite; - } - @keyframes patient-profile-modal-spin { - to { transform: rotate(360deg); } - } - @media (max-width: 640px) { - .patient-profile-modal__grid { - grid-template-columns: 1fr; - } - .patient-profile-modal__header, - .patient-profile-modal__body, - .patient-profile-modal__footer { - padding-left: 20px; - padding-right: 20px; - } - } -`;function T(t){return{name:t.name,mrn:t.mrn,joint:t.joint,status:t.status}}function de(){return e.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[e.jsx("path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}),e.jsx("circle",{cx:"9",cy:"7",r:"4"}),e.jsx("path",{d:"M19 8v6M22 11h-6"})]})}function pe(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:e.jsx("path",{d:"M20 6 9 17l-5-5"})})}function A(){return e.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:e.jsx("path",{d:"M18 6 6 18M6 6l12 12"})})}function ce(){return e.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[e.jsx("path",{d:"M3 6h18M8 6V4h8v2M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"}),e.jsx("path",{d:"M10 11v6M14 11v6"})]})}function me(){return e.jsxs("svg",{width:"40",height:"40",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5","aria-hidden":!0,children:[e.jsx("path",{d:"M12 16V4m0 0 4 4m-4-4-4 4"}),e.jsx("path",{d:"M4 17v1a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3v-1"})]})}function E(){return e.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[e.jsx("path",{d:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"}),e.jsx("path",{d:"M14 2v6h6"})]})}function L(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function fe(t,o){return new Promise(c=>{let i=0;const n=()=>{if(i+=12+Math.random()*18,i>=100){o(t,100),c();return}o(t,Math.round(i)),window.setTimeout(n,120+Math.random()*80)};n()})}function K(t){const{open:o,onClose:c,mode:i}=t,n=i==="edit"?t.patient:null,{getPatientScanAssets:u}=U(),f=l.useId(),F=l.useRef(null),[d,g]=l.useState(i==="edit"&&n?T(n):{name:"",mrn:"",joint:P[0],status:"processing"}),[j,N]=l.useState(d),[b,k]=l.useState([]),[h,y]=l.useState([]),[D,v]=l.useState(!1),[p,S]=l.useState(!1),[s,x]=l.useState(null),[M,z]=l.useState(!1),$=l.useCallback(()=>{const a={name:"",mrn:"",joint:P[0],status:"processing"};g(i==="edit"&&n?T(n):a),N(i==="edit"&&n?T(n):a),k([]),y([]),v(!1),S(!1),x(null),z(!1)},[i,n]);l.useEffect(()=>{if(!o){$();return}if(i==="edit"&&n){const a=T(n);g(a),N(a),z(!0),u(n.id).then(m=>{const r=m.map(w=>({meta:w,markedForDeletion:!1}));k(r)}).finally(()=>z(!1))}},[o,i,n,u,$]),l.useEffect(()=>{if(!o)return;const a=m=>{m.key==="Escape"&&!p&&c()};return document.addEventListener("keydown",a),()=>document.removeEventListener("keydown",a)},[o,c,p]);const O=d.name.trim().length>0&&d.mrn.trim().length>0&&d.joint.length>0&&!!d.status,R=d.name.trim()!==j.name.trim()||d.mrn.trim()!==j.mrn.trim()||d.joint!==j.joint||d.status!==j.status,V=b.some(a=>a.markedForDeletion)||h.length>0,q=b.filter(a=>!a.markedForDeletion).length+h.length,X=i==="edit"?R||V:!0,J=!V||q>0,Y=O&&h.length>0&&!p,W=i==="add"?Y:O&&X&&J&&!p&&!M,H=a=>{const m=Array.from(a),r=m.filter(ne);if(m.length-r.length>0?x("Chỉ chấp nhận ảnh PNG, JPEG hoặc DICOM (.dcm)."):x(null),r.length===0)return;const I=new Set([...b.filter(_=>!_.markedForDeletion).map(_=>_.meta.fileName),...h.map(_=>_.file.name)]),C=r.filter(_=>!I.has(_.name)).map(_=>({id:crypto.randomUUID(),file:_,progress:0,status:"queued"}));C.length>0&&y(_=>[..._,...C])},G=a=>{p||y(m=>m.filter(r=>r.id!==a))},Q=a=>{p||k(m=>m.map(r=>r.meta.id===a?{...r,markedForDeletion:!r.markedForDeletion}:r))},Z=async()=>{if(!W)return;S(!0);const a=i==="add"?h:h.filter(r=>r.status!=="done");y(r=>r.map(w=>({...w,status:"uploading",progress:0})));const m=(r,w)=>{y(I=>I.map(C=>C.id===r?{...C,progress:w,status:w>=100?"done":"uploading"}:C))};try{await Promise.all(a.map(r=>fe(r.id,m))),i==="add"?await t.onSubmit({name:d.name,mrn:d.mrn,joint:d.joint,status:d.status,scanFiles:h.map(r=>r.file)}):n&&await t.onSubmit({patientId:n.id,name:d.name,mrn:d.mrn,joint:d.joint,status:d.status,removedAssetIds:b.filter(r=>r.markedForDeletion).map(r=>r.meta.id),newScanFiles:h.map(r=>r.file)}),c()}finally{S(!1)}},ee=l.useMemo(()=>i==="edit"&&n?`Chỉnh sửa hồ sơ: ${n.name}`:"Thêm hồ sơ bệnh nhân mới",[i,n]);if(!o)return null;const ae=i==="edit";return e.jsxs("div",{className:"patient-profile-modal",role:"presentation",children:[e.jsx("button",{type:"button",className:"patient-profile-modal__backdrop","aria-label":i==="edit"?"Đóng biểu mẫu chỉnh sửa":"Đóng biểu mẫu thêm bệnh nhân",onClick:()=>!p&&c(),disabled:p}),e.jsxs("div",{className:"patient-profile-modal__sheet",role:"dialog","aria-modal":"true","aria-labelledby":f,children:[e.jsxs("header",{className:"patient-profile-modal__header",children:[e.jsx("h2",{id:f,className:"patient-profile-modal__title",children:ee}),e.jsx("button",{type:"button",className:"patient-profile-modal__close icon-btn","aria-label":"Đóng",onClick:c,disabled:p,children:e.jsx(A,{})})]}),e.jsxs("div",{className:"patient-profile-modal__body",children:[e.jsxs("div",{className:"patient-profile-modal__grid",children:[e.jsxs("label",{className:"patient-profile-modal__field patient-profile-modal__field--full",children:[e.jsx("span",{className:"patient-profile-modal__label",children:"Họ và tên bệnh nhân"}),e.jsx("input",{type:"text",value:d.name,onChange:a=>g(m=>({...m,name:a.target.value})),placeholder:"Ví dụ: Nguyễn Văn An",disabled:p})]}),e.jsxs("label",{className:"patient-profile-modal__field",children:[e.jsx("span",{className:"patient-profile-modal__label",children:"Mã bệnh nhân (MRN)"}),e.jsx("input",{type:"text",className:"tnum",value:d.mrn,onChange:a=>g(m=>({...m,mrn:a.target.value})),placeholder:"BN-2024-XXXX",disabled:p})]}),e.jsxs("label",{className:"patient-profile-modal__field",children:[e.jsx("span",{className:"patient-profile-modal__label",children:"Vị trí khớp chụp"}),e.jsx("select",{value:d.joint,onChange:a=>g(m=>({...m,joint:a.target.value})),disabled:p,children:P.map(a=>e.jsx("option",{value:a,children:a},a))})]}),e.jsxs("div",{className:"patient-profile-modal__field patient-profile-modal__field--full",children:[e.jsx("span",{className:"patient-profile-modal__label",children:"Trạng thái tiếp nhận"}),e.jsx("div",{className:"patient-profile-modal__status-pills",role:"radiogroup","aria-label":"Trạng thái tiếp nhận",children:te.map(a=>e.jsx("button",{type:"button",role:"radio","aria-checked":d.status===a.value,className:`patient-profile-modal__status-pill chip ${a.chipClass} ${d.status===a.value?"patient-profile-modal__status-pill--active":""}`,onClick:()=>g(m=>({...m,status:a.value})),disabled:p,children:a.label},a.value))})]})]}),e.jsxs("div",{className:`patient-profile-modal__dropzone ${D?"patient-profile-modal__dropzone--active":""}`,onDragEnter:a=>{a.preventDefault(),v(!0)},onDragOver:a=>{a.preventDefault(),v(!0)},onDragLeave:a=>{a.preventDefault(),!a.currentTarget.contains(a.relatedTarget)&&v(!1)},onDrop:a=>{a.preventDefault(),v(!1),!p&&H(a.dataTransfer.files)},children:[e.jsx(me,{}),e.jsx("p",{className:"patient-profile-modal__dropzone-primary",children:i==="edit"?"Kéo và thả tệp DICOM/ảnh mới tại đây để tải lên":"Kéo và thả ảnh siêu âm hoặc DICOM tại đây"}),e.jsxs("p",{className:"patient-profile-modal__dropzone-secondary",children:["hoặc"," ",e.jsx("button",{type:"button",className:"patient-profile-modal__browse-link",onClick:()=>{var a;return(a=F.current)==null?void 0:a.click()},disabled:p,children:"Chọn tệp từ máy tính"})]}),e.jsx("p",{className:"patient-profile-modal__dropzone-caption",children:"Hỗ trợ PNG, JPEG và DICOM (.dcm). Tự động trích xuất mốc thời gian quét."}),e.jsx("input",{ref:F,type:"file",accept:ie,multiple:!0,className:"sr-only",onChange:a=>{a.target.files&&H(a.target.files),a.target.value=""}})]}),s&&e.jsx("p",{className:"patient-profile-modal__file-error",children:s}),i==="add"&&h.length>0&&e.jsx("ul",{className:"patient-profile-modal__file-list","aria-label":"Danh sách tệp quét",children:h.map(a=>e.jsxs("li",{className:"patient-profile-modal__file-item",children:[e.jsx("span",{className:"patient-profile-modal__file-icon","aria-hidden":!0,children:e.jsx(E,{})}),e.jsxs("div",{className:"patient-profile-modal__file-meta",children:[e.jsx("span",{className:"patient-profile-modal__file-name",children:a.file.name}),e.jsx("span",{className:"patient-profile-modal__file-size tnum",children:L(a.file.size)}),(a.status==="uploading"||a.status==="done")&&e.jsx("div",{className:"patient-profile-modal__progress","aria-hidden":!0,children:e.jsx("div",{className:"patient-profile-modal__progress-bar",style:{width:`${a.progress}%`}})})]}),e.jsx("button",{type:"button",className:"patient-profile-modal__file-remove icon-btn","aria-label":`Xóa ${a.file.name}`,onClick:()=>G(a.id),disabled:p,children:e.jsx(A,{})})]},a.id))}),ae&&e.jsxs("section",{className:"patient-profile-modal__inventory",children:[e.jsx("h3",{className:"patient-profile-modal__inventory-title",children:"Danh sách tệp dữ liệu hiện tại"}),M?e.jsx("p",{className:"patient-profile-modal__inventory-loading",children:"Đang tải danh sách tệp…"}):b.length===0?e.jsx("p",{className:"patient-profile-modal__inventory-empty",children:"Chưa có tệp dữ liệu được lưu."}):e.jsx("ul",{className:"patient-profile-modal__file-list",children:b.map(a=>e.jsxs("li",{className:`patient-profile-modal__file-item ${a.markedForDeletion?"patient-profile-modal__file-item--deleted":""}`,children:[e.jsx("span",{className:"patient-profile-modal__file-icon","aria-hidden":!0,children:e.jsx(E,{})}),e.jsxs("div",{className:"patient-profile-modal__file-meta",children:[e.jsx("span",{className:"patient-profile-modal__file-name",children:a.meta.fileName}),e.jsxs("span",{className:"patient-profile-modal__file-size tnum",children:[L(a.meta.size),a.markedForDeletion&&e.jsx("span",{className:"patient-profile-modal__delete-badge",children:" · Sẽ xóa"})]})]}),e.jsx("button",{type:"button",className:a.markedForDeletion?"patient-profile-modal__undo-link":"patient-profile-modal__trash-btn icon-btn","aria-label":a.markedForDeletion?`Hoàn tác xóa ${a.meta.fileName}`:`Đánh dấu xóa ${a.meta.fileName}`,onClick:()=>Q(a.meta.id),disabled:p,children:a.markedForDeletion?"Hoàn tác":e.jsx(ce,{})})]},a.meta.id))})]}),i==="edit"&&h.length>0&&e.jsxs("section",{className:"patient-profile-modal__inventory",children:[e.jsx("h3",{className:"patient-profile-modal__inventory-title",children:"Tệp tải lên mới"}),e.jsx("ul",{className:"patient-profile-modal__file-list",children:h.map(a=>e.jsxs("li",{className:"patient-profile-modal__file-item patient-profile-modal__file-item--new",children:[e.jsx("span",{className:"patient-profile-modal__file-icon","aria-hidden":!0,children:e.jsx(E,{})}),e.jsxs("div",{className:"patient-profile-modal__file-meta",children:[e.jsxs("span",{className:"patient-profile-modal__file-name",children:[a.file.name,e.jsx("span",{className:"patient-profile-modal__new-badge",children:"Tải lên mới"})]}),e.jsx("span",{className:"patient-profile-modal__file-size tnum",children:L(a.file.size)}),a.status==="uploading"&&e.jsx("div",{className:"patient-profile-modal__progress","aria-hidden":!0,children:e.jsx("div",{className:"patient-profile-modal__progress-bar",style:{width:`${a.progress}%`}})})]}),e.jsx("button",{type:"button",className:"patient-profile-modal__file-remove icon-btn","aria-label":`Xóa ${a.file.name}`,onClick:()=>G(a.id),disabled:p,children:e.jsx(A,{})})]},a.id))})]})]}),e.jsxs("footer",{className:"patient-profile-modal__footer",children:[e.jsx("button",{type:"button",className:"btn btn-ghost",onClick:c,disabled:p,children:"Hủy bỏ"}),e.jsx("button",{type:"button",className:"btn btn-primary",disabled:!W,onClick:()=>void Z(),children:p?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"patient-profile-modal__spinner","aria-hidden":!0}),i==="edit"?"Đang lưu…":"Đang tải lên…"]}):i==="edit"?e.jsxs(e.Fragment,{children:[e.jsx(pe,{}),"Lưu thay đổi"]}):e.jsxs(e.Fragment,{children:[e.jsx(de,{}),"Tạo hồ sơ & tải lên"]})})]})]}),e.jsx("style",{children:se})]})}const he=[{value:"all",label:"Tất cả"},{value:"urgent",label:"Khẩn cấp"},{value:"processing",label:"Đang xử lý"},{value:"followup",label:"Theo dõi"}];function ue({search:t,onSearchChange:o,filter:c,onFilterChange:i,summary:n,children:u}){return e.jsxs("div",{className:"patient-list-shell",children:[e.jsx("section",{className:"patient-list-shell__hero",children:e.jsxs("div",{children:[e.jsx("h2",{children:"Danh sách công việc"}),e.jsx("p",{children:"Quản lý hàng đợi lâm sàng và khởi tạo phiên quét siêu âm"})]})}),e.jsx("section",{className:"patient-list-shell__summary",children:n}),e.jsxs("section",{className:"patient-list-shell__toolbar glass",children:[e.jsx("input",{type:"search",className:"patient-list-shell__search",placeholder:"Tìm bệnh nhân, mã BN...",value:t,onChange:f=>o(f.target.value),"aria-label":"Tìm kiếm bệnh nhân"}),e.jsx("div",{className:"patient-list-shell__filters",role:"tablist","aria-label":"Bộ lọc trạng thái",children:he.map(f=>e.jsx("button",{type:"button",role:"tab","aria-selected":c===f.value,className:`patient-list-shell__filter ${c===f.value?"patient-list-shell__filter--active":""}`,onClick:()=>i(f.value),children:f.label},f.value))})]}),e.jsx("section",{className:"patient-list-shell__grid",children:u}),e.jsx("style",{children:` - .patient-list-shell { - padding: var(--space-lg) var(--space-md); - max-width: 1280px; - margin: 0 auto; - width: 100%; - } - .patient-list-shell__hero h2 { - font-size: 28px; - font-weight: 700; - color: var(--color-secondary); - } - .patient-list-shell__hero p { - margin: 6px 0 0; - color: var(--color-on-surface-variant); - } - .patient-list-shell__summary { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: var(--space-md); - margin: var(--space-lg) 0; - } - .patient-list-shell__toolbar { - display: flex; - flex-wrap: wrap; - gap: var(--space-md); - align-items: center; - padding: var(--space-md); - border-radius: var(--radius-lg); - margin-bottom: var(--space-lg); - } - .patient-list-shell__search { - flex: 1; - min-width: 200px; - padding: 10px 14px; - border-radius: var(--radius-md); - border: 1px solid var(--color-outline-variant); - background: rgba(255, 255, 255, 0.6); - } - .patient-list-shell__search:focus { - outline: none; - border-color: var(--color-primary); - box-shadow: 0 0 0 3px rgba(5, 107, 88, 0.15); - } - .patient-list-shell__filters { - display: flex; - gap: 8px; - flex-wrap: wrap; - } - .patient-list-shell__filter { - padding: 8px 14px; - border-radius: var(--radius-full); - border: 1px solid var(--color-outline-variant); - background: transparent; - cursor: pointer; - font-size: 13px; - font-weight: 600; - color: var(--color-on-surface-variant); - } - .patient-list-shell__filter--active { - background: var(--color-primary); - color: var(--color-on-primary); - border-color: var(--color-primary); - } - .patient-list-shell__grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: var(--space-md); - } - @media (max-width: 768px) { - .patient-list-shell__summary { - grid-template-columns: 1fr; - } - } - `})]})}const xe={urgent:"chip-urgent",processing:"chip-processing",followup:"chip-followup",ready:"chip-success"};function _e(){return e.jsxs("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2","aria-hidden":!0,children:[e.jsx("path",{d:"M12 20h9"}),e.jsx("path",{d:"M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"})]})}function ge({patient:t,cachedAiGrade:o=null,aiGradeLoading:c=!1,onOpen:i,onEdit:n}){return e.jsxs("article",{className:"patient-card glass",children:[e.jsxs("div",{className:"patient-card__header",children:[e.jsx("div",{className:"patient-card__avatar","aria-hidden":!0,children:t.name.charAt(0)}),e.jsxs("div",{className:"patient-card__info",children:[e.jsx("h3",{children:t.name}),e.jsx("p",{className:"tnum",children:t.mrn})]}),e.jsx("span",{className:`chip ${xe[t.status]}`,children:t.statusLabel})]}),e.jsxs("div",{className:"patient-card__body",children:[e.jsxs("div",{className:"patient-card__row",children:[e.jsx("span",{children:"Vị trí"}),e.jsx("strong",{children:t.joint})]}),e.jsxs("div",{className:"patient-card__row",children:[e.jsx("span",{children:"Quét lúc"}),e.jsx("strong",{className:"tnum",children:t.lastScan})]}),e.jsxs("div",{className:"patient-card__row",children:[e.jsx("span",{children:"Đề xuất AI"}),c?e.jsx("strong",{className:"patient-card__ai-muted",children:"Đang tải…"}):o!=null?e.jsxs("strong",{className:"tnum",children:["Độ ",o]}):e.jsx("strong",{className:"patient-card__ai-muted",children:"Chưa phân tích"})]})]}),e.jsxs("div",{className:"patient-card__actions",children:[e.jsx("button",{type:"button",className:"btn btn-primary patient-card__cta",onClick:()=>i(t.id),children:"Mở phiên chẩn đoán"}),e.jsx("button",{type:"button",className:"patient-card__edit-btn","aria-label":`Chỉnh sửa hồ sơ ${t.name}`,onClick:()=>n(t),children:e.jsx(_e,{})})]}),e.jsx("style",{children:` - .patient-card { - border-radius: var(--radius-lg); - padding: var(--space-md); - display: flex; - flex-direction: column; - gap: var(--space-md); - } - .patient-card__header { - display: flex; - align-items: flex-start; - gap: 12px; - } - .patient-card__avatar { - width: 44px; - height: 44px; - border-radius: var(--radius-full); - background: linear-gradient(135deg, var(--color-primary-container), var(--color-secondary-container)); - display: flex; - align-items: center; - justify-content: center; - font-family: var(--font-headline); - font-weight: 700; - color: var(--color-on-primary-container); - flex-shrink: 0; - } - .patient-card__info { - flex: 1; - min-width: 0; - } - .patient-card__info h3 { - font-size: 16px; - font-weight: 600; - } - .patient-card__info p { - margin: 2px 0 0; - font-size: 13px; - color: var(--color-on-surface-variant); - } - .patient-card__body { - display: flex; - flex-direction: column; - gap: 8px; - } - .patient-card__row { - display: flex; - justify-content: space-between; - font-size: 14px; - } - .patient-card__row span { - color: var(--color-on-surface-variant); - } - .patient-card__ai-muted { - font-weight: 500; - font-size: 13px; - color: var(--color-on-surface-variant); - font-style: italic; - } - .patient-card__actions { - display: flex; - gap: 8px; - align-items: stretch; - } - .patient-card__cta { - flex: 1 1 78%; - min-width: 0; - } - .patient-card__edit-btn { - flex: 0 0 44px; - width: 44px; - height: auto; - min-height: 44px; - border-radius: 8px; - border: 1px solid #e2e8f0; - background: #f1f5f9; - color: var(--color-secondary); - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: background 0.15s ease, border-color 0.15s ease; - } - .patient-card__edit-btn:hover { - background: #cbd5e1; - border-color: #cbd5e1; - } - .patient-card__edit-btn:focus-visible { - outline: 2px solid var(--color-primary); - outline-offset: 2px; - } - `})]})}const be={urgent:"chip-urgent",processing:"chip-processing",followup:"chip-followup"};function B({label:t,count:o,variant:c,description:i}){return e.jsxs("div",{className:"status-card glass",children:[e.jsx("span",{className:`chip ${be[c]}`,children:t}),e.jsx("p",{className:"status-card__count tnum",children:o}),e.jsx("p",{className:"status-card__desc",children:i}),e.jsx("style",{children:` - .status-card { - border-radius: var(--radius-lg); - padding: var(--space-md); - display: flex; - flex-direction: column; - gap: 6px; - } - .status-card__count { - font-family: var(--font-headline); - font-size: 32px; - font-weight: 700; - color: var(--color-secondary); - margin: 4px 0 0; - line-height: 1; - } - .status-card__desc { - margin: 0; - font-size: 13px; - color: var(--color-on-surface-variant); - } - `})]})}function ve(t){const[o,c]=l.useState({}),[i,n]=l.useState(!0),u=l.useCallback(()=>{if(t.length===0){c({}),n(!1);return}n(!0),le(t).then(c).finally(()=>n(!1))},[t]);return l.useEffect(()=>{u()},[u]),l.useEffect(()=>{const f=()=>{document.visibilityState==="visible"&&u()};return document.addEventListener("visibilitychange",f),()=>document.removeEventListener("visibilitychange",f)},[u]),{gradesByMrn:o,loading:i,refresh:u}}function je(){return e.jsx("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2","aria-hidden":!0,children:e.jsx("path",{d:"M12 5v14M5 12h14"})})}function ke(){const t=re(),{patients:o,addPatient:c,updatePatient:i}=U(),[n,u]=l.useState(""),[f,F]=l.useState("all"),[d,g]=l.useState(!1),[j,N]=l.useState(null),[b,k]=l.useState(null),h=l.useMemo(()=>o.map(s=>s.mrn),[o]),{gradesByMrn:y,loading:D}=ve(h),v=l.useMemo(()=>({urgent:o.filter(s=>s.status==="urgent").length,processing:o.filter(s=>s.status==="processing").length,followup:o.filter(s=>s.status==="followup").length}),[o]),p=l.useMemo(()=>{const s=n.toLowerCase();return o.filter(x=>{const M=!s||x.name.toLowerCase().includes(s)||x.mrn.toLowerCase().includes(s)||x.joint.toLowerCase().includes(s),z=f==="all"||x.status===f;return M&&z})},[n,f,o]),S=s=>{k(s),window.setTimeout(()=>k(null),3200)};return e.jsxs(e.Fragment,{children:[e.jsx(oe,{topBar:{title:"Lumina MSK",subtitle:"Danh sách công việc",actions:e.jsxs("button",{type:"button",className:"btn btn-primary worklist-add-patient-btn",onClick:()=>g(!0),children:[e.jsx(je,{}),"Thêm bệnh nhân"]})},children:e.jsxs(ue,{search:n,onSearchChange:u,filter:f,onFilterChange:F,summary:e.jsxs(e.Fragment,{children:[e.jsx(B,{label:"Khẩn cấp",count:v.urgent,variant:"urgent",description:"Cần xem xét ngay"}),e.jsx(B,{label:"Đang xử lý",count:v.processing,variant:"processing",description:"AI đang phân tích"}),e.jsx(B,{label:"Theo dõi",count:v.followup,variant:"followup",description:"Tái khám / theo dõi"})]}),children:[p.map(s=>e.jsx(ge,{patient:s,cachedAiGrade:y[s.mrn]??null,aiGradeLoading:D,onOpen:x=>t(`/workspace/${x}`),onEdit:N},s.id)),p.length===0&&e.jsx("p",{style:{gridColumn:"1 / -1",textAlign:"center",color:"var(--color-on-surface-variant)"},children:"Không tìm thấy bệnh nhân phù hợp."})]})}),e.jsx(K,{mode:"add",open:d,onClose:()=>g(!1),onSubmit:async s=>{const x=await c(s);t(`/workspace/${x.id}`)}}),j&&e.jsx(K,{mode:"edit",patient:j,open:!!j,onClose:()=>N(null),onSubmit:async s=>{await i(s),N(null),S("Đã lưu thay đổi hồ sơ bệnh nhân.")}}),b&&e.jsx("div",{className:"worklist-toast",role:"status","aria-live":"polite",children:b}),e.jsx("style",{children:` - .worklist-add-patient-btn { - border-radius: 8px; - white-space: nowrap; - } - .worklist-add-patient-btn:hover { - opacity: 0.9; - } - .worklist-add-patient-btn:focus-visible { - outline: 2px solid var(--color-primary); - outline-offset: 2px; - } - .worklist-toast { - position: fixed; - bottom: 24px; - right: 24px; - z-index: 1400; - padding: 12px 18px; - border-radius: 10px; - background: var(--color-secondary); - color: var(--color-on-secondary); - font-size: 14px; - font-weight: 600; - box-shadow: 0 12px 28px rgba(35, 104, 108, 0.25); - animation: worklist-toast-in 0.2s ease-out; - } - @keyframes worklist-toast-in { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } - } - `})]})}export{ke as default}; diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/agent-runtime-CHsDDJds.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/agent-runtime-CHsDDJds.js deleted file mode 100644 index cb9f1f8..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/agent-runtime-CHsDDJds.js +++ /dev/null @@ -1,54 +0,0 @@ -var U=Object.defineProperty;var J=(t,e,n)=>e in t?U(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var v=(t,e,n)=>J(t,typeof e!="symbol"?e+"":e,n);const I="1.0.0",H=[{name:"exa_search",description:"Search the web for clinical or technical evidence via Exa (highlights mode).",parameters:{query:"string (required, max 512 chars, PHI-scrubbed)",type:"auto | fast | instant | deep-lite | deep | deep-reasoning (default auto)",numResults:"number 1-10 (default 10)",includeDomains:"string[] optional authoritative domains",excludeDomains:"string[] optional exclusions",session_id:"string (required)"}},{name:"supabase_query",description:"Read-only Supabase knowledge RPC (local MOH textbook corpus).",parameters:{rpc:"match_semantic_chunks | get_corpus_citation",args:"object validated server-side (query_text, filter_book_ids, match_count)",session_id:"string (required)"}},{name:"escalate_medgemma",description:"Escalate to Tier-3 MedGemma for deep clinical reasoning (requires consent).",parameters:{session_id:"string (required)",task_type:"clinical_deep_reasoning | report_finalization",prompt:"string (PHI-scrubbed, redaction_hash recommended)",include_images:"boolean optional",stream:"boolean default true"}}],me={maxSteps:6,mockTools:!1,bffBaseUrl:typeof import.meta<"u"?"":"http://localhost:8000",requireRetrievalBeforeFinal:!0,enablePlanPhase:!0},G={topK:20,temperature:.3},K={topK:20,temperature:.4};function Q(){return H.map(e=>{const n=Object.entries(e.parameters).map(([o,r])=>` ${o}: ${r}`).join(` -`);return`- ${e.name}: ${e.description} - parameters: -${n}`}).join(` - -`)}function Y(t,e){return t==="plan"?` - -PLAN STEP (strict): -- Output a numbered checklist of steps you will take to answer the user. -- One step per line (e.g. "1. Search …"). -- No , no , no greetings, no answer prose. -`:t==="tool_call"?` - -TOOL STEP (strict): -- Your entire reply MUST be exactly: - {"name":"tool_name","arguments":{...}} -- Do NOT output or any other text before or after the block. -- Do NOT explain, greet, or answer the user in this turn. -`+(e?`- Put reasoning in the thought channel only; the answer channel must start with "". -`:`- Do not output any characters outside the block. -`):` - -FINAL STEP (strict): -- Your entire reply MUST be exactly: - …complete user-facing answer with citations… -- Do NOT output or any text outside the block. -`+(e?`- Put reasoning in the thought channel only; the answer channel must start with "". -`:`- Do not output any characters outside the block. -`)}function z(t){const e=Q();return`${t.baseSystemPrompt} - -You are an agent with tools (catalog v${I}). -Available tools: -${e} - -Rules: -- Each generation step has a strict output mode (plan, tool, or final). -- To call a tool: {"name":"tool_name","arguments":{...}} -- When ready to answer the user: your answer with citations (chunk_id or URL) -- For clinical claims, prefer supabase_query (local MOH corpus) first; use exa_search for external/recency. -- Use escalate_medgemma only when deep reasoning is required and local retrieval is insufficient. -- Never invent tool names or fabricate citations. -- Never combine multiple block types in one turn. -`}function f(t,e,n){return t+Y(e,n)}function V(t,e){return`<|turn>${t==="user"?"user":"model"} -${e}`}function m(t,e,n){const o=[],r=n?`<|think|>${t}`:t;o.push(`<|turn>system -${r}`);for(const a of e)o.push(V(a.role,a.content));return o.push(`<|turn>model -`),o.join(` -`)}const x=/\s*([\s\S]*?)\s*<\/tool_call>/i,W=/\s*([\s\S]*?)\s*<\/final>/i,X=/<\|/;function R(t){const e=t.indexOf("<|channel>thought");if(e===-1)return t;const n="",o=t.slice(e),r=o.indexOf(n);return r===-1?t:o.slice(r+n.length).trim()}function L(t){return t.trim()}function E(t,e){return e==="tool_call"?/\s*[\s\S]*?\s*<\/tool_call>$/i)!==null}function ne(t){return t.match(/^\s*[\s\S]*?\s*<\/final>$/i)!==null}function oe(t,e){const n=e?R(t):L(t),o=n.split(` -`).map(r=>r.trim()).filter(Boolean).map(r=>r.replace(/^\d+[\).\]]\s*/,"").trim()).filter(r=>r.length>0&&!r.startsWith("<"));return o.length===0?{kind:"invalid",raw:n,reason:"plan step produced no checklist lines"}:{kind:"plan",checklist:o,raw:n}}function _(t,e,n){const o=e?R(t):L(t);if(n==="plan")return oe(t,e);const r=E(o,"tool_call"),a=E(o,"final");if(n==="tool_call"){if(a)return{kind:"invalid",raw:o,reason:"tool step must not contain "};if(!r)return o.length>0?{kind:"invalid",raw:o,reason:"tool step requires block only"}:{kind:"invalid",raw:o,reason:"empty tool step output"};if(!te(o))return{kind:"invalid",raw:o,reason:"tool step must be exactly one block"};const s=o.match(x);if(!s)return{kind:"invalid",raw:o,reason:"malformed tool_call tags"};const i=B(s[1]);return"error"in i?{kind:"invalid",raw:o,reason:i.error}:{kind:"tool_call",call:i}}if(r&&a)return{kind:"invalid",raw:o,reason:"final step must not combine and "};if(r&&!a){const s=Z(o);return s&&!o.replace(x,"").trim()?{kind:"tool_call",call:s}:{kind:"invalid",raw:o,reason:"final step received tool_call without "}}if(a){if(!ne(o))return{kind:"invalid",raw:o,reason:"final step must be exactly one block"};const s=ee(o);return s?{kind:"final",text:s}:{kind:"invalid",raw:o,reason:"empty final block"}}return o.includes("")||o.includes("")?{kind:"invalid",raw:o,reason:"malformed tool_call or final tags"}:{kind:"invalid",raw:o,reason:"final step requires ... block"}}function A(t,e){return`${JSON.stringify({tool:t,result:e})}`}function P(t,e){return e==="tool_call"?`Invalid (${t}). TOOL STEP — reply with ONLY one block, no other text: -{"name":"tool_name","arguments":{...}}`:e==="plan"?`Invalid (${t}). PLAN STEP — reply with a numbered checklist only (one step per line). No tool_call, no final, no prose.`:`Invalid (${t}). FINAL STEP — reply with ONLY one block, no other text: -your complete answer with citations`}class re{constructor(e){this.baseUrl=e}async post(e,n,o){const r=`${this.baseUrl.replace(/\/$/,"")}${e}`,a={"Content-Type":"application/json"};o&&(a.Authorization=`Bearer ${o}`);const s=await fetch(r,{method:"POST",headers:a,body:JSON.stringify(n)});if(!s.ok){let i=s.statusText;try{const u=await s.json();u.detail&&(i=u.detail)}catch{}throw new Error(`BFF ${e} failed (${s.status}): ${i}`)}return await s.json()}}function g(){return typeof navigator>"u"?!0:navigator.onLine}function ae(t){const e=t;return{hits:(e.results??[]).map((o,r)=>({id:o.id??`exa-${r}`,url:o.url??"",title:o.title??o.url??"Untitled",highlights:Array.isArray(o.highlights)?o.highlights:[],publishedDate:o.publishedDate,score:o.score})),requestId:e.requestId}}const se="lumina-agent-runtime",ie=1,T="agent_sessions",k="search_cache",p="tool_queue";function F(){return new Promise((t,e)=>{const n=indexedDB.open(se,ie);n.onerror=()=>e(n.error??new Error("IndexedDB open failed")),n.onsuccess=()=>t(n.result),n.onupgradeneeded=()=>{const o=n.result;o.objectStoreNames.contains(T)||o.createObjectStore(T,{keyPath:"sessionId"}),o.objectStoreNames.contains(k)||o.createObjectStore(k,{keyPath:"cacheKey"}),o.objectStoreNames.contains(p)||o.createObjectStore(p,{keyPath:"id",autoIncrement:!0})}})}async function b(t,e,n){const o=await F();return new Promise((r,a)=>{const i=o.transaction(t,e).objectStore(t),u=n(i);u.onsuccess=()=>r(u.result),u.onerror=()=>a(u.error??new Error("IndexedDB request failed"))})}async function le(t){await b(T,"readwrite",e=>e.put(t))}async function q(t){try{const e=await b(k,"readonly",n=>n.get(t));return!e||Date.now()-e.cachedAt>e.ttlMs?null:e}catch{return null}}async function y(t){await b(k,"readwrite",e=>e.put(t))}async function D(t){await b(p,"readwrite",e=>e.add(t))}async function ce(t){const e=await F();return new Promise((n,o)=>{const r=e.transaction(p,"readwrite"),s=r.objectStore(p).openCursor(),i=[];s.onsuccess=()=>{const u=s.result;if(!u)return;const l=u.value;l.sessionId===t&&(i.push(l),u.delete()),u.continue()},r.oncomplete=()=>n(i),r.onerror=()=>o(r.error??new Error("IndexedDB cursor failed"))})}async function $(t){if(typeof crypto<"u"&&crypto.subtle){const e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return Array.from(new Uint8Array(e)).map(n=>n.toString(16).padStart(2,"0")).join("")}return`fallback-${t.length}-${t.slice(0,32)}`}const C=1440*60*1e3,N=10080*60*1e3;function M(t){if(typeof t!="string"||!t.trim())throw new Error("query is required");const e=t.trim();if(e.length>512)throw new Error("query exceeds 512 characters");return e}function O(t){if(typeof t!="string"||!t.trim())throw new Error("session_id is required");return t.trim()}class ue{constructor(e){this.options=e}async run(e){try{switch(e.name){case"exa_search":return await this.runExaSearch(e.arguments);case"supabase_query":return await this.runSupabaseQuery(e.arguments);case"escalate_medgemma":return await this.runEscalateMedgemma(e.arguments);default:return{tool:e.name,ok:!1,error:`Unknown tool: ${e.name}`}}}catch(n){return{tool:e.name,ok:!1,error:n instanceof Error?n.message:String(n)}}}async flushOfflineQueue(e){if(!g())return[];const n=await ce(e),o=[];for(const r of n)o.push(await this.run({name:r.tool,arguments:r.arguments}));return o}async runExaSearch(e){const n={query:M(e.query),type:e.type??"auto",numResults:typeof e.numResults=="number"?e.numResults:10,includeDomains:e.includeDomains,excludeDomains:e.excludeDomains,session_id:O(e.session_id)},o=await $(`exa:${JSON.stringify(n)}`),r=await q(o);if(r)return{tool:"exa_search",ok:!0,data:r.payload};if(this.options.mockTools){const i={hits:[{id:"mock-exa-1",url:"https://example.org/guideline/synovitis",title:"Mock Exa — Synovitis grading overview",highlights:["Power Doppler grade 2 indicates moderate synovial inflammation."]}]};return await y({cacheKey:o,tool:"exa_search",payload:i,cachedAt:Date.now(),ttlMs:C}),{tool:"exa_search",ok:!0,data:i}}if(!g())return await D({sessionId:n.session_id,tool:"exa_search",arguments:e,queuedAt:Date.now()}),{tool:"exa_search",ok:!1,error:"Offline — exa_search queued for retry"};const a=await this.options.bff.post("/api/v1/agent/tools/exa/search",n,this.options.authToken),s=a.hits?a:ae(a);return await y({cacheKey:o,tool:"exa_search",payload:s,cachedAt:Date.now(),ttlMs:C}),{tool:"exa_search",ok:!0,data:s}}async runSupabaseQuery(e){const n={rpc:e.rpc,args:e.args??{},session_id:O(e.session_id)};if(n.rpc!=="match_semantic_chunks"&&n.rpc!=="get_corpus_citation")return{tool:"supabase_query",ok:!1,error:"rpc not allowlisted"};const o=await $(`supabase:${JSON.stringify(n)}`),r=await q(o);if(r)return{tool:"supabase_query",ok:!0,data:r.payload};if(this.options.mockTools){const s={rpc:n.rpc,rows:[{chunk_id:"mock-chunk-001",content:"Synovitis grade 2: moderate synovial thickening with positive power Doppler signal.",book_id:"mor",parent_title:"Mock MOH Guideline",similarity:.91}]};return await y({cacheKey:o,tool:"supabase_query",payload:s,cachedAt:Date.now(),ttlMs:N}),{tool:"supabase_query",ok:!0,data:s}}if(!g())return await D({sessionId:n.session_id,tool:"supabase_query",arguments:e,queuedAt:Date.now()}),{tool:"supabase_query",ok:!1,error:"Offline — supabase_query queued for retry"};const a=await this.options.bff.post("/api/v1/agent/tools/supabase/query",n,this.options.authToken);return await y({cacheKey:o,tool:"supabase_query",payload:a,cachedAt:Date.now(),ttlMs:N}),{tool:"supabase_query",ok:!0,data:a}}async runEscalateMedgemma(e){const n={session_id:O(e.session_id),task_type:e.task_type??"clinical_deep_reasoning",prompt:M(e.prompt),include_images:!!e.include_images,stream:e.stream!==!1,redaction_hash:typeof e.redaction_hash=="string"?e.redaction_hash:void 0};return this.options.mockTools?{tool:"escalate_medgemma",ok:!0,data:{tier:"medgemma",text:"[Mock MedGemma] Deep clinical reasoning would stream here after consent."}}:g()?{tool:"escalate_medgemma",ok:!0,data:await this.options.bff.post("/api/v1/cloud-consult",{session_id:n.session_id,prompt:n.prompt,task_type:n.task_type,stream:!1,redaction_hash:n.redaction_hash},this.options.authToken)}:{tool:"escalate_medgemma",ok:!1,error:"Offline — MedGemma escalation requires network + consent"}}}function he(t){return new ue(t)}function S(t){const e=t.toLowerCase();return e.includes("synovitis")||e.includes("grade")||e.includes("guideline")||e.includes("moh")||e.includes("clinical")||e.includes("diagnosis")}function de(t){const e=t.toLowerCase();return e.includes("recent")||e.includes("latest")||e.includes("update")||e.includes("2024")||e.includes("2025")||e.includes("2026")?{name:"exa_search",arguments:{query:t.slice(0,512),type:"auto",numResults:10,session_id:"auto"}}:{name:"supabase_query",arguments:{rpc:"match_semantic_chunks",args:{query_text:t.slice(0,512),match_count:5},session_id:"auto"}}}function pe(t,e,n){return t.requireRetrievalBeforeFinal&&S(e)&&!n?"tool_call":"final"}function w(t){if(t==="tool_call")return G;if(t==="plan")return K}class _e{constructor(e){v(this,"executor");this.deps=e,this.executor=he({bff:new re(e.config.bffBaseUrl),authToken:e.config.authToken,mockTools:e.config.mockTools})}emit(e){var n,o;(o=(n=this.deps).onEvent)==null||o.call(n,e)}async runTurn(e,n){const o=z({baseSystemPrompt:this.deps.baseSystemPrompt,chainOfThought:e.chainOfThought}),r=[{role:"user",content:e.userMessage}],a=[],s=[];let i=!1,u=[];this.deps.config.enablePlanPhase&&(u=await this.runPlanPhase(e,o,r,n));for(let l=0;lj.ok));const h=pe(this.deps.config,e.userMessage,i);this.emit({type:"step_start",step:l+1,expectedKind:h});const c=await this.generateAndParse({input:e,baseAgentPrompt:o,conversation:r,expectedKind:h,signal:n});if(c.kind==="final"){if(this.deps.config.requireRetrievalBeforeFinal&&S(e.userMessage)&&!i){r.push({role:"model",content:`${c.text}`}),r.push({role:"user",content:"You must call supabase_query or exa_search before giving a final clinical answer."});continue}return await le({sessionId:e.sessionId,toolCatalogVersion:I,turns:r,updatedAt:Date.now()}),this.emit({type:"turn_complete",steps:l+1}),{finalAnswer:c.text,steps:l+1,toolCalls:a,toolResults:s,planChecklist:u}}if(c.kind!=="tool_call")throw new Error("Unexpected parser result after repair");c.call.arguments.session_id=typeof c.call.arguments.session_id=="string"?c.call.arguments.session_id:e.sessionId,a.push(c.call),this.emit({type:"tool_call",call:c.call}),this.emit({type:"tool_running",call:c.call});const d=await this.executor.run(c.call);s.push(d),this.emit({type:"tool_result",call:c.call,result:d}),(c.call.name==="exa_search"||c.call.name==="supabase_query")&&(i=d.ok),r.push({role:"model",content:`${JSON.stringify(c.call)}`}),r.push({role:"user",content:A(c.call.name,d)})}throw new Error(`Agent exceeded max steps (${this.deps.config.maxSteps})`)}async runPlanPhase(e,n,o,r){this.emit({type:"plan_start"});const a="plan",s=f(n,a,e.chainOfThought),i=m(s,o,e.chainOfThought);this.emit({type:"llm_generating",expectedKind:a});const{rawOutput:u}=await this.deps.generateStep({conversationPrompt:i,chainOfThought:e.chainOfThought,expectedKind:a,decodeOverride:w(a)});if(r!=null&&r.aborted)throw new DOMException("Agent turn aborted","AbortError");let l=_(u,e.chainOfThought,a);if(l.kind==="invalid"){this.emit({type:"repair",reason:l.reason,expectedKind:a}),o.push({role:"model",content:u}),o.push({role:"user",content:P(l.reason,a)});const h=m(f(n,a,e.chainOfThought),o,e.chainOfThought);this.emit({type:"llm_generating",expectedKind:a});const c=await this.deps.generateStep({conversationPrompt:h,chainOfThought:e.chainOfThought,expectedKind:a,decodeOverride:w(a)});l=_(c.rawOutput,e.chainOfThought,a)}if(l.kind!=="plan"){const h=["Analyze the question","Retrieve evidence if needed","Compose cited answer"];return this.emit({type:"plan",checklist:h,raw:u}),o.push({role:"user",content:`Follow this plan: -${h.map((c,d)=>`${d+1}. ${c}`).join(` -`)}`}),h}return this.emit({type:"plan",checklist:l.checklist,raw:l.raw}),o.push({role:"user",content:`Follow this plan: -${l.checklist.map((h,c)=>`${c+1}. ${h}`).join(` -`)}`}),l.checklist}async runAutoRetrieval(e,n,o,r,a){const s=de(n);s.arguments.session_id=e,this.emit({type:"tool_call",call:s,auto:!0}),this.emit({type:"tool_running",call:s}),r.push(s);const i=await this.executor.run(s);a.push(i),this.emit({type:"tool_result",call:s,result:i}),o.push({role:"model",content:`${JSON.stringify(s)}`}),o.push({role:"user",content:A(s.name,i)})}async generateAndParse(e){const{input:n,baseAgentPrompt:o,conversation:r,expectedKind:a,signal:s}=e,i=f(o,a,n.chainOfThought),u=m(i,r,n.chainOfThought);a==="final"&&this.emit({type:"final_start"}),this.emit({type:"llm_generating",expectedKind:a});let{rawOutput:l}=await this.deps.generateStep({conversationPrompt:u,chainOfThought:n.chainOfThought,expectedKind:a,decodeOverride:w(a)});if(s!=null&&s.aborted)throw new DOMException("Agent turn aborted","AbortError");let h=_(l,n.chainOfThought,a);if(h.kind==="invalid"){this.emit({type:"repair",reason:h.reason,expectedKind:a}),r.push({role:"model",content:l}),r.push({role:"user",content:P(h.reason,a)});const c=m(f(o,a,n.chainOfThought),r,n.chainOfThought);if(this.emit({type:"llm_generating",expectedKind:a}),l=(await this.deps.generateStep({conversationPrompt:c,chainOfThought:n.chainOfThought,expectedKind:a,decodeOverride:w(a)})).rawOutput,h=_(l,n.chainOfThought,a),h.kind==="invalid")throw new Error(`Agent output invalid: ${h.reason}`)}return h}}export{_e as A,me as D,oe as p}; diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/gradcam-overlay.svg b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/gradcam-overlay.svg deleted file mode 100644 index ed5f9bc..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/gradcam-overlay.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/icons/palette-figma.svg b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/icons/palette-figma.svg deleted file mode 100644 index 9453d69..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/icons/palette-figma.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/index-BCa3bqPy.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/index-BCa3bqPy.js deleted file mode 100644 index e667e4e..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/index-BCa3bqPy.js +++ /dev/null @@ -1,34 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/PatientWorklistPage-D1i7UsXA.js","assets/vendor-react-BWW3uBCb.js","assets/mlInferenceCacheStore-BwGclW_P.js","assets/ClinicalWorkspacePage-BRH1XOmJ.js","assets/agent-runtime-CHsDDJds.js"])))=>i.map(i=>d[i]); -import{r as ld,a as td,b as jt,R as Ov,c as ad,d as ei,N as ud,B as ed}from"./vendor-react-BWW3uBCb.js";(function(){const U=document.createElement("link").relList;if(U&&U.supports&&U.supports("modulepreload"))return;for(const B of document.querySelectorAll('link[rel="modulepreload"]'))h(B);new MutationObserver(B=>{for(const C of B)if(C.type==="childList")for(const L of C.addedNodes)L.tagName==="LINK"&&L.rel==="modulepreload"&&h(L)}).observe(document,{childList:!0,subtree:!0});function O(B){const C={};return B.integrity&&(C.integrity=B.integrity),B.referrerPolicy&&(C.referrerPolicy=B.referrerPolicy),B.crossOrigin==="use-credentials"?C.credentials="include":B.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function h(B){if(B.ep)return;B.ep=!0;const C=O(B);fetch(B.href,C)}})();var ni={exports:{}},de={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var hv;function nd(){if(hv)return de;hv=1;var A=Symbol.for("react.transitional.element"),U=Symbol.for("react.fragment");function O(h,B,C){var L=null;if(C!==void 0&&(L=""+C),B.key!==void 0&&(L=""+B.key),"key"in B){C={};for(var _ in B)_!=="key"&&(C[_]=B[_])}else C=B;return B=C.ref,{$$typeof:A,type:h,key:L,ref:B!==void 0?B:null,props:C}}return de.Fragment=U,de.jsx=O,de.jsxs=O,de}var Sv;function fd(){return Sv||(Sv=1,ni.exports=nd()),ni.exports}var Nl=fd(),fi={exports:{}},he={},ci={exports:{}},ii={};/** - * @license React - * scheduler.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var ov;function cd(){return ov||(ov=1,(function(A){function U(b,T){var q=b.length;b.push(T);l:for(;0>>1,ll=b[P];if(0>>1;PB(J,q))olB(st,J)?(b[P]=st,b[ol]=q,P=ol):(b[P]=J,b[el]=q,P=el);else if(olB(st,q))b[P]=st,b[ol]=q,P=ol;else break l}}return T}function B(b,T){var q=b.sortIndex-T.sortIndex;return q!==0?q:b.id-T.id}if(A.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var C=performance;A.unstable_now=function(){return C.now()}}else{var L=Date,_=L.now();A.unstable_now=function(){return L.now()-_}}var ml=[],I=[],Hl=1,H=null,K=3,hl=!1,Dl=!1,Ol=!1,Ba=!1,qa=typeof setTimeout=="function"?setTimeout:null,zu=typeof clearTimeout=="function"?clearTimeout:null,wl=typeof setImmediate<"u"?setImmediate:null;function Gt(b){for(var T=O(I);T!==null;){if(T.callback===null)h(I);else if(T.startTime<=b)h(I),T.sortIndex=T.expirationTime,U(ml,T);else break;T=O(I)}}function sa(b){if(Ol=!1,Gt(b),!Dl)if(O(ml)!==null)Dl=!0,mt||(mt=!0,Wl());else{var T=O(I);T!==null&&ot(sa,T.startTime-b)}}var mt=!1,St=-1,nt=5,pa=-1;function Se(){return Ba?!0:!(A.unstable_now()-pab&&Se());){var P=H.callback;if(typeof P=="function"){H.callback=null,K=H.priorityLevel;var ll=P(H.expirationTime<=b);if(b=A.unstable_now(),typeof ll=="function"){H.callback=ll,Gt(b),T=!0;break t}H===O(ml)&&h(ml),Gt(b)}else h(ml);H=O(ml)}if(H!==null)T=!0;else{var Ul=O(I);Ul!==null&&ot(sa,Ul.startTime-b),T=!1}}break l}finally{H=null,K=q,hl=!1}T=void 0}}finally{T?Wl():mt=!1}}}var Wl;if(typeof wl=="function")Wl=function(){wl(Ya)};else if(typeof MessageChannel<"u"){var oe=new MessageChannel,Au=oe.port2;oe.port1.onmessage=Ya,Wl=function(){Au.postMessage(null)}}else Wl=function(){qa(Ya,0)};function ot(b,T){St=qa(function(){b(A.unstable_now())},T)}A.unstable_IdlePriority=5,A.unstable_ImmediatePriority=1,A.unstable_LowPriority=4,A.unstable_NormalPriority=3,A.unstable_Profiling=null,A.unstable_UserBlockingPriority=2,A.unstable_cancelCallback=function(b){b.callback=null},A.unstable_forceFrameRate=function(b){0>b||125P?(b.sortIndex=q,U(I,b),O(ml)===null&&b===O(I)&&(Ol?(zu(St),St=-1):Ol=!0,ot(sa,q-P))):(b.sortIndex=ll,U(ml,b),Dl||hl||(Dl=!0,mt||(mt=!0,Wl()))),b},A.unstable_shouldYield=Se,A.unstable_wrapCallback=function(b){var T=K;return function(){var q=K;K=T;try{return b.apply(this,arguments)}finally{K=q}}}})(ii)),ii}var gv;function id(){return gv||(gv=1,ci.exports=cd()),ci.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var bv;function md(){if(bv)return he;bv=1;var A=id(),U=ld(),O=td();function h(l){var t="https://react.dev/errors/"+l;if(1ll||(l.current=P[ll],P[ll]=null,ll--)}function J(l,t){ll++,P[ll]=l.current,l.current=t}var ol=Ul(null),st=Ul(null),Rt=Ul(null),ge=Ul(null);function be(l,t){switch(J(Rt,t),J(st,l),J(ol,null),t.nodeType){case 9:case 11:l=(l=t.documentElement)&&(l=l.namespaceURI)?Rs(l):0;break;default:if(l=t.tagName,t=t.namespaceURI)t=Rs(t),l=Qs(t,l);else switch(l){case"svg":l=1;break;case"math":l=2;break;default:l=0}}el(ol),J(ol,l)}function Ca(){el(ol),el(st),el(Rt)}function Qn(l){l.memoizedState!==null&&J(ge,l);var t=ol.current,a=Qs(t,l.type);t!==a&&(J(st,l),J(ol,a))}function ze(l){st.current===l&&(el(ol),el(st)),ge.current===l&&(el(ge),me._currentValue=q)}var Xn,di;function va(l){if(Xn===void 0)try{throw Error()}catch(a){var t=a.stack.trim().match(/\n( *(at )?)/);Xn=t&&t[1]||"",di=-1)":-1e||i[u]!==y[e]){var o=` -`+i[u].replace(" at new "," at ");return l.displayName&&o.includes("")&&(o=o.replace("",l.displayName)),o}while(1<=u&&0<=e);break}}}finally{Zn=!1,Error.prepareStackTrace=a}return(a=l?l.displayName||l.name:"")?va(a):""}function qv(l,t){switch(l.tag){case 26:case 27:case 5:return va(l.type);case 16:return va("Lazy");case 13:return l.child!==t&&t!==null?va("Suspense Fallback"):va("Suspense");case 19:return va("SuspenseList");case 0:case 15:return xn(l.type,!1);case 11:return xn(l.type.render,!1);case 1:return xn(l.type,!0);case 31:return va("Activity");default:return""}}function hi(l){try{var t="",a=null;do t+=qv(l,a),a=l,l=l.return;while(l);return t}catch(u){return` -Error generating stack: `+u.message+` -`+u.stack}}var Vn=Object.prototype.hasOwnProperty,Ln=A.unstable_scheduleCallback,Kn=A.unstable_cancelCallback,pv=A.unstable_shouldYield,Yv=A.unstable_requestPaint,Rl=A.unstable_now,Cv=A.unstable_getCurrentPriorityLevel,Si=A.unstable_ImmediatePriority,oi=A.unstable_UserBlockingPriority,Ae=A.unstable_NormalPriority,jv=A.unstable_LowPriority,gi=A.unstable_IdlePriority,Gv=A.log,Rv=A.unstable_setDisableYieldValue,Tu=null,Ql=null;function Qt(l){if(typeof Gv=="function"&&Rv(l),Ql&&typeof Ql.setStrictMode=="function")try{Ql.setStrictMode(Tu,l)}catch{}}var Xl=Math.clz32?Math.clz32:Zv,Qv=Math.log,Xv=Math.LN2;function Zv(l){return l>>>=0,l===0?32:31-(Qv(l)/Xv|0)|0}var Te=256,Ee=262144,Me=4194304;function ya(l){var t=l&42;if(t!==0)return t;switch(l&-l){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return l&261888;case 262144:case 524288:case 1048576:case 2097152:return l&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return l&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return l}}function De(l,t,a){var u=l.pendingLanes;if(u===0)return 0;var e=0,n=l.suspendedLanes,f=l.pingedLanes;l=l.warmLanes;var c=u&134217727;return c!==0?(u=c&~n,u!==0?e=ya(u):(f&=c,f!==0?e=ya(f):a||(a=c&~l,a!==0&&(e=ya(a))))):(c=u&~n,c!==0?e=ya(c):f!==0?e=ya(f):a||(a=u&~l,a!==0&&(e=ya(a)))),e===0?0:t!==0&&t!==e&&(t&n)===0&&(n=e&-e,a=t&-t,n>=a||n===32&&(a&4194048)!==0)?t:e}function Eu(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function xv(l,t){switch(l){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function bi(){var l=Me;return Me<<=1,(Me&62914560)===0&&(Me=4194304),l}function Jn(l){for(var t=[],a=0;31>a;a++)t.push(l);return t}function Mu(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function Vv(l,t,a,u,e,n){var f=l.pendingLanes;l.pendingLanes=a,l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0,l.expiredLanes&=a,l.entangledLanes&=a,l.errorRecoveryDisabledLanes&=a,l.shellSuspendCounter=0;var c=l.entanglements,i=l.expirationTimes,y=l.hiddenUpdates;for(a=f&~a;0"u")return null;try{return l.activeElement||l.body}catch{return l.body}}var Wv=/[\n"\\]/g;function Fl(l){return l.replace(Wv,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function kn(l,t,a,u,e,n,f,c){l.name="",f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"?l.type=f:l.removeAttribute("type"),t!=null?f==="number"?(t===0&&l.value===""||l.value!=t)&&(l.value=""+$l(t)):l.value!==""+$l(t)&&(l.value=""+$l(t)):f!=="submit"&&f!=="reset"||l.removeAttribute("value"),t!=null?In(l,f,$l(t)):a!=null?In(l,f,$l(a)):u!=null&&l.removeAttribute("value"),e==null&&n!=null&&(l.defaultChecked=!!n),e!=null&&(l.checked=e&&typeof e!="function"&&typeof e!="symbol"),c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"?l.name=""+$l(c):l.removeAttribute("name")}function qi(l,t,a,u,e,n,f,c){if(n!=null&&typeof n!="function"&&typeof n!="symbol"&&typeof n!="boolean"&&(l.type=n),t!=null||a!=null){if(!(n!=="submit"&&n!=="reset"||t!=null)){Fn(l);return}a=a!=null?""+$l(a):"",t=t!=null?""+$l(t):a,c||t===l.value||(l.value=t),l.defaultValue=t}u=u??e,u=typeof u!="function"&&typeof u!="symbol"&&!!u,l.checked=c?l.checked:!!u,l.defaultChecked=!!u,f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"&&(l.name=f),Fn(l)}function In(l,t,a){t==="number"&&_e(l.ownerDocument)===l||l.defaultValue===""+a||(l.defaultValue=""+a)}function Za(l,t,a,u){if(l=l.options,t){t={};for(var e=0;e"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),uf=!1;if(zt)try{var _u={};Object.defineProperty(_u,"passive",{get:function(){uf=!0}}),window.addEventListener("test",_u,_u),window.removeEventListener("test",_u,_u)}catch{uf=!1}var Zt=null,ef=null,He=null;function Qi(){if(He)return He;var l,t=ef,a=t.length,u,e="value"in Zt?Zt.value:Zt.textContent,n=e.length;for(l=0;l=Bu),Ki=" ",Ji=!1;function ri(l,t){switch(l){case"keyup":return E1.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function wi(l){return l=l.detail,typeof l=="object"&&"data"in l?l.data:null}var Ka=!1;function D1(l,t){switch(l){case"compositionend":return wi(t);case"keypress":return t.which!==32?null:(Ji=!0,Ki);case"textInput":return l=t.data,l===Ki&&Ji?null:l;default:return null}}function O1(l,t){if(Ka)return l==="compositionend"||!sf&&ri(l,t)?(l=Qi(),He=ef=Zt=null,Ka=!1,l):null;switch(l){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:a,offset:t-l};l=u}l:{for(;a;){if(a.nextSibling){a=a.nextSibling;break l}a=a.parentNode}a=void 0}a=t0(a)}}function u0(l,t){return l&&t?l===t?!0:l&&l.nodeType===3?!1:t&&t.nodeType===3?u0(l,t.parentNode):"contains"in l?l.contains(t):l.compareDocumentPosition?!!(l.compareDocumentPosition(t)&16):!1:!1}function e0(l){l=l!=null&&l.ownerDocument!=null&&l.ownerDocument.defaultView!=null?l.ownerDocument.defaultView:window;for(var t=_e(l.document);t instanceof l.HTMLIFrameElement;){try{var a=typeof t.contentWindow.location.href=="string"}catch{a=!1}if(a)l=t.contentWindow;else break;t=_e(l.document)}return t}function df(l){var t=l&&l.nodeName&&l.nodeName.toLowerCase();return t&&(t==="input"&&(l.type==="text"||l.type==="search"||l.type==="tel"||l.type==="url"||l.type==="password")||t==="textarea"||l.contentEditable==="true")}var Y1=zt&&"documentMode"in document&&11>=document.documentMode,Ja=null,hf=null,Cu=null,Sf=!1;function n0(l,t,a){var u=a.window===a?a.document:a.nodeType===9?a:a.ownerDocument;Sf||Ja==null||Ja!==_e(u)||(u=Ja,"selectionStart"in u&&df(u)?u={start:u.selectionStart,end:u.selectionEnd}:(u=(u.ownerDocument&&u.ownerDocument.defaultView||window).getSelection(),u={anchorNode:u.anchorNode,anchorOffset:u.anchorOffset,focusNode:u.focusNode,focusOffset:u.focusOffset}),Cu&&Yu(Cu,u)||(Cu=u,u=Mn(hf,"onSelect"),0>=f,e-=f,vt=1<<32-Xl(t)+e|a<Y?(Q=M,M=null):Q=M.sibling;var x=d(s,M,v[Y],g);if(x===null){M===null&&(M=Q);break}l&&M&&x.alternate===null&&t(s,M),m=n(x,m,Y),Z===null?D=x:Z.sibling=x,Z=x,M=Q}if(Y===v.length)return a(s,M),X&&Tt(s,Y),D;if(M===null){for(;YY?(Q=M,M=null):Q=M.sibling;var ia=d(s,M,x.value,g);if(ia===null){M===null&&(M=Q);break}l&&M&&ia.alternate===null&&t(s,M),m=n(ia,m,Y),Z===null?D=ia:Z.sibling=ia,Z=ia,M=Q}if(x.done)return a(s,M),X&&Tt(s,Y),D;if(M===null){for(;!x.done;Y++,x=v.next())x=z(s,x.value,g),x!==null&&(m=n(x,m,Y),Z===null?D=x:Z.sibling=x,Z=x);return X&&Tt(s,Y),D}for(M=u(M);!x.done;Y++,x=v.next())x=S(M,s,Y,x.value,g),x!==null&&(l&&x.alternate!==null&&M.delete(x.key===null?Y:x.key),m=n(x,m,Y),Z===null?D=x:Z.sibling=x,Z=x);return l&&M.forEach(function(Py){return t(s,Py)}),X&&Tt(s,Y),D}function F(s,m,v,g){if(typeof v=="object"&&v!==null&&v.type===Ol&&v.key===null&&(v=v.props.children),typeof v=="object"&&v!==null){switch(v.$$typeof){case hl:l:{for(var D=v.key;m!==null;){if(m.key===D){if(D=v.type,D===Ol){if(m.tag===7){a(s,m.sibling),g=e(m,v.props.children),g.return=s,s=g;break l}}else if(m.elementType===D||typeof D=="object"&&D!==null&&D.$$typeof===nt&&Ma(D)===m.type){a(s,m.sibling),g=e(m,v.props),Zu(g,v),g.return=s,s=g;break l}a(s,m);break}else t(s,m);m=m.sibling}v.type===Ol?(g=ba(v.props.children,s.mode,g,v.key),g.return=s,s=g):(g=Xe(v.type,v.key,v.props,null,s.mode,g),Zu(g,v),g.return=s,s=g)}return f(s);case Dl:l:{for(D=v.key;m!==null;){if(m.key===D)if(m.tag===4&&m.stateNode.containerInfo===v.containerInfo&&m.stateNode.implementation===v.implementation){a(s,m.sibling),g=e(m,v.children||[]),g.return=s,s=g;break l}else{a(s,m);break}else t(s,m);m=m.sibling}g=Ef(v,s.mode,g),g.return=s,s=g}return f(s);case nt:return v=Ma(v),F(s,m,v,g)}if(ot(v))return E(s,m,v,g);if(Wl(v)){if(D=Wl(v),typeof D!="function")throw Error(h(150));return v=D.call(v),N(s,m,v,g)}if(typeof v.then=="function")return F(s,m,re(v),g);if(v.$$typeof===wl)return F(s,m,Ve(s,v),g);we(s,v)}return typeof v=="string"&&v!==""||typeof v=="number"||typeof v=="bigint"?(v=""+v,m!==null&&m.tag===6?(a(s,m.sibling),g=e(m,v),g.return=s,s=g):(a(s,m),g=Tf(v,s.mode,g),g.return=s,s=g),f(s)):a(s,m)}return function(s,m,v,g){try{Xu=0;var D=F(s,m,v,g);return au=null,D}catch(M){if(M===tu||M===Ke)throw M;var Z=xl(29,M,null,s.mode);return Z.lanes=g,Z.return=s,Z}finally{}}}var Oa=N0(!0),H0=N0(!1),Jt=!1;function Cf(l){l.updateQueue={baseState:l.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function jf(l,t){l=l.updateQueue,t.updateQueue===l&&(t.updateQueue={baseState:l.baseState,firstBaseUpdate:l.firstBaseUpdate,lastBaseUpdate:l.lastBaseUpdate,shared:l.shared,callbacks:null})}function rt(l){return{lane:l,tag:0,payload:null,callback:null,next:null}}function wt(l,t,a){var u=l.updateQueue;if(u===null)return null;if(u=u.shared,(V&2)!==0){var e=u.pending;return e===null?t.next=t:(t.next=e.next,e.next=t),u.pending=t,t=Qe(l),y0(l,null,a),t}return Re(l,u,t,a),Qe(l)}function xu(l,t,a){if(t=t.updateQueue,t!==null&&(t=t.shared,(a&4194048)!==0)){var u=t.lanes;u&=l.pendingLanes,a|=u,t.lanes=a,Ai(l,a)}}function Gf(l,t){var a=l.updateQueue,u=l.alternate;if(u!==null&&(u=u.updateQueue,a===u)){var e=null,n=null;if(a=a.firstBaseUpdate,a!==null){do{var f={lane:a.lane,tag:a.tag,payload:a.payload,callback:null,next:null};n===null?e=n=f:n=n.next=f,a=a.next}while(a!==null);n===null?e=n=t:n=n.next=t}else e=n=t;a={baseState:u.baseState,firstBaseUpdate:e,lastBaseUpdate:n,shared:u.shared,callbacks:u.callbacks},l.updateQueue=a;return}l=a.lastBaseUpdate,l===null?a.firstBaseUpdate=t:l.next=t,a.lastBaseUpdate=t}var Rf=!1;function Vu(){if(Rf){var l=lu;if(l!==null)throw l}}function Lu(l,t,a,u){Rf=!1;var e=l.updateQueue;Jt=!1;var n=e.firstBaseUpdate,f=e.lastBaseUpdate,c=e.shared.pending;if(c!==null){e.shared.pending=null;var i=c,y=i.next;i.next=null,f===null?n=y:f.next=y,f=i;var o=l.alternate;o!==null&&(o=o.updateQueue,c=o.lastBaseUpdate,c!==f&&(c===null?o.firstBaseUpdate=y:c.next=y,o.lastBaseUpdate=i))}if(n!==null){var z=e.baseState;f=0,o=y=i=null,c=n;do{var d=c.lane&-536870913,S=d!==c.lane;if(S?(R&d)===d:(u&d)===d){d!==0&&d===Pa&&(Rf=!0),o!==null&&(o=o.next={lane:0,tag:c.tag,payload:c.payload,callback:null,next:null});l:{var E=l,N=c;d=t;var F=a;switch(N.tag){case 1:if(E=N.payload,typeof E=="function"){z=E.call(F,z,d);break l}z=E;break l;case 3:E.flags=E.flags&-65537|128;case 0:if(E=N.payload,d=typeof E=="function"?E.call(F,z,d):E,d==null)break l;z=H({},z,d);break l;case 2:Jt=!0}}d=c.callback,d!==null&&(l.flags|=64,S&&(l.flags|=8192),S=e.callbacks,S===null?e.callbacks=[d]:S.push(d))}else S={lane:d,tag:c.tag,payload:c.payload,callback:c.callback,next:null},o===null?(y=o=S,i=z):o=o.next=S,f|=d;if(c=c.next,c===null){if(c=e.shared.pending,c===null)break;S=c,c=S.next,S.next=null,e.lastBaseUpdate=S,e.shared.pending=null}}while(!0);o===null&&(i=z),e.baseState=i,e.firstBaseUpdate=y,e.lastBaseUpdate=o,n===null&&(e.shared.lanes=0),It|=f,l.lanes=f,l.memoizedState=z}}function B0(l,t){if(typeof l!="function")throw Error(h(191,l));l.call(t)}function q0(l,t){var a=l.callbacks;if(a!==null)for(l.callbacks=null,l=0;ln?n:8;var f=b.T,c={};b.T=c,ac(l,!1,t,a);try{var i=e(),y=b.S;if(y!==null&&y(c,i),i!==null&&typeof i=="object"&&typeof i.then=="function"){var o=V1(i,u);ru(l,t,o,rl(l))}else ru(l,t,u,rl(l))}catch(z){ru(l,t,{then:function(){},status:"rejected",reason:z},rl())}finally{T.p=n,f!==null&&c.types!==null&&(f.types=c.types),b.T=f}}function W1(){}function lc(l,t,a,u){if(l.tag!==5)throw Error(h(476));var e=mm(l).queue;im(l,e,t,q,a===null?W1:function(){return sm(l),a(u)})}function mm(l){var t=l.memoizedState;if(t!==null)return t;t={memoizedState:q,baseState:q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ot,lastRenderedState:q},next:null};var a={};return t.next={memoizedState:a,baseState:a,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Ot,lastRenderedState:a},next:null},l.memoizedState=t,l=l.alternate,l!==null&&(l.memoizedState=t),t}function sm(l){var t=mm(l);t.next===null&&(t=l.alternate.memoizedState),ru(l,t.next.queue,{},rl())}function tc(){return Tl(me)}function vm(){return il().memoizedState}function ym(){return il().memoizedState}function $1(l){for(var t=l.return;t!==null;){switch(t.tag){case 24:case 3:var a=rl();l=rt(a);var u=wt(t,l,a);u!==null&&(Gl(u,t,a),xu(u,t,a)),t={cache:Bf()},l.payload=t;return}t=t.return}}function F1(l,t,a){var u=rl();a={lane:u,revertLane:0,gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},un(l)?hm(t,a):(a=zf(l,t,a,u),a!==null&&(Gl(a,l,u),Sm(a,t,u)))}function dm(l,t,a){var u=rl();ru(l,t,a,u)}function ru(l,t,a,u){var e={lane:u,revertLane:0,gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null};if(un(l))hm(t,e);else{var n=l.alternate;if(l.lanes===0&&(n===null||n.lanes===0)&&(n=t.lastRenderedReducer,n!==null))try{var f=t.lastRenderedState,c=n(f,a);if(e.hasEagerState=!0,e.eagerState=c,Zl(c,f))return Re(l,t,e,0),k===null&&Ge(),!1}catch{}finally{}if(a=zf(l,t,e,u),a!==null)return Gl(a,l,u),Sm(a,t,u),!0}return!1}function ac(l,t,a,u){if(u={lane:2,revertLane:Cc(),gesture:null,action:u,hasEagerState:!1,eagerState:null,next:null},un(l)){if(t)throw Error(h(479))}else t=zf(l,a,u,2),t!==null&&Gl(t,l,2)}function un(l){var t=l.alternate;return l===p||t!==null&&t===p}function hm(l,t){eu=Fe=!0;var a=l.pending;a===null?t.next=t:(t.next=a.next,a.next=t),l.pending=t}function Sm(l,t,a){if((a&4194048)!==0){var u=t.lanes;u&=l.pendingLanes,a|=u,t.lanes=a,Ai(l,a)}}var wu={readContext:Tl,use:Pe,useCallback:nl,useContext:nl,useEffect:nl,useImperativeHandle:nl,useLayoutEffect:nl,useInsertionEffect:nl,useMemo:nl,useReducer:nl,useRef:nl,useState:nl,useDebugValue:nl,useDeferredValue:nl,useTransition:nl,useSyncExternalStore:nl,useId:nl,useHostTransitionStatus:nl,useFormState:nl,useActionState:nl,useOptimistic:nl,useMemoCache:nl,useCacheRefresh:nl};wu.useEffectEvent=nl;var om={readContext:Tl,use:Pe,useCallback:function(l,t){return _l().memoizedState=[l,t===void 0?null:t],l},useContext:Tl,useEffect:P0,useImperativeHandle:function(l,t,a){a=a!=null?a.concat([l]):null,tn(4194308,4,um.bind(null,t,l),a)},useLayoutEffect:function(l,t){return tn(4194308,4,l,t)},useInsertionEffect:function(l,t){tn(4,2,l,t)},useMemo:function(l,t){var a=_l();t=t===void 0?null:t;var u=l();if(Ua){Qt(!0);try{l()}finally{Qt(!1)}}return a.memoizedState=[u,t],u},useReducer:function(l,t,a){var u=_l();if(a!==void 0){var e=a(t);if(Ua){Qt(!0);try{a(t)}finally{Qt(!1)}}}else e=t;return u.memoizedState=u.baseState=e,l={pending:null,lanes:0,dispatch:null,lastRenderedReducer:l,lastRenderedState:e},u.queue=l,l=l.dispatch=F1.bind(null,p,l),[u.memoizedState,l]},useRef:function(l){var t=_l();return l={current:l},t.memoizedState=l},useState:function(l){l=$f(l);var t=l.queue,a=dm.bind(null,p,t);return t.dispatch=a,[l.memoizedState,a]},useDebugValue:If,useDeferredValue:function(l,t){var a=_l();return Pf(a,l,t)},useTransition:function(){var l=$f(!1);return l=im.bind(null,p,l.queue,!0,!1),_l().memoizedState=l,[!1,l]},useSyncExternalStore:function(l,t,a){var u=p,e=_l();if(X){if(a===void 0)throw Error(h(407));a=a()}else{if(a=t(),k===null)throw Error(h(349));(R&127)!==0||R0(u,t,a)}e.memoizedState=a;var n={value:a,getSnapshot:t};return e.queue=n,P0(X0.bind(null,u,n,l),[l]),u.flags|=2048,fu(9,{destroy:void 0},Q0.bind(null,u,n,a,t),null),a},useId:function(){var l=_l(),t=k.identifierPrefix;if(X){var a=yt,u=vt;a=(u&~(1<<32-Xl(u)-1)).toString(32)+a,t="_"+t+"R_"+a,a=ke++,0<\/script>",n=n.removeChild(n.firstChild);break;case"select":n=typeof u.is=="string"?f.createElement("select",{is:u.is}):f.createElement("select"),u.multiple?n.multiple=!0:u.size&&(n.size=u.size);break;default:n=typeof u.is=="string"?f.createElement(e,{is:u.is}):f.createElement(e)}}n[zl]=t,n[Bl]=u;l:for(f=t.child;f!==null;){if(f.tag===5||f.tag===6)n.appendChild(f.stateNode);else if(f.tag!==4&&f.tag!==27&&f.child!==null){f.child.return=f,f=f.child;continue}if(f===t)break l;for(;f.sibling===null;){if(f.return===null||f.return===t)break l;f=f.return}f.sibling.return=f.return,f=f.sibling}t.stateNode=n;l:switch(Ml(n,e,u),e){case"button":case"input":case"select":case"textarea":u=!!u.autoFocus;break l;case"img":u=!0;break l;default:u=!1}u&&_t(t)}}return al(t),oc(t,t.type,l===null?null:l.memoizedProps,t.pendingProps,a),null;case 6:if(l&&t.stateNode!=null)l.memoizedProps!==u&&_t(t);else{if(typeof u!="string"&&t.stateNode===null)throw Error(h(166));if(l=Rt.current,ka(t)){if(l=t.stateNode,a=t.memoizedProps,u=null,e=Al,e!==null)switch(e.tag){case 27:case 5:u=e.memoizedProps}l[zl]=t,l=!!(l.nodeValue===a||u!==null&&u.suppressHydrationWarning===!0||js(l.nodeValue,a)),l||Lt(t,!0)}else l=Dn(l).createTextNode(u),l[zl]=t,t.stateNode=l}return al(t),null;case 31:if(a=t.memoizedState,l===null||l.memoizedState!==null){if(u=ka(t),a!==null){if(l===null){if(!u)throw Error(h(318));if(l=t.memoizedState,l=l!==null?l.dehydrated:null,!l)throw Error(h(557));l[zl]=t}else za(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;al(t),l=!1}else a=Uf(),l!==null&&l.memoizedState!==null&&(l.memoizedState.hydrationErrors=a),l=!0;if(!l)return t.flags&256?(Ll(t),t):(Ll(t),null);if((t.flags&128)!==0)throw Error(h(558))}return al(t),null;case 13:if(u=t.memoizedState,l===null||l.memoizedState!==null&&l.memoizedState.dehydrated!==null){if(e=ka(t),u!==null&&u.dehydrated!==null){if(l===null){if(!e)throw Error(h(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(h(317));e[zl]=t}else za(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;al(t),e=!1}else e=Uf(),l!==null&&l.memoizedState!==null&&(l.memoizedState.hydrationErrors=e),e=!0;if(!e)return t.flags&256?(Ll(t),t):(Ll(t),null)}return Ll(t),(t.flags&128)!==0?(t.lanes=a,t):(a=u!==null,l=l!==null&&l.memoizedState!==null,a&&(u=t.child,e=null,u.alternate!==null&&u.alternate.memoizedState!==null&&u.alternate.memoizedState.cachePool!==null&&(e=u.alternate.memoizedState.cachePool.pool),n=null,u.memoizedState!==null&&u.memoizedState.cachePool!==null&&(n=u.memoizedState.cachePool.pool),n!==e&&(u.flags|=2048)),a!==l&&a&&(t.child.flags|=8192),mn(t,t.updateQueue),al(t),null);case 4:return Ca(),l===null&&Qc(t.stateNode.containerInfo),al(t),null;case 10:return Mt(t.type),al(t),null;case 19:if(el(cl),u=t.memoizedState,u===null)return al(t),null;if(e=(t.flags&128)!==0,n=u.rendering,n===null)if(e)$u(u,!1);else{if(fl!==0||l!==null&&(l.flags&128)!==0)for(l=t.child;l!==null;){if(n=$e(l),n!==null){for(t.flags|=128,$u(u,!1),l=n.updateQueue,t.updateQueue=l,mn(t,l),t.subtreeFlags=0,l=a,a=t.child;a!==null;)d0(a,l),a=a.sibling;return J(cl,cl.current&1|2),X&&Tt(t,u.treeForkCount),t.child}l=l.sibling}u.tail!==null&&Rl()>hn&&(t.flags|=128,e=!0,$u(u,!1),t.lanes=4194304)}else{if(!e)if(l=$e(n),l!==null){if(t.flags|=128,e=!0,l=l.updateQueue,t.updateQueue=l,mn(t,l),$u(u,!0),u.tail===null&&u.tailMode==="hidden"&&!n.alternate&&!X)return al(t),null}else 2*Rl()-u.renderingStartTime>hn&&a!==536870912&&(t.flags|=128,e=!0,$u(u,!1),t.lanes=4194304);u.isBackwards?(n.sibling=t.child,t.child=n):(l=u.last,l!==null?l.sibling=n:t.child=n,u.last=n)}return u.tail!==null?(l=u.tail,u.rendering=l,u.tail=l.sibling,u.renderingStartTime=Rl(),l.sibling=null,a=cl.current,J(cl,e?a&1|2:a&1),X&&Tt(t,u.treeForkCount),l):(al(t),null);case 22:case 23:return Ll(t),Xf(),u=t.memoizedState!==null,l!==null?l.memoizedState!==null!==u&&(t.flags|=8192):u&&(t.flags|=8192),u?(a&536870912)!==0&&(t.flags&128)===0&&(al(t),t.subtreeFlags&6&&(t.flags|=8192)):al(t),a=t.updateQueue,a!==null&&mn(t,a.retryQueue),a=null,l!==null&&l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(a=l.memoizedState.cachePool.pool),u=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(u=t.memoizedState.cachePool.pool),u!==a&&(t.flags|=2048),l!==null&&el(Ea),null;case 24:return a=null,l!==null&&(a=l.memoizedState.cache),t.memoizedState.cache!==a&&(t.flags|=2048),Mt(sl),al(t),null;case 25:return null;case 30:return null}throw Error(h(156,t.tag))}function ty(l,t){switch(Df(t),t.tag){case 1:return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 3:return Mt(sl),Ca(),l=t.flags,(l&65536)!==0&&(l&128)===0?(t.flags=l&-65537|128,t):null;case 26:case 27:case 5:return ze(t),null;case 31:if(t.memoizedState!==null){if(Ll(t),t.alternate===null)throw Error(h(340));za()}return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 13:if(Ll(t),l=t.memoizedState,l!==null&&l.dehydrated!==null){if(t.alternate===null)throw Error(h(340));za()}return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 19:return el(cl),null;case 4:return Ca(),null;case 10:return Mt(t.type),null;case 22:case 23:return Ll(t),Xf(),l!==null&&el(Ea),l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 24:return Mt(sl),null;case 25:return null;default:return null}}function Zm(l,t){switch(Df(t),t.tag){case 3:Mt(sl),Ca();break;case 26:case 27:case 5:ze(t);break;case 4:Ca();break;case 31:t.memoizedState!==null&&Ll(t);break;case 13:Ll(t);break;case 19:el(cl);break;case 10:Mt(t.type);break;case 22:case 23:Ll(t),Xf(),l!==null&&el(Ea);break;case 24:Mt(sl)}}function Fu(l,t){try{var a=t.updateQueue,u=a!==null?a.lastEffect:null;if(u!==null){var e=u.next;a=e;do{if((a.tag&l)===l){u=void 0;var n=a.create,f=a.inst;u=n(),f.destroy=u}a=a.next}while(a!==e)}}catch(c){w(t,t.return,c)}}function Ft(l,t,a){try{var u=t.updateQueue,e=u!==null?u.lastEffect:null;if(e!==null){var n=e.next;u=n;do{if((u.tag&l)===l){var f=u.inst,c=f.destroy;if(c!==void 0){f.destroy=void 0,e=t;var i=a,y=c;try{y()}catch(o){w(e,i,o)}}}u=u.next}while(u!==n)}}catch(o){w(t,t.return,o)}}function xm(l){var t=l.updateQueue;if(t!==null){var a=l.stateNode;try{q0(t,a)}catch(u){w(l,l.return,u)}}}function Vm(l,t,a){a.props=_a(l.type,l.memoizedProps),a.state=l.memoizedState;try{a.componentWillUnmount()}catch(u){w(l,t,u)}}function ku(l,t){try{var a=l.ref;if(a!==null){switch(l.tag){case 26:case 27:case 5:var u=l.stateNode;break;case 30:u=l.stateNode;break;default:u=l.stateNode}typeof a=="function"?l.refCleanup=a(u):a.current=u}}catch(e){w(l,t,e)}}function dt(l,t){var a=l.ref,u=l.refCleanup;if(a!==null)if(typeof u=="function")try{u()}catch(e){w(l,t,e)}finally{l.refCleanup=null,l=l.alternate,l!=null&&(l.refCleanup=null)}else if(typeof a=="function")try{a(null)}catch(e){w(l,t,e)}else a.current=null}function Lm(l){var t=l.type,a=l.memoizedProps,u=l.stateNode;try{l:switch(t){case"button":case"input":case"select":case"textarea":a.autoFocus&&u.focus();break l;case"img":a.src?u.src=a.src:a.srcSet&&(u.srcset=a.srcSet)}}catch(e){w(l,l.return,e)}}function gc(l,t,a){try{var u=l.stateNode;My(u,l.type,a,t),u[Bl]=t}catch(e){w(l,l.return,e)}}function Km(l){return l.tag===5||l.tag===3||l.tag===26||l.tag===27&&ua(l.type)||l.tag===4}function bc(l){l:for(;;){for(;l.sibling===null;){if(l.return===null||Km(l.return))return null;l=l.return}for(l.sibling.return=l.return,l=l.sibling;l.tag!==5&&l.tag!==6&&l.tag!==18;){if(l.tag===27&&ua(l.type)||l.flags&2||l.child===null||l.tag===4)continue l;l.child.return=l,l=l.child}if(!(l.flags&2))return l.stateNode}}function zc(l,t,a){var u=l.tag;if(u===5||u===6)l=l.stateNode,t?(a.nodeType===9?a.body:a.nodeName==="HTML"?a.ownerDocument.body:a).insertBefore(l,t):(t=a.nodeType===9?a.body:a.nodeName==="HTML"?a.ownerDocument.body:a,t.appendChild(l),a=a._reactRootContainer,a!=null||t.onclick!==null||(t.onclick=bt));else if(u!==4&&(u===27&&ua(l.type)&&(a=l.stateNode,t=null),l=l.child,l!==null))for(zc(l,t,a),l=l.sibling;l!==null;)zc(l,t,a),l=l.sibling}function sn(l,t,a){var u=l.tag;if(u===5||u===6)l=l.stateNode,t?a.insertBefore(l,t):a.appendChild(l);else if(u!==4&&(u===27&&ua(l.type)&&(a=l.stateNode),l=l.child,l!==null))for(sn(l,t,a),l=l.sibling;l!==null;)sn(l,t,a),l=l.sibling}function Jm(l){var t=l.stateNode,a=l.memoizedProps;try{for(var u=l.type,e=t.attributes;e.length;)t.removeAttributeNode(e[0]);Ml(t,u,a),t[zl]=l,t[Bl]=a}catch(n){w(l,l.return,n)}}var Nt=!1,dl=!1,Ac=!1,rm=typeof WeakSet=="function"?WeakSet:Set,bl=null;function ay(l,t){if(l=l.containerInfo,xc=qn,l=e0(l),df(l)){if("selectionStart"in l)var a={start:l.selectionStart,end:l.selectionEnd};else l:{a=(a=l.ownerDocument)&&a.defaultView||window;var u=a.getSelection&&a.getSelection();if(u&&u.rangeCount!==0){a=u.anchorNode;var e=u.anchorOffset,n=u.focusNode;u=u.focusOffset;try{a.nodeType,n.nodeType}catch{a=null;break l}var f=0,c=-1,i=-1,y=0,o=0,z=l,d=null;t:for(;;){for(var S;z!==a||e!==0&&z.nodeType!==3||(c=f+e),z!==n||u!==0&&z.nodeType!==3||(i=f+u),z.nodeType===3&&(f+=z.nodeValue.length),(S=z.firstChild)!==null;)d=z,z=S;for(;;){if(z===l)break t;if(d===a&&++y===e&&(c=f),d===n&&++o===u&&(i=f),(S=z.nextSibling)!==null)break;z=d,d=z.parentNode}z=S}a=c===-1||i===-1?null:{start:c,end:i}}else a=null}a=a||{start:0,end:0}}else a=null;for(Vc={focusedElem:l,selectionRange:a},qn=!1,bl=t;bl!==null;)if(t=bl,l=t.child,(t.subtreeFlags&1028)!==0&&l!==null)l.return=t,bl=l;else for(;bl!==null;){switch(t=bl,n=t.alternate,l=t.flags,t.tag){case 0:if((l&4)!==0&&(l=t.updateQueue,l=l!==null?l.events:null,l!==null))for(a=0;a title"))),Ml(n,u,a),n[zl]=l,gl(n),u=n;break l;case"link":var f=Is("link","href",e).get(u+(a.href||""));if(f){for(var c=0;cF&&(f=F,F=N,N=f);var s=a0(c,N),m=a0(c,F);if(s&&m&&(S.rangeCount!==1||S.anchorNode!==s.node||S.anchorOffset!==s.offset||S.focusNode!==m.node||S.focusOffset!==m.offset)){var v=z.createRange();v.setStart(s.node,s.offset),S.removeAllRanges(),N>F?(S.addRange(v),S.extend(m.node,m.offset)):(v.setEnd(m.node,m.offset),S.addRange(v))}}}}for(z=[],S=c;S=S.parentNode;)S.nodeType===1&&z.push({element:S,left:S.scrollLeft,top:S.scrollTop});for(typeof c.focus=="function"&&c.focus(),c=0;ca?32:a,b.T=null,a=_c,_c=null;var n=la,f=Yt;if(Sl=0,vu=la=null,Yt=0,(V&6)!==0)throw Error(h(331));var c=V;if(V|=4,us(n.current),ls(n,n.current,f,a),V=c,ue(0,!1),Ql&&typeof Ql.onPostCommitFiberRoot=="function")try{Ql.onPostCommitFiberRoot(Tu,n)}catch{}return!0}finally{T.p=e,b.T=u,Ts(l,t)}}function Ms(l,t,a){t=Il(a,t),t=fc(l.stateNode,t,2),l=wt(l,t,2),l!==null&&(Mu(l,2),ht(l))}function w(l,t,a){if(l.tag===3)Ms(l,l,a);else for(;t!==null;){if(t.tag===3){Ms(t,l,a);break}else if(t.tag===1){var u=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof u.componentDidCatch=="function"&&(Pt===null||!Pt.has(u))){l=Il(a,l),a=Dm(2),u=wt(t,a,2),u!==null&&(Om(a,u,t,l),Mu(u,2),ht(u));break}}t=t.return}}function qc(l,t,a){var u=l.pingCache;if(u===null){u=l.pingCache=new ny;var e=new Set;u.set(t,e)}else e=u.get(t),e===void 0&&(e=new Set,u.set(t,e));e.has(a)||(Mc=!0,e.add(a),l=sy.bind(null,l,t,a),t.then(l,l))}function sy(l,t,a){var u=l.pingCache;u!==null&&u.delete(t),l.pingedLanes|=l.suspendedLanes&a,l.warmLanes&=~a,k===l&&(R&a)===a&&(fl===4||fl===3&&(R&62914560)===R&&300>Rl()-dn?(V&2)===0&&yu(l,0):Dc|=a,su===R&&(su=0)),ht(l)}function Ds(l,t){t===0&&(t=bi()),l=ga(l,t),l!==null&&(Mu(l,t),ht(l))}function vy(l){var t=l.memoizedState,a=0;t!==null&&(a=t.retryLane),Ds(l,a)}function yy(l,t){var a=0;switch(l.tag){case 31:case 13:var u=l.stateNode,e=l.memoizedState;e!==null&&(a=e.retryLane);break;case 19:u=l.stateNode;break;case 22:u=l.stateNode._retryCache;break;default:throw Error(h(314))}u!==null&&u.delete(t),Ds(l,a)}function dy(l,t){return Ln(l,t)}var An=null,hu=null,pc=!1,Tn=!1,Yc=!1,aa=0;function ht(l){l!==hu&&l.next===null&&(hu===null?An=hu=l:hu=hu.next=l),Tn=!0,pc||(pc=!0,Sy())}function ue(l,t){if(!Yc&&Tn){Yc=!0;do for(var a=!1,u=An;u!==null;){if(l!==0){var e=u.pendingLanes;if(e===0)var n=0;else{var f=u.suspendedLanes,c=u.pingedLanes;n=(1<<31-Xl(42|l)+1)-1,n&=e&~(f&~c),n=n&201326741?n&201326741|1:n?n|2:0}n!==0&&(a=!0,Ns(u,n))}else n=R,n=De(u,u===k?n:0,u.cancelPendingCommit!==null||u.timeoutHandle!==-1),(n&3)===0||Eu(u,n)||(a=!0,Ns(u,n));u=u.next}while(a);Yc=!1}}function hy(){Os()}function Os(){Tn=pc=!1;var l=0;aa!==0&&Oy()&&(l=aa);for(var t=Rl(),a=null,u=An;u!==null;){var e=u.next,n=Us(u,t);n===0?(u.next=null,a===null?An=e:a.next=e,e===null&&(hu=a)):(a=u,(l!==0||(n&3)!==0)&&(Tn=!0)),u=e}Sl!==0&&Sl!==5||ue(l),aa!==0&&(aa=0)}function Us(l,t){for(var a=l.suspendedLanes,u=l.pingedLanes,e=l.expirationTimes,n=l.pendingLanes&-62914561;0c)break;var o=i.transferSize,z=i.initiatorType;o&&Gs(z)&&(i=i.responseEnd,f+=o*(i"u"?null:document;function Ws(l,t,a){var u=Su;if(u&&typeof t=="string"&&t){var e=Fl(t);e='link[rel="'+l+'"][href="'+e+'"]',typeof a=="string"&&(e+='[crossorigin="'+a+'"]'),ws.has(e)||(ws.add(e),l={rel:l,crossOrigin:a,href:t},u.querySelector(e)===null&&(t=u.createElement("link"),Ml(t,"link",l),gl(t),u.head.appendChild(t)))}}function Cy(l){Ct.D(l),Ws("dns-prefetch",l,null)}function jy(l,t){Ct.C(l,t),Ws("preconnect",l,t)}function Gy(l,t,a){Ct.L(l,t,a);var u=Su;if(u&&l&&t){var e='link[rel="preload"][as="'+Fl(t)+'"]';t==="image"&&a&&a.imageSrcSet?(e+='[imagesrcset="'+Fl(a.imageSrcSet)+'"]',typeof a.imageSizes=="string"&&(e+='[imagesizes="'+Fl(a.imageSizes)+'"]')):e+='[href="'+Fl(l)+'"]';var n=e;switch(t){case"style":n=ou(l);break;case"script":n=gu(l)}et.has(n)||(l=H({rel:"preload",href:t==="image"&&a&&a.imageSrcSet?void 0:l,as:t},a),et.set(n,l),u.querySelector(e)!==null||t==="style"&&u.querySelector(ce(n))||t==="script"&&u.querySelector(ie(n))||(t=u.createElement("link"),Ml(t,"link",l),gl(t),u.head.appendChild(t)))}}function Ry(l,t){Ct.m(l,t);var a=Su;if(a&&l){var u=t&&typeof t.as=="string"?t.as:"script",e='link[rel="modulepreload"][as="'+Fl(u)+'"][href="'+Fl(l)+'"]',n=e;switch(u){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":n=gu(l)}if(!et.has(n)&&(l=H({rel:"modulepreload",href:l},t),et.set(n,l),a.querySelector(e)===null)){switch(u){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(a.querySelector(ie(n)))return}u=a.createElement("link"),Ml(u,"link",l),gl(u),a.head.appendChild(u)}}}function Qy(l,t,a){Ct.S(l,t,a);var u=Su;if(u&&l){var e=Qa(u).hoistableStyles,n=ou(l);t=t||"default";var f=e.get(n);if(!f){var c={loading:0,preload:null};if(f=u.querySelector(ce(n)))c.loading=5;else{l=H({rel:"stylesheet",href:l,"data-precedence":t},a),(a=et.get(n))&&$c(l,a);var i=f=u.createElement("link");gl(i),Ml(i,"link",l),i._p=new Promise(function(y,o){i.onload=y,i.onerror=o}),i.addEventListener("load",function(){c.loading|=1}),i.addEventListener("error",function(){c.loading|=2}),c.loading|=4,Un(f,t,u)}f={type:"stylesheet",instance:f,count:1,state:c},e.set(n,f)}}}function Xy(l,t){Ct.X(l,t);var a=Su;if(a&&l){var u=Qa(a).hoistableScripts,e=gu(l),n=u.get(e);n||(n=a.querySelector(ie(e)),n||(l=H({src:l,async:!0},t),(t=et.get(e))&&Fc(l,t),n=a.createElement("script"),gl(n),Ml(n,"link",l),a.head.appendChild(n)),n={type:"script",instance:n,count:1,state:null},u.set(e,n))}}function Zy(l,t){Ct.M(l,t);var a=Su;if(a&&l){var u=Qa(a).hoistableScripts,e=gu(l),n=u.get(e);n||(n=a.querySelector(ie(e)),n||(l=H({src:l,async:!0,type:"module"},t),(t=et.get(e))&&Fc(l,t),n=a.createElement("script"),gl(n),Ml(n,"link",l),a.head.appendChild(n)),n={type:"script",instance:n,count:1,state:null},u.set(e,n))}}function $s(l,t,a,u){var e=(e=Rt.current)?On(e):null;if(!e)throw Error(h(446));switch(l){case"meta":case"title":return null;case"style":return typeof a.precedence=="string"&&typeof a.href=="string"?(t=ou(a.href),a=Qa(e).hoistableStyles,u=a.get(t),u||(u={type:"style",instance:null,count:0,state:null},a.set(t,u)),u):{type:"void",instance:null,count:0,state:null};case"link":if(a.rel==="stylesheet"&&typeof a.href=="string"&&typeof a.precedence=="string"){l=ou(a.href);var n=Qa(e).hoistableStyles,f=n.get(l);if(f||(e=e.ownerDocument||e,f={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},n.set(l,f),(n=e.querySelector(ce(l)))&&!n._p&&(f.instance=n,f.state.loading=5),et.has(l)||(a={rel:"preload",as:"style",href:a.href,crossOrigin:a.crossOrigin,integrity:a.integrity,media:a.media,hrefLang:a.hrefLang,referrerPolicy:a.referrerPolicy},et.set(l,a),n||xy(e,l,a,f.state))),t&&u===null)throw Error(h(528,""));return f}if(t&&u!==null)throw Error(h(529,""));return null;case"script":return t=a.async,a=a.src,typeof a=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=gu(a),a=Qa(e).hoistableScripts,u=a.get(t),u||(u={type:"script",instance:null,count:0,state:null},a.set(t,u)),u):{type:"void",instance:null,count:0,state:null};default:throw Error(h(444,l))}}function ou(l){return'href="'+Fl(l)+'"'}function ce(l){return'link[rel="stylesheet"]['+l+"]"}function Fs(l){return H({},l,{"data-precedence":l.precedence,precedence:null})}function xy(l,t,a,u){l.querySelector('link[rel="preload"][as="style"]['+t+"]")?u.loading=1:(t=l.createElement("link"),u.preload=t,t.addEventListener("load",function(){return u.loading|=1}),t.addEventListener("error",function(){return u.loading|=2}),Ml(t,"link",a),gl(t),l.head.appendChild(t))}function gu(l){return'[src="'+Fl(l)+'"]'}function ie(l){return"script[async]"+l}function ks(l,t,a){if(t.count++,t.instance===null)switch(t.type){case"style":var u=l.querySelector('style[data-href~="'+Fl(a.href)+'"]');if(u)return t.instance=u,gl(u),u;var e=H({},a,{"data-href":a.href,"data-precedence":a.precedence,href:null,precedence:null});return u=(l.ownerDocument||l).createElement("style"),gl(u),Ml(u,"style",e),Un(u,a.precedence,l),t.instance=u;case"stylesheet":e=ou(a.href);var n=l.querySelector(ce(e));if(n)return t.state.loading|=4,t.instance=n,gl(n),n;u=Fs(a),(e=et.get(e))&&$c(u,e),n=(l.ownerDocument||l).createElement("link"),gl(n);var f=n;return f._p=new Promise(function(c,i){f.onload=c,f.onerror=i}),Ml(n,"link",u),t.state.loading|=4,Un(n,a.precedence,l),t.instance=n;case"script":return n=gu(a.src),(e=l.querySelector(ie(n)))?(t.instance=e,gl(e),e):(u=a,(e=et.get(n))&&(u=H({},a),Fc(u,e)),l=l.ownerDocument||l,e=l.createElement("script"),gl(e),Ml(e,"link",u),l.head.appendChild(e),t.instance=e);case"void":return null;default:throw Error(h(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(u=t.instance,t.state.loading|=4,Un(u,a.precedence,l));return t.instance}function Un(l,t,a){for(var u=a.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),e=u.length?u[u.length-1]:null,n=e,f=0;f title"):null)}function Vy(l,t,a){if(a===1||t.itemProp!=null)return!1;switch(l){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;switch(t.rel){case"stylesheet":return l=t.disabled,typeof t.precedence=="string"&&l==null;default:return!0}case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function lv(l){return!(l.type==="stylesheet"&&(l.state.loading&3)===0)}function Ly(l,t,a,u){if(a.type==="stylesheet"&&(typeof u.media!="string"||matchMedia(u.media).matches!==!1)&&(a.state.loading&4)===0){if(a.instance===null){var e=ou(u.href),n=t.querySelector(ce(e));if(n){t=n._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(l.count++,l=Nn.bind(l),t.then(l,l)),a.state.loading|=4,a.instance=n,gl(n);return}n=t.ownerDocument||t,u=Fs(u),(e=et.get(e))&&$c(u,e),n=n.createElement("link"),gl(n);var f=n;f._p=new Promise(function(c,i){f.onload=c,f.onerror=i}),Ml(n,"link",u),a.instance=n}l.stylesheets===null&&(l.stylesheets=new Map),l.stylesheets.set(a,t),(t=a.state.preload)&&(a.state.loading&3)===0&&(l.count++,a=Nn.bind(l),t.addEventListener("load",a),t.addEventListener("error",a))}}var kc=0;function Ky(l,t){return l.stylesheets&&l.count===0&&Bn(l,l.stylesheets),0kc?50:800)+t);return l.unsuspend=a,function(){l.unsuspend=null,clearTimeout(u),clearTimeout(e)}}:null}function Nn(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Bn(this,this.stylesheets);else if(this.unsuspend){var l=this.unsuspend;this.unsuspend=null,l()}}}var Hn=null;function Bn(l,t){l.stylesheets=null,l.unsuspend!==null&&(l.count++,Hn=new Map,t.forEach(Jy,l),Hn=null,Nn.call(l))}function Jy(l,t){if(!(t.state.loading&4)){var a=Hn.get(l);if(a)var u=a.get(null);else{a=new Map,Hn.set(l,a);for(var e=l.querySelectorAll("link[data-precedence],style[data-precedence]"),n=0;n"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(A)}catch(U){console.error(U)}}return A(),fi.exports=md(),fi.exports}var vd=sd();const yd="modulepreload",dd=function(A){return"/"+A},Av={},Uv=function(U,O,h){let B=Promise.resolve();if(O&&O.length>0){let L=function(I){return Promise.all(I.map(Hl=>Promise.resolve(Hl).then(H=>({status:"fulfilled",value:H}),H=>({status:"rejected",reason:H}))))};document.getElementsByTagName("link");const _=document.querySelector("meta[property=csp-nonce]"),ml=(_==null?void 0:_.nonce)||(_==null?void 0:_.getAttribute("nonce"));B=L(O.map(I=>{if(I=dd(I),I in Av)return;Av[I]=!0;const Hl=I.endsWith(".css"),H=Hl?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${I}"]${H}`))return;const K=document.createElement("link");if(K.rel=Hl?"stylesheet":yd,Hl||(K.as="script"),K.crossOrigin="",K.href=I,ml&&K.setAttribute("nonce",ml),document.head.appendChild(K),Hl)return new Promise((hl,Dl)=>{K.addEventListener("load",hl),K.addEventListener("error",()=>Dl(new Error(`Unable to preload CSS for ${I}`)))})}))}function C(L){const _=new Event("vite:preloadError",{cancelable:!0});if(_.payload=L,window.dispatchEvent(_),!_.defaultPrevented)throw L}return B.then(L=>{for(const _ of L||[])_.status==="rejected"&&C(_.reason);return U().catch(C)})},Nd={0:"Rất nhẹ",1:"Nhẹ",2:"Trung bình",3:"Nặng"},Hd="BS. Trần Minh Khoa",mi=[{id:"p-001",name:"Nguyễn Văn An",mrn:"BN-2024-1847",joint:"Khớp gối phải",status:"urgent",statusLabel:"Khẩn cấp",lastScan:"09:42"},{id:"p-002",name:"Trần Thị Bích",mrn:"BN-2024-1923",joint:"Khớp gối trái",status:"processing",statusLabel:"Đang xử lý",lastScan:"10:15"},{id:"p-003",name:"Lê Hoàng Minh",mrn:"BN-2024-2011",joint:"Khớp gối phải",status:"followup",statusLabel:"Theo dõi",lastScan:"Hôm qua"},{id:"p-004",name:"Phạm Thu Hà",mrn:"BN-2024-2088",joint:"Khớp gối trái",status:"ready",statusLabel:"Sẵn sàng",lastScan:"08:30"}],hd=[{value:"urgent",label:"Khẩn cấp",chipClass:"chip-urgent"},{value:"processing",label:"Đang xử lý",chipClass:"chip-processing"},{value:"followup",label:"Theo dõi",chipClass:"chip-followup"},{value:"ready",label:"Sẵn sàng",chipClass:"chip-success"}],Bd=["Khớp gối trái","Khớp gối phải","Khớp vai trái","Khớp vai phải","Khớp khuỷu trái","Khớp khuỷu phải","Khớp cổ tay trái","Khớp cổ tay phải"];function Tv(A){var U;return((U=hd.find(O=>O.value===A))==null?void 0:U.label)??A}function Sd(A){const U=new Date;if(A.getFullYear()===U.getFullYear()&&A.getMonth()===U.getMonth()&&A.getDate()===U.getDate())return A.toLocaleTimeString("vi-VN",{hour:"2-digit",minute:"2-digit",hour12:!1});const h=new Date(U);return h.setDate(U.getDate()-1),A.getFullYear()===h.getFullYear()&&A.getMonth()===h.getMonth()&&A.getDate()===h.getDate()?"Hôm qua":A.toLocaleDateString("vi-VN",{day:"2-digit",month:"2-digit",year:"numeric"})}function Ev(A){if(A.length===0)return"—";const U=Math.max(...A.map(O=>O.lastModified));return Sd(new Date(U))}const od=[".dcm",".dicom",".png",".jpg",".jpeg"],gd=new Set(["image/png","image/jpeg","application/dicom"]),qd=".dcm,.dicom,.png,.jpg,.jpeg,image/png,image/jpeg,application/dicom";function pd(A){const U=A.name.toLowerCase(),O=od.some(B=>U.endsWith(B)),h=A.type.length>0&&gd.has(A.type);return O||h}const bd="lumina-msk-scan-assets",zd=1,ma="assets";function vi(){return new Promise((A,U)=>{const O=indexedDB.open(bd,zd);O.onerror=()=>U(O.error??new Error("Failed to open scan assets DB")),O.onsuccess=()=>A(O.result),O.onupgradeneeded=()=>{const h=O.result;h.objectStoreNames.contains(ma)||h.createObjectStore(ma,{keyPath:"id"}).createIndex("patientId","patientId",{unique:!1})}})}function yi(A){return new Promise((U,O)=>{A.onsuccess=()=>U(A.result),A.onerror=()=>O(A.error??new Error("IndexedDB request failed"))})}function _v(A){return{id:A.id,patientId:A.patientId,fileName:A.fileName,size:A.size,lastModified:A.lastModified,mimeType:A.mimeType}}async function si(A){const U=await vi();try{const O=U.transaction(ma,"readonly").objectStore(ma).index("patientId");return(await yi(O.getAll(A))).map(_v)}finally{U.close()}}async function Mv(A,U){if(U.length===0)return[];const O=await vi();try{const h=O.transaction(ma,"readwrite"),B=h.objectStore(ma),C=[];for(const L of U){const _={id:crypto.randomUUID(),patientId:A,fileName:L.name,size:L.size,lastModified:L.lastModified,mimeType:L.type,blob:L};await yi(B.put(_)),C.push(_v(_))}return await new Promise((L,_)=>{h.oncomplete=()=>L(),h.onerror=()=>_(h.error??new Error("Failed to save scan assets"))}),C}finally{O.close()}}async function Nv(A){if(A.length===0)return;const U=await vi();try{const O=U.transaction(ma,"readwrite"),h=O.objectStore(ma);for(const B of A)await yi(h.delete(B));await new Promise((B,C)=>{O.oncomplete=()=>B(),O.onerror=()=>C(O.error??new Error("Failed to delete scan assets"))})}finally{U.close()}}async function Yd(A){const U=await si(A);await Nv(U.map(O=>O.id))}const Hv="lumina-msk-patients",Bv=jt.createContext(null);function Ad(){try{const A=localStorage.getItem(Hv);if(!A)return mi;const U=JSON.parse(A);return Array.isArray(U)&&U.length>0?U:mi}catch{return mi}}function Dv(A){localStorage.setItem(Hv,JSON.stringify(A))}function Td(){return`p-${crypto.randomUUID().slice(0,8)}`}function Ed({children:A}){const[U,O]=jt.useState(Ad),h=jt.useCallback(async _=>{const ml=Td();await Mv(ml,_.scanFiles);const I={id:ml,name:_.name.trim(),mrn:_.mrn.trim(),joint:_.joint,status:_.status,statusLabel:Tv(_.status),lastScan:Ev(_.scanFiles)};return O(Hl=>{const H=[I,...Hl];return Dv(H),H}),I},[]),B=jt.useCallback(async _=>{await Nv(_.removedAssetIds),_.newScanFiles.length>0&&await Mv(_.patientId,_.newScanFiles);const ml=await si(_.patientId),I=_.newScanFiles.map(hl=>new File([],hl.name,{lastModified:hl.lastModified,type:hl.type})),H=[...ml.map(hl=>new File([],hl.fileName,{lastModified:hl.lastModified})),...I],K={id:_.patientId,name:_.name.trim(),mrn:_.mrn.trim(),joint:_.joint,status:_.status,statusLabel:Tv(_.status),lastScan:H.length>0?Ev(H):"—"};return O(hl=>{const Dl=hl.map(Ol=>Ol.id===_.patientId?K:Ol);return Dv(Dl),Dl}),K},[]),C=jt.useCallback(_=>si(_),[]),L=jt.useMemo(()=>({patients:U,addPatient:h,updatePatient:B,getPatientScanAssets:C}),[U,h,B,C]);return Nl.jsx(Bv.Provider,{value:L,children:A})}function Cd(){const A=jt.useContext(Bv);if(!A)throw new Error("usePatientStore must be used within PatientStoreProvider");return A}const Md=Ov.lazy(()=>Uv(()=>import("./PatientWorklistPage-D1i7UsXA.js"),__vite__mapDeps([0,1,2]))),Dd=Ov.lazy(()=>Uv(()=>import("./ClinicalWorkspacePage-BRH1XOmJ.js"),__vite__mapDeps([3,1,2,4])));function Od(){return Nl.jsx("div",{style:{padding:"2rem",textAlign:"center"},children:"Loading…"})}function Ud(){return Nl.jsx(Ed,{children:Nl.jsx(jt.Suspense,{fallback:Nl.jsx(Od,{}),children:Nl.jsxs(ad,{children:[Nl.jsx(ei,{path:"/",element:Nl.jsx(Md,{})}),Nl.jsx(ei,{path:"/workspace/:patientId",element:Nl.jsx(Dd,{})}),Nl.jsx(ei,{path:"*",element:Nl.jsx(ud,{to:"/",replace:!0})})]})})})}vd.createRoot(document.getElementById("root")).render(Nl.jsx(jt.StrictMode,{children:Nl.jsx(ed,{children:Nl.jsx(Ud,{})})}));export{qd as A,Nd as G,Bd as J,Hd as M,hd as P,Yd as c,pd as i,Nl as j,Cd as u}; diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/index-LQC2ade3.css b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/index-LQC2ade3.css deleted file mode 100644 index a508cd3..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/index-LQC2ade3.css +++ /dev/null @@ -1 +0,0 @@ -:root{--color-primary: #056b58;--color-on-primary: #ffffff;--color-primary-container: #76c8b1;--color-on-primary-container: #005344;--color-secondary: #23686c;--color-on-secondary: #ffffff;--color-secondary-container: #adeef2;--color-tertiary: #5f5983;--color-tertiary-container: #bab3e2;--color-error: #ba1a1a;--color-error-container: #ffdad6;--color-surface: #f5faf9;--color-surface-container: #eaefee;--color-surface-container-high: #e4e9e8;--color-on-surface: #171d1c;--color-on-surface-variant: #3e4945;--color-outline: #6f7975;--color-outline-variant: #bec9c4;--glass-bg: rgba(255, 255, 255, .55);--glass-border: rgba(5, 107, 88, .18);--glass-shadow: 0 10px 30px rgba(77, 141, 145, .08);--blur-glass: blur(12px);--radius-sm: .25rem;--radius-md: .75rem;--radius-lg: 1rem;--radius-full: 9999px;--font-headline: "Manrope", system-ui, sans-serif;--font-body: "Work Sans", system-ui, sans-serif;--space-sm: 8px;--space-md: 16px;--space-lg: 24px;--topbar-h: 56px;--bottombar-h: 40px}*,*:before,*:after{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font-body);font-size:16px;line-height:1.5;color:var(--color-on-surface);background:radial-gradient(ellipse at 20% 0%,rgba(118,200,177,.25),transparent 50%),radial-gradient(ellipse at 80% 100%,rgba(173,238,242,.2),transparent 45%),var(--color-surface);-webkit-font-smoothing:antialiased}h1,h2,h3,h4{font-family:var(--font-headline);margin:0}button,input,textarea,select{font:inherit}a{color:inherit;text-decoration:none}.glass{background:var(--glass-bg);backdrop-filter:var(--blur-glass);-webkit-backdrop-filter:var(--blur-glass);border:1px solid var(--glass-border);box-shadow:var(--glass-shadow)}.glass-elevated{background:#ffffffb8;backdrop-filter:var(--blur-glass);-webkit-backdrop-filter:var(--blur-glass);border:1px solid rgba(5,107,88,.22);box-shadow:0 16px 40px #4d8d911f}.tnum{font-variant-numeric:tabular-nums}.app-shell{display:flex;flex-direction:column;min-height:100%}.app-main{flex:1;display:flex;flex-direction:column;min-height:0}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:10px 18px;border-radius:var(--radius-md);border:none;cursor:pointer;font-weight:600;font-size:14px;transition:transform .15s,box-shadow .15s,opacity .15s}.btn:active{transform:scale(.98)}.btn-primary{background:var(--color-secondary);color:var(--color-on-secondary);box-shadow:inset 0 1px #fff3}.btn-primary:hover{background:#1a5559}.btn-secondary{background:var(--glass-bg);-webkit-backdrop-filter:var(--blur-glass);backdrop-filter:var(--blur-glass);color:var(--color-primary);border:1px solid var(--color-primary)}.btn-ghost{background:transparent;color:var(--color-on-surface-variant);border:1px solid var(--color-outline-variant)}.chip{display:inline-flex;align-items:center;padding:4px 10px;border-radius:var(--radius-full);font-size:12px;font-weight:600;letter-spacing:.02em}.chip-urgent{background:var(--color-error-container);color:var(--color-error)}.chip-processing{background:var(--color-secondary-container);color:var(--color-secondary)}.chip-followup{background:var(--color-tertiary-container);color:var(--color-tertiary)}.chip-success{background:var(--color-primary-container);color:var(--color-on-primary-container)}.icon-btn{width:40px;height:40px;border-radius:var(--radius-full);border:1px solid var(--glass-border);background:var(--glass-bg);-webkit-backdrop-filter:var(--blur-glass);backdrop-filter:var(--blur-glass);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:var(--color-secondary)}.icon-btn:hover{border-color:var(--color-primary)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0} diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/llm.worker-D-LkqiEp.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/llm.worker-D-LkqiEp.js deleted file mode 100644 index 27b83d9..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/llm.worker-D-LkqiEp.js +++ /dev/null @@ -1,15 +0,0 @@ -var me=typeof self<"u"?self:{};function on(e,t){e:{for(var r=["CLOSURE_FLAGS"],n=me,i=0;i>6|192;else{if(t>=55296&&t<=57343){if(t<=56319&&i=56320&&o<=57343){t=1024*(t-55296)+o-56320+65536,n[r++]=t>>18|240,n[r++]=t>>12&63|128,n[r++]=t>>6&63|128,n[r++]=63&t|128;continue}i--}t=65533}n[r++]=t>>12|224,n[r++]=t>>6&63|128}n[r++]=63&t|128}}e=r===n.length?n:n.subarray(0,r)}return e}var ci=on(610401301,!1),hr=on(748402147,!0);function cr(){var e=me.navigator;return e&&(e=e.userAgent)?e:""}var Dt,fr=me.navigator;Dt=fr&&fr.userAgentData||null;var sn={},Pe=null;function fi(e){var t=e.length,r=3*t/4;r%3?r=Math.floor(r):"=.".indexOf(e[t-1])!=-1&&(r="=.".indexOf(e[t-2])!=-1?r-2:r-1);var n=new Uint8Array(r),i=0;return(function(o,a){function s(l){for(;u>4),f!=64&&(a(h<<4&240|f>>2),d!=64&&a(f<<6&192|d))}})(e,function(o){n[i++]=o}),i!==r?n.subarray(0,i):n}function un(){if(!Pe){Pe={};var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""),t=["+/=","+/","-_=","-_.","-_"];for(let r=0;r<5;r++){let n=e.concat(t[r].split(""));sn[r]=n;for(let i=0;i0)&&(cr().indexOf("Trident")!=-1||cr().indexOf("MSIE")!=-1))&&typeof btoa=="function",dr=/[-_.]/g,pi={"-":"+",_:"/",".":"="};function mi(e){return pi[e]||""}function pr(e){if(!ln)return fi(e);e=dr.test(e)?e.replace(dr,mi):e,e=atob(e);var t=new Uint8Array(e.length);for(let r=0;r=t||(r[e]=n+1,cn(e=Error(),"incident"),(function(i){me.setTimeout(()=>{throw i},0)})(e))}}function Yt(){return typeof BigInt=="function"}var ft=typeof Symbol=="function"&&typeof Symbol()=="symbol";function Ge(e,t,r=!1){return typeof Symbol=="function"&&typeof Symbol()=="symbol"?r&&Symbol.for&&e?Symbol.for(e):e!=null?Symbol(e):Symbol():t}var ge,vi=Ge("jas",void 0,!0),Te=Ge(void 0,"1oa"),Ct=Ge(void 0,"0ubsb"),_i=Ge(void 0,"0actk"),dt=Ge("m_m","qa",!0),fn={ha:{value:0,configurable:!0,writable:!0,enumerable:!1}},dn=Object.defineProperties,y=ft?vi:"ha",mr=[];function pn(e,t){ft||y in e||dn(e,fn),e[y]|=t}function j(e,t){ft||y in e||dn(e,fn),e[y]=t}j(mr,7),ge=Object.freeze(mr);var pt={};function z(e,t){return t===void 0?e.i!==ye&&!!(2&e.m[y]):!!(2&t)&&e.i!==ye}var ye={},wi=Object.freeze({});function mt(e){return e.pa=!0,e}var bi=mt(e=>typeof e=="number"),mn=mt(e=>typeof e=="string"),Si=mt(e=>typeof e=="boolean"),gt=typeof me.BigInt=="function"&&typeof me.BigInt(0)=="bigint",Ti=mt(e=>gt?e>=Ai&&e<=Pi:e[0]==="-"?gr(e,Ei):gr(e,Ii)),Ei=Number.MIN_SAFE_INTEGER.toString(),Ai=gt?BigInt(Number.MIN_SAFE_INTEGER):void 0,Ii=Number.MAX_SAFE_INTEGER.toString(),Pi=gt?BigInt(Number.MAX_SAFE_INTEGER):void 0;function gr(e,t){if(e.length>t.length)return!1;if(e.lengthi)return!1;if(n>>0;b=t,S=(e-t)/4294967296>>>0}function De(e){if(e<0){yr(-e);let[t,r]=Jt(b,S);b=t>>>0,S=r>>>0}else yr(e)}function gn(e,t){var r=4294967296*t+(e>>>0);return Number.isSafeInteger(r)?r:et(e,t)}function et(e,t){if(e>>>=0,(t>>>=0)<=2097151)var r=""+(4294967296*t+e);else Yt()?r=""+(BigInt(t)<>>24|t<<8))+6710656*(t=t>>16&65535),r+=8147497*t,t*=2,e>=1e7&&(r+=e/1e7>>>0,e%=1e7),r>=1e7&&(t+=r/1e7>>>0,r%=1e7),r=t+vr(r)+vr(e));return r}function vr(e){return e=String(e),"0000000".slice(e.length)+e}function yt(e){if(e.length<16)De(Number(e));else if(Yt())e=BigInt(e),b=Number(e&BigInt(4294967295))>>>0,S=Number(e>>BigInt(32)&BigInt(4294967295));else{let t=+(e[0]==="-");S=b=0;let r=e.length;for(let n=t,i=(r-t)%6+t;i<=r;n=i,i+=6){let o=Number(e.slice(n,i));S*=1e6,(b=1e6*b+o)>=4294967296&&(S+=Math.trunc(b/4294967296),S>>>=0,b>>>=0)}if(t){let[n,i]=Jt(b,S);b=n,S=i}}}function Jt(e,t){return t=~t,e?e=1+~e:t+=1,[e,t]}function Oe(e){return Array.prototype.slice.call(e)}var Oi=typeof BigInt=="function"?BigInt.asIntN:void 0,ki=typeof BigInt=="function"?BigInt.asUintN:void 0,tt=Number.isSafeInteger,we=Number.isFinite,rt=Math.trunc;function kt(e){if(e!=null&&typeof e!="number")throw Error(`Value of float/double field must be a number, found ${typeof e}: ${e}`);return e}function yn(e){return e==null||typeof e=="number"?e:e==="NaN"||e==="Infinity"||e==="-Infinity"?Number(e):void 0}function oe(e){if(e!=null&&typeof e!="boolean"){var t=typeof e;throw Error(`Expected boolean but got ${t!="object"?t:e?Array.isArray(e)?"array":t:"null"}: ${e}`)}return e}function re(e){return e==null||typeof e=="boolean"?e:typeof e=="number"?!!e:void 0}var Mi,xi,Bi=/^-?([1-9][0-9]*|0)(\.[0-9]+)?$/;function vn(e){switch(typeof e){case"bigint":return!0;case"number":return we(e);case"string":return Bi.test(e);default:return!1}}function _n(e){if(typeof e!="number"||!we(e))throw Fe("int32");return 0|e}function Gt(e){return e==null?e:_n(e)}function be(e){if(e==null)return e;if(typeof e=="string"&&e)e=+e;else if(typeof e!="number")return;return we(e)?0|e:void 0}function nt(e){if(e==null)return e;if(typeof e=="string"&&e)e=+e;else if(typeof e!="number")return;return we(e)?e>>>0:void 0}function Xt(e){return e==null||typeof e=="string"?e:void 0}function wn(e,t,r){if(e!=null&&e[dt]===pt)return e;if(Array.isArray(e)){var n=0|e[y];return(r=n|32&r|2&r)!==n&&j(e,r),new t(e)}}function ji(e){return e}function vt(e,t,r,n){var i=n!==void 0;n=!!n;var o=[],a=e.length,s=4294967295,u=!1,l=!!(64&t),h=l?128&t?0:-1:void 0;if(!(1&t)){var f=a&&e[a-1];f!=null&&typeof f=="object"&&f.constructor===Object?s=--a:f=void 0,!l||128&t||i||(u=!0,s=ji(s-h,h,e,f,void 0)+h)}for(t=void 0,i=0;i=s){let m=i-h;(t??(t={}))[m]=d}else o[i]=d}if(f)for(let d in f){if((e=f[d])==null||(e=r(e,n))==null)continue;let m;a=+d,l&&!Number.isNaN(a)&&(m=a+h)>2];o=r[(3&o)<<4|a>>4],a=r[(15&a)<<2|s>>6],s=r[63&s],n[h++]=u+o+a+s}switch(u=0,s=i,t.length-l){case 2:s=r[(15&(u=t[l+1]))<<2]||i;case 1:t=t[l],n[h]=r[t>>2]+r[(3&t)<<4|u>>4]+s+i}t=n.join("")}e=e.i=t}return e}return}return e}function bn(e){return vt(e=e.m,0|e[y],Qt)}function Sn(e,t,r,n=0){if(e==null){var i=32;r?(e=[r],i|=128):e=[],t&&(i=-16760833&i|(1023&t)<<14)}else{if(!Array.isArray(e))throw Error("narr");if(i=0|e[y],hr&&1&i)throw Error("rfarr");if(2048&i&&!(2&i)&&(function(){if(hr)throw Error("carr");Ze(_i,5)})(),256&i)throw Error("farr");if(64&i)return(i|n)!==i&&j(e,i|n),e;if(r&&(i|=128,r!==e[0]))throw Error("mid");e:{i|=64;var o=(r=e).length;if(o){var a=o-1;let u=r[a];if(u!=null&&typeof u=="object"&&u.constructor===Object){if((a-=t=128&i?0:-1)>=1024)throw Error("pvtlmt");for(var s in u)(o=+s)1024)throw Error("spvt");i=-16760833&i|(1023&s)<<14}}}return j(e,64|i|n),e}function Ni(e,t){if(typeof e!="object")return e;if(Array.isArray(e)){var r=0|e[y];return e.length===0&&1&r?e=void 0:2&r||(!t||4096&r||16&r?e=it(e,r,!1,t&&!(16&r)):(pn(e,34),4&r&&Object.freeze(e))),e}return e!=null&&e[dt]===pt?z(e,r=0|(t=e.m)[y])?e:En(e,t,r)?Tn(e,t):it(t,r):e instanceof J?e:void 0}function Tn(e,t,r){return e=new e.constructor(t),r&&(e.i=ye),e.o=ye,e}function it(e,t,r,n){return n??(n=!!(34&t)),e=vt(e,t,Ni,n),n=32,r&&(n|=2),j(e,t=16769217&t|n),e}function _t(e){if(e.i!==ye)return!1;var t=e.m;return pn(t=it(t,0|t[y]),2048),e.m=t,e.i=void 0,e.o=void 0,!0}function Se(e){if(!_t(e)&&z(e,0|e.m[y]))throw Error()}function ve(e,t){t===void 0&&(t=0|e[y]),32&t&&!(4096&t)&&j(e,4096|t)}function En(e,t,r){return!!(2&r)||!(!(32&r)||4096&r)&&(j(t,2|r),e.i=ye,!0)}function V(e,t,r){if((e=wt(e.m,t,void 0,r))!==null)return e}function wt(e,t,r,n){if(t===-1)return null;var i=t+(r?0:-1),o=e.length-1;if(!(o<1+(r?0:-1))){if(i>=o){var a=e[o];if(a!=null&&typeof a=="object"&&a.constructor===Object){r=a[t];var s=!0}else{if(i!==o)return;r=a}}else r=e[i];if(n&&r!=null){if((n=n(r))==null)return n;if(!Object.is(n,r))return s?a[t]=n:e[i]=n,n}return r}}function ae(e,t,r){Se(e),W(e=e.m,0|e[y],t,r)}function W(e,t,r,n,i){var o=r+-1,a=e.length-1;if(a>=0&&o>=a){let s=e[a];if(s!=null&&typeof s=="object"&&s.constructor===Object)return s[r]=n,t}return o<=a?(e[o]=n,t):(n!==void 0&&(r>=(a=(t??(t=0|e[y]))>>14&1023||536870912)?n!=null&&(e[a+-1]={[r]:n}):e[o]=n),t)}function An(e,t,r,n,i){var o=e.m,a=0|o[y];n=z(e,a)?1:n,i=!!i||n===3,n===2&&_t(e)&&(a=0|(o=e.m)[y]);var s=(e=Pn(o,t))===ge?7:0|e[y],u=Ln(s,a),l=!(4&u);if(l){4&u&&(e=Oe(e),s=0,u=ke(u,a),a=W(o,a,t,e));let h=0,f=0;for(;h{var m=wn(d,l,u);return f=m!==d&&m!=null,m}))!=null)return f&&!z(h)&&ve(s,u),h})(n,i,t,r),t==null)return t;if(!z(e,i=0|n[y])){var o,a=t;let s=a.m,u=0|s[y];(o=z(a,u)?En(a,s,u)?Tn(a,s,!0):new a.constructor(it(s,u,!1)):a)!==t&&(_t(e)&&(i=0|(n=e.m)[y]),ve(n,i=W(n,i,r,t=o)))}return t}function kn(e){return e==null&&(e=void 0),e}function te(e,t,r){return ae(e,t,r=kn(r)),r&&!z(r)&&ve(e.m),e}function ke(e,t){return-273&(2&t?2|e:-3&e)}function Le(e,t,r,n){var i=n;Se(e);var o=n=e.m,a=0|n[y],s=z(e,a)?1:2;s===2&&_t(e)&&(a=0|(o=e.m)[y]);var u=(e=Pn(o,t))===ge?7:0|e[y],l=Ln(u,a),h=!(4&l);if(h){var f=e,d=a;let m=!!(2&l);m&&(d|=2);let _=!m,O=!0,k=0,M=0;for(;k>>=0}ae(e,t,r)}function U(e,t,r){if(r!=null&&typeof r!="string")throw Error();G(e,t,r,"")}function p(e,t,r){if(Se(e),t=(e=An(e,t,Xt,2,!0)).push,typeof r!="string")throw Error();t.call(e,r)}var ue=class{constructor(e,t,r){if(this.buffer=e,r&&!t)throw Error()}};function br(e){if(typeof e=="string")return new ue(pr(e),!0);if(Array.isArray(e))return new ue(new Uint8Array(e),!0);if(e.constructor===Uint8Array)return new ue(e,!1);if(e.constructor===ArrayBuffer)return e=new Uint8Array(e),new ue(e,!1);if(e.constructor===J){hn(Ue);var t=e.i;return t=((t=t==null||Rt(t)?t:typeof t=="string"?pr(t):null)==null?t:e.i=t)||new Uint8Array(0),new ue(t,!0,e)}if(e instanceof Uint8Array)return e=e.constructor===Uint8Array?e:new Uint8Array(e.buffer,e.byteOffset,e.byteLength),new ue(e,!1);throw Error()}function Ui(e){return new ot(4294967295&e,Math.floor(e/4294967296))}function Sr(e){return e?/^\d+$/.test(e)?(yt(e),new ot(b,S)):null:Fi||(Fi=new ot(0,0))}var Fi,ot=class{constructor(e,t){this.j=e>>>0,this.i=t>>>0}};function Di(e){return new at(4294967295&e,Math.floor(e/4294967296))}function Tr(e){return e?/^-?\d+$/.test(e)?(yt(e),new at(b,S)):null:Ri||(Ri=new at(0,0))}var Ri,Er,Ar,Ir,Mt,Pr,Ae,We,at=class{constructor(e,t){this.j=e>>>0,this.i=t>>>0}};function Mn(e,t,r){return typeof BigInt64Array<"u"?(Ae||(Ae=new BigInt64Array(1),We=new Uint32Array(Ae.buffer),Ae[0]=BigInt(1),Pr=We[0]===1),Ae[0]=e,new t(We[e=Pr?0:1],We[1-e])):(Mt||(Er=BigInt(Number.MIN_SAFE_INTEGER),Ar=BigInt(Number.MAX_SAFE_INTEGER),Ir=BigInt(4294967295),Mt=BigInt(32)),e>=Er&&e<=Ar?r(Number(e)):(e=BigInt.asUintN(64,e),new t(Number(e&Ir),Number(e>>Mt))))}function de(e,t,r){for(;r>0||t>127;)e.i.push(127&t|128),t=(t>>>7|r<<25)>>>0,r>>>=7;e.i.push(t)}function bt(e,t){for(;t>127;)e.i.push(127&t|128),t>>>=7;e.i.push(t)}function St(e,t){if(t>=0)bt(e,t);else{for(let r=0;r<9;r++)e.i.push(127&t|128),t>>=7;e.i.push(1)}}var Ci=class{constructor(){this.i=[]}length(){return this.i.length}end(){var e=this.i;return this.i=[],e}};function st(e,t){t.length!==0&&(e.l.push(t),e.j+=t.length)}function Q(e,t,r){bt(e.i,8*t+r)}function Tt(e,t){return Q(e,t,2),t=e.i.end(),st(e,t),t.push(e.j),t}function Et(e,t){var r=t.pop();for(r=e.j+e.i.length()-r;r>127;)t.push(127&r|128),r>>>=7,e.j++;t.push(r),e.j++}function ut(e,t,r){Q(e,t,2),bt(e.i,r.length),st(e,e.i.end()),st(e,r)}var Gi=class{constructor(){this.l=[],this.j=0,this.i=new Ci}};function K(){var e=class{constructor(){throw Error()}};return Object.setPrototypeOf(e,e.prototype),e}var Ve=K(),xn=K(),At=K(),It=K(),Bn=K(),Vi=K(),$i=K(),jn=K(),zi=K(),Zt=K(),A=class{constructor(e,t){this.m=Sn(e,t,void 0,2048)}toJSON(){return bn(this)}};A.prototype[dt]=pt,A.prototype.toString=function(){return this.m.toString()};var q=class{constructor(e,t){this.i=e,e=Ve,this.j=!!e&&t===e||!1}};function Nn(e,t,r,n,i){(t=Fn(t,n))!=null&&(r=Tt(e,r),i(t,e),Et(e,r))}var Un,Hi=new q(Nn,Ve),Wi=new q(Nn,Ve),Lr=Symbol(),Or=Symbol();function Pt(e){var t=Ki,r=qi,n=e[Lr];if(n)return n;(n={}).oa=e,n.W=(function(h){switch(typeof h){case"boolean":return Mi||(Mi=[0,void 0,!0]);case"number":return h>0?void 0:h===0?xi||(xi=[0,void 0]):[-h,void 0];case"string":return[0,h];case"object":return h}})(e[0]);var i=e[1],o=1;i&&i.constructor===Object&&(n.ca=i,typeof(i=e[++o])=="function"&&(n.ia=!0,Un??(Un=e[o+1]),i=e[o+=2]));for(var a={};i&&Array.isArray(i)&&i.length&&typeof i[0]=="number"&&i[0]>0;){for(var s=0;sa(s,u,l,o||(o=Pt(n).W),i||(i=Dn(n)))}function Dn(e){var t=e[Or];if(!t){let r=Pt(e);t=(n,i)=>Rn(n,i,r),e[Or]=t}return t}function Rn(e,t,r){(function(n,i,o){var a,s=128&i?0:-1,u=n.length;(a=!!u)&&(a=(a=n[u-1])!=null&&typeof a=="object"&&a.constructor===Object);var l=u+(a?-1:0);for(i=128&i?1:0;i{if(i!=null){var o=(function(a,s){var u=a[s];if(u)return u;if((u=a.ca)&&(u=u[s])){var l=(u=Array.isArray(u)?u[0]instanceof q?u:[Wi,u]:[u,void 0])[0].i;if(u=u[1]){let h=Dn(u),f=Pt(u).W;u=a.ia?Un(f,h):(d,m,_)=>l(d,m,_,f,h)}else u=l;return a[s]=u}})(r,n);o?o(t,i,n):n<500||Ze(Ct,3)}})}var xt,ie=0,le=ie;if(mn(le)){if(!/^\s*(?:-?[1-9]\d*|0)?\s*$/.test(le))throw Error(String(le))}else if((xt=bi(le))&&(xt=!Number.isSafeInteger(le)),xt)throw Error(String(le));function er(e,t){if(Array.isArray(t)){var r=0|t[y];if(4&r)return t;for(var n=0,i=0;n>>0&255),e.i.push(t>>>8&255),e.i.push(t>>>16&255),e.i.push(t>>>24&255))}function tr(e,t,r){(t=be(t))!=null&&t!=null&&(Q(e,r,0),St(e.i,t))}function Gn(e,t,r){(t=re(t))!=null&&(Q(e,r,0),e.i.i.push(t?1:0))}function rr(e,t,r){(t=Xt(t))!=null&&ut(e,r,an(t))}function Vn(e,t,r,n,i){(t=Fn(t,n))!=null&&(r=Tt(e,r),i(t,e),Et(e,r))}function $n(e,t,r){(t=nt(t))!=null&&t!=null&&(Q(e,r,0),bt(e.i,t))}function zn(e,t,r){(t=be(t))!=null&&(t=parseInt(t,10),Q(e,r,0),St(e.i,t))}gt||(ie=Si(ie)?ie?"1":"0":mn(ie)?ie.trim()||"0":String(ie));var se,Ie=P(Cn,jn),Qe=P(Cn,jn),Bt=P(function(e,t,r){if(t=(function(n){if(n==null)return n;var i=typeof n;if(i==="bigint")return String(Oi(64,n));if(vn(n)){if(i==="string"){if(i=rt(Number(n)),tt(i))n=String(i);else if((i=n.indexOf("."))!==-1&&(n=n.substring(0,i)),i=n.length,!(n[0]==="-"?i<20||i===20&&n<="-9223372036854775808":i<19||i===19&&n<="9223372036854775807"))if(yt(n),n=b,2147483648&(i=S))if(Yt())n=""+(BigInt(0|i)<>>0));else{let[a,s]=Jt(n,i);n="-"+et(a,s)}else n=et(n,i);return n}if(i==="number"){if(n=rt(n),!tt(n)){De(n),i=b;var o=S;(n=2147483648&o)&&(o=~o>>>0,(i=1+~i>>>0)==0&&(o=o+1>>>0)),n=typeof(i=gn(i,o))=="number"?n?-i:i:n?"-"+i:i}return n}}})(t),t!=null&&(typeof t=="string"&&Tr(t),t!=null))switch(Q(e,r,0),typeof t){case"number":e=e.i,De(t),de(e,b,S);break;case"bigint":r=Mn(t,at,Di),de(e.i,r.j,r.i);break;default:r=Tr(t),de(e.i,r.j,r.i)}},Vi),Yi=P(function(e,t,r){if(t=(function(n){if(n==null)return n;var i=typeof n;if(i==="bigint")return String(ki(64,n));if(vn(n)){if(i==="string")return i=rt(Number(n)),tt(i)&&i>=0?n=String(i):((i=n.indexOf("."))!==-1&&(n=n.substring(0,i)),(i=n[0]!=="-"&&((i=n.length)<20||i===20&&n<="18446744073709551615"))||(yt(n),n=et(b,S))),n;if(i==="number")return(n=rt(n))>=0&&tt(n)||(De(n),n=gn(b,S)),n}})(t),t!=null&&(typeof t=="string"&&Sr(t),t!=null))switch(Q(e,r,0),typeof t){case"number":e=e.i,De(t),de(e,b,S);break;case"bigint":r=Mn(t,ot,Ui),de(e.i,r.j,r.i);break;default:r=Sr(t),de(e.i,r.j,r.i)}},$i),I=P(tr,It);se=new q(function(e,t,r){if((t=er(be,t))!=null&&t.length){r=Tt(e,r);for(let n=0;ni;r=Mr.createPolicy("goog#html",{createHTML:n,createScript:n,createScriptURL:n})}catch{}return r})()),e=(t=Nt)?t.createScriptURL(e):e,new Qi(e)}function Ke(e,...t){if(t.length===0)return xr(e[0]);var r=e[0];for(let n=0;n{if(a.cache.length>0)u.enqueue(a.cache.shift());else{var{value:l,done:h}=await e.read();l&&(s.active&&s.cache.push(l),a.active&&u.enqueue(l)),h&&u.close()}}),await i},cancel(){a.active=!1,a.cache.length=0,s.active||e.cancel()}})}var r={cache:[],active:!0},n={cache:[],active:!0},i=Promise.resolve(),o=t(r,n);return r=t(n,r),[o.getReader(),r.getReader()]}async function Cr(e,t){for(var r=new Uint8Array(t),n=0;n{var t=new TextEncoder().encode("TFL3").length;return e=await Cr(e,t+4),new TextDecoder("utf-8").decode(e.subarray(4,t+4))==="TFL3"}],[1,async e=>(e=await Cr(e,6))[4]===80&&e[5]===75]];function ro(){var e=navigator;return typeof OffscreenCanvas<"u"&&(!(function(t=navigator){return(t=t.userAgent).includes("Safari")&&!t.includes("Chrome")})(e)||!!((e=e.userAgent.match(/Version\/([\d]+).*Safari/))&&e.length>=1&&Number(e[1])>=17))}async function Gr(e){if(typeof importScripts!="function"){let t=document.createElement("script");return t.src=e.toString(),t.crossOrigin="anonymous",new Promise((r,n)=>{t.addEventListener("load",()=>{r()},!1),t.addEventListener("error",i=>{n(i)},!1),document.body.appendChild(t)})}try{importScripts(e.toString())}catch(t){if(!(t instanceof TypeError))throw t;{let r=self.import;r?await r(e.toString()):await import(e.toString())}}}function c(e,t,r){e.o||console.error("No wasm multistream support detected: ensure dependency inclusion of :gl_graph_runner_internal_multi_input target"),r(t=e.h.stringToNewUTF8(t)),e.h._free(t)}function Vr(e,t,r){e.o||console.error("No wasm multistream support detected: ensure dependency inclusion of :gl_graph_runner_internal_multi_input target");var n=new Uint32Array(t.length);for(let i=0;i>2),r(t);for(let i of n)e.h._free(i);e.h._free(t)}function ee(e,t,r){e.h.simpleListeners=e.h.simpleListeners||{},e.h.simpleListeners[t]=r}function ne(e,t,r){var n=[];e.h.simpleListeners=e.h.simpleListeners||{},e.h.simpleListeners[t]=(i,o,a)=>{o?(r(n,a),n=[]):n.push(i)}}var $r,no=($r=class{constructor(e,t){this.l=!0,this.h=e,this.i=null,this.j=0,this.o=typeof this.h._addIntToInputStream=="function",t!==void 0?this.h.canvas=t:ro()?this.h.canvas=new OffscreenCanvas(1,1):(console.warn("OffscreenCanvas not supported and GraphRunner constructor glCanvas parameter is undefined. Creating backup canvas."),this.h.canvas=document.createElement("canvas"))}async initializeGraph(e){var t=await(await fetch(e)).arrayBuffer();e=!(e.endsWith(".pbtxt")||e.endsWith(".textproto")),this.setGraph(new Uint8Array(t),e)}setGraphFromString(e){this.setGraph(new TextEncoder().encode(e),!1)}setGraph(e,t){var r=e.length,n=this.h._malloc(r);this.h.HEAPU8.set(e,n),t?this.h._changeBinaryGraph(r,n):this.h._changeTextGraph(r,n),this.h._free(n)}configureAudio(e,t,r,n,i){this.h._configureAudio||console.warn('Attempting to use configureAudio without support for input audio. Is build dep ":gl_graph_runner_audio" missing?'),c(this,n||"input_audio",o=>{c(this,i=i||"audio_header",a=>{this.h._configureAudio(o,a,e,t??0,r)})})}setAutoResizeCanvas(e){this.l=e}setAutoRenderToScreen(e){this.h._setAutoRenderToScreen(e)}setGpuBufferVerticalFlip(e){this.h.gpuOriginForWebTexturesIsBottomLeft=e}attachErrorListener(e){this.h.errorListener=e}attachEmptyPacketListener(e,t){this.h.emptyPacketListeners=this.h.emptyPacketListeners||{},this.h.emptyPacketListeners[e]=t}addAudioToStream(e,t,r){this.addAudioToStreamWithShape(e,0,0,t,r)}addAudioToStreamWithShape(e,t,r,n,i){var o=4*e.length;this.j!==o&&(this.i&&this.h._free(this.i),this.i=this.h._malloc(o),this.j=o),this.h.HEAPF32.set(e,this.i/4),c(this,n,a=>{this.h._addAudioToInputStream(this.i,t,r,a,i)})}addGpuBufferToStream(e,t,r){c(this,t,n=>{if(!this.h.canvas)throw Error("No OpenGL canvas configured.");n?this.h._bindTextureToStream(n):this.h._bindTextureToCanvas();var i=this.h.canvas.getContext("webgl2")||this.h.canvas.getContext("webgl");if(!i)throw Error("Failed to obtain WebGL context from the provided canvas. `getContext()` should only be invoked with `webgl` or `webgl2`.");this.h.gpuOriginForWebTexturesIsBottomLeft&&i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL,!0),i.texImage2D(i.TEXTURE_2D,0,i.RGBA,i.RGBA,i.UNSIGNED_BYTE,e),this.h.gpuOriginForWebTexturesIsBottomLeft&&i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL,!1);var[o,a]=e.videoWidth!==void 0?[e.videoWidth,e.videoHeight]:e.naturalWidth!==void 0?[e.naturalWidth,e.naturalHeight]:e.displayWidth!==void 0?[e.displayWidth,e.displayHeight]:[e.width,e.height];!this.l||o===this.h.canvas.width&&a===this.h.canvas.height||(this.h.canvas.width=o,this.h.canvas.height=a);var[s,u]=[o,a];this.h._addBoundTextureToStream(n,s,u,r)})}addBoolToStream(e,t,r){c(this,t,n=>{this.h._addBoolToInputStream(e,n,r)})}addDoubleToStream(e,t,r){c(this,t,n=>{this.h._addDoubleToInputStream(e,n,r)})}addFloatToStream(e,t,r){c(this,t,n=>{this.h._addFloatToInputStream(e,n,r)})}addIntToStream(e,t,r){c(this,t,n=>{this.h._addIntToInputStream(e,n,r)})}addUintToStream(e,t,r){c(this,t,n=>{this.h._addUintToInputStream(e,n,r)})}addStringToStream(e,t,r){c(this,t,n=>{c(this,e,i=>{this.h._addStringToInputStream(i,n,r)})})}addStringRecordToStream(e,t,r){c(this,t,n=>{Vr(this,Object.keys(e),i=>{Vr(this,Object.values(e),o=>{this.h._addFlatHashMapToInputStream(i,o,Object.keys(e).length,n,r)})})})}addProtoToStream(e,t,r,n){c(this,r,i=>{c(this,t,o=>{var a=this.h._malloc(e.length);this.h.HEAPU8.set(e,a),this.h._addProtoToInputStream(a,e.length,o,i,n),this.h._free(a)})})}addEmptyPacketToStream(e,t){c(this,e,r=>{this.h._addEmptyPacketToInputStream(r,t)})}addBoolVectorToStream(e,t,r){c(this,t,n=>{var i=this.h._allocateBoolVector(e.length);if(!i)throw Error("Unable to allocate new bool vector on heap.");for(let o of e)this.h._addBoolVectorEntry(i,o);this.h._addBoolVectorToInputStream(i,n,r)})}addDoubleVectorToStream(e,t,r){c(this,t,n=>{var i=this.h._allocateDoubleVector(e.length);if(!i)throw Error("Unable to allocate new double vector on heap.");for(let o of e)this.h._addDoubleVectorEntry(i,o);this.h._addDoubleVectorToInputStream(i,n,r)})}addFloatVectorToStream(e,t,r){c(this,t,n=>{var i=this.h._allocateFloatVector(e.length);if(!i)throw Error("Unable to allocate new float vector on heap.");for(let o of e)this.h._addFloatVectorEntry(i,o);this.h._addFloatVectorToInputStream(i,n,r)})}addIntVectorToStream(e,t,r){c(this,t,n=>{var i=this.h._allocateIntVector(e.length);if(!i)throw Error("Unable to allocate new int vector on heap.");for(let o of e)this.h._addIntVectorEntry(i,o);this.h._addIntVectorToInputStream(i,n,r)})}addUintVectorToStream(e,t,r){c(this,t,n=>{var i=this.h._allocateUintVector(e.length);if(!i)throw Error("Unable to allocate new unsigned int vector on heap.");for(let o of e)this.h._addUintVectorEntry(i,o);this.h._addUintVectorToInputStream(i,n,r)})}addStringVectorToStream(e,t,r){c(this,t,n=>{var i=this.h._allocateStringVector(e.length);if(!i)throw Error("Unable to allocate new string vector on heap.");for(let o of e)c(this,o,a=>{this.h._addStringVectorEntry(i,a)});this.h._addStringVectorToInputStream(i,n,r)})}addBoolToInputSidePacket(e,t){c(this,t,r=>{this.h._addBoolToInputSidePacket(e,r)})}addDoubleToInputSidePacket(e,t){c(this,t,r=>{this.h._addDoubleToInputSidePacket(e,r)})}addFloatToInputSidePacket(e,t){c(this,t,r=>{this.h._addFloatToInputSidePacket(e,r)})}addIntToInputSidePacket(e,t){c(this,t,r=>{this.h._addIntToInputSidePacket(e,r)})}addUintToInputSidePacket(e,t){c(this,t,r=>{this.h._addUintToInputSidePacket(e,r)})}addStringToInputSidePacket(e,t){c(this,t,r=>{c(this,e,n=>{this.h._addStringToInputSidePacket(n,r)})})}addProtoToInputSidePacket(e,t,r){c(this,r,n=>{c(this,t,i=>{var o=this.h._malloc(e.length);this.h.HEAPU8.set(e,o),this.h._addProtoToInputSidePacket(o,e.length,i,n),this.h._free(o)})})}addBoolVectorToInputSidePacket(e,t){c(this,t,r=>{var n=this.h._allocateBoolVector(e.length);if(!n)throw Error("Unable to allocate new bool vector on heap.");for(let i of e)this.h._addBoolVectorEntry(n,i);this.h._addBoolVectorToInputSidePacket(n,r)})}addDoubleVectorToInputSidePacket(e,t){c(this,t,r=>{var n=this.h._allocateDoubleVector(e.length);if(!n)throw Error("Unable to allocate new double vector on heap.");for(let i of e)this.h._addDoubleVectorEntry(n,i);this.h._addDoubleVectorToInputSidePacket(n,r)})}addFloatVectorToInputSidePacket(e,t){c(this,t,r=>{var n=this.h._allocateFloatVector(e.length);if(!n)throw Error("Unable to allocate new float vector on heap.");for(let i of e)this.h._addFloatVectorEntry(n,i);this.h._addFloatVectorToInputSidePacket(n,r)})}addIntVectorToInputSidePacket(e,t){c(this,t,r=>{var n=this.h._allocateIntVector(e.length);if(!n)throw Error("Unable to allocate new int vector on heap.");for(let i of e)this.h._addIntVectorEntry(n,i);this.h._addIntVectorToInputSidePacket(n,r)})}addUintVectorToInputSidePacket(e,t){c(this,t,r=>{var n=this.h._allocateUintVector(e.length);if(!n)throw Error("Unable to allocate new unsigned int vector on heap.");for(let i of e)this.h._addUintVectorEntry(n,i);this.h._addUintVectorToInputSidePacket(n,r)})}addStringVectorToInputSidePacket(e,t){c(this,t,r=>{var n=this.h._allocateStringVector(e.length);if(!n)throw Error("Unable to allocate new string vector on heap.");for(let i of e)c(this,i,o=>{this.h._addStringVectorEntry(n,o)});this.h._addStringVectorToInputSidePacket(n,r)})}attachBoolListener(e,t){ee(this,e,t),c(this,e,r=>{this.h._attachBoolListener(r)})}attachBoolVectorListener(e,t){ne(this,e,t),c(this,e,r=>{this.h._attachBoolVectorListener(r)})}attachIntListener(e,t){ee(this,e,t),c(this,e,r=>{this.h._attachIntListener(r)})}attachIntVectorListener(e,t){ne(this,e,t),c(this,e,r=>{this.h._attachIntVectorListener(r)})}attachUintListener(e,t){ee(this,e,t),c(this,e,r=>{this.h._attachUintListener(r)})}attachUintVectorListener(e,t){ne(this,e,t),c(this,e,r=>{this.h._attachUintVectorListener(r)})}attachDoubleListener(e,t){ee(this,e,t),c(this,e,r=>{this.h._attachDoubleListener(r)})}attachDoubleVectorListener(e,t){ne(this,e,t),c(this,e,r=>{this.h._attachDoubleVectorListener(r)})}attachFloatListener(e,t){ee(this,e,t),c(this,e,r=>{this.h._attachFloatListener(r)})}attachFloatVectorListener(e,t){ne(this,e,t),c(this,e,r=>{this.h._attachFloatVectorListener(r)})}attachStringListener(e,t){ee(this,e,t),c(this,e,r=>{this.h._attachStringListener(r)})}attachStringVectorListener(e,t){ne(this,e,t),c(this,e,r=>{this.h._attachStringVectorListener(r)})}attachProtoListener(e,t,r){ee(this,e,t),c(this,e,n=>{this.h._attachProtoListener(n,r||!1)})}attachProtoVectorListener(e,t,r){ne(this,e,t),c(this,e,n=>{this.h._attachProtoVectorListener(n,r||!1)})}attachAudioListener(e,t,r){this.h._attachAudioListener||console.warn('Attempting to use attachAudioListener without support for output audio. Is build dep ":gl_graph_runner_audio_out" missing?'),ee(this,e,(n,i)=>{n=new Float32Array(n.buffer,n.byteOffset,n.length/4),t(n,i)}),c(this,e,n=>{this.h._attachAudioListener(n,r||!1)})}finishProcessing(){this.h._waitUntilIdle()}closeGraph(){this.h._closeGraph(),this.h.simpleListeners=void 0,this.h.emptyPacketListeners=void 0}},class extends $r{la(){this.h._registerModelResourcesGraphService()}});async function io(e,t){var r=await(async(n,i,o)=>{var a=T;if(n&&await Gr(n),!self.ModuleFactory||i&&(await Gr(i),!self.ModuleFactory))throw Error("ModuleFactory not set.");return self.Module&&o&&((n=self.Module).locateFile=o.locateFile,o.mainScriptUrlOrBlob&&(n.mainScriptUrlOrBlob=o.mainScriptUrlOrBlob)),o=await self.ModuleFactory(self.Module||o),self.ModuleFactory=self.Module=void 0,new a(o,null)})(e.wasmLoaderPath,e.assetLoaderPath,{locateFile:n=>n.endsWith(".wasm")?e.wasmBinaryPath.toString():e.assetBinaryPath&&n.endsWith(".data")?e.assetBinaryPath.toString():n});return r.ka=new eo,await r.N(t),r}async function Ut(e,t){return io(e,t)}function zr(e){try{let t=e.J.length;if(t===1)throw Error(e.J[0].message);if(t>1)throw Error("Encountered multiple errors: "+e.J.map(r=>r.message).join(", "))}finally{e.J=[]}}function fe(e,t){e.I=Math.max(e.I,t)}var Vt=class{constructor(e){this.j=e,this.J=[],this.I=0,this.j.setAutoRenderToScreen(!1)}setGraph(e,t){this.j.attachErrorListener((r,n)=>{this.J.push(Error(n))}),this.j.la(),this.j.setGraph(e,t),zr(this)}finishProcessing(){this.j.finishProcessing(),zr(this)}close(){var e;(e=this.ka)==null||e.close(),this.j.closeGraph()}};Vt.prototype.close=Vt.prototype.close;var _e=class extends A{constructor(e){super(e)}j(){return be(V(this,2))??0}};function Hr(e,t){te(e,1,t)}var oo=class extends A{constructor(e){super(e)}},Yn=[0,X,g,Qe,-1,I];function ao(e,t,r,n){if(e.data!==void 0){var i=new Uint8Array(e.data.buffer,t,r);return n===1&&(function(o,a,s){o.i.push([a,s]),o.i.sort((u,l)=>u[0]-l[0]),a=0;for(let[u,l]of o.i){let h=l;(s=u)<=a&&(a=Math.max(a,s+h))}a===o.length&&(o.data=void 0)})(e,t,r),i}}_e.prototype.l=$e(Yn);class so{constructor(t){this.i=[],this.data=t,this.length=t.length}}function Jn(e,t){return new uo(async()=>{var{value:r,done:n}=await e.read();return n?void 0:r},t)}async function Wr(e,t,r,n,i){if(i===2)return e.i=[],e.j=()=>Promise.resolve(void 0),setTimeout(()=>{e.l()},0),Promise.resolve(0);for(;e.size>>0;var a=0;for(let s=0;s=u.length){r-=u.length;continue}let l=Math.min(n,u.length-r);if((r=ao(u,r,l,i))===void 0)throw Error("Data has already been released.");if(t.HEAPU8.set(r,o+a),r=0,a+=l,(n-=l)===0)break}if(n!==0)throw Error("Data not found.");return Promise.resolve(o)}var uo=class{constructor(e,t){this.i=[],this.j=e,this.l=t}get size(){var e=0;for(let t=0;t>>0,this.o=this.h.HEAPU8,this.i=!!this.j}get offset(){if(!this.i)throw Error("WasmFileReference has been freed.");return this.j}get size(){if(!this.i)throw Error("WasmFileReference has been freed.");return this.l}set(e,t){this.o.set(e,this.j+(t??0))}},Zn=class extends A{constructor(e){super(e)}};Zn.prototype.j=$e([0,D,2,F,g,w]);var lo=class extends A{constructor(e){super(e)}},ho=class extends A{constructor(e){super(e)}},co=class extends A{constructor(e){super(e)}},ei=class extends A{constructor(e){super(e)}},Ft=[0,g,-6,1,g,1,[0,w,X,-2],[0,w,Qe],X,-2,[0,w,-1,X,Qe,$,I,v,-2],1,w,g,I,-1,[0,X,g],w,-1,Ie,g,-5,Ie,-1,[0,I,Ie],I,v,[0,I,-2],Ie,[0,g],[0,g,-4],v,I,-2,v,-1,Qe,Ie,v,g,-1,[0,I,-2],w,g,I,v,[0,I,-1],I,v],qr=[0,D,-2],Yr=[0,[4,6],Ft,g,1,Ji,F,Hn,nr,qr,I,[0,[0,g,-1,C,[0,g,[0,g,-1],-1,[0,X,-1],w],w,-2,g,-1],[0,g,-1,w],Ft,w,g,[0,g],-1],E,-3,[0,g,w],Ft,[0,qr,-2],se,C,[0,D,-2],se];ei.prototype.j=$e([0,D,8,[0,w,-6],1,g,1,g,[0,C,[0,D,Yi,-1,X],Yr,g],[0,g,w,-3],1,X,1,Yr,1,g,5,X,se,1,Yn,w,g,w]);var fo=class extends A{constructor(e){super(e)}},ti=class extends A{constructor(e){super(e)}},lt=[2,4];ti.prototype.j=$e([0,lt,g,Hn,g,x,[0,1,D]]);var po=(function(e){return class extends e{constructor(){super(...arguments),this.P=!1,this.F=this.H=0}M(){if(this.P)throw Error("Cannot process because LLM inference engine is currently loading or processing.");this.P=!0}L(){this.P=!1}async createLlmInferenceEngine(t,r){var n;this.M();try{let i=Jn(t,()=>{});await this.h.createLlmInferenceEngine(B(r,2)??512,((n=Re(r,_e,3))==null?void 0:n.j())??40,re(V(r,6))??!1??!1,B(r,7)??0,re(V(r,8))??!1??!1,(o,a,s)=>Wr(i,this.h,o,a,s))}finally{this.L()}}async ba(t,r){var n;this.M();try{await this.na(t),await this.h.ccall("CreateLlmInferenceEngineConverted","void",["number","number","boolean"],[B(r,2)??512,((n=Re(r,_e,3))==null?void 0:n.j())??40,re(V(r,6))??!1??!1],{async:!0})}finally{this.L()}}V(){this.M();try{let t=this.h;t.ccall("DeleteLlmInferenceEngine","void",[],[],{async:!1}),this.H&&(t._FreeSession(this.H),this.F===this.H&&(this.F=0),this.H=0),this.F&&(t._FreeSession(this.F),this.F=0)}finally{this.L()}}async R(t,r,n){this.M();try{let i=[],o=this.h;o._userProgressListener=(d,m)=>{d&&i.push(d),n&&n(d,m)};let a=r.l(),s=a.length,u=this.h._malloc(s);this.h.HEAPU8.set(a,u);let l=t.some(Be),h=t.some(xe);o.ccallNum=o.ccall;let f=await o.ccallNum("MakeSessionForPredict","number",["number","number","boolean","boolean"],[u,s,h,l],{async:!0});r=[];for(let d of t)if(typeof d=="string")c(this,d,m=>{o._AddTextQueryChunk(f,m)});else if(xe(d)){let{image:m,width:_,height:O}=await this.fa(d.imageSource),k=typeof OffscreenCanvas<"u"?new OffscreenCanvas(_,O):document.createElement("canvas");k.width=_,k.height=O;let M=k.getContext("2d");M.drawImage(m,0,0);let L=M.getImageData(0,0,_,O),N=this.h._malloc(L.width*L.height*4);this.h.HEAPU8.set(L.data,N),o._AddImageQueryChunk(f,N,L.width,L.height),r.push(N)}else{if(!Be(d))throw Error("Unsupported PromptPart type in query.");{let m=await this.ea(d.audioSource),_=this.h._malloc(m.audioSamples.byteLength);this.h.HEAPF32.set(m.audioSamples,_/4),o._AddAudioQueryChunk(f,m.audioSampleRateHz,_,m.audioSamples.length),r.push(_)}}await o.ccall("PredictSession","void",["number"],[f],{async:!0}),t=!0,h&&this.F===0&&(this.F=f,t=!1),l&&this.H===0&&(this.H=f,t=!1),t&&o._FreeSession(f);for(let d of r)this.h._free(d);return r.length=0,n&&n("",!0),this.h._free(u),o._userProgressListener=void 0,i.join("")}finally{this.L()}}S(t){this.M();var r=0,n="";for(let i of t)typeof i=="string"?n+=i:xe(i)?r+=260:Be(i)&&console.warn("sizeInTokens is not yet implemented for audio; audio tokens will not be counted");try{let i;return c(this,n,o=>{i=this.h._GetSizeInTokens(o)}),r+i}finally{this.L()}}async na(t){t=await(async function(r){for(var n=[],i=0;;){let{done:o,value:a}=await r.read();if(o)break;n.push(a),i+=a.length}if(n.length===0)return new Uint8Array(0);if(n.length===1)return n[0];r=new Uint8Array(i),i=0;for(let o of n)r.set(o,i),i+=o.length;return r})(t);try{this.h.FS_unlink("llm.task")}catch{}this.h.FS_createDataFile("/","llm.task",t,!0,!1,!1)}async fa(t){if(typeof t=="string"){let r=new Image;r.src=t,r.crossOrigin="Anonymous";try{await r.decode()}catch{throw Error(`Image from URL ${t} failed to load`)}return{image:r,width:r.naturalWidth,height:r.naturalHeight}}if(t instanceof HTMLImageElement){try{await t.decode()}catch{throw Error("Image from HTMLImageElement failed to load")}return{image:t,width:t.naturalWidth,height:t.naturalHeight}}return t instanceof HTMLVideoElement?{image:t,width:t.videoWidth,height:t.videoHeight}:t instanceof VideoFrame?{image:t,width:t.displayWidth,height:t.displayHeight}:{image:t,width:t.width,height:t.height}}async ea(t){if(typeof t=="string"){let r=await fetch(t);if(!r.ok)throw Error(`Audio fetch for ${t} had error: ${r.status}`);return t=await r.arrayBuffer(),{audioSamples:(t=await new AudioContext({sampleRate:16e3}).decodeAudioData(t)).getChannelData(0),audioSampleRateHz:t.sampleRate}}return typeof t=="object"&&t!=null&&"audioSamples"in t&&"audioSampleRateHz"in t?t:{audioSamples:t.getChannelData(0),audioSampleRateHz:t.sampleRate}}}})((function(e){var t=class extends e{static async ma(r,n){n||(n=await t.X());var i=[];for(let a of(r==null?void 0:r.requiredFeatures)??[])n.features.has(a)?i.push(a):console.warn(`WebGPU feature ${a} is not supported.`);r={...r,requiredFeatures:i};try{var o=await n.requestDevice(r)}catch(a){throw console.error("Unable to initialize WebGPU with the requested features."),a}return(r=o).adapterInfo||(r.adapterInfo=n.info),o}static async X(r){if(!(r=await navigator.gpu.requestAdapter(r)))throw Error("Unable to request adapter from navigator.gpu; Ensure WebGPU is enabled.");return r}ga(r){if(n)typeof HTMLCanvasElement<"u"&&n instanceof HTMLCanvasElement&&(n.id="canvas_webgpu");else var n=new OffscreenCanvas(1,1);n.getContext("webgpu").configure({device:r,format:navigator.gpu.getPreferredCanvasFormat()}),this.h.preinitializedWebGPUDevice=r}aa(){return this.h.ccall("closeGraph","void",[],[],{async:!0})}};return t})((function(e){return class extends e{addStreamingReaderToInputSidePacket(t,r){this.h.addStreamingReaderToInputSidePacket((n,i,o)=>Wr(t,this.h,n,i,o),r)}}})((function(e){return class extends e{Y(t,r){c(this,"lora_model_ref_in",n=>{this.h._addRawDataSpanToInputStream(t.offset,t.size,n,r)})}}})(class extends no{}))));class $t extends po{}var zt=class{constructor(e){this.j=e,this.i=Jr,Jr++}},Jr=1;class mo{constructor(){var t,r;this.promise=new Promise((n,i)=>{t=n,r=i}),this.resolve=t,this.reject=r}}function Xr(e){return e===1?1:e+e%2}async function Je(){var e=await $t.X({powerPreference:"high-performance"}),t=e.limits.maxBufferSize,r=e.limits.maxStorageBufferBindingSize;return t<524550144&&console.warn(`This WebGPU device is unable to execute most LLM tasks, because the required maxBufferSize is usually at least 524550144, but your device only supports maxBufferSize of ${t}`),r<524550144&&console.warn(`The WebGPU device is unable to execute LLM tasks, because the required maxStorageBufferBindingSize is usually at least 524550144, but your device only supports maxStorageBufferBindingSize of ${r}`),t={requiredFeatures:["shader-f16"],requiredLimits:{maxStorageBufferBindingSize:r,maxBufferSize:t,maxStorageBuffersPerShaderStage:e.limits.maxStorageBuffersPerShaderStage}},e.features.has("subgroups")&&(console.warn("Experimental Chromium WGSL subgroup support detected. Enabling this feature in the inference engine."),t.requiredFeatures=["shader-f16","subgroups"]),$t.ma(t,e)}function pe(e){if(e.D.length>0){let t=[...e.D];if(e.D.length=0,!e.o)throw t;e.o.reject(t),e.o=void 0}}function go(e){var n;var t=(function(i){var o=new Kn;p(o,10,"text_in"),p(o,10,"token_cost_in"),p(o,10,"lora_model_id_to_apply_in"),p(o,10,"lora_model_ref_in"),p(o,10,"lora_model_id_to_load_in"),p(o,16,"streaming_reader"),p(o,15,"text_out"),p(o,15,"text_end"),p(o,15,"token_cost_out");var a=new Y;U(a,2,"TokenizerInputBuildCalculator"),p(a,3,"PROMPT:text_in"),p(a,3,"LORA_ID:lora_model_id_to_apply_in"),p(a,4,"prompt"),Z(o,a),a=new Y,U(a,2,"ModelDataCalculator"),p(a,6,"MODEL_DATA:__side_packet_1"),p(a,6,"MODEL_TYPE:model_type"),p(a,5,"READ_DATA_FN:streaming_reader"),p(a,3,"LORA_MODEL_SPAN:lora_model_ref_in"),p(a,3,"LORA_MODEL_ID:lora_model_id_to_load_in"),p(a,4,"LORA_DATA:lora_model_data"),Z(o,a),a=new Y,U(a,2,"Gpt2UnicodeMappingCalculator"),p(a,5,"MODEL_TYPE:model_type"),p(a,6,"BYTES_TO_UNICODE_MAPPING:tokenizer_mapping"),Z(o,a),a=new he,U(a,1,"type.googleapis.com/odml.infra.proto.TokenizerCalculatorOptions");var s=new ti,u=B(i.i,2);H(s,1,u),U(u=new fo,2,"spm_vocab_model"),u=kn(u);e:{Se(s);var l=s.m,h=0|l[y];if(u==null){var f=_r(l);if(wr(f,l,h)!==4)break e;f.set(lt,0)}else{let d=_r(f=l),m=wr(d,f,h);m!==4&&(m&&(h=W(f,h,m)),d.set(lt,4))}W(l,h,4,u)}return u&&!z(u)&&ve(s.m),H(s,3,2),jt(a,s.j()),U(s=new Y,2,"TokenizerCalculator"),Le(s,8,he,a),p(s,5,"MODEL_DATA:__side_packet_1"),p(s,3,"PROMPT_AND_INPUT_OPTIONS:prompt"),p(s,5,"BYTES_TO_UNICODE_MAPPING:tokenizer_mapping"),p(s,6,"PROCESSOR_GETTER:__input_side_1"),p(s,4,"IDS_AND_INPUT_OPTIONS:__stream_0"),Z(o,s),a=new he,U(a,1,"type.googleapis.com/odml.infra.proto.LlmGpuCalculatorOptions"),H(s=new ei,12,3),U(s,1,"llm.tflite"),H(s,14,0),u=Xr(B(i.i,5)),H(s,22,u),u=Re(i.i,_e,3),te(s,31,u),G(u=new lo,1,oe(!0),!1),re(V(i.i,6))!=null&&(re(V(i.i,6))??!1)&&G(u,1,oe(!1),!1),G(u,2,oe(!0),!1),G(u,5,oe(!0),!1),te(s,10,u),u=An(i.i,4,be,wi===void 0?2:4),On(s,29,u),u=new co,H(l=new ho,1,1),f=B(i.i,2),H(l,2,f),te(u,1,l),te(s,20,u),jt(a,s.j()),U(s=new Y,2,"LlmGpuCalculator"),Le(s,8,he,a),p(s,3,"IDS_AND_INPUT_OPTIONS:__stream_0"),p(s,3,"FINISH:finish"),p(s,3,"LORA_DATA:lora_model_data"),p(s,5,"MODEL_DATA:__side_packet_1"),p(s,4,"DECODED_IDS:__stream_3"),p(s,4,"OUTPUT_END:__stream_4"),a=new jr,U(a,1,"FINISH"),G(a,2,oe(!0),!1),Le(s,13,jr,a),Z(o,s),a=new Y,U(a,2,"IsPacketPresentCalculator"),p(a,3,"__stream_4"),p(a,4,"text_end"),Z(o,a),a=new he,U(a,1,"type.googleapis.com/odml.infra.proto.DetokenizerCalculatorOptions"),s=new Zn,i=Xr(B(i.i,5)),H(s,5,i),p(s,4,""),p(s,4,"<|endoftext|>"),jt(a,s.j()),i=new Y,U(i,2,"DetokenizerCalculator"),Le(i,8,he,a),p(i,3,"IDS_AND_INPUT_OPTIONS:__stream_3"),p(i,5,"PROCESSOR_GETTER:__input_side_1"),p(i,5,"BYTES_TO_UNICODE_MAPPING:tokenizer_mapping"),p(i,5,"MODEL_DATA:__side_packet_1"),p(i,4,"FINISH_AND_INPUT_OPTIONS:finish"),p(i,4,"WORDS:text_out"),Z(o,i),i=new Y,U(i,2,"TokenCostCalculator"),p(i,3,"PROMPT:token_cost_in"),p(i,5,"PROCESSOR_GETTER:__input_side_1"),p(i,5,"BYTES_TO_UNICODE_MAPPING:tokenizer_mapping"),p(i,4,"NUM_TOKENS:token_cost_out"),Z(o,i),o})(e);e.j.attachStringVectorListener("text_out",(i,o)=>{i=(function(a,s){return a==null||a.length===0?[]:a.map(u=>(u=(u=u.replaceAll("▁"," ")).replaceAll("<0x0A>",` -`),s&&(u=u.trimStart()),u.split("\\[eod\\]",1)[0]))})(i,e.G.length===0),i.forEach((a,s)=>{sB(e.i,5)&&i.pop(),e.A(i,!1)):e.A(i[0],!1)),fe(e,o)}),e.j.attachEmptyPacketListener("text_out",i=>{fe(e,i)}),e.j.attachBoolListener("text_end",(i,o)=>{fe(e,o);try{pe(e)}catch(a){throw e.l=!1,a}if(e.o&&(e.o.resolve(e.G.map(a=>a.join(""))),e.o=void 0),e.A)if(e.B){for(i=[],o=0;o{e.l=!1,e.B=void 0,fe(e,i),pe(e),e.o&&(e.o.resolve(e.G.map(o=>o.join(""))),e.o=void 0)}),e.j.attachIntListener("token_cost_out",(i,o)=>{e.T=i,fe(e,o)}),e.U&&e.j.addStreamingReaderToInputSidePacket(e.U,"streaming_reader");var r=t.j();return(n=e.C)==null||n.removeEventListener("uncapturederror",e.K),e.j.aa().then(()=>{var i;(i=e.C)==null||i.addEventListener("uncapturederror",e.K),e.D.length=0,e.setGraph(new Uint8Array(r),!0),e.finishProcessing()})}function Qr(e,t,r,n){if(e.A=typeof r=="function"?r:n,(n=(t=Array.isArray(t)?t:[t]).filter(i=>xe(i)).length)>0&&(nt(V(e.i,7))==null||B(e.i,7)Be(i)).length)>0&&(re(V(e.i,8))==null||!re(V(e.i,8))))throw Error(`supportAudio was not enabled, but the query included ${n} audio chunks.`);if(e.v){if(e.B&&B(e.i,5)>1)throw Error("Multi-response generation is not supported for converted LLM models (.task format) yet, nor is it supported for multimodality. Please use the .bin format without multimodality or request only one response.");if(r instanceof zt)throw Error("LoRA is not supported for converted LLM models (.task format) yet, nor is it supported for multimodality. Please use the .bin format without multimodality to use LoRA.");return e.j.h.LLM_CANCEL_FLAG=void 0,e.j.R(t,e.u,(i,o)=>{e.D.length===0&&e.A&&(e.B?e.A([i],o):e.A(i,o))}).then(i=>(pe(e),[i]))}if(e.l)throw Error("Previous invocation or loading is still ongoing.");for(e.l=!0,e.j.h.LLM_CANCEL_FLAG=void 0,e.G.length=0,n=0;n{if((r=r.error).message.match(/exceeds the max buffer size limit/))throw Error(`Failed to run this LLM model because it requires a buffer size that exceeds the maximum size your device supports, but you could try a smaller LLM model or different device. -WebGPU throws: "${r.message}"`);if(r.message.match(/is larger than the maximum storage buffer binding size/))throw Error(`Failed to run this LLM model because it requires a storage buffer binding size that exceeds the maximum size your device supports, but you could try a smaller LLM model or different device. -WebGPU throws: "${r.message}"`);this.D.push(r)},this.i=new oo,Hr(this.i,new Dr),this.u=new _e,te(this.i,3,this.u),Ee(this.i,2,512),e=this.u,!we(2))throw Fe("enum");G(e,1,2,0),H(this.u,2,40),G(this.u,3,kt(1),0),ae(this.u,5,Gt(0)),G(this.u,4,kt(.8),0),Ee(this.i,5,1)}async N(e){var a,s,u,l,h,f,d;if(this.l)throw Error("Cannot set options while loading or processing.");if((a=e.baseOptions)!=null&&a.modelAssetPath&&((s=e.baseOptions)!=null&&s.modelAssetBuffer))throw Error("Cannot set both baseOptions.modelAssetPath and baseOptions.modelAssetBuffer");var t,r=new Promise(m=>{t=m});if((u=e.baseOptions)!=null&&u.modelAssetPath){var n=await fetch(e.baseOptions.modelAssetPath.toString());if(!n.ok)throw Error(`Failed to fetch model: ${e.baseOptions.modelAssetPath} (${n.status})`);if(!n.body)throw Error(`Failed to fetch model: ${e.baseOptions.modelAssetPath} (no body)`);n=n.body.getReader()}else((l=e.baseOptions)==null?void 0:l.modelAssetBuffer)instanceof Uint8Array?n=(function(m){return new ReadableStream({start(){},async pull(_){_.enqueue(m),_.close()}})})(e.baseOptions.modelAssetBuffer).getReader():((h=e.baseOptions)==null?void 0:h.modelAssetBuffer)instanceof ReadableStreamDefaultReader?(n=e.baseOptions.modelAssetBuffer,e.baseOptions.modelAssetBuffer=void 0):t();if(!n)throw Error("No model asset provided.");{let[m,_]=Rr(n);this.O=await(async function(O){var k,M=[];for(let[N,ze]of to){let He=N;var L=ze;[O,k]=Rr(O),L=await L(k),await k.cancel(),L&&M.push(He)}if(await O.cancel(),M.length===0)throw Error("No model format matched.");if(M.length===1)return M[0];throw Error(`Multiple model formats matched: ${M}`)})(_)===1;var i="maxNumImages"in e&&e.maxNumImages?e.maxNumImages:0;Ee(this.i,7,i);var o="supportAudio"in e&&!!e.supportAudio;ae(this.i,8,oe(o)),this.O||i>0||o?(this.v=!0,n=m):(this.v=!1,this.U=Jn(m,t))}if((d=(f=e.baseOptions)==null?void 0:f.gpuOptions)!=null&&d.device&&(this.C&&this.C.removeEventListener("uncapturederror",this.K),this.C=e.baseOptions.gpuOptions.device,this.j.ga(this.C),this.C.addEventListener("uncapturederror",this.K)),"maxTokens"in e&&Ee(this.i,2,e.maxTokens??512),"topK"in e&&H(this.u,2,e.topK??40),"temperature"in e&&G(this.u,4,kt(e.temperature??.8),0),"randomSeed"in e&&ae(this.u,5,Gt(e.randomSeed??0)),"loraRanks"in e&&(function(m,_){On(m,4,_)})(this.i,e.loraRanks??[]),"numResponses"in e){if((i=e.numResponses??1)<1)throw Error("'numResponses' must be at least 1.");if(this.v&&i>1)throw Error("'numResponses > 1' is not supported for converted LLM models yet, and is also not supported with multimodality.");Ee(this.i,5,i),o=Re(this.i,_e,3),i>1&&o&&(o.j()<=1||(V(o,4,yn)??0)<=0)&&console.warn("To generate multiple responses, it is expected topK > 1 and temperature > 0; otherwise, all the generated responses may be the same.")}return"forceF32"in e&&e.forceF32!==void 0&&ae(this.i,6,oe(e.forceF32)),this.v?(this.j.V(),this.O?this.j.ba(n,this.i).then(()=>{pe(this)}):this.j.createLlmInferenceEngine(n,this.i).then(()=>{pe(this)})):(this.l=!0,e=go(this).then(()=>{}),Promise.all([r,e]).then(()=>{this.l=!1,pe(this)}))}get baseOptions(){return Re(this.i,Dr,1)}set baseOptions(e){Hr(this.i,e)}get isIdle(){return!this.l&&!this.o}R(e,t,r){return B(this.i,5)>1&&console.warn("'numResponses' is set larger than 1 and this function only returns the first response, so we recommend either using 'generateResponses()' to obtain multiple responses, or else setting 'numResponses' to 1 for better performance."),this.B=!1,Qr(this,e,t,r).then(n=>n[0])}da(e,t,r){return this.B=!0,Qr(this,e,t,r)}S(e){if(e=Array.isArray(e)?e:[e],this.v)return this.j.S(e);if(this.l)throw Error("Previous invocation or loading is still ongoing.");if(e.some(xe))throw Error("sizeInTokens requires maxNumImages > 0 for images.");if(e.some(Be))throw Error("sizeInTokens requires supportAudio for audio.");return e=e.join(""),this.l=!0,this.T=void 0,this.j.addStringToStream(e,"token_cost_in",this.I+1),this.finishProcessing(),this.l=!1,this.T}Z(){var e=this.j.h;(this.v||this.l)&&(e.LLM_CANCEL_FLAG=1)}async ja(e){if(this.v)throw Error("LoRA is not supported for converted LLM models (.task format) yet, nor is it supported for multimodality. Please use the old format (.bin) without multimodality to use LoRA.");if(this.l)throw Error("Cannot load LoRA model while loading or processing.");if(this.l=!0,e instanceof Uint8Array){var t=new Qn(this.j.h,e.length);t.set(e),e=t}else e=e instanceof Blob?await(async function(n,i){return Kr(n,i.stream(),i.size)})(this.j.h,e):await(async function(n,i){i=await fetch(i.toString());var o=Number(i.headers.get("content-length"));if(!i.body)throw Error("Response body is not available.");if(!o)throw Error("File size is 0.");return Kr(n,i.body,o)})(this.j.h,e);t=new zt(this);var r=this.I+1;return this.j.Y(e,r),this.j.addUintToStream(t.i,"lora_model_id_to_load_in",r),this.finishProcessing(),Xn(e),fe(this,r),this.l=!1,t}close(){var e;this.v&&this.j.V(),(e=this.C)==null||e.removeEventListener("uncapturederror",this.K),super.close()}};T.prototype.loadLoraModel=T.prototype.ja,T.prototype.cancelProcessing=T.prototype.Z,T.prototype.sizeInTokens=T.prototype.S,T.prototype.generateResponses=T.prototype.da,T.prototype.generateResponse=T.prototype.R,T.prototype.setOptions=T.prototype.N,T.createWebGpuDevice=Je,T.createFromModelPath=async function(e,t){return Ut(e,t={baseOptions:{gpuOptions:{device:await Je()},modelAssetPath:t}})},T.createFromModelBuffer=async function(e,t){return Ut(e,t={baseOptions:{gpuOptions:{device:await Je()},modelAssetBuffer:t}})},T.createFromOptions=async function(e,t){var r,n,i;if(!((n=(r=t.baseOptions)==null?void 0:r.gpuOptions)!=null&&n.device)){let o=await Je();t.baseOptions=t.baseOptions??{},t.baseOptions.gpuOptions=((i=t==null?void 0:t.baseOptions)==null?void 0:i.gpuOptions)??{},t.baseOptions.gpuOptions.device=o}return Ut(e,t)};const ht="gemma-4-E2B-it-web.task",yo="/mediapipe-wasm",vo="0.10.28";function _o(e){const t=e.includes("?")?"&":"?";return`${e}${t}v=${vo}`}async function wo(e){const t=await fetch(_o(e));if(!t.ok)throw new Error(`Failed to fetch WASM loader (${t.status}): ${e}`);const r=await t.text();if(e.includes("_module_")||/\bexport\s+default\b/.test(r)){const i=new Blob([r],{type:"text/javascript"}),o=URL.createObjectURL(i);try{const a=await import(o);globalThis.ModuleFactory=a.default??a.ModuleFactory}finally{URL.revokeObjectURL(o)}return}(0,eval)(r)}async function bo(){const e=await ce.forGenAiTasks(yo,!0);if(globalThis.ModuleFactory||await wo(e.wasmLoaderPath),!globalThis.ModuleFactory)throw new Error("ModuleFactory not set. Run npm run sync-wasm, then hard-refresh the page.");return{wasmBinaryPath:e.wasmBinaryPath,assetLoaderPath:e.assetLoaderPath,assetBinaryPath:e.assetBinaryPath,wasmLoaderPath:""}}const So=500*1024*1024;function To(e){return e<1024*1024?`${(e/1024).toFixed(1)} KB`:e<1024*1024*1024?`${(e/(1024*1024)).toFixed(1)} MB`:`${(e/(1024*1024*1024)).toFixed(2)} GB`}const Zr=new TextEncoder().encode("TFL3");function Eo(e){return e.length<6?!1:e.length>=4+Zr.length&&new TextDecoder().decode(e.subarray(4,4+Zr.length))==="TFL3"?!0:e[4]===80&&e[5]===75}function Ao(e){const t=new TextDecoder().decode(e).trimStart();return t.startsWith("<")||t.startsWith("{")||t.startsWith("[")}function ri(e){if(e.endsWith(".litertlm"))throw new Error(`"${e}" is a LiteRT-LM checkpoint, not MediaPipe. Delete OPFS cache and download ${ht} instead.`);if(!e.endsWith(".task"))throw new Error(`"${e}" is not a MediaPipe .task file. Use ${ht} from the litert-community HuggingFace repo.`)}async function Io(e,t=8){const r=await e.slice(0,t).arrayBuffer();return new Uint8Array(r)}async function Po(e){if(ri(e.name),e.size${r}`:r;i.push(`<|turn>system -${a}`)}return i.push(`<|turn>user -${e}`),i.push(`<|turn>model -`),i.join(` -`)}function Fo(e,t,r={}){var s;const n=((s=r.systemPrompt)==null?void 0:s.trim())??"",i=r.chainOfThought??!1,o=[];if(i||n){const u=i?`<|think|>${n}`:n;o.push(`<|turn>system -${u}`)}o.push(`<|turn>user -${e}`);const a=i?ct(t).answer:t.trim();return o.push(`<|turn>model -${a}`),o.push(`<|turn>user -Continue your previous answer from exactly where you stopped. Do not repeat any earlier text.`),o.push(`<|turn>model -`),o.join(` -`)}function tn(e,t,r){if(!r)return e+t;const{thought:n,answer:i}=ct(e),{answer:o}=ct(t),a=o||t.trim(),s=i+a;return n?`${Ht} -${n}${Wt}${s}`:s}function Do(e,t){return t?ct(e).answer:e.trim()}function Ro(e){const t=e.maxContinuationAttempts??Bo;return e.continuationAttempt>=t||!e.hitTokenLimit?!1:oi(e.answerText)}function Co(e,t,r,n=ni){return t>=r-n||t-e>=r-e-n}function ct(e){const t=e.indexOf(Ht);if(t===-1)return{thought:null,answer:e};const r=e.slice(t+Ht.length).replace(/^\n/,""),n=r.indexOf(Wt);return n===-1?{thought:r.trim(),answer:""}:{thought:r.slice(0,n).trim(),answer:r.slice(n+Wt.length).trim()}}function oi(e){const t=e.trim();return t.length<80?!1:!/[.!?)"'\]]$/.test(t)}let R=null,Ce=null,je=null,ir=null,Lt=null,Ne="";function Kt(e){return Lt===e}function ai(){ir=null,Lt=null,Ne=""}const Go=16;function Vo(e,t){let r="",n=null;function i(){if(n=null,r.length===0)return;const s=r;r="",postMessage({type:"token",requestId:e,partial:s,done:!1,segment:t})}function o(s){s.length!==0&&(r+=s,n===null&&(n=setTimeout(i,Go)))}function a(){n!==null&&(clearTimeout(n),n=null),i()}return{push:o,flushNow:a}}function $o(e,t){return e.topK===t.topK&&e.temperature===t.temperature&&e.randomSeed===t.randomSeed}function zo(e,t){return R!==null&&Ce===e&&je!==null&&je.maxTokens>=t.maxTokens&&$o(je,t)}function si(){if(R){try{R.close()}catch{}R=null}Ce=null,je=null}function or(e,t){return{...e,maxTokens:ii(e.maxTokens,t)}}function ui(e){return R==null?void 0:R.sizeInTokens(e)}async function qt(e,t,r,n){if(zo(e,t))return 0;si(),postMessage({type:"init_progress",requestId:r,message:n});const i=performance.now(),o=await bo(),a=await xo(e);return R=await T.createFromOptions(o,{baseOptions:{modelAssetBuffer:a},maxTokens:t.maxTokens,topK:t.topK,temperature:t.temperature,randomSeed:t.randomSeed}),Ce=e,je=t,Math.round(performance.now()-i)}async function Ho(e,t,r,n){return postMessage({type:"init_progress",requestId:n,message:"Loading MediaPipe WASM + WebGPU…"}),postMessage({type:"init_progress",requestId:n,message:"Validating MediaPipe .task checkpoint…"}),qt(e,or(t,r),n,"Initializing Gemma 4 E2B (.task) from OPFS…")}async function Wo(e,t,r){if(!R)throw new Error("MediaPipe backend is not initialized");let n="";const i=R;return await new Promise((o,a)=>{try{i.generateResponse(e,(s,u)=>{if(Kt(t)){o();return}s.length>0&&(n+=s,r(s,n)),u&&o()})}catch(s){a(s)}}),n}function rn(e,t,r,n){postMessage({type:"generate_done",requestId:e,stats:{segments:Math.max(t,1),promptTokens:r,outputTokens:n,continued:t>1,truncated:!1,rawOutput:Ne,cancelled:!0}}),ai()}async function Ko(e,t,r,n,i,o){let a=or(r,n);await qt(e,a,i,o);const s=ui(t)??0,u=No(s,r,n);return u>a.maxTokens&&(a={...a,maxTokens:u},await qt(e,a,i,`Expanding token budget (${s} prompt + output room)…`)),{effectiveDecode:a,promptTokens:s}}async function qo(e,t,r,n,i){if(!Ce)throw new Error("LLM worker is not initialized");ir=n,Lt=null,Ne="";const o=t.chainOfThought;let a="",s=0,u=0,l=0,h=0,f=or(r,o);for(;;){if(Kt(n)){rn(n,u,l,h);return}const d=u+1,m=u===0?i??Uo(e,t):Fo(e,a,t),_=await Ko(Ce,m,r,o,n,u===0?"Applying decode settings…":"Adjusting token budget for continuation…");if(f=_.effectiveDecode,!R)throw new Error("MediaPipe backend is not initialized");postMessage({type:"segment_start",requestId:n,segment:d}),u>0&&postMessage({type:"generate_progress",requestId:n,message:`Continuing answer (part ${u+1})…`});const O=_.promptTokens;u===0&&(l=O);const k=a,M=Vo(n,d);let L=0;const N=await Wo(m,n,(sr,ur)=>{const lr=ur.slice(L);L=ur.length,lr.length>0&&M.push(lr)});if(M.flushNow(),Kt(n)){a=u===0?N:tn(k,N,o),Ne=a,rn(n,u+1,l,h);return}a=u===0?N:tn(k,N,o),Ne=a,u+=1;const ze=ui(`${m}${N}`)??O;h=Math.max(0,ze-O);const He=Co(O,ze,f.maxTokens),ar=Do(a,o);if(!Ro({hitTokenLimit:He,answerText:ar,continuationAttempt:s})){const sr=oi(ar);postMessage({type:"token",requestId:n,partial:"",done:!0,segment:d}),postMessage({type:"generate_done",requestId:n,stats:{segments:u,promptTokens:l,outputTokens:h,continued:u>1,truncated:sr&&He,rawOutput:a}}),ai();return}s+=1}}function Yo(e){ir===e&&(Lt=e,R==null||R.cancelProcessing())}function nn(e,t,r){const n=r instanceof Error?r.message:String(r);postMessage({type:t,requestId:e,error:n})}self.onmessage=async e=>{const t=e.data;try{if(t.type==="init"){const r=await Ho(t.modelFilename,t.decode,t.promptOptions.chainOfThought,t.requestId);postMessage({type:"init_done",requestId:t.requestId,init_ms:r});return}if(t.type==="cancel"){Yo(t.requestId);return}if(t.type==="release"){si(),postMessage({type:"release_done",requestId:t.requestId});return}t.type==="generate"&&await qo(t.userText,t.promptOptions,t.decode,t.requestId,t.rawConversationPrompt)}catch(r){t.type==="init"?nn(t.requestId,"init_error",r):t.type==="generate"&&nn(t.requestId,"generate_error",r)}}; diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/med-lat_1-Dto_0SyQ.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/med-lat_1-Dto_0SyQ.png deleted file mode 100644 index 5fe2fbe..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/med-lat_1-Dto_0SyQ.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/med_lat_2-DbnwLN1s.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/med_lat_2-DbnwLN1s.png deleted file mode 100644 index 3530ab3..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/med_lat_2-DbnwLN1s.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/mlInferenceCacheStore-BwGclW_P.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/mlInferenceCacheStore-BwGclW_P.js deleted file mode 100644 index 50f1cee..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/mlInferenceCacheStore-BwGclW_P.js +++ /dev/null @@ -1,79 +0,0 @@ -import{j as a}from"./index-BCa3bqPy.js";function m({title:e="Lumina MSK",subtitle:t,showBack:n,onBack:r,actions:o,phase:s}){return a.jsxs("header",{className:"topbar glass",children:[a.jsxs("div",{className:"topbar__left",children:[n&&a.jsx("button",{type:"button",className:"icon-btn",onClick:r,"aria-label":"Quay lại",children:"←"}),a.jsxs("div",{className:"topbar__brand",children:[a.jsx("span",{className:"topbar__logo",children:"◈"}),a.jsxs("div",{children:[a.jsx("h1",{className:"topbar__title",children:e}),t&&a.jsx("p",{className:"topbar__subtitle",children:t})]})]})]}),a.jsx("div",{className:"topbar__center",children:s&&a.jsx("span",{className:"topbar__phase chip chip-success",children:s})}),a.jsx("div",{className:"topbar__actions",children:o}),a.jsx("style",{children:` - .topbar { - height: var(--topbar-h); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 var(--space-md); - gap: var(--space-md); - border-radius: 0; - border-left: none; - border-right: none; - border-top: none; - position: sticky; - top: 0; - z-index: 100; - } - .topbar__left { - display: flex; - align-items: center; - gap: var(--space-sm); - min-width: 0; - } - .topbar__brand { - display: flex; - align-items: center; - gap: 10px; - min-width: 0; - } - .topbar__logo { - color: var(--color-primary); - font-size: 20px; - } - .topbar__title { - font-size: 16px; - font-weight: 700; - line-height: 1.2; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .topbar__subtitle { - margin: 0; - font-size: 12px; - color: var(--color-on-surface-variant); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .topbar__center { - flex-shrink: 0; - } - .topbar__phase { - font-size: 11px; - } - .topbar__actions { - display: flex; - align-items: center; - gap: var(--space-sm); - flex-shrink: 0; - } - `})]})}function b(){return a.jsxs("footer",{className:"bottombar glass",children:[a.jsx("span",{className:"bottombar__copyright",children:"© 2026, by VKIST"}),a.jsx("style",{children:` - .bottombar { - height: var(--bottombar-h); - display: flex; - align-items: center; - justify-content: center; - padding: 0 var(--space-md); - font-size: 12px; - color: var(--color-on-surface-variant); - border-radius: 0; - border-left: none; - border-right: none; - border-bottom: none; - flex-shrink: 0; - } - .bottombar__copyright { - letter-spacing: 0.02em; - } - `})]})}function N({children:e,topBar:t}){return a.jsxs("div",{className:"app-shell",children:[a.jsx(m,{...t}),a.jsx("main",{className:"app-main",children:e}),a.jsx(b,{})]})}const d="poc-v2-spec-cv-seg-norm",h=1440*60*1e3;function x(e,t,n,r=d){return`${e}|${t}|${n}|${r}`}function S(e,t,n=d){const r=[...t].sort().join(",");return`${e}|${n}|analyze:${r}`}async function E(e){const t=await fetch(e);if(!t.ok)throw new Error(`Failed to load image for hash (${t.status})`);const n=await t.arrayBuffer(),r=await crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(r)).map(o=>o.toString(16).padStart(2,"0")).join("")}const y="lumina-msk-ml",_=1,i="frame-inference-cache";function p(){return new Promise((e,t)=>{const n=indexedDB.open(y,_);n.onerror=()=>t(n.error??new Error("Failed to open ML cache IndexedDB")),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const r=n.result;if(!r.objectStoreNames.contains(i)){const o=r.createObjectStore(i,{keyPath:"cacheKey"});o.createIndex("patientMrn","patientMrn",{unique:!1}),o.createIndex("frameId","frameId",{unique:!1})}}})}function l(e){return new Promise((t,n)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>n(e.error??new Error("IndexedDB request failed"))})}function f(e){return e.pipelineVersion!==d?!1:Date.now()-e.cachedAto)&&(o=c)}return o}catch{return null}}async function M(e){const t=[...new Set(e)],n=await Promise.all(t.map(async r=>[r,await w(r)]));return Object.fromEntries(n)}async function A(e){try{const t=await p(),r=t.transaction(i,"readwrite").objectStore(i),o=r.index("patientMrn"),s=await l(o.getAllKeys(e));for(const c of s)await l(r.delete(c));t.close()}catch{}}export{N as A,x as a,S as b,g as c,A as d,E as h,M as l,I as m}; diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/other-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/other-0.png deleted file mode 100644 index b1270af..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/other-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/post-trans-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/post-trans-neg-0.png deleted file mode 100644 index 2d15b91..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/post-trans-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/post-trans-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/post-trans-pos-0.png deleted file mode 100644 index 64198c1..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/post-trans-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/sup-long-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/sup-long-neg-0.png deleted file mode 100644 index 6560330..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/sup-long-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/sup-long-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/sup-long-pos-0.png deleted file mode 100644 index 47fce78..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-001/sup-long-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/other-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/other-0.png deleted file mode 100644 index b1270af..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/other-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/post-trans-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/post-trans-neg-0.png deleted file mode 100644 index d0db334..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/post-trans-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/post-trans-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/post-trans-pos-0.png deleted file mode 100644 index 89364e8..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/post-trans-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/sup-long-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/sup-long-neg-0.png deleted file mode 100644 index 7445c88..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/sup-long-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/sup-long-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/sup-long-pos-0.png deleted file mode 100644 index 48e1942..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-002/sup-long-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/other-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/other-0.png deleted file mode 100644 index 5fe2fbe..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/other-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/post-trans-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/post-trans-neg-0.png deleted file mode 100644 index 2d15b91..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/post-trans-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/post-trans-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/post-trans-pos-0.png deleted file mode 100644 index 64198c1..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/post-trans-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/sup-long-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/sup-long-neg-0.png deleted file mode 100644 index de0cee1..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/sup-long-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/sup-long-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/sup-long-pos-0.png deleted file mode 100644 index c3ddf06..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-003/sup-long-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/other-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/other-0.png deleted file mode 100644 index b1270af..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/other-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/post-trans-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/post-trans-neg-0.png deleted file mode 100644 index 2d15b91..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/post-trans-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/post-trans-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/post-trans-pos-0.png deleted file mode 100644 index 89364e8..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/post-trans-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/sup-long-neg-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/sup-long-neg-0.png deleted file mode 100644 index de0cee1..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/sup-long-neg-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/sup-long-pos-0.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/sup-long-pos-0.png deleted file mode 100644 index dbd453b..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/patient-profiles/p-004/sup-long-pos-0.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/segmentation-mask.svg b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/segmentation-mask.svg deleted file mode 100644 index 4ed642a..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/segmentation-mask.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - Mng H - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/README.md b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/README.md deleted file mode 100644 index aa683ec..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Stitch Project Assets - -**Project:** MSK Collaborative Workspace -**Project ID:** `17628337625105266704` - -| Screen | Screen ID | Route mapping | -|--------|-----------|---------------| -| Danh sách công việc - Lumina MSK | `9d638112797c4b8d8a100d526bab43f2` | `/` (Patient Worklist) | -| Chế độ Kiểm tra Lâm sàng - Lumina MSK (VN) | `fa207d6a4f684f32a3b99706275daad9` | `/workspace/:id` (Clinical Review) | -| Không gian chẩn đoán - AI Review (VN) | `03c138643dd9427aaa59a6e16d089793` | Safety overlay state | -| Chế độ siêu âm gốc - Metadata Tinh gọn (Refined Raw View) | `85ece6c4ed6f4c179042b59813237d44` | Segmentation legend bubble on `DiagnosticCanvas` | -| Annotation Workspace with AI Hub | `3bdfcf6ba964447d88b98c8b9b0df9f1` | Floating `AnnotationRibbon` on `DiagnosticCanvas` | - -## Fetching assets - -Stitch API requires OAuth2 (`STITCH_ACCESS_TOKEN` + `GOOGLE_CLOUD_PROJECT`), not a generic API key. - -```bash -# Using @google/stitch-sdk (after OAuth setup) -npm install @google/stitch-sdk -STITCH_ACCESS_TOKEN=... GOOGLE_CLOUD_PROJECT=... node scripts/fetch-stitch.mjs -``` - -Or use Stitch MCP `get_screen` with `projectId` and `screenId`, then download: -- `htmlCode.downloadUrl` → save to `public/assets/stitch//index.html` -- `screenshot.downloadUrl` → save to `public/assets/stitch//screenshot.png` - -## Local placeholders - -This mockup uses handcrafted SVG assets in `public/assets/` aligned with `spec/DESIGN.md` tokens, since Stitch OAuth was unavailable during build. - -Reference thumbnails (design intent): -- `worklist-reference.svg` -- `clinical-review-reference.svg` -- `ai-review-reference.svg` diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/ai-review-reference.svg b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/ai-review-reference.svg deleted file mode 100644 index 290d9b8..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/ai-review-reference.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - Ch  Leo thang An ton - i thoi Socratic - - - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/clinical-review-reference.svg b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/clinical-review-reference.svg deleted file mode 100644 index bcb4df6..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/clinical-review-reference.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - -  XUT AI - - 2 - - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/worklist-reference.svg b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/worklist-reference.svg deleted file mode 100644 index 8629680..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/stitch/worklist-reference.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - Danh sch cng vic - - - - - - - NFR EMR - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/trans_flex-8mO3g_5i.png b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/trans_flex-8mO3g_5i.png deleted file mode 100644 index b1270af..0000000 Binary files a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/trans_flex-8mO3g_5i.png and /dev/null differ diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/uixSessionRecorder.worker-IeiLyPFv.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/uixSessionRecorder.worker-IeiLyPFv.js deleted file mode 100644 index d882601..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/uixSessionRecorder.worker-IeiLyPFv.js +++ /dev/null @@ -1 +0,0 @@ -function w(n){return[...n].sort((t,e)=>t.relTime!==e.relTime?t.relTime-e.relTime:t.timestamp-e.timestamp)}const f="UIX_Agent_Recorder",a="pending_chunks",p=1,I=1440*60*1e3;function i(n){return new Promise((t,e)=>{n.onsuccess=()=>t(n.result),n.onerror=()=>e(n.error??new Error("IndexedDB request failed"))})}function d(){return new Promise((n,t)=>{const e=indexedDB.open(f,p);e.onerror=()=>t(e.error??new Error("Failed to open UIX WAL IndexedDB")),e.onupgradeneeded=()=>{const o=e.result;if(!o.objectStoreNames.contains(a)){const r=o.createObjectStore(a,{keyPath:"chunkId"});r.createIndex("patientId","patientId",{unique:!1}),r.createIndex("sessionId","sessionId",{unique:!1}),r.createIndex("createdAt","createdAt",{unique:!1})}},e.onsuccess=()=>n(e.result)})}async function k(n){const t=await d();try{const e=t.transaction(a,"readwrite");await i(e.objectStore(a).put(n)),await new Promise((o,r)=>{e.oncomplete=()=>o(),e.onerror=()=>r(e.error??new Error("WAL put transaction failed"))})}finally{t.close()}}async function y(n){const t=await d();try{const e=t.transaction(a,"readwrite");await i(e.objectStore(a).delete(n)),await new Promise((o,r)=>{e.oncomplete=()=>o(),e.onerror=()=>r(e.error??new Error("WAL delete transaction failed"))})}finally{t.close()}}async function _(n){const t=await d();try{const e=t.transaction(a,"readonly"),o=e.objectStore(a).index("patientId"),r=await i(o.getAll(n));return await new Promise((c,s)=>{e.oncomplete=()=>c(),e.onerror=()=>s(e.error??new Error("WAL read transaction failed"))}),r.sort((c,s)=>c.createdAt-s.createdAt)}finally{t.close()}}async function h(){const n=Date.now()-I,t=await d();let e=0;try{const o=t.transaction(a,"readwrite"),r=o.objectStore(a),c=await i(r.getAll());for(const s of c)s.createdAt{o.oncomplete=()=>s(),o.onerror=()=>l(o.error??new Error("WAL purge transaction failed"))})}finally{t.close()}return e}async function m(n){return!0}async function u(n){try{await m(n)&&await y(n.chunkId)}catch{}}async function A(n){const t=w(n.chunk.events),e={...n.chunk,events:t};await k(e),self.postMessage({type:"wal_committed",requestId:n.requestId,chunkId:e.chunkId,events:t}),u(e)}async function g(n){const t=await _(n.patientId);for(const e of t)u(e);self.postMessage({type:"wal_recovered",requestId:n.requestId,patientId:n.patientId,chunks:t})}async function b(n){const t=await h();self.postMessage({type:"wal_purge_stale_done",requestId:n.requestId,removedCount:t})}self.onmessage=n=>{const t=n.data;if(!(t!=null&&t.requestId))return;const e=r=>{self.postMessage({type:"wal_error",requestId:t.requestId,message:r})};(async()=>{switch(t.type){case"wal_flush":await A(t);break;case"wal_recover_patient":await g(t);break;case"wal_purge_stale":await b(t);break;default:e("Unknown WAL worker message type")}})().catch(r=>{e(r instanceof Error?r.message:"WAL worker operation failed")})}; diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/ultrasound-frame.svg b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/ultrasound-frame.svg deleted file mode 100644 index 57c92fa..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/ultrasound-frame.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - SIU M KHP GI - 12.5 MHz Gain 62 - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/vendor-ml-l0sNRNKZ.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/vendor-ml-l0sNRNKZ.js deleted file mode 100644 index 8b13789..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/vendor-ml-l0sNRNKZ.js +++ /dev/null @@ -1 +0,0 @@ - diff --git a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/vendor-react-BWW3uBCb.js b/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/vendor-react-BWW3uBCb.js deleted file mode 100644 index bd5bc5d..0000000 --- a/workspace/sprint_1_2/CODEBASE/frontend/distribute_asset/assets/vendor-react-BWW3uBCb.js +++ /dev/null @@ -1,28 +0,0 @@ -function pt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var me={exports:{}},w={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Oe;function mt(){if(Oe)return w;Oe=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),u=Symbol.for("react.consumer"),s=Symbol.for("react.context"),f=Symbol.for("react.forward_ref"),l=Symbol.for("react.suspense"),o=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),h=Symbol.for("react.activity"),m=Symbol.iterator;function R(i){return i===null||typeof i!="object"?null:(i=m&&i[m]||i["@@iterator"],typeof i=="function"?i:null)}var E={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,g={};function y(i,p,S){this.props=i,this.context=p,this.refs=g,this.updater=S||E}y.prototype.isReactComponent={},y.prototype.setState=function(i,p){if(typeof i!="object"&&typeof i!="function"&&i!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,i,p,"setState")},y.prototype.forceUpdate=function(i){this.updater.enqueueForceUpdate(this,i,"forceUpdate")};function _(){}_.prototype=y.prototype;function x(i,p,S){this.props=i,this.context=p,this.refs=g,this.updater=S||E}var $=x.prototype=new _;$.constructor=x,v($,y.prototype),$.isPureReactComponent=!0;var O=Array.isArray;function D(){}var P={H:null,A:null,T:null,S:null},W=Object.prototype.hasOwnProperty;function Y(i,p,S){var C=S.ref;return{$$typeof:e,type:i,key:p,ref:C!==void 0?C:null,props:S}}function X(i,p){return Y(i.type,p,i.props)}function Q(i){return typeof i=="object"&&i!==null&&i.$$typeof===e}function pe(i){var p={"=":"=0",":":"=2"};return"$"+i.replace(/[=:]/g,function(S){return p[S]})}var Z=/\/+/g;function z(i,p){return typeof i=="object"&&i!==null&&i.key!=null?pe(""+i.key):p.toString(36)}function F(i){switch(i.status){case"fulfilled":return i.value;case"rejected":throw i.reason;default:switch(typeof i.status=="string"?i.then(D,D):(i.status="pending",i.then(function(p){i.status==="pending"&&(i.status="fulfilled",i.value=p)},function(p){i.status==="pending"&&(i.status="rejected",i.reason=p)})),i.status){case"fulfilled":return i.value;case"rejected":throw i.reason}}throw i}function K(i,p,S,C,b){var T=typeof i;(T==="undefined"||T==="boolean")&&(i=null);var L=!1;if(i===null)L=!0;else switch(T){case"bigint":case"string":case"number":L=!0;break;case"object":switch(i.$$typeof){case e:case t:L=!0;break;case d:return L=i._init,K(L(i._payload),p,S,C,b)}}if(L)return b=b(i),L=C===""?"."+z(i,0):C,O(b)?(S="",L!=null&&(S=L.replace(Z,"$&/")+"/"),K(b,p,S,"",function(ht){return ht})):b!=null&&(Q(b)&&(b=X(b,S+(b.key==null||i&&i.key===b.key?"":(""+b.key).replace(Z,"$&/")+"/")+L)),p.push(b)),1;L=0;var B=C===""?".":C+":";if(O(i))for(var N=0;N"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),ye.exports=yt(),ye.exports}/** - * react-router v7.18.0 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */var Ce=/^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i,We=/^[\\/]{2}/;function vt(e,t){return t+e.replace(/\\/g,"/")}var Ne="popstate";function Ie(e){return typeof e=="object"&&e!=null&&"pathname"in e&&"search"in e&&"hash"in e&&"state"in e&&"key"in e}function Rt(e={}){function t(r,a){var o;let u=(o=a.state)==null?void 0:o.masked,{pathname:s,search:f,hash:l}=u||r.location;return Ee("",{pathname:s,search:f,hash:l},a.state&&a.state.usr||null,a.state&&a.state.key||"default",u?{pathname:r.location.pathname,search:r.location.search,hash:r.location.hash}:void 0)}function n(r,a){return typeof a=="string"?a:te(a)}return wt(t,n,null,e)}function k(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function H(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Et(){return Math.random().toString(36).substring(2,10)}function Me(e,t){return{usr:e.state,key:e.key,idx:t,masked:e.mask?{pathname:e.pathname,search:e.search,hash:e.hash}:void 0}}function Ee(e,t,n=null,r,a){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?G(t):t,state:n,key:t&&t.key||r||Et(),mask:a}}function te({pathname:e="/",search:t="",hash:n=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),n&&n!=="#"&&(e+=n.charAt(0)==="#"?n:"#"+n),e}function G(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function wt(e,t,n,r={}){let{window:a=document.defaultView,v5Compat:u=!1}=r,s=a.history,f="POP",l=null,o=d();o==null&&(o=0,s.replaceState({...s.state,idx:o},""));function d(){return(s.state||{idx:null}).idx}function h(){f="POP";let g=d(),y=g==null?null:g-o;o=g,l&&l({action:f,location:v.location,delta:y})}function m(g,y){f="PUSH";let _=Ie(g)?g:Ee(v.location,g,y);o=d()+1;let x=Me(_,o),$=v.createHref(_.mask||_);try{s.pushState(x,"",$)}catch(O){if(O instanceof DOMException&&O.name==="DataCloneError")throw O;a.location.assign($)}u&&l&&l({action:f,location:v.location,delta:1})}function R(g,y){f="REPLACE";let _=Ie(g)?g:Ee(v.location,g,y);o=d();let x=Me(_,o),$=v.createHref(_.mask||_);s.replaceState(x,"",$),u&&l&&l({action:f,location:v.location,delta:0})}function E(g){return Ct(a,g)}let v={get action(){return f},get location(){return e(a,s)},listen(g){if(l)throw new Error("A history only accepts one active listener");return a.addEventListener(Ne,h),l=g,()=>{a.removeEventListener(Ne,h),l=null}},createHref(g){return t(a,g)},createURL:E,encodeLocation(g){let y=E(g);return{pathname:y.pathname,search:y.search,hash:y.hash}},push:m,replace:R,go(g){return s.go(g)}};return v}function Ct(e,t,n=!1){let r="http://localhost";e&&(r=e.location.origin!=="null"?e.location.origin:e.location.href),k(r,"No window.location.(origin|href) available to create URL");let a=typeof t=="string"?t:te(t);return a=a.replace(/ $/,"%20"),!n&&We.test(a)&&(a=r+a),new URL(a,r)}function Ye(e,t,n="/"){return St(e,t,n,!1)}function St(e,t,n,r,a){let u=typeof t=="string"?G(t):t,s=q(u.pathname||"/",n);if(s==null)return null;let f=_t(e),l=null,o=Nt(s);for(let d=0;l==null&&d{let d={relativePath:o===void 0?s.path||"":o,caseSensitive:s.caseSensitive===!0,childrenIndex:f,route:s};if(d.relativePath.startsWith("/")){if(!d.relativePath.startsWith(r)&&l)return;k(d.relativePath.startsWith(r),`Absolute route path "${d.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),d.relativePath=d.relativePath.slice(r.length)}let h=M([r,d.relativePath]),m=n.concat(d);s.children&&s.children.length>0&&(k(s.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${h}".`),qe(s.children,t,m,h,l)),!(s.path==null&&!s.index)&&t.push({path:h,score:$t(h,s.index),routesMeta:m.map((R,E)=>{let[v,g]=Ge(R.relativePath,R.caseSensitive,E===m.length-1);return{...R,matcher:v,compiledParams:g}})})};return e.forEach((s,f)=>{var l;if(s.path===""||!((l=s.path)!=null&&l.includes("?")))u(s,f);else for(let o of ze(s.path))u(s,f,!0,o)}),t}function ze(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,a=n.endsWith("?"),u=n.replace(/\?$/,"");if(r.length===0)return a?[u,""]:[u];let s=ze(r.join("/")),f=[];return f.push(...s.map(l=>l===""?u:[u,l].join("/"))),a&&f.push(...s),f.map(l=>e.startsWith("/")&&l===""?"/":l)}function xt(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:At(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}var Pt=/^:[\w-]+$/,bt=3,Tt=2,Lt=1,kt=10,Ot=-2,He=e=>e==="*";function $t(e,t){let n=e.split("/"),r=n.length;return n.some(He)&&(r+=Ot),t&&(r+=Tt),n.filter(a=>!He(a)).reduce((a,u)=>a+(Pt.test(u)?bt:u===""?Lt:kt),r)}function At(e,t){return e.length===t.length&&e.slice(0,-1).every((r,a)=>r===t[a])?e[e.length-1]-t[t.length-1]:0}function Dt(e,t,n=!1){let{routesMeta:r}=e,a={},u="/",s=[];for(let f=0;f{if(d==="*"){let E=f[m]||"";s=u.slice(0,u.length-E.length).replace(/(.)\/+$/,"$1")}const R=f[m];return h&&!R?o[d]=void 0:o[d]=(R||"").replace(/%2F/g,"/"),o},{}),pathname:u,pathnameBase:s,pattern:e}}function Ge(e,t=!1,n=!0){H(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let r=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(s,f,l,o,d)=>{if(r.push({paramName:f,isOptional:l!=null}),l){let h=d.charAt(o+s.length);return h&&h!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return e.endsWith("*")?(r.push({paramName:"*"}),a+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?a+="\\/*$":e!==""&&e!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),r]}function Nt(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return H(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function q(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function It(e,t="/"){let{pathname:n,search:r="",hash:a=""}=typeof e=="string"?G(e):e,u;return n?(n=Ve(n),n.startsWith("/")?u=Ue(n.substring(1),"/"):u=Ue(n,t)):u=t,{pathname:u,search:Ut(r),hash:Ft(a)}}function Ue(e,t){let n=se(t).split("/");return e.split("/").forEach(a=>{a===".."?n.length>1&&n.pop():a!=="."&&n.push(a)}),n.length>1?n.join("/"):"/"}function ge(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Mt(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function Se(e){let t=Mt(e);return t.map((n,r)=>r===t.length-1?n.pathname:n.pathnameBase)}function ce(e,t,n,r=!1){let a;typeof e=="string"?a=G(e):(a={...e},k(!a.pathname||!a.pathname.includes("?"),ge("?","pathname","search",a)),k(!a.pathname||!a.pathname.includes("#"),ge("#","pathname","hash",a)),k(!a.search||!a.search.includes("#"),ge("#","search","hash",a)));let u=e===""||a.pathname==="",s=u?"/":a.pathname,f;if(s==null)f=n;else{let h=t.length-1;if(!r&&s.startsWith("..")){let m=s.split("/");for(;m[0]==="..";)m.shift(),h-=1;a.pathname=m.join("/")}f=h>=0?t[h]:"/"}let l=It(a,f),o=s&&s!=="/"&&s.endsWith("/"),d=(u||s===".")&&n.endsWith("/");return!l.pathname.endsWith("/")&&(o||d)&&(l.pathname+="/"),l}var Ve=e=>e.replace(/[\\/]{2,}/g,"/"),M=e=>Ve(e.join("/")),se=e=>e.replace(/\/+$/,""),Ht=e=>se(e).replace(/^\/*/,"/"),Ut=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,Ft=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e,Bt=class{constructor(e,t,n,r=!1){this.status=e,this.statusText=t||"",this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function jt(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}function Wt(e){let t=e.map(n=>n.route.path).filter(Boolean);return M(t)||"/"}var Je=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Xe(e,t){let n=e;if(typeof n!="string"||!Ce.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,a=!1;if(Je)try{let u=new URL(window.location.href),s=We.test(n)?new URL(vt(n,u.protocol)):new URL(n),f=q(s.pathname,t);s.origin===u.origin&&f!=null?n=f+s.search+s.hash:a=!0}catch{H(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:a,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var Qe=["POST","PUT","PATCH","DELETE"];new Set(Qe);var Yt=["GET",...Qe];new Set(Yt);var qt=["about:","blob:","chrome:","chrome-untrusted:","content:","data:","devtools:","file:","filesystem:","javascript:"];function zt(e){try{return qt.includes(new URL(e).protocol)}catch{return!1}}var V=c.createContext(null);V.displayName="DataRouter";var fe=c.createContext(null);fe.displayName="DataRouterState";var Ze=c.createContext(!1);function Kt(){return c.useContext(Ze)}var et=c.createContext({isTransitioning:!1});et.displayName="ViewTransition";var Gt=c.createContext(new Map);Gt.displayName="Fetchers";var Vt=c.createContext(null);Vt.displayName="Await";var I=c.createContext(null);I.displayName="Navigation";var re=c.createContext(null);re.displayName="Location";var U=c.createContext({outlet:null,matches:[],isDataRoute:!1});U.displayName="Route";var _e=c.createContext(null);_e.displayName="RouteError";var tt="REACT_ROUTER_ERROR",Jt="REDIRECT",Xt="ROUTE_ERROR_RESPONSE";function Qt(e){if(e.startsWith(`${tt}:${Jt}:{`))try{let t=JSON.parse(e.slice(28));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.location=="string"&&typeof t.reloadDocument=="boolean"&&typeof t.replace=="boolean")return t}catch{}}function Zt(e){if(e.startsWith(`${tt}:${Xt}:{`))try{let t=JSON.parse(e.slice(40));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string")return new Bt(t.status,t.statusText,t.data)}catch{}}function er(e,{relative:t}={}){k(J(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:r}=c.useContext(I),{hash:a,pathname:u,search:s}=ne(e,{relative:t}),f=u;return n!=="/"&&(f=u==="/"?n:M([n,u])),r.createHref({pathname:f,search:s,hash:a})}function J(){return c.useContext(re)!=null}function j(){return k(J(),"useLocation() may be used only in the context of a component."),c.useContext(re).location}var rt="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function nt(e){c.useContext(I).static||c.useLayoutEffect(e)}function at(){let{isDataRoute:e}=c.useContext(U);return e?hr():tr()}function tr(){k(J(),"useNavigate() may be used only in the context of a component.");let e=c.useContext(V),{basename:t,navigator:n}=c.useContext(I),{matches:r}=c.useContext(U),{pathname:a}=j(),u=JSON.stringify(Se(r)),s=c.useRef(!1);return nt(()=>{s.current=!0}),c.useCallback((l,o={})=>{if(H(s.current,rt),!s.current)return;if(typeof l=="number"){n.go(l);return}let d=ce(l,JSON.parse(u),a,o.relative==="path");e==null&&t!=="/"&&(d.pathname=d.pathname==="/"?t:M([t,d.pathname])),(o.replace?n.replace:n.push)(d,o.state,o)},[t,n,u,a,e])}c.createContext(null);function Jr(){let{matches:e}=c.useContext(U),t=e[e.length-1];return(t==null?void 0:t.params)??{}}function ne(e,{relative:t}={}){let{matches:n}=c.useContext(U),{pathname:r}=j(),a=JSON.stringify(Se(n));return c.useMemo(()=>ce(e,JSON.parse(a),r,t==="path"),[e,a,r,t])}function rr(e,t){return ot(e,t)}function ot(e,t,n){var g;k(J(),"useRoutes() may be used only in the context of a component.");let{navigator:r}=c.useContext(I),{matches:a}=c.useContext(U),u=a[a.length-1],s=u?u.params:{},f=u?u.pathname:"/",l=u?u.pathnameBase:"/",o=u&&u.route;{let y=o&&o.path||"";ut(f,!o||y.endsWith("*")||y.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${f}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. - -Please change the parent to .`)}let d=j(),h;if(t){let y=typeof t=="string"?G(t):t;k(l==="/"||((g=y.pathname)==null?void 0:g.startsWith(l)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${l}" but pathname "${y.pathname}" was given in the \`location\` prop.`),h=y}else h=d;let m=h.pathname||"/",R=m;if(l!=="/"){let y=l.replace(/^\//,"").split("/");R="/"+m.replace(/^\//,"").split("/").slice(y.length).join("/")}let E=n&&n.state.matches.length?n.state.matches.map(y=>Object.assign(y,{route:n.manifest[y.route.id]||y.route})):Ye(e,{pathname:R});H(o||E!=null,`No routes matched location "${h.pathname}${h.search}${h.hash}" `),H(E==null||E[E.length-1].route.element!==void 0||E[E.length-1].route.Component!==void 0||E[E.length-1].route.lazy!==void 0,`Matched leaf route at location "${h.pathname}${h.search}${h.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let v=ur(E&&E.map(y=>Object.assign({},y,{params:Object.assign({},s,y.params),pathname:M([l,r.encodeLocation?r.encodeLocation(y.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:y.pathname]),pathnameBase:y.pathnameBase==="/"?l:M([l,r.encodeLocation?r.encodeLocation(y.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:y.pathnameBase])})),a,n);return t&&v?c.createElement(re.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...h},navigationType:"POP"}},v):v}function nr(){let e=dr(),t=jt(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:r},u={padding:"2px 4px",backgroundColor:r},s=null;return console.error("Error handled by React Router default ErrorBoundary:",e),s=c.createElement(c.Fragment,null,c.createElement("p",null,"💿 Hey developer 👋"),c.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",c.createElement("code",{style:u},"ErrorBoundary")," or"," ",c.createElement("code",{style:u},"errorElement")," prop on your route.")),c.createElement(c.Fragment,null,c.createElement("h2",null,"Unexpected Application Error!"),c.createElement("h3",{style:{fontStyle:"italic"}},t),n?c.createElement("pre",{style:a},n):null,s)}var ar=c.createElement(nr,null),it=class extends c.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error("React Router caught the following error during render",e)}render(){let e=this.state.error;if(this.context&&typeof e=="object"&&e&&"digest"in e&&typeof e.digest=="string"){const n=Zt(e.digest);n&&(e=n)}let t=e!==void 0?c.createElement(U.Provider,{value:this.props.routeContext},c.createElement(_e.Provider,{value:e,children:this.props.component})):this.props.children;return this.context?c.createElement(or,{error:e},t):t}};it.contextType=Ze;var ve=new WeakMap;function or({children:e,error:t}){let{basename:n}=c.useContext(I);if(typeof t=="object"&&t&&"digest"in t&&typeof t.digest=="string"){let r=Qt(t.digest);if(r){let a=ve.get(t);if(a)throw a;let u=Xe(r.location,n),s=u.absoluteURL||u.to;if(zt(s))throw new Error("Invalid redirect location");if(Je&&!ve.get(t))if(u.isExternal||r.reloadDocument)window.location.href=s;else{const f=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(u.to,{replace:r.replace}));throw ve.set(t,f),f}return c.createElement("meta",{httpEquiv:"refresh",content:`0;url=${s}`})}}return e}function ir({routeContext:e,match:t,children:n}){let r=c.useContext(V);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),c.createElement(U.Provider,{value:e},n)}function ur(e,t=[],n){let r=n==null?void 0:n.state;if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let a=e,u=r==null?void 0:r.errors;if(u!=null){let d=a.findIndex(h=>h.route.id&&(u==null?void 0:u[h.route.id])!==void 0);k(d>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(u).join(",")}`),a=a.slice(0,Math.min(a.length,d+1))}let s=!1,f=-1;if(n&&r){s=r.renderFallback;for(let d=0;d=0?a=a.slice(0,f+1):a=[a[0]];break}}}}let l=n==null?void 0:n.onError,o=r&&l?(d,h)=>{var m,R;l(d,{location:r.location,params:((R=(m=r.matches)==null?void 0:m[0])==null?void 0:R.params)??{},pattern:Wt(r.matches),errorInfo:h})}:void 0;return a.reduceRight((d,h,m)=>{let R,E=!1,v=null,g=null;r&&(R=u&&h.route.id?u[h.route.id]:void 0,v=h.route.errorElement||ar,s&&(f<0&&m===0?(ut("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),E=!0,g=null):f===m&&(E=!0,g=h.route.hydrateFallbackElement||null)));let y=t.concat(a.slice(0,m+1)),_=()=>{let x;return R?x=v:E?x=g:h.route.Component?x=c.createElement(h.route.Component,null):h.route.element?x=h.route.element:x=d,c.createElement(ir,{match:h,routeContext:{outlet:d,matches:y,isDataRoute:r!=null},children:x})};return r&&(h.route.ErrorBoundary||h.route.errorElement||m===0)?c.createElement(it,{location:r.location,revalidation:r.revalidation,component:v,error:R,children:_(),routeContext:{outlet:null,matches:y,isDataRoute:!0},onError:o}):_()},null)}function xe(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function lr(e){let t=c.useContext(V);return k(t,xe(e)),t}function sr(e){let t=c.useContext(fe);return k(t,xe(e)),t}function cr(e){let t=c.useContext(U);return k(t,xe(e)),t}function Pe(e){let t=cr(e),n=t.matches[t.matches.length-1];return k(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}function fr(){return Pe("useRouteId")}function dr(){var r;let e=c.useContext(_e),t=sr("useRouteError"),n=Pe("useRouteError");return e!==void 0?e:(r=t.errors)==null?void 0:r[n]}function hr(){let{router:e}=lr("useNavigate"),t=Pe("useNavigate"),n=c.useRef(!1);return nt(()=>{n.current=!0}),c.useCallback(async(a,u={})=>{H(n.current,rt),n.current&&(typeof a=="number"?await e.navigate(a):await e.navigate(a,{fromRouteId:t,...u}))},[e,t])}var Fe={};function ut(e,t,n){!t&&!Fe[e]&&(Fe[e]=!0,H(!1,n))}c.memo(pr);function pr({routes:e,manifest:t,future:n,state:r,isStatic:a,onError:u}){return ot(e,void 0,{manifest:t,state:r,isStatic:a,onError:u})}function Xr({to:e,replace:t,state:n,relative:r}){k(J()," may be used only in the context of a component.");let{static:a}=c.useContext(I);H(!a," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:u}=c.useContext(U),{pathname:s}=j(),f=at(),l=ce(e,Se(u),s,r==="path"),o=JSON.stringify(l);return c.useEffect(()=>{f(JSON.parse(o),{replace:t,state:n,relative:r})},[f,o,r,t,n]),null}function mr(e){k(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function yr({basename:e="/",children:t=null,location:n,navigationType:r="POP",navigator:a,static:u=!1,useTransitions:s}){k(!J(),"You cannot render a inside another . You should never have more than one in your app.");let f=e.replace(/^\/*/,"/"),l=c.useMemo(()=>({basename:f,navigator:a,static:u,useTransitions:s,future:{}}),[f,a,u,s]);typeof n=="string"&&(n=G(n));let{pathname:o="/",search:d="",hash:h="",state:m=null,key:R="default",mask:E}=n,v=c.useMemo(()=>{let g=q(o,f);return g==null?null:{location:{pathname:g,search:d,hash:h,state:m,key:R,mask:E},navigationType:r}},[f,o,d,h,m,R,r,E]);return H(v!=null,` is not able to match the URL "${o}${d}${h}" because it does not start with the basename, so the won't render anything.`),v==null?null:c.createElement(I.Provider,{value:l},c.createElement(re.Provider,{children:t,value:v}))}function Qr({children:e,location:t}){return rr(we(e),t)}function we(e,t=[]){let n=[];return c.Children.forEach(e,(r,a)=>{if(!c.isValidElement(r))return;let u=[...t,a];if(r.type===c.Fragment){n.push.apply(n,we(r.props.children,u));return}k(r.type===mr,`[${typeof r.type=="string"?r.type:r.type.name}] is not a component. All component children of must be a or `),k(!r.props.index||!r.props.children,"An index route cannot have child routes.");let s={id:r.props.id||u.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,middleware:r.props.middleware,loader:r.props.loader,action:r.props.action,hydrateFallbackElement:r.props.hydrateFallbackElement,HydrateFallback:r.props.HydrateFallback,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.hasErrorBoundary===!0||r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(s.children=we(r.props.children,u)),n.push(s)}),n}var ie="get",ue="application/x-www-form-urlencoded";function de(e){return typeof HTMLElement<"u"&&e instanceof HTMLElement}function gr(e){return de(e)&&e.tagName.toLowerCase()==="button"}function vr(e){return de(e)&&e.tagName.toLowerCase()==="form"}function Rr(e){return de(e)&&e.tagName.toLowerCase()==="input"}function Er(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function wr(e,t){return e.button===0&&(!t||t==="_self")&&!Er(e)}var oe=null;function Cr(){if(oe===null)try{new FormData(document.createElement("form"),0),oe=!1}catch{oe=!0}return oe}var Sr=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Re(e){return e!=null&&!Sr.has(e)?(H(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${ue}"`),null):e}function _r(e,t){let n,r,a,u,s;if(vr(e)){let f=e.getAttribute("action");r=f?q(f,t):null,n=e.getAttribute("method")||ie,a=Re(e.getAttribute("enctype"))||ue,u=new FormData(e)}else if(gr(e)||Rr(e)&&(e.type==="submit"||e.type==="image")){let f=e.form;if(f==null)throw new Error('Cannot submit a