From 0e3e3a29748fbf4b5f460fda3465824a3ddd6e1d Mon Sep 17 00:00:00 2001 From: lockbitchat Date: Fri, 26 Jun 2026 00:00:13 -0400 Subject: [PATCH] Add multiple-session support: run several independent encrypted chats at once Each conversation now runs its own WebRTC session with separate keys and SAS verification, so chats never mix. Adds a side panel to switch between open chats with unread badges, a New chat action that leaves existing chats connected, per-chat local labels stored only on this device, and an availability status (Available, Away, Busy, Invisible) shared end-to-end with connected peers. Also includes vendored Prism syntax highlighting, more reliable PWA update handling, and offline send queueing fixes. Version 4.10.0. --- README.md | 7 +- assets/tailwind.css | 2 +- dist/app.js | 1817 ++++++++++++++++++++++--------- dist/app.js.map | 8 +- index.html | 44 +- libs/prism/prism.css | 1 + libs/prism/prism.js | 18 + manifest.json | 2 +- meta.json | 14 +- package.json | 4 +- scripts/post-build.js | 32 +- src/app.jsx | 1249 +++++++++++++++------ src/state/sessionsStore.js | 336 ++++++ src/utils/updateManager.js | 74 +- sw.js | 5 + tests/sessions-reducer.test.mjs | 139 +++ 16 files changed, 2828 insertions(+), 924 deletions(-) create mode 100644 libs/prism/prism.css create mode 100644 libs/prism/prism.js create mode 100644 src/state/sessionsStore.js create mode 100644 tests/sessions-reducer.test.mjs diff --git a/README.md b/README.md index d1f5a80..e22d2d4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ No accounts. No servers storing your messages. No installation required. [![License: MIT](https://img.shields.io/badge/License-MIT-f0892a.svg)](LICENSE) -[![Version](https://img.shields.io/badge/version-4.9.1-3ecf8e.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-4.10.0-3ecf8e.svg)](CHANGELOG.md) [![PWA](https://img.shields.io/badge/PWA-installable-3ecf8e.svg)](#install-as-an-app) [![Encryption](https://img.shields.io/badge/crypto-ECDH%20P--384%20%C2%B7%20AES--256--GCM-blue.svg)](#security-model) @@ -48,6 +48,11 @@ It is designed for people who need a small, auditable, zero-infrastructure way t - Unsend (delete for everyone) over the authenticated control channel. - WhatsApp-style delivery status (sending → sent → delivered) with offline store-and-forward. +**Multiple conversations** +- Run several independent chats at the same time. Every conversation gets its own encrypted session, keys and verification, so two chats can never mix. +- A side panel lists your open chats with unread badges. Switching is instant, and starting a new chat leaves the others connected. +- Set your availability (Available, Away, Busy or Invisible) and connected peers can see it. You can also give each chat a private label that is stored only on your device and is never sent to the other side. + ** File transfer** - Consent-gated, end-to-end encrypted transfers with resumable, per-chunk progress. - Strict file-type allowlist; executable and scriptable formats are rejected. diff --git a/assets/tailwind.css b/assets/tailwind.css index d69e0fa..30fb34e 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -1 +1 @@ -*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:1rem}.right-0{right:0}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem}.mb-1,.my-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-20{margin-top:5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-28{height:7rem}.h-8{height:2rem}.h-80{height:20rem}.h-9{height:2.25rem}.h-96{height:24rem}.h-\[420px\]{height:420px}.max-h-\[80vh\]{max-height:80vh}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-28{width:7rem}.w-4{width:1rem}.w-72{width:18rem}.w-8{width:2rem}.w-full{width:100%}.min-w-\[160px\]{min-width:160px}.min-w-\[180px\]{min-width:180px}.min-w-\[240px\]{min-width:240px}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[78vw\]{max-width:78vw}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.translate-x-full{--tw-translate-x:100%}.translate-x-full,.translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.self-center{align-self:center}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-500\/20{border-color:rgba(59,130,246,.2)}.border-gray-500\/20{border-color:hsla(220,9%,46%,.2)}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.border-gray-600\/30{border-color:rgba(75,85,99,.3)}.border-gray-700\/30{border-color:rgba(55,65,81,.3)}.border-green-500\/20{border-color:rgba(34,197,94,.2)}.border-orange-500\/20{border-color:rgba(249,115,22,.2)}.border-purple-500\/20{border-color:rgba(168,85,247,.2)}.border-purple-500\/30{border-color:rgba(168,85,247,.3)}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-yellow-500\/20{border-color:rgba(234,179,8,.2)}.bg-\[rgb\(20_20_20_\/50\%\)\]{background-color:hsla(0,0%,8%,.5)}.bg-black\/20{background-color:rgba(0,0,0,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-blue-500\/90{background-color:rgba(59,130,246,.9)}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-gray-500\/10{background-color:hsla(220,9%,46%,.1)}.bg-gray-500\/20{background-color:hsla(220,9%,46%,.2)}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-gray-700\/50{background-color:rgba(55,65,81,.5)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.bg-gray-800\/95{background-color:rgba(31,41,55,.95)}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.bg-gray-900\/30{background-color:rgba(17,24,39,.3)}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-green-500\/10{background-color:rgba(34,197,94,.1)}.bg-green-500\/90{background-color:rgba(34,197,94,.9)}.bg-neutral-900{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity,1))}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.bg-orange-500\/10{background-color:rgba(249,115,22,.1)}.bg-purple-500\/10{background-color:rgba(168,85,247,.1)}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-red-500\/20{background-color:rgba(239,68,68,.2)}.bg-red-900\/50{background-color:rgba(127,29,29,.5)}.bg-transparent{background-color:transparent}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(234 179 8/var(--tw-bg-opacity,1))}.bg-yellow-500\/10{background-color:rgba(234,179,8,.1)}.bg-yellow-500\/90{background-color:rgba(234,179,8,.9)}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-\[\#1f1f1f\]\/90{--tw-gradient-from:rgba(31,31,31,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,31,31,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-800\/20{--tw-gradient-from:rgba(31,41,55,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-gray-900\/20{--tw-gradient-to:rgba(17,24,39,.2) var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.fill-blue-500{fill:#3b82f6}.fill-white{fill:#fff}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-14{padding-top:3.5rem;padding-bottom:3.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.text-\[3rem\]{font-size:3rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-\[0\.3em\]{letter-spacing:.3em}.tracking-wide{letter-spacing:.025em}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-cyan-300{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity,1))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-orange-200{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-purple-300{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity,1))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-sky-300{--tw-text-opacity:1;color:rgb(125 211 252/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/10{color:hsla(0,0%,100%,.1)}.text-yellow-200{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity,1))}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-60{opacity:.6}.opacity-90{opacity:.9}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-2xl,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-150,.transition-transform{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[checksum\:4\]{checksum:4}.\[data\:variable\]{data:variable}.\[name\:4\]{name:4}.\[size\:4\]{size:4}.hover\:bg-\[rgb\(20_20_20_\/30\%\)\]:hover{background-color:hsla(0,0%,8%,.3)}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.hover\:bg-gray-700\/40:hover{background-color:rgba(55,65,81,.4)}.hover\:bg-gray-700\/50:hover{background-color:rgba(55,65,81,.5)}.hover\:bg-green-600\/40:hover{background-color:rgba(22,163,74,.4)}.hover\:bg-orange-500\/40:hover{background-color:rgba(249,115,22,.4)}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.hover\:bg-red-500\/20:hover{background-color:rgba(239,68,68,.2)}.hover\:bg-red-500\/30:hover{background-color:rgba(239,68,68,.3)}.hover\:bg-red-600\/40:hover{background-color:rgba(220,38,38,.4)}.hover\:bg-white\/30:hover{background-color:hsla(0,0%,100%,.3)}.hover\:bg-yellow-600\/40:hover{background-color:rgba(202,138,4,.4)}.hover\:text-gray-200:hover{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.hover\:text-green-300:hover{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.hover\:text-green-400:hover{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.focus\:border-purple-400:focus{--tw-border-opacity:1;border-color:rgb(192 132 252/var(--tw-border-opacity,1))}.focus\:border-purple-500\/40:focus{border-color:rgba(168,85,247,.4)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.active\:bg-gray-700\/60:active{background-color:rgba(55,65,81,.6)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-60:disabled{opacity:.6}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width:640px){.sm\:mb-3{margin-bottom:.75rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:h-12{height:3rem}.sm\:w-12{width:3rem}.sm\:gap-4{gap:1rem}.sm\:p-4{padding:1rem}.sm\:py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:hidden{display:none}.md\:h-\[32rem\]{height:32rem}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}} \ No newline at end of file +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:1rem}.right-0{right:0}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem}.mb-1,.my-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-16{margin-top:4rem}.mt-20{margin-top:5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-28{height:7rem}.h-8{height:2rem}.h-80{height:20rem}.h-9{height:2.25rem}.h-\[420px\]{height:420px}.max-h-\[80vh\]{max-height:80vh}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-28{width:7rem}.w-4{width:1rem}.w-72{width:18rem}.w-8{width:2rem}.w-full{width:100%}.min-w-\[160px\]{min-width:160px}.min-w-\[180px\]{min-width:180px}.min-w-\[240px\]{min-width:240px}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[78vw\]{max-width:78vw}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.translate-x-full{--tw-translate-x:100%}.translate-x-full,.translate-y-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-full{--tw-translate-y:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.self-center{align-self:center}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-500\/20{border-color:rgba(59,130,246,.2)}.border-gray-500\/20{border-color:hsla(220,9%,46%,.2)}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.border-gray-600\/30{border-color:rgba(75,85,99,.3)}.border-gray-700\/30{border-color:rgba(55,65,81,.3)}.border-green-500\/20{border-color:rgba(34,197,94,.2)}.border-orange-500\/20{border-color:rgba(249,115,22,.2)}.border-purple-500\/20{border-color:rgba(168,85,247,.2)}.border-purple-500\/30{border-color:rgba(168,85,247,.3)}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-yellow-500\/20{border-color:rgba(234,179,8,.2)}.bg-\[rgb\(20_20_20_\/50\%\)\]{background-color:hsla(0,0%,8%,.5)}.bg-black\/20{background-color:rgba(0,0,0,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-blue-500\/90{background-color:rgba(59,130,246,.9)}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-gray-500\/10{background-color:hsla(220,9%,46%,.1)}.bg-gray-500\/20{background-color:hsla(220,9%,46%,.2)}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-gray-700\/50{background-color:rgba(55,65,81,.5)}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.bg-gray-800\/95{background-color:rgba(31,41,55,.95)}.bg-gray-900\/30{background-color:rgba(17,24,39,.3)}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-green-500\/10{background-color:rgba(34,197,94,.1)}.bg-green-500\/90{background-color:rgba(34,197,94,.9)}.bg-neutral-900{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity,1))}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.bg-orange-500\/10{background-color:rgba(249,115,22,.1)}.bg-purple-500\/10{background-color:rgba(168,85,247,.1)}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-red-500\/20{background-color:rgba(239,68,68,.2)}.bg-red-900\/50{background-color:rgba(127,29,29,.5)}.bg-transparent{background-color:transparent}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(234 179 8/var(--tw-bg-opacity,1))}.bg-yellow-500\/10{background-color:rgba(234,179,8,.1)}.bg-yellow-500\/90{background-color:rgba(234,179,8,.9)}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-\[\#1f1f1f\]\/90{--tw-gradient-from:rgba(31,31,31,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,31,31,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-800\/20{--tw-gradient-from:rgba(31,41,55,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-gray-900\/20{--tw-gradient-to:rgba(17,24,39,.2) var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.fill-blue-500{fill:#3b82f6}.fill-white{fill:#fff}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-14{padding-top:3.5rem;padding-bottom:3.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.text-\[3rem\]{font-size:3rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-\[0\.3em\]{letter-spacing:.3em}.tracking-wide{letter-spacing:.025em}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-cyan-300{--tw-text-opacity:1;color:rgb(103 232 249/var(--tw-text-opacity,1))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-orange-200{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-purple-300{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity,1))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-sky-300{--tw-text-opacity:1;color:rgb(125 211 252/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/10{color:hsla(0,0%,100%,.1)}.text-yellow-200{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity,1))}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-60{opacity:.6}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-150,.transition-transform{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[checksum\:4\]{checksum:4}.\[data\:variable\]{data:variable}.\[name\:4\]{name:4}.\[size\:4\]{size:4}.hover\:bg-\[rgb\(20_20_20_\/30\%\)\]:hover{background-color:hsla(0,0%,8%,.3)}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.hover\:bg-gray-700\/40:hover{background-color:rgba(55,65,81,.4)}.hover\:bg-gray-700\/50:hover{background-color:rgba(55,65,81,.5)}.hover\:bg-green-600\/40:hover{background-color:rgba(22,163,74,.4)}.hover\:bg-orange-500\/40:hover{background-color:rgba(249,115,22,.4)}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.hover\:bg-red-500\/20:hover{background-color:rgba(239,68,68,.2)}.hover\:bg-red-500\/30:hover{background-color:rgba(239,68,68,.3)}.hover\:bg-red-600\/40:hover{background-color:rgba(220,38,38,.4)}.hover\:bg-white\/30:hover{background-color:hsla(0,0%,100%,.3)}.hover\:bg-yellow-600\/40:hover{background-color:rgba(202,138,4,.4)}.hover\:text-gray-200:hover{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.hover\:text-green-300:hover{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.hover\:text-green-400:hover{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.focus\:border-purple-400:focus{--tw-border-opacity:1;border-color:rgb(192 132 252/var(--tw-border-opacity,1))}.focus\:border-purple-500\/40:focus{border-color:rgba(168,85,247,.4)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.active\:bg-gray-700\/60:active{background-color:rgba(55,65,81,.6)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-60:disabled{opacity:.6}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width:640px){.sm\:mb-3{margin-bottom:.75rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:h-12{height:3rem}.sm\:w-12{width:3rem}.sm\:gap-4{gap:1rem}.sm\:p-4{padding:1rem}.sm\:py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:hidden{display:none}.md\:h-\[32rem\]{height:32rem}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}} \ No newline at end of file diff --git a/dist/app.js b/dist/app.js index 1762eb1..96f1be7 100644 --- a/dist/app.js +++ b/dist/app.js @@ -151,6 +151,271 @@ async function clearIceSettings() { } } +// src/state/sessionsStore.js +var SESSION_ACTIONS = Object.freeze({ + CREATE_SESSION: "CREATE_SESSION", + REMOVE_SESSION: "REMOVE_SESSION", + SET_ACTIVE: "SET_ACTIVE", + SET_STATUS: "SET_STATUS", + SET_FINGERPRINT: "SET_FINGERPRINT", + SET_VERIFICATION: "SET_VERIFICATION", + SET_SAS: "SET_SAS", + ADD_MESSAGE: "ADD_MESSAGE", + SET_MESSAGES: "SET_MESSAGES", + UPDATE_MESSAGE_STATUS: "UPDATE_MESSAGE_STATUS", + DELETE_MESSAGE: "DELETE_MESSAGE", + EXPIRE_MESSAGE: "EXPIRE_MESSAGE", + INCREMENT_UNREAD: "INCREMENT_UNREAD", + CLEAR_UNREAD: "CLEAR_UNREAD", + SET_PENDING_FILES: "SET_PENDING_FILES", + PATCH_SETUP: "PATCH_SETUP", + RENAME: "RENAME", + SET_PEER_PRESENCE: "SET_PEER_PRESENCE" +}); +var PRESENCE_DOT = { available: "#3ecf8e", away: "#e3b341", busy: "#e5727a", offline: "#6b6b73" }; +var PRESENCE_WORD = { available: "Available", away: "Away", busy: "Busy", offline: "Offline" }; +var MY_STATUS_OPTIONS = [ + { key: "available", word: "Available", desc: "Online and reachable", dot: "#3ecf8e" }, + { key: "away", word: "Away", desc: "Idle \xB7 stepped away", dot: "#e3b341" }, + { key: "busy", word: "Busy", desc: "Do not disturb", dot: "#e5727a" }, + { key: "invisible", word: "Invisible", desc: "Appear offline to peers", dot: "#6b6b73" } +]; +function shortLabelFromId(id) { + const hex = String(id || "").replace(/[^a-z0-9]/gi, ""); + return "Chat " + (hex.slice(0, 4) || "0000").toUpperCase(); +} +function monoInitials(label) { + const words = String(label || "").trim().split(/\s+/).filter(Boolean); + const a = words[0]?.[0] || ""; + const b = words[1]?.[0] || words[0]?.[1] || ""; + return (a + b).toUpperCase() || "\xB7\xB7"; +} +function statusSub(status) { + switch (status) { + case "connected": + case "verified": + return "P2P \xB7 connected"; + case "verifying": + return "Verifying\u2026"; + case "connecting": + case "new": + return "Connecting\u2026"; + case "peer_disconnected": + return "Peer disconnected"; + default: + return "Disconnected"; + } +} +function emptySetup() { + return { + offerData: "", + answerData: "", + offerInput: "", + answerInput: "", + showOfferStep: false, + showAnswerStep: false, + showVerification: false, + showQRCode: false, + qrCodeUrl: "", + isGeneratingKeys: false, + qrFramesTotal: 0, + qrFrameIndex: 0, + qrManualMode: false + }; +} +function createSessionEntry(opts = {}) { + const id = opts.id || (typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : String(Date.now()) + Math.random()); + return { + id, + peerLabel: opts.peerLabel || shortLabelFromId(id), + labelIsCustom: false, + // becomes true once the user renames; blocks SAS auto-relabel + createdAt: opts.createdAt || Date.now(), + role: opts.role || "offer", + // 'offer' | 'answer' + status: opts.status || "new", + keyFingerprint: "", + verificationCode: "", + sas: { localConfirmed: false, remoteConfirmed: false, bothConfirmed: false, isVerified: false }, + messages: [], + unreadCount: 0, + pendingIncomingFiles: [], + peerPresence: null, + // peer's advertised availability ('available'|'away'|'busy'|'offline'); null = unknown + setup: emptySetup() + }; +} +function createInitialState() { + return { sessions: {}, order: [], activeSessionId: null }; +} +function patchSession(state, id, patch) { + const session = state.sessions[id]; + if (!session) return state; + return { + ...state, + sessions: { ...state.sessions, [id]: { ...session, ...patch } } + }; +} +function sessionsReducer(state, action) { + const A = SESSION_ACTIONS; + switch (action.type) { + case A.CREATE_SESSION: { + const entry = action.entry || createSessionEntry(action); + if (state.sessions[entry.id]) return state; + return { + sessions: { ...state.sessions, [entry.id]: entry }, + order: [...state.order, entry.id], + activeSessionId: action.activate === false ? state.activeSessionId : entry.id + }; + } + case A.REMOVE_SESSION: { + const { id } = action; + if (!state.sessions[id]) return state; + const sessions = { ...state.sessions }; + delete sessions[id]; + const order = state.order.filter((x) => x !== id); + let activeSessionId = state.activeSessionId; + if (activeSessionId === id) { + const removedIdx = state.order.indexOf(id); + activeSessionId = order[Math.max(0, removedIdx - 1)] || order[0] || null; + } + return { sessions, order, activeSessionId }; + } + case A.SET_ACTIVE: { + if (!state.sessions[action.id]) return state; + if (state.activeSessionId === action.id) return state; + return { ...state, activeSessionId: action.id }; + } + case A.SET_STATUS: { + const session = state.sessions[action.id]; + if (!session || session.status === action.status) return state; + return patchSession(state, action.id, { status: action.status }); + } + case A.SET_FINGERPRINT: + return patchSession(state, action.id, { keyFingerprint: action.fingerprint }); + case A.SET_VERIFICATION: + return patchSession(state, action.id, { verificationCode: action.code }); + case A.SET_SAS: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { sas: { ...session.sas, ...action.sas } }); + } + case A.ADD_MESSAGE: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { messages: [...session.messages, action.message] }); + } + case A.SET_MESSAGES: { + const session = state.sessions[action.id]; + if (!session) return state; + const next = typeof action.updater === "function" ? action.updater(session.messages) : action.messages; + return patchSession(state, action.id, { messages: Array.isArray(next) ? next : [] }); + } + case A.UPDATE_MESSAGE_STATUS: { + const session = state.sessions[action.id]; + if (!session) return state; + let changed = false; + const messages = session.messages.map((m) => { + if (String(m.mid) === String(action.mid) && m.status !== action.status) { + changed = true; + return { ...m, status: action.status }; + } + return m; + }); + return changed ? patchSession(state, action.id, { messages }) : state; + } + case A.DELETE_MESSAGE: { + const session = state.sessions[action.id]; + if (!session) return state; + const messages = session.messages.filter((m) => String(m.mid) !== String(action.mid)); + if (messages.length === session.messages.length) return state; + return patchSession(state, action.id, { messages }); + } + case A.EXPIRE_MESSAGE: { + const session = state.sessions[action.id]; + if (!session) return state; + let changed = false; + const messages = session.messages.map((m) => { + if (String(m.id) === String(action.messageId) && !m.expired) { + changed = true; + return { ...m, expired: true, message: "", expiresAt: void 0 }; + } + return m; + }); + return changed ? patchSession(state, action.id, { messages }) : state; + } + case A.INCREMENT_UNREAD: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { unreadCount: session.unreadCount + 1 }); + } + case A.CLEAR_UNREAD: { + const session = state.sessions[action.id]; + if (!session || session.unreadCount === 0) return state; + return patchSession(state, action.id, { unreadCount: 0 }); + } + case A.SET_PENDING_FILES: { + const session = state.sessions[action.id]; + if (!session) return state; + const next = typeof action.updater === "function" ? action.updater(session.pendingIncomingFiles) : action.files; + return patchSession(state, action.id, { pendingIncomingFiles: Array.isArray(next) ? next : [] }); + } + case A.PATCH_SETUP: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { setup: { ...session.setup, ...action.patch } }); + } + case A.RENAME: { + const session = state.sessions[action.id]; + if (!session) return state; + const label = String(action.label || "").trim() || session.peerLabel; + return patchSession(state, action.id, { peerLabel: label, labelIsCustom: true }); + } + case A.SET_PEER_PRESENCE: { + const session = state.sessions[action.id]; + if (!session || session.peerPresence === action.presence) return state; + return patchSession(state, action.id, { peerPresence: action.presence }); + } + default: + return state; + } +} +function decorateSession(session, activeSessionId) { + const lastMessage = [...session.messages].reverse().find((m) => !m.expired && typeof m.message === "string" && m.message.trim()); + const s = session.status; + const isUp = s === "connected" || s === "verified"; + const isPending = s === "connecting" || s === "verifying" || s === "new"; + let dot, headerSub; + if (isPending) { + dot = "#e3b341"; + headerSub = statusSub(s); + } else if (isUp) { + dot = session.peerPresence ? PRESENCE_DOT[session.peerPresence] || "#6b6b73" : "#3ecf8e"; + headerSub = session.peerPresence ? PRESENCE_WORD[session.peerPresence] || "Online" : "P2P \xB7 connected"; + } else { + dot = "#e5727a"; + headerSub = statusSub(s); + } + const preview = lastMessage ? lastMessage.message : headerSub; + return { + id: session.id, + name: session.peerLabel, + mono: monoInitials(session.peerLabel), + dot, + headerSub, + status: session.status, + peerPresence: session.peerPresence, + preview, + unread: session.unreadCount > 0 ? session.unreadCount > 99 ? "99+" : String(session.unreadCount) : null, + verified: !!session.sas.isVerified, + active: session.id === activeSessionId, + inactive: session.id !== activeSessionId + }; +} +function decorateSessions(state) { + return state.order.map((id) => state.sessions[id]).filter(Boolean).map((s) => decorateSession(s, state.activeSessionId)); +} + // src/app.jsx var copyToClipboardSecure = async (text, autoClearMs = 0) => { let ok = false; @@ -326,6 +591,45 @@ var highlightCode = (code) => { flush(); return nodes; }; +var PRISM_ALIAS = { + js: "javascript", + mjs: "javascript", + javascript: "javascript", + node: "javascript", + ts: "typescript", + typescript: "typescript", + jsx: "jsx", + tsx: "tsx", + py: "python", + python: "python", + sh: "bash", + shell: "bash", + zsh: "bash", + bash: "bash", + "c++": "cpp", + cpp: "cpp", + cc: "cpp", + cxx: "cpp", + c: "c", + h: "c", + cs: "csharp", + csharp: "csharp", + java: "java", + go: "go", + golang: "go", + rs: "rust", + rust: "rust", + json: "json", + yml: "yaml", + yaml: "yaml", + sql: "sql", + md: "markdown", + markdown: "markdown", + html: "markup", + xml: "markup", + svg: "markup", + css: "css" +}; var CodeBlock = ({ code, lang }) => { const [copied, setCopied] = React.useState(false); const handleCopy = async () => { @@ -335,6 +639,20 @@ var CodeBlock = ({ code, lang }) => { setTimeout(() => setCopied(false), 2e3); } }; + const norm = PRISM_ALIAS[(lang || "").toLowerCase()] || (lang || "").toLowerCase(); + const prism = typeof window !== "undefined" ? window.Prism : null; + const grammar = prism && prism.languages ? prism.languages[norm] : null; + const usePrism = !!(prism && grammar && typeof prism.highlight === "function"); + let highlightedHtml = null; + if (usePrism) { + try { + highlightedHtml = prism.highlight(code, grammar, norm); + } catch (_) { + highlightedHtml = null; + } + } + const displayLang = usePrism ? norm : lang || "code"; + const codeEl = usePrism && highlightedHtml != null ? React.createElement("code", { className: "language-" + norm, dangerouslySetInnerHTML: { __html: highlightedHtml } }) : React.createElement("code", null, highlightCode(code)); return React.createElement("div", { className: "my-1 rounded-lg overflow-hidden", style: { backgroundColor: "#1b1c1b", border: "0 solid #e5e7eb" } @@ -347,7 +665,7 @@ var CodeBlock = ({ code, lang }) => { React.createElement("span", { key: "lang", className: "text-[11px] uppercase tracking-wide text-gray-500 font-mono" - }, lang || "code"), + }, displayLang), React.createElement("button", { key: "copy", onClick: handleCopy, @@ -365,7 +683,7 @@ var CodeBlock = ({ code, lang }) => { key: "pre", className: "px-3 py-2 overflow-x-auto text-xs leading-relaxed text-gray-200 custom-scrollbar", style: { whiteSpace: "pre", fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace", margin: 0 } - }, React.createElement("code", null, highlightCode(code))) + }, codeEl) ]); }; var MessageBody = ({ text }) => { @@ -470,7 +788,10 @@ var EnhancedChatMessage = ({ message, type, timestamp, mid, status, viewOnce, vi const stCfg = { sending: { icon: "fa-clock", color: "#6b6b73", label: "Sending" }, sent: { icon: "fa-check", color: "#8a8a92", label: "Sent" }, - delivered: { icon: "fa-check-double", color: "#3ecf8e", label: "Delivered" }, + // Two GREY ticks = delivered to the peer's device but not yet read. + delivered: { icon: "fa-check-double", color: "#8a8a92", label: "Delivered" }, + // Two GREEN ticks = the peer actually opened the chat and read it. + read: { icon: "fa-check-double", color: "#3ecf8e", label: "Read" }, failed: { icon: "fa-triangle-exclamation", color: "#e5727a", label: "Not sent" } }[status || "sent"] || { icon: "fa-check", color: "#8a8a92", label: "Sent" }; metaLeft.push(React.createElement("span", { @@ -572,7 +893,10 @@ var EnhancedConnectionSetup = ({ iceSettingsPersisted, customIceServers, handleApplyIceSettings, - handleForgetIceSettings + handleForgetIceSettings, + // When true, render ONLY the create/connect card (no marketing landing, + // no hero) so it slots into the chat column for an additional session. + compact = false }) => { const [mode, setMode] = React.useState("create"); const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false); @@ -1109,7 +1433,7 @@ var EnhancedConnectionSetup = ({ onApply: handleApplyIceSettings, onForget: handleForgetIceSettings }) : null; - const rightPanel = h("div", { key: "right", style: { flex: "0.95 1 460px", minWidth: "min(100%, 320px)", position: "relative", overflow: "hidden", display: "flex", flexDirection: "column", height: "100vh" } }, [ + const rightPanel = h("div", { key: "right", style: compact ? { flex: 1, minWidth: 0, width: "100%", position: "relative", overflow: "hidden", display: "flex", flexDirection: "column", height: "100%" } : { flex: "0.95 1 460px", minWidth: "min(100%, 320px)", position: "relative", overflow: "hidden", display: "flex", flexDirection: "column", height: "100vh" } }, [ h( "div", { key: "scroll", className: "custom-scrollbar", style: { flex: 1, overflowY: "auto", display: "flex", flexDirection: "column", padding: "42px 44px" } }, @@ -1163,6 +1487,9 @@ var EnhancedConnectionSetup = ({ const keyframeStyle = h("style", { key: "kf", dangerouslySetInnerHTML: { __html: "@keyframes sbFlowR{0%{left:4%;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:96%;opacity:0}}@keyframes sbFlowL{0%{left:96%;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:4%;opacity:0}}@keyframes sbPulse{0%,100%{transform:translate(-50%,-50%) scale(1);opacity:.5}50%{transform:translate(-50%,-50%) scale(1.5);opacity:0}}@keyframes sbSpin{to{transform:rotate(360deg)}}@keyframes sbUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes sbNode{0%,100%{box-shadow:0 0 0 0 rgba(62,207,142,0)}50%{box-shadow:0 0 0 6px rgba(62,207,142,.06)}}@keyframes sbScan{0%{top:8%}100%{top:88%}}@keyframes sbBlink{0%,100%{opacity:1}50%{opacity:.35}}" } }); + if (compact) { + return h("div", { className: "sb-start", style: { flex: 1, minHeight: 0, width: "100%", display: "flex", flexDirection: "column", background: "#0f0f11", color: "#e8e8eb" } }, [keyframeStyle, rightPanel, qrModal]); + } return h("div", { className: "sb-start", style: { width: "100%" } }, [keyframeStyle, hero, uniqueSection, partnersSection, roadmapSection, communitySection, qrModal]); }; var createScrollToBottomFunction = (chatMessagesRef) => { @@ -1305,10 +1632,12 @@ var runSecurityReport = async (webrtcManager) => { }); document.body.appendChild(modal); }; -var SecureBitChatHeader = ({ status, onDisconnect, webrtcManager }) => { +var SecureBitChatHeader = ({ status, onDisconnect, webrtcManager, title, isOffline, peerPresence, onRenameTitle }) => { const MONO = "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace"; const [showNetwork, setShowNetwork] = React.useState(false); const [sec, setSec] = React.useState(null); + const [editingName, setEditingName] = React.useState(false); + const [nameDraft, setNameDraft] = React.useState(""); React.useEffect(() => { let alive = true; const fetchSec = async () => { @@ -1334,7 +1663,30 @@ var SecureBitChatHeader = ({ status, onDisconnect, webrtcManager }) => { document.removeEventListener("real-security-calculated", onCalc); }; }, [webrtcManager]); - const connected = status === "connected" || status === "verified"; + const onlineConnected = status === "connected" || status === "verified"; + const dropped = status === "disconnected" || status === "peer_disconnected"; + const connected = onlineConnected && !isOffline; + const connDot = isOffline || dropped ? "#e5727a" : onlineConnected ? "#3ecf8e" : "#e3c84e"; + const connLabel = isOffline ? "Offline" : onlineConnected ? "Connected" : status === "peer_disconnected" ? "Peer disconnected" : status === "disconnected" ? "Disconnected" : "Connecting\u2026"; + const connGlow = isOffline || dropped ? "0 0 0 3px rgba(229,114,122,0.16)" : onlineConnected ? "0 0 0 3px rgba(62,207,142,0.16)" : "0 0 0 3px rgba(227,200,78,0.16)"; + const peerDot = onlineConnected && !isOffline ? PRESENCE_DOT[peerPresence] || "#3ecf8e" : connDot; + const peerPresenceWord = onlineConnected && !isOffline && peerPresence ? PRESENCE_WORD[peerPresence] || null : null; + const startRename = () => { + setNameDraft(title || ""); + setEditingName(true); + }; + const commitRename = () => { + if (typeof onRenameTitle === "function") onRenameTitle(nameDraft); + setEditingName(false); + }; + const renameKey = (e) => { + if (e.key === "Enter") { + e.preventDefault(); + commitRename(); + } else if (e.key === "Escape") { + setEditingName(false); + } + }; const passed = sec && Number.isFinite(sec.passedChecks) ? sec.passedChecks : null; const total = sec && Number.isFinite(sec.totalChecks) ? sec.totalChecks : null; const scoreLabel = passed != null && total ? passed + "/" + total : sec ? sec.score + "%" : "\u2014"; @@ -1347,9 +1699,9 @@ var SecureBitChatHeader = ({ status, onDisconnect, webrtcManager }) => { style: { display: "flex", alignItems: "center", gap: "9px", padding: "7px 13px", borderRadius: "9px", border: "1px solid " + (showNetwork ? "rgba(255,255,255,0.16)" : "rgba(255,255,255,0.07)"), background: showNetwork ? "rgba(255,255,255,0.05)" : "rgba(255,255,255,0.02)", cursor: "pointer", fontFamily: "inherit", transition: "all .15s" } }, [ React.createElement("i", { key: "i", className: "fas fa-shield-halved", style: { color: accent, fontSize: "13px" } }), - React.createElement("span", { key: "l", style: { fontSize: "13px", fontWeight: 600, color: "#e8e8eb" } }, sec ? sec.level || "Secure" : "Secure"), - React.createElement("span", { key: "d", style: { width: "1px", height: "13px", background: "rgba(255,255,255,0.12)" } }), - React.createElement("span", { key: "s", style: { fontFamily: MONO, fontSize: "11.5px", fontWeight: 500, color: "#8a8a92" } }, scoreLabel), + React.createElement("span", { key: "l", className: "sb-sec-label", style: { fontSize: "13px", fontWeight: 600, color: "#e8e8eb" } }, sec ? sec.level || "Secure" : "Secure"), + React.createElement("span", { key: "d", className: "sb-sec-div", style: { width: "1px", height: "13px", background: "rgba(255,255,255,0.12)" } }), + React.createElement("span", { key: "s", className: "sb-sec-score", style: { fontFamily: MONO, fontSize: "11.5px", fontWeight: 500, color: "#8a8a92" } }, scoreLabel), React.createElement("button", { key: "c", type: "button", @@ -1361,29 +1713,45 @@ var SecureBitChatHeader = ({ status, onDisconnect, webrtcManager }) => { style: { background: "none", border: "none", padding: 0, margin: 0, cursor: "pointer", display: "grid", placeItems: "center" } }, React.createElement("i", { className: "fas fa-chevron-down", style: { color: "#6b6b73", fontSize: "11px", transform: showNetwork ? "rotate(180deg)" : "rotate(0deg)", transition: "transform .2s" } })) ]); + const headerResponsiveCss = React.createElement("style", { key: "hdr-css", dangerouslySetInnerHTML: { + __html: ( + // Mobile: leave room for the drawer hamburger and shed non-essential header + // chrome so avatar + name + status + Disconnect fit a narrow screen. + "@media (max-width:768px){.sb-chat-header{padding-left:60px !important;gap:10px !important;}.sb-chat-header .sb-sec-score,.sb-chat-header .sb-sec-label,.sb-chat-header .sb-sec-div{display:none !important;}.sb-chat-header .sb-secpill{padding:8px !important;gap:6px !important;}.sb-chat-header .sb-conn-text{display:none !important;}.sb-chat-header .sb-conn{padding:9px !important;}.sb-chat-header .sb-hdr-sub{display:none !important;}}@media (max-width:480px){.sb-chat-header{padding-right:12px !important;gap:8px !important;}}" + ) + } }); const header = React.createElement("header", { key: "hdr", + className: "sb-chat-header", style: { flex: "none", display: "flex", alignItems: "center", justifyContent: "space-between", gap: "24px", padding: "0 20px", height: "64px", borderBottom: "1px solid rgba(255,255,255,0.06)", background: "rgba(18,18,20,0.72)", backdropFilter: "blur(14px)", WebkitBackdropFilter: "blur(14px)" } }, [ + headerResponsiveCss, + // The SecureBit brand/logo lives in the left rail; this header identifies the + // ACTIVE conversation — avatar monogram + local label + connection status. React.createElement("div", { key: "left", style: { display: "flex", alignItems: "center", gap: "12px", minWidth: 0 } }, [ - React.createElement( - "div", - { key: "logo", style: { width: "36px", height: "36px", flex: "none", display: "grid", placeItems: "center" } }, - React.createElement("img", { src: "/logo/securebit-mark.svg", alt: "SecureBit", style: { width: "100%", height: "100%", objectFit: "contain", display: "block" } }) - ), - React.createElement("div", { key: "txt", style: { lineHeight: 1.2, minWidth: 0 } }, [ - React.createElement("div", { key: "r1", style: { display: "flex", alignItems: "baseline", gap: "7px" } }, [ - React.createElement("span", { key: "n", style: { fontSize: "16px", fontWeight: 800, letterSpacing: "-0.3px", color: "#e8e8eb" } }, "SecureBit"), - React.createElement("span", { key: "v", style: { fontFamily: MONO, fontSize: "10px", fontWeight: 500, color: "#56565e" } }, "v4.9.0") + React.createElement("div", { key: "avatar", style: { position: "relative", flex: "none", width: "36px", height: "36px", borderRadius: "10px", display: "grid", placeItems: "center", background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.09)", fontSize: "13px", fontWeight: 700, letterSpacing: "-0.3px", color: "#e8e8eb" } }, [ + monoInitials(title || "Chat"), + React.createElement("span", { key: "dot", style: { position: "absolute", right: "-2px", bottom: "-2px", width: "11px", height: "11px", borderRadius: "50%", background: peerDot, border: "2px solid #121214" } }) + ]), + editingName ? React.createElement("div", { key: "edit", style: { display: "flex", flexDirection: "column", gap: "4px", minWidth: 0 } }, [ + React.createElement("div", { key: "row", style: { display: "flex", alignItems: "center", gap: "6px" } }, [ + React.createElement("input", { key: "in", autoFocus: true, value: nameDraft, maxLength: 32, placeholder: "Name this chat", onChange: (e) => setNameDraft(e.target.value), onKeyDown: renameKey, onBlur: commitRename, style: { width: "210px", padding: "5px 10px", borderRadius: "8px", border: "1px solid rgba(240,137,42,0.55)", background: "#0f0f11", color: "#f4f4f6", fontFamily: "inherit", fontSize: "14px", fontWeight: 700, outline: "none" } }), + React.createElement("button", { key: "ok", onMouseDown: (e) => e.preventDefault(), onClick: commitRename, title: "Save", style: { flex: "none", width: "28px", height: "28px", borderRadius: "8px", display: "grid", placeItems: "center", border: "none", background: "#f0892a", color: "#1a0f04", cursor: "pointer" } }, React.createElement("i", { className: "fas fa-check", style: { fontSize: "12px" } })) ]), - React.createElement("div", { key: "r2", style: { fontSize: "11px", color: "#6b6b73", fontWeight: 500 } }, "End-to-end encrypted") + React.createElement("div", { key: "hint", style: { fontSize: "11px", color: "#56565e" } }, "Local label \xB7 stored only on this device") + ]) : React.createElement("div", { key: "txt", style: { lineHeight: 1.2, minWidth: 0 } }, [ + React.createElement("div", { key: "r1", style: { display: "flex", alignItems: "center", gap: "7px" } }, [ + React.createElement("span", { key: "n", style: { fontSize: "15px", fontWeight: 800, letterSpacing: "-0.3px", color: "#f4f4f6", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, title || "Secure chat"), + React.createElement("button", { key: "edit", onClick: startRename, title: "Rename chat (local only)", style: { flex: "none", width: "24px", height: "24px", borderRadius: "7px", display: "grid", placeItems: "center", border: "none", background: "transparent", color: "#56565e", cursor: "pointer" } }, React.createElement("i", { className: "fas fa-pen", style: { fontSize: "11px" } })) + ]), + React.createElement("div", { key: "r2", className: "sb-hdr-sub", style: { fontSize: "11px", color: "#6b6b73", fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, isOffline ? "No network \xB7 reconnecting" : peerPresenceWord || (onlineConnected ? "P2P \xB7 end-to-end encrypted" : status === "peer_disconnected" ? "Peer disconnected" : status === "disconnected" ? "Disconnected" : "Connecting\u2026")) ]) ]), secBtn, - React.createElement("div", { key: "right", style: { display: "flex", alignItems: "center", gap: "9px" } }, [ - React.createElement("div", { key: "conn", style: { display: "flex", alignItems: "center", gap: "8px", padding: "8px 13px", borderRadius: "9px", border: "1px solid rgba(255,255,255,0.07)", background: "rgba(255,255,255,0.02)" } }, [ - React.createElement("span", { key: "dot", style: { width: "7px", height: "7px", borderRadius: "50%", background: connected ? "#3ecf8e" : "#e3c84e", boxShadow: connected ? "0 0 0 3px rgba(62,207,142,0.16)" : "0 0 0 3px rgba(227,200,78,0.16)" } }), - React.createElement("span", { key: "t", style: { fontSize: "13px", fontWeight: 600, color: "#cfcfd4" } }, connected ? "Connected" : "Connecting\u2026") + React.createElement("div", { key: "right", className: "sb-hdr-right", style: { display: "flex", alignItems: "center", gap: "9px" } }, [ + React.createElement("div", { key: "conn", className: "sb-conn", style: { display: "flex", alignItems: "center", gap: "8px", padding: "8px 13px", borderRadius: "9px", border: "1px solid rgba(255,255,255,0.07)", background: "rgba(255,255,255,0.02)" } }, [ + React.createElement("span", { key: "dot", style: { flex: "none", width: "7px", height: "7px", borderRadius: "50%", background: connDot, boxShadow: connGlow } }), + React.createElement("span", { key: "t", className: "sb-conn-text", style: { fontSize: "13px", fontWeight: 600, color: "#cfcfd4" } }, connLabel) ]), React.createElement("button", { key: "dc", onClick: onDisconnect, className: "sb-disconnect", style: { display: "flex", alignItems: "center", gap: "7px", padding: "8px 14px", borderRadius: "9px", border: "1px solid rgba(255,255,255,0.08)", background: "transparent", color: "#9a9aa2", fontFamily: "inherit", fontSize: "13px", fontWeight: 600, cursor: "pointer", transition: "all .15s" } }, [ React.createElement("i", { key: "i", className: "fas fa-power-off", style: { fontSize: "12px" } }), @@ -1410,6 +1778,10 @@ var SecureBitChatHeader = ({ status, onDisconnect, webrtcManager }) => { return React.createElement("div", { style: { flex: "none" } }, [header, netPanel]); }; var EnhancedChatInterface = ({ + title, + isOffline, + peerPresence, + onRenameTitle, messages, messageInput, setMessageInput, @@ -1772,25 +2144,346 @@ var EnhancedChatInterface = ({ key: "chat-header", status, onDisconnect, - webrtcManager + webrtcManager, + title, + isOffline, + peerPresence, + onRenameTitle }); return React.createElement("div", { className: "chat-container", style: { display: "flex", flexDirection: "column", height: "100vh", background: "#0f0f11", color: "#e8e8eb" } }, [chatHeader, messagesArea, scrollBtn, composer]); }; +var buildSessionMessage = (message, type, opts = {}) => ({ + message, + type, + id: Date.now() + Math.random(), + timestamp: typeof opts.timestamp === "number" ? opts.timestamp : Date.now(), + mid: opts.mid, + status: opts.status, + viewOnce: opts.viewOnce === true, + viewOnceTtl: typeof opts.viewOnceTtl === "number" ? opts.viewOnceTtl : 15, + expiresAt: typeof opts.expiresAt === "number" ? opts.expiresAt : void 0 +}); +var SB_SVG = { + chevL: '', + chevR: '', + plus: '', + users: '', + burger: '' +}; +var SessionsSidebar = ({ chats, collapsed, drawerOpen, onToggleCollapse, onSelect, onNewChat, onRename, onCloseDrawer, myStatus, onSetStatus }) => { + const h = React.createElement; + const [editingId, setEditingId] = React.useState(null); + const [draft, setDraft] = React.useState(""); + const [presenceOpen, setPresenceOpen] = React.useState(false); + const startEdit = (c) => (e) => { + e.stopPropagation(); + setEditingId(c.id); + setDraft(c.name); + }; + const commitEdit = () => { + if (editingId) { + onRename(editingId, draft); + setEditingId(null); + } + }; + const editKey = (e) => { + if (e.key === "Enter") { + e.preventDefault(); + commitEdit(); + } else if (e.key === "Escape") { + setEditingId(null); + } + }; + const renameInput = (extra = {}) => h("input", { + autoFocus: true, + value: draft, + onChange: (e) => setDraft(e.target.value), + onKeyDown: editKey, + onBlur: commitEdit, + onClick: (e) => e.stopPropagation(), + style: Object.assign({ width: "100%", background: "rgba(255,255,255,0.06)", border: "1px solid rgba(240,137,42,0.5)", borderRadius: "6px", color: "#f4f4f6", fontFamily: "inherit", fontSize: "14px", fontWeight: 700, padding: "2px 6px", outline: "none" }, extra) + }); + const icon = (svg, style) => h("span", { style: Object.assign({ display: "grid", placeItems: "center" }, style || {}), dangerouslySetInnerHTML: { __html: svg } }); + const avatar = (c, size, ring) => h("div", { + style: { position: "relative", flex: "none", width: size + "px", height: size + "px", borderRadius: (size >= 44 ? 12 : 11) + "px", display: "grid", placeItems: "center", background: c.active ? "rgba(255,255,255,0.06)" : "rgba(255,255,255,0.035)", border: "1px solid rgba(255,255,255," + (c.active ? "0.14" : "0.07") + ")", fontSize: "13px", fontWeight: 700, letterSpacing: "-0.3px", color: c.active ? "#f4f4f6" : "#9a9aa2" } + }, [c.mono, h("span", { key: "dot", style: { position: "absolute", right: "-2px", bottom: "-2px", width: "11px", height: "11px", borderRadius: "50%", background: c.dot, border: "2px solid " + ring } })]); + const expandedRow = (c) => h("div", { + key: c.id, + onClick: () => onSelect(c.id), + style: { position: "relative", display: "flex", alignItems: "center", gap: "12px", padding: "11px 12px", marginBottom: "4px", borderRadius: "11px", background: c.active ? "#161618" : "transparent", border: "1px solid " + (c.active ? "rgba(255,255,255,0.08)" : "transparent"), cursor: "pointer" } + }, [ + c.active && h("span", { key: "bar", style: { position: "absolute", left: 0, top: "12px", bottom: "12px", width: "3px", borderRadius: "0 3px 3px 0", background: "#f0892a" } }), + avatar(c, 38, c.active ? "#161618" : "#0c0c0e"), + h("div", { key: "body", style: { flex: 1, minWidth: 0 } }, [ + h("div", { key: "top", style: { display: "flex", alignItems: "center", gap: "7px" } }, [ + editingId === c.id ? renameInput() : h("span", { + key: "name", + onDoubleClick: startEdit(c), + title: "Double-click to rename", + style: { flex: 1, minWidth: 0, fontSize: "14px", fontWeight: c.active ? 700 : 600, letterSpacing: "-0.2px", color: c.active ? "#f4f4f6" : "#cfcfd4", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } + }, c.name), + c.unread && editingId !== c.id && h("span", { key: "u", style: { flex: "none", minWidth: "18px", height: "18px", padding: "0 5px", borderRadius: "9px", display: "grid", placeItems: "center", background: "#f0892a", color: "#1a0f04", fontFamily: "'JetBrains Mono',monospace", fontSize: "10px", fontWeight: 700 } }, c.unread) + ]), + h("div", { key: "prev", style: { fontSize: "12px", color: c.active ? "#8a8a92" : "#6b6b73", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" } }, c.preview) + ]) + ]); + const dockItem = (c) => h("div", { key: c.id, style: { position: "relative" } }, [ + c.active && h("span", { key: "bar", style: { position: "absolute", left: "-13px", top: "9px", bottom: "9px", width: "3px", borderRadius: "0 3px 3px 0", background: "#f0892a" } }), + h("div", { + key: "tile", + onClick: () => onSelect(c.id), + title: c.name, + style: { position: "relative", width: "44px", height: "44px", borderRadius: "12px", display: "grid", placeItems: "center", cursor: "pointer", background: c.active ? "rgba(255,255,255,0.06)" : "rgba(255,255,255,0.03)", border: "1px solid rgba(255,255,255," + (c.active ? "0.14" : "0.07") + ")", fontSize: "13px", fontWeight: 700, letterSpacing: "-0.3px", color: c.active ? "#f4f4f6" : "#9a9aa2" } + }, [ + c.mono, + h("span", { key: "dot", style: { position: "absolute", right: "-2px", bottom: "-2px", width: "11px", height: "11px", borderRadius: "50%", background: c.dot, border: "2.5px solid #0c0c0e" } }), + c.unread && h("span", { key: "u", style: { position: "absolute", left: "-5px", top: "-5px", minWidth: "17px", height: "17px", padding: "0 4px", borderRadius: "9px", display: "grid", placeItems: "center", background: "#f0892a", color: "#1a0f04", fontFamily: "'JetBrains Mono',monospace", fontSize: "9.5px", fontWeight: 700, border: "2px solid #0c0c0e" } }, c.unread) + ]) + ]); + const brandMark = (size) => h( + "div", + { style: { width: size + "px", height: size + "px", flex: "none", display: "grid", placeItems: "center" } }, + h("img", { src: "/logo/securebit-mark.svg", alt: "SecureBit", style: { width: "100%", height: "100%", objectFit: "contain", display: "block" } }) + ); + const collapseBtn = (svg, title) => h("button", { onClick: onToggleCollapse, title, style: { width: "30px", height: "30px", borderRadius: "8px", display: "grid", placeItems: "center", border: "1px solid rgba(255,255,255,0.07)", background: "transparent", color: "#8a8a92", cursor: "pointer" }, dangerouslySetInnerHTML: { __html: svg } }); + const myMeta = MY_STATUS_OPTIONS.find((o) => o.key === myStatus) || MY_STATUS_OPTIONS[0]; + const PRES_SVG = { + user: '', + check: '', + chevUp: '', + lock: '' + }; + const presenceMenu = (pos) => presenceOpen ? h("div", { + key: "pmenu", + style: Object.assign({ position: "absolute", zIndex: 30, borderRadius: "14px", background: "#161618", border: "1px solid rgba(255,255,255,0.1)", boxShadow: "0 16px 40px rgba(0,0,0,0.55)", padding: "6px" }, pos) + }, [ + h("div", { key: "h", style: { padding: "9px 10px 7px", fontFamily: "'JetBrains Mono',monospace", fontSize: "10px", fontWeight: 600, color: "#56565e", textTransform: "uppercase", letterSpacing: "1.2px" } }, "Set your status"), + ...MY_STATUS_OPTIONS.map((o) => h("button", { + key: o.key, + onClick: () => { + onSetStatus(o.key); + setPresenceOpen(false); + }, + style: { width: "100%", display: "flex", alignItems: "center", gap: "11px", padding: "9px 10px", borderRadius: "9px", border: "none", background: "transparent", cursor: "pointer", textAlign: "left" } + }, [ + h("span", { key: "d", style: { flex: "none", width: "10px", height: "10px", borderRadius: "50%", background: o.dot } }), + h("span", { key: "t", style: { flex: 1, minWidth: 0 } }, [ + h("span", { key: "w", style: { display: "block", fontSize: "13.5px", fontWeight: 600, color: "#e8e8eb" } }, o.word), + h("span", { key: "de", style: { display: "block", fontSize: "11.5px", color: "#6b6b73" } }, o.desc) + ]), + o.key === myStatus && h("span", { key: "c", style: { flex: "none", display: "grid", placeItems: "center" }, dangerouslySetInnerHTML: { __html: PRES_SVG.check } }) + ])), + h("div", { key: "note", style: { display: "flex", alignItems: "flex-start", gap: "8px", margin: "6px 6px 4px", padding: "9px 10px", borderRadius: "9px", background: "rgba(62,207,142,0.06)", border: "1px solid rgba(62,207,142,0.16)" } }, [ + h("span", { key: "i", style: { flex: "none", marginTop: "1px", display: "grid" }, dangerouslySetInnerHTML: { __html: PRES_SVG.lock } }), + h("span", { key: "t", style: { fontSize: "11px", lineHeight: 1.45, color: "#8a8a92" } }, "Sent end-to-end to connected peers only \u2014 never stored on a server.") + ]) + ]) : null; + const presencePanelExpanded = h("div", { key: "you", style: { flex: "none", position: "relative", marginTop: "10px", borderTop: "1px solid rgba(255,255,255,0.06)", padding: "10px 12px 12px" } }, [ + presenceMenu({ left: "12px", right: "12px", bottom: "64px" }), + h("button", { key: "btn", onClick: () => setPresenceOpen((v) => !v), style: { width: "100%", display: "flex", alignItems: "center", gap: "11px", padding: "7px 8px", borderRadius: "11px", border: "1px solid rgba(255,255,255,0.06)", background: "rgba(255,255,255,0.02)", cursor: "pointer" } }, [ + h("div", { key: "av", style: { position: "relative", flex: "none", width: "36px", height: "36px", borderRadius: "10px", display: "grid", placeItems: "center", background: "rgba(240,137,42,0.12)", border: "1px solid rgba(240,137,42,0.24)", color: "#f0892a" } }, [ + h("span", { key: "i", style: { display: "grid" }, dangerouslySetInnerHTML: { __html: PRES_SVG.user } }), + h("span", { key: "dot", style: { position: "absolute", right: "-2px", bottom: "-2px", width: "11px", height: "11px", borderRadius: "50%", background: myMeta.dot, border: "2px solid #0c0c0e" } }) + ]), + h("div", { key: "tx", style: { flex: 1, minWidth: 0, textAlign: "left" } }, [ + h("div", { key: "y", style: { fontSize: "13.5px", fontWeight: 700, color: "#f4f4f6" } }, "You"), + h("div", { key: "w", style: { fontSize: "12px", color: "#8a8a92" } }, myMeta.word) + ]), + h("span", { key: "ch", style: { display: "grid", placeItems: "center" }, dangerouslySetInnerHTML: { __html: PRES_SVG.chevUp } }) + ]) + ]); + const presencePanelCollapsed = h("div", { key: "you", style: { flex: "none", position: "relative", display: "flex", flexDirection: "column", alignItems: "center", padding: "0 0 13px" } }, [ + presenceMenu({ left: "60px", bottom: "8px", width: "248px" }), + h("button", { key: "btn", onClick: () => setPresenceOpen((v) => !v), title: "Your status \u2014 " + myMeta.word, style: { position: "relative", width: "44px", height: "44px", borderRadius: "12px", display: "grid", placeItems: "center", cursor: "pointer", background: "rgba(240,137,42,0.12)", border: "1px solid rgba(240,137,42,0.24)", color: "#f0892a" } }, [ + h("span", { key: "i", style: { display: "grid" }, dangerouslySetInnerHTML: { __html: PRES_SVG.user } }), + h("span", { key: "dot", style: { position: "absolute", right: "-2px", bottom: "-2px", width: "12px", height: "12px", borderRadius: "50%", background: myMeta.dot, border: "2.5px solid #0c0c0e" } }) + ]) + ]); + const expandedInner = [ + h("div", { key: "head", style: { flex: "none", display: "flex", alignItems: "center", justifyContent: "space-between", padding: "0 12px 0 16px", height: "64px", borderBottom: "1px solid rgba(255,255,255,0.06)" } }, [ + h("div", { key: "brand", style: { display: "flex", alignItems: "center", gap: "10px" } }, [brandMark(30), h("span", { key: "t", style: { fontSize: "15px", fontWeight: 800, letterSpacing: "-0.3px", color: "#f4f4f6" } }, "SecureBit")]), + collapseBtn(SB_SVG.chevL, "Collapse") + ]), + h("div", { key: "label", style: { flex: "none", display: "flex", alignItems: "center", justifyContent: "space-between", padding: "16px 16px 9px" } }, [ + h("span", { key: "l", style: { fontFamily: "'JetBrains Mono',monospace", fontSize: "10px", fontWeight: 600, color: "#56565e", textTransform: "uppercase", letterSpacing: "1.3px" } }, "Chats"), + h("span", { key: "c", style: { fontFamily: "'JetBrains Mono',monospace", fontSize: "10px", fontWeight: 600, color: "#6b6b73" } }, String(chats.length)) + ]), + h("div", { key: "list", className: "msc-scroll", style: { flex: 1, overflowY: "auto", padding: "0 10px" } }, [ + ...chats.map(expandedRow), + h("div", { key: "gh", style: { marginTop: "14px", padding: "0 2px 6px" } }, h("span", { style: { fontFamily: "'JetBrains Mono',monospace", fontSize: "10px", fontWeight: 600, color: "#56565e", textTransform: "uppercase", letterSpacing: "1.3px" } }, "Group chats")), + h("div", { key: "gph", title: "Coming in v6.0", style: { display: "flex", alignItems: "center", gap: "12px", padding: "11px 12px", borderRadius: "11px", background: "transparent", border: "1px dashed rgba(255,255,255,0.09)", cursor: "not-allowed" } }, [ + h("div", { key: "i", style: { flex: "none", width: "38px", height: "38px", borderRadius: "11px", display: "grid", placeItems: "center", background: "rgba(255,255,255,0.025)", border: "1px solid rgba(255,255,255,0.06)", color: "#56565e" }, dangerouslySetInnerHTML: { __html: SB_SVG.users } }), + h("div", { key: "b", style: { flex: 1, minWidth: 0 } }, [ + h("div", { key: "t", style: { fontSize: "14px", fontWeight: 600, color: "#8a8a92" } }, "Group chats"), + h("div", { key: "s", style: { fontSize: "11.5px", color: "#56565e" } }, "Up to 8 peers \xB7 P2P mesh") + ]), + h("span", { key: "soon", style: { flex: "none", padding: "4px 9px", borderRadius: "7px", background: "rgba(240,137,42,0.1)", border: "1px solid rgba(240,137,42,0.24)", fontFamily: "'JetBrains Mono',monospace", fontSize: "9.5px", fontWeight: 700, color: "#f0892a", textTransform: "uppercase", letterSpacing: "0.8px" } }, "Soon") + ]) + ]), + h("div", { key: "new", style: { flex: "none", padding: "12px" } }, h("button", { + onClick: onNewChat, + style: { width: "100%", display: "inline-flex", alignItems: "center", justifyContent: "center", gap: "9px", padding: "12px", borderRadius: "11px", border: "none", background: "#f0892a", color: "#1a0f04", fontFamily: "inherit", fontSize: "14px", fontWeight: 700, cursor: "pointer", boxShadow: "0 8px 24px rgba(240,137,42,0.28)" } + }, [icon(SB_SVG.plus, { key: "p" }), "New chat"])), + presencePanelExpanded + ]; + const collapsedInner = [ + h("div", { key: "head", style: { flex: "none", display: "flex", flexDirection: "column", alignItems: "center", gap: "10px", padding: "13px 0", width: "100%", borderBottom: "1px solid rgba(255,255,255,0.06)" } }, [brandMark(32), collapseBtn(SB_SVG.chevR, "Expand")]), + h("div", { key: "list", className: "msc-scroll", style: { flex: 1, overflowY: "auto", display: "flex", flexDirection: "column", alignItems: "center", gap: "10px", padding: "14px 0", width: "100%" } }, [ + ...chats.map(dockItem), + h("div", { key: "sep", style: { width: "30px", height: "1px", background: "rgba(255,255,255,0.07)", margin: "2px 0" } }), + h("div", { key: "gph", title: "Group chats \u2014 coming in v6.0", style: { position: "relative", width: "44px", height: "44px", borderRadius: "12px", display: "grid", placeItems: "center", cursor: "not-allowed", background: "transparent", border: "1px dashed rgba(255,255,255,0.1)", color: "#56565e" }, dangerouslySetInnerHTML: { __html: SB_SVG.users } }) + ]), + h("div", { key: "new", style: { flex: "none", padding: "13px 0" } }, h("button", { + onClick: onNewChat, + title: "New chat", + style: { width: "44px", height: "44px", borderRadius: "12px", display: "grid", placeItems: "center", border: "none", background: "#f0892a", color: "#1a0f04", cursor: "pointer", boxShadow: "0 8px 24px rgba(240,137,42,0.28)" }, + dangerouslySetInnerHTML: { __html: SB_SVG.plus } + })), + presencePanelCollapsed + ]; + const railWidth = collapsed ? "72px" : "292px"; + const railStyle = { flex: "none", width: railWidth, display: "flex", flexDirection: "column", alignItems: collapsed ? "center" : "stretch", background: "#0c0c0e", borderRight: "1px solid rgba(255,255,255,0.06)" }; + const inner = collapsed ? collapsedInner : expandedInner; + return h(React.Fragment, null, [ + // Responsive behaviour (inline styles can't express media queries). + h("style", { key: "css", dangerouslySetInnerHTML: { __html: "@media (max-width:1023px){.sb-rail{display:none !important;}.sb-burger{display:grid !important;}}@media (min-width:1024px){.sb-drawer-overlay{display:none !important;}}" } }), + // Desktop rail + h("aside", { key: "rail", className: "sb-rail", style: railStyle }, inner), + // Mobile drawer overlay + h("div", { + key: "drawer", + className: "sb-drawer-overlay", + onClick: onCloseDrawer, + style: { position: "fixed", inset: 0, zIndex: 60, background: "rgba(6,6,8,0.6)", backdropFilter: "blur(4px)", WebkitBackdropFilter: "blur(4px)", display: drawerOpen ? "block" : "none" } + }, h("aside", { onClick: (e) => e.stopPropagation(), style: { position: "absolute", left: 0, top: 0, bottom: 0, width: "292px", display: "flex", flexDirection: "column", background: "#0c0c0e", borderRight: "1px solid rgba(255,255,255,0.06)", boxShadow: "0 0 60px rgba(0,0,0,0.6)" } }, expandedInner)) + ]); +}; var EnhancedSecureP2PChat = () => { - const [messages, setMessages] = React.useState([]); + const [sessionsState, dispatch] = React.useReducer(sessionsReducer, void 0, createInitialState); + const activeSessionId = sessionsState.activeSessionId; + const activeIdRef = React.useRef(null); + activeIdRef.current = activeSessionId; + const active = activeSessionId ? sessionsState.sessions[activeSessionId] : null; + const EMPTY_ARR = React.useRef([]).current; + const managersRef = React.useRef(/* @__PURE__ */ new Map()); + const integrationsRef = React.useRef(/* @__PURE__ */ new Map()); + const queuesRef = React.useRef(/* @__PURE__ */ new Map()); + const dispatchActive = React.useCallback((build) => { + const id = activeIdRef.current; + if (!id) return; + dispatch(build(id)); + }, []); + const messages = active ? active.messages : EMPTY_ARR; + const setMessages = React.useCallback((updaterOrArr) => { + const id = activeIdRef.current; + if (!id) return; + if (typeof updaterOrArr === "function") dispatch({ type: SESSION_ACTIONS.SET_MESSAGES, id, updater: updaterOrArr }); + else dispatch({ type: SESSION_ACTIONS.SET_MESSAGES, id, messages: updaterOrArr }); + }, []); + const connectionStatus = active ? active.status : "disconnected"; + const setConnectionStatus = React.useCallback((status) => dispatchActive((id) => ({ type: SESSION_ACTIONS.SET_STATUS, id, status })), [dispatchActive]); + const keyFingerprint = active ? active.keyFingerprint : ""; + const setKeyFingerprint = React.useCallback((fingerprint) => dispatchActive((id) => ({ type: SESSION_ACTIONS.SET_FINGERPRINT, id, fingerprint })), [dispatchActive]); + const verificationCode = active ? active.verificationCode : ""; + const setVerificationCode = React.useCallback((code) => dispatchActive((id) => ({ type: SESSION_ACTIONS.SET_VERIFICATION, id, code })), [dispatchActive]); + const isVerified = active ? active.sas.isVerified : false; + const setIsVerified = React.useCallback((v) => dispatchActive((id) => ({ type: SESSION_ACTIONS.SET_SAS, id, sas: { isVerified: !!v } })), [dispatchActive]); + const localVerificationConfirmed = active ? active.sas.localConfirmed : false; + const setLocalVerificationConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SESSION_ACTIONS.SET_SAS, id, sas: { localConfirmed: !!v } })), [dispatchActive]); + const remoteVerificationConfirmed = active ? active.sas.remoteConfirmed : false; + const setRemoteVerificationConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SESSION_ACTIONS.SET_SAS, id, sas: { remoteConfirmed: !!v } })), [dispatchActive]); + const bothVerificationsConfirmed = active ? active.sas.bothConfirmed : false; + const setBothVerificationsConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SESSION_ACTIONS.SET_SAS, id, sas: { bothConfirmed: !!v } })), [dispatchActive]); + const pendingIncomingFiles = active ? active.pendingIncomingFiles : EMPTY_ARR; + const setPendingIncomingFiles = React.useCallback((updaterOrArr) => { + const id = activeIdRef.current; + if (!id) return; + if (typeof updaterOrArr === "function") dispatch({ type: SESSION_ACTIONS.SET_PENDING_FILES, id, updater: updaterOrArr }); + else dispatch({ type: SESSION_ACTIONS.SET_PENDING_FILES, id, files: updaterOrArr }); + }, []); + const setupField = (name, fallback) => active ? active.setup[name] : fallback; + const setSetupField = (name) => React.useCallback((value) => dispatchActive((id) => ({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { [name]: value } })), [dispatchActive]); + const offerData = setupField("offerData", ""); + const setOfferData = setSetupField("offerData"); + const answerData = setupField("answerData", ""); + const setAnswerData = setSetupField("answerData"); + const offerInput = setupField("offerInput", ""); + const setOfferInput = setSetupField("offerInput"); + const answerInput = setupField("answerInput", ""); + const setAnswerInput = setSetupField("answerInput"); + const showOfferStep = setupField("showOfferStep", false); + const setShowOfferStep = setSetupField("showOfferStep"); + const showAnswerStep = setupField("showAnswerStep", false); + const setShowAnswerStep = setSetupField("showAnswerStep"); + const showVerification = setupField("showVerification", false); + const setShowVerification = setSetupField("showVerification"); + const showQRCode = setupField("showQRCode", false); + const setShowQRCode = setSetupField("showQRCode"); + const qrCodeUrl = setupField("qrCodeUrl", ""); + const setQrCodeUrl = setSetupField("qrCodeUrl"); + const isGeneratingKeys = setupField("isGeneratingKeys", false); + const setIsGeneratingKeys = setSetupField("isGeneratingKeys"); + const webrtcManagerRef = React.useMemo(() => ({ + get current() { + return managersRef.current.get(activeIdRef.current) || null; + }, + set current(v) { + const id = activeIdRef.current; + if (!id) return; + if (v) managersRef.current.set(id, v); + else managersRef.current.delete(id); + } + }), []); + const notificationIntegrationRef = React.useMemo(() => ({ + get current() { + return integrationsRef.current.get(activeIdRef.current) || null; + }, + set current(v) { + const id = activeIdRef.current; + if (!id) return; + if (v) integrationsRef.current.set(id, v); + else integrationsRef.current.delete(id); + } + }), []); + const [myStatus, setMyStatusState] = React.useState(() => { + try { + return localStorage.getItem("securebit_my_status") || "available"; + } catch { + return "available"; + } + }); + const myStatusRef = React.useRef(myStatus); + myStatusRef.current = myStatus; + const wirePresence = (s) => s === "invisible" ? "offline" : s; + const sendPresenceTo = React.useCallback((mgr, s) => { + if (!mgr || typeof mgr.sendMessage !== "function") return; + try { + if (mgr.isConnected && mgr.isConnected()) { + const p = mgr.sendMessage(JSON.stringify({ type: "presence", status: wirePresence(s) })); + if (p && typeof p.catch === "function") p.catch(() => { + }); + } + } catch (_) { + } + }, []); + const setMyStatus = React.useCallback((key) => { + setMyStatusState(key); + try { + localStorage.setItem("securebit_my_status", key); + } catch { + } + for (const mgr of managersRef.current.values()) sendPresenceTo(mgr, key); + }, [sendPresenceTo]); const [codeMode, setCodeMode] = React.useState(false); const [viewOnceMode, setViewOnceMode] = React.useState(false); const [viewOnceTtl, setViewOnceTtl] = React.useState(15); const [disappearTtl, setDisappearTtl] = React.useState(0); const [nowTick, setNowTick] = React.useState(() => Date.now()); - const [connectionStatus, setConnectionStatus] = React.useState("disconnected"); const [isOffline, setIsOffline] = React.useState(typeof navigator !== "undefined" && navigator.onLine === false); const offlineRef = React.useRef(isOffline); - const outgoingQueueRef = React.useRef([]); - const incomingQueueRef = React.useRef([]); React.useEffect(() => { offlineRef.current = isOffline; }, [isOffline]); @@ -1862,28 +2555,11 @@ var EnhancedSecureP2PChat = () => { setIceServersText(""); }, []); const [messageInput, setMessageInput] = React.useState(""); - const [offerData, setOfferData] = React.useState(""); - const [answerData, setAnswerData] = React.useState(""); - const [offerInput, setOfferInput] = React.useState(""); - const [answerInput, setAnswerInput] = React.useState(""); - const [keyFingerprint, setKeyFingerprint] = React.useState(""); - const [verificationCode, setVerificationCode] = React.useState(""); - const [showOfferStep, setShowOfferStep] = React.useState(false); - const [showAnswerStep, setShowAnswerStep] = React.useState(false); - const [showVerification, setShowVerification] = React.useState(false); - const [showQRCode, setShowQRCode] = React.useState(false); - const [qrCodeUrl, setQrCodeUrl] = React.useState(""); const [showQRScanner, setShowQRScanner] = React.useState(false); const [showQRScannerModal, setShowQRScannerModal] = React.useState(false); - const [isGeneratingKeys, setIsGeneratingKeys] = React.useState(false); - const [isVerified, setIsVerified] = React.useState(false); const [securityLevel, setSecurityLevel] = React.useState(null); const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0); - const [localVerificationConfirmed, setLocalVerificationConfirmed] = React.useState(false); - const [remoteVerificationConfirmed, setRemoteVerificationConfirmed] = React.useState(false); - const [bothVerificationsConfirmed, setBothVerificationsConfirmed] = React.useState(false); const [pendingSession, setPendingSession] = React.useState(null); - const [pendingIncomingFiles, setPendingIncomingFiles] = React.useState([]); const [connectionState, setConnectionState] = React.useState({ status: "disconnected", hasActiveAnswer: false, @@ -1912,8 +2588,6 @@ var EnhancedSecureP2PChat = () => { answerCreatedAt: Date.now() }); }; - const webrtcManagerRef = React.useRef(null); - const notificationIntegrationRef = React.useRef(null); React.useEffect(() => { return installDebugWindowHooks({ targetWindow: window, @@ -1966,32 +2640,38 @@ var EnhancedSecureP2PChat = () => { setMessages((prev) => prev.map((m) => String(m.mid) === String(mid) && m.type === "sent" ? { ...m, status } : m)); }, []); const flushOfflineQueues = React.useCallback(() => { - const out = outgoingQueueRef.current; - outgoingQueueRef.current = []; - for (const item of out) { - const send = webrtcManagerRef.current?.sendMessage?.(item.outText, item.meta); - if (send && typeof send.then === "function") { - send.then(() => updateMessageStatus(item.mid, "sent")).catch(() => updateMessageStatus(item.mid, "failed")); + for (const [id, q] of queuesRef.current.entries()) { + const mgr = managersRef.current.get(id); + const out = q.outgoing; + q.outgoing = []; + for (const item of out) { + const send = mgr?.sendMessage?.(item.outText, item.meta); + if (send && typeof send.then === "function") { + send.then(() => dispatch({ type: SESSION_ACTIONS.UPDATE_MESSAGE_STATUS, id, mid: item.mid, status: "delivered" })).catch(() => dispatch({ type: SESSION_ACTIONS.UPDATE_MESSAGE_STATUS, id, mid: item.mid, status: "failed" })); + } } - } - const inc = incomingQueueRef.current; - incomingQueueRef.current = []; - if (inc.length > 0) { - addMessageWithAutoScroll( - `Connection restored \u2014 ${inc.length} message${inc.length === 1 ? "" : "s"} received while you were offline.`, - "notice" - ); - } - for (const item of inc) { - addMessageWithAutoScroll(item.message, item.type, item.opts); - if (item.opts && item.opts.mid && (item.type === "received" || item.type === "sent")) { - try { - webrtcManagerRef.current?.sendDeliveryReceipt?.(item.opts.mid); - } catch (_) { + const inc = q.incoming; + q.incoming = []; + if (inc.length > 0) { + dispatch({ type: SESSION_ACTIONS.ADD_MESSAGE, id, message: buildSessionMessage( + `Connection restored \u2014 ${inc.length} message${inc.length === 1 ? "" : "s"} received while you were offline.`, + "notice" + ) }); + } + const viewing = id === activeIdRef.current && (typeof document === "undefined" || document.visibilityState === "visible"); + for (const item of inc) { + dispatch({ type: SESSION_ACTIONS.ADD_MESSAGE, id, message: buildSessionMessage(item.message, item.type, item.opts) }); + if (item.opts && item.opts.mid && item.type === "received") { + if (viewing) { + try { + mgr?.sendDeliveryReceipt?.(item.opts.mid); + } catch (_) { + } + } else if (q.pendingReadAcks) q.pendingReadAcks.push(item.opts.mid); } } } - }, [addMessageWithAutoScroll, updateMessageStatus]); + }, []); React.useEffect(() => { if (isOffline) return; flushOfflineQueues(); @@ -2054,35 +2734,85 @@ var EnhancedSecureP2PChat = () => { setTimeout(scrollToBottom, 150); } }, [messages]); - const hasExpiring = messages.some((m) => typeof m.expiresAt === "number"); + const anyExpiring = sessionsState.order.some((id) => (sessionsState.sessions[id]?.messages || []).some((m) => typeof m.expiresAt === "number")); + const sessionsStateRef = React.useRef(sessionsState); + sessionsStateRef.current = sessionsState; React.useEffect(() => { - if (!hasExpiring) return; + if (!anyExpiring) return; + const expireFn = (prev) => { + const now = Date.now(); + let changed = false; + const next = prev.map((m) => { + if (typeof m.expiresAt === "number" && m.expiresAt <= now && !m.expired) { + changed = true; + return { ...m, expired: true, message: "", expiresAt: void 0 }; + } + return m; + }); + return changed ? next : prev; + }; const interval = setInterval(() => { const now = Date.now(); setNowTick(now); - setMessages((prev) => { - let changed = false; - const next = prev.map((m) => { - if (typeof m.expiresAt === "number" && m.expiresAt <= now && !m.expired) { - changed = true; - return { ...m, expired: true, message: "", expiresAt: void 0 }; - } - return m; - }); - return changed ? next : prev; - }); + const st = sessionsStateRef.current; + for (const id of st.order) { + const msgs = st.sessions[id]?.messages || []; + if (msgs.some((m) => typeof m.expiresAt === "number" && m.expiresAt <= now && !m.expired)) { + dispatch({ type: SESSION_ACTIONS.SET_MESSAGES, id, updater: expireFn }); + } + } }, 1e3); return () => clearInterval(interval); - }, [hasExpiring]); - React.useEffect(() => { - if (webrtcManagerRef.current) { - console.log("\u26A0\uFE0F WebRTC Manager already initialized, skipping..."); - return; - } + }, [anyExpiring]); + const createSession = (opts = {}) => { + const role = opts.role || "offer"; + const entry = createSessionEntry({ role }); + const id = entry.id; + dispatch({ type: SESSION_ACTIONS.CREATE_SESSION, entry, activate: opts.activate !== false }); + queuesRef.current.set(id, { incoming: [], outgoing: [], pendingReadAcks: [] }); + const setMessages2 = (u) => { + if (typeof u === "function") dispatch({ type: SESSION_ACTIONS.SET_MESSAGES, id, updater: u }); + else dispatch({ type: SESSION_ACTIONS.SET_MESSAGES, id, messages: u }); + }; + const addMessageWithAutoScroll2 = (message, type, opts2 = {}) => { + dispatch({ type: SESSION_ACTIONS.ADD_MESSAGE, id, message: buildSessionMessage(message, type, opts2) }); + if (type === "received" && id !== activeIdRef.current) { + dispatch({ type: SESSION_ACTIONS.INCREMENT_UNREAD, id }); + } + }; + const updateMessageStatus2 = (mid, status) => { + if (mid) dispatch({ type: SESSION_ACTIONS.UPDATE_MESSAGE_STATUS, id, mid, status }); + }; + const setConnectionStatus2 = (status) => dispatch({ type: SESSION_ACTIONS.SET_STATUS, id, status }); + const setKeyFingerprint2 = (fingerprint) => dispatch({ type: SESSION_ACTIONS.SET_FINGERPRINT, id, fingerprint }); + const setVerificationCode2 = (code) => dispatch({ type: SESSION_ACTIONS.SET_VERIFICATION, id, code }); + const setIsVerified2 = (v) => dispatch({ type: SESSION_ACTIONS.SET_SAS, id, sas: { isVerified: !!v } }); + const setLocalVerificationConfirmed2 = (v) => dispatch({ type: SESSION_ACTIONS.SET_SAS, id, sas: { localConfirmed: !!v } }); + const setRemoteVerificationConfirmed2 = (v) => dispatch({ type: SESSION_ACTIONS.SET_SAS, id, sas: { remoteConfirmed: !!v } }); + const setBothVerificationsConfirmed2 = (v) => dispatch({ type: SESSION_ACTIONS.SET_SAS, id, sas: { bothConfirmed: !!v } }); + const setShowVerification2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { showVerification: !!v } }); + const setShowOfferStep2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { showOfferStep: !!v } }); + const setShowAnswerStep2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { showAnswerStep: !!v } }); + const setShowQRCode2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { showQRCode: !!v } }); + const setQrCodeUrl2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { qrCodeUrl: v } }); + const setOfferData2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { offerData: v } }); + const setAnswerData2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { answerData: v } }); + const setOfferInput2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { offerInput: v } }); + const setAnswerInput2 = (v) => dispatch({ type: SESSION_ACTIONS.PATCH_SETUP, id, patch: { answerInput: v } }); + const setPendingIncomingFiles2 = (u) => { + if (typeof u === "function") dispatch({ type: SESSION_ACTIONS.SET_PENDING_FILES, id, updater: u }); + else dispatch({ type: SESSION_ACTIONS.SET_PENDING_FILES, id, files: u }); + }; + const sessionQueues = () => queuesRef.current.get(id) || { incoming: [], outgoing: [] }; const handleMessage = (message, type, meta) => { if (typeof message === "string" && message.trim().startsWith("{")) { try { const parsedMessage = JSON.parse(message); + if (parsedMessage.type === "presence") { + const st = parsedMessage.data && parsedMessage.data.status || parsedMessage.status; + if (st) dispatch({ type: SESSION_ACTIONS.SET_PEER_PRESENCE, id, presence: st }); + return; + } const blockedTypes = [ "file_transfer_start", "file_transfer_response", @@ -2106,53 +2836,75 @@ var EnhancedSecureP2PChat = () => { console.log(`Blocked system/file message from chat: ${parsedMessage.type}`); return; } + if (parsedMessage.type === "message" && typeof parsedMessage.data === "string") { + message = parsedMessage.data; + if (parsedMessage.meta && typeof parsedMessage.meta === "object") meta = parsedMessage.meta; + } } catch (parseError) { } } - const opts = {}; + const opts2 = {}; if (meta && typeof meta === "object") { - if (typeof meta.mid === "string") opts.mid = meta.mid; + if (typeof meta.mid === "string") opts2.mid = meta.mid; if (meta.once === true) { - opts.viewOnce = true; - opts.viewOnceTtl = Number.isFinite(meta.onceTtl) ? meta.onceTtl : 15; + opts2.viewOnce = true; + opts2.viewOnceTtl = Number.isFinite(meta.onceTtl) ? meta.onceTtl : 15; } if (Number.isFinite(meta.ttl) && meta.ttl > 0) { - opts.expiresAt = Date.now() + meta.ttl * 1e3; + opts2.expiresAt = Date.now() + meta.ttl * 1e3; } - if (Number.isFinite(meta.ts)) opts.timestamp = meta.ts; + if (Number.isFinite(meta.ts)) opts2.timestamp = meta.ts; } if (offlineRef.current && type === "received") { - incomingQueueRef.current.push({ message, type, opts }); + sessionQueues().incoming.push({ message, type, opts: opts2 }); return; } - addMessageWithAutoScroll(message, type, opts); - if (opts.mid && (type === "received" || type === "sent")) { - try { - webrtcManagerRef.current?.sendDeliveryReceipt?.(opts.mid); - } catch (_) { + addMessageWithAutoScroll2(message, type, opts2); + if (opts2.mid && type === "received") { + const beingViewed = id === activeIdRef.current && (typeof document === "undefined" || document.visibilityState === "visible"); + if (beingViewed) { + try { + manager?.sendDeliveryReceipt?.(opts2.mid); + } catch (_) { + } + } else { + const q = sessionQueues(); + if (q.pendingReadAcks) q.pendingReadAcks.push(opts2.mid); } } }; const handleStatusChange = (status) => { - setConnectionStatus(status); + setConnectionStatus2(status); if (status === "connected") { document.dispatchEvent(new CustomEvent("new-connection")); if (!window.isUpdatingSecurity) { updateSecurityLevel().catch(console.error); } } else if (status === "verifying") { - setShowVerification(true); + setShowVerification2(true); if (!window.isUpdatingSecurity) { updateSecurityLevel().catch(console.error); } } else if (status === "verified") { - setIsVerified(true); - setShowVerification(false); - setBothVerificationsConfirmed(true); - setConnectionStatus("connected"); + setIsVerified2(true); + setShowVerification2(false); + setBothVerificationsConfirmed2(true); + setConnectionStatus2("connected"); setTimeout(() => { - setIsVerified(true); + setIsVerified2(true); }, 0); + try { + const s = myStatusRef.current === "invisible" ? "offline" : myStatusRef.current; + setTimeout(() => { + try { + const p = manager.sendMessage?.(JSON.stringify({ type: "presence", status: s })); + if (p && typeof p.catch === "function") p.catch(() => { + }); + } catch (_) { + } + }, 400); + } catch (_) { + } if (!window.isUpdatingSecurity) { updateSecurityLevel().catch(console.error); } @@ -2161,98 +2913,59 @@ var EnhancedSecureP2PChat = () => { updateSecurityLevel().catch(console.error); } } else if (status === "disconnected") { - updateConnectionState({ status: "disconnected" }); - setConnectionStatus("disconnected"); - if (shouldPreserveAnswerData()) { - setIsVerified(false); - setShowVerification(false); - return; - } - setIsVerified(false); - setShowVerification(false); - document.dispatchEvent(new CustomEvent("disconnected")); - setLocalVerificationConfirmed(false); - setRemoteVerificationConfirmed(false); - setBothVerificationsConfirmed(false); - setOfferData(""); - setAnswerData(""); - setOfferInput(""); - setAnswerInput(""); - setShowOfferStep(false); - setShowAnswerStep(false); - setKeyFingerprint(""); - setVerificationCode(""); - setSecurityLevel(null); - setTimeout(() => { - setConnectionStatus("disconnected"); - setShowVerification(false); - setOfferData(""); - setAnswerData(""); - setOfferInput(""); - setAnswerInput(""); - setShowOfferStep(false); - setShowAnswerStep(false); - setMessages([]); - }, 1e3); + setConnectionStatus2("disconnected"); + setIsVerified2(false); + setShowVerification2(false); + setLocalVerificationConfirmed2(false); + setRemoteVerificationConfirmed2(false); + setBothVerificationsConfirmed2(false); + if (id === activeIdRef.current) document.dispatchEvent(new CustomEvent("disconnected")); } else if (status === "peer_disconnected") { - setSessionTimeLeft(0); - document.dispatchEvent(new CustomEvent("peer-disconnect")); - setTimeout(() => { - setKeyFingerprint(""); - setVerificationCode(""); - setSecurityLevel(null); - setIsVerified(false); - setShowVerification(false); - setConnectionStatus("disconnected"); - setLocalVerificationConfirmed(false); - setRemoteVerificationConfirmed(false); - setBothVerificationsConfirmed(false); - setOfferData(""); - setAnswerData(""); - setOfferInput(""); - setAnswerInput(""); - setShowOfferStep(false); - setShowAnswerStep(false); - setMessages([]); - if (typeof console.clear === "function") { - console.clear(); - } - }, 2e3); + if (id === activeIdRef.current) { + setSessionTimeLeft(0); + document.dispatchEvent(new CustomEvent("peer-disconnect")); + } + setConnectionStatus2("peer_disconnected"); + setIsVerified2(false); + setShowVerification2(false); + setLocalVerificationConfirmed2(false); + setRemoteVerificationConfirmed2(false); + setBothVerificationsConfirmed2(false); } }; const handleKeyExchange = (fingerprint) => { if (fingerprint === "") { - setKeyFingerprint(""); + setKeyFingerprint2(""); } else { - setKeyFingerprint(fingerprint); + setKeyFingerprint2(fingerprint); } }; const handleVerificationRequired = (code) => { if (code === "") { - setVerificationCode(""); - setShowVerification(false); + setVerificationCode2(""); + setShowVerification2(false); } else { - setVerificationCode(code); - setShowVerification(true); + setVerificationCode2(code); + setShowVerification2(true); } }; const handleVerificationStateChange = (state) => { - setLocalVerificationConfirmed(state.localConfirmed); - setRemoteVerificationConfirmed(state.remoteConfirmed); - setBothVerificationsConfirmed(state.bothConfirmed); + setLocalVerificationConfirmed2(state.localConfirmed); + setRemoteVerificationConfirmed2(state.remoteConfirmed); + setBothVerificationsConfirmed2(state.bothConfirmed); }; const handleAnswerError = (errorType, errorMessage) => { if (errorType === "replay_attack") { setSessionTimeLeft(0); setPendingSession(null); - addMessageWithAutoScroll("\u{1F4A1} Data is outdated. Please create a new invitation or use a current response code.", "system"); + addMessageWithAutoScroll2("\u{1F4A1} Data is outdated. Please create a new invitation or use a current response code.", "system"); if (typeof console.clear === "function") { console.clear(); } } else if (errorType === "security_violation") { setSessionTimeLeft(0); setPendingSession(null); - addMessageWithAutoScroll(` Security breach: ${errorMessage}`, "system"); + addMessageWithAutoScroll2(` Security breach: ${errorMessage}`, "system"); if (typeof console.clear === "function") { console.clear(); } @@ -2261,7 +2974,7 @@ var EnhancedSecureP2PChat = () => { if (typeof console.clear === "function") { console.clear(); } - webrtcManagerRef.current = new EnhancedSecureWebRTCManager( + const manager = new EnhancedSecureWebRTCManager( handleMessage, handleStatusChange, handleKeyExchange, @@ -2276,57 +2989,184 @@ var EnhancedSecureP2PChat = () => { } } ); - webrtcManagerRef.current.onMessageDelete = (mid) => { + managersRef.current.set(id, manager); + manager.onMessageDelete = (mid) => { if (!mid) return; - setMessages((prev) => prev.filter((m) => String(m.mid) !== String(mid))); + setMessages2((prev) => prev.filter((m) => String(m.mid) !== String(mid))); }; - webrtcManagerRef.current.onMessageDelivered = (mid) => { - updateMessageStatus(mid, "delivered"); + manager.onMessageDelivered = (mid) => { + updateMessageStatus2(mid, "read"); }; - if (typeof Notification !== "undefined" && Notification && Notification.permission === "granted" && window.NotificationIntegration && !notificationIntegrationRef.current) { + if (typeof Notification !== "undefined" && Notification && Notification.permission === "granted" && window.NotificationIntegration && !integrationsRef.current.get(id)) { try { - const integration = new window.NotificationIntegration(webrtcManagerRef.current); + const integration = new window.NotificationIntegration(manager); integration.init().then(() => { - notificationIntegrationRef.current = integration; + integrationsRef.current.set(id, integration); }).catch((error) => { }); } catch (error) { } } - handleMessage(" SecureBit.chat Enhanced Security Edition v4.9.0 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.", "system"); + handleMessage(" SecureBit.chat Enhanced Security Edition v4.10.0 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.", "system"); + manager.setFileTransferCallbacks( + // Progress callback + (progress) => { + console.log("File progress:", progress); + }, + // File received callback — auto-save to disk, no button press needed. + (fileData) => { + const sizeMb = Math.max(1, Math.round((fileData.fileSize || 0) / (1024 * 1024))); + const saveToDisk = async () => { + const url = await fileData.getObjectURL(); + const a = document.createElement("a"); + a.href = url; + a.download = fileData.fileName || "file"; + document.body.appendChild(a); + a.click(); + a.remove(); + setTimeout(() => fileData.revokeObjectURL(url), 15e3); + }; + saveToDisk().then(() => { + addMessageWithAutoScroll2(`File received & saved: ${fileData.fileName} (${sizeMb} MB)`, "system"); + }).catch((e) => { + console.error("Auto-save failed:", e); + addMessageWithAutoScroll2(`File received: ${fileData.fileName} (${sizeMb} MB). Open the file panel to download it.`, "system"); + }); + }, + // Error callback + (error) => { + console.error("File transfer error:", error); + if (error.includes("Connection not ready")) { + addMessageWithAutoScroll2(` File transfer error: connection not ready. Try again later.`, "system"); + } else if (error.includes("File too large")) { + addMessageWithAutoScroll2(` File is too big. Maximum size: 100 MB`, "system"); + } else { + addMessageWithAutoScroll2(` File transfer error: ${error}`, "system"); + } + }, + // Incoming file request callback — receiver must explicitly accept before any data is sent + (fileRequest) => { + setPendingIncomingFiles2((prev) => { + if (prev.some((f) => f.fileId === fileRequest.fileId)) return prev; + return [...prev, fileRequest]; + }); + } + ); + return id; + }; + const createSessionRef = React.useRef(createSession); + createSessionRef.current = createSession; + const destroyingRef = React.useRef(/* @__PURE__ */ new Set()); + const destroySession = React.useCallback((id) => { + if (!id || destroyingRef.current.has(id)) return; + destroyingRef.current.add(id); + try { + const mgr = managersRef.current.get(id); + if (mgr) { + try { + mgr.disconnect(); + } catch (_) { + } + managersRef.current.delete(id); + } + const integ = integrationsRef.current.get(id); + if (integ) { + try { + integ.cleanup?.(); + } catch (_) { + } + integrationsRef.current.delete(id); + } + queuesRef.current.delete(id); + dispatch({ type: SESSION_ACTIONS.REMOVE_SESSION, id }); + } finally { + destroyingRef.current.delete(id); + } + }, []); + React.useEffect(() => { + if (sessionsState.order.length === 0) createSessionRef.current({ role: "offer" }); + }, [sessionsState.order.length]); + const [sidebarCollapsed, setSidebarCollapsed] = React.useState(() => { + try { + return localStorage.getItem("securebit_sidebar_collapsed") === "true"; + } catch { + return false; + } + }); + React.useEffect(() => { + try { + localStorage.setItem("securebit_sidebar_collapsed", String(sidebarCollapsed)); + } catch { + } + }, [sidebarCollapsed]); + const [sidebarDrawerOpen, setSidebarDrawerOpen] = React.useState(false); + const handleSelectSession = React.useCallback((id) => { + dispatch({ type: SESSION_ACTIONS.SET_ACTIVE, id }); + dispatch({ type: SESSION_ACTIONS.CLEAR_UNREAD, id }); + setSidebarDrawerOpen(false); + }, []); + const handleNewChat = React.useCallback(() => { + createSessionRef.current({ role: "offer" }); + setSidebarDrawerOpen(false); + }, []); + const handleRenameSession = React.useCallback((id, label) => { + dispatch({ type: SESSION_ACTIONS.RENAME, id, label }); + }, []); + const flushReadAcks = React.useCallback((id) => { + if (!id) return; + const q = queuesRef.current.get(id); + const mgr = managersRef.current.get(id); + if (!q || !mgr || !q.pendingReadAcks || q.pendingReadAcks.length === 0) return; + const acks = q.pendingReadAcks; + q.pendingReadAcks = []; + for (const mid of acks) { + try { + mgr.sendDeliveryReceipt?.(mid); + } catch (_) { + } + } + }, []); + React.useEffect(() => { + if (!activeSessionId) return; + dispatch({ type: SESSION_ACTIONS.CLEAR_UNREAD, id: activeSessionId }); + if (typeof document === "undefined" || document.visibilityState === "visible") flushReadAcks(activeSessionId); + }, [activeSessionId, flushReadAcks]); + const didInitRef = React.useRef(false); + React.useEffect(() => { + if (didInitRef.current) return; + didInitRef.current = true; + let isTabSwitching = false; + let tabSwitchTimeout = null; const handleBeforeUnload = (event) => { if (event.type === "beforeunload" && !isTabSwitching) { - if (webrtcManagerRef.current && webrtcManagerRef.current.isConnected()) { + for (const mgr of managersRef.current.values()) { try { - webrtcManagerRef.current.sendSystemMessage({ - type: "peer_disconnect", - reason: "user_disconnect", - timestamp: Date.now() - }); - } catch (error) { - } - setTimeout(() => { - if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); + if (mgr.isConnected && mgr.isConnected()) { + try { + mgr.sendSystemMessage({ type: "peer_disconnect", reason: "user_disconnect", timestamp: Date.now() }); + } catch (_) { + } + setTimeout(() => { + try { + mgr.disconnect(); + } catch (_) { + } + }, 100); + } else { + mgr.disconnect(); } - }, 100); - } else if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); + } catch (_) { + } } } else if (isTabSwitching) { event.preventDefault(); event.returnValue = ""; } }; - window.addEventListener("beforeunload", handleBeforeUnload); - let isTabSwitching = false; - let tabSwitchTimeout = null; const handleVisibilityChange = () => { if (document.visibilityState === "hidden") { isTabSwitching = true; - if (tabSwitchTimeout) { - clearTimeout(tabSwitchTimeout); - } + if (tabSwitchTimeout) clearTimeout(tabSwitchTimeout); tabSwitchTimeout = setTimeout(() => { isTabSwitching = false; }, 5e3); @@ -2336,55 +3176,11 @@ var EnhancedSecureP2PChat = () => { clearTimeout(tabSwitchTimeout); tabSwitchTimeout = null; } + flushReadAcks(activeIdRef.current); } }; + window.addEventListener("beforeunload", handleBeforeUnload); document.addEventListener("visibilitychange", handleVisibilityChange); - if (webrtcManagerRef.current) { - webrtcManagerRef.current.setFileTransferCallbacks( - // Progress callback - (progress) => { - console.log("File progress:", progress); - }, - // File received callback — auto-save to disk, no button press needed. - (fileData) => { - const sizeMb = Math.max(1, Math.round((fileData.fileSize || 0) / (1024 * 1024))); - const saveToDisk = async () => { - const url = await fileData.getObjectURL(); - const a = document.createElement("a"); - a.href = url; - a.download = fileData.fileName || "file"; - document.body.appendChild(a); - a.click(); - a.remove(); - setTimeout(() => fileData.revokeObjectURL(url), 15e3); - }; - saveToDisk().then(() => { - addMessageWithAutoScroll(`File received & saved: ${fileData.fileName} (${sizeMb} MB)`, "system"); - }).catch((e) => { - console.error("Auto-save failed:", e); - addMessageWithAutoScroll(`File received: ${fileData.fileName} (${sizeMb} MB). Open the file panel to download it.`, "system"); - }); - }, - // Error callback - (error) => { - console.error("File transfer error:", error); - if (error.includes("Connection not ready")) { - addMessageWithAutoScroll(` File transfer error: connection not ready. Try again later.`, "system"); - } else if (error.includes("File too large")) { - addMessageWithAutoScroll(` File is too big. Maximum size: 100 MB`, "system"); - } else { - addMessageWithAutoScroll(` File transfer error: ${error}`, "system"); - } - }, - // Incoming file request callback — receiver must explicitly accept before any data is sent - (fileRequest) => { - setPendingIncomingFiles((prev) => { - if (prev.some((f) => f.fileId === fileRequest.fileId)) return prev; - return [...prev, fileRequest]; - }); - } - ); - } return () => { window.removeEventListener("beforeunload", handleBeforeUnload); document.removeEventListener("visibilitychange", handleVisibilityChange); @@ -2392,10 +3188,21 @@ var EnhancedSecureP2PChat = () => { clearTimeout(tabSwitchTimeout); tabSwitchTimeout = null; } - if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); - webrtcManagerRef.current = null; + for (const mgr of managersRef.current.values()) { + try { + mgr.disconnect(); + } catch (_) { + } } + managersRef.current.clear(); + for (const integ of integrationsRef.current.values()) { + try { + integ.cleanup?.(); + } catch (_) { + } + } + integrationsRef.current.clear(); + queuesRef.current.clear(); }; }, []); const compressOfferData = (offerData2) => { @@ -2460,6 +3267,15 @@ var EnhancedSecureP2PChat = () => { const [qrFrameIndex, setQrFrameIndex] = React.useState(0); const [qrManualMode, setQrManualMode] = React.useState(false); const qrAnimationRef = React.useRef({ timer: null, chunks: [], idx: 0, active: false }); + React.useEffect(() => () => { + try { + if (qrAnimationRef.current && qrAnimationRef.current.timer) { + clearInterval(qrAnimationRef.current.timer); + qrAnimationRef.current.timer = null; + } + } catch { + } + }, [activeSessionId]); const stopQrAnimation = () => { try { if (qrAnimationRef.current.timer) { @@ -3132,7 +3948,7 @@ var EnhancedSecureP2PChat = () => { timestamp: Date.now() }]); setMessages((prev) => [...prev, { - message: "\u{1F4E4} Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.)..", + message: "Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.).", type: "system", id: Date.now(), timestamp: Date.now() @@ -3478,9 +4294,6 @@ var EnhancedSecureP2PChat = () => { if (!webrtcManagerRef.current) { return; } - if (!webrtcManagerRef.current.isConnected()) { - return; - } const baseTextEarly = messageInput.trim(); const midEarly = `m_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`; const offlineNow = isOffline || typeof navigator !== "undefined" && navigator.onLine === false || window.pwaOfflineManager && window.pwaOfflineManager.isOnline === false; @@ -3496,12 +4309,16 @@ var EnhancedSecureP2PChat = () => { const echoOpts = { mid: midEarly, status: "sent", timestamp: tsOff }; if (disappearTtl > 0) echoOpts.expiresAt = tsOff + disappearTtl * 1e3; addMessageWithAutoScroll(outTextOff, "sent", echoOpts); - outgoingQueueRef.current.push({ outText: outTextOff, meta: metaOff, mid: midEarly }); + const q = queuesRef.current.get(activeIdRef.current); + if (q) q.outgoing.push({ outText: outTextOff, meta: metaOff, mid: midEarly }); setMessageInput(""); if (codeMode) setCodeMode(false); if (viewOnceMode) setViewOnceMode(false); return; } + if (!webrtcManagerRef.current.isConnected()) { + return; + } try { const baseText = baseTextEarly; const outText = codeMode ? "```\n" + baseText + "\n```" : baseText; @@ -3517,7 +4334,7 @@ var EnhancedSecureP2PChat = () => { addMessageWithAutoScroll(outText, "sent", localOpts); try { await webrtcManagerRef.current.sendMessage(outText, meta); - updateMessageStatus(mid, "sent"); + updateMessageStatus(mid, "delivered"); } catch (sendErr) { updateMessageStatus(mid, "failed"); throw sendErr; @@ -3591,56 +4408,15 @@ var EnhancedSecureP2PChat = () => { }, []); const handleDisconnect = () => { try { + const id = activeIdRef.current; setSessionTimeLeft(0); - updateConnectionState({ - status: "disconnected", - isUserInitiatedDisconnect: true - }); - if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); - } - if (notificationIntegrationRef.current) { - notificationIntegrationRef.current.cleanup(); - notificationIntegrationRef.current = null; - } - setKeyFingerprint(""); - setVerificationCode(""); - setSecurityLevel(null); - setIsVerified(false); - setShowVerification(false); - setConnectionStatus("disconnected"); - setLocalVerificationConfirmed(false); - setRemoteVerificationConfirmed(false); - setBothVerificationsConfirmed(false); - setOfferData(""); - setAnswerData(""); - setOfferInput(""); - setAnswerInput(""); - setShowOfferStep(false); - setShowAnswerStep(false); - setIsGeneratingKeys(false); - setShowQRCode(false); - setQrCodeUrl(""); - setShowQRScanner(false); - setShowQRScannerModal(false); - setMessages([]); - setPendingIncomingFiles([]); - if (typeof console.clear === "function") { - console.clear(); - } document.dispatchEvent(new CustomEvent("peer-disconnect")); document.dispatchEvent(new CustomEvent("disconnected")); document.dispatchEvent(new CustomEvent("session-cleanup", { - detail: { - timestamp: Date.now(), - reason: "manual_disconnect" - } + detail: { timestamp: Date.now(), reason: "manual_disconnect" } })); - handleClearData(); - setTimeout(() => { - setSessionTimeLeft(0); - }, 500); - console.log("Disconnect completed successfully"); + destroySession(id); + if (typeof console.clear === "function") console.clear(); } catch (error) { console.error("Error during disconnect:", error); } @@ -3754,214 +4530,261 @@ var EnhancedSecureP2PChat = () => { }; } }, [showQRScannerModal]); + const sessionChats = decorateSessions(sessionsState); + const showSidebar = sessionsState.order.length > 1 || sessionsState.order.some((id) => { + const s = sessionsState.sessions[id]; + return s && s.sas && s.sas.isVerified; + }); return React.createElement("div", { - className: "minimal-bg min-h-screen" + className: "minimal-bg", + // With the rail visible the app is a fixed-height shell (rail + column + // fill the viewport, design-style). Otherwise it's the scrollable landing. + // flexDirection:'row' is explicit — the .minimal-bg class forces + // flex-direction:column, which would otherwise stack the rail ABOVE the chat. + style: showSidebar ? { display: "flex", flexDirection: "row", height: "100vh", width: "100%", overflow: "hidden" } : { minHeight: "100vh" } }, [ - // Advanced network settings now render inside the connection - // screen's right panel (see EnhancedConnectionSetup), matching - // the design's slide-up-within-the-right-column behavior. - // The verified chat renders its own in-chat header (SecureBit Chat - // design); the shared header is shown only on the landing/setup view. - !isConnectedAndVerified && window.EnhancedMinimalHeader && React.createElement(window.EnhancedMinimalHeader, { - key: "header", - status: connectionStatus, - fingerprint: keyFingerprint, - verificationCode, - onDisconnect: handleDisconnect, - isConnected: isConnectedAndVerified, - securityLevel, - // sessionManager removed - all features enabled by default - webrtcManager: webrtcManagerRef.current + showSidebar && React.createElement(SessionsSidebar, { + key: "sessions-sidebar", + chats: sessionChats, + collapsed: sidebarCollapsed, + drawerOpen: sidebarDrawerOpen, + onToggleCollapse: () => setSidebarCollapsed((v) => !v), + onSelect: handleSelectSession, + onNewChat: handleNewChat, + onRename: handleRenameSession, + onCloseDrawer: () => setSidebarDrawerOpen(false), + myStatus, + onSetStatus: setMyStatus }), - React.createElement( - "main", - { - key: "main" - }, - /* @__PURE__ */ (() => { - return isConnectedAndVerified; - })() ? (() => { - return React.createElement(EnhancedChatInterface, { - messages, - messageInput, - setMessageInput, - onSendMessage: handleSendMessage, - onDisconnect: handleDisconnect, - keyFingerprint, - isVerified, - chatMessagesRef, - scrollToBottom, - webrtcManager: webrtcManagerRef.current, - status: connectionStatus, - pendingIncomingFiles, - onIncomingDecision: handleIncomingDecision, - // Secure chat extras - codeMode, - setCodeMode, - viewOnceMode, - setViewOnceMode, - viewOnceTtl, - setViewOnceTtl, - disappearTtl, - setDisappearTtl, - nowTick, - onUnsendMessage: handleUnsendMessage, - onMessageExpire: handleMessageExpire - }); - })() : React.createElement(EnhancedConnectionSetup, { - onCreateOffer: handleCreateOffer, - onCreateAnswer: handleCreateAnswer, - onConnect: handleConnect, - onClearData: handleClearData, - onVerifyConnection: handleVerifyConnection, - connectionStatus, - offerData, - answerData, - offerInput, - setOfferInput, - answerInput, - setAnswerInput, - showOfferStep, - showAnswerStep, + // Mobile-only hamburger that opens the drawer (hidden on desktop via CSS). + showSidebar && React.createElement("button", { + key: "sb-burger", + className: "sb-burger", + onClick: () => setSidebarDrawerOpen(true), + style: { display: "none", position: "fixed", top: "13px", left: "13px", zIndex: 55, width: "38px", height: "38px", borderRadius: "10px", placeItems: "center", border: "1px solid rgba(255,255,255,0.1)", background: "rgba(18,18,20,0.9)", color: "#cfcfd4", cursor: "pointer" }, + dangerouslySetInnerHTML: { __html: SB_SVG.burger } + }), + React.createElement("div", { + key: "app-column", + className: showSidebar ? "minimal-bg" : "minimal-bg min-h-screen", + style: showSidebar ? { flex: 1, minWidth: 0, height: "100vh", overflow: "hidden", display: "flex", flexDirection: "column" } : {} + }, [ + // Advanced network settings now render inside the connection + // screen's right panel (see EnhancedConnectionSetup), matching + // the design's slide-up-within-the-right-column behavior. + // The verified chat renders its own in-chat header (SecureBit Chat + // design); the shared header is shown only on the landing/setup view. + !isConnectedAndVerified && !showSidebar && window.EnhancedMinimalHeader && React.createElement(window.EnhancedMinimalHeader, { + key: "header", + status: connectionStatus, + fingerprint: keyFingerprint, verificationCode, - showVerification, - showQRCode, - qrCodeUrl, - showQRScanner, - setShowQRCode, - setShowQRScanner, - setShowQRScannerModal, - messages, - localVerificationConfirmed, - remoteVerificationConfirmed, - bothVerificationsConfirmed, - // QR control props - qrFramesTotal, - qrFrameIndex, - qrManualMode, - toggleQrManualMode, - nextQrFrame, - prevQrFrame, - // PAKE passwords removed - using SAS verification instead - markAnswerCreated, - notificationIntegrationRef, - isGeneratingKeys, - setIsGeneratingKeys, - handleCreateOffer, - relayOnlyMode, - setRelayOnlyMode, - webrtcManagerRef, - showIceSettings, - setShowIceSettings, - iceServersText, - iceSettingsPersisted, - customIceServers, - handleApplyIceSettings, - handleForgetIceSettings - }) - ), - // QR Scanner Modal — camera scan (design import: "Start Secure" / Camera scan modal) - showQRScannerModal && (() => { - const closeScanner = () => { - setShowQRScannerModal(false); - qrChunksBufferRef.current = { id: null, total: 0, seen: /* @__PURE__ */ new Set(), items: [] }; - }; - const buf = qrChunksBufferRef.current; - const hasParts = !!(buf && buf.id && buf.total > 1); - const framesText = hasParts ? `Scanning frames\u2026 ${buf.seen.size} / ${buf.total}` : "Scanning\u2026"; - const corner = (k, st) => React.createElement("span", { - key: k, - style: Object.assign({ position: "absolute", width: "34px", height: "34px", zIndex: 3 }, st) - }); - return React.createElement("div", { - key: "qr-scanner-modal", - onClick: closeScanner, - style: { position: "fixed", inset: 0, zIndex: 50, display: "flex", alignItems: "center", justifyContent: "center", padding: "32px", background: "rgba(6,6,8,0.82)", backdropFilter: "blur(10px)", WebkitBackdropFilter: "blur(10px)", animation: "sbUp .2s ease" } - }, [ - React.createElement("div", { - key: "scanner-container", - onClick: (e) => e.stopPropagation(), - style: { width: "100%", maxWidth: "420px", borderRadius: "22px", border: "1px solid rgba(255,255,255,0.1)", background: "#111113", boxShadow: "0 30px 90px rgba(0,0,0,0.6)", overflow: "hidden" } + onDisconnect: handleDisconnect, + isConnected: isConnectedAndVerified, + securityLevel, + // sessionManager removed - all features enabled by default + webrtcManager: webrtcManagerRef.current + }), + React.createElement( + "main", + { + key: "main" + }, + /* @__PURE__ */ (() => { + return isConnectedAndVerified; + })() ? (() => { + return React.createElement(EnhancedChatInterface, { + title: active ? active.peerLabel : "", + isOffline, + peerPresence: active ? active.peerPresence : null, + onRenameTitle: (label) => { + if (activeSessionId) dispatch({ type: SESSION_ACTIONS.RENAME, id: activeSessionId, label }); + }, + messages, + messageInput, + setMessageInput, + onSendMessage: handleSendMessage, + onDisconnect: handleDisconnect, + keyFingerprint, + isVerified, + chatMessagesRef, + scrollToBottom, + webrtcManager: webrtcManagerRef.current, + status: connectionStatus, + pendingIncomingFiles, + onIncomingDecision: handleIncomingDecision, + // Secure chat extras + codeMode, + setCodeMode, + viewOnceMode, + setViewOnceMode, + viewOnceTtl, + setViewOnceTtl, + disappearTtl, + setDisappearTtl, + nowTick, + onUnsendMessage: handleUnsendMessage, + onMessageExpire: handleMessageExpire + }); + })() : React.createElement(EnhancedConnectionSetup, { + onCreateOffer: handleCreateOffer, + onCreateAnswer: handleCreateAnswer, + onConnect: handleConnect, + onClearData: handleClearData, + onVerifyConnection: handleVerifyConnection, + connectionStatus, + offerData, + answerData, + offerInput, + setOfferInput, + answerInput, + setAnswerInput, + showOfferStep, + showAnswerStep, + verificationCode, + showVerification, + showQRCode, + qrCodeUrl, + showQRScanner, + setShowQRCode, + setShowQRScanner, + setShowQRScannerModal, + messages, + localVerificationConfirmed, + remoteVerificationConfirmed, + bothVerificationsConfirmed, + // QR control props + qrFramesTotal, + qrFrameIndex, + qrManualMode, + toggleQrManualMode, + nextQrFrame, + prevQrFrame, + // PAKE passwords removed - using SAS verification instead + markAnswerCreated, + notificationIntegrationRef, + isGeneratingKeys, + setIsGeneratingKeys, + handleCreateOffer, + relayOnlyMode, + setRelayOnlyMode, + webrtcManagerRef, + showIceSettings, + setShowIceSettings, + iceServersText, + iceSettingsPersisted, + customIceServers, + handleApplyIceSettings, + handleForgetIceSettings, + // Render only the create/connect card inside the chat column + // (an additional session), instead of the full landing. + compact: showSidebar + }) + ), + // QR Scanner Modal — camera scan (design import: "Start Secure" / Camera scan modal) + showQRScannerModal && (() => { + const closeScanner = () => { + setShowQRScannerModal(false); + qrChunksBufferRef.current = { id: null, total: 0, seen: /* @__PURE__ */ new Set(), items: [] }; + }; + const buf = qrChunksBufferRef.current; + const hasParts = !!(buf && buf.id && buf.total > 1); + const framesText = hasParts ? `Scanning frames\u2026 ${buf.seen.size} / ${buf.total}` : "Scanning\u2026"; + const corner = (k, st) => React.createElement("span", { + key: k, + style: Object.assign({ position: "absolute", width: "34px", height: "34px", zIndex: 3 }, st) + }); + return React.createElement("div", { + key: "qr-scanner-modal", + onClick: closeScanner, + style: { position: "fixed", inset: 0, zIndex: 50, display: "flex", alignItems: "center", justifyContent: "center", padding: "32px", background: "rgba(6,6,8,0.82)", backdropFilter: "blur(10px)", WebkitBackdropFilter: "blur(10px)", animation: "sbUp .2s ease" } }, [ - // Header React.createElement("div", { - key: "scanner-header", - style: { display: "flex", alignItems: "center", gap: "11px", padding: "18px 20px", borderBottom: "1px solid rgba(255,255,255,0.06)" } + key: "scanner-container", + onClick: (e) => e.stopPropagation(), + style: { width: "100%", maxWidth: "420px", borderRadius: "22px", border: "1px solid rgba(255,255,255,0.1)", background: "#111113", boxShadow: "0 30px 90px rgba(0,0,0,0.6)", overflow: "hidden" } }, [ - React.createElement("span", { - key: "scanner-icon", - style: { display: "flex" }, - dangerouslySetInnerHTML: { __html: '' } - }), + // Header React.createElement("div", { - key: "scanner-titles", - style: { flex: 1, lineHeight: 1.2 } + key: "scanner-header", + style: { display: "flex", alignItems: "center", gap: "11px", padding: "18px 20px", borderBottom: "1px solid rgba(255,255,255,0.06)" } }, [ - React.createElement("div", { - key: "scanner-title", - style: { fontSize: "15.5px", fontWeight: 800, color: "#f4f4f6" } - }, "Scan QR code"), - React.createElement("div", { - key: "scanner-hint", - style: { fontSize: "12px", color: "#7b7b83" } - }, "Point your camera at their QR") - ]), - React.createElement("button", { - key: "close-btn", - onClick: closeScanner, - style: { width: "32px", height: "32px", display: "grid", placeItems: "center", borderRadius: "9px", border: "none", background: "rgba(255,255,255,0.05)", color: "#9a9aa2", cursor: "pointer" } - }, React.createElement("i", { className: "fas fa-times" })) - ]), - // Body - React.createElement("div", { - key: "scanner-body", - style: { padding: "22px 24px 24px" } - }, [ - React.createElement("div", { - key: "viewfinder", - style: { position: "relative", aspectRatio: "1", borderRadius: "18px", overflow: "hidden", background: "#000", border: "1px solid rgba(255,255,255,0.1)" } - }, [ - React.createElement("div", { - key: "vf-bg", - style: { position: "absolute", inset: 0, background: "radial-gradient(circle at 50% 45%, #1a1a1f, #000)" } - }), - // Camera video is injected here by Html5Qrcode - React.createElement("div", { - key: "qr-reader", - id: "qr-reader", - style: { position: "absolute", inset: 0, zIndex: 1 } - }), - corner("c-tl", { top: "18px", left: "18px", borderTop: "2.5px solid #3ecf8e", borderLeft: "2.5px solid #3ecf8e", borderRadius: "8px 0 0 0" }), - corner("c-tr", { top: "18px", right: "18px", borderTop: "2.5px solid #3ecf8e", borderRight: "2.5px solid #3ecf8e", borderRadius: "0 8px 0 0" }), - corner("c-bl", { bottom: "18px", left: "18px", borderBottom: "2.5px solid #3ecf8e", borderLeft: "2.5px solid #3ecf8e", borderRadius: "0 0 0 8px" }), - corner("c-br", { bottom: "18px", right: "18px", borderBottom: "2.5px solid #3ecf8e", borderRight: "2.5px solid #3ecf8e", borderRadius: "0 0 8px 0" }), React.createElement("span", { - key: "scan-line", - style: { position: "absolute", left: "18px", right: "18px", height: "2.5px", zIndex: 2, background: "linear-gradient(90deg, transparent, #3ecf8e, transparent)", boxShadow: "0 0 16px #3ecf8e", animation: "sbScan 1.5s ease-in-out infinite alternate" } + key: "scanner-icon", + style: { display: "flex" }, + dangerouslySetInnerHTML: { __html: '' } }), React.createElement("div", { - key: "scan-status", - style: { position: "absolute", bottom: 0, left: 0, right: 0, zIndex: 3, display: "flex", alignItems: "center", justifyContent: "center", gap: "8px", padding: "14px", background: "linear-gradient(transparent, rgba(0,0,0,0.6))" } + key: "scanner-titles", + style: { flex: 1, lineHeight: 1.2 } }, [ - React.createElement("span", { - key: "spinner", - style: { display: "flex", animation: "sbSpin 1.4s linear infinite" }, - dangerouslySetInnerHTML: { __html: '' } - }), - React.createElement("span", { - key: "scan-frames", - style: { fontSize: "12.5px", fontWeight: 600, color: "#cfcfd4" } - }, framesText) - ]) + React.createElement("div", { + key: "scanner-title", + style: { fontSize: "15.5px", fontWeight: 800, color: "#f4f4f6" } + }, "Scan QR code"), + React.createElement("div", { + key: "scanner-hint", + style: { fontSize: "12px", color: "#7b7b83" } + }, "Point your camera at their QR") + ]), + React.createElement("button", { + key: "close-btn", + onClick: closeScanner, + style: { width: "32px", height: "32px", display: "grid", placeItems: "center", borderRadius: "9px", border: "none", background: "rgba(255,255,255,0.05)", color: "#9a9aa2", cursor: "pointer" } + }, React.createElement("i", { className: "fas fa-times" })) ]), - React.createElement("p", { - key: "scanner-note", - style: { margin: "16px 0 0", textAlign: "center", fontSize: "12px", lineHeight: 1.5, color: "#6b6b73" } - }, "Hold steady until all parts are captured. Camera access is local \u2014 nothing is uploaded.") + // Body + React.createElement("div", { + key: "scanner-body", + style: { padding: "22px 24px 24px" } + }, [ + React.createElement("div", { + key: "viewfinder", + style: { position: "relative", aspectRatio: "1", borderRadius: "18px", overflow: "hidden", background: "#000", border: "1px solid rgba(255,255,255,0.1)" } + }, [ + React.createElement("div", { + key: "vf-bg", + style: { position: "absolute", inset: 0, background: "radial-gradient(circle at 50% 45%, #1a1a1f, #000)" } + }), + // Camera video is injected here by Html5Qrcode + React.createElement("div", { + key: "qr-reader", + id: "qr-reader", + style: { position: "absolute", inset: 0, zIndex: 1 } + }), + corner("c-tl", { top: "18px", left: "18px", borderTop: "2.5px solid #3ecf8e", borderLeft: "2.5px solid #3ecf8e", borderRadius: "8px 0 0 0" }), + corner("c-tr", { top: "18px", right: "18px", borderTop: "2.5px solid #3ecf8e", borderRight: "2.5px solid #3ecf8e", borderRadius: "0 8px 0 0" }), + corner("c-bl", { bottom: "18px", left: "18px", borderBottom: "2.5px solid #3ecf8e", borderLeft: "2.5px solid #3ecf8e", borderRadius: "0 0 0 8px" }), + corner("c-br", { bottom: "18px", right: "18px", borderBottom: "2.5px solid #3ecf8e", borderRight: "2.5px solid #3ecf8e", borderRadius: "0 0 8px 0" }), + React.createElement("span", { + key: "scan-line", + style: { position: "absolute", left: "18px", right: "18px", height: "2.5px", zIndex: 2, background: "linear-gradient(90deg, transparent, #3ecf8e, transparent)", boxShadow: "0 0 16px #3ecf8e", animation: "sbScan 1.5s ease-in-out infinite alternate" } + }), + React.createElement("div", { + key: "scan-status", + style: { position: "absolute", bottom: 0, left: 0, right: 0, zIndex: 3, display: "flex", alignItems: "center", justifyContent: "center", gap: "8px", padding: "14px", background: "linear-gradient(transparent, rgba(0,0,0,0.6))" } + }, [ + React.createElement("span", { + key: "spinner", + style: { display: "flex", animation: "sbSpin 1.4s linear infinite" }, + dangerouslySetInnerHTML: { __html: '' } + }), + React.createElement("span", { + key: "scan-frames", + style: { fontSize: "12.5px", fontWeight: 600, color: "#cfcfd4" } + }, framesText) + ]) + ]), + React.createElement("p", { + key: "scanner-note", + style: { margin: "16px 0 0", textAlign: "center", fontSize: "12px", lineHeight: 1.5, color: "#6b6b73" } + }, "Hold steady until all parts are captured. Camera access is local \u2014 nothing is uploaded.") + ]) ]) - ]) - ]); - })() + ]); + })() + ]) + // end app-column ]); }; var UpdateCheckerWrapper = ({ children }) => { diff --git a/dist/app.js.map b/dist/app.js.map index ba073d0..f6404d3 100644 --- a/dist/app.js.map +++ b/dist/app.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["../src/utils/debugWindowHooks.js", "../src/network/iceSettingsStore.js", "../src/app.jsx"], - "sourcesContent": ["function isSecureBitDebugEnabled(targetWindow = globalThis.window) {\n return targetWindow?.SECUREBIT_DEBUG === true;\n}\n\nfunction installDebugWindowHooks({\n targetWindow = globalThis.window,\n webrtcManagerRef,\n onClearData,\n clearConsole = () => {\n if (typeof console.clear === 'function') {\n console.clear();\n }\n }\n}) {\n if (!isSecureBitDebugEnabled(targetWindow)) {\n return () => {};\n }\n\n targetWindow.forceCleanup = () => {\n onClearData();\n if (webrtcManagerRef.current) {\n webrtcManagerRef.current.disconnect();\n }\n };\n targetWindow.clearLogs = clearConsole;\n targetWindow.webrtcManagerRef = webrtcManagerRef;\n\n return () => {\n delete targetWindow.forceCleanup;\n delete targetWindow.clearLogs;\n delete targetWindow.webrtcManagerRef;\n };\n}\n\nexport { installDebugWindowHooks, isSecureBitDebugEnabled };\n", "// Persistent, at-rest-encrypted storage for the user's custom ICE (STUN/TURN)\n// configuration. Persistence is OPTIONAL: the UI only calls saveIceSettings when\n// the user explicitly opts in (\"Save on this device\"). Session-only use never\n// touches this store \u2014 the settings live in React state and vanish on reload.\n//\n// At-rest protection model:\n// - A non-extractable AES-GCM device key is generated once and kept in\n// IndexedDB. It can never be exported back into JS, so a copy of the\n// on-disk database is useless without executing code in this exact origin.\n// - The settings blob (which may contain TURN credentials) is encrypted with\n// that key before being written.\n// This protects against disk/profile inspection. It does NOT protect against a\n// live code-execution compromise of the page (consistent with the project's\n// stated threat model \u2014 see SECURITY.md). Credentials are never persisted in\n// plaintext, and the user can delete them at any time via clearIceSettings().\n\nconst DB_NAME = 'securebit-net';\nconst DB_VERSION = 1;\nconst STORE = 'kv';\nconst KEY_RECORD = 'ice-device-key';\nconst SETTINGS_RECORD = 'ice-settings';\nconst SETTINGS_VERSION = 1;\n\nfunction isSupported() {\n return typeof indexedDB !== 'undefined' &&\n typeof crypto !== 'undefined' &&\n !!crypto.subtle;\n}\n\nfunction openDb() {\n return new Promise((resolve, reject) => {\n let request;\n try {\n request = indexedDB.open(DB_NAME, DB_VERSION);\n } catch (error) {\n reject(error);\n return;\n }\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(STORE)) {\n db.createObjectStore(STORE);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction idbGet(db, key) {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readonly');\n const req = tx.objectStore(STORE).get(key);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\nfunction idbPut(db, key, value) {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).put(value, key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n\nfunction idbDelete(db, key) {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).delete(key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n\nasync function getOrCreateDeviceKey(db) {\n const existing = await idbGet(db, KEY_RECORD);\n if (existing instanceof CryptoKey) {\n return existing;\n }\n const key = await crypto.subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n false, // non-extractable\n ['encrypt', 'decrypt']\n );\n await idbPut(db, KEY_RECORD, key);\n return key;\n}\n\n/**\n * Persist custom ICE settings, encrypted at rest.\n * @param {{ servers: Array, privacyMode: string }} settings\n */\nexport async function saveIceSettings(settings) {\n if (!isSupported()) throw new Error('Persistent storage is not available in this browser');\n\n const db = await openDb();\n const key = await getOrCreateDeviceKey(db);\n\n const payload = JSON.stringify({\n version: SETTINGS_VERSION,\n servers: Array.isArray(settings?.servers) ? settings.servers : [],\n privacyMode: settings?.privacyMode === 'relay-only' ? 'relay-only' : 'standard'\n });\n\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const ciphertext = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n key,\n new TextEncoder().encode(payload)\n );\n\n await idbPut(db, SETTINGS_RECORD, {\n iv: Array.from(iv),\n data: Array.from(new Uint8Array(ciphertext))\n });\n}\n\n/**\n * Load and decrypt previously saved ICE settings.\n * Fails closed: returns null if absent, unsupported, or undecryptable.\n * @returns {Promise<{ servers: Array, privacyMode: string }|null>}\n */\nexport async function loadIceSettings() {\n if (!isSupported()) return null;\n\n try {\n const db = await openDb();\n const record = await idbGet(db, SETTINGS_RECORD);\n if (!record || !Array.isArray(record.iv) || !Array.isArray(record.data)) {\n return null;\n }\n const key = await idbGet(db, KEY_RECORD);\n if (!(key instanceof CryptoKey)) return null;\n\n const plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: new Uint8Array(record.iv) },\n key,\n new Uint8Array(record.data)\n );\n const parsed = JSON.parse(new TextDecoder().decode(plaintext));\n return {\n servers: Array.isArray(parsed.servers) ? parsed.servers : [],\n privacyMode: parsed.privacyMode === 'relay-only' ? 'relay-only' : 'standard'\n };\n } catch {\n // Corrupted or tampered record: fail closed.\n return null;\n }\n}\n\n/** Delete any persisted ICE settings (the device key is left in place). */\nexport async function clearIceSettings() {\n if (!isSupported()) return;\n try {\n const db = await openDb();\n await idbDelete(db, SETTINGS_RECORD);\n } catch {\n // Best-effort deletion; nothing to surface to the user.\n }\n}\n\nexport async function hasSavedIceSettings() {\n if (!isSupported()) return false;\n try {\n const db = await openDb();\n const record = await idbGet(db, SETTINGS_RECORD);\n return !!record;\n } catch {\n return false;\n }\n}\n", "import { installDebugWindowHooks } from './utils/debugWindowHooks.js';\nimport { loadIceSettings, saveIceSettings, clearIceSettings } from './network/iceSettingsStore.js';\n\n // \u2500\u2500 Secure chat extras: code blocks, clipboard hygiene \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Copy text to the clipboard and (optionally) wipe it after a delay so\n // copied secrets (keys, commands, codes) don't linger. We only clear\n // when we can confirm the clipboard still holds exactly what we wrote,\n // or when the clipboard can't be read back at all \u2014 never clobbering\n // something the user copied afterwards that we can see is different.\n const copyToClipboardSecure = async (text, autoClearMs = 0) => {\n let ok = false;\n try {\n await navigator.clipboard.writeText(text);\n ok = true;\n } catch (e) {\n try {\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n ok = document.execCommand('copy');\n document.body.removeChild(ta);\n } catch (_) { ok = false; }\n }\n if (ok && autoClearMs > 0 && navigator.clipboard && navigator.clipboard.writeText) {\n setTimeout(async () => {\n let current = null;\n let readable = true;\n try { current = await navigator.clipboard.readText(); }\n catch (_) { readable = false; }\n if (!readable || current === text) {\n try { await navigator.clipboard.writeText(''); } catch (_) {}\n }\n }, autoClearMs);\n }\n return ok;\n };\n\n // Split a message into plain-text and fenced ``` code segments.\n // Returns null when there is no fenced code so callers can fast-path.\n const parseMessageSegments = (text) => {\n if (typeof text !== 'string' || text.indexOf('```') === -1) return null;\n const segments = [];\n const re = /```([a-zA-Z0-9_+#.-]*)\\n?([\\s\\S]*?)```/g;\n let last = 0;\n let m;\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) segments.push({ kind: 'text', content: text.slice(last, m.index) });\n segments.push({ kind: 'code', lang: (m[1] || '').toLowerCase(), content: m[2].replace(/\\n$/, '') });\n last = re.lastIndex;\n }\n if (last < text.length) segments.push({ kind: 'text', content: text.slice(last) });\n return segments.some(s => s.kind === 'code') ? segments : null;\n };\n\n // Lightweight, dependency-free syntax highlighter. Returns React nodes\n // (no innerHTML / dangerouslySetInnerHTML) so it stays CSP-safe and adds\n // no XSS surface. Language-agnostic: highlights comments, strings,\n // numbers, common keywords and literals \u2014 good enough for snippets\n // without shipping a heavy library or allowing remote scripts.\n const HL_KEYWORDS = new Set([\n 'const','let','var','function','return','if','else','for','while','do','switch',\n 'case','break','continue','class','extends','new','this','super','import','export',\n 'from','as','default','async','await','try','catch','finally','throw','typeof',\n 'instanceof','delete','yield','in','of','def','elif','lambda','pass','with','global',\n 'public','private','protected','static','final','void','int','long','float','double',\n 'char','bool','boolean','string','struct','enum','interface','package','func','fn',\n 'type','where','select','update','insert','delete','where','and','or','not','end',\n 'then','fi','done','echo','use','mut','impl','trait','match','module','require'\n ]);\n const HL_LITERALS = new Set(['true','false','null','undefined','None','True','False','nil','NaN','Infinity']);\n const highlightCode = (code) => {\n const re = /(\\/\\/[^\\n]*|#[^\\n]*|\\/\\*[\\s\\S]*?\\*\\/|--[^\\n]*)|(`(?:\\\\.|[^`\\\\])*`|\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*')|(\\b\\d[\\d_.]*(?:[eE][+-]?\\d+)?\\b|\\b0[xX][0-9a-fA-F]+\\b)|([A-Za-z_$][A-Za-z0-9_$]*)/g;\n const nodes = [];\n let buffer = '';\n let last = 0;\n let key = 0;\n const flush = () => { if (buffer) { nodes.push(buffer); buffer = ''; } };\n let m;\n while ((m = re.exec(code)) !== null) {\n if (m.index > last) buffer += code.slice(last, m.index);\n last = re.lastIndex;\n let cls = null;\n if (m[1]) cls = 'text-gray-500 italic'; // comment\n else if (m[2]) cls = 'text-amber-300'; // string\n else if (m[3]) cls = 'text-sky-300'; // number\n else if (m[4]) { // identifier\n if (HL_KEYWORDS.has(m[4])) cls = 'text-purple-300';\n else if (HL_LITERALS.has(m[4])) cls = 'text-sky-300';\n }\n if (cls) {\n flush();\n nodes.push(React.createElement('span', { key: `h${key++}`, className: cls }, m[0]));\n } else {\n buffer += m[0];\n }\n }\n if (last < code.length) buffer += code.slice(last);\n flush();\n return nodes;\n };\n\n // Monospace code window with a copy button (clipboard auto-clears in 30s).\n const CodeBlock = ({ code, lang }) => {\n const [copied, setCopied] = React.useState(false);\n const handleCopy = async () => {\n const ok = await copyToClipboardSecure(code, 30000);\n if (ok) {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n return React.createElement('div', {\n className: \"my-1 rounded-lg overflow-hidden\",\n style: { backgroundColor: '#1b1c1b', border: '0 solid #e5e7eb' }\n }, [\n React.createElement('div', {\n key: 'hdr',\n className: \"flex items-center justify-between px-3 py-1.5\",\n style: { backgroundColor: '#222322', border: '0 solid #e5e7eb' }\n }, [\n React.createElement('span', {\n key: 'lang',\n className: \"text-[11px] uppercase tracking-wide text-gray-500 font-mono\"\n }, lang || 'code'),\n React.createElement('button', {\n key: 'copy',\n onClick: handleCopy,\n title: \"Copy \u2014 clipboard auto-clears in 30s\",\n className: \"flex items-center text-[11px] text-gray-400 hover:text-green-400 transition-colors\"\n }, [\n React.createElement('i', {\n key: 'ic',\n className: `${copied ? 'fas fa-check text-green-400' : 'far fa-copy'} mr-1`\n }),\n copied ? 'Copied' : 'Copy'\n ])\n ]),\n React.createElement('pre', {\n key: 'pre',\n className: \"px-3 py-2 overflow-x-auto text-xs leading-relaxed text-gray-200 custom-scrollbar\",\n style: { whiteSpace: 'pre', fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', margin: 0 }\n }, React.createElement('code', null, highlightCode(code)))\n ]);\n };\n\n // Render a message body: code windows for fenced blocks, line-preserving\n // text otherwise. Built from already-sanitized text via React nodes only.\n const MessageBody = ({ text }) => {\n const segments = parseMessageSegments(text);\n if (!segments) {\n return React.createElement('div', {\n className: \"text-sm break-words\",\n style: { whiteSpace: 'pre-wrap', wordBreak: 'break-word' }\n }, text);\n }\n return React.createElement('div', { className: \"text-sm\" },\n segments.map((seg, i) => seg.kind === 'code'\n ? React.createElement(CodeBlock, { key: i, code: seg.content, lang: seg.lang })\n : (seg.content.trim()\n ? React.createElement('div', {\n key: i,\n className: \"break-words\",\n style: { whiteSpace: 'pre-wrap', wordBreak: 'break-word' }\n }, seg.content)\n : null)\n )\n );\n };\n\n // Composer toolbar: code / view-once / disappearing.\n // Borderless icon+label buttons; active state uses the brand orange\n // (accent-orange). View-once and Timer open a small time picker.\n const ChatToolbar = ({ codeMode, setCodeMode, viewOnceMode, setViewOnceMode, viewOnceTtl, setViewOnceTtl, disappearTtl, setDisappearTtl }) => {\n const [openMenu, setOpenMenu] = React.useState(null); // 'once' | 'timer' | null\n const fmt = (s) => s >= 3600 ? `${Math.round(s / 3600)}h` : (s >= 60 ? `${Math.round(s / 60)}m` : `${s}s`);\n\n const btnClass = (active) =>\n `inline-flex items-center gap-2 h-9 px-3 rounded-lg text-xs font-medium transition-colors duration-150 ${active\n ? 'accent-orange bg-orange-500/10'\n : 'text-gray-400 hover:text-gray-200 hover:bg-gray-700/40'}`;\n\n const pickerItem = (opt, current, onPick) =>\n React.createElement('button', {\n key: String(opt.value),\n type: 'button',\n onClick: () => { onPick(opt.value); setOpenMenu(null); },\n // Comfortable tap target + readable size on mobile.\n className: `w-full text-left px-4 py-3 sm:py-2.5 text-sm flex items-center gap-3 transition-colors ${current === opt.value\n ? 'accent-orange bg-orange-500/10'\n : 'text-gray-200 hover:bg-gray-700/50 active:bg-gray-700/60'}`\n }, [\n React.createElement('i', { key: 'i', className: `${opt.icon || 'far fa-clock'} w-4 text-center` }),\n React.createElement('span', { key: 'l' }, opt.label)\n ]);\n\n // Opens UPWARD (bottom:100%) via inline style so it never depends on a\n // purgeable utility class and never pushes the composer layout down.\n const picker = (options, current, onPick) =>\n React.createElement('div', {\n className: \"absolute right-0 z-50 min-w-[180px] max-w-[78vw] rounded-xl shadow-2xl overflow-hidden\",\n style: { backgroundColor: '#1f201f', border: '0 solid #e5e7eb', bottom: '100%', marginBottom: '8px' }\n }, options.map(opt => pickerItem(opt, current, onPick)));\n\n const labelBtn = (key, { active, icon, label, title, onClick }) =>\n React.createElement('button', {\n key, type: 'button', onClick, title, 'aria-pressed': !!active,\n className: btnClass(active)\n }, [\n React.createElement('i', { key: 'i', className: `${icon} text-[13px]` }),\n React.createElement('span', { key: 'l', className: 'leading-none' }, label)\n ]);\n\n return React.createElement('div', { className: \"flex items-center gap-1\" }, [\n // Invisible backdrop closes any open picker on outside click.\n openMenu && React.createElement('div', {\n key: 'backdrop',\n className: \"fixed inset-0 z-40\",\n onClick: () => setOpenMenu(null)\n }),\n\n // Code \u2014 toggles code mode (expands the input box).\n labelBtn('code', {\n active: codeMode,\n icon: 'fas fa-code',\n label: 'Code',\n title: 'Send as a code block (expands the input)',\n onClick: () => setCodeMode(v => !v)\n }),\n\n // View once \u2014 pick how long it stays after the peer opens it.\n React.createElement('div', { key: 'once', className: 'relative' }, [\n labelBtn('once-btn', {\n active: viewOnceMode,\n icon: 'fas fa-eye-slash',\n label: viewOnceMode ? `Once \u00B7 ${fmt(viewOnceTtl)}` : 'View once',\n title: 'View once \u2014 vanishes after the peer reads it',\n onClick: () => setOpenMenu(openMenu === 'once' ? null : 'once')\n }),\n openMenu === 'once' && picker([\n { value: 0, label: 'Off', icon: 'fas fa-ban' },\n { value: 5, label: '5s after reading' },\n { value: 15, label: '15s after reading' },\n { value: 30, label: '30s after reading' },\n { value: 60, label: '1m after reading' }\n ], viewOnceMode ? viewOnceTtl : 0, (v) => {\n if (v === 0) { setViewOnceMode(false); }\n else { setViewOnceTtl(v); setViewOnceMode(true); }\n })\n ]),\n\n // Timer \u2014 pick the disappearing duration.\n React.createElement('div', { key: 'timer', className: 'relative' }, [\n labelBtn('timer-btn', {\n active: disappearTtl > 0,\n icon: 'fas fa-stopwatch',\n label: disappearTtl > 0 ? `Timer \u00B7 ${fmt(disappearTtl)}` : 'Timer',\n title: 'Disappearing message \u2014 deletes on both sides',\n onClick: () => setOpenMenu(openMenu === 'timer' ? null : 'timer')\n }),\n openMenu === 'timer' && picker([\n { value: 0, label: 'Off', icon: 'fas fa-ban' },\n { value: 30, label: '30 seconds' },\n { value: 300, label: '5 minutes' },\n { value: 3600, label: '1 hour' }\n ], disappearTtl, (v) => setDisappearTtl(v))\n ])\n ]);\n };\n\n // Enhanced Copy Button with better UX\n const EnhancedCopyButton = ({ text, className = \"\", children }) => {\n const [copied, setCopied] = React.useState(false);\n \n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (error) {\n console.error('Copy failed:', error);\n // Fallback for older browsers\n const textArea = document.createElement('textarea');\n textArea.value = text;\n document.body.appendChild(textArea);\n textArea.select();\n document.execCommand('copy');\n document.body.removeChild(textArea);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n \n return React.createElement('button', {\n onClick: handleCopy,\n className: `${className} transition-all duration-200`\n }, [\n React.createElement('i', {\n key: 'icon',\n className: `${copied ? 'fas fa-check accent-green' : 'fas fa-copy text-secondary'} mr-2`\n }),\n copied ? 'Copied!' : children\n ]);\n };\n \n // Verification Component\n const VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed, remoteConfirmed, bothConfirmed }) => {\n const [sasInput, setSasInput] = React.useState('');\n const [error, setError] = React.useState('');\n const normalizedExpectedLength = (verificationCode || '').replace(/[-\\s]/g, '').length;\n const normalizedInputLength = sasInput.replace(/[-\\s]/g, '').length;\n const canConfirm = !localConfirmed && normalizedExpectedLength > 0 && normalizedInputLength === normalizedExpectedLength;\n\n React.useEffect(() => {\n setSasInput('');\n setError('');\n }, [verificationCode]);\n\n const handleConfirm = async () => {\n try {\n setError('');\n await onConfirm(sasInput);\n } catch (confirmationError) {\n setSasInput('');\n if (confirmationError?.message === 'SAS_MAX_ATTEMPTS') {\n setError('Too many incorrect attempts. Session reset for safety.');\n } else {\n setError('Incorrect code. Check it with your peer and try again.');\n }\n }\n };\n\n return React.createElement('div', {\n className: \"card-minimal rounded-xl p-6 border-purple-500/20\"\n }, [\n React.createElement('div', {\n key: 'header',\n className: \"flex items-center mb-4\"\n }, [\n React.createElement('div', {\n key: 'icon',\n className: \"w-10 h-10 bg-purple-500/10 border border-purple-500/20 rounded-lg flex items-center justify-center mr-3\"\n }, [\n React.createElement('i', {\n className: 'fas fa-shield-alt accent-purple'\n })\n ]),\n React.createElement('h3', {\n key: 'title',\n className: \"text-lg font-medium text-primary\"\n }, \"Security verification\")\n ]),\n React.createElement('div', {\n key: 'content',\n className: \"space-y-4\"\n }, [\n React.createElement('p', {\n key: 'description',\n className: \"text-secondary text-sm\"\n }, \"Compare this code with your peer out-of-band, then type the same code below to unlock the chat.\"),\n React.createElement('div', {\n key: 'code-display',\n className: \"text-center\"\n }, [\n React.createElement('div', {\n key: 'code',\n className: \"verification-code text-2xl py-4\"\n }, verificationCode)\n ]),\n React.createElement('div', {\n key: 'sas-input-wrap',\n className: \"space-y-2\"\n }, [\n React.createElement('label', {\n key: 'sas-label',\n className: \"block text-sm text-secondary\"\n }, \"Enter the verified code\"),\n React.createElement('input', {\n key: 'sas-input',\n type: 'text',\n value: sasInput,\n onChange: (event) => {\n setSasInput(event.target.value.toUpperCase());\n if (error) setError('');\n },\n autoFocus: true,\n autoComplete: 'off',\n spellCheck: false,\n inputMode: 'text',\n disabled: localConfirmed,\n placeholder: verificationCode ? 'Type code here' : 'Waiting for code\u2026',\n className: \"w-full rounded-lg border border-purple-500/30 bg-black/20 px-4 py-3 text-center text-xl tracking-[0.3em] text-primary uppercase focus:border-purple-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60\",\n style: { fontFamily: 'monospace', textTransform: 'uppercase' }\n }),\n error && React.createElement('p', {\n key: 'sas-error',\n className: \"text-sm text-red-400\"\n }, error)\n ]),\n // Verification status indicators\n React.createElement('div', {\n key: 'verification-status',\n className: \"space-y-2\"\n }, [\n React.createElement('div', {\n key: 'local-status',\n className: `flex items-center justify-between p-2 rounded-lg ${localConfirmed ? 'bg-green-500/10 border border-green-500/20' : 'bg-gray-500/10 border border-gray-500/20'}`\n }, [\n React.createElement('span', {\n key: 'local-label',\n className: \"text-sm text-secondary\"\n }, \"Your confirmation:\"),\n React.createElement('div', {\n key: 'local-indicator',\n className: \"flex items-center\"\n }, [\n React.createElement('i', {\n key: 'local-icon',\n className: `fas ${localConfirmed ? 'fa-check-circle text-green-400' : 'fa-clock text-gray-400'} mr-2`\n }),\n React.createElement('span', {\n key: 'local-text',\n className: `text-sm ${localConfirmed ? 'text-green-400' : 'text-gray-400'}`\n }, localConfirmed ? 'Confirmed' : 'Pending')\n ])\n ]),\n React.createElement('div', {\n key: 'remote-status',\n className: `flex items-center justify-between p-2 rounded-lg ${remoteConfirmed ? 'bg-green-500/10 border border-green-500/20' : 'bg-gray-500/10 border border-gray-500/20'}`\n }, [\n React.createElement('span', {\n key: 'remote-label',\n className: \"text-sm text-secondary\"\n }, \"Peer confirmation:\"),\n React.createElement('div', {\n key: 'remote-indicator',\n className: \"flex items-center\"\n }, [\n React.createElement('i', {\n key: 'remote-icon',\n className: `fas ${remoteConfirmed ? 'fa-check-circle text-green-400' : 'fa-clock text-gray-400'} mr-2`\n }),\n React.createElement('span', {\n key: 'remote-text',\n className: `text-sm ${remoteConfirmed ? 'text-green-400' : 'text-gray-400'}`\n }, remoteConfirmed ? 'Confirmed' : 'Pending')\n ])\n ])\n ]),\n React.createElement('div', {\n key: 'warning',\n className: \"p-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg\"\n }, [\n React.createElement('p', {\n className: \"text-yellow-400 text-sm flex items-center\"\n }, [\n React.createElement('i', {\n className: 'fas fa-exclamation-triangle mr-2'\n }),\n 'Make sure the codes match exactly.!'\n ])\n ]),\n React.createElement('div', {\n key: 'buttons',\n className: \"flex space-x-3\"\n }, [\n React.createElement('button', {\n key: 'confirm',\n onClick: handleConfirm,\n disabled: !canConfirm,\n className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${!canConfirm ? 'bg-gray-500/20 text-gray-400 cursor-not-allowed' : 'btn-verify text-white'}`\n }, [\n React.createElement('i', {\n className: `fas ${localConfirmed ? 'fa-check-circle' : 'fa-check'} mr-2`\n }),\n localConfirmed ? 'Confirmed' : 'Confirm code'\n ]),\n React.createElement('button', {\n key: 'reject',\n onClick: onReject,\n className: \"flex-1 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 py-3 px-4 rounded-lg font-medium transition-all duration-200\"\n }, [\n React.createElement('i', {\n className: 'fas fa-times mr-2'\n }),\n 'The codes do not match'\n ])\n ])\n ])\n ]);\n };\n \n // Grain overlay for the view-once cover (Telegram-style blur + noise).\n const GRAIN_URL = `url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='100'%20height='100'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.9'%20numOctaves='2'%20stitchTiles='stitch'/%3E%3C/filter%3E%3Crect%20width='100%25'%20height='100%25'%20filter='url(%23n)'/%3E%3C/svg%3E\")`;\n const SB_MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n\n // Enhanced Chat Message \u2014 redesigned bubbles (SecureBit Chat design).\n const EnhancedChatMessage = ({ message, type, timestamp, mid, status, viewOnce, viewOnceTtl, expiresAt, expired, nowTick, canUnsend, onUnsend, onExpire }) => {\n const [revealed, setRevealed] = React.useState(false);\n const revealTimerRef = React.useRef(null);\n\n const formatTime = (ts) => new Date(ts).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' });\n\n React.useEffect(() => () => { if (revealTimerRef.current) clearTimeout(revealTimerRef.current); }, []);\n\n // System / notice messages: centered subtle pill. \"notice\" is shown\n // in the message flow (e.g. \"connection restored\"); \"system\" lives in\n // the handshake log. Notices use a green accent.\n if (type === 'system' || type === 'notice') {\n const isNotice = type === 'notice';\n return React.createElement('div', { className: 'message-slide', style: { display: 'flex', justifyContent: 'center', margin: '4px 0' } },\n React.createElement('div', {\n style: { maxWidth: '80%', padding: '8px 14px', borderRadius: '10px', border: '1px solid ' + (isNotice ? 'rgba(62,207,142,0.25)' : 'rgba(240,137,42,0.22)'), background: isNotice ? 'rgba(62,207,142,0.08)' : 'rgba(240,137,42,0.08)', color: isNotice ? '#8fe0bb' : '#e8b27a', fontSize: '12.5px', textAlign: 'center', lineHeight: 1.5 }\n }, message)\n );\n }\n\n const isMe = type === 'sent';\n const encrypted = isMe;\n const isViewOnce = type === 'received' && viewOnce === true;\n\n const remaining = (typeof expiresAt === 'number')\n ? Math.max(0, Math.ceil((expiresAt - (nowTick || Date.now())) / 1000))\n : null;\n const fmtRemaining = (sec) => {\n if (sec == null) return '';\n const h = Math.floor(sec / 3600), m = Math.floor((sec % 3600) / 60), s = sec % 60;\n const pad = (n) => String(n).padStart(2, '0');\n return h > 0 ? (h + ':' + pad(m) + ':' + pad(s)) : (m + ':' + pad(s));\n };\n\n const handleReveal = () => {\n if (revealed) return;\n setRevealed(true);\n const ms = Math.max(1, (typeof viewOnceTtl === 'number' ? viewOnceTtl : 15)) * 1000;\n revealTimerRef.current = setTimeout(() => { onExpire && onExpire(); }, ms);\n };\n\n const radius = isMe ? '14px 14px 4px 14px' : '14px 14px 14px 4px';\n const border = isMe ? '1px solid rgba(255,255,255,0.10)' : '1px solid rgba(255,255,255,0.06)';\n const bg = isMe ? '#26262b' : '#161618';\n\n // Expired tombstone (disappearing / view-once) \u2014 content already wiped.\n const isExpired = expired === true || (typeof expiresAt === 'number' && (nowTick || Date.now()) >= expiresAt);\n if (isExpired) {\n return React.createElement('div', {\n className: 'message-slide',\n style: { display: 'flex', width: '100%', justifyContent: isMe ? 'flex-end' : 'flex-start' }\n }, React.createElement('div', { style: { maxWidth: '74%', minWidth: '170px' } },\n React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '12px 15px', borderRadius: radius, border: '1px dashed rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.018)' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-clock', style: { color: '#6b6b73', fontSize: '13px' } }),\n React.createElement('span', { key: 't', style: { fontSize: '13px', color: '#6b6b73', fontStyle: 'italic' } }, 'This message has expired')\n ])\n ));\n }\n\n let body;\n if (isViewOnce && !revealed) {\n body = React.createElement('div', {\n key: 'cover',\n onClick: handleReveal,\n style: { position: 'relative', cursor: 'pointer', padding: '12px 15px 10px', overflow: 'hidden' }\n }, [\n React.createElement('div', { key: 'blur', style: { fontSize: '14.5px', lineHeight: 1.55, color: '#b3b3ba', filter: 'blur(7px)', userSelect: 'none', pointerEvents: 'none', wordBreak: 'break-word', minHeight: '22px' } }, message),\n React.createElement('div', { key: 'grain', style: { position: 'absolute', inset: 0, backgroundImage: GRAIN_URL, backgroundSize: '90px', opacity: 0.18, mixBlendMode: 'screen', pointerEvents: 'none' } }),\n React.createElement('div', { key: 'lbl', style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '7px', pointerEvents: 'none' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-eye-slash', style: { color: '#e8e8eb', fontSize: '13px' } }),\n React.createElement('span', { key: 't', style: { fontSize: '12px', fontWeight: 600, color: '#e8e8eb', textShadow: '0 1px 5px rgba(0,0,0,0.75)' } }, 'View once \u00B7 tap to reveal')\n ])\n ]);\n } else {\n body = React.createElement('div', { key: 'body', style: { padding: '12px 15px 10px', color: '#e9e9ec' } },\n React.createElement(MessageBody, { text: message })\n );\n }\n\n const metaLeft = [\n React.createElement('span', { key: 'time', style: { fontFamily: SB_MONO, fontSize: '10.5px', color: '#6b6b73' } }, formatTime(timestamp))\n ];\n // WhatsApp-style delivery state on our own messages.\n if (isMe) {\n const stCfg = ({\n sending: { icon: 'fa-clock', color: '#6b6b73', label: 'Sending' },\n sent: { icon: 'fa-check', color: '#8a8a92', label: 'Sent' },\n delivered: { icon: 'fa-check-double', color: '#3ecf8e', label: 'Delivered' },\n failed: { icon: 'fa-triangle-exclamation', color: '#e5727a', label: 'Not sent' }\n })[status || 'sent'] || { icon: 'fa-check', color: '#8a8a92', label: 'Sent' };\n metaLeft.push(React.createElement('span', {\n key: 'dlv', title: stCfg.label,\n style: { display: 'inline-flex', alignItems: 'center', color: stCfg.color }\n }, React.createElement('i', { className: 'fas ' + stCfg.icon, style: { fontSize: '10.5px' } })));\n }\n if (isViewOnce && revealed) {\n metaLeft.push(React.createElement('span', { key: 'vo', style: { display: 'inline-flex', alignItems: 'center', gap: '4px', fontSize: '10px', fontWeight: 600, color: '#8a8a92' } }, [\n React.createElement('span', { key: 'd', style: { width: '4px', height: '4px', borderRadius: '50%', background: '#8a8a92' } }),\n 'Viewed once'\n ]));\n } else if (remaining !== null) {\n metaLeft.push(React.createElement('span', { key: 'ttl', style: { display: 'inline-flex', alignItems: 'center', gap: '4px', fontFamily: SB_MONO, fontSize: '10.5px', fontWeight: 500, color: '#f0892a' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-clock', style: { fontSize: '10px' } }),\n fmtRemaining(remaining)\n ]));\n }\n\n const metaRight = [];\n metaRight.push(React.createElement('span', { key: 'status', style: { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '10.5px', fontWeight: 600, color: '#3ecf8e', flex: 'none' } }, [\n React.createElement('i', { key: 'i', className: encrypted ? 'fas fa-lock' : 'fas fa-lock-open', style: { fontSize: '10px' } }),\n encrypted ? 'Encrypted' : 'Decrypted'\n ]));\n // Delete-for-everyone sits AFTER the Encrypted/Decrypted status.\n if (canUnsend && isMe && mid) {\n metaRight.push(React.createElement('button', {\n key: 'unsend', onClick: () => onUnsend && onUnsend(mid), title: 'Delete for everyone',\n className: 'sb-unsend',\n style: { background: 'none', border: 'none', cursor: 'pointer', color: '#56565e', fontSize: '11px', padding: 0, lineHeight: 1 }\n }, React.createElement('i', { className: 'fas fa-trash-can' })));\n }\n\n const meta = React.createElement('div', {\n key: 'meta',\n style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '14px', padding: '0 15px 10px' }\n }, [\n React.createElement('div', { key: 'l', style: { display: 'flex', alignItems: 'center', gap: '9px', minWidth: 0 } }, metaLeft),\n React.createElement('div', { key: 'r', style: { display: 'flex', alignItems: 'center', gap: '9px', flex: 'none' } }, metaRight)\n ]);\n\n return React.createElement('div', {\n className: 'message-slide',\n style: { display: 'flex', width: '100%', justifyContent: isMe ? 'flex-end' : 'flex-start' }\n }, [\n React.createElement('div', { key: 'wrap', style: { maxWidth: '74%', minWidth: '170px' } },\n React.createElement('div', { style: { borderRadius: radius, border: border, background: bg, overflow: 'hidden' } }, [body, meta])\n )\n ]);\n };\n \n // Enhanced Connection Setup with verification\n const EnhancedConnectionSetup = ({\n messages, \n onCreateOffer, \n onCreateAnswer, \n onConnect, \n onClearData,\n onVerifyConnection,\n connectionStatus,\n offerData,\n answerData,\n offerInput,\n setOfferInput,\n answerInput,\n setAnswerInput,\n showOfferStep,\n showAnswerStep,\n verificationCode,\n showVerification,\n showQRCode,\n qrCodeUrl,\n showQRScanner,\n setShowQRCode,\n setShowQRScanner,\n setShowQRScannerModal,\n offerPassword,\n answerPassword,\n localVerificationConfirmed,\n remoteVerificationConfirmed,\n bothVerificationsConfirmed,\n // QR control props\n qrFramesTotal,\n qrFrameIndex,\n qrManualMode,\n toggleQrManualMode,\n nextQrFrame,\n prevQrFrame,\n markAnswerCreated,\n notificationIntegrationRef,\n isGeneratingKeys,\n setIsGeneratingKeys,\n handleCreateOffer,\n relayOnlyMode,\n setRelayOnlyMode,\n webrtcManagerRef,\n showIceSettings,\n setShowIceSettings,\n iceServersText,\n iceSettingsPersisted,\n customIceServers,\n handleApplyIceSettings,\n handleForgetIceSettings\n }) => {\n const [mode, setMode] = React.useState('create');\n const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false);\n // Local UI state for the redesigned Start Secure screen\n const [qrModalOpen, setQrModalOpen] = React.useState(false);\n const [copied, setCopied] = React.useState(false);\n const [sasInput, setSasInput] = React.useState('');\n const [sasError, setSasError] = React.useState('');\n const [platformsOpen, setPlatformsOpen] = React.useState(false);\n const [codeRevealed, setCodeRevealed] = React.useState(false);\n const [genProgress, setGenProgress] = React.useState(0);\n\n // Reset the typed SAS whenever a fresh verification code arrives\n React.useEffect(() => { setSasInput(''); setSasError(''); }, [verificationCode]);\n // Close the QR popup and re-hide (blur) the code whenever the\n // exchange step changes \u2014 every new offer/answer starts concealed.\n React.useEffect(() => {\n if (!showOfferStep && !showAnswerStep) setQrModalOpen(false);\n setCodeRevealed(false);\n }, [showOfferStep, showAnswerStep]);\n // Animate the \"Securing your channel\" steps while keys generate.\n React.useEffect(() => {\n const generating = isGeneratingKeys && !showOfferStep && !showAnswerStep && !showVerification;\n if (!generating) { setGenProgress(0); return; }\n setGenProgress(0);\n let p = 0;\n const id = setInterval(() => {\n p += 1;\n setGenProgress(p);\n if (p >= 3) clearInterval(id);\n }, 520);\n return () => clearInterval(id);\n }, [isGeneratingKeys, showOfferStep, showAnswerStep, showVerification]);\n // Dismiss the download platforms popover on any outside click\n React.useEffect(() => {\n if (!platformsOpen) return;\n const onDoc = () => setPlatformsOpen(false);\n const id = setTimeout(() => document.addEventListener('click', onDoc), 0);\n return () => { clearTimeout(id); document.removeEventListener('click', onDoc); };\n }, [platformsOpen]);\n\n // \"Back\" \u2014 clear the in-progress exchange and return to the intro of\n // the current tab. onClearData() resets offer/answer/verification in\n // the parent, which flips the derived step back to \"intro\".\n const resetToSelect = () => {\n setIsGeneratingKeys(false);\n setQrModalOpen(false);\n onClearData();\n };\n \n const handleVerificationConfirm = (userCode) => {\n return onVerifyConnection(userCode);\n };\n \n const handleVerificationReject = () => {\n onVerifyConnection(null, false);\n };\n \n // Request notification permission on first user interaction\n const requestNotificationPermissionOnInteraction = async () => {\n if (notificationPermissionRequested) {\n return; // Already requested\n }\n \n try {\n // Check if Notification API is supported\n if (!('Notification' in window)) {\n return;\n }\n \n // Check if we're in a secure context\n if (!window.isSecureContext && window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') {\n return;\n }\n \n // Check current permission status\n const currentPermission = (typeof Notification !== 'undefined' && Notification) \n ? Notification.permission \n : 'denied';\n \n // Only request if permission is default (not granted or denied)\n if (currentPermission === 'default' && typeof Notification !== 'undefined' && Notification) {\n const permission = await Notification.requestPermission();\n \n if (permission === 'granted') {\n // Initialize notification integration immediately\n try {\n if (window.NotificationIntegration && webrtcManagerRef.current) {\n const integration = new window.NotificationIntegration(webrtcManagerRef.current);\n await integration.init();\n \n // Store reference for cleanup\n notificationIntegrationRef.current = integration;\n }\n } catch (error) {\n // Handle error silently\n }\n \n // Send welcome notification\n setTimeout(() => {\n try {\n const welcomeNotification = new Notification('SecureBit Chat', {\n body: 'Notifications enabled! You will receive alerts for new messages.',\n icon: '/logo/icon-192x192.png',\n tag: 'welcome-notification'\n });\n \n welcomeNotification.onclick = () => {\n welcomeNotification.close();\n };\n \n setTimeout(() => {\n welcomeNotification.close();\n }, 5000);\n \n } catch (error) {\n // Handle error silently\n }\n }, 1000);\n \n }\n } else if (currentPermission === 'granted') {\n // Initialize notification integration immediately\n try {\n if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {\n const integration = new window.NotificationIntegration(webrtcManagerRef.current);\n await integration.init();\n \n // Store reference for cleanup\n notificationIntegrationRef.current = integration;\n }\n } catch (error) {\n // Handle error silently\n }\n \n // Test notification to confirm it works\n setTimeout(() => {\n try {\n const testNotification = new Notification('SecureBit Chat', {\n body: 'Notifications are working! You will receive alerts for new messages.',\n icon: '/logo/icon-192x192.png',\n tag: 'test-notification'\n });\n \n testNotification.onclick = () => {\n testNotification.close();\n };\n \n setTimeout(() => {\n testNotification.close();\n }, 5000);\n } catch (error) {\n // Handle error silently\n }\n }, 1000);\n }\n \n setNotificationPermissionRequested(true);\n \n } catch (error) {\n // Handle error silently\n }\n };\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Start Secure \u2014 redesigned two-column connection screen.\n // Layout/colors/animation from the imported design; wired to the\n // app's real crypto handlers. Icons use FontAwesome (house style).\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const h = React.createElement;\n const C_ORANGE = '#f0892a';\n const C_GREEN = '#3ecf8e';\n const MONO = SB_MONO;\n\n const encode = (data) => {\n try {\n const min = typeof data === 'object' ? JSON.stringify(data) : (data || '');\n if (!min) return '';\n if (typeof window.encodeBinaryToPrefixed === 'function') return window.encodeBinaryToPrefixed(min);\n if (typeof window.compressToPrefixedGzip === 'function') return window.compressToPrefixedGzip(min);\n return min;\n } catch { return typeof data === 'object' ? JSON.stringify(data) : (data || ''); }\n };\n\n // Derived flow step from the parent's real connection state\n const isCreate = mode === 'create';\n const isGenerating = isGeneratingKeys && !showOfferStep && !showAnswerStep && !showVerification;\n const isOfferCred = isCreate && showOfferStep && !showVerification;\n const isAnswerCred = !isCreate && showAnswerStep && !showVerification;\n const atIntro = !showVerification && !isGenerating && !isOfferCred && !isAnswerCred;\n const accent = isCreate ? C_ORANGE : C_GREEN;\n const kicker = showVerification\n ? 'Step 3 \u00B7 verification'\n : ((isOfferCred || isAnswerCred) ? 'Step 2 \u00B7 exchange' : 'Step 1 \u00B7 open a channel');\n\n const credCode = isCreate ? encode(offerData) : encode(answerData);\n const hasInvite = (offerInput || '').trim().length > 0;\n const hasAnswer = (answerInput || '').trim().length > 0;\n\n const copyCred = async () => {\n try {\n if (typeof copyToClipboardSecure === 'function') await copyToClipboardSecure(credCode);\n else await navigator.clipboard.writeText(credCode);\n } catch (e) {}\n setCopied(true);\n setTimeout(() => setCopied(false), 1600);\n };\n\n // SAS verification (alphanumeric, variable length \u2014 matches real codes)\n const normExpected = (verificationCode || '').replace(/[-\\s]/g, '').length;\n const normInput = sasInput.replace(/[-\\s]/g, '').length;\n const canConfirm = !localVerificationConfirmed && normExpected > 0 && normInput === normExpected;\n const handleSasConfirm = async () => {\n try {\n setSasError('');\n await onVerifyConnection(sasInput);\n } catch (err) {\n setSasInput('');\n setSasError(err?.message === 'SAS_MAX_ATTEMPTS'\n ? 'Too many incorrect attempts. Session reset for safety.'\n : 'Incorrect code. Check it with your peer and try again.');\n }\n };\n\n // Icon set transcribed from the design (inline SVG, not FontAwesome).\n // Each entry: stroke-width + the SVG child elements [tag, attrs].\n const ICON_DEFS = {\n 'fa-user': { sw: 1.9, e: [['circle', { cx: 12, cy: 8, r: 3.6 }], ['path', { d: 'M5 20c0-3.5 3-5.5 7-5.5s7 2 7 5.5' }]] },\n 'fa-lock': { sw: 2, e: [['path', { d: 'M7 11V7a5 5 0 0 1 10 0v4' }], ['rect', { x: 4.5, y: 11, width: 15, height: 9, rx: 2.2 }]] },\n 'fa-plus': { sw: 2.1, e: [['path', { d: 'M12 5v14M5 12h14' }]] },\n 'fa-link': { sw: 2, e: [['path', { d: 'M9.5 14.5l5-5M8 11l-2.2 2.2a3.5 3.5 0 0 0 4.95 4.95L13 16M16 13l2.2-2.2a3.5 3.5 0 0 0-4.95-4.95L11 8' }]] },\n 'fa-bolt': { sw: 2.1, e: [['path', { d: 'M13 2L4.5 13H11l-1 9 8.5-11H12l1-9z' }]] },\n 'fa-camera': { sw: 1.8, e: [['path', { d: 'M2 8.5V6.5A2.5 2.5 0 0 1 4.5 4h2M17.5 4h2A2.5 2.5 0 0 1 22 6.5v2M22 15.5v2a2.5 2.5 0 0 1-2.5 2.5h-2M6.5 20h-2A2.5 2.5 0 0 1 2 17.5v-2' }], ['circle', { cx: 12, cy: 12, r: 3.2 }]] },\n 'fa-qrcode': { sw: 1.9, e: [['rect', { x: 3, y: 3, width: 7, height: 7, rx: 1.3 }], ['rect', { x: 14, y: 3, width: 7, height: 7, rx: 1.3 }], ['rect', { x: 3, y: 14, width: 7, height: 7, rx: 1.3 }], ['path', { d: 'M14 14h3v3M21 14v.01M14 21h.01M21 21v-4M17.5 21H21' }]] },\n 'fa-chevron-right': { sw: 2, e: [['path', { d: 'M9 6l6 6-6 6' }]] },\n 'fa-chevron-left': { sw: 2, e: [['path', { d: 'M15 6l-6 6 6 6' }]] },\n 'fa-chevron-down': { sw: 2, e: [['path', { d: 'M6 9l6 6 6-6' }]] },\n 'fa-circle-notch': { sw: 2, e: [['path', { d: 'M21 12a9 9 0 1 1-6.2-8.6' }]] },\n 'fa-check': { sw: 2.4, e: [['path', { d: 'M20 6L9 17l-5-5' }]] },\n 'fa-check-circle': { sw: 2, e: [['circle', { cx: 12, cy: 12, r: 9 }], ['path', { d: 'M8.5 12.4l2.4 2.4 4.6-5' }]] },\n 'fa-shield-alt': { sw: 1.9, e: [['path', { d: 'M12 2.6l7 3v5.1c0 4.5-3 8.3-7 10.2-4-1.9-7-5.7-7-10.2V5.6l7-3z' }], ['path', { d: 'M9 12l2 2 4-4.1' }]] },\n 'fa-download': { sw: 2, e: [['path', { d: 'M12 3v12M12 15l-4.5-4.5M12 15l4.5-4.5' }], ['path', { d: 'M4 20h16' }]] },\n 'fa-clock': { sw: 1.8, e: [['circle', { cx: 12, cy: 13, r: 8 }], ['path', { d: 'M12 9v4l2.5 2M9 2h6' }]] },\n 'fa-times': { sw: 2.2, e: [['path', { d: 'M18 6L6 18M6 6l12 12' }]] },\n 'fa-eye': { sw: 1.9, e: [['path', { d: 'M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z' }], ['circle', { cx: 12, cy: 12, r: 3 }]] }\n };\n const fa = (name, opts) => {\n opts = opts || {};\n const def = ICON_DEFS[name];\n if (!def) {\n // Fallback to FontAwesome (e.g. fa-sliders-h, kept by preference).\n const st = {};\n if (opts.color) st.color = opts.color;\n if (opts.fontSize) st.fontSize = opts.fontSize;\n if (opts.animation) st.animation = opts.animation;\n if (opts.style) Object.assign(st, opts.style);\n return h('i', { key: opts.key, className: `fas ${name}`, style: st });\n }\n const size = opts.fontSize ? parseFloat(opts.fontSize) : 16;\n const svgStyle = {};\n if (opts.animation) { svgStyle.animation = opts.animation; svgStyle.transformOrigin = 'center'; svgStyle.transformBox = 'fill-box'; }\n if (opts.style) Object.assign(svgStyle, opts.style);\n return h('svg', {\n key: opts.key, width: size, height: size, viewBox: '0 0 24 24',\n fill: 'none', stroke: opts.color || 'currentColor',\n strokeWidth: def.sw || 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n style: svgStyle\n }, def.e.map((el, i) => h(el[0], Object.assign({ key: i }, el[1]))));\n };\n\n // \u2500\u2500 LEFT PANEL \u00B7 branding + animated channel + crypto badges \u2500\u2500\n const leftPanel = h('div', {\n key: 'left',\n className: 'sb-start-left',\n style: {\n flex: '1.05 1 380px', position: 'relative', overflow: 'hidden',\n // Full viewport height even when the panels stack on mobile, so the\n // branding column isn't collapsed/cramped (looked broken otherwise).\n minHeight: '100vh', boxSizing: 'border-box',\n padding: '46px', display: 'flex', flexDirection: 'column',\n justifyContent: 'space-between', gap: '36px',\n borderRight: '1px solid rgba(255,255,255,0.06)',\n background: 'radial-gradient(900px 600px at 25% 18%, rgba(240,137,42,0.07), transparent 62%), radial-gradient(800px 700px at 80% 92%, rgba(62,207,142,0.06), transparent 60%), #0c0c0e'\n }\n }, [\n h('div', { key: 'herowrap', style: { flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', position: 'relative', zIndex: 2 } },\n h('div', { key: 'hero', style: { maxWidth: '470px' } }, [\n h('h1', { key: 'h1', style: { margin: '0 0 14px', fontSize: '34px', fontWeight: 800, letterSpacing: '-1.1px', lineHeight: 1.1, color: '#f4f4f6' } }, [\n 'A direct line', h('br', { key: 'br' }), 'only you two can read.'\n ]),\n h('p', { key: 'p', style: { margin: '0 0 38px', fontSize: '14.5px', lineHeight: 1.6, color: '#8a8a92', maxWidth: '390px' } },\n 'Keys are generated on your device and exchanged peer-to-peer. No accounts, no servers storing your messages.'),\n // animated channel\n h('div', { key: 'channel', style: { display: 'flex', alignItems: 'center', height: '74px' } }, [\n h('div', { key: 'you', style: { flex: 'none', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', width: '74px' } }, [\n h('div', { key: 'n', style: { width: '50px', height: '50px', borderRadius: '15px', display: 'grid', placeItems: 'center', background: 'rgba(240,137,42,0.13)', border: '1px solid rgba(240,137,42,0.3)', animation: 'sbNode 3s ease-in-out infinite' } }, fa('fa-user', { color: C_ORANGE, fontSize: '20px' })),\n h('span', { key: 'l', style: { fontSize: '11px', fontWeight: 600, color: '#9a9aa2' } }, 'You')\n ]),\n h('div', { key: 'wire', style: { flex: 1, position: 'relative', height: '52px', margin: '0 -6px' } }, [\n h('div', { key: 'line', style: { position: 'absolute', top: '50%', left: 0, right: 0, height: '2px', transform: 'translateY(-50%)', background: 'linear-gradient(90deg, rgba(240,137,42,0.45), rgba(62,207,142,0.45))' } }),\n h('span', { key: 'd1', style: { position: 'absolute', top: '50%', transform: 'translateY(-50%)', width: '6px', height: '6px', borderRadius: '50%', background: C_ORANGE, boxShadow: `0 0 8px ${C_ORANGE}`, animation: 'sbFlowR 2.6s linear infinite' } }),\n h('span', { key: 'd2', style: { position: 'absolute', top: '50%', transform: 'translateY(-50%)', width: '6px', height: '6px', borderRadius: '50%', background: C_GREEN, boxShadow: `0 0 8px ${C_GREEN}`, animation: 'sbFlowL 2.6s linear infinite', animationDelay: '0.6s' } }),\n h('div', { key: 'hub', style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: '38px', height: '38px' } }, [\n h('span', { key: 'pulse', style: { position: 'absolute', top: '50%', left: '50%', width: '38px', height: '38px', borderRadius: '50%', border: '1.5px solid rgba(62,207,142,0.5)', animation: 'sbPulse 2.4s ease-out infinite' } }),\n h('div', { key: 'core', style: { position: 'relative', width: '38px', height: '38px', borderRadius: '50%', display: 'grid', placeItems: 'center', background: '#121214', border: '1px solid rgba(62,207,142,0.45)', boxShadow: '0 0 18px rgba(62,207,142,0.25)' } }, fa('fa-lock', { color: C_GREEN, fontSize: '14px' }))\n ])\n ]),\n h('div', { key: 'peer', style: { flex: 'none', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', width: '74px' } }, [\n h('div', { key: 'n', style: { width: '50px', height: '50px', borderRadius: '15px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.1)', border: '1px solid rgba(62,207,142,0.3)', animation: 'sbNode 3s ease-in-out infinite', animationDelay: '1.5s' } }, fa('fa-user', { color: C_GREEN, fontSize: '20px' })),\n h('span', { key: 'l', style: { fontSize: '11px', fontWeight: 600, color: '#9a9aa2' } }, 'Peer')\n ])\n ])\n ])),\n h('div', { key: 'badges', style: { position: 'relative', zIndex: 2, display: 'flex', flexWrap: 'wrap', gap: '8px' } },\n ['ECDH P-384', 'AES-256-GCM', 'Perfect Forward Secrecy'].map((label) =>\n h('span', { key: label, style: { display: 'inline-flex', alignItems: 'center', gap: '6px', padding: '6px 11px', borderRadius: '8px', border: '1px solid rgba(255,255,255,0.07)', background: 'rgba(255,255,255,0.025)', fontFamily: MONO, fontSize: '11px', fontWeight: 500, color: '#9a9aa2' } }, [\n h('span', { key: 'dot', style: { width: '5px', height: '5px', borderRadius: '50%', background: C_GREEN } }),\n label\n ])\n )\n )\n ]);\n\n // \u2500\u2500 RIGHT PANEL \u00B7 flow body (varies by step) \u2500\u2500\n const segToggle = atIntro && h('div', { key: 'seg', style: { position: 'relative', display: 'flex', padding: '4px', borderRadius: '12px', border: '1px solid rgba(255,255,255,0.07)', background: '#141416', marginBottom: '26px' } }, [\n h('div', { key: 'ind', style: { position: 'absolute', top: '4px', bottom: '4px', left: '4px', width: 'calc(50% - 4px)', borderRadius: '9px', background: 'rgba(255,255,255,0.07)', border: '1px solid rgba(255,255,255,0.08)', transform: isCreate ? 'translateX(0%)' : 'translateX(100%)', transition: 'transform .26s cubic-bezier(.3,.8,.3,1)' } }),\n h('button', { key: 'c', className: 'sb-seg-btn', onClick: () => setMode('create'), style: { position: 'relative', zIndex: 1, flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '11px', border: 'none', background: 'transparent', color: isCreate ? '#f4f4f6' : '#7b7b83', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer' } }, [fa('fa-plus', { key: 'i' }), 'Create']),\n h('button', { key: 'j', className: 'sb-seg-btn', onClick: () => setMode('join'), style: { position: 'relative', zIndex: 1, flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '11px', border: 'none', background: 'transparent', color: !isCreate ? '#f4f4f6' : '#7b7b83', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer' } }, [fa('fa-link', { key: 'i' }), 'Join'])\n ]);\n\n const backButton = (key) => h('button', { key: key || 'back', className: 'sb-soft-btn', onClick: resetToSelect, style: { display: 'inline-flex', alignItems: 'center', gap: '6px', marginBottom: '14px', padding: '6px 11px 6px 8px', borderRadius: '8px', border: '1px solid rgba(255,255,255,0.08)', background: 'transparent', color: '#9a9aa2', fontFamily: 'inherit', fontSize: '12.5px', fontWeight: 600, cursor: 'pointer' } }, [fa('fa-chevron-left', { key: 'i' }), 'Back']);\n\n // credential code block (offer/answer text fallback + copy)\n const credBlock = h('div', { key: 'codeblock', style: { borderRadius: '13px', border: '1px solid rgba(255,255,255,0.08)', background: '#141416', overflow: 'hidden', marginBottom: '16px' } }, [\n h('div', { key: 'bar', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '9px 12px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(0,0,0,0.2)' } }, [\n h('span', { key: 'dot', style: { width: '7px', height: '7px', borderRadius: '50%', background: accent } }),\n h('span', { key: 'tag', style: { fontFamily: MONO, fontSize: '10.5px', fontWeight: 600, color: '#8a8a92' } }, `${isCreate ? 'offer' : 'answer'} \u00B7 or copy text`),\n h('button', { key: 'copy', onClick: copyCred, style: { marginLeft: 'auto', padding: '4px 9px', borderRadius: '6px', border: `1px solid ${copied ? 'rgba(62,207,142,0.4)' : 'rgba(255,255,255,0.1)'}`, background: copied ? 'rgba(62,207,142,0.1)' : 'rgba(255,255,255,0.04)', color: copied ? C_GREEN : '#b3b3ba', fontFamily: 'inherit', fontSize: '11px', fontWeight: 600, cursor: 'pointer', transition: 'all .14s' } }, copied ? 'Copied' : 'Copy')\n ]),\n // The handshake code is sensitive \u2014 keep it blurred until the\n // user deliberately reveals it, underscoring that it must be\n // shared only over a channel they trust.\n h('div', { key: 'codewrap', style: { position: 'relative' } }, [\n h('div', { key: 'code', className: 'sb-sc', style: { fontFamily: MONO, fontSize: '11px', lineHeight: 1.55, color: '#c9ccd8', wordBreak: 'break-all', padding: '11px 12px', maxHeight: '72px', overflowY: 'auto', filter: codeRevealed ? 'none' : 'blur(6px)', userSelect: codeRevealed ? 'text' : 'none', transition: 'filter .2s' } }, credCode),\n !codeRevealed && h('button', { key: 'reveal', onClick: () => setCodeRevealed(true), style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', border: 'none', background: 'rgba(20,20,22,0.25)', color: '#cfcfd4', fontFamily: 'inherit', fontSize: '12px', fontWeight: 600, cursor: 'pointer' } }, [\n fa('fa-eye', { key: 'i', fontSize: '15px' }),\n 'Click to reveal \u2014 keep this code private'\n ])\n ])\n ]);\n\n const showQrButton = qrCodeUrl && h('button', { key: 'showqr', onClick: () => setQrModalOpen(true), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '15px 16px', borderRadius: '14px', border: `1px solid ${isCreate ? 'rgba(240,137,42,0.3)' : 'rgba(62,207,142,0.3)'}`, background: isCreate ? 'rgba(240,137,42,0.06)' : 'rgba(62,207,142,0.06)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left', marginBottom: '14px' } }, [\n h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: isCreate ? 'rgba(240,137,42,0.12)' : 'rgba(62,207,142,0.12)', border: `1px solid ${isCreate ? 'rgba(240,137,42,0.28)' : 'rgba(62,207,142,0.28)'}` } }, fa('fa-qrcode', { color: accent, fontSize: '18px' })),\n h('span', { key: 'tx', style: { flex: 1 } }, [\n h('span', { key: 't', style: { display: 'block', fontSize: '14.5px', fontWeight: 700, color: '#f4f4f6' } }, 'Show QR code'),\n h('span', { key: 's', style: { display: 'block', fontSize: '12.5px', color: '#8a8a92', marginTop: '1px' } }, `Full-screen \u00B7 let your peer scan${(qrFramesTotal || 0) > 1 ? ` all ${qrFramesTotal} frames` : ''}`)\n ]),\n fa('fa-chevron-right', { color: '#6b6b73' })\n ]);\n\n let inner;\n if (showVerification) {\n const verified = bothVerificationsConfirmed;\n const cells = (verificationCode || '').split('').map((ch, i) =>\n h('div', { key: i, style: { flex: 1, maxWidth: '46px', aspectRatio: '0.82', display: 'grid', placeItems: 'center', borderRadius: '10px', border: '1px solid rgba(62,207,142,0.25)', background: 'rgba(62,207,142,0.05)', fontFamily: MONO, fontSize: '22px', fontWeight: 700, color: C_GREEN } }, ch));\n inner = h('div', { key: 'verify', style: { animation: 'sbUp .3s ease' } }, [\n !verified && backButton('vback'),\n h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '11px', marginBottom: '8px' } }, [\n h('div', { key: 'i', style: { width: '34px', height: '34px', flex: 'none', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.1)', border: '1px solid rgba(62,207,142,0.25)' } }, fa('fa-shield-alt', { color: C_GREEN })),\n h('h2', { key: 't', style: { margin: 0, fontSize: '21px', fontWeight: 800, letterSpacing: '-0.4px', color: '#f4f4f6' } }, 'Security verification')\n ]),\n h('p', { key: 'sub', style: { margin: '0 0 18px', fontSize: '13.5px', lineHeight: 1.55, color: '#8a8a92' } }, 'Compare this safety code with your peer over a separate channel (voice / in person), then type it to unlock the chat.'),\n h('div', { key: 'cells', style: { display: 'flex', gap: '6px', justifyContent: 'center', marginBottom: '20px', flexWrap: 'wrap' } }, cells),\n verified\n ? h('div', { key: 'ok', style: { display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', padding: '24px 16px', borderRadius: '16px', border: '1px solid rgba(62,207,142,0.25)', background: 'rgba(62,207,142,0.06)', animation: 'sbUp .3s ease' } }, [\n h('div', { key: 'i', style: { width: '54px', height: '54px', borderRadius: '16px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.14)', border: '1px solid rgba(62,207,142,0.35)', marginBottom: '14px' } }, fa('fa-check', { color: C_GREEN, fontSize: '24px' })),\n h('div', { key: 't', style: { fontSize: '18px', fontWeight: 800, color: '#f4f4f6' } }, 'Channel verified'),\n h('div', { key: 's', style: { fontSize: '13.5px', color: '#8a8a92', marginTop: '5px' } }, 'Both parties confirmed. Opening the secure chat\u2026')\n ])\n : h('div', { key: 'form' }, [\n h('div', { key: 'lbl', style: { fontSize: '12.5px', fontWeight: 600, color: '#9a9aa2', marginBottom: '8px' } }, 'Enter the verified code'),\n h('input', { key: 'in', value: sasInput, onChange: (e) => { setSasInput(e.target.value.toUpperCase()); if (sasError) setSasError(''); }, disabled: localVerificationConfirmed, autoFocus: true, autoComplete: 'off', spellCheck: false, placeholder: verificationCode ? 'Type code here' : 'Waiting for code\u2026', style: { width: '100%', textAlign: 'center', letterSpacing: '6px', borderRadius: '12px', border: `1px solid ${sasInput.length ? (canConfirm || localVerificationConfirmed ? 'rgba(62,207,142,0.5)' : 'rgba(255,255,255,0.14)') : 'rgba(255,255,255,0.08)'}`, background: '#141416', color: '#f4f4f6', fontFamily: MONO, fontSize: '20px', fontWeight: 700, padding: '14px', outline: 'none', textTransform: 'uppercase', marginBottom: sasError ? '8px' : '16px' } }),\n sasError && h('p', { key: 'err', style: { color: '#e5727a', fontSize: '12.5px', margin: '0 0 16px' } }, sasError),\n h('div', { key: 'status', style: { display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '16px' } }, [\n h('div', { key: 'you', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '11px 14px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.06)', background: '#141416' } }, [\n h('span', { key: 'l', style: { fontSize: '13px', color: '#cfcfd4', fontWeight: 600 } }, 'Your confirmation'),\n h('span', { key: 'v', style: { display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: '12.5px', fontWeight: 600, color: localVerificationConfirmed ? C_GREEN : '#7b7b83' } }, [fa(localVerificationConfirmed ? 'fa-check-circle' : 'fa-clock', { key: 'i' }), localVerificationConfirmed ? 'Confirmed' : 'Pending'])\n ]),\n h('div', { key: 'peer', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '11px 14px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.06)', background: '#141416' } }, [\n h('span', { key: 'l', style: { fontSize: '13px', color: '#cfcfd4', fontWeight: 600 } }, 'Peer confirmation'),\n h('span', { key: 'v', style: { display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: '12.5px', fontWeight: 600, color: remoteVerificationConfirmed ? C_GREEN : '#7b7b83' } }, [fa(remoteVerificationConfirmed ? 'fa-check-circle' : 'fa-clock', { key: 'i' }), remoteVerificationConfirmed ? 'Confirmed' : 'Pending'])\n ])\n ]),\n h('div', { key: 'btns', style: { display: 'flex', gap: '10px' } }, [\n h('button', { key: 'ok', onClick: handleSasConfirm, disabled: !canConfirm, style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '14px', borderRadius: '13px', border: 'none', background: canConfirm ? C_GREEN : 'rgba(255,255,255,0.05)', color: canConfirm ? '#08160e' : '#56565e', fontFamily: 'inherit', fontSize: '14.5px', fontWeight: 700, cursor: canConfirm ? 'pointer' : 'not-allowed', boxShadow: canConfirm ? '0 8px 24px rgba(62,207,142,0.25)' : 'none' } }, [fa(localVerificationConfirmed ? 'fa-check-circle' : 'fa-check', { key: 'i' }), localVerificationConfirmed ? 'Confirmed' : 'Confirm code']),\n h('button', { key: 'no', onClick: handleVerificationReject, style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '7px', padding: '14px 16px', borderRadius: '13px', border: '1px solid rgba(229,114,122,0.3)', background: 'transparent', color: '#e5727a', fontFamily: 'inherit', fontSize: '13.5px', fontWeight: 600, cursor: 'pointer' } }, [fa('fa-times', { key: 'i' }), \"Don't match\"])\n ])\n ])\n ]);\n } else if (isGenerating) {\n const genSteps = ['Generating ECDH P-384 key pair', 'Deriving verification code', 'Pinning Perfect Forward Secrecy'];\n inner = h('div', { key: 'gen', style: { animation: 'sbUp .28s ease' } }, [\n h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '13px', marginBottom: '22px' } }, [\n h('div', { key: 'sp', style: { width: '44px', height: '44px', flex: 'none', display: 'grid', placeItems: 'center' } }, fa('fa-circle-notch', { color: C_ORANGE, fontSize: '32px', animation: 'sbSpin 1s linear infinite' })),\n h('div', { key: 'tx' }, [\n h('h2', { key: 't', style: { margin: 0, fontSize: '20px', fontWeight: 800, letterSpacing: '-0.4px', color: '#f4f4f6' } }, isCreate ? 'Securing your channel' : 'Building your answer'),\n h('p', { key: 's', style: { margin: '3px 0 0', fontSize: '13px', color: '#8a8a92' } }, 'Forging keys strong enough to resist tampering.')\n ])\n ]),\n h('div', { key: 'steps', style: { display: 'flex', flexDirection: 'column', borderRadius: '13px', border: '1px solid rgba(255,255,255,0.07)', background: '#141416', overflow: 'hidden' } },\n genSteps.map((label, i) => {\n const done = genProgress > i;\n const active = genProgress === i;\n return h('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: '12px', padding: '13px 15px', borderTop: i ? '1px solid rgba(255,255,255,0.05)' : 'none', transition: 'background .3s', background: done ? 'rgba(62,207,142,0.04)' : 'transparent' } }, [\n h('div', { key: 'd', style: { flex: 'none', width: '20px', height: '20px', borderRadius: '50%', display: 'grid', placeItems: 'center', background: done ? 'rgba(62,207,142,0.12)' : (active ? 'rgba(240,137,42,0.12)' : 'rgba(255,255,255,0.04)'), border: `1px solid ${done ? 'rgba(62,207,142,0.3)' : (active ? 'rgba(240,137,42,0.3)' : 'rgba(255,255,255,0.1)')}`, transition: 'all .3s' } },\n done\n ? fa('fa-check', { color: C_GREEN, fontSize: '11px' })\n : h('span', { style: { width: '6px', height: '6px', borderRadius: '50%', background: active ? C_ORANGE : '#56565e', animation: active ? 'sbBlink 1s ease-in-out infinite' : 'none' } })),\n h('span', { key: 'l', style: { fontSize: '13.5px', color: done ? '#cfcfd4' : (active ? '#e8e8eb' : '#6b6b73'), transition: 'color .3s' } }, label)\n ]);\n })\n )\n ]);\n } else if (isOfferCred || isAnswerCred) {\n inner = h('div', { key: 'cred', style: { animation: 'sbUp .3s ease' } }, [\n backButton('cback'),\n h('h2', { key: 'h', style: { margin: '0 0 6px', fontSize: '23px', fontWeight: 800, letterSpacing: '-0.5px', color: '#f4f4f6' } }, isCreate ? 'Share your invitation' : 'Send back your answer'),\n h('p', { key: 'p', style: { margin: '0 0 18px', fontSize: '14px', lineHeight: 1.55, color: '#8a8a92' } }, isCreate ? 'Show the QR or send the code to your peer. It is one-time and expires shortly.' : 'Give this answer to the channel creator so they can finish the handshake.'),\n showQrButton,\n credBlock,\n isOfferCred && h('div', { key: 'offerextra', style: { marginTop: '4px' } }, [\n h('div', { key: 'lbl', style: { fontSize: '12.5px', fontWeight: 600, color: '#9a9aa2', marginBottom: '8px' } }, \"Then receive the answer your peer sends back\"),\n h('div', { key: 'ta', style: { borderRadius: '12px', border: `1px solid ${hasAnswer ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.07)'}`, background: '#141416', padding: '11px 14px', marginBottom: '10px' } },\n h('textarea', { value: answerInput, onChange: (e) => { setAnswerInput(e.target.value); if (e.target.value.trim().length > 0 && typeof markAnswerCreated === 'function') markAnswerCreated(); }, rows: 2, placeholder: \"Paste peer's answer code\u2026\", style: { width: '100%', resize: 'none', border: 'none', outline: 'none', background: 'transparent', color: '#d7d7db', fontFamily: MONO, fontSize: '12px', lineHeight: 1.55, minHeight: '44px' } })),\n h('div', { key: 'btns', style: { display: 'flex', gap: '10px' } }, [\n h('button', { key: 'scan', className: 'sb-scan-btn', onClick: () => setShowQRScannerModal(true), style: { flex: 'none', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '14px 16px', borderRadius: '13px', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.04)', color: '#cfcfd4', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer' } }, [fa('fa-camera', { key: 'i' }), 'Scan']),\n h('button', { key: 'est', onClick: onConnect, disabled: !hasAnswer, style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '14px', borderRadius: '13px', border: 'none', background: hasAnswer ? C_ORANGE : 'rgba(255,255,255,0.05)', color: hasAnswer ? '#1a0f04' : '#56565e', fontFamily: 'inherit', fontSize: '14.5px', fontWeight: 700, cursor: hasAnswer ? 'pointer' : 'not-allowed', boxShadow: hasAnswer ? '0 8px 24px rgba(240,137,42,0.28)' : 'none' } }, 'Establish connection')\n ])\n ]),\n isAnswerCred && h('div', { key: 'answerextra', style: { marginTop: '4px', display: 'flex', alignItems: 'center', gap: '10px', padding: '12px 14px', borderRadius: '12px', border: '1px solid rgba(62,207,142,0.18)', background: 'rgba(62,207,142,0.05)' } }, [\n fa('fa-circle-notch', { key: 'i', color: C_GREEN, animation: 'sbSpin 1.4s linear infinite' }),\n h('span', { key: 't', style: { fontSize: '13px', color: '#cfcfd4', fontWeight: 500 } }, 'Send this answer to the creator, then wait \u2014 the chat opens once they connect.')\n ])\n ]);\n } else if (isCreate) {\n // CREATE intro\n inner = h('div', { key: 'introC', style: { animation: 'sbUp .28s ease' } }, [\n h('h2', { key: 'h', style: { margin: '0 0 6px', fontSize: '23px', fontWeight: 800, letterSpacing: '-0.5px', color: '#f4f4f6' } }, 'Create a new channel'),\n h('p', { key: 'p', style: { margin: '0 0 22px', fontSize: '14px', lineHeight: 1.55, color: '#8a8a92' } }, 'Your device generates the keys and a one-time invitation. Nothing touches a server.'),\n h('button', { key: 'gen', className: 'sb-gen-btn', onClick: () => { requestNotificationPermissionOnInteraction(); if (webrtcManagerRef.current) handleCreateOffer(); }, style: { width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '15px', borderRadius: '13px', border: 'none', background: C_ORANGE, color: '#1a0f04', fontFamily: 'inherit', fontSize: '15px', fontWeight: 700, cursor: 'pointer', boxShadow: '0 8px 24px rgba(240,137,42,0.28)' } }, [fa('fa-bolt', { key: 'i' }), 'Generate keys & invitation'])\n ]);\n } else {\n // JOIN intro\n inner = h('div', { key: 'introJ', style: { animation: 'sbUp .28s ease' } }, [\n h('h2', { key: 'h', style: { margin: '0 0 6px', fontSize: '23px', fontWeight: 800, letterSpacing: '-0.5px', color: '#f4f4f6' } }, 'Join a channel'),\n h('p', { key: 'p', style: { margin: '0 0 16px', fontSize: '14px', lineHeight: 1.55, color: '#8a8a92' } }, \"Scan your peer's QR with your camera, or paste their invitation code.\"),\n h('button', { key: 'scan', className: 'sb-scan-btn', onClick: () => { requestNotificationPermissionOnInteraction(); setShowQRScannerModal(true); }, style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '15px 16px', borderRadius: '14px', border: '1px solid rgba(62,207,142,0.3)', background: 'rgba(62,207,142,0.06)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left', marginBottom: '14px' } }, [\n h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.12)', border: '1px solid rgba(62,207,142,0.28)' } }, fa('fa-camera', { color: C_GREEN, fontSize: '18px' })),\n h('span', { key: 'tx', style: { flex: 1 } }, [\n h('span', { key: 't', style: { display: 'block', fontSize: '14.5px', fontWeight: 700, color: '#f4f4f6' } }, 'Scan QR with camera'),\n h('span', { key: 's', style: { display: 'block', fontSize: '12.5px', color: '#8a8a92', marginTop: '1px' } }, \"Fastest \u2014 point at your peer's screen\")\n ]),\n fa('fa-chevron-right', { color: '#6b6b73' })\n ]),\n h('div', { key: 'or', style: { display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '14px' } }, [\n h('span', { key: 'a', style: { flex: 1, height: '1px', background: 'rgba(255,255,255,0.07)' } }),\n h('span', { key: 'm', style: { fontSize: '11px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '0.7px' } }, 'or paste code'),\n h('span', { key: 'b', style: { flex: 1, height: '1px', background: 'rgba(255,255,255,0.07)' } })\n ]),\n h('div', { key: 'ta', style: { borderRadius: '13px', border: `1px solid ${hasInvite ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.07)'}`, background: '#141416', padding: '13px 15px', marginBottom: '12px' } },\n h('textarea', { value: offerInput, onChange: (e) => { setOfferInput(e.target.value); if (e.target.value.trim().length > 0 && typeof markAnswerCreated === 'function') markAnswerCreated(); }, rows: 3, placeholder: 'Paste invitation code here\u2026', style: { width: '100%', resize: 'none', border: 'none', outline: 'none', background: 'transparent', color: '#d7d7db', fontFamily: MONO, fontSize: '12.5px', lineHeight: 1.6, minHeight: '66px' } })),\n h('button', { key: 'connect', onClick: () => { requestNotificationPermissionOnInteraction(); onCreateAnswer(); }, disabled: !hasInvite || connectionStatus === 'connecting', style: { width: '100%', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '14px', borderRadius: '13px', border: 'none', background: (hasInvite && connectionStatus !== 'connecting') ? C_ORANGE : 'rgba(255,255,255,0.05)', color: (hasInvite && connectionStatus !== 'connecting') ? '#1a0f04' : '#56565e', fontFamily: 'inherit', fontSize: '15px', fontWeight: 700, cursor: (hasInvite && connectionStatus !== 'connecting') ? 'pointer' : 'not-allowed', boxShadow: (hasInvite && connectionStatus !== 'connecting') ? '0 8px 24px rgba(240,137,42,0.28)' : 'none' } }, connectionStatus === 'connecting' ? 'Processing\u2026' : 'Connect')\n ]);\n }\n\n // Desktop downloads (real GitHub release assets) + OS detection.\n const DOWNLOADS = {\n mac: { name: 'macOS', format: '.dmg \u00B7 Apple Silicon & Intel', icon: 'fab fa-apple', url: 'https://github.com/SecureBitChat/securebit-desktop/releases/download/v0.1.0/SecureBit.Chat_0.1.0_x64.dmg' },\n win: { name: 'Windows', format: '.exe \u00B7 64-bit installer', icon: 'fab fa-windows', url: 'https://github.com/SecureBitChat/securebit-desktop/releases/latest/download/SecureBit.Chat_0.1.0_x64-setup.exe' },\n linux: { name: 'Linux', format: '.AppImage', icon: 'fab fa-linux', url: 'https://github.com/SecureBitChat/securebit-desktop/releases/latest/download/SecureBit.Chat_0.1.0_amd64.AppImage' }\n };\n const detectOS = () => {\n const ua = (navigator.userAgent || '') + ' ' + (navigator.platform || '');\n if (/Mac|iPhone|iPad|iPod/i.test(ua) && !/Android/i.test(ua)) return 'mac';\n if (/Win/i.test(ua)) return 'win';\n if (/Linux/i.test(ua) && !/Android/i.test(ua)) return 'linux';\n return 'win';\n };\n const detectedOS = detectOS();\n const otherOS = ['mac', 'win', 'linux'].filter((k) => k !== detectedOS);\n const dlLink = (url) => { try { window.open(url, '_blank', 'noopener'); } catch (e) {} };\n\n const platformsMenu = platformsOpen && h('div', { key: 'platmenu', className: 'sb-platforms-menu', style: { position: 'absolute', left: 0, bottom: 'calc(100% + 10px)', width: '344px', maxWidth: '100%', borderRadius: '16px', border: '1px solid rgba(255,255,255,0.1)', background: '#161618', boxShadow: '0 24px 60px rgba(0,0,0,0.55)', overflow: 'hidden', zIndex: 25, animation: 'sbUp .2s ease' } }, [\n h('div', { key: 'mh', style: { display: 'flex', alignItems: 'center', gap: '10px', padding: '14px 16px', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [\n h('div', { key: 't', style: { flex: 1, lineHeight: 1.2 } }, [\n h('div', { key: 'a', style: { fontSize: '14px', fontWeight: 800, color: '#f4f4f6' } }, 'Download SecureBit'),\n h('div', { key: 'b', style: { fontSize: '11.5px', color: '#7b7b83' } }, 'Free \u00B7 open source')\n ]),\n h('span', { key: 'pill', style: { fontFamily: MONO, fontSize: '10px', fontWeight: 600, color: C_GREEN, padding: '3px 8px', borderRadius: '6px', background: 'rgba(62,207,142,0.1)', border: '1px solid rgba(62,207,142,0.22)' } }, \"You're on Web\")\n ]),\n h('div', { key: 'rec', style: { padding: '12px 12px 6px' } },\n h('button', { key: 'b', onClick: () => dlLink(DOWNLOADS[detectedOS].url), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '12px', padding: '13px 14px', borderRadius: '12px', border: '1px solid rgba(240,137,42,0.4)', background: 'rgba(240,137,42,0.08)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left' } }, [\n h('span', { key: 'ic', style: { flex: 'none', display: 'grid', placeItems: 'center', width: '38px', height: '38px', borderRadius: '11px', background: 'rgba(240,137,42,0.14)', border: '1px solid rgba(240,137,42,0.3)', color: C_ORANGE } }, h('i', { className: DOWNLOADS[detectedOS].icon, style: { fontSize: '17px' } })),\n h('span', { key: 'tx', style: { flex: 1, minWidth: 0 } }, [\n h('span', { key: 'n', style: { display: 'block', fontSize: '13.5px', fontWeight: 700, color: '#f4f4f6' } }, DOWNLOADS[detectedOS].name),\n h('span', { key: 'f', style: { display: 'block', fontSize: '11px', color: '#f0b072', marginTop: '1px' } }, `Recommended for this device \u00B7 ${DOWNLOADS[detectedOS].format}`)\n ]),\n fa('fa-download', { color: C_ORANGE })\n ])),\n h('div', { key: 'others', style: { padding: '0 12px 8px', display: 'flex', flexDirection: 'column', gap: '2px' } },\n otherOS.map((k) => h('button', { key: k, onClick: () => dlLink(DOWNLOADS[k].url), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '12px', padding: '11px 14px', borderRadius: '11px', border: 'none', background: 'transparent', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left' } }, [\n h('span', { key: 'ic', style: { flex: 'none', display: 'grid', placeItems: 'center', width: '34px', height: '34px', borderRadius: '10px', background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)', color: '#cfcfd4' } }, h('i', { className: DOWNLOADS[k].icon, style: { fontSize: '15px' } })),\n h('span', { key: 'tx', style: { flex: 1, minWidth: 0 } }, [\n h('span', { key: 'n', style: { display: 'block', fontSize: '13px', fontWeight: 600, color: '#e8e8eb' } }, DOWNLOADS[k].name),\n h('span', { key: 'f', style: { display: 'block', fontSize: '11px', color: '#7b7b83', marginTop: '1px' } }, DOWNLOADS[k].format)\n ]),\n fa('fa-download', { color: '#8a8a92' })\n ]))),\n h('div', { key: 'soon', style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '12px 16px', borderTop: '1px solid rgba(255,255,255,0.06)', background: 'rgba(255,255,255,0.015)' } }, [\n fa('fa-clock', { key: 'i', color: '#6b6b73' }),\n h('span', { key: 't', style: { fontSize: '11.5px', lineHeight: 1.45, color: '#7b7b83' } }, 'Mobile (iOS, Android) and browser extensions (Chrome, Firefox, Opera) are coming soon.')\n ])\n ]);\n\n const footer = h('div', { key: 'footer', className: 'sb-conn-footer', style: { position: 'relative', marginTop: '30px', paddingTop: '18px', borderTop: '1px solid rgba(255,255,255,0.06)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '12px', flexWrap: 'wrap' } }, [\n h('button', { key: 'dl', onClick: () => setPlatformsOpen((v) => !v), style: { display: 'inline-flex', alignItems: 'center', gap: '9px', padding: '8px 13px 8px 9px', borderRadius: '10px', border: `1px solid ${platformsOpen ? 'rgba(240,137,42,0.4)' : 'rgba(255,255,255,0.08)'}`, background: platformsOpen ? 'rgba(240,137,42,0.06)' : 'rgba(255,255,255,0.02)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', transition: 'all .15s' } }, [\n fa('fa-download', { key: 'i', color: C_ORANGE }),\n h('span', { key: 't', style: { fontSize: '12.5px', fontWeight: 700, color: '#e8e8eb' } }, 'Download desktop app'),\n fa('fa-chevron-down', { key: 'c', color: '#6b6b73', style: { fontSize: '11px', transform: platformsOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } })\n ]),\n h('button', { key: 'settings', className: 'sb-link', onClick: () => setShowIceSettings && setShowIceSettings(true), style: { display: 'inline-flex', alignItems: 'center', gap: '7px', background: 'none', border: 'none', color: '#8a8a92', fontFamily: 'inherit', fontSize: '12.5px', fontWeight: 600, cursor: 'pointer' } }, [fa('fa-sliders-h', { key: 'i' }), 'Advanced settings']),\n platformsMenu\n ]);\n\n // Advanced settings overlay \u2014 rendered inside the right column,\n // sliding up over it (z-30), exactly as in the design.\n const settingsOverlay = (showIceSettings && typeof window !== 'undefined' && window.IceServerSettings)\n ? h(window.IceServerSettings, {\n key: 'ice-settings',\n isOpen: true,\n embedded: true,\n onClose: () => setShowIceSettings(false),\n initial: {\n useCustom: Array.isArray(customIceServers) && customIceServers.length > 0,\n serversText: iceServersText,\n privacyMode: relayOnlyMode ? 'relay-only' : 'standard',\n persisted: iceSettingsPersisted\n },\n hasSaved: iceSettingsPersisted,\n onApply: handleApplyIceSettings,\n onForget: handleForgetIceSettings\n })\n : null;\n\n const rightPanel = h('div', { key: 'right', style: { flex: '0.95 1 460px', minWidth: 'min(100%, 320px)', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', height: '100vh' } }, [\n h('div', { key: 'scroll', className: 'custom-scrollbar', style: { flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', padding: '42px 44px' } },\n h('div', { style: { maxWidth: '430px', width: '100%', margin: 'auto' } }, [\n h('div', { key: 'kicker', style: { fontFamily: MONO, fontSize: '11px', fontWeight: 600, color: '#6b6b73', textTransform: 'uppercase', letterSpacing: '1px', marginBottom: '10px' } }, kicker),\n segToggle,\n inner,\n footer\n ])),\n settingsOverlay\n ]);\n\n // \u2500\u2500 QR display modal (real qrCodeUrl + frame controls) \u2500\u2500\n const qrModal = (qrModalOpen && qrCodeUrl) && h('div', { key: 'qrmodal', onClick: () => setQrModalOpen(false), style: { position: 'fixed', inset: 0, zIndex: 50, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', background: 'rgba(6,6,8,0.82)', backdropFilter: 'blur(10px)', animation: 'sbUp .2s ease' } },\n h('div', { onClick: (e) => e.stopPropagation(), style: { width: '100%', maxWidth: '460px', borderRadius: '22px', border: '1px solid rgba(255,255,255,0.1)', background: '#111113', boxShadow: '0 30px 90px rgba(0,0,0,0.6)', overflow: 'hidden' } }, [\n h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '11px', padding: '18px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [\n h('span', { key: 'd', style: { width: '9px', height: '9px', borderRadius: '50%', background: accent } }),\n h('div', { key: 'tx', style: { flex: 1, lineHeight: 1.2 } }, [\n h('div', { key: 't', style: { fontSize: '15.5px', fontWeight: 800, color: '#f4f4f6' } }, isCreate ? 'Share your invitation' : 'Send back your answer'),\n h('div', { key: 's', style: { fontSize: '12px', color: '#7b7b83' } }, `${isCreate ? 'offer' : 'answer'} \u00B7 one-time`)\n ]),\n h('button', { key: 'x', onClick: () => setQrModalOpen(false), style: { width: '32px', height: '32px', display: 'grid', placeItems: 'center', borderRadius: '9px', border: 'none', background: 'rgba(255,255,255,0.05)', color: '#9a9aa2', cursor: 'pointer' } }, fa('fa-times'))\n ]),\n h('div', { key: 'body', style: { padding: '22px 24px 24px' } }, [\n h('div', { key: 'qr', style: { position: 'relative', width: '100%', aspectRatio: '1', borderRadius: '18px', overflow: 'hidden', background: '#fff', padding: '18px', display: 'grid', placeItems: 'center' } },\n h('img', { src: qrCodeUrl, alt: 'QR code', style: { width: '100%', height: '100%', objectFit: 'contain', display: 'block' } })),\n h('div', { key: 'ctrls', style: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '12px', marginTop: '18px' } }, [\n (qrFramesTotal || 0) >= 1 && h('div', { key: 'frame', style: { display: 'flex', alignItems: 'center', gap: '9px' } }, [\n h('span', { key: 'l', style: { fontFamily: MONO, fontSize: '12px', fontWeight: 600, color: '#9a9aa2' } }, `Frame ${Math.max(1, qrFrameIndex || 1)} / ${qrFramesTotal || 1}`),\n h('div', { key: 'dots', style: { display: 'flex', gap: '5px' } }, Array.from({ length: qrFramesTotal || 1 }, (_, i) => h('span', { key: i, style: { width: '7px', height: '7px', borderRadius: '50%', background: (i + 1) === (qrFrameIndex || 1) ? accent : 'rgba(255,255,255,0.14)', transition: 'background .25s' } })))\n ]),\n (qrFramesTotal || 0) > 1 && h('div', { key: 'nav', style: { display: 'flex', alignItems: 'center', gap: '6px' } }, [\n h('button', { key: 'prev', onClick: prevQrFrame, style: { width: '40px', height: '36px', display: 'grid', placeItems: 'center', borderRadius: '10px', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.04)', color: '#cfcfd4', cursor: 'pointer' } }, fa('fa-chevron-left')),\n h('button', { key: 'auto', onClick: toggleQrManualMode, style: { display: 'inline-flex', alignItems: 'center', gap: '7px', padding: '9px 18px', borderRadius: '10px', border: `1px solid ${qrManualMode ? 'rgba(255,255,255,0.1)' : 'rgba(240,137,42,0.45)'}`, background: qrManualMode ? 'rgba(255,255,255,0.04)' : 'rgba(240,137,42,0.08)', color: qrManualMode ? '#9a9aa2' : C_ORANGE, fontFamily: 'inherit', fontSize: '13px', fontWeight: 600, cursor: 'pointer' } }, qrManualMode ? 'Manual' : 'Auto'),\n h('button', { key: 'next', onClick: nextQrFrame, style: { width: '40px', height: '36px', display: 'grid', placeItems: 'center', borderRadius: '10px', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.04)', color: '#cfcfd4', cursor: 'pointer' } }, fa('fa-chevron-right'))\n ]),\n h('p', { key: 'hint', style: { margin: '2px 0 0', textAlign: 'center', fontSize: '12px', lineHeight: 1.5, color: '#6b6b73' } }, (qrFramesTotal || 0) > 1 ? `The handshake is split across ${qrFramesTotal} frames \u2014 keep this open until your peer captures all of them.` : 'Keep this open until your peer captures the code.')\n ])\n ])\n ])\n );\n\n const hero = h('div', { key: 'hero', style: { display: 'flex', flexWrap: 'wrap', minHeight: '100vh', width: '100%', background: '#0f0f11', color: '#e8e8eb' } }, [leftPanel, rightPanel]);\n\n // Full-bleed dark bands \u2014 match the design mockups (not clamped to max-w-4xl).\n const uniqueSection = atIntro && h(UniqueFeatureSlider, { key: 'unique-features-slider' });\n const partnersSection = atIntro && h(BecomePartner, { key: 'become-partner' });\n const roadmapSection = atIntro && h(Roadmap, { key: 'roadmap' });\n const communitySection = atIntro && h(CommunityCTA, { key: 'community-cta' });\n\n // Ship the keyframes inside app.js so the animations can never go\n // stale against a service-worker-cached components.css.\n const keyframeStyle = h('style', { key: 'kf', dangerouslySetInnerHTML: { __html:\n '@keyframes sbFlowR{0%{left:4%;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:96%;opacity:0}}' +\n '@keyframes sbFlowL{0%{left:96%;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:4%;opacity:0}}' +\n '@keyframes sbPulse{0%,100%{transform:translate(-50%,-50%) scale(1);opacity:.5}50%{transform:translate(-50%,-50%) scale(1.5);opacity:0}}' +\n '@keyframes sbSpin{to{transform:rotate(360deg)}}' +\n '@keyframes sbUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}' +\n '@keyframes sbNode{0%,100%{box-shadow:0 0 0 0 rgba(62,207,142,0)}50%{box-shadow:0 0 0 6px rgba(62,207,142,.06)}}' +\n '@keyframes sbScan{0%{top:8%}100%{top:88%}}' +\n '@keyframes sbBlink{0%,100%{opacity:1}50%{opacity:.35}}'\n } });\n\n return h('div', { className: 'sb-start', style: { width: '100%' } }, [keyframeStyle, hero, uniqueSection, partnersSection, roadmapSection, communitySection, qrModal]);\n };\n \n // Global scroll function - defined outside components to ensure availability\n const createScrollToBottomFunction = (chatMessagesRef) => {\n return () => {\n if (chatMessagesRef && chatMessagesRef.current) {\n const scrollAttempt = () => {\n if (chatMessagesRef.current) {\n chatMessagesRef.current.scrollTo({\n top: chatMessagesRef.current.scrollHeight,\n behavior: 'smooth'\n });\n }\n };\n scrollAttempt();\n \n setTimeout(scrollAttempt, 50);\n setTimeout(scrollAttempt, 150);\n setTimeout(scrollAttempt, 300);\n \n requestAnimationFrame(() => {\n setTimeout(scrollAttempt, 100);\n });\n }\n };\n };\n\n\n // Runs the real-time cryptographic verification and shows a detailed report\n // modal (same behaviour the shared header used to provide on click).\n const runSecurityReport = async (webrtcManager) => {\n let securityData = null;\n try {\n if (webrtcManager && window.EnhancedSecureCryptoUtils) {\n securityData = await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(webrtcManager);\n }\n } catch (e) { /* ignore */ }\n if (!securityData) {\n alert('Security verification in progress\u2026\\nPlease wait for real-time cryptographic verification to complete.');\n return;\n }\n\n // Security verification report \u2014 translated from the Claude Design\n // component (Security Verification.dc.html): a gauge + live test grid\n // driven by the real cryptographic results in securityData.\n const MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n const esc = (s) => String(s).replace(/[&<>\"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '\"': '"' }[c]));\n const accent = securityData.color === 'orange' ? '#f0892a'\n : securityData.color === 'yellow' ? '#e3c84e'\n : securityData.color === 'red' ? '#e5727a'\n : '#3ecf8e';\n const accentRGB = securityData.color === 'orange' ? '240,137,42'\n : securityData.color === 'yellow' ? '227,200,78'\n : securityData.color === 'red' ? '229,114,122'\n : '62,207,142';\n const score = Math.max(0, Math.min(100, Math.round(securityData.score || 0)));\n const circ = 2 * Math.PI * 56;\n const dashArray = `${(circ * Math.min(1, score / 100)).toFixed(1)} ${circ.toFixed(1)}`;\n const level = String(securityData.level || 'SECURE').toUpperCase();\n const isReal = securityData.isRealData !== false;\n const entries = securityData.verificationResults ? Object.entries(securityData.verificationResults) : [];\n const passedCount = Number.isFinite(securityData.passedChecks) ? securityData.passedChecks : entries.filter(([, r]) => r && r.passed).length;\n const totalCount = Number.isFinite(securityData.totalChecks) ? securityData.totalChecks : entries.length;\n const verifiedAt = new Date(securityData.timestamp || Date.now()).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });\n\n const pretty = (k) => {\n let s = String(k).replace(/^verify/i, '').replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2').trim();\n s = s.replace(/\\b(ecdh|ecdsa|aes|gcm|hmac|pfs|sas|mitm|asn|dtls|hkdf|spki|oid|p384)\\b/gi, (m) => m.toUpperCase());\n return s.charAt(0).toUpperCase() + s.slice(1);\n };\n\n const checkIcon = ``;\n const xIcon = ``;\n const testsHTML = entries.map(([k, r], i) => {\n const passed = !!(r && r.passed);\n const desc = (r && r.details) || (passed ? 'Test passed' : 'Test failed or unavailable');\n const bg = passed ? '#161618' : '#121214';\n const border = passed ? 'rgba(62,207,142,0.16)' : 'rgba(229,114,122,0.18)';\n const iconBg = passed ? 'rgba(62,207,142,0.12)' : 'rgba(229,114,122,0.1)';\n const iconBorder = passed ? 'rgba(62,207,142,0.26)' : 'rgba(229,114,122,0.24)';\n const titleColor = passed ? '#f4f4f6' : '#cfcfd4';\n return `
\n ${passed ? checkIcon : xIcon}\n
\n
${esc(pretty(k))}
\n
${esc(desc)}
\n
\n
`;\n }).join('');\n\n const modal = document.createElement('div');\n modal.id = 'sb-security-report';\n modal.style.cssText = \"position:fixed; inset:0; z-index:10000; display:flex; align-items:center; justify-content:center; padding:24px; background:rgba(8,8,10,0.62); backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px); font-family:'Manrope',system-ui,-apple-system,sans-serif; overflow:auto;\";\n modal.innerHTML = `\n \n
\n \n
\n
\n
\n \n \n \n \n
\n ${score}\n / 100 pts\n
\n
\n
\n
Real-time security verification
\n
\n

Security level: ${esc(level)}

\n Active\n
\n
\n
Tests passed
${passedCount} / ${totalCount}
\n
Verified at
${esc(verifiedAt)}
\n
Source
${isReal ? 'Real cryptographic tests' : 'Simulated data'}
\n
\n
\n \n
\n
${testsHTML}
\n
\n \n ${isReal ? 'Real-time verification using actual cryptographic functions \u2014 no mock data.' : 'Warning: connection may not be fully established \u2014 values may be simulated.'}\n
\n
`;\n\n const onKey = (e) => { if (e.key === 'Escape') close(); };\n const close = () => { if (modal.parentNode) modal.remove(); document.removeEventListener('keydown', onKey); };\n modal.querySelector('.sv-close').addEventListener('click', close);\n modal.addEventListener('click', (e) => { if (e.target === modal) close(); });\n document.addEventListener('keydown', onKey);\n const rerun = modal.querySelector('.sv-rerun');\n rerun.addEventListener('mouseenter', () => { rerun.style.borderColor = 'rgba(240,137,42,0.45)'; rerun.style.color = '#f0892a'; });\n rerun.addEventListener('mouseleave', () => { rerun.style.borderColor = 'rgba(255,255,255,0.1)'; rerun.style.color = '#cfcfd4'; });\n rerun.addEventListener('click', () => { close(); runSecurityReport(webrtcManager); });\n document.body.appendChild(modal);\n };\n\n // In-chat header matching the SecureBit Chat design: logo + version,\n // a \"Secure\" pill (click = run the security verification report; the chevron\n // toggles the network/crypto detail panel), a connection indicator, Disconnect.\n const SecureBitChatHeader = ({ status, onDisconnect, webrtcManager }) => {\n const MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n const [showNetwork, setShowNetwork] = React.useState(false);\n const [sec, setSec] = React.useState(null);\n\n React.useEffect(() => {\n let alive = true;\n const fetchSec = async () => {\n try {\n if (!webrtcManager) return;\n let data = null;\n if (typeof webrtcManager.getRealSecurityLevel === 'function') data = await webrtcManager.getRealSecurityLevel();\n else if (typeof webrtcManager.calculateAndReportSecurityLevel === 'function') data = await webrtcManager.calculateAndReportSecurityLevel();\n else if (window.EnhancedSecureCryptoUtils) data = await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(webrtcManager);\n if (alive && data && data.isRealData !== false) setSec(data);\n } catch (e) { /* ignore */ }\n };\n fetchSec();\n const onCalc = (e) => { if (alive && e.detail && e.detail.securityData) setSec(e.detail.securityData); };\n document.addEventListener('real-security-calculated', onCalc);\n const iv = setInterval(fetchSec, 15000);\n return () => { alive = false; clearInterval(iv); document.removeEventListener('real-security-calculated', onCalc); };\n }, [webrtcManager]);\n\n const connected = status === 'connected' || status === 'verified';\n const passed = sec && Number.isFinite(sec.passedChecks) ? sec.passedChecks : null;\n const total = sec && Number.isFinite(sec.totalChecks) ? sec.totalChecks : null;\n const scoreLabel = (passed != null && total) ? (passed + '/' + total) : (sec ? (sec.score + '%') : '\u2014');\n const accent = sec\n ? (sec.color === 'green' ? '#3ecf8e' : sec.color === 'orange' ? '#f0892a' : sec.color === 'yellow' ? '#e3c84e' : '#e5727a')\n : '#3ecf8e';\n\n const secBtn = React.createElement('div', {\n key: 'sec', title: 'Run security verification',\n onClick: () => runSecurityReport(webrtcManager),\n className: 'sb-secpill',\n style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '7px 13px', borderRadius: '9px', border: '1px solid ' + (showNetwork ? 'rgba(255,255,255,0.16)' : 'rgba(255,255,255,0.07)'), background: showNetwork ? 'rgba(255,255,255,0.05)' : 'rgba(255,255,255,0.02)', cursor: 'pointer', fontFamily: 'inherit', transition: 'all .15s' }\n }, [\n React.createElement('i', { key: 'i', className: 'fas fa-shield-halved', style: { color: accent, fontSize: '13px' } }),\n React.createElement('span', { key: 'l', style: { fontSize: '13px', fontWeight: 600, color: '#e8e8eb' } }, sec ? (sec.level || 'Secure') : 'Secure'),\n React.createElement('span', { key: 'd', style: { width: '1px', height: '13px', background: 'rgba(255,255,255,0.12)' } }),\n React.createElement('span', { key: 's', style: { fontFamily: MONO, fontSize: '11.5px', fontWeight: 500, color: '#8a8a92' } }, scoreLabel),\n React.createElement('button', {\n key: 'c', type: 'button', title: 'Network & crypto details',\n onClick: (e) => { e.stopPropagation(); setShowNetwork(v => !v); },\n style: { background: 'none', border: 'none', padding: 0, margin: 0, cursor: 'pointer', display: 'grid', placeItems: 'center' }\n }, React.createElement('i', { className: 'fas fa-chevron-down', style: { color: '#6b6b73', fontSize: '11px', transform: showNetwork ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } }))\n ]);\n\n const header = React.createElement('header', {\n key: 'hdr', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '24px', padding: '0 20px', height: '64px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(18,18,20,0.72)', backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)' }\n }, [\n React.createElement('div', { key: 'left', style: { display: 'flex', alignItems: 'center', gap: '12px', minWidth: 0 } }, [\n React.createElement('div', { key: 'logo', style: { width: '36px', height: '36px', flex: 'none', display: 'grid', placeItems: 'center' } },\n React.createElement('img', { src: '/logo/securebit-mark.svg', alt: 'SecureBit', style: { width: '100%', height: '100%', objectFit: 'contain', display: 'block' } })\n ),\n React.createElement('div', { key: 'txt', style: { lineHeight: 1.2, minWidth: 0 } }, [\n React.createElement('div', { key: 'r1', style: { display: 'flex', alignItems: 'baseline', gap: '7px' } }, [\n React.createElement('span', { key: 'n', style: { fontSize: '16px', fontWeight: 800, letterSpacing: '-0.3px', color: '#e8e8eb' } }, 'SecureBit'),\n React.createElement('span', { key: 'v', style: { fontFamily: MONO, fontSize: '10px', fontWeight: 500, color: '#56565e' } }, 'v4.9.0')\n ]),\n React.createElement('div', { key: 'r2', style: { fontSize: '11px', color: '#6b6b73', fontWeight: 500 } }, 'End-to-end encrypted')\n ])\n ]),\n secBtn,\n React.createElement('div', { key: 'right', style: { display: 'flex', alignItems: 'center', gap: '9px' } }, [\n React.createElement('div', { key: 'conn', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 13px', borderRadius: '9px', border: '1px solid rgba(255,255,255,0.07)', background: 'rgba(255,255,255,0.02)' } }, [\n React.createElement('span', { key: 'dot', style: { width: '7px', height: '7px', borderRadius: '50%', background: connected ? '#3ecf8e' : '#e3c84e', boxShadow: connected ? '0 0 0 3px rgba(62,207,142,0.16)' : '0 0 0 3px rgba(227,200,78,0.16)' } }),\n React.createElement('span', { key: 't', style: { fontSize: '13px', fontWeight: 600, color: '#cfcfd4' } }, connected ? 'Connected' : 'Connecting\u2026')\n ]),\n React.createElement('button', { key: 'dc', onClick: onDisconnect, className: 'sb-disconnect', style: { display: 'flex', alignItems: 'center', gap: '7px', padding: '8px 14px', borderRadius: '9px', border: '1px solid rgba(255,255,255,0.08)', background: 'transparent', color: '#9a9aa2', fontFamily: 'inherit', fontSize: '13px', fontWeight: 600, cursor: 'pointer', transition: 'all .15s' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-power-off', style: { fontSize: '12px' } }),\n React.createElement('span', { key: 't', className: 'sb-hide-sm' }, 'Disconnect')\n ])\n ])\n ]);\n\n const netPanel = showNetwork && React.createElement('div', {\n key: 'net', style: { flex: 'none', padding: '13px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(18,18,20,0.72)', backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)' }\n }, React.createElement('div', { style: { maxWidth: '1000px', margin: '0 auto', display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(140px,1fr))', gap: '14px', fontFamily: MONO } },\n [\n ['Transport', 'WebRTC \u00B7 DTLS'],\n ['Cipher', 'AES-256-GCM'],\n ['Key exchange', 'ECDH P-384'],\n ['Security', scoreLabel + (sec ? (' \u00B7 ' + sec.score + '%') : '')]\n ].map(([k, v], i) => React.createElement('div', { key: 'nf' + i }, [\n React.createElement('div', { key: 'k', style: { fontSize: '10px', color: '#6b6b73', textTransform: 'uppercase', letterSpacing: '0.6px', marginBottom: '4px' } }, k),\n React.createElement('div', { key: 'v', style: { fontSize: '12.5px', color: i === 3 ? accent : '#cfcfd4', fontWeight: 500 } }, v)\n ]))\n ));\n\n return React.createElement('div', { style: { flex: 'none' } }, [header, netPanel]);\n };\n\n\n const EnhancedChatInterface = ({\n messages,\n messageInput,\n setMessageInput,\n onSendMessage,\n onDisconnect,\n keyFingerprint,\n isVerified,\n chatMessagesRef,\n scrollToBottom,\n webrtcManager,\n status,\n pendingIncomingFiles = [],\n onIncomingDecision,\n // Secure chat extras\n codeMode,\n setCodeMode,\n viewOnceMode,\n setViewOnceMode,\n viewOnceTtl,\n setViewOnceTtl,\n disappearTtl,\n setDisappearTtl,\n nowTick,\n onUnsendMessage,\n onMessageExpire\n }) => {\n const [showScrollButton, setShowScrollButton] = React.useState(false);\n const [showFileTransfer, setShowFileTransfer] = React.useState(false);\n // True only when the user opened the panel to SEND (shows the drop-zone).\n // Incoming-file auto-open leaves this false, so the receiver sees the\n // incoming request + receiving progress, not the \"send attachments\" UI.\n const [fileSendMode, setFileSendMode] = React.useState(false);\n const [showTimer, setShowTimer] = React.useState(false);\n const [showOnce, setShowOnce] = React.useState(false);\n const [showHandshake, setShowHandshake] = React.useState(false);\n const taRef = React.useRef(null);\n\n // Auto-grow the message textarea (and reset its height after sending).\n React.useEffect(() => {\n const el = taRef.current;\n if (!el || codeMode) return;\n el.style.height = 'auto';\n el.style.height = Math.min(el.scrollHeight, 240) + 'px';\n }, [messageInput, codeMode]);\n\n // Auto-open the file transfer panel when an incoming request arrives\n React.useEffect(() => {\n if (pendingIncomingFiles.length > 0) {\n setShowFileTransfer(true);\n }\n }, [pendingIncomingFiles.length]);\n\n React.useEffect(() => {\n if (chatMessagesRef.current && messages.length > 0) {\n const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;\n const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;\n if (isNearBottom) {\n const smoothScroll = () => {\n if (chatMessagesRef.current) {\n chatMessagesRef.current.scrollTo({\n top: chatMessagesRef.current.scrollHeight,\n behavior: 'smooth'\n });\n }\n };\n smoothScroll();\n setTimeout(smoothScroll, 50);\n setTimeout(smoothScroll, 150);\n }\n }\n }, [messages, chatMessagesRef]);\n\n const handleScroll = () => {\n if (chatMessagesRef.current) {\n const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;\n const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;\n setShowScrollButton(!isNearBottom);\n }\n };\n\n const handleScrollToBottom = () => {\n if (typeof scrollToBottom === 'function') {\n scrollToBottom();\n setShowScrollButton(false);\n } else if (chatMessagesRef.current) {\n chatMessagesRef.current.scrollTo({ top: chatMessagesRef.current.scrollHeight, behavior: 'smooth' });\n setShowScrollButton(false);\n }\n };\n\n const handleKeyPress = (e) => {\n if (e.key !== 'Enter') return;\n if (codeMode) {\n if (e.metaKey || e.ctrlKey) { e.preventDefault(); onSendMessage(); }\n } else if (!e.shiftKey) {\n e.preventDefault();\n onSendMessage();\n }\n };\n\n const isFileTransferReady = () => {\n if (!webrtcManager) return false;\n const connected = webrtcManager.isConnected ? webrtcManager.isConnected() : false;\n const verified = webrtcManager.isVerified || false;\n const hasDataChannel = webrtcManager.dataChannel && webrtcManager.dataChannel.readyState === 'open';\n return connected && verified && hasDataChannel;\n };\n\n // ---- design tokens / helpers ----\n const MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n const fmtShort = (s) => {\n if (!s) return '';\n if (s >= 86400 && s % 86400 === 0) return (s / 86400) + 'd';\n if (s >= 3600 && s % 3600 === 0) return (s / 3600) + 'h';\n if (s >= 60) return Math.round(s / 60) + 'm';\n return s + 's';\n };\n const chipStyle = (active) => ({\n display: 'flex', alignItems: 'center', gap: '6px', padding: '7px 11px', borderRadius: '8px',\n border: '1px solid ' + (active ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.07)'),\n background: active ? 'rgba(255,255,255,0.06)' : 'transparent',\n color: active ? '#fff' : '#9a9aa2',\n fontFamily: 'inherit', fontSize: '12.5px', fontWeight: 600, cursor: 'pointer', transition: 'all .15s'\n });\n const optStyle = (sel) => ({\n padding: '6px 12px', borderRadius: '8px',\n border: '1px solid ' + (sel ? 'rgba(255,255,255,0.22)' : 'rgba(255,255,255,0.07)'),\n background: sel ? 'rgba(255,255,255,0.07)' : 'transparent',\n color: sel ? '#fff' : '#8a8a92',\n fontFamily: MONO, fontSize: '12px', fontWeight: 500, cursor: 'pointer', transition: 'all .14s'\n });\n\n const timerDefs = [\n { label: 'Off', v: 0 }, { label: '5s', v: 5 }, { label: '30s', v: 30 },\n { label: '1m', v: 60 }, { label: '1h', v: 3600 }, { label: '24h', v: 86400 }\n ];\n const onceDefs = [\n { label: 'Off', v: 0 }, { label: '5s', v: 5 }, { label: '10s', v: 10 },\n { label: '30s', v: 30 }, { label: '1m', v: 60 }\n ];\n const onceSelected = viewOnceMode ? viewOnceTtl : 0;\n const pickTimer = (v) => { setDisappearTtl(v); setShowTimer(false); };\n const pickOnce = (v) => {\n if (v === 0) setViewOnceMode(false);\n else { setViewOnceTtl(v); setViewOnceMode(true); }\n setShowOnce(false);\n };\n\n const hasText = !!(messageInput && messageInput.trim());\n\n // System notices are surfaced inside the handshake/connection log card\n // (matching the design) rather than as bubbles in the message flow.\n const fmtT = (ts) => { try { return new Date(ts).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } catch (e) { return ''; } };\n // Connection log = string-only system notices (a React-element message\n // would render as \"[object Object]\" in the log, so it's excluded).\n const systemMessages = messages.filter((m) => m.type === 'system' && typeof m.message === 'string' && m.message.trim());\n const chatMessages = messages.filter((m) => m.type !== 'system');\n\n // ---- handshake / connection log card ----\n const handshakeCard = (isVerified || systemMessages.length > 0) && React.createElement('div', {\n key: 'handshake',\n style: { border: '1px solid rgba(255,255,255,0.07)', borderRadius: '12px', background: '#161618', overflow: 'hidden' }\n }, [\n React.createElement('button', {\n key: 'hs-btn', onClick: () => setShowHandshake(v => !v),\n style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '14px 16px', background: 'transparent', border: 'none', color: 'inherit', cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit' }\n }, [\n React.createElement('div', { key: 'ic', style: { flex: 'none', width: '30px', height: '30px', display: 'grid', placeItems: 'center' } },\n React.createElement('i', { className: 'fas fa-check', style: { color: '#3ecf8e', fontSize: '16px' } })\n ),\n React.createElement('div', { key: 'tx', style: { flex: 1, minWidth: 0 } }, [\n React.createElement('div', { key: 't1', style: { fontSize: '13.5px', fontWeight: 600, color: '#e8e8eb', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, 'Secure channel established'),\n React.createElement('div', { key: 't2', style: { fontSize: '12px', color: '#7b7b83', marginTop: '1px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, 'Verified \u00B7 Perfect Forward Secrecy' + (systemMessages.length ? (' \u00B7 ' + systemMessages.length + (systemMessages.length === 1 ? ' event' : ' events')) : ''))\n ]),\n React.createElement('i', { key: 'chev', className: 'fas fa-chevron-down', style: { flex: 'none', color: '#6b6b73', fontSize: '13px', transform: showHandshake ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } })\n ]),\n showHandshake && React.createElement('div', { key: 'hs-body', style: { padding: '2px 16px 14px 59px' } }, [\n systemMessages.length > 0 && React.createElement('div', { key: 'steps', className: 'sb-scroll', style: { marginBottom: '12px', maxHeight: '220px', overflowY: 'auto', paddingRight: '6px' } },\n systemMessages.map((m, i) => React.createElement('div', { key: 's' + i, style: { display: 'flex', gap: '11px', padding: '6px 0', borderTop: i === 0 ? 'none' : '1px solid rgba(255,255,255,0.04)' } }, [\n React.createElement('span', { key: 'd', style: { flex: 'none', width: '5px', height: '5px', borderRadius: '50%', background: '#3ecf8e', marginTop: '7px', opacity: 0.6 } }),\n React.createElement('span', { key: 't', style: { flex: 1, fontSize: '12.5px', color: '#9a9aa2', lineHeight: 1.5, wordBreak: 'break-word' } }, String(m.message || '').trim()),\n React.createElement('span', { key: 'tm', style: { flex: 'none', fontFamily: MONO, fontSize: '10.5px', color: '#56565e' } }, fmtT(m.timestamp))\n ]))\n ),\n keyFingerprint && React.createElement('div', { key: 'sn', style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '10px 12px', borderRadius: '9px', background: 'rgba(255,255,255,0.025)', border: '1px solid rgba(255,255,255,0.06)' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-lock', style: { color: '#8a8a92', fontSize: '12px' } }),\n React.createElement('span', { key: 'l', style: { fontSize: '11.5px', color: '#8a8a92' } }, 'Safety number'),\n React.createElement('span', { key: 'v', style: { fontFamily: MONO, fontSize: '12px', color: '#cfcfd4', letterSpacing: '0.8px', fontWeight: 500, wordBreak: 'break-all' } }, keyFingerprint)\n ])\n ])\n ]);\n\n // ---- empty state ----\n const emptyState = React.createElement('div', { key: 'empty', style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1, minHeight: '40vh' } },\n React.createElement('div', { style: { textAlign: 'center', maxWidth: '420px' } }, [\n React.createElement('img', { key: 'ic', src: '/logo/securebit-mark.svg', alt: 'SecureBit', style: { width: '60px', height: '60px', objectFit: 'contain', display: 'block', margin: '0 auto 16px' } }),\n React.createElement('h3', { key: 't', style: { fontSize: '17px', fontWeight: 700, color: '#e8e8eb', margin: '0 0 6px' } }, 'Secure channel is ready'),\n React.createElement('p', { key: 'p', style: { fontSize: '13px', color: '#7b7b83', margin: 0 } }, 'Every message is end-to-end encrypted on your device before it leaves.')\n ])\n );\n\n // ---- messages list ----\n const messagesArea = React.createElement('main', {\n key: 'main',\n ref: chatMessagesRef,\n onScroll: handleScroll,\n className: 'sb-scroll',\n style: { flex: 1, overflowY: 'auto', padding: '20px 20px 22px' }\n }, React.createElement('div', { style: { width: '100%', maxWidth: '1000px', margin: '0 auto', display: 'flex', flexDirection: 'column', gap: '16px', minHeight: '100%' } },\n chatMessages.length === 0\n ? [handshakeCard, emptyState]\n : [handshakeCard].concat(chatMessages.map((msg) => React.createElement(EnhancedChatMessage, {\n key: msg.id,\n message: msg.message,\n type: msg.type,\n timestamp: msg.timestamp,\n mid: msg.mid,\n status: msg.status,\n viewOnce: msg.viewOnce,\n viewOnceTtl: msg.viewOnceTtl,\n expiresAt: msg.expiresAt,\n expired: msg.expired,\n nowTick: nowTick,\n canUnsend: typeof onUnsendMessage === 'function',\n onUnsend: onUnsendMessage,\n onExpire: () => onMessageExpire && onMessageExpire(msg.id)\n })))\n ));\n\n // ---- option rows ----\n const timerRow = showTimer && React.createElement('div', { key: 'timer-row', style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '8px', padding: '10px 12px', marginBottom: '10px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.07)', background: '#161618' } },\n [React.createElement('span', { key: 'lbl', style: { fontSize: '12px', color: '#8a8a92', fontWeight: 600, marginRight: '4px' } }, 'Disappear after')].concat(\n timerDefs.map((d) => React.createElement('button', { key: 'td' + d.v, onClick: () => pickTimer(d.v), style: optStyle(disappearTtl === d.v) }, d.label))\n )\n );\n const onceRow = showOnce && React.createElement('div', { key: 'once-row', style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '8px', padding: '10px 12px', marginBottom: '10px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.07)', background: '#161618' } },\n [React.createElement('span', { key: 'lbl', style: { fontSize: '12px', color: '#8a8a92', fontWeight: 600, marginRight: '4px' } }, 'Visible for')].concat(\n onceDefs.map((d) => React.createElement('button', { key: 'od' + d.v, onClick: () => pickOnce(d.v), style: optStyle(onceSelected === d.v) }, d.label))\n )\n );\n\n // ---- file transfer panel ----\n const filePanel = showFileTransfer && React.createElement('div', { key: 'file-panel', style: { marginBottom: '10px' } },\n React.createElement(window.FileTransferComponent || (() => React.createElement('div', { style: { padding: '16px', textAlign: 'center', color: '#e5727a' } }, 'FileTransferComponent not loaded')), {\n webrtcManager: webrtcManager,\n isConnected: isFileTransferReady(),\n pendingIncomingFiles: pendingIncomingFiles,\n onIncomingDecision: onIncomingDecision,\n showDropzone: fileSendMode\n })\n );\n\n // ---- chips row ----\n const chipsRow = React.createElement('div', { key: 'chips', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '8px', marginBottom: '10px' } }, [\n React.createElement('button', { key: 'files', onClick: () => {\n if (showFileTransfer && fileSendMode) { setShowFileTransfer(false); setFileSendMode(false); }\n else { setShowFileTransfer(true); setFileSendMode(true); }\n }, className: 'sb-chip', style: chipStyle(showFileTransfer && fileSendMode) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-paperclip', style: { fontSize: '13px' } }),\n (showFileTransfer && fileSendMode) ? 'Hide files' : 'Send files'\n ]),\n React.createElement('div', { key: 'right', style: { display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'wrap' } }, [\n React.createElement('button', { key: 'code', onClick: () => setCodeMode(v => !v), className: 'sb-chip', style: chipStyle(codeMode) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-code', style: { fontSize: '13px' } }), 'Code'\n ]),\n React.createElement('button', { key: 'once', onClick: () => { setShowOnce(v => !v); setShowTimer(false); }, className: 'sb-chip', style: chipStyle(showOnce || viewOnceMode) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-eye-slash', style: { fontSize: '13px' } }),\n viewOnceMode ? ('View once \u00B7 ' + fmtShort(viewOnceTtl)) : 'View once'\n ]),\n React.createElement('button', { key: 'timer', onClick: () => { setShowTimer(v => !v); setShowOnce(false); }, className: 'sb-chip', style: chipStyle(showTimer || disappearTtl > 0) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-stopwatch', style: { fontSize: '13px' } }),\n disappearTtl > 0 ? ('Timer \u00B7 ' + fmtShort(disappearTtl)) : 'Timer'\n ])\n ])\n ]);\n\n // ---- code-mode header strip ----\n const codeStrip = codeMode && React.createElement('div', { key: 'code-strip', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 14px', border: '1px solid rgba(255,255,255,0.08)', borderBottom: 'none', borderRadius: '14px 14px 0 0', background: '#161618' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-code', style: { color: '#8a8a92', fontSize: '12px' } }),\n React.createElement('span', { key: 's', style: { fontSize: '11.5px', fontWeight: 600, color: '#8a8a92' } }, 'Code snippet \u00B7 formatting preserved \u00B7 \u2318\u21B5 to send'),\n React.createElement('button', { key: 'c', onClick: () => setCodeMode(false), className: 'sb-link', style: { marginLeft: 'auto', background: 'none', border: 'none', color: '#6b6b73', cursor: 'pointer', fontSize: '11.5px', fontFamily: 'inherit', fontWeight: 600 } }, 'Close')\n ]);\n\n // ---- input row ----\n const inputRow = React.createElement('div', {\n key: 'input',\n style: { display: 'flex', alignItems: 'flex-end', gap: '11px', padding: '11px 11px 11px 16px', border: '1px solid ' + (hasText ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.08)'), background: '#161618', borderRadius: codeMode ? '0 0 14px 14px' : '14px', transition: 'border .15s' }\n }, [\n React.createElement('div', { key: 'ta-wrap', style: { flex: 1, minWidth: 0 } }, [\n React.createElement('textarea', {\n key: 'ta',\n value: messageInput,\n ref: taRef,\n onChange: (e) => setMessageInput(e.target.value),\n onKeyDown: handleKeyPress,\n rows: 1,\n maxLength: 2000,\n placeholder: codeMode ? 'Paste or write code\u2026' : 'Type an encrypted message\u2026',\n className: 'sb-textarea',\n style: { width: '100%', minHeight: codeMode ? '120px' : '22px', maxHeight: '240px', resize: 'none', border: 'none', outline: 'none', background: 'transparent', color: '#e8e8eb', fontFamily: codeMode ? MONO : 'inherit', fontSize: codeMode ? '13px' : '14.5px', lineHeight: 1.55, padding: '6px 0' }\n }),\n React.createElement('div', { key: 'foot', style: { display: 'flex', alignItems: 'center', gap: '12px', marginTop: '3px' } }, [\n React.createElement('span', { key: 'enc', style: { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '11px', color: '#56565e' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-lock', style: { color: '#3ecf8e', fontSize: '10px' } }),\n 'Encrypted on your device'\n ]),\n React.createElement('span', { key: 'cnt', style: { fontFamily: MONO, fontSize: '10.5px', color: '#56565e', marginLeft: 'auto' } }, (messageInput ? messageInput.length : 0) + '/2000')\n ])\n ]),\n React.createElement('button', {\n key: 'send', onClick: onSendMessage, disabled: !hasText, title: 'Send', className: 'sb-send',\n style: { flex: 'none', width: '44px', height: '44px', borderRadius: '11px', border: 'none', display: 'grid', placeItems: 'center', cursor: hasText ? 'pointer' : 'default', background: hasText ? '#f0892a' : 'rgba(255,255,255,0.05)', color: hasText ? '#1a0f04' : '#56565e', transition: 'all .15s' }\n }, React.createElement('i', { className: 'fas fa-paper-plane', style: { fontSize: '15px' } }))\n ]);\n\n const composer = React.createElement('footer', { key: 'composer', style: { flex: 'none', padding: '12px 20px 18px', background: '#0f0f11', borderTop: '1px solid rgba(255,255,255,0.05)' } },\n React.createElement('div', { style: { maxWidth: '1000px', margin: '0 auto' } }, [\n timerRow, onceRow, filePanel, chipsRow, codeStrip, inputRow\n ])\n );\n\n const scrollBtn = showScrollButton && React.createElement('button', {\n key: 'scrollbtn', onClick: handleScrollToBottom,\n style: { position: 'fixed', right: '24px', bottom: '150px', width: '44px', height: '44px', borderRadius: '50%', border: '1px solid rgba(255,255,255,0.1)', background: '#26262b', color: '#cfcfd4', display: 'grid', placeItems: 'center', cursor: 'pointer', zIndex: 50, boxShadow: '0 6px 20px rgba(0,0,0,0.4)' }\n }, React.createElement('i', { className: 'fas fa-arrow-down', style: { fontSize: '15px' } }));\n\n const chatHeader = React.createElement(SecureBitChatHeader, {\n key: 'chat-header', status: status, onDisconnect: onDisconnect, webrtcManager: webrtcManager\n });\n\n return React.createElement('div', {\n className: 'chat-container',\n style: { display: 'flex', flexDirection: 'column', height: '100vh', background: '#0f0f11', color: '#e8e8eb' }\n }, [chatHeader, messagesArea, scrollBtn, composer]);\n };\n \n \n // Main Enhanced Application Component\n const EnhancedSecureP2PChat = () => {\n\n const [messages, setMessages] = React.useState([]);\n // Secure chat extras: per-message send modes + 1s tick for countdowns.\n const [codeMode, setCodeMode] = React.useState(false);\n const [viewOnceMode, setViewOnceMode] = React.useState(false);\n const [viewOnceTtl, setViewOnceTtl] = React.useState(15); // seconds visible after the peer opens it\n const [disappearTtl, setDisappearTtl] = React.useState(0); // seconds; 0 = off (sticky)\n const [nowTick, setNowTick] = React.useState(() => Date.now());\n const [connectionStatus, setConnectionStatus] = React.useState('disconnected');\n // Offline awareness \u2014 tracks the real online/offline events (which is\n // also what a console-simulated `dispatchEvent(new Event('offline'))`\n // fires, even when navigator.onLine stays true).\n const [isOffline, setIsOffline] = React.useState(typeof navigator !== 'undefined' && navigator.onLine === false);\n // Ref mirror so manager callbacks (which close over stale state) always\n // read the current offline status. Two queues implement store-and-forward\n // over the still-live P2P channel: outgoing waits until WE reconnect,\n // incoming waits until WE reconnect before being shown/acked.\n const offlineRef = React.useRef(isOffline);\n const outgoingQueueRef = React.useRef([]);\n const incomingQueueRef = React.useRef([]);\n React.useEffect(() => { offlineRef.current = isOffline; }, [isOffline]);\n React.useEffect(() => {\n const goOffline = () => setIsOffline(true);\n const goOnline = () => setIsOffline(false);\n window.addEventListener('offline', goOffline);\n window.addEventListener('online', goOnline);\n return () => { window.removeEventListener('offline', goOffline); window.removeEventListener('online', goOnline); };\n }, []);\n const [relayOnlyMode, setRelayOnlyMode] = React.useState(() => {\n try { return localStorage.getItem('securebit_relay_only_mode') === 'true'; } catch { return false; }\n });\n // Custom ICE (STUN/TURN) servers \u2014 advanced network settings.\n const [customIceServers, setCustomIceServers] = React.useState(null); // null => use public defaults\n const [iceServersText, setIceServersText] = React.useState('');\n const [iceSettingsPersisted, setIceSettingsPersisted] = React.useState(false);\n const [showIceSettings, setShowIceSettings] = React.useState(false);\n\n // Load any previously saved (encrypted) custom ICE settings on mount.\n React.useEffect(() => {\n let cancelled = false;\n loadIceSettings().then((saved) => {\n if (cancelled || !saved) return;\n if (Array.isArray(saved.servers) && saved.servers.length > 0) {\n setCustomIceServers(saved.servers);\n setIceServersText(JSON.stringify(saved.servers, null, 2));\n }\n if (saved.privacyMode === 'relay-only') {\n setRelayOnlyMode(true);\n }\n setIceSettingsPersisted(true);\n }).catch(() => { /* fail closed: keep defaults */ });\n return () => { cancelled = true; };\n }, []);\n\n // Global entry point: the header gear dispatches this event.\n React.useEffect(() => {\n const open = () => setShowIceSettings(true);\n window.addEventListener('securebit:open-network-settings', open);\n return () => window.removeEventListener('securebit:open-network-settings', open);\n }, []);\n\n const handleApplyIceSettings = React.useCallback((next, persist) => {\n const servers = next.useCustom && Array.isArray(next.servers) ? next.servers : null;\n setCustomIceServers(servers && servers.length ? servers : null);\n setIceServersText(next.serversText || '');\n setRelayOnlyMode(next.privacyMode === 'relay-only');\n setShowIceSettings(false);\n if (persist) {\n setIceSettingsPersisted(true);\n saveIceSettings({ servers: servers || [], privacyMode: next.privacyMode }).catch(() => { /* surfaced as no-op */ });\n } else if (iceSettingsPersisted) {\n // User turned persistence off \u2014 remove the stored copy.\n setIceSettingsPersisted(false);\n clearIceSettings().catch(() => {});\n }\n }, [iceSettingsPersisted]);\n\n const handleForgetIceSettings = React.useCallback(async () => {\n await clearIceSettings().catch(() => {});\n setIceSettingsPersisted(false);\n setCustomIceServers(null);\n setIceServersText('');\n }, []);\n \n // Moved scrollToBottom logic to be available globally\n const [messageInput, setMessageInput] = React.useState('');\n const [offerData, setOfferData] = React.useState('');\n const [answerData, setAnswerData] = React.useState('');\n const [offerInput, setOfferInput] = React.useState('');\n const [answerInput, setAnswerInput] = React.useState('');\n const [keyFingerprint, setKeyFingerprint] = React.useState('');\n const [verificationCode, setVerificationCode] = React.useState('');\n const [showOfferStep, setShowOfferStep] = React.useState(false);\n const [showAnswerStep, setShowAnswerStep] = React.useState(false);\n const [showVerification, setShowVerification] = React.useState(false);\n const [showQRCode, setShowQRCode] = React.useState(false);\n const [qrCodeUrl, setQrCodeUrl] = React.useState('');\n const [showQRScanner, setShowQRScanner] = React.useState(false);\n const [showQRScannerModal, setShowQRScannerModal] = React.useState(false);\n const [isGeneratingKeys, setIsGeneratingKeys] = React.useState(false);\n \n\n const [isVerified, setIsVerified] = React.useState(false);\n const [securityLevel, setSecurityLevel] = React.useState(null);\n const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);\n \n // Mutual verification states\n const [localVerificationConfirmed, setLocalVerificationConfirmed] = React.useState(false);\n const [remoteVerificationConfirmed, setRemoteVerificationConfirmed] = React.useState(false);\n const [bothVerificationsConfirmed, setBothVerificationsConfirmed] = React.useState(false);\n \n // PAKE password states removed - using SAS verification instead\n \n // Session state - all security features enabled by default\n const [pendingSession, setPendingSession] = React.useState(null);\n \n // All security features are enabled by default - no payment required\n\n const [pendingIncomingFiles, setPendingIncomingFiles] = React.useState([]);\n\n\n\n // ============================================\n // CENTRALIZED CONNECTION STATE MANAGEMENT\n // ============================================\n \n const [connectionState, setConnectionState] = React.useState({\n status: 'disconnected',\n hasActiveAnswer: false,\n answerCreatedAt: null,\n isUserInitiatedDisconnect: false\n });\n \n // Centralized connection state handler\n const updateConnectionState = (newState, options = {}) => {\n const { preserveAnswer = false, isUserAction = false } = options;\n \n setConnectionState(prev => ({\n ...prev,\n ...newState,\n isUserInitiatedDisconnect: isUserAction,\n hasActiveAnswer: preserveAnswer ? prev.hasActiveAnswer : false,\n answerCreatedAt: preserveAnswer ? prev.answerCreatedAt : null\n }));\n };\n \n // Check if we should preserve answer data\n const shouldPreserveAnswerData = () => {\n const hasAnswerData = !!answerData || \n (answerInput && typeof answerInput === 'string' && answerInput.trim().length > 0);\n\n const hasAnswerQR = qrCodeUrl && typeof qrCodeUrl === 'string' && qrCodeUrl.trim().length > 0;\n \n const shouldPreserve = (connectionState.hasActiveAnswer && \n !connectionState.isUserInitiatedDisconnect) ||\n (hasAnswerData && \n !connectionState.isUserInitiatedDisconnect) ||\n (hasAnswerQR && \n !connectionState.isUserInitiatedDisconnect);\n \n \n return shouldPreserve;\n };\n \n // Mark answer as created\n const markAnswerCreated = () => {\n updateConnectionState({\n hasActiveAnswer: true,\n answerCreatedAt: Date.now()\n });\n };\n \n const webrtcManagerRef = React.useRef(null);\n const notificationIntegrationRef = React.useRef(null);\n\n // Development-only debug helpers. Production never exposes\n // manager internals or cleanup controls on `window`.\n React.useEffect(() => {\n return installDebugWindowHooks({\n targetWindow: window,\n webrtcManagerRef,\n onClearData: handleClearData\n });\n }, []);\n \n const addMessageWithAutoScroll = React.useCallback((message, type, opts = {}) => {\n const newMessage = {\n message,\n type,\n id: Date.now() + Math.random(),\n timestamp: (typeof opts.timestamp === 'number') ? opts.timestamp : Date.now(),\n mid: opts.mid,\n status: opts.status, // WhatsApp-style: sending | sent | delivered | failed\n viewOnce: opts.viewOnce === true,\n viewOnceTtl: (typeof opts.viewOnceTtl === 'number') ? opts.viewOnceTtl : 15,\n expiresAt: (typeof opts.expiresAt === 'number') ? opts.expiresAt : undefined\n };\n\n setMessages(prev => {\n const updated = [...prev, newMessage];\n \n setTimeout(() => {\n if (chatMessagesRef?.current) {\n const container = chatMessagesRef.current;\n try {\n const { scrollTop, scrollHeight, clientHeight } = container;\n const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;\n \n if (isNearBottom || prev.length === 0) {\n requestAnimationFrame(() => {\n if (container && container.scrollTo) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'smooth'\n });\n }\n });\n }\n } catch (error) {\n console.warn('Scroll error:', error);\n container.scrollTop = container.scrollHeight;\n }\n }\n }, 50);\n \n return updated;\n });\n }, []);\n\n // Flip a sent message's delivery state (sending \u2192 sent \u2192 delivered, or failed).\n const updateMessageStatus = React.useCallback((mid, status) => {\n if (!mid) return;\n setMessages(prev => prev.map(m => (String(m.mid) === String(mid) && m.type === 'sent') ? { ...m, status } : m));\n }, []);\n\n // When WE come back online: transmit anything we queued while offline,\n // and surface (and acknowledge) anything that arrived while we were offline.\n const flushOfflineQueues = React.useCallback(() => {\n const out = outgoingQueueRef.current;\n outgoingQueueRef.current = [];\n for (const item of out) {\n const send = webrtcManagerRef.current?.sendMessage?.(item.outText, item.meta);\n if (send && typeof send.then === 'function') {\n send.then(() => updateMessageStatus(item.mid, 'sent')).catch(() => updateMessageStatus(item.mid, 'failed'));\n }\n }\n const inc = incomingQueueRef.current;\n incomingQueueRef.current = [];\n if (inc.length > 0) {\n addMessageWithAutoScroll(\n `Connection restored \u2014 ${inc.length} message${inc.length === 1 ? '' : 's'} received while you were offline.`,\n 'notice'\n );\n }\n for (const item of inc) {\n addMessageWithAutoScroll(item.message, item.type, item.opts);\n if (item.opts && item.opts.mid && (item.type === 'received' || item.type === 'sent')) {\n try { webrtcManagerRef.current?.sendDeliveryReceipt?.(item.opts.mid); } catch (_) {}\n }\n }\n }, [addMessageWithAutoScroll, updateMessageStatus]);\n\n React.useEffect(() => {\n if (isOffline) return; // only act on the offline \u2192 online edge\n flushOfflineQueues();\n }, [isOffline, flushOfflineQueues]);\n\n // Update security level based on real verification\n const updateSecurityLevel = React.useCallback(async () => {\n if (window.isUpdatingSecurity) {\n return;\n }\n \n window.isUpdatingSecurity = true;\n \n try {\n if (webrtcManagerRef.current) {\n // All security features are enabled by default - always show MAXIMUM level\n setSecurityLevel({\n level: 'MAXIMUM',\n score: 100,\n color: 'green',\n details: 'All security features enabled by default',\n passedChecks: 10,\n totalChecks: 10,\n isRealData: true\n });\n \n if (window.DEBUG_MODE) {\n const currentLevel = webrtcManagerRef.current.ecdhKeyPair && webrtcManagerRef.current.ecdsaKeyPair \n ? await webrtcManagerRef.current.calculateSecurityLevel()\n : {\n level: 'MAXIMUM',\n score: 100,\n sessionType: 'premium',\n passedChecks: 10,\n totalChecks: 10\n };\n }\n }\n } catch (error) {\n console.error('Failed to update security level:', error);\n setSecurityLevel({\n level: 'ERROR',\n score: 0,\n color: 'red',\n details: 'Verification failed'\n });\n } finally {\n setTimeout(() => {\n window.isUpdatingSecurity = false;\n }, 2000);\n }\n }, []);\n \n // Session time ticker removed - sessions are unlimited\n \n // Sessions are unlimited - no expiration handler needed\n \n // All security features are enabled by default - no demo sessions needed\n const chatMessagesRef = React.useRef(null);\n \n // Create scroll function using global helper\n const scrollToBottom = createScrollToBottomFunction(chatMessagesRef);\n\n React.useEffect(() => {\n try { localStorage.setItem('securebit_relay_only_mode', String(relayOnlyMode)); } catch {}\n if (webrtcManagerRef.current?._config?.webrtc) {\n webrtcManagerRef.current._setRelayOnlyMode(relayOnlyMode);\n }\n }, [relayOnlyMode]);\n \n // Auto-scroll when messages change\n React.useEffect(() => {\n if (messages.length > 0 && chatMessagesRef.current) {\n scrollToBottom();\n setTimeout(scrollToBottom, 50);\n setTimeout(scrollToBottom, 150);\n }\n }, [messages]);\n\n // Disappearing-message clock: tick every second (only while some\n // message has an expiry) and prune anything past its deadline.\n const hasExpiring = messages.some(m => typeof m.expiresAt === 'number');\n React.useEffect(() => {\n if (!hasExpiring) return;\n const interval = setInterval(() => {\n const now = Date.now();\n setNowTick(now);\n setMessages(prev => {\n // Disappearing messages leave a tombstone (\"This message has\n // expired\") instead of vanishing; the content is wiped.\n let changed = false;\n const next = prev.map(m => {\n if (typeof m.expiresAt === 'number' && m.expiresAt <= now && !m.expired) {\n changed = true;\n return { ...m, expired: true, message: '', expiresAt: undefined };\n }\n return m;\n });\n return changed ? next : prev;\n });\n }, 1000);\n return () => clearInterval(interval);\n }, [hasExpiring]);\n \n // PAKE password functions removed - using SAS verification instead\n \n React.useEffect(() => {\n // Prevent multiple initializations\n if (webrtcManagerRef.current) {\n console.log('\u26A0\uFE0F WebRTC Manager already initialized, skipping...');\n return;\n }\n \n const handleMessage = (message, type, meta) => {\n if (typeof message === 'string' && message.trim().startsWith('{')) {\n try {\n const parsedMessage = JSON.parse(message);\n const blockedTypes = [\n 'file_transfer_start',\n 'file_transfer_response',\n 'file_chunk',\n 'chunk_confirmation',\n 'file_transfer_complete',\n 'file_transfer_error',\n 'heartbeat',\n 'verification',\n 'verification_response',\n 'verification_confirmed',\n 'verification_both_confirmed',\n 'peer_disconnect',\n 'key_rotation_signal',\n 'key_rotation_ready',\n 'security_upgrade',\n 'message_delete',\n 'message_receipt'\n ];\n if (parsedMessage.type && blockedTypes.includes(parsedMessage.type)) {\n console.log(`Blocked system/file message from chat: ${parsedMessage.type}`);\n return; \n }\n } catch (parseError) {\n\n }\n }\n \n // Per-message UI metadata from the peer (view-once / disappearing / id).\n const opts = {};\n if (meta && typeof meta === 'object') {\n if (typeof meta.mid === 'string') opts.mid = meta.mid;\n if (meta.once === true) {\n opts.viewOnce = true;\n opts.viewOnceTtl = Number.isFinite(meta.onceTtl) ? meta.onceTtl : 15;\n }\n if (Number.isFinite(meta.ttl) && meta.ttl > 0) {\n opts.expiresAt = Date.now() + meta.ttl * 1000;\n }\n if (Number.isFinite(meta.ts)) opts.timestamp = meta.ts;\n }\n\n // If WE are offline, hold the peer's message back: don't show it\n // and don't acknowledge it yet, so the sender stays at one check.\n // It's surfaced (and acked \u2192 \u2713\u2713) the moment we reconnect.\n if (offlineRef.current && type === 'received') {\n incomingQueueRef.current.push({ message, type, opts });\n return;\n }\n\n addMessageWithAutoScroll(message, type, opts);\n\n // Acknowledge receipt so the sender's bubble shows \"delivered\" (\u2713\u2713).\n if (opts.mid && (type === 'received' || type === 'sent')) {\n try { webrtcManagerRef.current?.sendDeliveryReceipt?.(opts.mid); } catch (_) {}\n }\n };\n\n const handleStatusChange = (status) => {\n setConnectionStatus(status);\n \n if (status === 'connected') {\n document.dispatchEvent(new CustomEvent('new-connection'));\n \n // \u041D\u0435 \u0441\u043A\u0440\u044B\u0432\u0430\u0435\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E \u043F\u0440\u0438 'connected' - \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0440\u0438 'verified'\n // setIsVerified(true);\n // setShowVerification(false);\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'verifying') {\n setShowVerification(true);\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'verified') {\n setIsVerified(true);\n setShowVerification(false);\n setBothVerificationsConfirmed(true);\n setConnectionStatus('connected');\n // Force immediate update of isVerified state\n setTimeout(() => {\n setIsVerified(true);\n }, 0);\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'connecting') {\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'disconnected') {\n updateConnectionState({ status: 'disconnected' });\n setConnectionStatus('disconnected');\n\n if (shouldPreserveAnswerData()) {\n setIsVerified(false);\n setShowVerification(false);\n return;\n }\n\n setIsVerified(false);\n setShowVerification(false);\n \n // Dispatch disconnected event for SessionTimer\n document.dispatchEvent(new CustomEvent('disconnected'));\n \n // Clear verification states\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n \n // Clear connection data\n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setShowAnswerStep(false);\n setKeyFingerprint('');\n setVerificationCode('');\n setSecurityLevel(null);\n \n \n // Return to main page after a short delay\n setTimeout(() => {\n setConnectionStatus('disconnected');\n setShowVerification(false);\n \n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setShowAnswerStep(false);\n setMessages([]);\n }, 1000);\n\n } else if (status === 'peer_disconnected') {\n setSessionTimeLeft(0);\n \n document.dispatchEvent(new CustomEvent('peer-disconnect'));\n \n // A short delay before clearing to display the status\n setTimeout(() => {\n setKeyFingerprint('');\n setVerificationCode('');\n setSecurityLevel(null);\n setIsVerified(false);\n setShowVerification(false);\n setConnectionStatus('disconnected');\n \n // Clear verification states\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n \n // Clear connection data\n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setShowAnswerStep(false);\n setMessages([]);\n \n\n if (typeof console.clear === 'function') {\n console.clear();\n }\n\n }, 2000);\n }\n };\n \n const handleKeyExchange = (fingerprint) => {\n if (fingerprint === '') {\n setKeyFingerprint('');\n } else {\n setKeyFingerprint(fingerprint);\n }\n };\n \n const handleVerificationRequired = (code) => {\n if (code === '') {\n setVerificationCode('');\n setShowVerification(false);\n } else {\n setVerificationCode(code);\n setShowVerification(true);\n }\n };\n \n const handleVerificationStateChange = (state) => {\n setLocalVerificationConfirmed(state.localConfirmed);\n setRemoteVerificationConfirmed(state.remoteConfirmed);\n setBothVerificationsConfirmed(state.bothConfirmed);\n };\n \n // Callback for handling response errors\n const handleAnswerError = (errorType, errorMessage) => {\n if (errorType === 'replay_attack') {\n // Reset the session upon replay attack\n setSessionTimeLeft(0);\n setPendingSession(null);\n \n addMessageWithAutoScroll('\uD83D\uDCA1 Data is outdated. Please create a new invitation or use a current response code.', 'system');\n \n if (typeof console.clear === 'function') {\n console.clear();\n }\n } else if (errorType === 'security_violation') {\n // Reset the session upon security breach\n setSessionTimeLeft(0);\n setPendingSession(null);\n \n addMessageWithAutoScroll(` Security breach: ${errorMessage}`, 'system');\n \n if (typeof console.clear === 'function') {\n console.clear();\n }\n }\n };\n \n \n if (typeof console.clear === 'function') {\n console.clear();\n }\n \n webrtcManagerRef.current = new EnhancedSecureWebRTCManager(\n handleMessage, \n handleStatusChange, \n handleKeyExchange,\n handleVerificationRequired,\n handleAnswerError,\n handleVerificationStateChange,\n {\n webrtc: {\n relayOnly: relayOnlyMode,\n // Priority: user's custom servers > operator override > built-in defaults.\n iceServers: (Array.isArray(customIceServers) && customIceServers.length)\n ? customIceServers\n : (Array.isArray(window.SECUREBIT_ICE_SERVERS) ? window.SECUREBIT_ICE_SERVERS : undefined)\n }\n }\n );\n\n // Unsend / delete-for-everyone: peer asked us to drop a message.\n webrtcManagerRef.current.onMessageDelete = (mid) => {\n if (!mid) return;\n setMessages(prev => prev.filter(m => String(m.mid) !== String(mid)));\n };\n\n // Delivery receipt: peer confirmed they got our message \u2192 \"delivered\".\n webrtcManagerRef.current.onMessageDelivered = (mid) => {\n updateMessageStatus(mid, 'delivered');\n };\n\n // Initialize notification integration if permission was already granted\n if (typeof Notification !== 'undefined' && Notification && Notification.permission === 'granted' && window.NotificationIntegration && !notificationIntegrationRef.current) {\n try {\n const integration = new window.NotificationIntegration(webrtcManagerRef.current);\n integration.init().then(() => {\n notificationIntegrationRef.current = integration;\n }).catch((error) => {\n // Handle error silently\n });\n } catch (error) {\n // Handle error silently\n }\n }\n \n handleMessage(' SecureBit.chat Enhanced Security Edition v4.9.0 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');\n \n const handleBeforeUnload = (event) => {\n if (event.type === 'beforeunload' && !isTabSwitching) {\n \n if (webrtcManagerRef.current && webrtcManagerRef.current.isConnected()) {\n try {\n webrtcManagerRef.current.sendSystemMessage({\n type: 'peer_disconnect',\n reason: 'user_disconnect',\n timestamp: Date.now()\n });\n } catch (error) {\n }\n \n setTimeout(() => {\n if (webrtcManagerRef.current) {\n webrtcManagerRef.current.disconnect();\n }\n }, 100);\n } else if (webrtcManagerRef.current) {\n webrtcManagerRef.current.disconnect();\n }\n } else if (isTabSwitching) {\n event.preventDefault();\n event.returnValue = '';\n }\n };\n \n window.addEventListener('beforeunload', handleBeforeUnload);\n \n let isTabSwitching = false;\n let tabSwitchTimeout = null;\n \n const handleVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n isTabSwitching = true;\n \n if (tabSwitchTimeout) {\n clearTimeout(tabSwitchTimeout);\n }\n \n tabSwitchTimeout = setTimeout(() => {\n isTabSwitching = false;\n }, 5000); \n \n } else if (document.visibilityState === 'visible') {\n isTabSwitching = false;\n \n if (tabSwitchTimeout) {\n clearTimeout(tabSwitchTimeout);\n tabSwitchTimeout = null;\n }\n }\n };\n \n document.addEventListener('visibilitychange', handleVisibilityChange);\n \n // Setup file transfer callbacks\n if (webrtcManagerRef.current) {\n webrtcManagerRef.current.setFileTransferCallbacks(\n // Progress callback\n (progress) => {\n console.log('File progress:', progress);\n },\n \n // File received callback \u2014 auto-save to disk, no button press needed.\n (fileData) => {\n const sizeMb = Math.max(1, Math.round((fileData.fileSize || 0) / (1024 * 1024)));\n\n const saveToDisk = async () => {\n const url = await fileData.getObjectURL();\n const a = document.createElement('a');\n a.href = url;\n a.download = fileData.fileName || 'file';\n document.body.appendChild(a);\n a.click();\n a.remove();\n setTimeout(() => fileData.revokeObjectURL(url), 15000);\n };\n\n // Trigger the download automatically once assembly completes.\n // (Plain-string notice \u2014 system messages render as text in the\n // connection log, so a React button here would show \"[object\n // Object]\". The file panel keeps a manual Download as fallback.)\n saveToDisk()\n .then(() => {\n addMessageWithAutoScroll(`File received & saved: ${fileData.fileName} (${sizeMb} MB)`, 'system');\n })\n .catch((e) => {\n console.error('Auto-save failed:', e);\n addMessageWithAutoScroll(`File received: ${fileData.fileName} (${sizeMb} MB). Open the file panel to download it.`, 'system');\n });\n },\n \n // Error callback\n (error) => {\n console.error('File transfer error:', error);\n\n if (error.includes('Connection not ready')) {\n addMessageWithAutoScroll(` File transfer error: connection not ready. Try again later.`, 'system');\n } else if (error.includes('File too large')) {\n addMessageWithAutoScroll(` File is too big. Maximum size: 100 MB`, 'system');\n } else {\n addMessageWithAutoScroll(` File transfer error: ${error}`, 'system');\n }\n },\n\n // Incoming file request callback \u2014 receiver must explicitly accept before any data is sent\n (fileRequest) => {\n setPendingIncomingFiles(prev => {\n if (prev.some(f => f.fileId === fileRequest.fileId)) return prev;\n return [...prev, fileRequest];\n });\n }\n );\n }\n \n return () => {\n window.removeEventListener('beforeunload', handleBeforeUnload);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n \n if (tabSwitchTimeout) {\n clearTimeout(tabSwitchTimeout);\n tabSwitchTimeout = null;\n }\n \n if (webrtcManagerRef.current) {\n webrtcManagerRef.current.disconnect();\n webrtcManagerRef.current = null;\n }\n };\n }, []); // Empty dependency array to run only once\n \n // All security features are enabled by default - no session purchase needed\n \n const compressOfferData = (offerData) => {\n try {\n // Parse the offer data if it's a string\n const offer = typeof offerData === 'string' ? JSON.parse(offerData) : offerData;\n \n // Create a minimal version with only the most essential data\n const minimalOffer = {\n type: offer.type,\n version: offer.version,\n timestamp: offer.timestamp,\n sessionId: offer.sessionId,\n connectionId: offer.connectionId,\n verificationCode: offer.verificationCode,\n salt: offer.salt,\n // Use only key fingerprints instead of full keys\n keyFingerprints: offer.keyFingerprints,\n // Add a reference to get full data\n fullDataAvailable: true,\n compressionLevel: 'minimal'\n };\n \n return JSON.stringify(minimalOffer);\n } catch (error) {\n console.error('Error compressing offer data:', error);\n return offerData; // Return original if compression fails\n }\n };\n\n const createQRReference = (offerData) => {\n try {\n // Create a unique reference ID for this offer\n const referenceId = `offer_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n \n // Store the full offer data in localStorage with the reference ID\n localStorage.setItem(`qr_offer_${referenceId}`, JSON.stringify(offerData));\n \n // Create a minimal QR code with just the reference\n const qrReference = {\n type: 'secure_offer_reference',\n referenceId: referenceId,\n timestamp: Date.now(),\n message: 'Scan this QR code and use the reference ID to get full offer data'\n };\n \n return JSON.stringify(qrReference);\n } catch (error) {\n console.error('Error creating QR reference:', error);\n return null;\n }\n };\n\n const createTemplateOffer = (offer) => {\n // Minimal template to keep QR within single image capacity\n const templateOffer = {\n type: 'enhanced_secure_offer_template',\n version: '4.0',\n sessionId: offer.sessionId,\n connectionId: offer.connectionId,\n verificationCode: offer.verificationCode,\n timestamp: offer.timestamp,\n // Avoid bulky fields (SDP, raw keys); keep only fingerprints and essentials\n keyFingerprints: offer.keyFingerprints,\n // Keep concise auth hints (omit large nonces)\n authChallenge: offer?.authChallenge?.challenge,\n // Optionally include a compact capability hint if small\n capabilities: Array.isArray(offer.capabilities) && offer.capabilities.length <= 5\n ? offer.capabilities\n : undefined\n };\n \n return templateOffer;\n };\n\n // Conservative QR payload limits (characters). Adjust per error correction level.\n const MAX_QR_LEN = 800; // for JSON/plain/gzip\n const BIN_MAX_QR_LEN = 400; // stricter for SB1:bin to improve scan reliability\n const [qrFramesTotal, setQrFramesTotal] = React.useState(0);\n const [qrFrameIndex, setQrFrameIndex] = React.useState(0);\n const [qrManualMode, setQrManualMode] = React.useState(false);\n\n // Animated QR state (for multi-chunk COSE)\n const qrAnimationRef = React.useRef({ timer: null, chunks: [], idx: 0, active: false });\n const stopQrAnimation = () => {\n try { if (qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n };\n\n // Render frame at current index (no index mutation)\n const renderCurrent = async () => {\n const { chunks, idx } = qrAnimationRef.current || {};\n if (!chunks || !chunks.length) return;\n const current = chunks[idx % chunks.length];\n try {\n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n const url = await (window.generateQRCode ? window.generateQRCode(current, { errorCorrectionLevel: 'M', margin: 2, size: QR_SIZE }) : Promise.resolve(''));\n if (url) setQrCodeUrl(url);\n } catch (e) {\n console.warn('Animated QR render error (current):', e);\n }\n setQrFrameIndex(((qrAnimationRef.current?.idx || 0) % (qrAnimationRef.current?.chunks?.length || 1)) + 1);\n };\n\n // Render current frame, then advance index by 1\n const renderAndAdvance = async () => {\n await renderCurrent();\n const len = qrAnimationRef.current?.chunks?.length || 0;\n if (len > 0) {\n const nextIdx = ((qrAnimationRef.current?.idx || 0) + 1) % len;\n qrAnimationRef.current.idx = nextIdx;\n setQrFrameIndex(nextIdx + 1);\n }\n };\n\n const toggleQrManualMode = () => {\n const newManualMode = !qrManualMode;\n setQrManualMode(newManualMode);\n \n if (newManualMode) {\n\n if (qrAnimationRef.current.timer) {\n clearInterval(qrAnimationRef.current.timer);\n qrAnimationRef.current.timer = null;\n }\n console.log('QR Manual mode enabled - auto-scroll stopped');\n } else {\n if (qrAnimationRef.current.chunks.length > 1) {\n const intervalMs = 3000;\n qrAnimationRef.current.active = true;\n clearInterval(qrAnimationRef.current.timer);\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n }\n console.log('QR Manual mode disabled - auto-scroll resumed');\n }\n };\n\n const nextQrFrame = async () => {\n console.log('\uD83C\uDFAE nextQrFrame called, qrFramesTotal:', qrFramesTotal, 'qrAnimationRef.current:', qrAnimationRef.current);\n if (qrAnimationRef.current.chunks.length > 1) {\n const nextIdx = (qrAnimationRef.current.idx + 1) % qrAnimationRef.current.chunks.length;\n qrAnimationRef.current.idx = nextIdx;\n setQrFrameIndex(nextIdx + 1);\n console.log('\uD83C\uDFAE Next frame index:', nextIdx + 1);\n // Ensure auto-advance timer runs in manual mode too\n try { clearInterval(qrAnimationRef.current.timer); } catch {}\n qrAnimationRef.current.timer = null;\n await renderCurrent();\n // If not in manual mode, restart auto timer\n if (!qrManualMode && qrAnimationRef.current.chunks.length > 1) {\n const intervalMs = 3000;\n qrAnimationRef.current.active = true;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n } else {\n qrAnimationRef.current.active = false;\n }\n } else {\n console.log('\uD83C\uDFAE No multiple frames to navigate');\n }\n };\n\n const prevQrFrame = async () => {\n console.log('\uD83C\uDFAE prevQrFrame called, qrFramesTotal:', qrFramesTotal, 'qrAnimationRef.current:', qrAnimationRef.current);\n if (qrAnimationRef.current.chunks.length > 1) {\n const prevIdx = (qrAnimationRef.current.idx - 1 + qrAnimationRef.current.chunks.length) % qrAnimationRef.current.chunks.length;\n qrAnimationRef.current.idx = prevIdx;\n setQrFrameIndex(prevIdx + 1);\n console.log('\uD83C\uDFAE Previous frame index:', prevIdx + 1);\n try { clearInterval(qrAnimationRef.current.timer); } catch {}\n qrAnimationRef.current.timer = null;\n await renderCurrent();\n if (!qrManualMode && qrAnimationRef.current.chunks.length > 1) {\n const intervalMs = 3000;\n qrAnimationRef.current.active = true;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n } else {\n qrAnimationRef.current.active = false;\n }\n } else {\n console.log('\uD83C\uDFAE No multiple frames to navigate');\n }\n };\n\n // Buffer for assembling scanned COSE chunks\n const qrChunksBufferRef = React.useRef({ id: null, total: 0, seen: new Set(), items: [] });\n\n const generateQRCode = async (data) => {\n try {\n const originalSize = typeof data === 'string' ? data.length : JSON.stringify(data).length;\n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n \n // Try binary format first (CBOR + deflate + base64url)\n if (typeof window.generateBinaryQRCodeFromObject === 'function') {\n try {\n const obj = typeof data === 'string' ? JSON.parse(data) : data;\n const qrDataUrl = await window.generateBinaryQRCodeFromObject(obj, { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (qrDataUrl) {\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n setQrCodeUrl(qrDataUrl);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n } catch (e) {\n console.warn('Binary QR generation failed, falling back to compressed:', e?.message || e);\n }\n }\n \n // Fallback to compressed JSON\n if (typeof window.generateCompressedQRCode === 'function') {\n try {\n const payload = typeof data === 'string' ? data : JSON.stringify(data);\n const qrDataUrl = await window.generateCompressedQRCode(payload, { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (qrDataUrl) {\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n setQrCodeUrl(qrDataUrl);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n } catch (e) {\n console.warn('Compressed QR generation failed, falling back to plain:', e?.message || e);\n }\n }\n \n // Final fallback to plain JSON\n const payload = typeof data === 'string' ? data : JSON.stringify(data);\n if (payload.length <= MAX_QR_LEN) {\n if (!window.generateQRCode) throw new Error('QR code generator unavailable');\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n const qrDataUrl = await window.generateQRCode(payload, { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n setQrCodeUrl(qrDataUrl);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n\n // Large payload: \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u043C \u043D\u0430 \u0444\u0440\u0435\u0439\u043C\u044B (plain JSON)\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n const id = `raw_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n const TARGET_CHUNKS = 10;\n const FRAME_MAX = Math.max(200, Math.floor(payload.length / TARGET_CHUNKS));\n const total = Math.ceil(payload.length / FRAME_MAX);\n const rawChunks = [];\n for (let i = 0; i < total; i++) {\n const seq = i + 1;\n const part = payload.slice(i * FRAME_MAX, (i + 1) * FRAME_MAX);\n rawChunks.push(JSON.stringify({ hdr: { v: 1, id, seq, total, rt: 'raw' }, body: part }));\n }\n if (!window.generateQRCode) throw new Error('QR code generator unavailable');\n if (rawChunks.length === 1) {\n const url = await window.generateQRCode(rawChunks[0], { errorCorrectionLevel: 'M', margin: 2, size: QR_SIZE });\n setQrCodeUrl(url);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n qrAnimationRef.current.chunks = rawChunks;\n qrAnimationRef.current.idx = 0;\n qrAnimationRef.current.active = true;\n setQrFramesTotal(rawChunks.length);\n setQrFrameIndex(1);\n const EC_OPTS = { errorCorrectionLevel: 'M', margin: 2, size: QR_SIZE };\n await renderNext();\n\n if (!qrManualMode) {\n const intervalMs = 4000; // 4 seconds per frame for better readability\n qrAnimationRef.current.active = true;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n }\n return;\n } catch (error) {\n console.error('QR code generation failed:', error);\n setMessages(prev => [...prev, {\n message: ` QR code generation failed: ${error.message}`,\n type: 'error'\n }]);\n }\n };\n\n const reconstructFromTemplate = (templateData) => {\n // Reconstruct full offer from template\n const fullOffer = {\n type: \"enhanced_secure_offer\",\n version: templateData.version,\n timestamp: templateData.timestamp,\n sessionId: templateData.sessionId,\n connectionId: templateData.connectionId,\n verificationCode: templateData.verificationCode,\n salt: templateData.salt,\n sdp: templateData.sdp,\n keyFingerprints: templateData.keyFingerprints,\n capabilities: templateData.capabilities,\n \n // Reconstruct ECDH key object\n ecdhPublicKey: {\n keyType: \"ECDH\",\n keyData: templateData.ecdhKeyData,\n timestamp: templateData.timestamp - 1000, // Approximate\n version: templateData.version,\n signature: templateData.ecdhSignature\n },\n \n // Reconstruct ECDSA key object\n ecdsaPublicKey: {\n keyType: \"ECDSA\",\n keyData: templateData.ecdsaKeyData,\n timestamp: templateData.timestamp - 999, // Approximate\n version: templateData.version,\n signature: templateData.ecdsaSignature\n },\n \n // Reconstruct auth challenge\n authChallenge: {\n challenge: templateData.authChallenge,\n timestamp: templateData.timestamp,\n nonce: templateData.authNonce,\n version: templateData.version\n },\n \n // Generate security level (can be recalculated)\n securityLevel: {\n level: \"CRITICAL\",\n score: 20,\n color: \"red\",\n verificationResults: {\n encryption: { passed: false, details: \"Encryption not working\", points: 0 },\n keyExchange: { passed: true, details: \"Simple key exchange verified\", points: 15 },\n messageIntegrity: { passed: false, details: \"Message integrity failed\", points: 0 },\n rateLimiting: { passed: true, details: \"Rate limiting active\", points: 5 },\n ecdsa: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n metadataProtection: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n pfs: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n nestedEncryption: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n packetPadding: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n advancedFeatures: { passed: false, details: \"Premium session required - feature not available\", points: 0 }\n },\n timestamp: templateData.timestamp,\n details: \"Real verification: 20/100 security checks passed (2/4 available)\",\n isRealData: true,\n passedChecks: 2,\n totalChecks: 4,\n sessionType: \"demo\",\n maxPossibleScore: 50\n }\n };\n \n return fullOffer;\n };\n\n const handleQRScan = async (scannedData) => {\n try {\n console.log('QR Code scanned:', scannedData.substring(0, 100) + '...');\n console.log('Current buffer state:', qrChunksBufferRef.current);\n \n // Check if this is a binary chunk (starts with SB1:bin: or is a raw binary chunk)\n if (scannedData.startsWith('SB1:bin:') || (qrChunksBufferRef.current && qrChunksBufferRef.current.id)) {\n console.log('Binary chunk detected:', scannedData.substring(0, 50) + '...');\n \n // This is a binary chunk - add to buffer\n if (!qrChunksBufferRef.current.id) {\n console.log('Initializing buffer for binary chunks');\n // Initialize buffer for binary chunks\n qrChunksBufferRef.current = { \n id: `bin_${Date.now()}`, \n total: 4, // We expect 4 chunks\n seen: new Set(), \n items: [],\n lastUpdateMs: Date.now()\n };\n }\n \n // Add chunk to buffer (use data hash as identifier)\n const chunkHash = scannedData.substring(0, 50); // Use first 50 chars as hash\n \n // Check if this chunk was already scanned\n if (qrChunksBufferRef.current.seen.has(chunkHash)) {\n console.log(`Chunk already scanned, ignoring...`);\n return Promise.resolve(false);\n }\n \n qrChunksBufferRef.current.seen.add(chunkHash);\n qrChunksBufferRef.current.items.push(scannedData);\n qrChunksBufferRef.current.lastUpdateMs = Date.now();\n \n // Emit progress and force re-render\n try {\n const uniqueCount = qrChunksBufferRef.current.seen.size;\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { \n detail: { \n id: qrChunksBufferRef.current.id, \n seq: uniqueCount, \n total: qrChunksBufferRef.current.total \n } \n }));\n \n // Force re-render to update progress indicator\n setQrFramesTotal(qrChunksBufferRef.current.total);\n setQrFrameIndex(uniqueCount);\n } catch {}\n \n // Check if we have all chunks\n const isComplete = qrChunksBufferRef.current.seen.size >= qrChunksBufferRef.current.total;\n console.log(`Chunks collected: ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, complete: ${isComplete}`);\n \n if (!isComplete) {\n // Keep scanner open for more chunks\n console.log(`Scanned chunk ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, waiting for more...`);\n return Promise.resolve(false);\n }\n \n // All chunks collected - reconstruct binary data\n try {\n const fullBinaryData = qrChunksBufferRef.current.items.join('');\n // Store the original binary data, not decoded JSON\n if (showOfferStep) {\n setAnswerInput(fullBinaryData);\n } else {\n setOfferInput(fullBinaryData);\n }\n \n setMessages(prev => [...prev, { \n message: 'All binary chunks captured. Payload reconstructed.', \n type: 'success' \n }]);\n \n // Clear buffer and close scanner\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('Binary chunks reconstruction failed:', e);\n return Promise.resolve(false);\n }\n }\n \n // Check if this might be a binary chunk (long string without JSON structure)\n if (scannedData.length > 100 && !scannedData.startsWith('{') && !scannedData.startsWith('[')) {\n console.log('Detected potential binary chunk (long non-JSON string):', scannedData.substring(0, 50) + '...');\n \n // Initialize buffer if not exists\n if (!qrChunksBufferRef.current.id) {\n console.log('Initializing buffer for potential binary chunks');\n qrChunksBufferRef.current = { \n id: `bin_${Date.now()}`, \n total: 4, // We expect 4 chunks\n seen: new Set(), \n items: [],\n lastUpdateMs: Date.now()\n };\n }\n \n // Add chunk to buffer (use data hash as identifier)\n const chunkHash = scannedData.substring(0, 50); // Use first 50 chars as hash\n \n // Check if this chunk was already scanned\n if (qrChunksBufferRef.current.seen.has(chunkHash)) {\n console.log(`Chunk already scanned, ignoring...`);\n return Promise.resolve(false);\n }\n \n qrChunksBufferRef.current.seen.add(chunkHash);\n qrChunksBufferRef.current.items.push(scannedData);\n qrChunksBufferRef.current.lastUpdateMs = Date.now();\n \n // Force re-render to update progress indicator\n try {\n const uniqueCount = qrChunksBufferRef.current.seen.size;\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { \n detail: { \n id: qrChunksBufferRef.current.id, \n seq: uniqueCount, \n total: qrChunksBufferRef.current.total \n } \n }));\n \n // Force re-render to update progress indicator\n setQrFramesTotal(qrChunksBufferRef.current.total);\n setQrFrameIndex(uniqueCount);\n } catch {}\n \n // Check if we have all chunks\n const isComplete = qrChunksBufferRef.current.seen.size >= qrChunksBufferRef.current.total;\n console.log(`Chunks collected: ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, complete: ${isComplete}`);\n \n if (!isComplete) {\n // Keep scanner open for more chunks\n console.log(`Scanned chunk ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, waiting for more...`);\n return Promise.resolve(false);\n }\n \n // All chunks collected - reconstruct binary data\n try {\n const fullBinaryData = qrChunksBufferRef.current.items.join('');\n // Store the original binary data, not decoded JSON\n if (showOfferStep) {\n setAnswerInput(fullBinaryData);\n } else {\n setOfferInput(fullBinaryData);\n }\n \n setMessages(prev => [...prev, { \n message: 'All binary chunks captured. Payload reconstructed.', \n type: 'success' \n }]);\n \n // Clear buffer and close scanner\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('Binary chunks reconstruction failed:', e);\n return Promise.resolve(false);\n }\n }\n \n // Single QR code - try to decode directly\n // Removed verbose debug log\n let parsedData;\n if (typeof window.decodeAnyPayload === 'function') {\n const any = window.decodeAnyPayload(scannedData);\n if (typeof any === 'string') {\n parsedData = JSON.parse(any);\n } else {\n parsedData = any; // object from binary\n }\n } else {\n const maybeDecompressed = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(scannedData) : scannedData;\n parsedData = JSON.parse(maybeDecompressed);\n }\n console.log('Decoded data:', parsedData);\n \n // QR with hdr/body: COSE or RAW/BIN animated frames\n if (parsedData.hdr && parsedData.body) {\n const { hdr } = parsedData;\n // Initialize/rotate buffer by id\n if (!qrChunksBufferRef.current.id || qrChunksBufferRef.current.id !== hdr.id) {\n qrChunksBufferRef.current = { id: hdr.id, total: hdr.total || 1, seen: new Set(), items: [], lastUpdateMs: Date.now() };\n try {\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { detail: { id: hdr.id, seq: 0, total: hdr.total || 1 } }));\n } catch {}\n }\n // Deduplicate & record\n if (!qrChunksBufferRef.current.seen.has(hdr.seq)) {\n qrChunksBufferRef.current.seen.add(hdr.seq);\n qrChunksBufferRef.current.items.push(scannedData);\n qrChunksBufferRef.current.lastUpdateMs = Date.now();\n }\n // Emit progress based on unique frames captured\n try {\n const uniqueCount = qrChunksBufferRef.current.seen.size;\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { detail: { id: hdr.id, seq: uniqueCount, total: qrChunksBufferRef.current.total || hdr.total || 0 } }));\n } catch {}\n const isComplete = qrChunksBufferRef.current.seen.size >= (qrChunksBufferRef.current.total || 1);\n if (!isComplete) {\n // Explicitly keep scanner open\n return Promise.resolve(false);\n }\n // Completed: decide RAW vs BIN vs COSE\n if (hdr.rt === 'raw') {\n try {\n // Sort by seq and concatenate bodies\n const parts = qrChunksBufferRef.current.items\n .map(s => JSON.parse(s))\n .sort((a, b) => (a.hdr.seq || 0) - (b.hdr.seq || 0))\n .map(p => p.body || '');\n const fullText = parts.join('');\n const payloadObj = JSON.parse(fullText);\n if (showOfferStep) {\n setAnswerInput(JSON.stringify(payloadObj, null, 2));\n } else {\n setOfferInput(JSON.stringify(payloadObj, null, 2));\n }\n setMessages(prev => [...prev, { message: 'All frames captured. RAW payload reconstructed.', type: 'success' }]);\n try { document.dispatchEvent(new CustomEvent('qr-scan-complete', { detail: { id: hdr.id } })); } catch {}\n // Close scanner from caller by returning true\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('RAW multi-frame reconstruction failed:', e);\n return Promise.resolve(false);\n }\n } else if (hdr.rt === 'bin') {\n try {\n const parts = qrChunksBufferRef.current.items\n .map(s => JSON.parse(s))\n .sort((a, b) => (a.hdr.seq || 0) - (b.hdr.seq || 0))\n .map(p => p.body || '');\n const fullText = parts.join(''); // SB1:bin:...\n let payloadObj;\n if (typeof window.decodeAnyPayload === 'function') {\n const any = window.decodeAnyPayload(fullText);\n payloadObj = (typeof any === 'string') ? JSON.parse(any) : any;\n } else {\n payloadObj = JSON.parse(fullText);\n }\n if (showOfferStep) {\n setAnswerInput(JSON.stringify(payloadObj, null, 2));\n } else {\n setOfferInput(JSON.stringify(payloadObj, null, 2));\n }\n setMessages(prev => [...prev, { message: 'All frames captured. BIN payload reconstructed.', type: 'success' }]);\n try { document.dispatchEvent(new CustomEvent('qr-scan-complete', { detail: { id: hdr.id } })); } catch {}\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('BIN multi-frame reconstruction failed:', e);\n return Promise.resolve(false);\n }\n } else if (window.receiveAndProcess) {\n try {\n const results = await window.receiveAndProcess(qrChunksBufferRef.current.items);\n if (results.length > 0) {\n const { payloadObj } = results[0];\n if (showOfferStep) {\n setAnswerInput(JSON.stringify(payloadObj, null, 2));\n } else {\n setOfferInput(JSON.stringify(payloadObj, null, 2));\n }\n setMessages(prev => [...prev, { message: 'All frames captured. COSE payload reconstructed.', type: 'success' }]);\n try { document.dispatchEvent(new CustomEvent('qr-scan-complete', { detail: { id: hdr.id } })); } catch {}\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n }\n } catch (e) {\n console.warn('COSE multi-chunk processing failed:', e);\n }\n return Promise.resolve(false);\n } else {\n return Promise.resolve(false);\n }\n }\n \n // Check if this is a template-based QR code\n if (parsedData.type === 'enhanced_secure_offer_template') {\n console.log('QR scan: Template-based offer detected, reconstructing...');\n const fullOffer = reconstructFromTemplate(parsedData);\n \n // Determine which input to populate based on current mode\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n setAnswerInput(JSON.stringify(fullOffer, null, 2));\n console.log('\uD83D\uDCF1 Template data populated to answerInput (waiting for response mode)');\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n setOfferInput(JSON.stringify(fullOffer, null, 2));\n console.log('\uD83D\uDCF1 Template data populated to offerInput (paste invitation mode)');\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully! Full offer reconstructed from template.',\n type: 'success'\n }]);\n setShowQRScannerModal(false); // Close QR scanner modal\n return true;\n }\n // Check if this is a reference-based QR code\n else if (parsedData.type === 'secure_offer_reference' && parsedData.referenceId) {\n // Try to get the full offer data from localStorage\n const fullOfferData = localStorage.getItem(`qr_offer_${parsedData.referenceId}`);\n if (fullOfferData) {\n const fullOffer = JSON.parse(fullOfferData);\n // Determine which input to populate based on current mode\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n setAnswerInput(JSON.stringify(fullOffer, null, 2));\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n setOfferInput(JSON.stringify(fullOffer, null, 2));\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully! Full offer data retrieved.',\n type: 'success'\n }]);\n setShowQRScannerModal(false); // Close QR scanner modal\n return true;\n } else {\n setMessages(prev => [...prev, {\n message: 'QR code reference found but full data not available. Please use copy/paste.',\n type: 'error'\n }]);\n return false;\n }\n } else {\n // If payload was compressed, it's already decompressed above; keep legacy warning only when clearly incomplete\n if (!parsedData.sdp && parsedData.type === 'enhanced_secure_offer') {\n setMessages(prev => [...prev, {\n message: 'Compressed QR may omit SDP for brevity. Use copy/paste if connection fails.',\n type: 'warning'\n }]);\n }\n \n // Determine which input to populate based on current mode\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n console.log('QR scan: Populating answerInput with:', parsedData);\n setAnswerInput(JSON.stringify(parsedData, null, 2));\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n console.log('QR scan: Populating offerInput with:', parsedData);\n setOfferInput(JSON.stringify(parsedData, null, 2));\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully!',\n type: 'success'\n }]);\n setShowQRScannerModal(false);\n return true;\n }\n } catch (error) {\n // If not JSON, use as plain text\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n setAnswerInput(scannedData);\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n setOfferInput(scannedData);\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully!',\n type: 'success'\n }]);\n setShowQRScannerModal(false);\n return true;\n }\n };\n\n\n\n \n const handleCreateOffer = async () => {\n try {\n // All security features are enabled by default\n setIsGeneratingKeys(true);\n setOfferData('');\n setShowOfferStep(false);\n setShowQRCode(false);\n setQrCodeUrl('');\n \n const offer = await webrtcManagerRef.current.createSecureOffer();\n \n // Store offer data directly (no encryption needed with SAS)\n setOfferData(offer);\n setShowOfferStep(true);\n \n // Generate QR code with binary format and chunking\n const offerString = typeof offer === 'object' ? JSON.stringify(offer) : offer;\n try {\n if (typeof window.encodeBinaryToPrefixed === 'function') {\n const bin = window.encodeBinaryToPrefixed(offerString);\n // Force chunking into 4 parts - split binary data directly\n const TARGET_CHUNKS = 4;\n let total = TARGET_CHUNKS;\n let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));\n if (FRAME_MAX <= 0) FRAME_MAX = 200;\n \n // Ensure we don't exceed TARGET_CHUNKS\n if (bin.length <= FRAME_MAX) {\n total = 1;\n FRAME_MAX = bin.length;\n } else {\n // Recalculate to ensure exactly TARGET_CHUNKS parts\n FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);\n total = TARGET_CHUNKS;\n }\n \n const id = `bin_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const chunks = [];\n for (let i = 0; i < total; i++) {\n const seq = i + 1;\n const part = bin.slice(i * FRAME_MAX, (i + 1) * FRAME_MAX);\n // Store binary chunks directly without JSON wrapper\n chunks.push(part);\n }\n \n // Seed first frame and start auto-advance immediately\n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n if (window.generateQRCode && chunks.length > 0) {\n const firstUrl = await window.generateQRCode(chunks[0], { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (firstUrl) setQrCodeUrl(firstUrl);\n }\n \n // Store precomputed chunks to ref, ready for animation\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks, idx: 0, active: true };\n setQrFramesTotal(chunks.length);\n setQrFrameIndex(1);\n setQrManualMode(false);\n \n // Start auto-advance loop for Offer immediately\n const intervalMs = 3000;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n \n // Show QR immediately for Offer flow\n try { setShowQRCode(true); } catch {}\n } else {\n // Fallback to single QR\n await generateQRCode(offer);\n try { setShowQRCode(true); } catch {}\n }\n } catch (e) {\n console.warn('Offer QR generation failed:', e);\n }\n \n const existingMessages = messages.filter(m => \n m.type === 'system' && \n (m.message.includes('Secure invitation created') || m.message.includes('Send the encrypted code'))\n );\n \n if (existingMessages.length === 0) {\n setMessages(prev => [...prev, { \n message: 'Secure invitation created and encrypted!', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n setMessages(prev => [...prev, { \n message: '\uD83D\uDCE4 Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.)..', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n }\n \n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } catch (error) {\n setMessages(prev => [...prev, { \n message: `Error creating invitation: ${error.message}`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n } finally {\n setIsGeneratingKeys(false);\n }\n };\n \n const handleCreateAnswer = async () => {\n try {\n \n if (!offerInput.trim()) {\n setMessages(prev => [...prev, { \n message: 'You need to insert the invitation code from your interlocutor.', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n return;\n }\n \n try {\n setMessages(prev => [...prev, { \n message: 'Processing the secure invitation...', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n let offer;\n try {\n // Prefer binary decode first, then gzip JSON\n if (typeof window.decodeAnyPayload === 'function') {\n const any = window.decodeAnyPayload(offerInput.trim());\n offer = (typeof any === 'string') ? JSON.parse(any) : any;\n } else {\n const rawText = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(offerInput.trim()) : offerInput.trim();\n offer = JSON.parse(rawText);\n }\n } catch (parseError) {\n throw new Error(`Invalid invitation format: ${parseError.message}`);\n }\n \n if (!offer || typeof offer !== 'object') {\n throw new Error('The invitation must be an object');\n }\n \n // Support both compact and legacy offer formats\n const isValidOfferType = (offer.t === 'offer') || (offer.type === 'enhanced_secure_offer');\n if (!isValidOfferType) {\n throw new Error('Invalid invitation type. Expected offer or enhanced_secure_offer');\n }\n\n const answer = await webrtcManagerRef.current.createSecureAnswer(offer);\n \n // Store answer data directly (no encryption needed with SAS)\n setAnswerData(answer);\n setShowAnswerStep(true);\n \n // Generate QR code with binary format and chunking\n const answerString = typeof answer === 'object' ? JSON.stringify(answer) : answer;\n try {\n if (typeof window.encodeBinaryToPrefixed === 'function') {\n const bin = window.encodeBinaryToPrefixed(answerString);\n // Force chunking into 4 parts - split binary data directly\n const TARGET_CHUNKS = 4;\n let total = TARGET_CHUNKS;\n let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));\n if (FRAME_MAX <= 0) FRAME_MAX = 200;\n \n // Ensure we don't exceed TARGET_CHUNKS\n if (bin.length <= FRAME_MAX) {\n total = 1;\n FRAME_MAX = bin.length;\n } else {\n // Recalculate to ensure exactly TARGET_CHUNKS parts\n FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);\n total = TARGET_CHUNKS;\n }\n \n const id = `ans_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const chunks = [];\n for (let i = 0; i < total; i++) {\n const seq = i + 1;\n const part = bin.slice(i * FRAME_MAX, (i + 1) * FRAME_MAX);\n // Store binary chunks directly without JSON wrapper\n chunks.push(part);\n }\n \n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n if (window.generateQRCode && chunks.length > 0) {\n const firstUrl = await window.generateQRCode(chunks[0], { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (firstUrl) setQrCodeUrl(firstUrl);\n }\n \n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks, idx: 0, active: true };\n setQrFramesTotal(chunks.length);\n setQrFrameIndex(1);\n setQrManualMode(false);\n \n const intervalMs = 3000;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n \n // Show QR immediately for Answer flow\n try { setShowQRCode(true); } catch {}\n } else {\n // Fallback to single QR\n await generateQRCode(answer);\n try { setShowQRCode(true); } catch {}\n }\n } catch (e) {\n console.warn('Answer QR generation failed:', e);\n }\n \n // Mark generated answers as active immediately.\n // `answerInput` is empty on the joiner path\n // because the response was created locally,\n // not pasted by the user.\n if (typeof markAnswerCreated === 'function') {\n markAnswerCreated();\n }\n\n \n const existingResponseMessages = messages.filter(m => \n m.type === 'system' && \n (m.message.includes('Secure response created') || m.message.includes('Send the response'))\n );\n \n if (existingResponseMessages.length === 0) {\n setMessages(prev => [...prev, { \n message: 'Secure response created!', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n setMessages(prev => [...prev, { \n message: 'Send the response code to the initiator via a secure channel or let them scan the QR code below.', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n }\n \n // Update security level after creating answer\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } catch (error) {\n console.error('Error in handleCreateAnswer:', error);\n setMessages(prev => [...prev, { \n message: `Error processing the invitation: ${error.message}`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n } catch (error) {\n console.error('Error in handleCreateAnswer:', error);\n setMessages(prev => [...prev, { \n message: `Invitation processing error: ${error.message}`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n };\n \n const handleConnect = async () => {\n try {\n if (!answerInput.trim()) {\n setMessages(prev => [...prev, { \n message: 'You need to insert the response code from your interlocutor.', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n return;\n }\n \n try {\n setMessages(prev => [...prev, { \n message: 'Processing the secure response...', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n let answer;\n try {\n // Prefer binary decode first, then gzip JSON\n if (typeof window.decodeAnyPayload === 'function') {\n const anyAnswer = window.decodeAnyPayload(answerInput.trim());\n answer = (typeof anyAnswer === 'string') ? JSON.parse(anyAnswer) : anyAnswer;\n } else {\n const rawText = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(answerInput.trim()) : answerInput.trim();\n answer = JSON.parse(rawText);\n }\n } catch (parseError) {\n throw new Error(`Invalid response format: ${parseError.message}`);\n }\n \n if (!answer || typeof answer !== 'object') {\n throw new Error('The response must be an object');\n }\n \n // Support both compact and legacy formats\n const answerType = answer.t || answer.type;\n if (!answerType || (answerType !== 'answer' && answerType !== 'enhanced_secure_answer')) {\n throw new Error('Invalid response type. Expected answer or enhanced_secure_answer');\n }\n \n await webrtcManagerRef.current.handleSecureAnswer(answer);\n \n // All security features are enabled by default - no session activation needed\n if (pendingSession) {\n setPendingSession(null);\n setMessages(prev => [...prev, { \n message: `All security features enabled by default`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n \n setMessages(prev => [...prev, { \n message: 'Finalizing the secure connection...', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n // Update security level after handling answer\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } catch (error) {\n console.error('Error in handleConnect inner try:', error);\n \n // \u0411\u043E\u043B\u0435\u0435 \u0434\u0435\u0442\u0430\u043B\u044C\u043D\u0430\u044F \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0430 \u043E\u0448\u0438\u0431\u043E\u043A\n let errorMessage = 'Connection setup error';\n if (error.message.includes('CRITICAL SECURITY FAILURE')) {\n if (error.message.includes('ECDH public key structure')) {\n errorMessage = 'Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.';\n } else if (error.message.includes('ECDSA public key structure')) {\n errorMessage = 'Invalid response code - missing signature verification key. Please check the code and try again.';\n } else {\n errorMessage = 'Security validation failed - possible attack detected';\n }\n } else if (error.message.includes('too old') || error.message.includes('replay')) {\n errorMessage = 'Response data is outdated - please use a fresh invitation';\n } else if (error.message.includes('MITM') || error.message.includes('signature')) {\n errorMessage = 'Security breach detected - connection rejected';\n } else if (error.message.includes('Invalid') || error.message.includes('format')) {\n errorMessage = 'Invalid response format - please check the code';\n } else {\n errorMessage = ` ${error.message}`;\n }\n \n setMessages(prev => [...prev, { \n message: errorMessage, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now(),\n showRetryButton: true\n }]);\n\n if (!error.message.includes('too old') && !error.message.includes('replay')) {\n setPendingSession(null);\n setSessionTimeLeft(0);\n }\n\n setConnectionStatus('failed');\n \n } \n } catch (error) {\n console.error('Error in handleConnect outer try:', error);\n\n let errorMessage = 'Connection setup error';\n if (error.message.includes('CRITICAL SECURITY FAILURE')) {\n if (error.message.includes('ECDH public key structure')) {\n errorMessage = 'Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.';\n } else if (error.message.includes('ECDSA public key structure')) {\n errorMessage = 'Invalid response code - missing signature verification key. Please check the code and try again.';\n } else {\n errorMessage = 'Security validation failed - possible attack detected';\n }\n } else if (error.message.includes('too old') || error.message.includes('replay')) {\n errorMessage = 'Response data is outdated - please use a fresh invitation';\n } else if (error.message.includes('MITM') || error.message.includes('signature')) {\n errorMessage = 'Security breach detected - connection rejected';\n } else if (error.message.includes('Invalid') || error.message.includes('format')) {\n errorMessage = 'Invalid response format - please check the code';\n } else {\n errorMessage = `${error.message}`;\n }\n \n setMessages(prev => [...prev, { \n message: errorMessage, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now(),\n showRetryButton: true\n }]);\n\n if (!error.message.includes('too old') && !error.message.includes('replay')) {\n setPendingSession(null);\n setSessionTimeLeft(0);\n }\n\n setConnectionStatus('failed');\n }\n };\n \n const handleVerifyConnection = async (userCode, isValid = true) => {\n if (isValid) {\n webrtcManagerRef.current.confirmVerification(userCode);\n // Mark local verification as confirmed\n setLocalVerificationConfirmed(true);\n \n // Initialize notification integration if permission was granted\n try {\n if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {\n const integration = new window.NotificationIntegration(webrtcManagerRef.current);\n await integration.init();\n \n // Store reference for cleanup\n notificationIntegrationRef.current = integration;\n \n \n // Check if permission was already granted\n const status = integration.getStatus();\n if (status.permission === 'granted') {\n setMessages(prev => [...prev, { \n message: '\u2713 Notifications enabled - you will receive alerts when the tab is inactive', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n } else {\n setMessages(prev => [...prev, { \n message: '\u2139 Notifications disabled - you can enable them using the button on the main page', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n } else if (notificationIntegrationRef.current) {\n } else {\n // Handle error silently\n }\n } catch (error) {\n console.warn('Failed to initialize notifications:', error);\n // Don't show error to user, notifications are optional\n }\n } else {\n setMessages(prev => [...prev, { \n message: ' Verification rejected. The connection is unsafe! Session reset..', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n // Clear verification states\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n setShowVerification(false);\n setVerificationCode('');\n \n // Reset UI to initial state\n setConnectionStatus('disconnected');\n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setShowAnswerStep(false);\n setKeyFingerprint('');\n setSecurityLevel(null);\n setIsVerified(false);\n setMessages([]);\n \n setSessionTimeLeft(0);\n setPendingSession(null);\n \n // Dispatch disconnected event for SessionTimer\n document.dispatchEvent(new CustomEvent('disconnected'));\n \n handleDisconnect();\n }\n };\n \n const handleSendMessage = async () => {\n if (!messageInput.trim()) {\n return;\n }\n \n if (!webrtcManagerRef.current) {\n return;\n }\n \n if (!webrtcManagerRef.current.isConnected()) {\n return;\n }\n\n const baseTextEarly = messageInput.trim();\n const midEarly = `m_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n\n // Offline guard: a P2P data channel can stay \"open\" after the\n // browser loses connectivity, so isConnected() isn't enough \u2014 show\n // the bubble as \"not sent\" (\u2717) instead of silently transmitting.\n // Uses the live offline state (catches console-simulated offline too).\n const offlineNow = isOffline\n || (typeof navigator !== 'undefined' && navigator.onLine === false)\n || (window.pwaOfflineManager && window.pwaOfflineManager.isOnline === false);\n if (offlineNow) {\n // Store-and-forward: show one check (sent), keep it in the\n // conversation at its original time, and transmit on reconnect.\n const outTextOff = codeMode ? '```\\n' + baseTextEarly + '\\n```' : baseTextEarly;\n const tsOff = Date.now();\n const metaOff = { mid: midEarly, ts: tsOff };\n if (viewOnceMode) { metaOff.once = true; metaOff.onceTtl = viewOnceTtl; }\n if (disappearTtl > 0) metaOff.ttl = disappearTtl;\n const echoOpts = { mid: midEarly, status: 'sent', timestamp: tsOff };\n if (disappearTtl > 0) echoOpts.expiresAt = tsOff + disappearTtl * 1000;\n addMessageWithAutoScroll(outTextOff, 'sent', echoOpts);\n outgoingQueueRef.current.push({ outText: outTextOff, meta: metaOff, mid: midEarly });\n setMessageInput('');\n if (codeMode) setCodeMode(false);\n if (viewOnceMode) setViewOnceMode(false);\n return;\n }\n\n try {\n const baseText = baseTextEarly;\n // Code mode wraps the text in a fenced block so both sides render\n // a code window with a copy button (the marker travels as text).\n const outText = codeMode ? '```\\n' + baseText + '\\n```' : baseText;\n\n // Shared id lets unsend/disappearing/receipts reference the\n // same message on both peers.\n const mid = midEarly;\n const meta = { mid, ts: Date.now() }; // ts \u2192 recipient shows the original send time\n if (viewOnceMode) { // applies to the recipient\n meta.once = true;\n meta.onceTtl = viewOnceTtl; // seconds visible after opening\n }\n if (disappearTtl > 0) meta.ttl = disappearTtl; // applies to both sides\n\n // Local echo: sender sees their own text normally (view-once is a\n // recipient-side control), but disappearing also expires our copy.\n // Starts as \"sending\"; flips to \"sent\" once the channel accepts it\n // and \"delivered\" when the peer's receipt arrives.\n const localOpts = { mid, status: 'sending' };\n if (disappearTtl > 0) localOpts.expiresAt = Date.now() + disappearTtl * 1000;\n addMessageWithAutoScroll(outText, 'sent', localOpts);\n\n try {\n await webrtcManagerRef.current.sendMessage(outText, meta);\n updateMessageStatus(mid, 'sent');\n } catch (sendErr) {\n updateMessageStatus(mid, 'failed');\n throw sendErr;\n }\n setMessageInput('');\n // Per-message toggles reset; disappearing stays as a sticky setting.\n if (codeMode) setCodeMode(false);\n if (viewOnceMode) setViewOnceMode(false);\n } catch (error) {\n const msg = String(error?.message || error);\n if (!/queued for sending|Data channel not ready/i.test(msg)) {\n addMessageWithAutoScroll(`Sending error: ${msg}`,'system');\n }\n }\n };\n\n // Unsend: remove locally and ask the peer to drop it too.\n const handleUnsendMessage = React.useCallback((mid) => {\n if (!mid) return;\n setMessages(prev => prev.filter(m => String(m.mid) !== String(mid)));\n try { webrtcManagerRef.current?.sendMessageDelete?.(String(mid)); } catch (_) {}\n }, []);\n\n // View-once reveal timeout / disappearing expiry: wipe the content and\n // leave a \"This message has expired\" tombstone (per the design).\n const handleMessageExpire = React.useCallback((id) => {\n setMessages(prev => prev.map(m => m.id === id ? { ...m, expired: true, message: '', expiresAt: undefined } : m));\n }, []);\n\n // Panic wipe: clear the conversation, tear down the session and wipe keys.\n const handleClearData = () => {\n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setIsGeneratingKeys(false);\n\n if (!shouldPreserveAnswerData()) {\n setShowAnswerStep(false);\n }\n \n setShowVerification(false);\n setShowQRCode(false);\n setShowQRScanner(false);\n setShowQRScannerModal(false);\n // Clear QR scanner buffer\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n\n if (!shouldPreserveAnswerData()) {\n setQrCodeUrl('');\n }\n \n setVerificationCode('');\n setIsVerified(false);\n setKeyFingerprint('');\n setSecurityLevel(null);\n setConnectionStatus('disconnected');\n setMessages([]);\n setMessageInput('');\n \n // Clear verification states\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n \n // PAKE passwords removed - using SAS verification instead \n\n if (typeof console.clear === 'function') {\n console.clear();\n }\n \n // Cleanup session state\n setSessionTimeLeft(0);\n \n setPendingSession(null);\n document.dispatchEvent(new CustomEvent('peer-disconnect'));\n // Session manager removed - all features enabled by default\n };\n \n const handleIncomingDecision = React.useCallback(async (fileId, accepted) => {\n try {\n if (accepted) {\n await webrtcManagerRef.current?.acceptIncomingFile(fileId);\n } else {\n await webrtcManagerRef.current?.rejectIncomingFile(fileId);\n }\n } finally {\n setPendingIncomingFiles(prev => prev.filter(f => f.fileId !== fileId));\n }\n }, []);\n\n const handleDisconnect = () => {\n try {\n setSessionTimeLeft(0);\n \n // Mark as user-initiated disconnect\n updateConnectionState({ \n status: 'disconnected',\n isUserInitiatedDisconnect: true \n });\n \n // Cleanup WebRTC connection\n if (webrtcManagerRef.current) {\n webrtcManagerRef.current.disconnect();\n }\n \n // Cleanup notification integration\n if (notificationIntegrationRef.current) {\n notificationIntegrationRef.current.cleanup();\n notificationIntegrationRef.current = null;\n }\n \n // Clear all connection-related states\n setKeyFingerprint('');\n setVerificationCode('');\n setSecurityLevel(null);\n setIsVerified(false);\n setShowVerification(false);\n setConnectionStatus('disconnected');\n \n // Clear verification states\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n \n // Reset UI to initial state\n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setShowAnswerStep(false);\n setIsGeneratingKeys(false);\n setShowQRCode(false);\n setQrCodeUrl('');\n setShowQRScanner(false);\n setShowQRScannerModal(false);\n \n // Clear messages\n setMessages([]);\n setPendingIncomingFiles([]);\n\n // Clear console\n if (typeof console.clear === 'function') {\n console.clear();\n }\n \n // Dispatch disconnect events\n document.dispatchEvent(new CustomEvent('peer-disconnect'));\n document.dispatchEvent(new CustomEvent('disconnected'));\n \n // Dispatch session cleanup event\n document.dispatchEvent(new CustomEvent('session-cleanup', {\n detail: { \n timestamp: Date.now(),\n reason: 'manual_disconnect'\n }\n }));\n \n // Clear data and reset session timer\n handleClearData();\n \n setTimeout(() => {\n setSessionTimeLeft(0);\n }, 500);\n \n console.log('Disconnect completed successfully');\n } catch (error) {\n console.error('Error during disconnect:', error);\n }\n };\n \n const handleSessionActivated = (session) => {\n let message;\n if (session.type === 'demo') {\n message = ` Demo session activated for 6 minutes. You can create invitations!`;\n } else {\n message = ` All security features enabled by default. You can create invitations!`;\n }\n \n addMessageWithAutoScroll(message, 'system');\n \n };\n \n React.useEffect(() => {\n if (connectionStatus === 'connected' && isVerified) {\n addMessageWithAutoScroll(' Secure connection successfully established and verified! You can now communicate safely with full protection against MITM attacks and Perfect Forward Secrecy..', 'system');\n \n }\n }, [connectionStatus, isVerified]);\n \n const isConnectedAndVerified = (connectionStatus === 'connected' || connectionStatus === 'verified') && isVerified;\n\n // The PWA \"Install app\" pill is a landing-page affordance \u2014 hide it once\n // we're inside the chat (CSS: body.sb-in-chat #pwa-install-button).\n React.useEffect(() => {\n document.body.classList.toggle('sb-in-chat', isConnectedAndVerified);\n return () => document.body.classList.remove('sb-in-chat');\n }, [isConnectedAndVerified]);\n \n React.useEffect(() => {\n // All security features are enabled by default - no session activation needed\n if (isConnectedAndVerified && pendingSession && connectionStatus !== 'failed') {\n setPendingSession(null);\n setSessionTimeLeft(0); \n addMessageWithAutoScroll(' All security features enabled by default', 'system');\n }\n }, [isConnectedAndVerified, pendingSession, connectionStatus]);\n\n // QR Scanner initialization\n React.useEffect(() => {\n if (showQRScannerModal && window.Html5Qrcode) {\n const html5Qrcode = new window.Html5Qrcode(\"qr-reader\");\n const config = { \n fps: 10\n // \u0423\u0431\u0438\u0440\u0430\u0435\u043C qrbox \u0447\u0442\u043E\u0431\u044B \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0432\u0441\u044E \u043E\u0431\u043B\u0430\u0441\u0442\u044C\n };\n\n let isScanning = true;\n\n html5Qrcode.start(\n { facingMode: \"environment\" }, // Use back camera\n config,\n (decodedText, decodedResult) => {\n if (!isScanning) {\n console.log('Scanner stopped, ignoring scan');\n return;\n }\n \n console.log('QR Code scanned:', decodedText);\n console.log('Current buffer state:', qrChunksBufferRef.current);\n \n handleQRScan(decodedText).then((success) => {\n console.log('QR scan result:', success);\n if (success) {\n // Successfully processed - stop scanner and close modal\n console.log('Closing scanner and modal');\n isScanning = false;\n \n // Stop scanner first, then clear\n try {\n console.log('Stopping scanner...');\n html5Qrcode.stop().then(() => {\n console.log('Scanner stopped, clearing...');\n html5Qrcode.clear();\n setShowQRScannerModal(false);\n }).catch((err) => {\n console.log('Error stopping scanner:', err);\n // Try to clear anyway\n try {\n html5Qrcode.clear();\n } catch (clearErr) {\n console.log('Error clearing scanner:', clearErr);\n }\n setShowQRScannerModal(false);\n });\n } catch (err) {\n console.log('Error in scanner cleanup:', err);\n setShowQRScannerModal(false);\n }\n } else {\n console.log('Continuing to scan for more chunks...');\n }\n }).catch((error) => {\n console.error('QR scan processing error:', error);\n // Continue scanning on error\n });\n },\n (error) => {\n // Ignore scanning errors - continue scanning\n if (isScanning) {\n console.log('QR scan error (ignored):', error);\n }\n }\n ).catch((err) => {\n console.error('QR Scanner start error:', err);\n // Close modal on start error\n setShowQRScannerModal(false);\n });\n\n return () => {\n isScanning = false;\n try {\n // Try to stop scanner, but don't worry if it's already stopped\n html5Qrcode.stop().then(() => {\n html5Qrcode.clear();\n }).catch((err) => {\n // Scanner might already be stopped, just clear it\n console.log('Scanner already stopped or error stopping:', err);\n try {\n html5Qrcode.clear();\n } catch (clearErr) {\n console.log('Error clearing scanner in cleanup:', clearErr);\n }\n });\n } catch (err) {\n console.log('Error in cleanup:', err);\n // Just try to clear, don't worry about stopping\n try {\n html5Qrcode.clear();\n } catch (clearErr) {\n console.log('Error clearing scanner in cleanup:', clearErr);\n }\n }\n };\n }\n }, [showQRScannerModal]);\n \n return React.createElement('div', {\n className: \"minimal-bg min-h-screen\"\n }, [\n // Advanced network settings now render inside the connection\n // screen's right panel (see EnhancedConnectionSetup), matching\n // the design's slide-up-within-the-right-column behavior.\n // The verified chat renders its own in-chat header (SecureBit Chat\n // design); the shared header is shown only on the landing/setup view.\n (!isConnectedAndVerified && window.EnhancedMinimalHeader) && React.createElement(window.EnhancedMinimalHeader, {\n key: 'header',\n status: connectionStatus,\n fingerprint: keyFingerprint,\n verificationCode: verificationCode,\n onDisconnect: handleDisconnect,\n isConnected: isConnectedAndVerified,\n securityLevel: securityLevel,\n // sessionManager removed - all features enabled by default\n webrtcManager: webrtcManagerRef.current\n }),\n \n React.createElement('main', {\n key: 'main'\n }, \n (() => {\n return isConnectedAndVerified;\n })()\n ? (() => {\n return React.createElement(EnhancedChatInterface, {\n messages: messages,\n messageInput: messageInput,\n setMessageInput: setMessageInput,\n onSendMessage: handleSendMessage,\n onDisconnect: handleDisconnect,\n keyFingerprint: keyFingerprint,\n isVerified: isVerified,\n chatMessagesRef: chatMessagesRef,\n scrollToBottom: scrollToBottom,\n webrtcManager: webrtcManagerRef.current,\n status: connectionStatus,\n pendingIncomingFiles: pendingIncomingFiles,\n onIncomingDecision: handleIncomingDecision,\n // Secure chat extras\n codeMode: codeMode,\n setCodeMode: setCodeMode,\n viewOnceMode: viewOnceMode,\n setViewOnceMode: setViewOnceMode,\n viewOnceTtl: viewOnceTtl,\n setViewOnceTtl: setViewOnceTtl,\n disappearTtl: disappearTtl,\n setDisappearTtl: setDisappearTtl,\n nowTick: nowTick,\n onUnsendMessage: handleUnsendMessage,\n onMessageExpire: handleMessageExpire\n });\n })()\n : React.createElement(EnhancedConnectionSetup, {\n onCreateOffer: handleCreateOffer,\n onCreateAnswer: handleCreateAnswer,\n onConnect: handleConnect,\n onClearData: handleClearData,\n onVerifyConnection: handleVerifyConnection,\n connectionStatus: connectionStatus,\n offerData: offerData,\n answerData: answerData,\n offerInput: offerInput,\n setOfferInput: setOfferInput,\n answerInput: answerInput,\n setAnswerInput: setAnswerInput,\n showOfferStep: showOfferStep,\n showAnswerStep: showAnswerStep,\n verificationCode: verificationCode,\n showVerification: showVerification,\n showQRCode: showQRCode,\n qrCodeUrl: qrCodeUrl,\n showQRScanner: showQRScanner,\n setShowQRCode: setShowQRCode,\n setShowQRScanner: setShowQRScanner,\n setShowQRScannerModal: setShowQRScannerModal,\n messages: messages,\n localVerificationConfirmed: localVerificationConfirmed,\n remoteVerificationConfirmed: remoteVerificationConfirmed,\n bothVerificationsConfirmed: bothVerificationsConfirmed,\n // QR control props\n qrFramesTotal: qrFramesTotal,\n qrFrameIndex: qrFrameIndex,\n qrManualMode: qrManualMode,\n toggleQrManualMode: toggleQrManualMode,\n nextQrFrame: nextQrFrame,\n prevQrFrame: prevQrFrame,\n // PAKE passwords removed - using SAS verification instead\n markAnswerCreated: markAnswerCreated,\n notificationIntegrationRef: notificationIntegrationRef,\n isGeneratingKeys: isGeneratingKeys,\n setIsGeneratingKeys: setIsGeneratingKeys,\n handleCreateOffer: handleCreateOffer,\n relayOnlyMode: relayOnlyMode,\n setRelayOnlyMode: setRelayOnlyMode,\n webrtcManagerRef: webrtcManagerRef,\n showIceSettings: showIceSettings,\n setShowIceSettings: setShowIceSettings,\n iceServersText: iceServersText,\n iceSettingsPersisted: iceSettingsPersisted,\n customIceServers: customIceServers,\n handleApplyIceSettings: handleApplyIceSettings,\n handleForgetIceSettings: handleForgetIceSettings\n })\n ),\n \n // QR Scanner Modal \u2014 camera scan (design import: \"Start Secure\" / Camera scan modal)\n showQRScannerModal && (() => {\n const closeScanner = () => {\n setShowQRScannerModal(false);\n // Clear QR scanner buffer\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n };\n const buf = qrChunksBufferRef.current;\n const hasParts = !!(buf && buf.id && buf.total > 1);\n const framesText = hasParts\n ? `Scanning frames\u2026 ${buf.seen.size} / ${buf.total}`\n : 'Scanning\u2026';\n const corner = (k, st) => React.createElement('span', {\n key: k,\n style: Object.assign({ position: 'absolute', width: '34px', height: '34px', zIndex: 3 }, st)\n });\n return React.createElement('div', {\n key: 'qr-scanner-modal',\n onClick: closeScanner,\n style: { position: 'fixed', inset: 0, zIndex: 50, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', background: 'rgba(6,6,8,0.82)', backdropFilter: 'blur(10px)', WebkitBackdropFilter: 'blur(10px)', animation: 'sbUp .2s ease' }\n }, [\n React.createElement('div', {\n key: 'scanner-container',\n onClick: (e) => e.stopPropagation(),\n style: { width: '100%', maxWidth: '420px', borderRadius: '22px', border: '1px solid rgba(255,255,255,0.1)', background: '#111113', boxShadow: '0 30px 90px rgba(0,0,0,0.6)', overflow: 'hidden' }\n }, [\n // Header\n React.createElement('div', {\n key: 'scanner-header',\n style: { display: 'flex', alignItems: 'center', gap: '11px', padding: '18px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)' }\n }, [\n React.createElement('span', {\n key: 'scanner-icon',\n style: { display: 'flex' },\n dangerouslySetInnerHTML: { __html: '' }\n }),\n React.createElement('div', {\n key: 'scanner-titles',\n style: { flex: 1, lineHeight: 1.2 }\n }, [\n React.createElement('div', {\n key: 'scanner-title',\n style: { fontSize: '15.5px', fontWeight: 800, color: '#f4f4f6' }\n }, 'Scan QR code'),\n React.createElement('div', {\n key: 'scanner-hint',\n style: { fontSize: '12px', color: '#7b7b83' }\n }, \"Point your camera at their QR\")\n ]),\n React.createElement('button', {\n key: 'close-btn',\n onClick: closeScanner,\n style: { width: '32px', height: '32px', display: 'grid', placeItems: 'center', borderRadius: '9px', border: 'none', background: 'rgba(255,255,255,0.05)', color: '#9a9aa2', cursor: 'pointer' }\n }, React.createElement('i', { className: 'fas fa-times' }))\n ]),\n // Body\n React.createElement('div', {\n key: 'scanner-body',\n style: { padding: '22px 24px 24px' }\n }, [\n React.createElement('div', {\n key: 'viewfinder',\n style: { position: 'relative', aspectRatio: '1', borderRadius: '18px', overflow: 'hidden', background: '#000', border: '1px solid rgba(255,255,255,0.1)' }\n }, [\n React.createElement('div', {\n key: 'vf-bg',\n style: { position: 'absolute', inset: 0, background: 'radial-gradient(circle at 50% 45%, #1a1a1f, #000)' }\n }),\n // Camera video is injected here by Html5Qrcode\n React.createElement('div', {\n key: 'qr-reader',\n id: 'qr-reader',\n style: { position: 'absolute', inset: 0, zIndex: 1 }\n }),\n corner('c-tl', { top: '18px', left: '18px', borderTop: '2.5px solid #3ecf8e', borderLeft: '2.5px solid #3ecf8e', borderRadius: '8px 0 0 0' }),\n corner('c-tr', { top: '18px', right: '18px', borderTop: '2.5px solid #3ecf8e', borderRight: '2.5px solid #3ecf8e', borderRadius: '0 8px 0 0' }),\n corner('c-bl', { bottom: '18px', left: '18px', borderBottom: '2.5px solid #3ecf8e', borderLeft: '2.5px solid #3ecf8e', borderRadius: '0 0 0 8px' }),\n corner('c-br', { bottom: '18px', right: '18px', borderBottom: '2.5px solid #3ecf8e', borderRight: '2.5px solid #3ecf8e', borderRadius: '0 0 8px 0' }),\n React.createElement('span', {\n key: 'scan-line',\n style: { position: 'absolute', left: '18px', right: '18px', height: '2.5px', zIndex: 2, background: 'linear-gradient(90deg, transparent, #3ecf8e, transparent)', boxShadow: '0 0 16px #3ecf8e', animation: 'sbScan 1.5s ease-in-out infinite alternate' }\n }),\n React.createElement('div', {\n key: 'scan-status',\n style: { position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 3, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '14px', background: 'linear-gradient(transparent, rgba(0,0,0,0.6))' }\n }, [\n React.createElement('span', {\n key: 'spinner',\n style: { display: 'flex', animation: 'sbSpin 1.4s linear infinite' },\n dangerouslySetInnerHTML: { __html: '' }\n }),\n React.createElement('span', {\n key: 'scan-frames',\n style: { fontSize: '12.5px', fontWeight: 600, color: '#cfcfd4' }\n }, framesText)\n ])\n ]),\n React.createElement('p', {\n key: 'scanner-note',\n style: { margin: '16px 0 0', textAlign: 'center', fontSize: '12px', lineHeight: 1.5, color: '#6b6b73' }\n }, 'Hold steady until all parts are captured. Camera access is local \u2014 nothing is uploaded.')\n ])\n ])\n ]);\n })(),\n \n \n ]);\n };\n // UpdateChecker \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442 \u0434\u043B\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0439 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439\n const UpdateCheckerWrapper = ({ children }) => {\n // \u041F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C UpdateChecker\n if (typeof window !== 'undefined' && window.UpdateChecker) {\n return React.createElement(window.UpdateChecker, {\n debug: false\n }, children);\n }\n // Fallback \u0435\u0441\u043B\u0438 UpdateChecker \u043D\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\n return children;\n };\n\n function initializeApp() {\n if (window.EnhancedSecureCryptoUtils && window.EnhancedSecureWebRTCManager) {\n // \u041E\u0431\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0432 UpdateChecker \u0434\u043B\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0439 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439\n const AppWithUpdateChecker = React.createElement(UpdateCheckerWrapper, null,\n React.createElement(EnhancedSecureP2PChat)\n );\n ReactDOM.render(AppWithUpdateChecker, document.getElementById('root'));\n } else {\n console.error('\u041C\u043E\u0434\u0443\u043B\u0438 \u043D\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u044B:', {\n hasCrypto: !!window.EnhancedSecureCryptoUtils,\n hasWebRTC: !!window.EnhancedSecureWebRTCManager\n });\n }\n }\n\n if (typeof window !== 'undefined') {\n\n window.addEventListener('unhandledrejection', (event) => {\n console.error('Unhandled promise rejection:', event.reason);\n event.preventDefault(); \n });\n \n\n window.addEventListener('error', (event) => {\n console.error('Global error:', event.error);\n event.preventDefault(); \n });\n \n if (!window.initializeApp) {\n window.initializeApp = initializeApp;\n }\n };\n \n // Render Enhanced Application with UpdateChecker\n if (window.EnhancedSecureCryptoUtils && window.EnhancedSecureWebRTCManager) {\n const UpdateCheckerWrapper = ({ children }) => {\n if (typeof window !== 'undefined' && window.UpdateChecker) {\n return React.createElement(window.UpdateChecker, {\n debug: false\n }, children);\n }\n return children;\n };\n const AppWithUpdateChecker = React.createElement(UpdateCheckerWrapper, null,\n React.createElement(EnhancedSecureP2PChat)\n );\n ReactDOM.render(AppWithUpdateChecker, document.getElementById('root'));\n } else {\n ReactDOM.render(React.createElement(EnhancedSecureP2PChat), document.getElementById('root'));\n }\n"], - "mappings": "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", - "names": ["offerData", "payload", "UpdateCheckerWrapper"] + "sources": ["../src/utils/debugWindowHooks.js", "../src/network/iceSettingsStore.js", "../src/state/sessionsStore.js", "../src/app.jsx"], + "sourcesContent": ["function isSecureBitDebugEnabled(targetWindow = globalThis.window) {\n return targetWindow?.SECUREBIT_DEBUG === true;\n}\n\nfunction installDebugWindowHooks({\n targetWindow = globalThis.window,\n webrtcManagerRef,\n onClearData,\n clearConsole = () => {\n if (typeof console.clear === 'function') {\n console.clear();\n }\n }\n}) {\n if (!isSecureBitDebugEnabled(targetWindow)) {\n return () => {};\n }\n\n targetWindow.forceCleanup = () => {\n onClearData();\n if (webrtcManagerRef.current) {\n webrtcManagerRef.current.disconnect();\n }\n };\n targetWindow.clearLogs = clearConsole;\n targetWindow.webrtcManagerRef = webrtcManagerRef;\n\n return () => {\n delete targetWindow.forceCleanup;\n delete targetWindow.clearLogs;\n delete targetWindow.webrtcManagerRef;\n };\n}\n\nexport { installDebugWindowHooks, isSecureBitDebugEnabled };\n", "// Persistent, at-rest-encrypted storage for the user's custom ICE (STUN/TURN)\n// configuration. Persistence is OPTIONAL: the UI only calls saveIceSettings when\n// the user explicitly opts in (\"Save on this device\"). Session-only use never\n// touches this store \u2014 the settings live in React state and vanish on reload.\n//\n// At-rest protection model:\n// - A non-extractable AES-GCM device key is generated once and kept in\n// IndexedDB. It can never be exported back into JS, so a copy of the\n// on-disk database is useless without executing code in this exact origin.\n// - The settings blob (which may contain TURN credentials) is encrypted with\n// that key before being written.\n// This protects against disk/profile inspection. It does NOT protect against a\n// live code-execution compromise of the page (consistent with the project's\n// stated threat model \u2014 see SECURITY.md). Credentials are never persisted in\n// plaintext, and the user can delete them at any time via clearIceSettings().\n\nconst DB_NAME = 'securebit-net';\nconst DB_VERSION = 1;\nconst STORE = 'kv';\nconst KEY_RECORD = 'ice-device-key';\nconst SETTINGS_RECORD = 'ice-settings';\nconst SETTINGS_VERSION = 1;\n\nfunction isSupported() {\n return typeof indexedDB !== 'undefined' &&\n typeof crypto !== 'undefined' &&\n !!crypto.subtle;\n}\n\nfunction openDb() {\n return new Promise((resolve, reject) => {\n let request;\n try {\n request = indexedDB.open(DB_NAME, DB_VERSION);\n } catch (error) {\n reject(error);\n return;\n }\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(STORE)) {\n db.createObjectStore(STORE);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction idbGet(db, key) {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readonly');\n const req = tx.objectStore(STORE).get(key);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\nfunction idbPut(db, key, value) {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).put(value, key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n\nfunction idbDelete(db, key) {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, 'readwrite');\n tx.objectStore(STORE).delete(key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n}\n\nasync function getOrCreateDeviceKey(db) {\n const existing = await idbGet(db, KEY_RECORD);\n if (existing instanceof CryptoKey) {\n return existing;\n }\n const key = await crypto.subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n false, // non-extractable\n ['encrypt', 'decrypt']\n );\n await idbPut(db, KEY_RECORD, key);\n return key;\n}\n\n/**\n * Persist custom ICE settings, encrypted at rest.\n * @param {{ servers: Array, privacyMode: string }} settings\n */\nexport async function saveIceSettings(settings) {\n if (!isSupported()) throw new Error('Persistent storage is not available in this browser');\n\n const db = await openDb();\n const key = await getOrCreateDeviceKey(db);\n\n const payload = JSON.stringify({\n version: SETTINGS_VERSION,\n servers: Array.isArray(settings?.servers) ? settings.servers : [],\n privacyMode: settings?.privacyMode === 'relay-only' ? 'relay-only' : 'standard'\n });\n\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const ciphertext = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n key,\n new TextEncoder().encode(payload)\n );\n\n await idbPut(db, SETTINGS_RECORD, {\n iv: Array.from(iv),\n data: Array.from(new Uint8Array(ciphertext))\n });\n}\n\n/**\n * Load and decrypt previously saved ICE settings.\n * Fails closed: returns null if absent, unsupported, or undecryptable.\n * @returns {Promise<{ servers: Array, privacyMode: string }|null>}\n */\nexport async function loadIceSettings() {\n if (!isSupported()) return null;\n\n try {\n const db = await openDb();\n const record = await idbGet(db, SETTINGS_RECORD);\n if (!record || !Array.isArray(record.iv) || !Array.isArray(record.data)) {\n return null;\n }\n const key = await idbGet(db, KEY_RECORD);\n if (!(key instanceof CryptoKey)) return null;\n\n const plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: new Uint8Array(record.iv) },\n key,\n new Uint8Array(record.data)\n );\n const parsed = JSON.parse(new TextDecoder().decode(plaintext));\n return {\n servers: Array.isArray(parsed.servers) ? parsed.servers : [],\n privacyMode: parsed.privacyMode === 'relay-only' ? 'relay-only' : 'standard'\n };\n } catch {\n // Corrupted or tampered record: fail closed.\n return null;\n }\n}\n\n/** Delete any persisted ICE settings (the device key is left in place). */\nexport async function clearIceSettings() {\n if (!isSupported()) return;\n try {\n const db = await openDb();\n await idbDelete(db, SETTINGS_RECORD);\n } catch {\n // Best-effort deletion; nothing to surface to the user.\n }\n}\n\nexport async function hasSavedIceSettings() {\n if (!isSupported()) return false;\n try {\n const db = await openDb();\n const record = await idbGet(db, SETTINGS_RECORD);\n return !!record;\n } catch {\n return false;\n }\n}\n", "// Sessions registry for SecureBit.chat multi-session support.\n//\n// Pure, framework-free reducer + helpers. The root React component drives it via\n// React.useReducer; non-serializable per-session objects (the EnhancedSecureWebRTCManager\n// instance, its NotificationIntegration, offline queues, QR-animation timers) live OUTSIDE\n// this state in ref-held Maps keyed by sessionId \u2014 never in here, never shared between\n// sessions. Every reducer case returns fresh objects for the touched session only, so a\n// change to one session can never mutate another (full isolation).\n//\n// sessionId is LOCAL ONLY (crypto.randomUUID). It is never sent to the peer as identity.\n\nexport const SESSION_ACTIONS = Object.freeze({\n CREATE_SESSION: 'CREATE_SESSION',\n REMOVE_SESSION: 'REMOVE_SESSION',\n SET_ACTIVE: 'SET_ACTIVE',\n SET_STATUS: 'SET_STATUS',\n SET_FINGERPRINT: 'SET_FINGERPRINT',\n SET_VERIFICATION: 'SET_VERIFICATION',\n SET_SAS: 'SET_SAS',\n ADD_MESSAGE: 'ADD_MESSAGE',\n SET_MESSAGES: 'SET_MESSAGES',\n UPDATE_MESSAGE_STATUS: 'UPDATE_MESSAGE_STATUS',\n DELETE_MESSAGE: 'DELETE_MESSAGE',\n EXPIRE_MESSAGE: 'EXPIRE_MESSAGE',\n INCREMENT_UNREAD: 'INCREMENT_UNREAD',\n CLEAR_UNREAD: 'CLEAR_UNREAD',\n SET_PENDING_FILES: 'SET_PENDING_FILES',\n PATCH_SETUP: 'PATCH_SETUP',\n RENAME: 'RENAME',\n SET_PEER_PRESENCE: 'SET_PEER_PRESENCE'\n});\n\n// Availability presence the PEER advertises to us (sent E2E over the data channel, never\n// stored on a server). 'invisible' is sent on the wire as 'offline' so peers can't tell.\nexport const PRESENCE_DOT = { available: '#3ecf8e', away: '#e3b341', busy: '#e5727a', offline: '#6b6b73' };\nexport const PRESENCE_WORD = { available: 'Available', away: 'Away', busy: 'Busy', offline: 'Offline' };\n// The statuses the local user can pick for themselves (design: Set your status).\nexport const MY_STATUS_OPTIONS = [\n { key: 'available', word: 'Available', desc: 'Online and reachable', dot: '#3ecf8e' },\n { key: 'away', word: 'Away', desc: 'Idle \u00B7 stepped away', dot: '#e3b341' },\n { key: 'busy', word: 'Busy', desc: 'Do not disturb', dot: '#e5727a' },\n { key: 'invisible', word: 'Invisible', desc: 'Appear offline to peers', dot: '#6b6b73' }\n];\n\n// Short, human-friendly default label derived from the local sessionId. Never the peer's\n// identity \u2014 just something stable to show before the SAS-derived label is available.\nexport function shortLabelFromId(id) {\n const hex = String(id || '').replace(/[^a-z0-9]/gi, '');\n return 'Chat ' + (hex.slice(0, 4) || '0000').toUpperCase();\n}\n\n// Two-letter monogram for the avatar tile (mirrors the design's `mono()` helper).\nexport function monoInitials(label) {\n const words = String(label || '').trim().split(/\\s+/).filter(Boolean);\n const a = words[0]?.[0] || '';\n const b = words[1]?.[0] || words[0]?.[1] || '';\n return (a + b).toUpperCase() || '\u00B7\u00B7';\n}\n\n// Status \u2192 dot colour (mirrors the design's DOT map).\nexport function statusDot(status) {\n switch (status) {\n case 'connected':\n case 'verified':\n return '#3ecf8e';\n case 'connecting':\n case 'verifying':\n case 'new':\n return '#e3b341';\n default:\n return '#e5727a'; // disconnected / peer_disconnected / lost\n }\n}\n\n// Status \u2192 header sub-text (mirrors the design's SUB map).\nexport function statusSub(status) {\n switch (status) {\n case 'connected':\n case 'verified':\n return 'P2P \u00B7 connected';\n case 'verifying':\n return 'Verifying\u2026';\n case 'connecting':\n case 'new':\n return 'Connecting\u2026';\n case 'peer_disconnected':\n return 'Peer disconnected';\n default:\n return 'Disconnected';\n }\n}\n\nfunction emptySetup() {\n return {\n offerData: '',\n answerData: '',\n offerInput: '',\n answerInput: '',\n showOfferStep: false,\n showAnswerStep: false,\n showVerification: false,\n showQRCode: false,\n qrCodeUrl: '',\n isGeneratingKeys: false,\n qrFramesTotal: 0,\n qrFrameIndex: 0,\n qrManualMode: false\n };\n}\n\nexport function createSessionEntry(opts = {}) {\n const id = opts.id || (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : String(Date.now()) + Math.random());\n return {\n id,\n peerLabel: opts.peerLabel || shortLabelFromId(id),\n labelIsCustom: false, // becomes true once the user renames; blocks SAS auto-relabel\n createdAt: opts.createdAt || Date.now(),\n role: opts.role || 'offer', // 'offer' | 'answer'\n status: opts.status || 'new',\n keyFingerprint: '',\n verificationCode: '',\n sas: { localConfirmed: false, remoteConfirmed: false, bothConfirmed: false, isVerified: false },\n messages: [],\n unreadCount: 0,\n pendingIncomingFiles: [],\n peerPresence: null, // peer's advertised availability ('available'|'away'|'busy'|'offline'); null = unknown\n setup: emptySetup()\n };\n}\n\nexport function createInitialState() {\n return { sessions: {}, order: [], activeSessionId: null };\n}\n\n// Apply a partial patch to one session, returning a new state with only that session's\n// object replaced. Other sessions keep their identity (referential isolation).\nfunction patchSession(state, id, patch) {\n const session = state.sessions[id];\n if (!session) return state;\n return {\n ...state,\n sessions: { ...state.sessions, [id]: { ...session, ...patch } }\n };\n}\n\nexport function sessionsReducer(state, action) {\n const A = SESSION_ACTIONS;\n switch (action.type) {\n case A.CREATE_SESSION: {\n const entry = action.entry || createSessionEntry(action);\n if (state.sessions[entry.id]) return state;\n return {\n sessions: { ...state.sessions, [entry.id]: entry },\n order: [...state.order, entry.id],\n activeSessionId: action.activate === false ? state.activeSessionId : entry.id\n };\n }\n\n case A.REMOVE_SESSION: {\n const { id } = action;\n if (!state.sessions[id]) return state;\n const sessions = { ...state.sessions };\n delete sessions[id];\n const order = state.order.filter((x) => x !== id);\n let activeSessionId = state.activeSessionId;\n if (activeSessionId === id) {\n // Re-point to the previous sibling in display order, else the first remaining.\n const removedIdx = state.order.indexOf(id);\n activeSessionId = order[Math.max(0, removedIdx - 1)] || order[0] || null;\n }\n return { sessions, order, activeSessionId };\n }\n\n case A.SET_ACTIVE: {\n if (!state.sessions[action.id]) return state;\n if (state.activeSessionId === action.id) return state;\n return { ...state, activeSessionId: action.id };\n }\n\n case A.SET_STATUS: {\n const session = state.sessions[action.id];\n if (!session || session.status === action.status) return state; // no-op if unchanged\n return patchSession(state, action.id, { status: action.status });\n }\n\n case A.SET_FINGERPRINT:\n return patchSession(state, action.id, { keyFingerprint: action.fingerprint });\n\n case A.SET_VERIFICATION:\n return patchSession(state, action.id, { verificationCode: action.code });\n\n case A.SET_SAS: {\n const session = state.sessions[action.id];\n if (!session) return state;\n return patchSession(state, action.id, { sas: { ...session.sas, ...action.sas } });\n }\n\n case A.ADD_MESSAGE: {\n const session = state.sessions[action.id];\n if (!session) return state;\n return patchSession(state, action.id, { messages: [...session.messages, action.message] });\n }\n\n case A.SET_MESSAGES: {\n const session = state.sessions[action.id];\n if (!session) return state;\n const next = typeof action.updater === 'function'\n ? action.updater(session.messages)\n : action.messages;\n return patchSession(state, action.id, { messages: Array.isArray(next) ? next : [] });\n }\n\n case A.UPDATE_MESSAGE_STATUS: {\n const session = state.sessions[action.id];\n if (!session) return state;\n let changed = false;\n const messages = session.messages.map((m) => {\n if (String(m.mid) === String(action.mid) && m.status !== action.status) {\n changed = true;\n return { ...m, status: action.status };\n }\n return m;\n });\n return changed ? patchSession(state, action.id, { messages }) : state;\n }\n\n case A.DELETE_MESSAGE: {\n const session = state.sessions[action.id];\n if (!session) return state;\n const messages = session.messages.filter((m) => String(m.mid) !== String(action.mid));\n if (messages.length === session.messages.length) return state;\n return patchSession(state, action.id, { messages });\n }\n\n case A.EXPIRE_MESSAGE: {\n const session = state.sessions[action.id];\n if (!session) return state;\n let changed = false;\n const messages = session.messages.map((m) => {\n if (String(m.id) === String(action.messageId) && !m.expired) {\n changed = true;\n return { ...m, expired: true, message: '', expiresAt: undefined };\n }\n return m;\n });\n return changed ? patchSession(state, action.id, { messages }) : state;\n }\n\n case A.INCREMENT_UNREAD: {\n const session = state.sessions[action.id];\n if (!session) return state;\n return patchSession(state, action.id, { unreadCount: session.unreadCount + 1 });\n }\n\n case A.CLEAR_UNREAD: {\n const session = state.sessions[action.id];\n if (!session || session.unreadCount === 0) return state;\n return patchSession(state, action.id, { unreadCount: 0 });\n }\n\n case A.SET_PENDING_FILES: {\n const session = state.sessions[action.id];\n if (!session) return state;\n const next = typeof action.updater === 'function'\n ? action.updater(session.pendingIncomingFiles)\n : action.files;\n return patchSession(state, action.id, { pendingIncomingFiles: Array.isArray(next) ? next : [] });\n }\n\n case A.PATCH_SETUP: {\n const session = state.sessions[action.id];\n if (!session) return state;\n return patchSession(state, action.id, { setup: { ...session.setup, ...action.patch } });\n }\n\n case A.RENAME: {\n const session = state.sessions[action.id];\n if (!session) return state;\n const label = String(action.label || '').trim() || session.peerLabel;\n return patchSession(state, action.id, { peerLabel: label, labelIsCustom: true });\n }\n\n case A.SET_PEER_PRESENCE: {\n const session = state.sessions[action.id];\n if (!session || session.peerPresence === action.presence) return state;\n return patchSession(state, action.id, { peerPresence: action.presence });\n }\n\n default:\n return state;\n }\n}\n\n// Decorate a session into the shape the sidebar/header rendering consumes (avatar monogram,\n// status dot, sub-text, last-message preview, unread badge). Pure derivation \u2014 no state.\nexport function decorateSession(session, activeSessionId) {\n const lastMessage = [...session.messages].reverse().find((m) => !m.expired && typeof m.message === 'string' && m.message.trim());\n const s = session.status;\n const isUp = s === 'connected' || s === 'verified';\n const isPending = s === 'connecting' || s === 'verifying' || s === 'new';\n // Avatar dot + sub-text: while a session is up, reflect the PEER's advertised presence;\n // otherwise reflect the connection state (amber = connecting, red = dropped).\n let dot, headerSub;\n if (isPending) {\n dot = '#e3b341';\n headerSub = statusSub(s);\n } else if (isUp) {\n dot = session.peerPresence ? (PRESENCE_DOT[session.peerPresence] || '#6b6b73') : '#3ecf8e';\n headerSub = session.peerPresence ? (PRESENCE_WORD[session.peerPresence] || 'Online') : 'P2P \u00B7 connected';\n } else {\n dot = '#e5727a';\n headerSub = statusSub(s);\n }\n const preview = lastMessage ? lastMessage.message : headerSub;\n return {\n id: session.id,\n name: session.peerLabel,\n mono: monoInitials(session.peerLabel),\n dot,\n headerSub,\n status: session.status,\n peerPresence: session.peerPresence,\n preview,\n unread: session.unreadCount > 0 ? (session.unreadCount > 99 ? '99+' : String(session.unreadCount)) : null,\n verified: !!session.sas.isVerified,\n active: session.id === activeSessionId,\n inactive: session.id !== activeSessionId\n };\n}\n\nexport function decorateSessions(state) {\n return state.order\n .map((id) => state.sessions[id])\n .filter(Boolean)\n .map((s) => decorateSession(s, state.activeSessionId));\n}\n", "import { installDebugWindowHooks } from './utils/debugWindowHooks.js';\nimport { loadIceSettings, saveIceSettings, clearIceSettings } from './network/iceSettingsStore.js';\nimport {\n sessionsReducer,\n createInitialState,\n createSessionEntry,\n decorateSessions,\n monoInitials,\n statusDot,\n MY_STATUS_OPTIONS,\n PRESENCE_DOT,\n PRESENCE_WORD,\n SESSION_ACTIONS as SA\n} from './state/sessionsStore.js';\n\n // \u2500\u2500 Secure chat extras: code blocks, clipboard hygiene \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Copy text to the clipboard and (optionally) wipe it after a delay so\n // copied secrets (keys, commands, codes) don't linger. We only clear\n // when we can confirm the clipboard still holds exactly what we wrote,\n // or when the clipboard can't be read back at all \u2014 never clobbering\n // something the user copied afterwards that we can see is different.\n const copyToClipboardSecure = async (text, autoClearMs = 0) => {\n let ok = false;\n try {\n await navigator.clipboard.writeText(text);\n ok = true;\n } catch (e) {\n try {\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n ok = document.execCommand('copy');\n document.body.removeChild(ta);\n } catch (_) { ok = false; }\n }\n if (ok && autoClearMs > 0 && navigator.clipboard && navigator.clipboard.writeText) {\n setTimeout(async () => {\n let current = null;\n let readable = true;\n try { current = await navigator.clipboard.readText(); }\n catch (_) { readable = false; }\n if (!readable || current === text) {\n try { await navigator.clipboard.writeText(''); } catch (_) {}\n }\n }, autoClearMs);\n }\n return ok;\n };\n\n // Split a message into plain-text and fenced ``` code segments.\n // Returns null when there is no fenced code so callers can fast-path.\n const parseMessageSegments = (text) => {\n if (typeof text !== 'string' || text.indexOf('```') === -1) return null;\n const segments = [];\n const re = /```([a-zA-Z0-9_+#.-]*)\\n?([\\s\\S]*?)```/g;\n let last = 0;\n let m;\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) segments.push({ kind: 'text', content: text.slice(last, m.index) });\n segments.push({ kind: 'code', lang: (m[1] || '').toLowerCase(), content: m[2].replace(/\\n$/, '') });\n last = re.lastIndex;\n }\n if (last < text.length) segments.push({ kind: 'text', content: text.slice(last) });\n return segments.some(s => s.kind === 'code') ? segments : null;\n };\n\n // Lightweight, dependency-free syntax highlighter. Returns React nodes\n // (no innerHTML / dangerouslySetInnerHTML) so it stays CSP-safe and adds\n // no XSS surface. Language-agnostic: highlights comments, strings,\n // numbers, common keywords and literals \u2014 good enough for snippets\n // without shipping a heavy library or allowing remote scripts.\n const HL_KEYWORDS = new Set([\n 'const','let','var','function','return','if','else','for','while','do','switch',\n 'case','break','continue','class','extends','new','this','super','import','export',\n 'from','as','default','async','await','try','catch','finally','throw','typeof',\n 'instanceof','delete','yield','in','of','def','elif','lambda','pass','with','global',\n 'public','private','protected','static','final','void','int','long','float','double',\n 'char','bool','boolean','string','struct','enum','interface','package','func','fn',\n 'type','where','select','update','insert','delete','where','and','or','not','end',\n 'then','fi','done','echo','use','mut','impl','trait','match','module','require'\n ]);\n const HL_LITERALS = new Set(['true','false','null','undefined','None','True','False','nil','NaN','Infinity']);\n const highlightCode = (code) => {\n const re = /(\\/\\/[^\\n]*|#[^\\n]*|\\/\\*[\\s\\S]*?\\*\\/|--[^\\n]*)|(`(?:\\\\.|[^`\\\\])*`|\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*')|(\\b\\d[\\d_.]*(?:[eE][+-]?\\d+)?\\b|\\b0[xX][0-9a-fA-F]+\\b)|([A-Za-z_$][A-Za-z0-9_$]*)/g;\n const nodes = [];\n let buffer = '';\n let last = 0;\n let key = 0;\n const flush = () => { if (buffer) { nodes.push(buffer); buffer = ''; } };\n let m;\n while ((m = re.exec(code)) !== null) {\n if (m.index > last) buffer += code.slice(last, m.index);\n last = re.lastIndex;\n let cls = null;\n if (m[1]) cls = 'text-gray-500 italic'; // comment\n else if (m[2]) cls = 'text-amber-300'; // string\n else if (m[3]) cls = 'text-sky-300'; // number\n else if (m[4]) { // identifier\n if (HL_KEYWORDS.has(m[4])) cls = 'text-purple-300';\n else if (HL_LITERALS.has(m[4])) cls = 'text-sky-300';\n }\n if (cls) {\n flush();\n nodes.push(React.createElement('span', { key: `h${key++}`, className: cls }, m[0]));\n } else {\n buffer += m[0];\n }\n }\n if (last < code.length) buffer += code.slice(last);\n flush();\n return nodes;\n };\n\n // Fenced-language \u2192 Prism grammar id (with common aliases).\n const PRISM_ALIAS = {\n js: 'javascript', mjs: 'javascript', javascript: 'javascript', node: 'javascript',\n ts: 'typescript', typescript: 'typescript', jsx: 'jsx', tsx: 'tsx',\n py: 'python', python: 'python', sh: 'bash', shell: 'bash', zsh: 'bash', bash: 'bash',\n 'c++': 'cpp', cpp: 'cpp', cc: 'cpp', cxx: 'cpp', c: 'c', h: 'c',\n cs: 'csharp', csharp: 'csharp', java: 'java', go: 'go', golang: 'go',\n rs: 'rust', rust: 'rust', json: 'json', yml: 'yaml', yaml: 'yaml', sql: 'sql',\n md: 'markdown', markdown: 'markdown', html: 'markup', xml: 'markup', svg: 'markup', css: 'css'\n };\n\n // Monospace code window with a copy button (clipboard auto-clears in 30s).\n // Syntax highlighting is done by Prism when available. SECURITY: Prism only\n // TOKENIZES the snippet \u2014 it never executes it. Prism.highlight() HTML-escapes\n // the input before tokenizing, so the highlighted markup contains no live code\n // and can't inject scripts (CSP also blocks inline execution). When Prism or the\n // grammar is missing we fall back to the dependency-free React-node highlighter.\n const CodeBlock = ({ code, lang }) => {\n const [copied, setCopied] = React.useState(false);\n const handleCopy = async () => {\n const ok = await copyToClipboardSecure(code, 30000);\n if (ok) {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n const norm = PRISM_ALIAS[(lang || '').toLowerCase()] || (lang || '').toLowerCase();\n const prism = (typeof window !== 'undefined') ? window.Prism : null;\n const grammar = (prism && prism.languages) ? prism.languages[norm] : null;\n const usePrism = !!(prism && grammar && typeof prism.highlight === 'function');\n let highlightedHtml = null;\n if (usePrism) {\n try { highlightedHtml = prism.highlight(code, grammar, norm); } catch (_) { highlightedHtml = null; }\n }\n const displayLang = (usePrism ? norm : (lang || 'code'));\n const codeEl = (usePrism && highlightedHtml != null)\n // Prism-escaped highlight markup \u2014 safe to inject (no live code).\n ? React.createElement('code', { className: 'language-' + norm, dangerouslySetInnerHTML: { __html: highlightedHtml } })\n // Fallback: React text nodes, never HTML.\n : React.createElement('code', null, highlightCode(code));\n return React.createElement('div', {\n className: \"my-1 rounded-lg overflow-hidden\",\n style: { backgroundColor: '#1b1c1b', border: '0 solid #e5e7eb' }\n }, [\n React.createElement('div', {\n key: 'hdr',\n className: \"flex items-center justify-between px-3 py-1.5\",\n style: { backgroundColor: '#222322', border: '0 solid #e5e7eb' }\n }, [\n React.createElement('span', {\n key: 'lang',\n className: \"text-[11px] uppercase tracking-wide text-gray-500 font-mono\"\n }, displayLang),\n React.createElement('button', {\n key: 'copy',\n onClick: handleCopy,\n title: \"Copy \u2014 clipboard auto-clears in 30s\",\n className: \"flex items-center text-[11px] text-gray-400 hover:text-green-400 transition-colors\"\n }, [\n React.createElement('i', {\n key: 'ic',\n className: `${copied ? 'fas fa-check text-green-400' : 'far fa-copy'} mr-1`\n }),\n copied ? 'Copied' : 'Copy'\n ])\n ]),\n React.createElement('pre', {\n key: 'pre',\n className: \"px-3 py-2 overflow-x-auto text-xs leading-relaxed text-gray-200 custom-scrollbar\",\n style: { whiteSpace: 'pre', fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', margin: 0 }\n }, codeEl)\n ]);\n };\n\n // Render a message body: code windows for fenced blocks, line-preserving\n // text otherwise. Built from already-sanitized text via React nodes only.\n const MessageBody = ({ text }) => {\n const segments = parseMessageSegments(text);\n if (!segments) {\n return React.createElement('div', {\n className: \"text-sm break-words\",\n style: { whiteSpace: 'pre-wrap', wordBreak: 'break-word' }\n }, text);\n }\n return React.createElement('div', { className: \"text-sm\" },\n segments.map((seg, i) => seg.kind === 'code'\n ? React.createElement(CodeBlock, { key: i, code: seg.content, lang: seg.lang })\n : (seg.content.trim()\n ? React.createElement('div', {\n key: i,\n className: \"break-words\",\n style: { whiteSpace: 'pre-wrap', wordBreak: 'break-word' }\n }, seg.content)\n : null)\n )\n );\n };\n\n // Composer toolbar: code / view-once / disappearing.\n // Borderless icon+label buttons; active state uses the brand orange\n // (accent-orange). View-once and Timer open a small time picker.\n const ChatToolbar = ({ codeMode, setCodeMode, viewOnceMode, setViewOnceMode, viewOnceTtl, setViewOnceTtl, disappearTtl, setDisappearTtl }) => {\n const [openMenu, setOpenMenu] = React.useState(null); // 'once' | 'timer' | null\n const fmt = (s) => s >= 3600 ? `${Math.round(s / 3600)}h` : (s >= 60 ? `${Math.round(s / 60)}m` : `${s}s`);\n\n const btnClass = (active) =>\n `inline-flex items-center gap-2 h-9 px-3 rounded-lg text-xs font-medium transition-colors duration-150 ${active\n ? 'accent-orange bg-orange-500/10'\n : 'text-gray-400 hover:text-gray-200 hover:bg-gray-700/40'}`;\n\n const pickerItem = (opt, current, onPick) =>\n React.createElement('button', {\n key: String(opt.value),\n type: 'button',\n onClick: () => { onPick(opt.value); setOpenMenu(null); },\n // Comfortable tap target + readable size on mobile.\n className: `w-full text-left px-4 py-3 sm:py-2.5 text-sm flex items-center gap-3 transition-colors ${current === opt.value\n ? 'accent-orange bg-orange-500/10'\n : 'text-gray-200 hover:bg-gray-700/50 active:bg-gray-700/60'}`\n }, [\n React.createElement('i', { key: 'i', className: `${opt.icon || 'far fa-clock'} w-4 text-center` }),\n React.createElement('span', { key: 'l' }, opt.label)\n ]);\n\n // Opens UPWARD (bottom:100%) via inline style so it never depends on a\n // purgeable utility class and never pushes the composer layout down.\n const picker = (options, current, onPick) =>\n React.createElement('div', {\n className: \"absolute right-0 z-50 min-w-[180px] max-w-[78vw] rounded-xl shadow-2xl overflow-hidden\",\n style: { backgroundColor: '#1f201f', border: '0 solid #e5e7eb', bottom: '100%', marginBottom: '8px' }\n }, options.map(opt => pickerItem(opt, current, onPick)));\n\n const labelBtn = (key, { active, icon, label, title, onClick }) =>\n React.createElement('button', {\n key, type: 'button', onClick, title, 'aria-pressed': !!active,\n className: btnClass(active)\n }, [\n React.createElement('i', { key: 'i', className: `${icon} text-[13px]` }),\n React.createElement('span', { key: 'l', className: 'leading-none' }, label)\n ]);\n\n return React.createElement('div', { className: \"flex items-center gap-1\" }, [\n // Invisible backdrop closes any open picker on outside click.\n openMenu && React.createElement('div', {\n key: 'backdrop',\n className: \"fixed inset-0 z-40\",\n onClick: () => setOpenMenu(null)\n }),\n\n // Code \u2014 toggles code mode (expands the input box).\n labelBtn('code', {\n active: codeMode,\n icon: 'fas fa-code',\n label: 'Code',\n title: 'Send as a code block (expands the input)',\n onClick: () => setCodeMode(v => !v)\n }),\n\n // View once \u2014 pick how long it stays after the peer opens it.\n React.createElement('div', { key: 'once', className: 'relative' }, [\n labelBtn('once-btn', {\n active: viewOnceMode,\n icon: 'fas fa-eye-slash',\n label: viewOnceMode ? `Once \u00B7 ${fmt(viewOnceTtl)}` : 'View once',\n title: 'View once \u2014 vanishes after the peer reads it',\n onClick: () => setOpenMenu(openMenu === 'once' ? null : 'once')\n }),\n openMenu === 'once' && picker([\n { value: 0, label: 'Off', icon: 'fas fa-ban' },\n { value: 5, label: '5s after reading' },\n { value: 15, label: '15s after reading' },\n { value: 30, label: '30s after reading' },\n { value: 60, label: '1m after reading' }\n ], viewOnceMode ? viewOnceTtl : 0, (v) => {\n if (v === 0) { setViewOnceMode(false); }\n else { setViewOnceTtl(v); setViewOnceMode(true); }\n })\n ]),\n\n // Timer \u2014 pick the disappearing duration.\n React.createElement('div', { key: 'timer', className: 'relative' }, [\n labelBtn('timer-btn', {\n active: disappearTtl > 0,\n icon: 'fas fa-stopwatch',\n label: disappearTtl > 0 ? `Timer \u00B7 ${fmt(disappearTtl)}` : 'Timer',\n title: 'Disappearing message \u2014 deletes on both sides',\n onClick: () => setOpenMenu(openMenu === 'timer' ? null : 'timer')\n }),\n openMenu === 'timer' && picker([\n { value: 0, label: 'Off', icon: 'fas fa-ban' },\n { value: 30, label: '30 seconds' },\n { value: 300, label: '5 minutes' },\n { value: 3600, label: '1 hour' }\n ], disappearTtl, (v) => setDisappearTtl(v))\n ])\n ]);\n };\n\n // Enhanced Copy Button with better UX\n const EnhancedCopyButton = ({ text, className = \"\", children }) => {\n const [copied, setCopied] = React.useState(false);\n \n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (error) {\n console.error('Copy failed:', error);\n // Fallback for older browsers\n const textArea = document.createElement('textarea');\n textArea.value = text;\n document.body.appendChild(textArea);\n textArea.select();\n document.execCommand('copy');\n document.body.removeChild(textArea);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n \n return React.createElement('button', {\n onClick: handleCopy,\n className: `${className} transition-all duration-200`\n }, [\n React.createElement('i', {\n key: 'icon',\n className: `${copied ? 'fas fa-check accent-green' : 'fas fa-copy text-secondary'} mr-2`\n }),\n copied ? 'Copied!' : children\n ]);\n };\n \n // Verification Component\n const VerificationStep = ({ verificationCode, onConfirm, onReject, localConfirmed, remoteConfirmed, bothConfirmed }) => {\n const [sasInput, setSasInput] = React.useState('');\n const [error, setError] = React.useState('');\n const normalizedExpectedLength = (verificationCode || '').replace(/[-\\s]/g, '').length;\n const normalizedInputLength = sasInput.replace(/[-\\s]/g, '').length;\n const canConfirm = !localConfirmed && normalizedExpectedLength > 0 && normalizedInputLength === normalizedExpectedLength;\n\n React.useEffect(() => {\n setSasInput('');\n setError('');\n }, [verificationCode]);\n\n const handleConfirm = async () => {\n try {\n setError('');\n await onConfirm(sasInput);\n } catch (confirmationError) {\n setSasInput('');\n if (confirmationError?.message === 'SAS_MAX_ATTEMPTS') {\n setError('Too many incorrect attempts. Session reset for safety.');\n } else {\n setError('Incorrect code. Check it with your peer and try again.');\n }\n }\n };\n\n return React.createElement('div', {\n className: \"card-minimal rounded-xl p-6 border-purple-500/20\"\n }, [\n React.createElement('div', {\n key: 'header',\n className: \"flex items-center mb-4\"\n }, [\n React.createElement('div', {\n key: 'icon',\n className: \"w-10 h-10 bg-purple-500/10 border border-purple-500/20 rounded-lg flex items-center justify-center mr-3\"\n }, [\n React.createElement('i', {\n className: 'fas fa-shield-alt accent-purple'\n })\n ]),\n React.createElement('h3', {\n key: 'title',\n className: \"text-lg font-medium text-primary\"\n }, \"Security verification\")\n ]),\n React.createElement('div', {\n key: 'content',\n className: \"space-y-4\"\n }, [\n React.createElement('p', {\n key: 'description',\n className: \"text-secondary text-sm\"\n }, \"Compare this code with your peer out-of-band, then type the same code below to unlock the chat.\"),\n React.createElement('div', {\n key: 'code-display',\n className: \"text-center\"\n }, [\n React.createElement('div', {\n key: 'code',\n className: \"verification-code text-2xl py-4\"\n }, verificationCode)\n ]),\n React.createElement('div', {\n key: 'sas-input-wrap',\n className: \"space-y-2\"\n }, [\n React.createElement('label', {\n key: 'sas-label',\n className: \"block text-sm text-secondary\"\n }, \"Enter the verified code\"),\n React.createElement('input', {\n key: 'sas-input',\n type: 'text',\n value: sasInput,\n onChange: (event) => {\n setSasInput(event.target.value.toUpperCase());\n if (error) setError('');\n },\n autoFocus: true,\n autoComplete: 'off',\n spellCheck: false,\n inputMode: 'text',\n disabled: localConfirmed,\n placeholder: verificationCode ? 'Type code here' : 'Waiting for code\u2026',\n className: \"w-full rounded-lg border border-purple-500/30 bg-black/20 px-4 py-3 text-center text-xl tracking-[0.3em] text-primary uppercase focus:border-purple-400 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60\",\n style: { fontFamily: 'monospace', textTransform: 'uppercase' }\n }),\n error && React.createElement('p', {\n key: 'sas-error',\n className: \"text-sm text-red-400\"\n }, error)\n ]),\n // Verification status indicators\n React.createElement('div', {\n key: 'verification-status',\n className: \"space-y-2\"\n }, [\n React.createElement('div', {\n key: 'local-status',\n className: `flex items-center justify-between p-2 rounded-lg ${localConfirmed ? 'bg-green-500/10 border border-green-500/20' : 'bg-gray-500/10 border border-gray-500/20'}`\n }, [\n React.createElement('span', {\n key: 'local-label',\n className: \"text-sm text-secondary\"\n }, \"Your confirmation:\"),\n React.createElement('div', {\n key: 'local-indicator',\n className: \"flex items-center\"\n }, [\n React.createElement('i', {\n key: 'local-icon',\n className: `fas ${localConfirmed ? 'fa-check-circle text-green-400' : 'fa-clock text-gray-400'} mr-2`\n }),\n React.createElement('span', {\n key: 'local-text',\n className: `text-sm ${localConfirmed ? 'text-green-400' : 'text-gray-400'}`\n }, localConfirmed ? 'Confirmed' : 'Pending')\n ])\n ]),\n React.createElement('div', {\n key: 'remote-status',\n className: `flex items-center justify-between p-2 rounded-lg ${remoteConfirmed ? 'bg-green-500/10 border border-green-500/20' : 'bg-gray-500/10 border border-gray-500/20'}`\n }, [\n React.createElement('span', {\n key: 'remote-label',\n className: \"text-sm text-secondary\"\n }, \"Peer confirmation:\"),\n React.createElement('div', {\n key: 'remote-indicator',\n className: \"flex items-center\"\n }, [\n React.createElement('i', {\n key: 'remote-icon',\n className: `fas ${remoteConfirmed ? 'fa-check-circle text-green-400' : 'fa-clock text-gray-400'} mr-2`\n }),\n React.createElement('span', {\n key: 'remote-text',\n className: `text-sm ${remoteConfirmed ? 'text-green-400' : 'text-gray-400'}`\n }, remoteConfirmed ? 'Confirmed' : 'Pending')\n ])\n ])\n ]),\n React.createElement('div', {\n key: 'warning',\n className: \"p-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg\"\n }, [\n React.createElement('p', {\n className: \"text-yellow-400 text-sm flex items-center\"\n }, [\n React.createElement('i', {\n className: 'fas fa-exclamation-triangle mr-2'\n }),\n 'Make sure the codes match exactly.!'\n ])\n ]),\n React.createElement('div', {\n key: 'buttons',\n className: \"flex space-x-3\"\n }, [\n React.createElement('button', {\n key: 'confirm',\n onClick: handleConfirm,\n disabled: !canConfirm,\n className: `flex-1 py-3 px-4 rounded-lg font-medium transition-all duration-200 ${!canConfirm ? 'bg-gray-500/20 text-gray-400 cursor-not-allowed' : 'btn-verify text-white'}`\n }, [\n React.createElement('i', {\n className: `fas ${localConfirmed ? 'fa-check-circle' : 'fa-check'} mr-2`\n }),\n localConfirmed ? 'Confirmed' : 'Confirm code'\n ]),\n React.createElement('button', {\n key: 'reject',\n onClick: onReject,\n className: \"flex-1 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 py-3 px-4 rounded-lg font-medium transition-all duration-200\"\n }, [\n React.createElement('i', {\n className: 'fas fa-times mr-2'\n }),\n 'The codes do not match'\n ])\n ])\n ])\n ]);\n };\n \n // Grain overlay for the view-once cover (Telegram-style blur + noise).\n const GRAIN_URL = `url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='100'%20height='100'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.9'%20numOctaves='2'%20stitchTiles='stitch'/%3E%3C/filter%3E%3Crect%20width='100%25'%20height='100%25'%20filter='url(%23n)'/%3E%3C/svg%3E\")`;\n const SB_MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n\n // Enhanced Chat Message \u2014 redesigned bubbles (SecureBit Chat design).\n const EnhancedChatMessage = ({ message, type, timestamp, mid, status, viewOnce, viewOnceTtl, expiresAt, expired, nowTick, canUnsend, onUnsend, onExpire }) => {\n const [revealed, setRevealed] = React.useState(false);\n const revealTimerRef = React.useRef(null);\n\n const formatTime = (ts) => new Date(ts).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' });\n\n React.useEffect(() => () => { if (revealTimerRef.current) clearTimeout(revealTimerRef.current); }, []);\n\n // System / notice messages: centered subtle pill. \"notice\" is shown\n // in the message flow (e.g. \"connection restored\"); \"system\" lives in\n // the handshake log. Notices use a green accent.\n if (type === 'system' || type === 'notice') {\n const isNotice = type === 'notice';\n return React.createElement('div', { className: 'message-slide', style: { display: 'flex', justifyContent: 'center', margin: '4px 0' } },\n React.createElement('div', {\n style: { maxWidth: '80%', padding: '8px 14px', borderRadius: '10px', border: '1px solid ' + (isNotice ? 'rgba(62,207,142,0.25)' : 'rgba(240,137,42,0.22)'), background: isNotice ? 'rgba(62,207,142,0.08)' : 'rgba(240,137,42,0.08)', color: isNotice ? '#8fe0bb' : '#e8b27a', fontSize: '12.5px', textAlign: 'center', lineHeight: 1.5 }\n }, message)\n );\n }\n\n const isMe = type === 'sent';\n const encrypted = isMe;\n const isViewOnce = type === 'received' && viewOnce === true;\n\n const remaining = (typeof expiresAt === 'number')\n ? Math.max(0, Math.ceil((expiresAt - (nowTick || Date.now())) / 1000))\n : null;\n const fmtRemaining = (sec) => {\n if (sec == null) return '';\n const h = Math.floor(sec / 3600), m = Math.floor((sec % 3600) / 60), s = sec % 60;\n const pad = (n) => String(n).padStart(2, '0');\n return h > 0 ? (h + ':' + pad(m) + ':' + pad(s)) : (m + ':' + pad(s));\n };\n\n const handleReveal = () => {\n if (revealed) return;\n setRevealed(true);\n const ms = Math.max(1, (typeof viewOnceTtl === 'number' ? viewOnceTtl : 15)) * 1000;\n revealTimerRef.current = setTimeout(() => { onExpire && onExpire(); }, ms);\n };\n\n const radius = isMe ? '14px 14px 4px 14px' : '14px 14px 14px 4px';\n const border = isMe ? '1px solid rgba(255,255,255,0.10)' : '1px solid rgba(255,255,255,0.06)';\n const bg = isMe ? '#26262b' : '#161618';\n\n // Expired tombstone (disappearing / view-once) \u2014 content already wiped.\n const isExpired = expired === true || (typeof expiresAt === 'number' && (nowTick || Date.now()) >= expiresAt);\n if (isExpired) {\n return React.createElement('div', {\n className: 'message-slide',\n style: { display: 'flex', width: '100%', justifyContent: isMe ? 'flex-end' : 'flex-start' }\n }, React.createElement('div', { style: { maxWidth: '74%', minWidth: '170px' } },\n React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '12px 15px', borderRadius: radius, border: '1px dashed rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.018)' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-clock', style: { color: '#6b6b73', fontSize: '13px' } }),\n React.createElement('span', { key: 't', style: { fontSize: '13px', color: '#6b6b73', fontStyle: 'italic' } }, 'This message has expired')\n ])\n ));\n }\n\n let body;\n if (isViewOnce && !revealed) {\n body = React.createElement('div', {\n key: 'cover',\n onClick: handleReveal,\n style: { position: 'relative', cursor: 'pointer', padding: '12px 15px 10px', overflow: 'hidden' }\n }, [\n React.createElement('div', { key: 'blur', style: { fontSize: '14.5px', lineHeight: 1.55, color: '#b3b3ba', filter: 'blur(7px)', userSelect: 'none', pointerEvents: 'none', wordBreak: 'break-word', minHeight: '22px' } }, message),\n React.createElement('div', { key: 'grain', style: { position: 'absolute', inset: 0, backgroundImage: GRAIN_URL, backgroundSize: '90px', opacity: 0.18, mixBlendMode: 'screen', pointerEvents: 'none' } }),\n React.createElement('div', { key: 'lbl', style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '7px', pointerEvents: 'none' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-eye-slash', style: { color: '#e8e8eb', fontSize: '13px' } }),\n React.createElement('span', { key: 't', style: { fontSize: '12px', fontWeight: 600, color: '#e8e8eb', textShadow: '0 1px 5px rgba(0,0,0,0.75)' } }, 'View once \u00B7 tap to reveal')\n ])\n ]);\n } else {\n body = React.createElement('div', { key: 'body', style: { padding: '12px 15px 10px', color: '#e9e9ec' } },\n React.createElement(MessageBody, { text: message })\n );\n }\n\n const metaLeft = [\n React.createElement('span', { key: 'time', style: { fontFamily: SB_MONO, fontSize: '10.5px', color: '#6b6b73' } }, formatTime(timestamp))\n ];\n // WhatsApp-style delivery state on our own messages.\n if (isMe) {\n const stCfg = ({\n sending: { icon: 'fa-clock', color: '#6b6b73', label: 'Sending' },\n sent: { icon: 'fa-check', color: '#8a8a92', label: 'Sent' },\n // Two GREY ticks = delivered to the peer's device but not yet read.\n delivered: { icon: 'fa-check-double', color: '#8a8a92', label: 'Delivered' },\n // Two GREEN ticks = the peer actually opened the chat and read it.\n read: { icon: 'fa-check-double', color: '#3ecf8e', label: 'Read' },\n failed: { icon: 'fa-triangle-exclamation', color: '#e5727a', label: 'Not sent' }\n })[status || 'sent'] || { icon: 'fa-check', color: '#8a8a92', label: 'Sent' };\n metaLeft.push(React.createElement('span', {\n key: 'dlv', title: stCfg.label,\n style: { display: 'inline-flex', alignItems: 'center', color: stCfg.color }\n }, React.createElement('i', { className: 'fas ' + stCfg.icon, style: { fontSize: '10.5px' } })));\n }\n if (isViewOnce && revealed) {\n metaLeft.push(React.createElement('span', { key: 'vo', style: { display: 'inline-flex', alignItems: 'center', gap: '4px', fontSize: '10px', fontWeight: 600, color: '#8a8a92' } }, [\n React.createElement('span', { key: 'd', style: { width: '4px', height: '4px', borderRadius: '50%', background: '#8a8a92' } }),\n 'Viewed once'\n ]));\n } else if (remaining !== null) {\n metaLeft.push(React.createElement('span', { key: 'ttl', style: { display: 'inline-flex', alignItems: 'center', gap: '4px', fontFamily: SB_MONO, fontSize: '10.5px', fontWeight: 500, color: '#f0892a' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-clock', style: { fontSize: '10px' } }),\n fmtRemaining(remaining)\n ]));\n }\n\n const metaRight = [];\n metaRight.push(React.createElement('span', { key: 'status', style: { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '10.5px', fontWeight: 600, color: '#3ecf8e', flex: 'none' } }, [\n React.createElement('i', { key: 'i', className: encrypted ? 'fas fa-lock' : 'fas fa-lock-open', style: { fontSize: '10px' } }),\n encrypted ? 'Encrypted' : 'Decrypted'\n ]));\n // Delete-for-everyone sits AFTER the Encrypted/Decrypted status.\n if (canUnsend && isMe && mid) {\n metaRight.push(React.createElement('button', {\n key: 'unsend', onClick: () => onUnsend && onUnsend(mid), title: 'Delete for everyone',\n className: 'sb-unsend',\n style: { background: 'none', border: 'none', cursor: 'pointer', color: '#56565e', fontSize: '11px', padding: 0, lineHeight: 1 }\n }, React.createElement('i', { className: 'fas fa-trash-can' })));\n }\n\n const meta = React.createElement('div', {\n key: 'meta',\n style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '14px', padding: '0 15px 10px' }\n }, [\n React.createElement('div', { key: 'l', style: { display: 'flex', alignItems: 'center', gap: '9px', minWidth: 0 } }, metaLeft),\n React.createElement('div', { key: 'r', style: { display: 'flex', alignItems: 'center', gap: '9px', flex: 'none' } }, metaRight)\n ]);\n\n return React.createElement('div', {\n className: 'message-slide',\n style: { display: 'flex', width: '100%', justifyContent: isMe ? 'flex-end' : 'flex-start' }\n }, [\n React.createElement('div', { key: 'wrap', style: { maxWidth: '74%', minWidth: '170px' } },\n React.createElement('div', { style: { borderRadius: radius, border: border, background: bg, overflow: 'hidden' } }, [body, meta])\n )\n ]);\n };\n \n // Enhanced Connection Setup with verification\n const EnhancedConnectionSetup = ({\n messages, \n onCreateOffer, \n onCreateAnswer, \n onConnect, \n onClearData,\n onVerifyConnection,\n connectionStatus,\n offerData,\n answerData,\n offerInput,\n setOfferInput,\n answerInput,\n setAnswerInput,\n showOfferStep,\n showAnswerStep,\n verificationCode,\n showVerification,\n showQRCode,\n qrCodeUrl,\n showQRScanner,\n setShowQRCode,\n setShowQRScanner,\n setShowQRScannerModal,\n offerPassword,\n answerPassword,\n localVerificationConfirmed,\n remoteVerificationConfirmed,\n bothVerificationsConfirmed,\n // QR control props\n qrFramesTotal,\n qrFrameIndex,\n qrManualMode,\n toggleQrManualMode,\n nextQrFrame,\n prevQrFrame,\n markAnswerCreated,\n notificationIntegrationRef,\n isGeneratingKeys,\n setIsGeneratingKeys,\n handleCreateOffer,\n relayOnlyMode,\n setRelayOnlyMode,\n webrtcManagerRef,\n showIceSettings,\n setShowIceSettings,\n iceServersText,\n iceSettingsPersisted,\n customIceServers,\n handleApplyIceSettings,\n handleForgetIceSettings,\n // When true, render ONLY the create/connect card (no marketing landing,\n // no hero) so it slots into the chat column for an additional session.\n compact = false\n }) => {\n const [mode, setMode] = React.useState('create');\n const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false);\n // Local UI state for the redesigned Start Secure screen\n const [qrModalOpen, setQrModalOpen] = React.useState(false);\n const [copied, setCopied] = React.useState(false);\n const [sasInput, setSasInput] = React.useState('');\n const [sasError, setSasError] = React.useState('');\n const [platformsOpen, setPlatformsOpen] = React.useState(false);\n const [codeRevealed, setCodeRevealed] = React.useState(false);\n const [genProgress, setGenProgress] = React.useState(0);\n\n // Reset the typed SAS whenever a fresh verification code arrives\n React.useEffect(() => { setSasInput(''); setSasError(''); }, [verificationCode]);\n // Close the QR popup and re-hide (blur) the code whenever the\n // exchange step changes \u2014 every new offer/answer starts concealed.\n React.useEffect(() => {\n if (!showOfferStep && !showAnswerStep) setQrModalOpen(false);\n setCodeRevealed(false);\n }, [showOfferStep, showAnswerStep]);\n // Animate the \"Securing your channel\" steps while keys generate.\n React.useEffect(() => {\n const generating = isGeneratingKeys && !showOfferStep && !showAnswerStep && !showVerification;\n if (!generating) { setGenProgress(0); return; }\n setGenProgress(0);\n let p = 0;\n const id = setInterval(() => {\n p += 1;\n setGenProgress(p);\n if (p >= 3) clearInterval(id);\n }, 520);\n return () => clearInterval(id);\n }, [isGeneratingKeys, showOfferStep, showAnswerStep, showVerification]);\n // Dismiss the download platforms popover on any outside click\n React.useEffect(() => {\n if (!platformsOpen) return;\n const onDoc = () => setPlatformsOpen(false);\n const id = setTimeout(() => document.addEventListener('click', onDoc), 0);\n return () => { clearTimeout(id); document.removeEventListener('click', onDoc); };\n }, [platformsOpen]);\n\n // \"Back\" \u2014 clear the in-progress exchange and return to the intro of\n // the current tab. onClearData() resets offer/answer/verification in\n // the parent, which flips the derived step back to \"intro\".\n const resetToSelect = () => {\n setIsGeneratingKeys(false);\n setQrModalOpen(false);\n onClearData();\n };\n \n const handleVerificationConfirm = (userCode) => {\n return onVerifyConnection(userCode);\n };\n \n const handleVerificationReject = () => {\n onVerifyConnection(null, false);\n };\n \n // Request notification permission on first user interaction\n const requestNotificationPermissionOnInteraction = async () => {\n if (notificationPermissionRequested) {\n return; // Already requested\n }\n \n try {\n // Check if Notification API is supported\n if (!('Notification' in window)) {\n return;\n }\n \n // Check if we're in a secure context\n if (!window.isSecureContext && window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') {\n return;\n }\n \n // Check current permission status\n const currentPermission = (typeof Notification !== 'undefined' && Notification) \n ? Notification.permission \n : 'denied';\n \n // Only request if permission is default (not granted or denied)\n if (currentPermission === 'default' && typeof Notification !== 'undefined' && Notification) {\n const permission = await Notification.requestPermission();\n \n if (permission === 'granted') {\n // Initialize notification integration immediately\n try {\n if (window.NotificationIntegration && webrtcManagerRef.current) {\n const integration = new window.NotificationIntegration(webrtcManagerRef.current);\n await integration.init();\n \n // Store reference for cleanup\n notificationIntegrationRef.current = integration;\n }\n } catch (error) {\n // Handle error silently\n }\n \n // Send welcome notification\n setTimeout(() => {\n try {\n const welcomeNotification = new Notification('SecureBit Chat', {\n body: 'Notifications enabled! You will receive alerts for new messages.',\n icon: '/logo/icon-192x192.png',\n tag: 'welcome-notification'\n });\n \n welcomeNotification.onclick = () => {\n welcomeNotification.close();\n };\n \n setTimeout(() => {\n welcomeNotification.close();\n }, 5000);\n \n } catch (error) {\n // Handle error silently\n }\n }, 1000);\n \n }\n } else if (currentPermission === 'granted') {\n // Initialize notification integration immediately\n try {\n if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {\n const integration = new window.NotificationIntegration(webrtcManagerRef.current);\n await integration.init();\n \n // Store reference for cleanup\n notificationIntegrationRef.current = integration;\n }\n } catch (error) {\n // Handle error silently\n }\n \n // Test notification to confirm it works\n setTimeout(() => {\n try {\n const testNotification = new Notification('SecureBit Chat', {\n body: 'Notifications are working! You will receive alerts for new messages.',\n icon: '/logo/icon-192x192.png',\n tag: 'test-notification'\n });\n \n testNotification.onclick = () => {\n testNotification.close();\n };\n \n setTimeout(() => {\n testNotification.close();\n }, 5000);\n } catch (error) {\n // Handle error silently\n }\n }, 1000);\n }\n \n setNotificationPermissionRequested(true);\n \n } catch (error) {\n // Handle error silently\n }\n };\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Start Secure \u2014 redesigned two-column connection screen.\n // Layout/colors/animation from the imported design; wired to the\n // app's real crypto handlers. Icons use FontAwesome (house style).\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const h = React.createElement;\n const C_ORANGE = '#f0892a';\n const C_GREEN = '#3ecf8e';\n const MONO = SB_MONO;\n\n const encode = (data) => {\n try {\n const min = typeof data === 'object' ? JSON.stringify(data) : (data || '');\n if (!min) return '';\n if (typeof window.encodeBinaryToPrefixed === 'function') return window.encodeBinaryToPrefixed(min);\n if (typeof window.compressToPrefixedGzip === 'function') return window.compressToPrefixedGzip(min);\n return min;\n } catch { return typeof data === 'object' ? JSON.stringify(data) : (data || ''); }\n };\n\n // Derived flow step from the parent's real connection state\n const isCreate = mode === 'create';\n const isGenerating = isGeneratingKeys && !showOfferStep && !showAnswerStep && !showVerification;\n const isOfferCred = isCreate && showOfferStep && !showVerification;\n const isAnswerCred = !isCreate && showAnswerStep && !showVerification;\n const atIntro = !showVerification && !isGenerating && !isOfferCred && !isAnswerCred;\n const accent = isCreate ? C_ORANGE : C_GREEN;\n const kicker = showVerification\n ? 'Step 3 \u00B7 verification'\n : ((isOfferCred || isAnswerCred) ? 'Step 2 \u00B7 exchange' : 'Step 1 \u00B7 open a channel');\n\n const credCode = isCreate ? encode(offerData) : encode(answerData);\n const hasInvite = (offerInput || '').trim().length > 0;\n const hasAnswer = (answerInput || '').trim().length > 0;\n\n const copyCred = async () => {\n try {\n if (typeof copyToClipboardSecure === 'function') await copyToClipboardSecure(credCode);\n else await navigator.clipboard.writeText(credCode);\n } catch (e) {}\n setCopied(true);\n setTimeout(() => setCopied(false), 1600);\n };\n\n // SAS verification (alphanumeric, variable length \u2014 matches real codes)\n const normExpected = (verificationCode || '').replace(/[-\\s]/g, '').length;\n const normInput = sasInput.replace(/[-\\s]/g, '').length;\n const canConfirm = !localVerificationConfirmed && normExpected > 0 && normInput === normExpected;\n const handleSasConfirm = async () => {\n try {\n setSasError('');\n await onVerifyConnection(sasInput);\n } catch (err) {\n setSasInput('');\n setSasError(err?.message === 'SAS_MAX_ATTEMPTS'\n ? 'Too many incorrect attempts. Session reset for safety.'\n : 'Incorrect code. Check it with your peer and try again.');\n }\n };\n\n // Icon set transcribed from the design (inline SVG, not FontAwesome).\n // Each entry: stroke-width + the SVG child elements [tag, attrs].\n const ICON_DEFS = {\n 'fa-user': { sw: 1.9, e: [['circle', { cx: 12, cy: 8, r: 3.6 }], ['path', { d: 'M5 20c0-3.5 3-5.5 7-5.5s7 2 7 5.5' }]] },\n 'fa-lock': { sw: 2, e: [['path', { d: 'M7 11V7a5 5 0 0 1 10 0v4' }], ['rect', { x: 4.5, y: 11, width: 15, height: 9, rx: 2.2 }]] },\n 'fa-plus': { sw: 2.1, e: [['path', { d: 'M12 5v14M5 12h14' }]] },\n 'fa-link': { sw: 2, e: [['path', { d: 'M9.5 14.5l5-5M8 11l-2.2 2.2a3.5 3.5 0 0 0 4.95 4.95L13 16M16 13l2.2-2.2a3.5 3.5 0 0 0-4.95-4.95L11 8' }]] },\n 'fa-bolt': { sw: 2.1, e: [['path', { d: 'M13 2L4.5 13H11l-1 9 8.5-11H12l1-9z' }]] },\n 'fa-camera': { sw: 1.8, e: [['path', { d: 'M2 8.5V6.5A2.5 2.5 0 0 1 4.5 4h2M17.5 4h2A2.5 2.5 0 0 1 22 6.5v2M22 15.5v2a2.5 2.5 0 0 1-2.5 2.5h-2M6.5 20h-2A2.5 2.5 0 0 1 2 17.5v-2' }], ['circle', { cx: 12, cy: 12, r: 3.2 }]] },\n 'fa-qrcode': { sw: 1.9, e: [['rect', { x: 3, y: 3, width: 7, height: 7, rx: 1.3 }], ['rect', { x: 14, y: 3, width: 7, height: 7, rx: 1.3 }], ['rect', { x: 3, y: 14, width: 7, height: 7, rx: 1.3 }], ['path', { d: 'M14 14h3v3M21 14v.01M14 21h.01M21 21v-4M17.5 21H21' }]] },\n 'fa-chevron-right': { sw: 2, e: [['path', { d: 'M9 6l6 6-6 6' }]] },\n 'fa-chevron-left': { sw: 2, e: [['path', { d: 'M15 6l-6 6 6 6' }]] },\n 'fa-chevron-down': { sw: 2, e: [['path', { d: 'M6 9l6 6 6-6' }]] },\n 'fa-circle-notch': { sw: 2, e: [['path', { d: 'M21 12a9 9 0 1 1-6.2-8.6' }]] },\n 'fa-check': { sw: 2.4, e: [['path', { d: 'M20 6L9 17l-5-5' }]] },\n 'fa-check-circle': { sw: 2, e: [['circle', { cx: 12, cy: 12, r: 9 }], ['path', { d: 'M8.5 12.4l2.4 2.4 4.6-5' }]] },\n 'fa-shield-alt': { sw: 1.9, e: [['path', { d: 'M12 2.6l7 3v5.1c0 4.5-3 8.3-7 10.2-4-1.9-7-5.7-7-10.2V5.6l7-3z' }], ['path', { d: 'M9 12l2 2 4-4.1' }]] },\n 'fa-download': { sw: 2, e: [['path', { d: 'M12 3v12M12 15l-4.5-4.5M12 15l4.5-4.5' }], ['path', { d: 'M4 20h16' }]] },\n 'fa-clock': { sw: 1.8, e: [['circle', { cx: 12, cy: 13, r: 8 }], ['path', { d: 'M12 9v4l2.5 2M9 2h6' }]] },\n 'fa-times': { sw: 2.2, e: [['path', { d: 'M18 6L6 18M6 6l12 12' }]] },\n 'fa-eye': { sw: 1.9, e: [['path', { d: 'M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z' }], ['circle', { cx: 12, cy: 12, r: 3 }]] }\n };\n const fa = (name, opts) => {\n opts = opts || {};\n const def = ICON_DEFS[name];\n if (!def) {\n // Fallback to FontAwesome (e.g. fa-sliders-h, kept by preference).\n const st = {};\n if (opts.color) st.color = opts.color;\n if (opts.fontSize) st.fontSize = opts.fontSize;\n if (opts.animation) st.animation = opts.animation;\n if (opts.style) Object.assign(st, opts.style);\n return h('i', { key: opts.key, className: `fas ${name}`, style: st });\n }\n const size = opts.fontSize ? parseFloat(opts.fontSize) : 16;\n const svgStyle = {};\n if (opts.animation) { svgStyle.animation = opts.animation; svgStyle.transformOrigin = 'center'; svgStyle.transformBox = 'fill-box'; }\n if (opts.style) Object.assign(svgStyle, opts.style);\n return h('svg', {\n key: opts.key, width: size, height: size, viewBox: '0 0 24 24',\n fill: 'none', stroke: opts.color || 'currentColor',\n strokeWidth: def.sw || 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n style: svgStyle\n }, def.e.map((el, i) => h(el[0], Object.assign({ key: i }, el[1]))));\n };\n\n // \u2500\u2500 LEFT PANEL \u00B7 branding + animated channel + crypto badges \u2500\u2500\n const leftPanel = h('div', {\n key: 'left',\n className: 'sb-start-left',\n style: {\n flex: '1.05 1 380px', position: 'relative', overflow: 'hidden',\n // Full viewport height even when the panels stack on mobile, so the\n // branding column isn't collapsed/cramped (looked broken otherwise).\n minHeight: '100vh', boxSizing: 'border-box',\n padding: '46px', display: 'flex', flexDirection: 'column',\n justifyContent: 'space-between', gap: '36px',\n borderRight: '1px solid rgba(255,255,255,0.06)',\n background: 'radial-gradient(900px 600px at 25% 18%, rgba(240,137,42,0.07), transparent 62%), radial-gradient(800px 700px at 80% 92%, rgba(62,207,142,0.06), transparent 60%), #0c0c0e'\n }\n }, [\n h('div', { key: 'herowrap', style: { flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', position: 'relative', zIndex: 2 } },\n h('div', { key: 'hero', style: { maxWidth: '470px' } }, [\n h('h1', { key: 'h1', style: { margin: '0 0 14px', fontSize: '34px', fontWeight: 800, letterSpacing: '-1.1px', lineHeight: 1.1, color: '#f4f4f6' } }, [\n 'A direct line', h('br', { key: 'br' }), 'only you two can read.'\n ]),\n h('p', { key: 'p', style: { margin: '0 0 38px', fontSize: '14.5px', lineHeight: 1.6, color: '#8a8a92', maxWidth: '390px' } },\n 'Keys are generated on your device and exchanged peer-to-peer. No accounts, no servers storing your messages.'),\n // animated channel\n h('div', { key: 'channel', style: { display: 'flex', alignItems: 'center', height: '74px' } }, [\n h('div', { key: 'you', style: { flex: 'none', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', width: '74px' } }, [\n h('div', { key: 'n', style: { width: '50px', height: '50px', borderRadius: '15px', display: 'grid', placeItems: 'center', background: 'rgba(240,137,42,0.13)', border: '1px solid rgba(240,137,42,0.3)', animation: 'sbNode 3s ease-in-out infinite' } }, fa('fa-user', { color: C_ORANGE, fontSize: '20px' })),\n h('span', { key: 'l', style: { fontSize: '11px', fontWeight: 600, color: '#9a9aa2' } }, 'You')\n ]),\n h('div', { key: 'wire', style: { flex: 1, position: 'relative', height: '52px', margin: '0 -6px' } }, [\n h('div', { key: 'line', style: { position: 'absolute', top: '50%', left: 0, right: 0, height: '2px', transform: 'translateY(-50%)', background: 'linear-gradient(90deg, rgba(240,137,42,0.45), rgba(62,207,142,0.45))' } }),\n h('span', { key: 'd1', style: { position: 'absolute', top: '50%', transform: 'translateY(-50%)', width: '6px', height: '6px', borderRadius: '50%', background: C_ORANGE, boxShadow: `0 0 8px ${C_ORANGE}`, animation: 'sbFlowR 2.6s linear infinite' } }),\n h('span', { key: 'd2', style: { position: 'absolute', top: '50%', transform: 'translateY(-50%)', width: '6px', height: '6px', borderRadius: '50%', background: C_GREEN, boxShadow: `0 0 8px ${C_GREEN}`, animation: 'sbFlowL 2.6s linear infinite', animationDelay: '0.6s' } }),\n h('div', { key: 'hub', style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: '38px', height: '38px' } }, [\n h('span', { key: 'pulse', style: { position: 'absolute', top: '50%', left: '50%', width: '38px', height: '38px', borderRadius: '50%', border: '1.5px solid rgba(62,207,142,0.5)', animation: 'sbPulse 2.4s ease-out infinite' } }),\n h('div', { key: 'core', style: { position: 'relative', width: '38px', height: '38px', borderRadius: '50%', display: 'grid', placeItems: 'center', background: '#121214', border: '1px solid rgba(62,207,142,0.45)', boxShadow: '0 0 18px rgba(62,207,142,0.25)' } }, fa('fa-lock', { color: C_GREEN, fontSize: '14px' }))\n ])\n ]),\n h('div', { key: 'peer', style: { flex: 'none', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', width: '74px' } }, [\n h('div', { key: 'n', style: { width: '50px', height: '50px', borderRadius: '15px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.1)', border: '1px solid rgba(62,207,142,0.3)', animation: 'sbNode 3s ease-in-out infinite', animationDelay: '1.5s' } }, fa('fa-user', { color: C_GREEN, fontSize: '20px' })),\n h('span', { key: 'l', style: { fontSize: '11px', fontWeight: 600, color: '#9a9aa2' } }, 'Peer')\n ])\n ])\n ])),\n h('div', { key: 'badges', style: { position: 'relative', zIndex: 2, display: 'flex', flexWrap: 'wrap', gap: '8px' } },\n ['ECDH P-384', 'AES-256-GCM', 'Perfect Forward Secrecy'].map((label) =>\n h('span', { key: label, style: { display: 'inline-flex', alignItems: 'center', gap: '6px', padding: '6px 11px', borderRadius: '8px', border: '1px solid rgba(255,255,255,0.07)', background: 'rgba(255,255,255,0.025)', fontFamily: MONO, fontSize: '11px', fontWeight: 500, color: '#9a9aa2' } }, [\n h('span', { key: 'dot', style: { width: '5px', height: '5px', borderRadius: '50%', background: C_GREEN } }),\n label\n ])\n )\n )\n ]);\n\n // \u2500\u2500 RIGHT PANEL \u00B7 flow body (varies by step) \u2500\u2500\n const segToggle = atIntro && h('div', { key: 'seg', style: { position: 'relative', display: 'flex', padding: '4px', borderRadius: '12px', border: '1px solid rgba(255,255,255,0.07)', background: '#141416', marginBottom: '26px' } }, [\n h('div', { key: 'ind', style: { position: 'absolute', top: '4px', bottom: '4px', left: '4px', width: 'calc(50% - 4px)', borderRadius: '9px', background: 'rgba(255,255,255,0.07)', border: '1px solid rgba(255,255,255,0.08)', transform: isCreate ? 'translateX(0%)' : 'translateX(100%)', transition: 'transform .26s cubic-bezier(.3,.8,.3,1)' } }),\n h('button', { key: 'c', className: 'sb-seg-btn', onClick: () => setMode('create'), style: { position: 'relative', zIndex: 1, flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '11px', border: 'none', background: 'transparent', color: isCreate ? '#f4f4f6' : '#7b7b83', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer' } }, [fa('fa-plus', { key: 'i' }), 'Create']),\n h('button', { key: 'j', className: 'sb-seg-btn', onClick: () => setMode('join'), style: { position: 'relative', zIndex: 1, flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '11px', border: 'none', background: 'transparent', color: !isCreate ? '#f4f4f6' : '#7b7b83', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer' } }, [fa('fa-link', { key: 'i' }), 'Join'])\n ]);\n\n const backButton = (key) => h('button', { key: key || 'back', className: 'sb-soft-btn', onClick: resetToSelect, style: { display: 'inline-flex', alignItems: 'center', gap: '6px', marginBottom: '14px', padding: '6px 11px 6px 8px', borderRadius: '8px', border: '1px solid rgba(255,255,255,0.08)', background: 'transparent', color: '#9a9aa2', fontFamily: 'inherit', fontSize: '12.5px', fontWeight: 600, cursor: 'pointer' } }, [fa('fa-chevron-left', { key: 'i' }), 'Back']);\n\n // credential code block (offer/answer text fallback + copy)\n const credBlock = h('div', { key: 'codeblock', style: { borderRadius: '13px', border: '1px solid rgba(255,255,255,0.08)', background: '#141416', overflow: 'hidden', marginBottom: '16px' } }, [\n h('div', { key: 'bar', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '9px 12px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(0,0,0,0.2)' } }, [\n h('span', { key: 'dot', style: { width: '7px', height: '7px', borderRadius: '50%', background: accent } }),\n h('span', { key: 'tag', style: { fontFamily: MONO, fontSize: '10.5px', fontWeight: 600, color: '#8a8a92' } }, `${isCreate ? 'offer' : 'answer'} \u00B7 or copy text`),\n h('button', { key: 'copy', onClick: copyCred, style: { marginLeft: 'auto', padding: '4px 9px', borderRadius: '6px', border: `1px solid ${copied ? 'rgba(62,207,142,0.4)' : 'rgba(255,255,255,0.1)'}`, background: copied ? 'rgba(62,207,142,0.1)' : 'rgba(255,255,255,0.04)', color: copied ? C_GREEN : '#b3b3ba', fontFamily: 'inherit', fontSize: '11px', fontWeight: 600, cursor: 'pointer', transition: 'all .14s' } }, copied ? 'Copied' : 'Copy')\n ]),\n // The handshake code is sensitive \u2014 keep it blurred until the\n // user deliberately reveals it, underscoring that it must be\n // shared only over a channel they trust.\n h('div', { key: 'codewrap', style: { position: 'relative' } }, [\n h('div', { key: 'code', className: 'sb-sc', style: { fontFamily: MONO, fontSize: '11px', lineHeight: 1.55, color: '#c9ccd8', wordBreak: 'break-all', padding: '11px 12px', maxHeight: '72px', overflowY: 'auto', filter: codeRevealed ? 'none' : 'blur(6px)', userSelect: codeRevealed ? 'text' : 'none', transition: 'filter .2s' } }, credCode),\n !codeRevealed && h('button', { key: 'reveal', onClick: () => setCodeRevealed(true), style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', border: 'none', background: 'rgba(20,20,22,0.25)', color: '#cfcfd4', fontFamily: 'inherit', fontSize: '12px', fontWeight: 600, cursor: 'pointer' } }, [\n fa('fa-eye', { key: 'i', fontSize: '15px' }),\n 'Click to reveal \u2014 keep this code private'\n ])\n ])\n ]);\n\n const showQrButton = qrCodeUrl && h('button', { key: 'showqr', onClick: () => setQrModalOpen(true), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '15px 16px', borderRadius: '14px', border: `1px solid ${isCreate ? 'rgba(240,137,42,0.3)' : 'rgba(62,207,142,0.3)'}`, background: isCreate ? 'rgba(240,137,42,0.06)' : 'rgba(62,207,142,0.06)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left', marginBottom: '14px' } }, [\n h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: isCreate ? 'rgba(240,137,42,0.12)' : 'rgba(62,207,142,0.12)', border: `1px solid ${isCreate ? 'rgba(240,137,42,0.28)' : 'rgba(62,207,142,0.28)'}` } }, fa('fa-qrcode', { color: accent, fontSize: '18px' })),\n h('span', { key: 'tx', style: { flex: 1 } }, [\n h('span', { key: 't', style: { display: 'block', fontSize: '14.5px', fontWeight: 700, color: '#f4f4f6' } }, 'Show QR code'),\n h('span', { key: 's', style: { display: 'block', fontSize: '12.5px', color: '#8a8a92', marginTop: '1px' } }, `Full-screen \u00B7 let your peer scan${(qrFramesTotal || 0) > 1 ? ` all ${qrFramesTotal} frames` : ''}`)\n ]),\n fa('fa-chevron-right', { color: '#6b6b73' })\n ]);\n\n let inner;\n if (showVerification) {\n const verified = bothVerificationsConfirmed;\n const cells = (verificationCode || '').split('').map((ch, i) =>\n h('div', { key: i, style: { flex: 1, maxWidth: '46px', aspectRatio: '0.82', display: 'grid', placeItems: 'center', borderRadius: '10px', border: '1px solid rgba(62,207,142,0.25)', background: 'rgba(62,207,142,0.05)', fontFamily: MONO, fontSize: '22px', fontWeight: 700, color: C_GREEN } }, ch));\n inner = h('div', { key: 'verify', style: { animation: 'sbUp .3s ease' } }, [\n !verified && backButton('vback'),\n h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '11px', marginBottom: '8px' } }, [\n h('div', { key: 'i', style: { width: '34px', height: '34px', flex: 'none', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.1)', border: '1px solid rgba(62,207,142,0.25)' } }, fa('fa-shield-alt', { color: C_GREEN })),\n h('h2', { key: 't', style: { margin: 0, fontSize: '21px', fontWeight: 800, letterSpacing: '-0.4px', color: '#f4f4f6' } }, 'Security verification')\n ]),\n h('p', { key: 'sub', style: { margin: '0 0 18px', fontSize: '13.5px', lineHeight: 1.55, color: '#8a8a92' } }, 'Compare this safety code with your peer over a separate channel (voice / in person), then type it to unlock the chat.'),\n h('div', { key: 'cells', style: { display: 'flex', gap: '6px', justifyContent: 'center', marginBottom: '20px', flexWrap: 'wrap' } }, cells),\n verified\n ? h('div', { key: 'ok', style: { display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', padding: '24px 16px', borderRadius: '16px', border: '1px solid rgba(62,207,142,0.25)', background: 'rgba(62,207,142,0.06)', animation: 'sbUp .3s ease' } }, [\n h('div', { key: 'i', style: { width: '54px', height: '54px', borderRadius: '16px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.14)', border: '1px solid rgba(62,207,142,0.35)', marginBottom: '14px' } }, fa('fa-check', { color: C_GREEN, fontSize: '24px' })),\n h('div', { key: 't', style: { fontSize: '18px', fontWeight: 800, color: '#f4f4f6' } }, 'Channel verified'),\n h('div', { key: 's', style: { fontSize: '13.5px', color: '#8a8a92', marginTop: '5px' } }, 'Both parties confirmed. Opening the secure chat\u2026')\n ])\n : h('div', { key: 'form' }, [\n h('div', { key: 'lbl', style: { fontSize: '12.5px', fontWeight: 600, color: '#9a9aa2', marginBottom: '8px' } }, 'Enter the verified code'),\n h('input', { key: 'in', value: sasInput, onChange: (e) => { setSasInput(e.target.value.toUpperCase()); if (sasError) setSasError(''); }, disabled: localVerificationConfirmed, autoFocus: true, autoComplete: 'off', spellCheck: false, placeholder: verificationCode ? 'Type code here' : 'Waiting for code\u2026', style: { width: '100%', textAlign: 'center', letterSpacing: '6px', borderRadius: '12px', border: `1px solid ${sasInput.length ? (canConfirm || localVerificationConfirmed ? 'rgba(62,207,142,0.5)' : 'rgba(255,255,255,0.14)') : 'rgba(255,255,255,0.08)'}`, background: '#141416', color: '#f4f4f6', fontFamily: MONO, fontSize: '20px', fontWeight: 700, padding: '14px', outline: 'none', textTransform: 'uppercase', marginBottom: sasError ? '8px' : '16px' } }),\n sasError && h('p', { key: 'err', style: { color: '#e5727a', fontSize: '12.5px', margin: '0 0 16px' } }, sasError),\n h('div', { key: 'status', style: { display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '16px' } }, [\n h('div', { key: 'you', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '11px 14px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.06)', background: '#141416' } }, [\n h('span', { key: 'l', style: { fontSize: '13px', color: '#cfcfd4', fontWeight: 600 } }, 'Your confirmation'),\n h('span', { key: 'v', style: { display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: '12.5px', fontWeight: 600, color: localVerificationConfirmed ? C_GREEN : '#7b7b83' } }, [fa(localVerificationConfirmed ? 'fa-check-circle' : 'fa-clock', { key: 'i' }), localVerificationConfirmed ? 'Confirmed' : 'Pending'])\n ]),\n h('div', { key: 'peer', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '11px 14px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.06)', background: '#141416' } }, [\n h('span', { key: 'l', style: { fontSize: '13px', color: '#cfcfd4', fontWeight: 600 } }, 'Peer confirmation'),\n h('span', { key: 'v', style: { display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: '12.5px', fontWeight: 600, color: remoteVerificationConfirmed ? C_GREEN : '#7b7b83' } }, [fa(remoteVerificationConfirmed ? 'fa-check-circle' : 'fa-clock', { key: 'i' }), remoteVerificationConfirmed ? 'Confirmed' : 'Pending'])\n ])\n ]),\n h('div', { key: 'btns', style: { display: 'flex', gap: '10px' } }, [\n h('button', { key: 'ok', onClick: handleSasConfirm, disabled: !canConfirm, style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '14px', borderRadius: '13px', border: 'none', background: canConfirm ? C_GREEN : 'rgba(255,255,255,0.05)', color: canConfirm ? '#08160e' : '#56565e', fontFamily: 'inherit', fontSize: '14.5px', fontWeight: 700, cursor: canConfirm ? 'pointer' : 'not-allowed', boxShadow: canConfirm ? '0 8px 24px rgba(62,207,142,0.25)' : 'none' } }, [fa(localVerificationConfirmed ? 'fa-check-circle' : 'fa-check', { key: 'i' }), localVerificationConfirmed ? 'Confirmed' : 'Confirm code']),\n h('button', { key: 'no', onClick: handleVerificationReject, style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '7px', padding: '14px 16px', borderRadius: '13px', border: '1px solid rgba(229,114,122,0.3)', background: 'transparent', color: '#e5727a', fontFamily: 'inherit', fontSize: '13.5px', fontWeight: 600, cursor: 'pointer' } }, [fa('fa-times', { key: 'i' }), \"Don't match\"])\n ])\n ])\n ]);\n } else if (isGenerating) {\n const genSteps = ['Generating ECDH P-384 key pair', 'Deriving verification code', 'Pinning Perfect Forward Secrecy'];\n inner = h('div', { key: 'gen', style: { animation: 'sbUp .28s ease' } }, [\n h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '13px', marginBottom: '22px' } }, [\n h('div', { key: 'sp', style: { width: '44px', height: '44px', flex: 'none', display: 'grid', placeItems: 'center' } }, fa('fa-circle-notch', { color: C_ORANGE, fontSize: '32px', animation: 'sbSpin 1s linear infinite' })),\n h('div', { key: 'tx' }, [\n h('h2', { key: 't', style: { margin: 0, fontSize: '20px', fontWeight: 800, letterSpacing: '-0.4px', color: '#f4f4f6' } }, isCreate ? 'Securing your channel' : 'Building your answer'),\n h('p', { key: 's', style: { margin: '3px 0 0', fontSize: '13px', color: '#8a8a92' } }, 'Forging keys strong enough to resist tampering.')\n ])\n ]),\n h('div', { key: 'steps', style: { display: 'flex', flexDirection: 'column', borderRadius: '13px', border: '1px solid rgba(255,255,255,0.07)', background: '#141416', overflow: 'hidden' } },\n genSteps.map((label, i) => {\n const done = genProgress > i;\n const active = genProgress === i;\n return h('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: '12px', padding: '13px 15px', borderTop: i ? '1px solid rgba(255,255,255,0.05)' : 'none', transition: 'background .3s', background: done ? 'rgba(62,207,142,0.04)' : 'transparent' } }, [\n h('div', { key: 'd', style: { flex: 'none', width: '20px', height: '20px', borderRadius: '50%', display: 'grid', placeItems: 'center', background: done ? 'rgba(62,207,142,0.12)' : (active ? 'rgba(240,137,42,0.12)' : 'rgba(255,255,255,0.04)'), border: `1px solid ${done ? 'rgba(62,207,142,0.3)' : (active ? 'rgba(240,137,42,0.3)' : 'rgba(255,255,255,0.1)')}`, transition: 'all .3s' } },\n done\n ? fa('fa-check', { color: C_GREEN, fontSize: '11px' })\n : h('span', { style: { width: '6px', height: '6px', borderRadius: '50%', background: active ? C_ORANGE : '#56565e', animation: active ? 'sbBlink 1s ease-in-out infinite' : 'none' } })),\n h('span', { key: 'l', style: { fontSize: '13.5px', color: done ? '#cfcfd4' : (active ? '#e8e8eb' : '#6b6b73'), transition: 'color .3s' } }, label)\n ]);\n })\n )\n ]);\n } else if (isOfferCred || isAnswerCred) {\n inner = h('div', { key: 'cred', style: { animation: 'sbUp .3s ease' } }, [\n backButton('cback'),\n h('h2', { key: 'h', style: { margin: '0 0 6px', fontSize: '23px', fontWeight: 800, letterSpacing: '-0.5px', color: '#f4f4f6' } }, isCreate ? 'Share your invitation' : 'Send back your answer'),\n h('p', { key: 'p', style: { margin: '0 0 18px', fontSize: '14px', lineHeight: 1.55, color: '#8a8a92' } }, isCreate ? 'Show the QR or send the code to your peer. It is one-time and expires shortly.' : 'Give this answer to the channel creator so they can finish the handshake.'),\n showQrButton,\n credBlock,\n isOfferCred && h('div', { key: 'offerextra', style: { marginTop: '4px' } }, [\n h('div', { key: 'lbl', style: { fontSize: '12.5px', fontWeight: 600, color: '#9a9aa2', marginBottom: '8px' } }, \"Then receive the answer your peer sends back\"),\n h('div', { key: 'ta', style: { borderRadius: '12px', border: `1px solid ${hasAnswer ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.07)'}`, background: '#141416', padding: '11px 14px', marginBottom: '10px' } },\n h('textarea', { value: answerInput, onChange: (e) => { setAnswerInput(e.target.value); if (e.target.value.trim().length > 0 && typeof markAnswerCreated === 'function') markAnswerCreated(); }, rows: 2, placeholder: \"Paste peer's answer code\u2026\", style: { width: '100%', resize: 'none', border: 'none', outline: 'none', background: 'transparent', color: '#d7d7db', fontFamily: MONO, fontSize: '12px', lineHeight: 1.55, minHeight: '44px' } })),\n h('div', { key: 'btns', style: { display: 'flex', gap: '10px' } }, [\n h('button', { key: 'scan', className: 'sb-scan-btn', onClick: () => setShowQRScannerModal(true), style: { flex: 'none', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '14px 16px', borderRadius: '13px', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.04)', color: '#cfcfd4', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer' } }, [fa('fa-camera', { key: 'i' }), 'Scan']),\n h('button', { key: 'est', onClick: onConnect, disabled: !hasAnswer, style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '14px', borderRadius: '13px', border: 'none', background: hasAnswer ? C_ORANGE : 'rgba(255,255,255,0.05)', color: hasAnswer ? '#1a0f04' : '#56565e', fontFamily: 'inherit', fontSize: '14.5px', fontWeight: 700, cursor: hasAnswer ? 'pointer' : 'not-allowed', boxShadow: hasAnswer ? '0 8px 24px rgba(240,137,42,0.28)' : 'none' } }, 'Establish connection')\n ])\n ]),\n isAnswerCred && h('div', { key: 'answerextra', style: { marginTop: '4px', display: 'flex', alignItems: 'center', gap: '10px', padding: '12px 14px', borderRadius: '12px', border: '1px solid rgba(62,207,142,0.18)', background: 'rgba(62,207,142,0.05)' } }, [\n fa('fa-circle-notch', { key: 'i', color: C_GREEN, animation: 'sbSpin 1.4s linear infinite' }),\n h('span', { key: 't', style: { fontSize: '13px', color: '#cfcfd4', fontWeight: 500 } }, 'Send this answer to the creator, then wait \u2014 the chat opens once they connect.')\n ])\n ]);\n } else if (isCreate) {\n // CREATE intro\n inner = h('div', { key: 'introC', style: { animation: 'sbUp .28s ease' } }, [\n h('h2', { key: 'h', style: { margin: '0 0 6px', fontSize: '23px', fontWeight: 800, letterSpacing: '-0.5px', color: '#f4f4f6' } }, 'Create a new channel'),\n h('p', { key: 'p', style: { margin: '0 0 22px', fontSize: '14px', lineHeight: 1.55, color: '#8a8a92' } }, 'Your device generates the keys and a one-time invitation. Nothing touches a server.'),\n h('button', { key: 'gen', className: 'sb-gen-btn', onClick: () => { requestNotificationPermissionOnInteraction(); if (webrtcManagerRef.current) handleCreateOffer(); }, style: { width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '15px', borderRadius: '13px', border: 'none', background: C_ORANGE, color: '#1a0f04', fontFamily: 'inherit', fontSize: '15px', fontWeight: 700, cursor: 'pointer', boxShadow: '0 8px 24px rgba(240,137,42,0.28)' } }, [fa('fa-bolt', { key: 'i' }), 'Generate keys & invitation'])\n ]);\n } else {\n // JOIN intro\n inner = h('div', { key: 'introJ', style: { animation: 'sbUp .28s ease' } }, [\n h('h2', { key: 'h', style: { margin: '0 0 6px', fontSize: '23px', fontWeight: 800, letterSpacing: '-0.5px', color: '#f4f4f6' } }, 'Join a channel'),\n h('p', { key: 'p', style: { margin: '0 0 16px', fontSize: '14px', lineHeight: 1.55, color: '#8a8a92' } }, \"Scan your peer's QR with your camera, or paste their invitation code.\"),\n h('button', { key: 'scan', className: 'sb-scan-btn', onClick: () => { requestNotificationPermissionOnInteraction(); setShowQRScannerModal(true); }, style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '15px 16px', borderRadius: '14px', border: '1px solid rgba(62,207,142,0.3)', background: 'rgba(62,207,142,0.06)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left', marginBottom: '14px' } }, [\n h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: 'rgba(62,207,142,0.12)', border: '1px solid rgba(62,207,142,0.28)' } }, fa('fa-camera', { color: C_GREEN, fontSize: '18px' })),\n h('span', { key: 'tx', style: { flex: 1 } }, [\n h('span', { key: 't', style: { display: 'block', fontSize: '14.5px', fontWeight: 700, color: '#f4f4f6' } }, 'Scan QR with camera'),\n h('span', { key: 's', style: { display: 'block', fontSize: '12.5px', color: '#8a8a92', marginTop: '1px' } }, \"Fastest \u2014 point at your peer's screen\")\n ]),\n fa('fa-chevron-right', { color: '#6b6b73' })\n ]),\n h('div', { key: 'or', style: { display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '14px' } }, [\n h('span', { key: 'a', style: { flex: 1, height: '1px', background: 'rgba(255,255,255,0.07)' } }),\n h('span', { key: 'm', style: { fontSize: '11px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '0.7px' } }, 'or paste code'),\n h('span', { key: 'b', style: { flex: 1, height: '1px', background: 'rgba(255,255,255,0.07)' } })\n ]),\n h('div', { key: 'ta', style: { borderRadius: '13px', border: `1px solid ${hasInvite ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.07)'}`, background: '#141416', padding: '13px 15px', marginBottom: '12px' } },\n h('textarea', { value: offerInput, onChange: (e) => { setOfferInput(e.target.value); if (e.target.value.trim().length > 0 && typeof markAnswerCreated === 'function') markAnswerCreated(); }, rows: 3, placeholder: 'Paste invitation code here\u2026', style: { width: '100%', resize: 'none', border: 'none', outline: 'none', background: 'transparent', color: '#d7d7db', fontFamily: MONO, fontSize: '12.5px', lineHeight: 1.6, minHeight: '66px' } })),\n h('button', { key: 'connect', onClick: () => { requestNotificationPermissionOnInteraction(); onCreateAnswer(); }, disabled: !hasInvite || connectionStatus === 'connecting', style: { width: '100%', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '14px', borderRadius: '13px', border: 'none', background: (hasInvite && connectionStatus !== 'connecting') ? C_ORANGE : 'rgba(255,255,255,0.05)', color: (hasInvite && connectionStatus !== 'connecting') ? '#1a0f04' : '#56565e', fontFamily: 'inherit', fontSize: '15px', fontWeight: 700, cursor: (hasInvite && connectionStatus !== 'connecting') ? 'pointer' : 'not-allowed', boxShadow: (hasInvite && connectionStatus !== 'connecting') ? '0 8px 24px rgba(240,137,42,0.28)' : 'none' } }, connectionStatus === 'connecting' ? 'Processing\u2026' : 'Connect')\n ]);\n }\n\n // Desktop downloads (real GitHub release assets) + OS detection.\n const DOWNLOADS = {\n mac: { name: 'macOS', format: '.dmg \u00B7 Apple Silicon & Intel', icon: 'fab fa-apple', url: 'https://github.com/SecureBitChat/securebit-desktop/releases/download/v0.1.0/SecureBit.Chat_0.1.0_x64.dmg' },\n win: { name: 'Windows', format: '.exe \u00B7 64-bit installer', icon: 'fab fa-windows', url: 'https://github.com/SecureBitChat/securebit-desktop/releases/latest/download/SecureBit.Chat_0.1.0_x64-setup.exe' },\n linux: { name: 'Linux', format: '.AppImage', icon: 'fab fa-linux', url: 'https://github.com/SecureBitChat/securebit-desktop/releases/latest/download/SecureBit.Chat_0.1.0_amd64.AppImage' }\n };\n const detectOS = () => {\n const ua = (navigator.userAgent || '') + ' ' + (navigator.platform || '');\n if (/Mac|iPhone|iPad|iPod/i.test(ua) && !/Android/i.test(ua)) return 'mac';\n if (/Win/i.test(ua)) return 'win';\n if (/Linux/i.test(ua) && !/Android/i.test(ua)) return 'linux';\n return 'win';\n };\n const detectedOS = detectOS();\n const otherOS = ['mac', 'win', 'linux'].filter((k) => k !== detectedOS);\n const dlLink = (url) => { try { window.open(url, '_blank', 'noopener'); } catch (e) {} };\n\n const platformsMenu = platformsOpen && h('div', { key: 'platmenu', className: 'sb-platforms-menu', style: { position: 'absolute', left: 0, bottom: 'calc(100% + 10px)', width: '344px', maxWidth: '100%', borderRadius: '16px', border: '1px solid rgba(255,255,255,0.1)', background: '#161618', boxShadow: '0 24px 60px rgba(0,0,0,0.55)', overflow: 'hidden', zIndex: 25, animation: 'sbUp .2s ease' } }, [\n h('div', { key: 'mh', style: { display: 'flex', alignItems: 'center', gap: '10px', padding: '14px 16px', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [\n h('div', { key: 't', style: { flex: 1, lineHeight: 1.2 } }, [\n h('div', { key: 'a', style: { fontSize: '14px', fontWeight: 800, color: '#f4f4f6' } }, 'Download SecureBit'),\n h('div', { key: 'b', style: { fontSize: '11.5px', color: '#7b7b83' } }, 'Free \u00B7 open source')\n ]),\n h('span', { key: 'pill', style: { fontFamily: MONO, fontSize: '10px', fontWeight: 600, color: C_GREEN, padding: '3px 8px', borderRadius: '6px', background: 'rgba(62,207,142,0.1)', border: '1px solid rgba(62,207,142,0.22)' } }, \"You're on Web\")\n ]),\n h('div', { key: 'rec', style: { padding: '12px 12px 6px' } },\n h('button', { key: 'b', onClick: () => dlLink(DOWNLOADS[detectedOS].url), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '12px', padding: '13px 14px', borderRadius: '12px', border: '1px solid rgba(240,137,42,0.4)', background: 'rgba(240,137,42,0.08)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left' } }, [\n h('span', { key: 'ic', style: { flex: 'none', display: 'grid', placeItems: 'center', width: '38px', height: '38px', borderRadius: '11px', background: 'rgba(240,137,42,0.14)', border: '1px solid rgba(240,137,42,0.3)', color: C_ORANGE } }, h('i', { className: DOWNLOADS[detectedOS].icon, style: { fontSize: '17px' } })),\n h('span', { key: 'tx', style: { flex: 1, minWidth: 0 } }, [\n h('span', { key: 'n', style: { display: 'block', fontSize: '13.5px', fontWeight: 700, color: '#f4f4f6' } }, DOWNLOADS[detectedOS].name),\n h('span', { key: 'f', style: { display: 'block', fontSize: '11px', color: '#f0b072', marginTop: '1px' } }, `Recommended for this device \u00B7 ${DOWNLOADS[detectedOS].format}`)\n ]),\n fa('fa-download', { color: C_ORANGE })\n ])),\n h('div', { key: 'others', style: { padding: '0 12px 8px', display: 'flex', flexDirection: 'column', gap: '2px' } },\n otherOS.map((k) => h('button', { key: k, onClick: () => dlLink(DOWNLOADS[k].url), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '12px', padding: '11px 14px', borderRadius: '11px', border: 'none', background: 'transparent', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left' } }, [\n h('span', { key: 'ic', style: { flex: 'none', display: 'grid', placeItems: 'center', width: '34px', height: '34px', borderRadius: '10px', background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)', color: '#cfcfd4' } }, h('i', { className: DOWNLOADS[k].icon, style: { fontSize: '15px' } })),\n h('span', { key: 'tx', style: { flex: 1, minWidth: 0 } }, [\n h('span', { key: 'n', style: { display: 'block', fontSize: '13px', fontWeight: 600, color: '#e8e8eb' } }, DOWNLOADS[k].name),\n h('span', { key: 'f', style: { display: 'block', fontSize: '11px', color: '#7b7b83', marginTop: '1px' } }, DOWNLOADS[k].format)\n ]),\n fa('fa-download', { color: '#8a8a92' })\n ]))),\n h('div', { key: 'soon', style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '12px 16px', borderTop: '1px solid rgba(255,255,255,0.06)', background: 'rgba(255,255,255,0.015)' } }, [\n fa('fa-clock', { key: 'i', color: '#6b6b73' }),\n h('span', { key: 't', style: { fontSize: '11.5px', lineHeight: 1.45, color: '#7b7b83' } }, 'Mobile (iOS, Android) and browser extensions (Chrome, Firefox, Opera) are coming soon.')\n ])\n ]);\n\n const footer = h('div', { key: 'footer', className: 'sb-conn-footer', style: { position: 'relative', marginTop: '30px', paddingTop: '18px', borderTop: '1px solid rgba(255,255,255,0.06)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '12px', flexWrap: 'wrap' } }, [\n h('button', { key: 'dl', onClick: () => setPlatformsOpen((v) => !v), style: { display: 'inline-flex', alignItems: 'center', gap: '9px', padding: '8px 13px 8px 9px', borderRadius: '10px', border: `1px solid ${platformsOpen ? 'rgba(240,137,42,0.4)' : 'rgba(255,255,255,0.08)'}`, background: platformsOpen ? 'rgba(240,137,42,0.06)' : 'rgba(255,255,255,0.02)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', transition: 'all .15s' } }, [\n fa('fa-download', { key: 'i', color: C_ORANGE }),\n h('span', { key: 't', style: { fontSize: '12.5px', fontWeight: 700, color: '#e8e8eb' } }, 'Download desktop app'),\n fa('fa-chevron-down', { key: 'c', color: '#6b6b73', style: { fontSize: '11px', transform: platformsOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } })\n ]),\n h('button', { key: 'settings', className: 'sb-link', onClick: () => setShowIceSettings && setShowIceSettings(true), style: { display: 'inline-flex', alignItems: 'center', gap: '7px', background: 'none', border: 'none', color: '#8a8a92', fontFamily: 'inherit', fontSize: '12.5px', fontWeight: 600, cursor: 'pointer' } }, [fa('fa-sliders-h', { key: 'i' }), 'Advanced settings']),\n platformsMenu\n ]);\n\n // Advanced settings overlay \u2014 rendered inside the right column,\n // sliding up over it (z-30), exactly as in the design.\n const settingsOverlay = (showIceSettings && typeof window !== 'undefined' && window.IceServerSettings)\n ? h(window.IceServerSettings, {\n key: 'ice-settings',\n isOpen: true,\n embedded: true,\n onClose: () => setShowIceSettings(false),\n initial: {\n useCustom: Array.isArray(customIceServers) && customIceServers.length > 0,\n serversText: iceServersText,\n privacyMode: relayOnlyMode ? 'relay-only' : 'standard',\n persisted: iceSettingsPersisted\n },\n hasSaved: iceSettingsPersisted,\n onApply: handleApplyIceSettings,\n onForget: handleForgetIceSettings\n })\n : null;\n\n const rightPanel = h('div', { key: 'right', style: compact\n ? { flex: 1, minWidth: 0, width: '100%', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', height: '100%' }\n : { flex: '0.95 1 460px', minWidth: 'min(100%, 320px)', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', height: '100vh' } }, [\n h('div', { key: 'scroll', className: 'custom-scrollbar', style: { flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', padding: '42px 44px' } },\n h('div', { style: { maxWidth: '430px', width: '100%', margin: 'auto' } }, [\n h('div', { key: 'kicker', style: { fontFamily: MONO, fontSize: '11px', fontWeight: 600, color: '#6b6b73', textTransform: 'uppercase', letterSpacing: '1px', marginBottom: '10px' } }, kicker),\n segToggle,\n inner,\n footer\n ])),\n settingsOverlay\n ]);\n\n // \u2500\u2500 QR display modal (real qrCodeUrl + frame controls) \u2500\u2500\n const qrModal = (qrModalOpen && qrCodeUrl) && h('div', { key: 'qrmodal', onClick: () => setQrModalOpen(false), style: { position: 'fixed', inset: 0, zIndex: 50, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', background: 'rgba(6,6,8,0.82)', backdropFilter: 'blur(10px)', animation: 'sbUp .2s ease' } },\n h('div', { onClick: (e) => e.stopPropagation(), style: { width: '100%', maxWidth: '460px', borderRadius: '22px', border: '1px solid rgba(255,255,255,0.1)', background: '#111113', boxShadow: '0 30px 90px rgba(0,0,0,0.6)', overflow: 'hidden' } }, [\n h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '11px', padding: '18px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [\n h('span', { key: 'd', style: { width: '9px', height: '9px', borderRadius: '50%', background: accent } }),\n h('div', { key: 'tx', style: { flex: 1, lineHeight: 1.2 } }, [\n h('div', { key: 't', style: { fontSize: '15.5px', fontWeight: 800, color: '#f4f4f6' } }, isCreate ? 'Share your invitation' : 'Send back your answer'),\n h('div', { key: 's', style: { fontSize: '12px', color: '#7b7b83' } }, `${isCreate ? 'offer' : 'answer'} \u00B7 one-time`)\n ]),\n h('button', { key: 'x', onClick: () => setQrModalOpen(false), style: { width: '32px', height: '32px', display: 'grid', placeItems: 'center', borderRadius: '9px', border: 'none', background: 'rgba(255,255,255,0.05)', color: '#9a9aa2', cursor: 'pointer' } }, fa('fa-times'))\n ]),\n h('div', { key: 'body', style: { padding: '22px 24px 24px' } }, [\n h('div', { key: 'qr', style: { position: 'relative', width: '100%', aspectRatio: '1', borderRadius: '18px', overflow: 'hidden', background: '#fff', padding: '18px', display: 'grid', placeItems: 'center' } },\n h('img', { src: qrCodeUrl, alt: 'QR code', style: { width: '100%', height: '100%', objectFit: 'contain', display: 'block' } })),\n h('div', { key: 'ctrls', style: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '12px', marginTop: '18px' } }, [\n (qrFramesTotal || 0) >= 1 && h('div', { key: 'frame', style: { display: 'flex', alignItems: 'center', gap: '9px' } }, [\n h('span', { key: 'l', style: { fontFamily: MONO, fontSize: '12px', fontWeight: 600, color: '#9a9aa2' } }, `Frame ${Math.max(1, qrFrameIndex || 1)} / ${qrFramesTotal || 1}`),\n h('div', { key: 'dots', style: { display: 'flex', gap: '5px' } }, Array.from({ length: qrFramesTotal || 1 }, (_, i) => h('span', { key: i, style: { width: '7px', height: '7px', borderRadius: '50%', background: (i + 1) === (qrFrameIndex || 1) ? accent : 'rgba(255,255,255,0.14)', transition: 'background .25s' } })))\n ]),\n (qrFramesTotal || 0) > 1 && h('div', { key: 'nav', style: { display: 'flex', alignItems: 'center', gap: '6px' } }, [\n h('button', { key: 'prev', onClick: prevQrFrame, style: { width: '40px', height: '36px', display: 'grid', placeItems: 'center', borderRadius: '10px', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.04)', color: '#cfcfd4', cursor: 'pointer' } }, fa('fa-chevron-left')),\n h('button', { key: 'auto', onClick: toggleQrManualMode, style: { display: 'inline-flex', alignItems: 'center', gap: '7px', padding: '9px 18px', borderRadius: '10px', border: `1px solid ${qrManualMode ? 'rgba(255,255,255,0.1)' : 'rgba(240,137,42,0.45)'}`, background: qrManualMode ? 'rgba(255,255,255,0.04)' : 'rgba(240,137,42,0.08)', color: qrManualMode ? '#9a9aa2' : C_ORANGE, fontFamily: 'inherit', fontSize: '13px', fontWeight: 600, cursor: 'pointer' } }, qrManualMode ? 'Manual' : 'Auto'),\n h('button', { key: 'next', onClick: nextQrFrame, style: { width: '40px', height: '36px', display: 'grid', placeItems: 'center', borderRadius: '10px', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(255,255,255,0.04)', color: '#cfcfd4', cursor: 'pointer' } }, fa('fa-chevron-right'))\n ]),\n h('p', { key: 'hint', style: { margin: '2px 0 0', textAlign: 'center', fontSize: '12px', lineHeight: 1.5, color: '#6b6b73' } }, (qrFramesTotal || 0) > 1 ? `The handshake is split across ${qrFramesTotal} frames \u2014 keep this open until your peer captures all of them.` : 'Keep this open until your peer captures the code.')\n ])\n ])\n ])\n );\n\n const hero = h('div', { key: 'hero', style: { display: 'flex', flexWrap: 'wrap', minHeight: '100vh', width: '100%', background: '#0f0f11', color: '#e8e8eb' } }, [leftPanel, rightPanel]);\n\n // Full-bleed dark bands \u2014 match the design mockups (not clamped to max-w-4xl).\n const uniqueSection = atIntro && h(UniqueFeatureSlider, { key: 'unique-features-slider' });\n const partnersSection = atIntro && h(BecomePartner, { key: 'become-partner' });\n const roadmapSection = atIntro && h(Roadmap, { key: 'roadmap' });\n const communitySection = atIntro && h(CommunityCTA, { key: 'community-cta' });\n\n // Ship the keyframes inside app.js so the animations can never go\n // stale against a service-worker-cached components.css.\n const keyframeStyle = h('style', { key: 'kf', dangerouslySetInnerHTML: { __html:\n '@keyframes sbFlowR{0%{left:4%;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:96%;opacity:0}}' +\n '@keyframes sbFlowL{0%{left:96%;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:4%;opacity:0}}' +\n '@keyframes sbPulse{0%,100%{transform:translate(-50%,-50%) scale(1);opacity:.5}50%{transform:translate(-50%,-50%) scale(1.5);opacity:0}}' +\n '@keyframes sbSpin{to{transform:rotate(360deg)}}' +\n '@keyframes sbUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}' +\n '@keyframes sbNode{0%,100%{box-shadow:0 0 0 0 rgba(62,207,142,0)}50%{box-shadow:0 0 0 6px rgba(62,207,142,.06)}}' +\n '@keyframes sbScan{0%{top:8%}100%{top:88%}}' +\n '@keyframes sbBlink{0%,100%{opacity:1}50%{opacity:.35}}'\n } });\n\n // Embedded in the chat column (additional session): just the create/connect\n // card filling the area \u2014 no hero, no marketing landing.\n if (compact) {\n return h('div', { className: 'sb-start', style: { flex: 1, minHeight: 0, width: '100%', display: 'flex', flexDirection: 'column', background: '#0f0f11', color: '#e8e8eb' } }, [keyframeStyle, rightPanel, qrModal]);\n }\n\n return h('div', { className: 'sb-start', style: { width: '100%' } }, [keyframeStyle, hero, uniqueSection, partnersSection, roadmapSection, communitySection, qrModal]);\n };\n \n // Global scroll function - defined outside components to ensure availability\n const createScrollToBottomFunction = (chatMessagesRef) => {\n return () => {\n if (chatMessagesRef && chatMessagesRef.current) {\n const scrollAttempt = () => {\n if (chatMessagesRef.current) {\n chatMessagesRef.current.scrollTo({\n top: chatMessagesRef.current.scrollHeight,\n behavior: 'smooth'\n });\n }\n };\n scrollAttempt();\n \n setTimeout(scrollAttempt, 50);\n setTimeout(scrollAttempt, 150);\n setTimeout(scrollAttempt, 300);\n \n requestAnimationFrame(() => {\n setTimeout(scrollAttempt, 100);\n });\n }\n };\n };\n\n\n // Runs the real-time cryptographic verification and shows a detailed report\n // modal (same behaviour the shared header used to provide on click).\n const runSecurityReport = async (webrtcManager) => {\n let securityData = null;\n try {\n if (webrtcManager && window.EnhancedSecureCryptoUtils) {\n securityData = await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(webrtcManager);\n }\n } catch (e) { /* ignore */ }\n if (!securityData) {\n alert('Security verification in progress\u2026\\nPlease wait for real-time cryptographic verification to complete.');\n return;\n }\n\n // Security verification report \u2014 translated from the Claude Design\n // component (Security Verification.dc.html): a gauge + live test grid\n // driven by the real cryptographic results in securityData.\n const MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n const esc = (s) => String(s).replace(/[&<>\"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '\"': '"' }[c]));\n const accent = securityData.color === 'orange' ? '#f0892a'\n : securityData.color === 'yellow' ? '#e3c84e'\n : securityData.color === 'red' ? '#e5727a'\n : '#3ecf8e';\n const accentRGB = securityData.color === 'orange' ? '240,137,42'\n : securityData.color === 'yellow' ? '227,200,78'\n : securityData.color === 'red' ? '229,114,122'\n : '62,207,142';\n const score = Math.max(0, Math.min(100, Math.round(securityData.score || 0)));\n const circ = 2 * Math.PI * 56;\n const dashArray = `${(circ * Math.min(1, score / 100)).toFixed(1)} ${circ.toFixed(1)}`;\n const level = String(securityData.level || 'SECURE').toUpperCase();\n const isReal = securityData.isRealData !== false;\n const entries = securityData.verificationResults ? Object.entries(securityData.verificationResults) : [];\n const passedCount = Number.isFinite(securityData.passedChecks) ? securityData.passedChecks : entries.filter(([, r]) => r && r.passed).length;\n const totalCount = Number.isFinite(securityData.totalChecks) ? securityData.totalChecks : entries.length;\n const verifiedAt = new Date(securityData.timestamp || Date.now()).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });\n\n const pretty = (k) => {\n let s = String(k).replace(/^verify/i, '').replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2').trim();\n s = s.replace(/\\b(ecdh|ecdsa|aes|gcm|hmac|pfs|sas|mitm|asn|dtls|hkdf|spki|oid|p384)\\b/gi, (m) => m.toUpperCase());\n return s.charAt(0).toUpperCase() + s.slice(1);\n };\n\n const checkIcon = ``;\n const xIcon = ``;\n const testsHTML = entries.map(([k, r], i) => {\n const passed = !!(r && r.passed);\n const desc = (r && r.details) || (passed ? 'Test passed' : 'Test failed or unavailable');\n const bg = passed ? '#161618' : '#121214';\n const border = passed ? 'rgba(62,207,142,0.16)' : 'rgba(229,114,122,0.18)';\n const iconBg = passed ? 'rgba(62,207,142,0.12)' : 'rgba(229,114,122,0.1)';\n const iconBorder = passed ? 'rgba(62,207,142,0.26)' : 'rgba(229,114,122,0.24)';\n const titleColor = passed ? '#f4f4f6' : '#cfcfd4';\n return `
\n ${passed ? checkIcon : xIcon}\n
\n
${esc(pretty(k))}
\n
${esc(desc)}
\n
\n
`;\n }).join('');\n\n const modal = document.createElement('div');\n modal.id = 'sb-security-report';\n modal.style.cssText = \"position:fixed; inset:0; z-index:10000; display:flex; align-items:center; justify-content:center; padding:24px; background:rgba(8,8,10,0.62); backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px); font-family:'Manrope',system-ui,-apple-system,sans-serif; overflow:auto;\";\n modal.innerHTML = `\n \n
\n \n
\n
\n
\n \n \n \n \n
\n ${score}\n / 100 pts\n
\n
\n
\n
Real-time security verification
\n
\n

Security level: ${esc(level)}

\n Active\n
\n
\n
Tests passed
${passedCount} / ${totalCount}
\n
Verified at
${esc(verifiedAt)}
\n
Source
${isReal ? 'Real cryptographic tests' : 'Simulated data'}
\n
\n
\n \n
\n
${testsHTML}
\n
\n \n ${isReal ? 'Real-time verification using actual cryptographic functions \u2014 no mock data.' : 'Warning: connection may not be fully established \u2014 values may be simulated.'}\n
\n
`;\n\n const onKey = (e) => { if (e.key === 'Escape') close(); };\n const close = () => { if (modal.parentNode) modal.remove(); document.removeEventListener('keydown', onKey); };\n modal.querySelector('.sv-close').addEventListener('click', close);\n modal.addEventListener('click', (e) => { if (e.target === modal) close(); });\n document.addEventListener('keydown', onKey);\n const rerun = modal.querySelector('.sv-rerun');\n rerun.addEventListener('mouseenter', () => { rerun.style.borderColor = 'rgba(240,137,42,0.45)'; rerun.style.color = '#f0892a'; });\n rerun.addEventListener('mouseleave', () => { rerun.style.borderColor = 'rgba(255,255,255,0.1)'; rerun.style.color = '#cfcfd4'; });\n rerun.addEventListener('click', () => { close(); runSecurityReport(webrtcManager); });\n document.body.appendChild(modal);\n };\n\n // In-chat header matching the SecureBit Chat design: logo + version,\n // a \"Secure\" pill (click = run the security verification report; the chevron\n // toggles the network/crypto detail panel), a connection indicator, Disconnect.\n const SecureBitChatHeader = ({ status, onDisconnect, webrtcManager, title, isOffline, peerPresence, onRenameTitle }) => {\n const MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n const [showNetwork, setShowNetwork] = React.useState(false);\n const [sec, setSec] = React.useState(null);\n const [editingName, setEditingName] = React.useState(false);\n const [nameDraft, setNameDraft] = React.useState('');\n\n React.useEffect(() => {\n let alive = true;\n const fetchSec = async () => {\n try {\n if (!webrtcManager) return;\n let data = null;\n if (typeof webrtcManager.getRealSecurityLevel === 'function') data = await webrtcManager.getRealSecurityLevel();\n else if (typeof webrtcManager.calculateAndReportSecurityLevel === 'function') data = await webrtcManager.calculateAndReportSecurityLevel();\n else if (window.EnhancedSecureCryptoUtils) data = await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(webrtcManager);\n if (alive && data && data.isRealData !== false) setSec(data);\n } catch (e) { /* ignore */ }\n };\n fetchSec();\n const onCalc = (e) => { if (alive && e.detail && e.detail.securityData) setSec(e.detail.securityData); };\n document.addEventListener('real-security-calculated', onCalc);\n const iv = setInterval(fetchSec, 15000);\n return () => { alive = false; clearInterval(iv); document.removeEventListener('real-security-calculated', onCalc); };\n }, [webrtcManager]);\n\n // Offline (our device lost connectivity) takes precedence over the P2P state \u2014\n // the data channel can stay \"open\" after the network drops, so without this the\n // header would keep saying \"Connected\" while the user is actually offline.\n const onlineConnected = status === 'connected' || status === 'verified';\n const dropped = status === 'disconnected' || status === 'peer_disconnected';\n const connected = onlineConnected && !isOffline;\n const connDot = (isOffline || dropped) ? '#e5727a' : (onlineConnected ? '#3ecf8e' : '#e3c84e');\n const connLabel = isOffline\n ? 'Offline'\n : (onlineConnected ? 'Connected'\n : (status === 'peer_disconnected' ? 'Peer disconnected'\n : (status === 'disconnected' ? 'Disconnected' : 'Connecting\u2026')));\n const connGlow = (isOffline || dropped) ? '0 0 0 3px rgba(229,114,122,0.16)' : (onlineConnected ? '0 0 0 3px rgba(62,207,142,0.16)' : '0 0 0 3px rgba(227,200,78,0.16)');\n // The avatar dot + subtitle reflect the PEER's advertised availability while connected.\n const peerDot = (onlineConnected && !isOffline) ? (PRESENCE_DOT[peerPresence] || '#3ecf8e') : connDot;\n const peerPresenceWord = (onlineConnected && !isOffline && peerPresence) ? (PRESENCE_WORD[peerPresence] || null) : null;\n const startRename = () => { setNameDraft(title || ''); setEditingName(true); };\n const commitRename = () => { if (typeof onRenameTitle === 'function') onRenameTitle(nameDraft); setEditingName(false); };\n const renameKey = (e) => { if (e.key === 'Enter') { e.preventDefault(); commitRename(); } else if (e.key === 'Escape') { setEditingName(false); } };\n const passed = sec && Number.isFinite(sec.passedChecks) ? sec.passedChecks : null;\n const total = sec && Number.isFinite(sec.totalChecks) ? sec.totalChecks : null;\n const scoreLabel = (passed != null && total) ? (passed + '/' + total) : (sec ? (sec.score + '%') : '\u2014');\n const accent = sec\n ? (sec.color === 'green' ? '#3ecf8e' : sec.color === 'orange' ? '#f0892a' : sec.color === 'yellow' ? '#e3c84e' : '#e5727a')\n : '#3ecf8e';\n\n const secBtn = React.createElement('div', {\n key: 'sec', title: 'Run security verification',\n onClick: () => runSecurityReport(webrtcManager),\n className: 'sb-secpill',\n style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '7px 13px', borderRadius: '9px', border: '1px solid ' + (showNetwork ? 'rgba(255,255,255,0.16)' : 'rgba(255,255,255,0.07)'), background: showNetwork ? 'rgba(255,255,255,0.05)' : 'rgba(255,255,255,0.02)', cursor: 'pointer', fontFamily: 'inherit', transition: 'all .15s' }\n }, [\n React.createElement('i', { key: 'i', className: 'fas fa-shield-halved', style: { color: accent, fontSize: '13px' } }),\n React.createElement('span', { key: 'l', className: 'sb-sec-label', style: { fontSize: '13px', fontWeight: 600, color: '#e8e8eb' } }, sec ? (sec.level || 'Secure') : 'Secure'),\n React.createElement('span', { key: 'd', className: 'sb-sec-div', style: { width: '1px', height: '13px', background: 'rgba(255,255,255,0.12)' } }),\n React.createElement('span', { key: 's', className: 'sb-sec-score', style: { fontFamily: MONO, fontSize: '11.5px', fontWeight: 500, color: '#8a8a92' } }, scoreLabel),\n React.createElement('button', {\n key: 'c', type: 'button', title: 'Network & crypto details',\n onClick: (e) => { e.stopPropagation(); setShowNetwork(v => !v); },\n style: { background: 'none', border: 'none', padding: 0, margin: 0, cursor: 'pointer', display: 'grid', placeItems: 'center' }\n }, React.createElement('i', { className: 'fas fa-chevron-down', style: { color: '#6b6b73', fontSize: '11px', transform: showNetwork ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } }))\n ]);\n\n const headerResponsiveCss = React.createElement('style', { key: 'hdr-css', dangerouslySetInnerHTML: { __html:\n // Mobile: leave room for the drawer hamburger and shed non-essential header\n // chrome so avatar + name + status + Disconnect fit a narrow screen.\n '@media (max-width:768px){' +\n '.sb-chat-header{padding-left:60px !important;gap:10px !important;}' +\n '.sb-chat-header .sb-sec-score,.sb-chat-header .sb-sec-label,.sb-chat-header .sb-sec-div{display:none !important;}' +\n '.sb-chat-header .sb-secpill{padding:8px !important;gap:6px !important;}' +\n '.sb-chat-header .sb-conn-text{display:none !important;}' +\n '.sb-chat-header .sb-conn{padding:9px !important;}' +\n '.sb-chat-header .sb-hdr-sub{display:none !important;}' +\n '}' +\n '@media (max-width:480px){.sb-chat-header{padding-right:12px !important;gap:8px !important;}}'\n } });\n const header = React.createElement('header', {\n key: 'hdr', className: 'sb-chat-header', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '24px', padding: '0 20px', height: '64px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(18,18,20,0.72)', backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)' }\n }, [\n headerResponsiveCss,\n // The SecureBit brand/logo lives in the left rail; this header identifies the\n // ACTIVE conversation \u2014 avatar monogram + local label + connection status.\n React.createElement('div', { key: 'left', style: { display: 'flex', alignItems: 'center', gap: '12px', minWidth: 0 } }, [\n React.createElement('div', { key: 'avatar', style: { position: 'relative', flex: 'none', width: '36px', height: '36px', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.09)', fontSize: '13px', fontWeight: 700, letterSpacing: '-0.3px', color: '#e8e8eb' } }, [\n monoInitials(title || 'Chat'),\n React.createElement('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: peerDot, border: '2px solid #121214' } })\n ]),\n editingName\n ? React.createElement('div', { key: 'edit', style: { display: 'flex', flexDirection: 'column', gap: '4px', minWidth: 0 } }, [\n React.createElement('div', { key: 'row', style: { display: 'flex', alignItems: 'center', gap: '6px' } }, [\n React.createElement('input', { key: 'in', autoFocus: true, value: nameDraft, maxLength: 32, placeholder: 'Name this chat', onChange: (e) => setNameDraft(e.target.value), onKeyDown: renameKey, onBlur: commitRename, style: { width: '210px', padding: '5px 10px', borderRadius: '8px', border: '1px solid rgba(240,137,42,0.55)', background: '#0f0f11', color: '#f4f4f6', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, outline: 'none' } }),\n React.createElement('button', { key: 'ok', onMouseDown: (e) => e.preventDefault(), onClick: commitRename, title: 'Save', style: { flex: 'none', width: '28px', height: '28px', borderRadius: '8px', display: 'grid', placeItems: 'center', border: 'none', background: '#f0892a', color: '#1a0f04', cursor: 'pointer' } }, React.createElement('i', { className: 'fas fa-check', style: { fontSize: '12px' } }))\n ]),\n React.createElement('div', { key: 'hint', style: { fontSize: '11px', color: '#56565e' } }, 'Local label \u00B7 stored only on this device')\n ])\n : React.createElement('div', { key: 'txt', style: { lineHeight: 1.2, minWidth: 0 } }, [\n React.createElement('div', { key: 'r1', style: { display: 'flex', alignItems: 'center', gap: '7px' } }, [\n React.createElement('span', { key: 'n', style: { fontSize: '15px', fontWeight: 800, letterSpacing: '-0.3px', color: '#f4f4f6', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, title || 'Secure chat'),\n React.createElement('button', { key: 'edit', onClick: startRename, title: 'Rename chat (local only)', style: { flex: 'none', width: '24px', height: '24px', borderRadius: '7px', display: 'grid', placeItems: 'center', border: 'none', background: 'transparent', color: '#56565e', cursor: 'pointer' } }, React.createElement('i', { className: 'fas fa-pen', style: { fontSize: '11px' } }))\n ]),\n React.createElement('div', { key: 'r2', className: 'sb-hdr-sub', style: { fontSize: '11px', color: '#6b6b73', fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, isOffline ? 'No network \u00B7 reconnecting' : (peerPresenceWord || (onlineConnected ? 'P2P \u00B7 end-to-end encrypted' : (status === 'peer_disconnected' ? 'Peer disconnected' : (status === 'disconnected' ? 'Disconnected' : 'Connecting\u2026')))))\n ])\n ]),\n secBtn,\n React.createElement('div', { key: 'right', className: 'sb-hdr-right', style: { display: 'flex', alignItems: 'center', gap: '9px' } }, [\n React.createElement('div', { key: 'conn', className: 'sb-conn', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 13px', borderRadius: '9px', border: '1px solid rgba(255,255,255,0.07)', background: 'rgba(255,255,255,0.02)' } }, [\n React.createElement('span', { key: 'dot', style: { flex: 'none', width: '7px', height: '7px', borderRadius: '50%', background: connDot, boxShadow: connGlow } }),\n React.createElement('span', { key: 't', className: 'sb-conn-text', style: { fontSize: '13px', fontWeight: 600, color: '#cfcfd4' } }, connLabel)\n ]),\n React.createElement('button', { key: 'dc', onClick: onDisconnect, className: 'sb-disconnect', style: { display: 'flex', alignItems: 'center', gap: '7px', padding: '8px 14px', borderRadius: '9px', border: '1px solid rgba(255,255,255,0.08)', background: 'transparent', color: '#9a9aa2', fontFamily: 'inherit', fontSize: '13px', fontWeight: 600, cursor: 'pointer', transition: 'all .15s' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-power-off', style: { fontSize: '12px' } }),\n React.createElement('span', { key: 't', className: 'sb-hide-sm' }, 'Disconnect')\n ])\n ])\n ]);\n\n const netPanel = showNetwork && React.createElement('div', {\n key: 'net', style: { flex: 'none', padding: '13px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(18,18,20,0.72)', backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)' }\n }, React.createElement('div', { style: { maxWidth: '1000px', margin: '0 auto', display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(140px,1fr))', gap: '14px', fontFamily: MONO } },\n [\n ['Transport', 'WebRTC \u00B7 DTLS'],\n ['Cipher', 'AES-256-GCM'],\n ['Key exchange', 'ECDH P-384'],\n ['Security', scoreLabel + (sec ? (' \u00B7 ' + sec.score + '%') : '')]\n ].map(([k, v], i) => React.createElement('div', { key: 'nf' + i }, [\n React.createElement('div', { key: 'k', style: { fontSize: '10px', color: '#6b6b73', textTransform: 'uppercase', letterSpacing: '0.6px', marginBottom: '4px' } }, k),\n React.createElement('div', { key: 'v', style: { fontSize: '12.5px', color: i === 3 ? accent : '#cfcfd4', fontWeight: 500 } }, v)\n ]))\n ));\n\n return React.createElement('div', { style: { flex: 'none' } }, [header, netPanel]);\n };\n\n\n const EnhancedChatInterface = ({\n title,\n isOffline,\n peerPresence,\n onRenameTitle,\n messages,\n messageInput,\n setMessageInput,\n onSendMessage,\n onDisconnect,\n keyFingerprint,\n isVerified,\n chatMessagesRef,\n scrollToBottom,\n webrtcManager,\n status,\n pendingIncomingFiles = [],\n onIncomingDecision,\n // Secure chat extras\n codeMode,\n setCodeMode,\n viewOnceMode,\n setViewOnceMode,\n viewOnceTtl,\n setViewOnceTtl,\n disappearTtl,\n setDisappearTtl,\n nowTick,\n onUnsendMessage,\n onMessageExpire\n }) => {\n const [showScrollButton, setShowScrollButton] = React.useState(false);\n const [showFileTransfer, setShowFileTransfer] = React.useState(false);\n // True only when the user opened the panel to SEND (shows the drop-zone).\n // Incoming-file auto-open leaves this false, so the receiver sees the\n // incoming request + receiving progress, not the \"send attachments\" UI.\n const [fileSendMode, setFileSendMode] = React.useState(false);\n const [showTimer, setShowTimer] = React.useState(false);\n const [showOnce, setShowOnce] = React.useState(false);\n const [showHandshake, setShowHandshake] = React.useState(false);\n const taRef = React.useRef(null);\n\n // Auto-grow the message textarea (and reset its height after sending).\n React.useEffect(() => {\n const el = taRef.current;\n if (!el || codeMode) return;\n el.style.height = 'auto';\n el.style.height = Math.min(el.scrollHeight, 240) + 'px';\n }, [messageInput, codeMode]);\n\n // Auto-open the file transfer panel when an incoming request arrives\n React.useEffect(() => {\n if (pendingIncomingFiles.length > 0) {\n setShowFileTransfer(true);\n }\n }, [pendingIncomingFiles.length]);\n\n React.useEffect(() => {\n if (chatMessagesRef.current && messages.length > 0) {\n const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;\n const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;\n if (isNearBottom) {\n const smoothScroll = () => {\n if (chatMessagesRef.current) {\n chatMessagesRef.current.scrollTo({\n top: chatMessagesRef.current.scrollHeight,\n behavior: 'smooth'\n });\n }\n };\n smoothScroll();\n setTimeout(smoothScroll, 50);\n setTimeout(smoothScroll, 150);\n }\n }\n }, [messages, chatMessagesRef]);\n\n const handleScroll = () => {\n if (chatMessagesRef.current) {\n const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;\n const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;\n setShowScrollButton(!isNearBottom);\n }\n };\n\n const handleScrollToBottom = () => {\n if (typeof scrollToBottom === 'function') {\n scrollToBottom();\n setShowScrollButton(false);\n } else if (chatMessagesRef.current) {\n chatMessagesRef.current.scrollTo({ top: chatMessagesRef.current.scrollHeight, behavior: 'smooth' });\n setShowScrollButton(false);\n }\n };\n\n const handleKeyPress = (e) => {\n if (e.key !== 'Enter') return;\n if (codeMode) {\n if (e.metaKey || e.ctrlKey) { e.preventDefault(); onSendMessage(); }\n } else if (!e.shiftKey) {\n e.preventDefault();\n onSendMessage();\n }\n };\n\n const isFileTransferReady = () => {\n if (!webrtcManager) return false;\n const connected = webrtcManager.isConnected ? webrtcManager.isConnected() : false;\n const verified = webrtcManager.isVerified || false;\n const hasDataChannel = webrtcManager.dataChannel && webrtcManager.dataChannel.readyState === 'open';\n return connected && verified && hasDataChannel;\n };\n\n // ---- design tokens / helpers ----\n const MONO = \"'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace\";\n const fmtShort = (s) => {\n if (!s) return '';\n if (s >= 86400 && s % 86400 === 0) return (s / 86400) + 'd';\n if (s >= 3600 && s % 3600 === 0) return (s / 3600) + 'h';\n if (s >= 60) return Math.round(s / 60) + 'm';\n return s + 's';\n };\n const chipStyle = (active) => ({\n display: 'flex', alignItems: 'center', gap: '6px', padding: '7px 11px', borderRadius: '8px',\n border: '1px solid ' + (active ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.07)'),\n background: active ? 'rgba(255,255,255,0.06)' : 'transparent',\n color: active ? '#fff' : '#9a9aa2',\n fontFamily: 'inherit', fontSize: '12.5px', fontWeight: 600, cursor: 'pointer', transition: 'all .15s'\n });\n const optStyle = (sel) => ({\n padding: '6px 12px', borderRadius: '8px',\n border: '1px solid ' + (sel ? 'rgba(255,255,255,0.22)' : 'rgba(255,255,255,0.07)'),\n background: sel ? 'rgba(255,255,255,0.07)' : 'transparent',\n color: sel ? '#fff' : '#8a8a92',\n fontFamily: MONO, fontSize: '12px', fontWeight: 500, cursor: 'pointer', transition: 'all .14s'\n });\n\n const timerDefs = [\n { label: 'Off', v: 0 }, { label: '5s', v: 5 }, { label: '30s', v: 30 },\n { label: '1m', v: 60 }, { label: '1h', v: 3600 }, { label: '24h', v: 86400 }\n ];\n const onceDefs = [\n { label: 'Off', v: 0 }, { label: '5s', v: 5 }, { label: '10s', v: 10 },\n { label: '30s', v: 30 }, { label: '1m', v: 60 }\n ];\n const onceSelected = viewOnceMode ? viewOnceTtl : 0;\n const pickTimer = (v) => { setDisappearTtl(v); setShowTimer(false); };\n const pickOnce = (v) => {\n if (v === 0) setViewOnceMode(false);\n else { setViewOnceTtl(v); setViewOnceMode(true); }\n setShowOnce(false);\n };\n\n const hasText = !!(messageInput && messageInput.trim());\n\n // System notices are surfaced inside the handshake/connection log card\n // (matching the design) rather than as bubbles in the message flow.\n const fmtT = (ts) => { try { return new Date(ts).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } catch (e) { return ''; } };\n // Connection log = string-only system notices (a React-element message\n // would render as \"[object Object]\" in the log, so it's excluded).\n const systemMessages = messages.filter((m) => m.type === 'system' && typeof m.message === 'string' && m.message.trim());\n const chatMessages = messages.filter((m) => m.type !== 'system');\n\n // ---- handshake / connection log card ----\n const handshakeCard = (isVerified || systemMessages.length > 0) && React.createElement('div', {\n key: 'handshake',\n style: { border: '1px solid rgba(255,255,255,0.07)', borderRadius: '12px', background: '#161618', overflow: 'hidden' }\n }, [\n React.createElement('button', {\n key: 'hs-btn', onClick: () => setShowHandshake(v => !v),\n style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '14px 16px', background: 'transparent', border: 'none', color: 'inherit', cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit' }\n }, [\n React.createElement('div', { key: 'ic', style: { flex: 'none', width: '30px', height: '30px', display: 'grid', placeItems: 'center' } },\n React.createElement('i', { className: 'fas fa-check', style: { color: '#3ecf8e', fontSize: '16px' } })\n ),\n React.createElement('div', { key: 'tx', style: { flex: 1, minWidth: 0 } }, [\n React.createElement('div', { key: 't1', style: { fontSize: '13.5px', fontWeight: 600, color: '#e8e8eb', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, 'Secure channel established'),\n React.createElement('div', { key: 't2', style: { fontSize: '12px', color: '#7b7b83', marginTop: '1px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, 'Verified \u00B7 Perfect Forward Secrecy' + (systemMessages.length ? (' \u00B7 ' + systemMessages.length + (systemMessages.length === 1 ? ' event' : ' events')) : ''))\n ]),\n React.createElement('i', { key: 'chev', className: 'fas fa-chevron-down', style: { flex: 'none', color: '#6b6b73', fontSize: '13px', transform: showHandshake ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } })\n ]),\n showHandshake && React.createElement('div', { key: 'hs-body', style: { padding: '2px 16px 14px 59px' } }, [\n systemMessages.length > 0 && React.createElement('div', { key: 'steps', className: 'sb-scroll', style: { marginBottom: '12px', maxHeight: '220px', overflowY: 'auto', paddingRight: '6px' } },\n systemMessages.map((m, i) => React.createElement('div', { key: 's' + i, style: { display: 'flex', gap: '11px', padding: '6px 0', borderTop: i === 0 ? 'none' : '1px solid rgba(255,255,255,0.04)' } }, [\n React.createElement('span', { key: 'd', style: { flex: 'none', width: '5px', height: '5px', borderRadius: '50%', background: '#3ecf8e', marginTop: '7px', opacity: 0.6 } }),\n React.createElement('span', { key: 't', style: { flex: 1, fontSize: '12.5px', color: '#9a9aa2', lineHeight: 1.5, wordBreak: 'break-word' } }, String(m.message || '').trim()),\n React.createElement('span', { key: 'tm', style: { flex: 'none', fontFamily: MONO, fontSize: '10.5px', color: '#56565e' } }, fmtT(m.timestamp))\n ]))\n ),\n keyFingerprint && React.createElement('div', { key: 'sn', style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '10px 12px', borderRadius: '9px', background: 'rgba(255,255,255,0.025)', border: '1px solid rgba(255,255,255,0.06)' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-lock', style: { color: '#8a8a92', fontSize: '12px' } }),\n React.createElement('span', { key: 'l', style: { fontSize: '11.5px', color: '#8a8a92' } }, 'Safety number'),\n React.createElement('span', { key: 'v', style: { fontFamily: MONO, fontSize: '12px', color: '#cfcfd4', letterSpacing: '0.8px', fontWeight: 500, wordBreak: 'break-all' } }, keyFingerprint)\n ])\n ])\n ]);\n\n // ---- empty state ----\n const emptyState = React.createElement('div', { key: 'empty', style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1, minHeight: '40vh' } },\n React.createElement('div', { style: { textAlign: 'center', maxWidth: '420px' } }, [\n React.createElement('img', { key: 'ic', src: '/logo/securebit-mark.svg', alt: 'SecureBit', style: { width: '60px', height: '60px', objectFit: 'contain', display: 'block', margin: '0 auto 16px' } }),\n React.createElement('h3', { key: 't', style: { fontSize: '17px', fontWeight: 700, color: '#e8e8eb', margin: '0 0 6px' } }, 'Secure channel is ready'),\n React.createElement('p', { key: 'p', style: { fontSize: '13px', color: '#7b7b83', margin: 0 } }, 'Every message is end-to-end encrypted on your device before it leaves.')\n ])\n );\n\n // ---- messages list ----\n const messagesArea = React.createElement('main', {\n key: 'main',\n ref: chatMessagesRef,\n onScroll: handleScroll,\n className: 'sb-scroll',\n style: { flex: 1, overflowY: 'auto', padding: '20px 20px 22px' }\n }, React.createElement('div', { style: { width: '100%', maxWidth: '1000px', margin: '0 auto', display: 'flex', flexDirection: 'column', gap: '16px', minHeight: '100%' } },\n chatMessages.length === 0\n ? [handshakeCard, emptyState]\n : [handshakeCard].concat(chatMessages.map((msg) => React.createElement(EnhancedChatMessage, {\n key: msg.id,\n message: msg.message,\n type: msg.type,\n timestamp: msg.timestamp,\n mid: msg.mid,\n status: msg.status,\n viewOnce: msg.viewOnce,\n viewOnceTtl: msg.viewOnceTtl,\n expiresAt: msg.expiresAt,\n expired: msg.expired,\n nowTick: nowTick,\n canUnsend: typeof onUnsendMessage === 'function',\n onUnsend: onUnsendMessage,\n onExpire: () => onMessageExpire && onMessageExpire(msg.id)\n })))\n ));\n\n // ---- option rows ----\n const timerRow = showTimer && React.createElement('div', { key: 'timer-row', style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '8px', padding: '10px 12px', marginBottom: '10px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.07)', background: '#161618' } },\n [React.createElement('span', { key: 'lbl', style: { fontSize: '12px', color: '#8a8a92', fontWeight: 600, marginRight: '4px' } }, 'Disappear after')].concat(\n timerDefs.map((d) => React.createElement('button', { key: 'td' + d.v, onClick: () => pickTimer(d.v), style: optStyle(disappearTtl === d.v) }, d.label))\n )\n );\n const onceRow = showOnce && React.createElement('div', { key: 'once-row', style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '8px', padding: '10px 12px', marginBottom: '10px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.07)', background: '#161618' } },\n [React.createElement('span', { key: 'lbl', style: { fontSize: '12px', color: '#8a8a92', fontWeight: 600, marginRight: '4px' } }, 'Visible for')].concat(\n onceDefs.map((d) => React.createElement('button', { key: 'od' + d.v, onClick: () => pickOnce(d.v), style: optStyle(onceSelected === d.v) }, d.label))\n )\n );\n\n // ---- file transfer panel ----\n const filePanel = showFileTransfer && React.createElement('div', { key: 'file-panel', style: { marginBottom: '10px' } },\n React.createElement(window.FileTransferComponent || (() => React.createElement('div', { style: { padding: '16px', textAlign: 'center', color: '#e5727a' } }, 'FileTransferComponent not loaded')), {\n webrtcManager: webrtcManager,\n isConnected: isFileTransferReady(),\n pendingIncomingFiles: pendingIncomingFiles,\n onIncomingDecision: onIncomingDecision,\n showDropzone: fileSendMode\n })\n );\n\n // ---- chips row ----\n const chipsRow = React.createElement('div', { key: 'chips', style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '8px', marginBottom: '10px' } }, [\n React.createElement('button', { key: 'files', onClick: () => {\n if (showFileTransfer && fileSendMode) { setShowFileTransfer(false); setFileSendMode(false); }\n else { setShowFileTransfer(true); setFileSendMode(true); }\n }, className: 'sb-chip', style: chipStyle(showFileTransfer && fileSendMode) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-paperclip', style: { fontSize: '13px' } }),\n (showFileTransfer && fileSendMode) ? 'Hide files' : 'Send files'\n ]),\n React.createElement('div', { key: 'right', style: { display: 'flex', alignItems: 'center', gap: '6px', flexWrap: 'wrap' } }, [\n React.createElement('button', { key: 'code', onClick: () => setCodeMode(v => !v), className: 'sb-chip', style: chipStyle(codeMode) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-code', style: { fontSize: '13px' } }), 'Code'\n ]),\n React.createElement('button', { key: 'once', onClick: () => { setShowOnce(v => !v); setShowTimer(false); }, className: 'sb-chip', style: chipStyle(showOnce || viewOnceMode) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-eye-slash', style: { fontSize: '13px' } }),\n viewOnceMode ? ('View once \u00B7 ' + fmtShort(viewOnceTtl)) : 'View once'\n ]),\n React.createElement('button', { key: 'timer', onClick: () => { setShowTimer(v => !v); setShowOnce(false); }, className: 'sb-chip', style: chipStyle(showTimer || disappearTtl > 0) }, [\n React.createElement('i', { key: 'i', className: 'fas fa-stopwatch', style: { fontSize: '13px' } }),\n disappearTtl > 0 ? ('Timer \u00B7 ' + fmtShort(disappearTtl)) : 'Timer'\n ])\n ])\n ]);\n\n // ---- code-mode header strip ----\n const codeStrip = codeMode && React.createElement('div', { key: 'code-strip', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 14px', border: '1px solid rgba(255,255,255,0.08)', borderBottom: 'none', borderRadius: '14px 14px 0 0', background: '#161618' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-code', style: { color: '#8a8a92', fontSize: '12px' } }),\n React.createElement('span', { key: 's', style: { fontSize: '11.5px', fontWeight: 600, color: '#8a8a92' } }, 'Code snippet \u00B7 formatting preserved \u00B7 \u2318\u21B5 to send'),\n React.createElement('button', { key: 'c', onClick: () => setCodeMode(false), className: 'sb-link', style: { marginLeft: 'auto', background: 'none', border: 'none', color: '#6b6b73', cursor: 'pointer', fontSize: '11.5px', fontFamily: 'inherit', fontWeight: 600 } }, 'Close')\n ]);\n\n // ---- input row ----\n const inputRow = React.createElement('div', {\n key: 'input',\n style: { display: 'flex', alignItems: 'flex-end', gap: '11px', padding: '11px 11px 11px 16px', border: '1px solid ' + (hasText ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.08)'), background: '#161618', borderRadius: codeMode ? '0 0 14px 14px' : '14px', transition: 'border .15s' }\n }, [\n React.createElement('div', { key: 'ta-wrap', style: { flex: 1, minWidth: 0 } }, [\n React.createElement('textarea', {\n key: 'ta',\n value: messageInput,\n ref: taRef,\n onChange: (e) => setMessageInput(e.target.value),\n onKeyDown: handleKeyPress,\n rows: 1,\n maxLength: 2000,\n placeholder: codeMode ? 'Paste or write code\u2026' : 'Type an encrypted message\u2026',\n className: 'sb-textarea',\n style: { width: '100%', minHeight: codeMode ? '120px' : '22px', maxHeight: '240px', resize: 'none', border: 'none', outline: 'none', background: 'transparent', color: '#e8e8eb', fontFamily: codeMode ? MONO : 'inherit', fontSize: codeMode ? '13px' : '14.5px', lineHeight: 1.55, padding: '6px 0' }\n }),\n React.createElement('div', { key: 'foot', style: { display: 'flex', alignItems: 'center', gap: '12px', marginTop: '3px' } }, [\n React.createElement('span', { key: 'enc', style: { display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '11px', color: '#56565e' } }, [\n React.createElement('i', { key: 'i', className: 'fas fa-lock', style: { color: '#3ecf8e', fontSize: '10px' } }),\n 'Encrypted on your device'\n ]),\n React.createElement('span', { key: 'cnt', style: { fontFamily: MONO, fontSize: '10.5px', color: '#56565e', marginLeft: 'auto' } }, (messageInput ? messageInput.length : 0) + '/2000')\n ])\n ]),\n React.createElement('button', {\n key: 'send', onClick: onSendMessage, disabled: !hasText, title: 'Send', className: 'sb-send',\n style: { flex: 'none', width: '44px', height: '44px', borderRadius: '11px', border: 'none', display: 'grid', placeItems: 'center', cursor: hasText ? 'pointer' : 'default', background: hasText ? '#f0892a' : 'rgba(255,255,255,0.05)', color: hasText ? '#1a0f04' : '#56565e', transition: 'all .15s' }\n }, React.createElement('i', { className: 'fas fa-paper-plane', style: { fontSize: '15px' } }))\n ]);\n\n const composer = React.createElement('footer', { key: 'composer', style: { flex: 'none', padding: '12px 20px 18px', background: '#0f0f11', borderTop: '1px solid rgba(255,255,255,0.05)' } },\n React.createElement('div', { style: { maxWidth: '1000px', margin: '0 auto' } }, [\n timerRow, onceRow, filePanel, chipsRow, codeStrip, inputRow\n ])\n );\n\n const scrollBtn = showScrollButton && React.createElement('button', {\n key: 'scrollbtn', onClick: handleScrollToBottom,\n style: { position: 'fixed', right: '24px', bottom: '150px', width: '44px', height: '44px', borderRadius: '50%', border: '1px solid rgba(255,255,255,0.1)', background: '#26262b', color: '#cfcfd4', display: 'grid', placeItems: 'center', cursor: 'pointer', zIndex: 50, boxShadow: '0 6px 20px rgba(0,0,0,0.4)' }\n }, React.createElement('i', { className: 'fas fa-arrow-down', style: { fontSize: '15px' } }));\n\n const chatHeader = React.createElement(SecureBitChatHeader, {\n key: 'chat-header', status: status, onDisconnect: onDisconnect, webrtcManager: webrtcManager, title: title, isOffline: isOffline, peerPresence: peerPresence, onRenameTitle: onRenameTitle\n });\n\n return React.createElement('div', {\n className: 'chat-container',\n style: { display: 'flex', flexDirection: 'column', height: '100vh', background: '#0f0f11', color: '#e8e8eb' }\n }, [chatHeader, messagesArea, scrollBtn, composer]);\n };\n \n \n // Build a chat message in the shape addMessageWithAutoScroll produces, so the\n // per-session callbacks can dispatch ADD_MESSAGE without an active-view setter.\n const buildSessionMessage = (message, type, opts = {}) => ({\n message,\n type,\n id: Date.now() + Math.random(),\n timestamp: (typeof opts.timestamp === 'number') ? opts.timestamp : Date.now(),\n mid: opts.mid,\n status: opts.status,\n viewOnce: opts.viewOnce === true,\n viewOnceTtl: (typeof opts.viewOnceTtl === 'number') ? opts.viewOnceTtl : 15,\n expiresAt: (typeof opts.expiresAt === 'number') ? opts.expiresAt : undefined\n });\n\n // Left rail listing every open session (design import: \"Multi Session\n // Concepts\"). Two desktop states \u2014 expanded (292px) and a collapsed icon dock\n // (72px) \u2014 plus a mobile slide-out drawer. Pure presentational: all data comes\n // from decorated session objects, all actions are callbacks.\n const SB_SVG = {\n chevL: '',\n chevR: '',\n plus: '',\n users: '',\n burger: ''\n };\n\n const SessionsSidebar = ({ chats, collapsed, drawerOpen, onToggleCollapse, onSelect, onNewChat, onRename, onCloseDrawer, myStatus, onSetStatus }) => {\n const h = React.createElement;\n const [editingId, setEditingId] = React.useState(null);\n const [draft, setDraft] = React.useState('');\n const [presenceOpen, setPresenceOpen] = React.useState(false);\n const startEdit = (c) => (e) => { e.stopPropagation(); setEditingId(c.id); setDraft(c.name); };\n const commitEdit = () => { if (editingId) { onRename(editingId, draft); setEditingId(null); } };\n const editKey = (e) => {\n if (e.key === 'Enter') { e.preventDefault(); commitEdit(); }\n else if (e.key === 'Escape') { setEditingId(null); }\n };\n const renameInput = (extra = {}) => h('input', {\n autoFocus: true,\n value: draft,\n onChange: (e) => setDraft(e.target.value),\n onKeyDown: editKey,\n onBlur: commitEdit,\n onClick: (e) => e.stopPropagation(),\n style: Object.assign({ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(240,137,42,0.5)', borderRadius: '6px', color: '#f4f4f6', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, padding: '2px 6px', outline: 'none' }, extra)\n });\n const icon = (svg, style) => h('span', { style: Object.assign({ display: 'grid', placeItems: 'center' }, style || {}), dangerouslySetInnerHTML: { __html: svg } });\n const avatar = (c, size, ring) => h('div', {\n style: { position: 'relative', flex: 'none', width: size + 'px', height: size + 'px', borderRadius: (size >= 44 ? 12 : 11) + 'px', display: 'grid', placeItems: 'center', background: c.active ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.035)', border: '1px solid rgba(255,255,255,' + (c.active ? '0.14' : '0.07') + ')', fontSize: '13px', fontWeight: 700, letterSpacing: '-0.3px', color: c.active ? '#f4f4f6' : '#9a9aa2' }\n }, [c.mono, h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: c.dot, border: '2px solid ' + ring } })]);\n\n // ---- Expanded list row ----\n const expandedRow = (c) => h('div', {\n key: c.id,\n onClick: () => onSelect(c.id),\n style: { position: 'relative', display: 'flex', alignItems: 'center', gap: '12px', padding: '11px 12px', marginBottom: '4px', borderRadius: '11px', background: c.active ? '#161618' : 'transparent', border: '1px solid ' + (c.active ? 'rgba(255,255,255,0.08)' : 'transparent'), cursor: 'pointer' }\n }, [\n c.active && h('span', { key: 'bar', style: { position: 'absolute', left: 0, top: '12px', bottom: '12px', width: '3px', borderRadius: '0 3px 3px 0', background: '#f0892a' } }),\n avatar(c, 38, c.active ? '#161618' : '#0c0c0e'),\n h('div', { key: 'body', style: { flex: 1, minWidth: 0 } }, [\n h('div', { key: 'top', style: { display: 'flex', alignItems: 'center', gap: '7px' } }, [\n editingId === c.id\n ? renameInput()\n : h('span', {\n key: 'name',\n onDoubleClick: startEdit(c),\n title: 'Double-click to rename',\n style: { flex: 1, minWidth: 0, fontSize: '14px', fontWeight: c.active ? 700 : 600, letterSpacing: '-0.2px', color: c.active ? '#f4f4f6' : '#cfcfd4', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }\n }, c.name),\n c.unread && editingId !== c.id && h('span', { key: 'u', style: { flex: 'none', minWidth: '18px', height: '18px', padding: '0 5px', borderRadius: '9px', display: 'grid', placeItems: 'center', background: '#f0892a', color: '#1a0f04', fontFamily: \"'JetBrains Mono',monospace\", fontSize: '10px', fontWeight: 700 } }, c.unread)\n ]),\n h('div', { key: 'prev', style: { fontSize: '12px', color: c.active ? '#8a8a92' : '#6b6b73', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, c.preview)\n ])\n ]);\n\n // ---- Collapsed dock item ----\n const dockItem = (c) => h('div', { key: c.id, style: { position: 'relative' } }, [\n c.active && h('span', { key: 'bar', style: { position: 'absolute', left: '-13px', top: '9px', bottom: '9px', width: '3px', borderRadius: '0 3px 3px 0', background: '#f0892a' } }),\n h('div', {\n key: 'tile',\n onClick: () => onSelect(c.id),\n title: c.name,\n style: { position: 'relative', width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', cursor: 'pointer', background: c.active ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,' + (c.active ? '0.14' : '0.07') + ')', fontSize: '13px', fontWeight: 700, letterSpacing: '-0.3px', color: c.active ? '#f4f4f6' : '#9a9aa2' }\n }, [\n c.mono,\n h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: c.dot, border: '2.5px solid #0c0c0e' } }),\n c.unread && h('span', { key: 'u', style: { position: 'absolute', left: '-5px', top: '-5px', minWidth: '17px', height: '17px', padding: '0 4px', borderRadius: '9px', display: 'grid', placeItems: 'center', background: '#f0892a', color: '#1a0f04', fontFamily: \"'JetBrains Mono',monospace\", fontSize: '9.5px', fontWeight: 700, border: '2px solid #0c0c0e' } }, c.unread)\n ])\n ]);\n\n // Same logo treatment as the landing header (Header.jsx): the mark on a\n // transparent background \u2014 no black tile.\n const brandMark = (size) => h('div', { style: { width: size + 'px', height: size + 'px', flex: 'none', display: 'grid', placeItems: 'center' } },\n h('img', { src: '/logo/securebit-mark.svg', alt: 'SecureBit', style: { width: '100%', height: '100%', objectFit: 'contain', display: 'block' } }));\n const collapseBtn = (svg, title) => h('button', { onClick: onToggleCollapse, title, style: { width: '30px', height: '30px', borderRadius: '8px', display: 'grid', placeItems: 'center', border: '1px solid rgba(255,255,255,0.07)', background: 'transparent', color: '#8a8a92', cursor: 'pointer' }, dangerouslySetInnerHTML: { __html: svg } });\n\n // ---- Expanded rail content ----\n // ---- Presence (\"You\" status) panel ----\n const myMeta = MY_STATUS_OPTIONS.find((o) => o.key === myStatus) || MY_STATUS_OPTIONS[0];\n const PRES_SVG = {\n user: '',\n check: '',\n chevUp: '',\n lock: ''\n };\n const presenceMenu = (pos) => (presenceOpen ? h('div', {\n key: 'pmenu',\n style: Object.assign({ position: 'absolute', zIndex: 30, borderRadius: '14px', background: '#161618', border: '1px solid rgba(255,255,255,0.1)', boxShadow: '0 16px 40px rgba(0,0,0,0.55)', padding: '6px' }, pos)\n }, [\n h('div', { key: 'h', style: { padding: '9px 10px 7px', fontFamily: \"'JetBrains Mono',monospace\", fontSize: '10px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '1.2px' } }, 'Set your status'),\n ...MY_STATUS_OPTIONS.map((o) => h('button', {\n key: o.key,\n onClick: () => { onSetStatus(o.key); setPresenceOpen(false); },\n style: { width: '100%', display: 'flex', alignItems: 'center', gap: '11px', padding: '9px 10px', borderRadius: '9px', border: 'none', background: 'transparent', cursor: 'pointer', textAlign: 'left' }\n }, [\n h('span', { key: 'd', style: { flex: 'none', width: '10px', height: '10px', borderRadius: '50%', background: o.dot } }),\n h('span', { key: 't', style: { flex: 1, minWidth: 0 } }, [\n h('span', { key: 'w', style: { display: 'block', fontSize: '13.5px', fontWeight: 600, color: '#e8e8eb' } }, o.word),\n h('span', { key: 'de', style: { display: 'block', fontSize: '11.5px', color: '#6b6b73' } }, o.desc)\n ]),\n o.key === myStatus && h('span', { key: 'c', style: { flex: 'none', display: 'grid', placeItems: 'center' }, dangerouslySetInnerHTML: { __html: PRES_SVG.check } })\n ])),\n h('div', { key: 'note', style: { display: 'flex', alignItems: 'flex-start', gap: '8px', margin: '6px 6px 4px', padding: '9px 10px', borderRadius: '9px', background: 'rgba(62,207,142,0.06)', border: '1px solid rgba(62,207,142,0.16)' } }, [\n h('span', { key: 'i', style: { flex: 'none', marginTop: '1px', display: 'grid' }, dangerouslySetInnerHTML: { __html: PRES_SVG.lock } }),\n h('span', { key: 't', style: { fontSize: '11px', lineHeight: 1.45, color: '#8a8a92' } }, 'Sent end-to-end to connected peers only \u2014 never stored on a server.')\n ])\n ]) : null);\n const presencePanelExpanded = h('div', { key: 'you', style: { flex: 'none', position: 'relative', marginTop: '10px', borderTop: '1px solid rgba(255,255,255,0.06)', padding: '10px 12px 12px' } }, [\n presenceMenu({ left: '12px', right: '12px', bottom: '64px' }),\n h('button', { key: 'btn', onClick: () => setPresenceOpen((v) => !v), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '11px', padding: '7px 8px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.06)', background: 'rgba(255,255,255,0.02)', cursor: 'pointer' } }, [\n h('div', { key: 'av', style: { position: 'relative', flex: 'none', width: '36px', height: '36px', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(240,137,42,0.12)', border: '1px solid rgba(240,137,42,0.24)', color: '#f0892a' } }, [\n h('span', { key: 'i', style: { display: 'grid' }, dangerouslySetInnerHTML: { __html: PRES_SVG.user } }),\n h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: myMeta.dot, border: '2px solid #0c0c0e' } })\n ]),\n h('div', { key: 'tx', style: { flex: 1, minWidth: 0, textAlign: 'left' } }, [\n h('div', { key: 'y', style: { fontSize: '13.5px', fontWeight: 700, color: '#f4f4f6' } }, 'You'),\n h('div', { key: 'w', style: { fontSize: '12px', color: '#8a8a92' } }, myMeta.word)\n ]),\n h('span', { key: 'ch', style: { display: 'grid', placeItems: 'center' }, dangerouslySetInnerHTML: { __html: PRES_SVG.chevUp } })\n ])\n ]);\n const presencePanelCollapsed = h('div', { key: 'you', style: { flex: 'none', position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '0 0 13px' } }, [\n presenceMenu({ left: '60px', bottom: '8px', width: '248px' }),\n h('button', { key: 'btn', onClick: () => setPresenceOpen((v) => !v), title: 'Your status \u2014 ' + myMeta.word, style: { position: 'relative', width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', cursor: 'pointer', background: 'rgba(240,137,42,0.12)', border: '1px solid rgba(240,137,42,0.24)', color: '#f0892a' } }, [\n h('span', { key: 'i', style: { display: 'grid' }, dangerouslySetInnerHTML: { __html: PRES_SVG.user } }),\n h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '12px', height: '12px', borderRadius: '50%', background: myMeta.dot, border: '2.5px solid #0c0c0e' } })\n ])\n ]);\n\n const expandedInner = [\n h('div', { key: 'head', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 12px 0 16px', height: '64px', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [\n h('div', { key: 'brand', style: { display: 'flex', alignItems: 'center', gap: '10px' } }, [brandMark(30), h('span', { key: 't', style: { fontSize: '15px', fontWeight: 800, letterSpacing: '-0.3px', color: '#f4f4f6' } }, 'SecureBit')]),\n collapseBtn(SB_SVG.chevL, 'Collapse')\n ]),\n h('div', { key: 'label', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 16px 9px' } }, [\n h('span', { key: 'l', style: { fontFamily: \"'JetBrains Mono',monospace\", fontSize: '10px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '1.3px' } }, 'Chats'),\n h('span', { key: 'c', style: { fontFamily: \"'JetBrains Mono',monospace\", fontSize: '10px', fontWeight: 600, color: '#6b6b73' } }, String(chats.length))\n ]),\n h('div', { key: 'list', className: 'msc-scroll', style: { flex: 1, overflowY: 'auto', padding: '0 10px' } }, [\n ...chats.map(expandedRow),\n h('div', { key: 'gh', style: { marginTop: '14px', padding: '0 2px 6px' } }, h('span', { style: { fontFamily: \"'JetBrains Mono',monospace\", fontSize: '10px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '1.3px' } }, 'Group chats')),\n h('div', { key: 'gph', title: 'Coming in v6.0', style: { display: 'flex', alignItems: 'center', gap: '12px', padding: '11px 12px', borderRadius: '11px', background: 'transparent', border: '1px dashed rgba(255,255,255,0.09)', cursor: 'not-allowed' } }, [\n h('div', { key: 'i', style: { flex: 'none', width: '38px', height: '38px', borderRadius: '11px', display: 'grid', placeItems: 'center', background: 'rgba(255,255,255,0.025)', border: '1px solid rgba(255,255,255,0.06)', color: '#56565e' }, dangerouslySetInnerHTML: { __html: SB_SVG.users } }),\n h('div', { key: 'b', style: { flex: 1, minWidth: 0 } }, [\n h('div', { key: 't', style: { fontSize: '14px', fontWeight: 600, color: '#8a8a92' } }, 'Group chats'),\n h('div', { key: 's', style: { fontSize: '11.5px', color: '#56565e' } }, 'Up to 8 peers \u00B7 P2P mesh')\n ]),\n h('span', { key: 'soon', style: { flex: 'none', padding: '4px 9px', borderRadius: '7px', background: 'rgba(240,137,42,0.1)', border: '1px solid rgba(240,137,42,0.24)', fontFamily: \"'JetBrains Mono',monospace\", fontSize: '9.5px', fontWeight: 700, color: '#f0892a', textTransform: 'uppercase', letterSpacing: '0.8px' } }, 'Soon')\n ])\n ]),\n h('div', { key: 'new', style: { flex: 'none', padding: '12px' } }, h('button', {\n onClick: onNewChat,\n style: { width: '100%', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '12px', borderRadius: '11px', border: 'none', background: '#f0892a', color: '#1a0f04', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer', boxShadow: '0 8px 24px rgba(240,137,42,0.28)' }\n }, [icon(SB_SVG.plus, { key: 'p' }), 'New chat'])),\n presencePanelExpanded\n ];\n\n // ---- Collapsed dock content ----\n const collapsedInner = [\n h('div', { key: 'head', style: { flex: 'none', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px', padding: '13px 0', width: '100%', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [brandMark(32), collapseBtn(SB_SVG.chevR, 'Expand')]),\n h('div', { key: 'list', className: 'msc-scroll', style: { flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px', padding: '14px 0', width: '100%' } }, [\n ...chats.map(dockItem),\n h('div', { key: 'sep', style: { width: '30px', height: '1px', background: 'rgba(255,255,255,0.07)', margin: '2px 0' } }),\n h('div', { key: 'gph', title: 'Group chats \u2014 coming in v6.0', style: { position: 'relative', width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', cursor: 'not-allowed', background: 'transparent', border: '1px dashed rgba(255,255,255,0.1)', color: '#56565e' }, dangerouslySetInnerHTML: { __html: SB_SVG.users } })\n ]),\n h('div', { key: 'new', style: { flex: 'none', padding: '13px 0' } }, h('button', {\n onClick: onNewChat, title: 'New chat',\n style: { width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', border: 'none', background: '#f0892a', color: '#1a0f04', cursor: 'pointer', boxShadow: '0 8px 24px rgba(240,137,42,0.28)' }, dangerouslySetInnerHTML: { __html: SB_SVG.plus }\n })),\n presencePanelCollapsed\n ];\n\n const railWidth = collapsed ? '72px' : '292px';\n const railStyle = { flex: 'none', width: railWidth, display: 'flex', flexDirection: 'column', alignItems: collapsed ? 'center' : 'stretch', background: '#0c0c0e', borderRight: '1px solid rgba(255,255,255,0.06)' };\n const inner = collapsed ? collapsedInner : expandedInner;\n\n return h(React.Fragment, null, [\n // Responsive behaviour (inline styles can't express media queries).\n h('style', { key: 'css', dangerouslySetInnerHTML: { __html: '@media (max-width:1023px){.sb-rail{display:none !important;}.sb-burger{display:grid !important;}}@media (min-width:1024px){.sb-drawer-overlay{display:none !important;}}' } }),\n // Desktop rail\n h('aside', { key: 'rail', className: 'sb-rail', style: railStyle }, inner),\n // Mobile drawer overlay\n h('div', {\n key: 'drawer', className: 'sb-drawer-overlay',\n onClick: onCloseDrawer,\n style: { position: 'fixed', inset: 0, zIndex: 60, background: 'rgba(6,6,8,0.6)', backdropFilter: 'blur(4px)', WebkitBackdropFilter: 'blur(4px)', display: drawerOpen ? 'block' : 'none' }\n }, h('aside', { onClick: (e) => e.stopPropagation(), style: { position: 'absolute', left: 0, top: 0, bottom: 0, width: '292px', display: 'flex', flexDirection: 'column', background: '#0c0c0e', borderRight: '1px solid rgba(255,255,255,0.06)', boxShadow: '0 0 60px rgba(0,0,0,0.6)' } }, expandedInner))\n ]);\n };\n\n // Main Enhanced Application Component\n const EnhancedSecureP2PChat = () => {\n\n // ============================================\n // MULTI-SESSION REGISTRY\n // Each conversation is an independent session with its OWN\n // EnhancedSecureWebRTCManager (full key/ratchet/SAS isolation). The reducer\n // holds serializable per-session state; non-serializable objects (managers,\n // notification integrations, offline queues) live in ref-held Maps keyed by\n // sessionId and are NEVER shared between sessions. sessionId is local-only.\n // ============================================\n const [sessionsState, dispatch] = React.useReducer(sessionsReducer, undefined, createInitialState);\n const activeSessionId = sessionsState.activeSessionId;\n const activeIdRef = React.useRef(null);\n activeIdRef.current = activeSessionId;\n const active = activeSessionId ? sessionsState.sessions[activeSessionId] : null;\n const EMPTY_ARR = React.useRef([]).current;\n\n const managersRef = React.useRef(new Map()); // id -> EnhancedSecureWebRTCManager\n const integrationsRef = React.useRef(new Map()); // id -> NotificationIntegration\n const queuesRef = React.useRef(new Map()); // id -> { incoming:[], outgoing:[] }\n\n // Active-session VIEW. The rest of the component (and the child setup/chat\n // components) read these names unchanged; the setters dispatch to the active\n // session read from activeIdRef at call time, so even memoized callbacks stay\n // correct across session switches.\n const dispatchActive = React.useCallback((build) => {\n const id = activeIdRef.current; if (!id) return;\n dispatch(build(id));\n }, []);\n\n const messages = active ? active.messages : EMPTY_ARR;\n const setMessages = React.useCallback((updaterOrArr) => {\n const id = activeIdRef.current; if (!id) return;\n if (typeof updaterOrArr === 'function') dispatch({ type: SA.SET_MESSAGES, id, updater: updaterOrArr });\n else dispatch({ type: SA.SET_MESSAGES, id, messages: updaterOrArr });\n }, []);\n\n const connectionStatus = active ? active.status : 'disconnected';\n const setConnectionStatus = React.useCallback((status) => dispatchActive((id) => ({ type: SA.SET_STATUS, id, status })), [dispatchActive]);\n\n const keyFingerprint = active ? active.keyFingerprint : '';\n const setKeyFingerprint = React.useCallback((fingerprint) => dispatchActive((id) => ({ type: SA.SET_FINGERPRINT, id, fingerprint })), [dispatchActive]);\n\n const verificationCode = active ? active.verificationCode : '';\n const setVerificationCode = React.useCallback((code) => dispatchActive((id) => ({ type: SA.SET_VERIFICATION, id, code })), [dispatchActive]);\n\n const isVerified = active ? active.sas.isVerified : false;\n const setIsVerified = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { isVerified: !!v } })), [dispatchActive]);\n const localVerificationConfirmed = active ? active.sas.localConfirmed : false;\n const setLocalVerificationConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { localConfirmed: !!v } })), [dispatchActive]);\n const remoteVerificationConfirmed = active ? active.sas.remoteConfirmed : false;\n const setRemoteVerificationConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { remoteConfirmed: !!v } })), [dispatchActive]);\n const bothVerificationsConfirmed = active ? active.sas.bothConfirmed : false;\n const setBothVerificationsConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { bothConfirmed: !!v } })), [dispatchActive]);\n\n const pendingIncomingFiles = active ? active.pendingIncomingFiles : EMPTY_ARR;\n const setPendingIncomingFiles = React.useCallback((updaterOrArr) => {\n const id = activeIdRef.current; if (!id) return;\n if (typeof updaterOrArr === 'function') dispatch({ type: SA.SET_PENDING_FILES, id, updater: updaterOrArr });\n else dispatch({ type: SA.SET_PENDING_FILES, id, files: updaterOrArr });\n }, []);\n\n // Per-session offer/answer setup flow (preserved when switching chats).\n const setupField = (name, fallback) => (active ? active.setup[name] : fallback);\n const setSetupField = (name) => React.useCallback((value) => dispatchActive((id) => ({ type: SA.PATCH_SETUP, id, patch: { [name]: value } })), [dispatchActive]);\n const offerData = setupField('offerData', '');\n const setOfferData = setSetupField('offerData');\n const answerData = setupField('answerData', '');\n const setAnswerData = setSetupField('answerData');\n const offerInput = setupField('offerInput', '');\n const setOfferInput = setSetupField('offerInput');\n const answerInput = setupField('answerInput', '');\n const setAnswerInput = setSetupField('answerInput');\n const showOfferStep = setupField('showOfferStep', false);\n const setShowOfferStep = setSetupField('showOfferStep');\n const showAnswerStep = setupField('showAnswerStep', false);\n const setShowAnswerStep = setSetupField('showAnswerStep');\n const showVerification = setupField('showVerification', false);\n const setShowVerification = setSetupField('showVerification');\n const showQRCode = setupField('showQRCode', false);\n const setShowQRCode = setSetupField('showQRCode');\n const qrCodeUrl = setupField('qrCodeUrl', '');\n const setQrCodeUrl = setSetupField('qrCodeUrl');\n const isGeneratingKeys = setupField('isGeneratingKeys', false);\n const setIsGeneratingKeys = setSetupField('isGeneratingKeys');\n\n // Accessor over the ACTIVE session's manager / notification integration.\n // Existing `webrtcManagerRef.current.X()` call sites keep working against the\n // active session; per-session callbacks use their own captured manager instead.\n const webrtcManagerRef = React.useMemo(() => ({\n get current() { return managersRef.current.get(activeIdRef.current) || null; },\n set current(v) {\n const id = activeIdRef.current; if (!id) return;\n if (v) managersRef.current.set(id, v); else managersRef.current.delete(id);\n }\n }), []);\n const notificationIntegrationRef = React.useMemo(() => ({\n get current() { return integrationsRef.current.get(activeIdRef.current) || null; },\n set current(v) {\n const id = activeIdRef.current; if (!id) return;\n if (v) integrationsRef.current.set(id, v); else integrationsRef.current.delete(id);\n }\n }), []);\n\n // ---- Presence / availability ----\n // My status is broadcast E2E over each connected session's data channel\n // (sendSystemMessage) and never stored on a server. 'invisible' goes on the\n // wire as 'offline' so peers cannot tell. myStatusRef lets the per-session\n // callbacks (captured at create time) read the current status.\n const [myStatus, setMyStatusState] = React.useState(() => {\n try { return localStorage.getItem('securebit_my_status') || 'available'; } catch { return 'available'; }\n });\n const myStatusRef = React.useRef(myStatus);\n myStatusRef.current = myStatus;\n const wirePresence = (s) => (s === 'invisible' ? 'offline' : s);\n // Presence travels as a normal ENCRYPTED chat message (type 'message') \u2014 that is\n // the only inbound path the manager delivers to onMessage; unknown raw system\n // types are dropped. handleMessage recognises the {type:'presence'} payload and\n // consumes it without displaying it.\n const sendPresenceTo = React.useCallback((mgr, s) => {\n if (!mgr || typeof mgr.sendMessage !== 'function') return;\n try {\n if (mgr.isConnected && mgr.isConnected()) {\n const p = mgr.sendMessage(JSON.stringify({ type: 'presence', status: wirePresence(s) }));\n if (p && typeof p.catch === 'function') p.catch(() => {});\n }\n } catch (_) {}\n }, []);\n const setMyStatus = React.useCallback((key) => {\n setMyStatusState(key);\n try { localStorage.setItem('securebit_my_status', key); } catch {}\n for (const mgr of managersRef.current.values()) sendPresenceTo(mgr, key);\n }, [sendPresenceTo]);\n\n // Secure chat extras: per-message send modes + 1s tick for countdowns.\n const [codeMode, setCodeMode] = React.useState(false);\n const [viewOnceMode, setViewOnceMode] = React.useState(false);\n const [viewOnceTtl, setViewOnceTtl] = React.useState(15); // seconds visible after the peer opens it\n const [disappearTtl, setDisappearTtl] = React.useState(0); // seconds; 0 = off (sticky)\n const [nowTick, setNowTick] = React.useState(() => Date.now());\n // connectionStatus \u2192 per-session status (active-session view, above).\n // Offline awareness \u2014 tracks the real online/offline events (which is\n // also what a console-simulated `dispatchEvent(new Event('offline'))`\n // fires, even when navigator.onLine stays true).\n const [isOffline, setIsOffline] = React.useState(typeof navigator !== 'undefined' && navigator.onLine === false);\n // Ref mirror so manager callbacks (which close over stale state) always\n // read the current offline status. Two queues implement store-and-forward\n // over the still-live P2P channel: outgoing waits until WE reconnect,\n // incoming waits until WE reconnect before being shown/acked.\n const offlineRef = React.useRef(isOffline);\n // Offline store-and-forward queues are now per-session (queuesRef, above).\n React.useEffect(() => { offlineRef.current = isOffline; }, [isOffline]);\n React.useEffect(() => {\n const goOffline = () => setIsOffline(true);\n const goOnline = () => setIsOffline(false);\n window.addEventListener('offline', goOffline);\n window.addEventListener('online', goOnline);\n return () => { window.removeEventListener('offline', goOffline); window.removeEventListener('online', goOnline); };\n }, []);\n const [relayOnlyMode, setRelayOnlyMode] = React.useState(() => {\n try { return localStorage.getItem('securebit_relay_only_mode') === 'true'; } catch { return false; }\n });\n // Custom ICE (STUN/TURN) servers \u2014 advanced network settings.\n const [customIceServers, setCustomIceServers] = React.useState(null); // null => use public defaults\n const [iceServersText, setIceServersText] = React.useState('');\n const [iceSettingsPersisted, setIceSettingsPersisted] = React.useState(false);\n const [showIceSettings, setShowIceSettings] = React.useState(false);\n\n // Load any previously saved (encrypted) custom ICE settings on mount.\n React.useEffect(() => {\n let cancelled = false;\n loadIceSettings().then((saved) => {\n if (cancelled || !saved) return;\n if (Array.isArray(saved.servers) && saved.servers.length > 0) {\n setCustomIceServers(saved.servers);\n setIceServersText(JSON.stringify(saved.servers, null, 2));\n }\n if (saved.privacyMode === 'relay-only') {\n setRelayOnlyMode(true);\n }\n setIceSettingsPersisted(true);\n }).catch(() => { /* fail closed: keep defaults */ });\n return () => { cancelled = true; };\n }, []);\n\n // Global entry point: the header gear dispatches this event.\n React.useEffect(() => {\n const open = () => setShowIceSettings(true);\n window.addEventListener('securebit:open-network-settings', open);\n return () => window.removeEventListener('securebit:open-network-settings', open);\n }, []);\n\n const handleApplyIceSettings = React.useCallback((next, persist) => {\n const servers = next.useCustom && Array.isArray(next.servers) ? next.servers : null;\n setCustomIceServers(servers && servers.length ? servers : null);\n setIceServersText(next.serversText || '');\n setRelayOnlyMode(next.privacyMode === 'relay-only');\n setShowIceSettings(false);\n if (persist) {\n setIceSettingsPersisted(true);\n saveIceSettings({ servers: servers || [], privacyMode: next.privacyMode }).catch(() => { /* surfaced as no-op */ });\n } else if (iceSettingsPersisted) {\n // User turned persistence off \u2014 remove the stored copy.\n setIceSettingsPersisted(false);\n clearIceSettings().catch(() => {});\n }\n }, [iceSettingsPersisted]);\n\n const handleForgetIceSettings = React.useCallback(async () => {\n await clearIceSettings().catch(() => {});\n setIceSettingsPersisted(false);\n setCustomIceServers(null);\n setIceServersText('');\n }, []);\n \n // Moved scrollToBottom logic to be available globally\n const [messageInput, setMessageInput] = React.useState('');\n // offerData/answerData/offerInput/answerInput, showOfferStep/showAnswerStep,\n // showVerification/showQRCode/qrCodeUrl, isGeneratingKeys \u2192 per-session setup\n // slice (declared in the active-session view block above).\n const [showQRScanner, setShowQRScanner] = React.useState(false);\n const [showQRScannerModal, setShowQRScannerModal] = React.useState(false);\n\n // isVerified + mutual-verification flags \u2192 per-session SAS slice (above).\n const [securityLevel, setSecurityLevel] = React.useState(null);\n const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);\n\n // PAKE password states removed - using SAS verification instead\n\n // Session state - all security features enabled by default\n const [pendingSession, setPendingSession] = React.useState(null);\n\n // All security features are enabled by default - no payment required\n // pendingIncomingFiles \u2192 per-session slice (above).\n\n\n\n // ============================================\n // CENTRALIZED CONNECTION STATE MANAGEMENT\n // ============================================\n \n const [connectionState, setConnectionState] = React.useState({\n status: 'disconnected',\n hasActiveAnswer: false,\n answerCreatedAt: null,\n isUserInitiatedDisconnect: false\n });\n \n // Centralized connection state handler\n const updateConnectionState = (newState, options = {}) => {\n const { preserveAnswer = false, isUserAction = false } = options;\n \n setConnectionState(prev => ({\n ...prev,\n ...newState,\n isUserInitiatedDisconnect: isUserAction,\n hasActiveAnswer: preserveAnswer ? prev.hasActiveAnswer : false,\n answerCreatedAt: preserveAnswer ? prev.answerCreatedAt : null\n }));\n };\n \n // Check if we should preserve answer data\n const shouldPreserveAnswerData = () => {\n const hasAnswerData = !!answerData || \n (answerInput && typeof answerInput === 'string' && answerInput.trim().length > 0);\n\n const hasAnswerQR = qrCodeUrl && typeof qrCodeUrl === 'string' && qrCodeUrl.trim().length > 0;\n \n const shouldPreserve = (connectionState.hasActiveAnswer && \n !connectionState.isUserInitiatedDisconnect) ||\n (hasAnswerData && \n !connectionState.isUserInitiatedDisconnect) ||\n (hasAnswerQR && \n !connectionState.isUserInitiatedDisconnect);\n \n \n return shouldPreserve;\n };\n \n // Mark answer as created\n const markAnswerCreated = () => {\n updateConnectionState({\n hasActiveAnswer: true,\n answerCreatedAt: Date.now()\n });\n };\n \n // webrtcManagerRef / notificationIntegrationRef \u2192 accessors over the active\n // session's manager / integration (active-session view block, above).\n\n // Development-only debug helpers. Production never exposes\n // manager internals or cleanup controls on `window`.\n React.useEffect(() => {\n return installDebugWindowHooks({\n targetWindow: window,\n webrtcManagerRef,\n onClearData: handleClearData\n });\n }, []);\n \n const addMessageWithAutoScroll = React.useCallback((message, type, opts = {}) => {\n const newMessage = {\n message,\n type,\n id: Date.now() + Math.random(),\n timestamp: (typeof opts.timestamp === 'number') ? opts.timestamp : Date.now(),\n mid: opts.mid,\n status: opts.status, // WhatsApp-style: sending | sent | delivered | failed\n viewOnce: opts.viewOnce === true,\n viewOnceTtl: (typeof opts.viewOnceTtl === 'number') ? opts.viewOnceTtl : 15,\n expiresAt: (typeof opts.expiresAt === 'number') ? opts.expiresAt : undefined\n };\n\n setMessages(prev => {\n const updated = [...prev, newMessage];\n \n setTimeout(() => {\n if (chatMessagesRef?.current) {\n const container = chatMessagesRef.current;\n try {\n const { scrollTop, scrollHeight, clientHeight } = container;\n const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;\n \n if (isNearBottom || prev.length === 0) {\n requestAnimationFrame(() => {\n if (container && container.scrollTo) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'smooth'\n });\n }\n });\n }\n } catch (error) {\n console.warn('Scroll error:', error);\n container.scrollTop = container.scrollHeight;\n }\n }\n }, 50);\n \n return updated;\n });\n }, []);\n\n // Flip a sent message's delivery state (sending \u2192 sent \u2192 delivered, or failed).\n const updateMessageStatus = React.useCallback((mid, status) => {\n if (!mid) return;\n setMessages(prev => prev.map(m => (String(m.mid) === String(mid) && m.type === 'sent') ? { ...m, status } : m));\n }, []);\n\n // When WE come back online: for EVERY session, transmit anything queued while\n // offline and surface (and acknowledge) anything that arrived meanwhile. Each\n // session flushes against its own manager and into its own slice.\n const flushOfflineQueues = React.useCallback(() => {\n for (const [id, q] of queuesRef.current.entries()) {\n const mgr = managersRef.current.get(id);\n const out = q.outgoing; q.outgoing = [];\n for (const item of out) {\n const send = mgr?.sendMessage?.(item.outText, item.meta);\n if (send && typeof send.then === 'function') {\n send.then(() => dispatch({ type: SA.UPDATE_MESSAGE_STATUS, id, mid: item.mid, status: 'delivered' }))\n .catch(() => dispatch({ type: SA.UPDATE_MESSAGE_STATUS, id, mid: item.mid, status: 'failed' }));\n }\n }\n const inc = q.incoming; q.incoming = [];\n if (inc.length > 0) {\n dispatch({ type: SA.ADD_MESSAGE, id, message: buildSessionMessage(\n `Connection restored \u2014 ${inc.length} message${inc.length === 1 ? '' : 's'} received while you were offline.`,\n 'notice'\n ) });\n }\n const viewing = id === activeIdRef.current && (typeof document === 'undefined' || document.visibilityState === 'visible');\n for (const item of inc) {\n dispatch({ type: SA.ADD_MESSAGE, id, message: buildSessionMessage(item.message, item.type, item.opts) });\n if (item.opts && item.opts.mid && item.type === 'received') {\n if (viewing) { try { mgr?.sendDeliveryReceipt?.(item.opts.mid); } catch (_) {} }\n else if (q.pendingReadAcks) q.pendingReadAcks.push(item.opts.mid);\n }\n }\n }\n }, []);\n\n React.useEffect(() => {\n if (isOffline) return; // only act on the offline \u2192 online edge\n flushOfflineQueues();\n }, [isOffline, flushOfflineQueues]);\n\n // Update security level based on real verification\n const updateSecurityLevel = React.useCallback(async () => {\n if (window.isUpdatingSecurity) {\n return;\n }\n \n window.isUpdatingSecurity = true;\n \n try {\n if (webrtcManagerRef.current) {\n // All security features are enabled by default - always show MAXIMUM level\n setSecurityLevel({\n level: 'MAXIMUM',\n score: 100,\n color: 'green',\n details: 'All security features enabled by default',\n passedChecks: 10,\n totalChecks: 10,\n isRealData: true\n });\n \n if (window.DEBUG_MODE) {\n const currentLevel = webrtcManagerRef.current.ecdhKeyPair && webrtcManagerRef.current.ecdsaKeyPair \n ? await webrtcManagerRef.current.calculateSecurityLevel()\n : {\n level: 'MAXIMUM',\n score: 100,\n sessionType: 'premium',\n passedChecks: 10,\n totalChecks: 10\n };\n }\n }\n } catch (error) {\n console.error('Failed to update security level:', error);\n setSecurityLevel({\n level: 'ERROR',\n score: 0,\n color: 'red',\n details: 'Verification failed'\n });\n } finally {\n setTimeout(() => {\n window.isUpdatingSecurity = false;\n }, 2000);\n }\n }, []);\n \n // Session time ticker removed - sessions are unlimited\n \n // Sessions are unlimited - no expiration handler needed\n \n // All security features are enabled by default - no demo sessions needed\n const chatMessagesRef = React.useRef(null);\n \n // Create scroll function using global helper\n const scrollToBottom = createScrollToBottomFunction(chatMessagesRef);\n\n React.useEffect(() => {\n try { localStorage.setItem('securebit_relay_only_mode', String(relayOnlyMode)); } catch {}\n if (webrtcManagerRef.current?._config?.webrtc) {\n webrtcManagerRef.current._setRelayOnlyMode(relayOnlyMode);\n }\n }, [relayOnlyMode]);\n \n // Auto-scroll when messages change\n React.useEffect(() => {\n if (messages.length > 0 && chatMessagesRef.current) {\n scrollToBottom();\n setTimeout(scrollToBottom, 50);\n setTimeout(scrollToBottom, 150);\n }\n }, [messages]);\n\n // Disappearing-message clock: tick every second (while ANY session has an\n // expiry) and prune past-deadline messages \u2014 independently per session, so\n // a background conversation's timers keep running too.\n const anyExpiring = sessionsState.order.some((id) => (sessionsState.sessions[id]?.messages || []).some((m) => typeof m.expiresAt === 'number'));\n const sessionsStateRef = React.useRef(sessionsState);\n sessionsStateRef.current = sessionsState;\n React.useEffect(() => {\n if (!anyExpiring) return;\n // Disappearing messages leave a tombstone (content wiped) instead of vanishing.\n const expireFn = (prev) => {\n const now = Date.now();\n let changed = false;\n const next = prev.map((m) => {\n if (typeof m.expiresAt === 'number' && m.expiresAt <= now && !m.expired) {\n changed = true;\n return { ...m, expired: true, message: '', expiresAt: undefined };\n }\n return m;\n });\n return changed ? next : prev;\n };\n const interval = setInterval(() => {\n const now = Date.now();\n setNowTick(now);\n const st = sessionsStateRef.current;\n for (const id of st.order) {\n const msgs = st.sessions[id]?.messages || [];\n if (msgs.some((m) => typeof m.expiresAt === 'number' && m.expiresAt <= now && !m.expired)) {\n dispatch({ type: SA.SET_MESSAGES, id, updater: expireFn });\n }\n }\n }, 1000);\n return () => clearInterval(interval);\n }, [anyExpiring]);\n \n // PAKE password functions removed - using SAS verification instead\n \n // Build a brand-new, fully-isolated session: its own manager, its own\n // id-bound callbacks, its own notification integration and offline queues.\n // The callback bodies below are the original single-session handlers; the\n // id-bound shadow setters declared here redirect every set*/dispatch into\n // THIS session's slice, so nothing leaks into another conversation.\n const createSession = (opts = {}) => {\n const role = opts.role || 'offer';\n const entry = createSessionEntry({ role });\n const id = entry.id;\n dispatch({ type: SA.CREATE_SESSION, entry, activate: opts.activate !== false });\n // pendingReadAcks: mids received while this chat was NOT being viewed; the\n // read receipt is held back until the user actually opens the conversation.\n queuesRef.current.set(id, { incoming: [], outgoing: [], pendingReadAcks: [] });\n\n // --- id-bound shadow setters (override the active-session view names) ---\n const setMessages = (u) => {\n if (typeof u === 'function') dispatch({ type: SA.SET_MESSAGES, id, updater: u });\n else dispatch({ type: SA.SET_MESSAGES, id, messages: u });\n };\n const addMessageWithAutoScroll = (message, type, opts2 = {}) => {\n dispatch({ type: SA.ADD_MESSAGE, id, message: buildSessionMessage(message, type, opts2) });\n // Background session received a chat message \u2192 bump its unread badge.\n if (type === 'received' && id !== activeIdRef.current) {\n dispatch({ type: SA.INCREMENT_UNREAD, id });\n }\n };\n const updateMessageStatus = (mid, status) => { if (mid) dispatch({ type: SA.UPDATE_MESSAGE_STATUS, id, mid, status }); };\n const setConnectionStatus = (status) => dispatch({ type: SA.SET_STATUS, id, status });\n const setKeyFingerprint = (fingerprint) => dispatch({ type: SA.SET_FINGERPRINT, id, fingerprint });\n const setVerificationCode = (code) => dispatch({ type: SA.SET_VERIFICATION, id, code });\n const setIsVerified = (v) => dispatch({ type: SA.SET_SAS, id, sas: { isVerified: !!v } });\n const setLocalVerificationConfirmed = (v) => dispatch({ type: SA.SET_SAS, id, sas: { localConfirmed: !!v } });\n const setRemoteVerificationConfirmed = (v) => dispatch({ type: SA.SET_SAS, id, sas: { remoteConfirmed: !!v } });\n const setBothVerificationsConfirmed = (v) => dispatch({ type: SA.SET_SAS, id, sas: { bothConfirmed: !!v } });\n const setShowVerification = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showVerification: !!v } });\n const setShowOfferStep = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showOfferStep: !!v } });\n const setShowAnswerStep = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showAnswerStep: !!v } });\n const setShowQRCode = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showQRCode: !!v } });\n const setQrCodeUrl = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { qrCodeUrl: v } });\n const setOfferData = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { offerData: v } });\n const setAnswerData = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { answerData: v } });\n const setOfferInput = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { offerInput: v } });\n const setAnswerInput = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { answerInput: v } });\n const setPendingIncomingFiles = (u) => {\n if (typeof u === 'function') dispatch({ type: SA.SET_PENDING_FILES, id, updater: u });\n else dispatch({ type: SA.SET_PENDING_FILES, id, files: u });\n };\n const sessionQueues = () => queuesRef.current.get(id) || { incoming: [], outgoing: [] };\n\n const handleMessage = (message, type, meta) => {\n if (typeof message === 'string' && message.trim().startsWith('{')) {\n try {\n const parsedMessage = JSON.parse(message);\n // Peer availability update (E2E control message) \u2014 store it for THIS\n // session and never show it in the chat.\n if (parsedMessage.type === 'presence') {\n const st = (parsedMessage.data && parsedMessage.data.status) || parsedMessage.status;\n if (st) dispatch({ type: SA.SET_PEER_PRESENCE, id, presence: st });\n return;\n }\n const blockedTypes = [\n 'file_transfer_start',\n 'file_transfer_response',\n 'file_chunk',\n 'chunk_confirmation',\n 'file_transfer_complete',\n 'file_transfer_error',\n 'heartbeat',\n 'verification',\n 'verification_response',\n 'verification_confirmed',\n 'verification_both_confirmed',\n 'peer_disconnect',\n 'key_rotation_signal',\n 'key_rotation_ready',\n 'security_upgrade',\n 'message_delete',\n 'message_receipt'\n ];\n if (parsedMessage.type && blockedTypes.includes(parsedMessage.type)) {\n console.log(`Blocked system/file message from chat: ${parsedMessage.type}`);\n return;\n }\n // Defensive unwrap: a plain-message envelope can leak through as\n // raw JSON for some payloads (e.g. fenced code blocks). Show the\n // real text, not {\"type\":\"message\",\"data\":\"```...\"}.\n if (parsedMessage.type === 'message' && typeof parsedMessage.data === 'string') {\n message = parsedMessage.data;\n if (parsedMessage.meta && typeof parsedMessage.meta === 'object') meta = parsedMessage.meta;\n }\n } catch (parseError) {\n\n }\n }\n \n // Per-message UI metadata from the peer (view-once / disappearing / id).\n const opts = {};\n if (meta && typeof meta === 'object') {\n if (typeof meta.mid === 'string') opts.mid = meta.mid;\n if (meta.once === true) {\n opts.viewOnce = true;\n opts.viewOnceTtl = Number.isFinite(meta.onceTtl) ? meta.onceTtl : 15;\n }\n if (Number.isFinite(meta.ttl) && meta.ttl > 0) {\n opts.expiresAt = Date.now() + meta.ttl * 1000;\n }\n if (Number.isFinite(meta.ts)) opts.timestamp = meta.ts;\n }\n\n // If WE are offline, hold the peer's message back: don't show it\n // and don't acknowledge it yet, so the sender stays at one check.\n // It's surfaced (and acked \u2192 \u2713\u2713) the moment we reconnect.\n if (offlineRef.current && type === 'received') {\n sessionQueues().incoming.push({ message, type, opts });\n return;\n }\n\n addMessageWithAutoScroll(message, type, opts);\n\n // Read receipt: only ack once the user is actually looking at THIS\n // conversation (active session + visible tab). Otherwise hold the mid\n // back so a message read in a background chat stays \"delivered\" (grey\n // \u2713\u2713) on the sender's side \u2014 it flips to green only when this chat is\n // opened (see flushReadAcks).\n if (opts.mid && type === 'received') {\n const beingViewed = id === activeIdRef.current && (typeof document === 'undefined' || document.visibilityState === 'visible');\n if (beingViewed) {\n try { manager?.sendDeliveryReceipt?.(opts.mid); } catch (_) {}\n } else {\n const q = sessionQueues();\n if (q.pendingReadAcks) q.pendingReadAcks.push(opts.mid);\n }\n }\n };\n\n const handleStatusChange = (status) => {\n setConnectionStatus(status);\n \n if (status === 'connected') {\n document.dispatchEvent(new CustomEvent('new-connection'));\n \n // \u041D\u0435 \u0441\u043A\u0440\u044B\u0432\u0430\u0435\u043C \u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E \u043F\u0440\u0438 'connected' - \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0440\u0438 'verified'\n // setIsVerified(true);\n // setShowVerification(false);\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'verifying') {\n setShowVerification(true);\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'verified') {\n setIsVerified(true);\n setShowVerification(false);\n setBothVerificationsConfirmed(true);\n setConnectionStatus('connected');\n // Force immediate update of isVerified state\n setTimeout(() => {\n setIsVerified(true);\n }, 0);\n // Tell the newly-connected peer my current availability (E2E, via\n // the normal encrypted message path \u2014 see sendPresenceTo).\n try {\n const s = myStatusRef.current === 'invisible' ? 'offline' : myStatusRef.current;\n setTimeout(() => {\n try {\n const p = manager.sendMessage?.(JSON.stringify({ type: 'presence', status: s }));\n if (p && typeof p.catch === 'function') p.catch(() => {});\n } catch (_) {}\n }, 400);\n } catch (_) {}\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'connecting') {\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } else if (status === 'disconnected') {\n // Drop: the manager has cleared its verification (re-establishment\n // required), so reset this session's verification flags too \u2014 the\n // setup/connect screen then shows so the user can reconnect. We do\n // NOT remove the session (that create\u2192destroy\u2192create churn flashed\n // the console) and we keep the message history; closing a chat is\n // done only via the Disconnect button.\n setConnectionStatus('disconnected');\n setIsVerified(false);\n setShowVerification(false);\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n if (id === activeIdRef.current) document.dispatchEvent(new CustomEvent('disconnected'));\n } else if (status === 'peer_disconnected') {\n if (id === activeIdRef.current) {\n setSessionTimeLeft(0);\n document.dispatchEvent(new CustomEvent('peer-disconnect'));\n }\n setConnectionStatus('peer_disconnected');\n setIsVerified(false);\n setShowVerification(false);\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n }\n };\n \n const handleKeyExchange = (fingerprint) => {\n if (fingerprint === '') {\n setKeyFingerprint('');\n } else {\n setKeyFingerprint(fingerprint);\n }\n };\n \n const handleVerificationRequired = (code) => {\n if (code === '') {\n setVerificationCode('');\n setShowVerification(false);\n } else {\n setVerificationCode(code);\n setShowVerification(true);\n }\n };\n \n const handleVerificationStateChange = (state) => {\n setLocalVerificationConfirmed(state.localConfirmed);\n setRemoteVerificationConfirmed(state.remoteConfirmed);\n setBothVerificationsConfirmed(state.bothConfirmed);\n };\n \n // Callback for handling response errors\n const handleAnswerError = (errorType, errorMessage) => {\n if (errorType === 'replay_attack') {\n // Reset the session upon replay attack\n setSessionTimeLeft(0);\n setPendingSession(null);\n \n addMessageWithAutoScroll('\uD83D\uDCA1 Data is outdated. Please create a new invitation or use a current response code.', 'system');\n \n if (typeof console.clear === 'function') {\n console.clear();\n }\n } else if (errorType === 'security_violation') {\n // Reset the session upon security breach\n setSessionTimeLeft(0);\n setPendingSession(null);\n \n addMessageWithAutoScroll(` Security breach: ${errorMessage}`, 'system');\n \n if (typeof console.clear === 'function') {\n console.clear();\n }\n }\n };\n \n \n if (typeof console.clear === 'function') {\n console.clear();\n }\n \n const manager = new EnhancedSecureWebRTCManager(\n handleMessage,\n handleStatusChange,\n handleKeyExchange,\n handleVerificationRequired,\n handleAnswerError,\n handleVerificationStateChange,\n {\n webrtc: {\n relayOnly: relayOnlyMode,\n // Priority: user's custom servers > operator override > built-in defaults.\n iceServers: (Array.isArray(customIceServers) && customIceServers.length)\n ? customIceServers\n : (Array.isArray(window.SECUREBIT_ICE_SERVERS) ? window.SECUREBIT_ICE_SERVERS : undefined)\n }\n }\n );\n managersRef.current.set(id, manager);\n\n // Unsend / delete-for-everyone: peer asked us to drop a message.\n manager.onMessageDelete = (mid) => {\n if (!mid) return;\n setMessages(prev => prev.filter(m => String(m.mid) !== String(mid)));\n };\n\n // Read receipt: the peer actually opened the chat and read it \u2192 green.\n // (A receipt is only sent once the recipient is viewing the conversation,\n // so background-read messages stay at two grey 'delivered' ticks.)\n manager.onMessageDelivered = (mid) => {\n updateMessageStatus(mid, 'read');\n };\n\n // Per-session notification integration (raises OS notifications when the\n // tab is hidden; the unread badge handles the in-app, focused case).\n if (typeof Notification !== 'undefined' && Notification && Notification.permission === 'granted' && window.NotificationIntegration && !integrationsRef.current.get(id)) {\n try {\n const integration = new window.NotificationIntegration(manager);\n integration.init().then(() => {\n integrationsRef.current.set(id, integration);\n }).catch((error) => {\n // Handle error silently\n });\n } catch (error) {\n // Handle error silently\n }\n }\n\n handleMessage(' SecureBit.chat Enhanced Security Edition v4.10.0 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');\n\n // Setup file transfer callbacks (id-bound to THIS session's manager).\n manager.setFileTransferCallbacks(\n // Progress callback\n (progress) => {\n console.log('File progress:', progress);\n },\n\n // File received callback \u2014 auto-save to disk, no button press needed.\n (fileData) => {\n const sizeMb = Math.max(1, Math.round((fileData.fileSize || 0) / (1024 * 1024)));\n\n const saveToDisk = async () => {\n const url = await fileData.getObjectURL();\n const a = document.createElement('a');\n a.href = url;\n a.download = fileData.fileName || 'file';\n document.body.appendChild(a);\n a.click();\n a.remove();\n setTimeout(() => fileData.revokeObjectURL(url), 15000);\n };\n\n saveToDisk()\n .then(() => {\n addMessageWithAutoScroll(`File received & saved: ${fileData.fileName} (${sizeMb} MB)`, 'system');\n })\n .catch((e) => {\n console.error('Auto-save failed:', e);\n addMessageWithAutoScroll(`File received: ${fileData.fileName} (${sizeMb} MB). Open the file panel to download it.`, 'system');\n });\n },\n\n // Error callback\n (error) => {\n console.error('File transfer error:', error);\n\n if (error.includes('Connection not ready')) {\n addMessageWithAutoScroll(` File transfer error: connection not ready. Try again later.`, 'system');\n } else if (error.includes('File too large')) {\n addMessageWithAutoScroll(` File is too big. Maximum size: 100 MB`, 'system');\n } else {\n addMessageWithAutoScroll(` File transfer error: ${error}`, 'system');\n }\n },\n\n // Incoming file request callback \u2014 receiver must explicitly accept before any data is sent\n (fileRequest) => {\n setPendingIncomingFiles(prev => {\n if (prev.some(f => f.fileId === fileRequest.fileId)) return prev;\n return [...prev, fileRequest];\n });\n }\n );\n\n return id;\n };\n\n // Keep createSession reachable from non-render callers (new-chat button etc.).\n const createSessionRef = React.useRef(createSession);\n createSessionRef.current = createSession;\n\n // Tear down ONE session completely: its manager (existing key-wipe logic),\n // its notification integration and its queues. Other sessions are untouched.\n // Re-entrancy guarded: mgr.disconnect() can synchronously re-fire the\n // 'disconnected' status callback, which would otherwise recurse back in here.\n const destroyingRef = React.useRef(new Set());\n const destroySession = React.useCallback((id) => {\n if (!id || destroyingRef.current.has(id)) return;\n destroyingRef.current.add(id);\n try {\n const mgr = managersRef.current.get(id);\n if (mgr) { try { mgr.disconnect(); } catch (_) {} managersRef.current.delete(id); }\n const integ = integrationsRef.current.get(id);\n if (integ) { try { integ.cleanup?.(); } catch (_) {} integrationsRef.current.delete(id); }\n queuesRef.current.delete(id);\n dispatch({ type: SA.REMOVE_SESSION, id });\n } finally {\n destroyingRef.current.delete(id);\n }\n }, []);\n\n // Always keep at least one session around: when the last chat is removed the\n // user lands back on the single-column \"Start Secure\" page with a fresh session.\n React.useEffect(() => {\n if (sessionsState.order.length === 0) createSessionRef.current({ role: 'offer' });\n }, [sessionsState.order.length]);\n\n // ---- Sidebar (session list) state + actions ----\n const [sidebarCollapsed, setSidebarCollapsed] = React.useState(() => {\n try { return localStorage.getItem('securebit_sidebar_collapsed') === 'true'; } catch { return false; }\n });\n React.useEffect(() => { try { localStorage.setItem('securebit_sidebar_collapsed', String(sidebarCollapsed)); } catch {} }, [sidebarCollapsed]);\n const [sidebarDrawerOpen, setSidebarDrawerOpen] = React.useState(false);\n const handleSelectSession = React.useCallback((id) => {\n dispatch({ type: SA.SET_ACTIVE, id });\n dispatch({ type: SA.CLEAR_UNREAD, id });\n setSidebarDrawerOpen(false);\n }, []);\n const handleNewChat = React.useCallback(() => {\n createSessionRef.current({ role: 'offer' });\n setSidebarDrawerOpen(false);\n }, []);\n const handleRenameSession = React.useCallback((id, label) => { dispatch({ type: SA.RENAME, id, label }); }, []);\n // Send any held-back read receipts for a session (call when the user opens it).\n const flushReadAcks = React.useCallback((id) => {\n if (!id) return;\n const q = queuesRef.current.get(id);\n const mgr = managersRef.current.get(id);\n if (!q || !mgr || !q.pendingReadAcks || q.pendingReadAcks.length === 0) return;\n const acks = q.pendingReadAcks; q.pendingReadAcks = [];\n for (const mid of acks) { try { mgr.sendDeliveryReceipt?.(mid); } catch (_) {} }\n }, []);\n // Opening a session clears its unread badge and releases its read receipts.\n React.useEffect(() => {\n if (!activeSessionId) return;\n dispatch({ type: SA.CLEAR_UNREAD, id: activeSessionId });\n if (typeof document === 'undefined' || document.visibilityState === 'visible') flushReadAcks(activeSessionId);\n }, [activeSessionId, flushReadAcks]);\n\n // App-level lifecycle: create the first session on mount, wire the global\n // tab/unload guards, and on unmount disconnect EVERY live manager.\n const didInitRef = React.useRef(false);\n React.useEffect(() => {\n if (didInitRef.current) return;\n didInitRef.current = true;\n\n // (The first session is created by the ensure-at-least-one-session effect,\n // which also handles re-landing after the last chat is closed.)\n\n let isTabSwitching = false;\n let tabSwitchTimeout = null;\n\n const handleBeforeUnload = (event) => {\n if (event.type === 'beforeunload' && !isTabSwitching) {\n for (const mgr of managersRef.current.values()) {\n try {\n if (mgr.isConnected && mgr.isConnected()) {\n try { mgr.sendSystemMessage({ type: 'peer_disconnect', reason: 'user_disconnect', timestamp: Date.now() }); } catch (_) {}\n setTimeout(() => { try { mgr.disconnect(); } catch (_) {} }, 100);\n } else {\n mgr.disconnect();\n }\n } catch (_) {}\n }\n } else if (isTabSwitching) {\n event.preventDefault();\n event.returnValue = '';\n }\n };\n\n const handleVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n isTabSwitching = true;\n if (tabSwitchTimeout) clearTimeout(tabSwitchTimeout);\n tabSwitchTimeout = setTimeout(() => { isTabSwitching = false; }, 5000);\n } else if (document.visibilityState === 'visible') {\n isTabSwitching = false;\n if (tabSwitchTimeout) { clearTimeout(tabSwitchTimeout); tabSwitchTimeout = null; }\n // Tab regained focus \u2192 release held read receipts for the open chat.\n flushReadAcks(activeIdRef.current);\n }\n };\n\n window.addEventListener('beforeunload', handleBeforeUnload);\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n return () => {\n window.removeEventListener('beforeunload', handleBeforeUnload);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n if (tabSwitchTimeout) { clearTimeout(tabSwitchTimeout); tabSwitchTimeout = null; }\n for (const mgr of managersRef.current.values()) { try { mgr.disconnect(); } catch (_) {} }\n managersRef.current.clear();\n for (const integ of integrationsRef.current.values()) { try { integ.cleanup?.(); } catch (_) {} }\n integrationsRef.current.clear();\n queuesRef.current.clear();\n };\n }, []); // run once\n \n // All security features are enabled by default - no session purchase needed\n \n const compressOfferData = (offerData) => {\n try {\n // Parse the offer data if it's a string\n const offer = typeof offerData === 'string' ? JSON.parse(offerData) : offerData;\n \n // Create a minimal version with only the most essential data\n const minimalOffer = {\n type: offer.type,\n version: offer.version,\n timestamp: offer.timestamp,\n sessionId: offer.sessionId,\n connectionId: offer.connectionId,\n verificationCode: offer.verificationCode,\n salt: offer.salt,\n // Use only key fingerprints instead of full keys\n keyFingerprints: offer.keyFingerprints,\n // Add a reference to get full data\n fullDataAvailable: true,\n compressionLevel: 'minimal'\n };\n \n return JSON.stringify(minimalOffer);\n } catch (error) {\n console.error('Error compressing offer data:', error);\n return offerData; // Return original if compression fails\n }\n };\n\n const createQRReference = (offerData) => {\n try {\n // Create a unique reference ID for this offer\n const referenceId = `offer_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n \n // Store the full offer data in localStorage with the reference ID\n localStorage.setItem(`qr_offer_${referenceId}`, JSON.stringify(offerData));\n \n // Create a minimal QR code with just the reference\n const qrReference = {\n type: 'secure_offer_reference',\n referenceId: referenceId,\n timestamp: Date.now(),\n message: 'Scan this QR code and use the reference ID to get full offer data'\n };\n \n return JSON.stringify(qrReference);\n } catch (error) {\n console.error('Error creating QR reference:', error);\n return null;\n }\n };\n\n const createTemplateOffer = (offer) => {\n // Minimal template to keep QR within single image capacity\n const templateOffer = {\n type: 'enhanced_secure_offer_template',\n version: '4.0',\n sessionId: offer.sessionId,\n connectionId: offer.connectionId,\n verificationCode: offer.verificationCode,\n timestamp: offer.timestamp,\n // Avoid bulky fields (SDP, raw keys); keep only fingerprints and essentials\n keyFingerprints: offer.keyFingerprints,\n // Keep concise auth hints (omit large nonces)\n authChallenge: offer?.authChallenge?.challenge,\n // Optionally include a compact capability hint if small\n capabilities: Array.isArray(offer.capabilities) && offer.capabilities.length <= 5\n ? offer.capabilities\n : undefined\n };\n \n return templateOffer;\n };\n\n // Conservative QR payload limits (characters). Adjust per error correction level.\n const MAX_QR_LEN = 800; // for JSON/plain/gzip\n const BIN_MAX_QR_LEN = 400; // stricter for SB1:bin to improve scan reliability\n const [qrFramesTotal, setQrFramesTotal] = React.useState(0);\n const [qrFrameIndex, setQrFrameIndex] = React.useState(0);\n const [qrManualMode, setQrManualMode] = React.useState(false);\n\n // Animated QR state (for multi-chunk COSE)\n const qrAnimationRef = React.useRef({ timer: null, chunks: [], idx: 0, active: false });\n // Stop any running QR auto-advance when the active session changes, so frames\n // are never pushed into the wrong session's setup slice.\n React.useEffect(() => () => {\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); qrAnimationRef.current.timer = null; } } catch {}\n }, [activeSessionId]);\n const stopQrAnimation = () => {\n try { if (qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n };\n\n // Render frame at current index (no index mutation)\n const renderCurrent = async () => {\n const { chunks, idx } = qrAnimationRef.current || {};\n if (!chunks || !chunks.length) return;\n const current = chunks[idx % chunks.length];\n try {\n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n const url = await (window.generateQRCode ? window.generateQRCode(current, { errorCorrectionLevel: 'M', margin: 2, size: QR_SIZE }) : Promise.resolve(''));\n if (url) setQrCodeUrl(url);\n } catch (e) {\n console.warn('Animated QR render error (current):', e);\n }\n setQrFrameIndex(((qrAnimationRef.current?.idx || 0) % (qrAnimationRef.current?.chunks?.length || 1)) + 1);\n };\n\n // Render current frame, then advance index by 1\n const renderAndAdvance = async () => {\n await renderCurrent();\n const len = qrAnimationRef.current?.chunks?.length || 0;\n if (len > 0) {\n const nextIdx = ((qrAnimationRef.current?.idx || 0) + 1) % len;\n qrAnimationRef.current.idx = nextIdx;\n setQrFrameIndex(nextIdx + 1);\n }\n };\n\n const toggleQrManualMode = () => {\n const newManualMode = !qrManualMode;\n setQrManualMode(newManualMode);\n \n if (newManualMode) {\n\n if (qrAnimationRef.current.timer) {\n clearInterval(qrAnimationRef.current.timer);\n qrAnimationRef.current.timer = null;\n }\n console.log('QR Manual mode enabled - auto-scroll stopped');\n } else {\n if (qrAnimationRef.current.chunks.length > 1) {\n const intervalMs = 3000;\n qrAnimationRef.current.active = true;\n clearInterval(qrAnimationRef.current.timer);\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n }\n console.log('QR Manual mode disabled - auto-scroll resumed');\n }\n };\n\n const nextQrFrame = async () => {\n console.log('\uD83C\uDFAE nextQrFrame called, qrFramesTotal:', qrFramesTotal, 'qrAnimationRef.current:', qrAnimationRef.current);\n if (qrAnimationRef.current.chunks.length > 1) {\n const nextIdx = (qrAnimationRef.current.idx + 1) % qrAnimationRef.current.chunks.length;\n qrAnimationRef.current.idx = nextIdx;\n setQrFrameIndex(nextIdx + 1);\n console.log('\uD83C\uDFAE Next frame index:', nextIdx + 1);\n // Ensure auto-advance timer runs in manual mode too\n try { clearInterval(qrAnimationRef.current.timer); } catch {}\n qrAnimationRef.current.timer = null;\n await renderCurrent();\n // If not in manual mode, restart auto timer\n if (!qrManualMode && qrAnimationRef.current.chunks.length > 1) {\n const intervalMs = 3000;\n qrAnimationRef.current.active = true;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n } else {\n qrAnimationRef.current.active = false;\n }\n } else {\n console.log('\uD83C\uDFAE No multiple frames to navigate');\n }\n };\n\n const prevQrFrame = async () => {\n console.log('\uD83C\uDFAE prevQrFrame called, qrFramesTotal:', qrFramesTotal, 'qrAnimationRef.current:', qrAnimationRef.current);\n if (qrAnimationRef.current.chunks.length > 1) {\n const prevIdx = (qrAnimationRef.current.idx - 1 + qrAnimationRef.current.chunks.length) % qrAnimationRef.current.chunks.length;\n qrAnimationRef.current.idx = prevIdx;\n setQrFrameIndex(prevIdx + 1);\n console.log('\uD83C\uDFAE Previous frame index:', prevIdx + 1);\n try { clearInterval(qrAnimationRef.current.timer); } catch {}\n qrAnimationRef.current.timer = null;\n await renderCurrent();\n if (!qrManualMode && qrAnimationRef.current.chunks.length > 1) {\n const intervalMs = 3000;\n qrAnimationRef.current.active = true;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n } else {\n qrAnimationRef.current.active = false;\n }\n } else {\n console.log('\uD83C\uDFAE No multiple frames to navigate');\n }\n };\n\n // Buffer for assembling scanned COSE chunks\n const qrChunksBufferRef = React.useRef({ id: null, total: 0, seen: new Set(), items: [] });\n\n const generateQRCode = async (data) => {\n try {\n const originalSize = typeof data === 'string' ? data.length : JSON.stringify(data).length;\n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n \n // Try binary format first (CBOR + deflate + base64url)\n if (typeof window.generateBinaryQRCodeFromObject === 'function') {\n try {\n const obj = typeof data === 'string' ? JSON.parse(data) : data;\n const qrDataUrl = await window.generateBinaryQRCodeFromObject(obj, { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (qrDataUrl) {\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n setQrCodeUrl(qrDataUrl);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n } catch (e) {\n console.warn('Binary QR generation failed, falling back to compressed:', e?.message || e);\n }\n }\n \n // Fallback to compressed JSON\n if (typeof window.generateCompressedQRCode === 'function') {\n try {\n const payload = typeof data === 'string' ? data : JSON.stringify(data);\n const qrDataUrl = await window.generateCompressedQRCode(payload, { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (qrDataUrl) {\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n setQrCodeUrl(qrDataUrl);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n } catch (e) {\n console.warn('Compressed QR generation failed, falling back to plain:', e?.message || e);\n }\n }\n \n // Final fallback to plain JSON\n const payload = typeof data === 'string' ? data : JSON.stringify(data);\n if (payload.length <= MAX_QR_LEN) {\n if (!window.generateQRCode) throw new Error('QR code generator unavailable');\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n const qrDataUrl = await window.generateQRCode(payload, { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n setQrCodeUrl(qrDataUrl);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n\n // Large payload: \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u043C \u043D\u0430 \u0444\u0440\u0435\u0439\u043C\u044B (plain JSON)\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false };\n setQrFrameIndex(0);\n setQrFramesTotal(0);\n setQrManualMode(false);\n const id = `raw_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n const TARGET_CHUNKS = 10;\n const FRAME_MAX = Math.max(200, Math.floor(payload.length / TARGET_CHUNKS));\n const total = Math.ceil(payload.length / FRAME_MAX);\n const rawChunks = [];\n for (let i = 0; i < total; i++) {\n const seq = i + 1;\n const part = payload.slice(i * FRAME_MAX, (i + 1) * FRAME_MAX);\n rawChunks.push(JSON.stringify({ hdr: { v: 1, id, seq, total, rt: 'raw' }, body: part }));\n }\n if (!window.generateQRCode) throw new Error('QR code generator unavailable');\n if (rawChunks.length === 1) {\n const url = await window.generateQRCode(rawChunks[0], { errorCorrectionLevel: 'M', margin: 2, size: QR_SIZE });\n setQrCodeUrl(url);\n setQrFramesTotal(1);\n setQrFrameIndex(1);\n return;\n }\n qrAnimationRef.current.chunks = rawChunks;\n qrAnimationRef.current.idx = 0;\n qrAnimationRef.current.active = true;\n setQrFramesTotal(rawChunks.length);\n setQrFrameIndex(1);\n const EC_OPTS = { errorCorrectionLevel: 'M', margin: 2, size: QR_SIZE };\n await renderNext();\n\n if (!qrManualMode) {\n const intervalMs = 4000; // 4 seconds per frame for better readability\n qrAnimationRef.current.active = true;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n }\n return;\n } catch (error) {\n console.error('QR code generation failed:', error);\n setMessages(prev => [...prev, {\n message: ` QR code generation failed: ${error.message}`,\n type: 'error'\n }]);\n }\n };\n\n const reconstructFromTemplate = (templateData) => {\n // Reconstruct full offer from template\n const fullOffer = {\n type: \"enhanced_secure_offer\",\n version: templateData.version,\n timestamp: templateData.timestamp,\n sessionId: templateData.sessionId,\n connectionId: templateData.connectionId,\n verificationCode: templateData.verificationCode,\n salt: templateData.salt,\n sdp: templateData.sdp,\n keyFingerprints: templateData.keyFingerprints,\n capabilities: templateData.capabilities,\n \n // Reconstruct ECDH key object\n ecdhPublicKey: {\n keyType: \"ECDH\",\n keyData: templateData.ecdhKeyData,\n timestamp: templateData.timestamp - 1000, // Approximate\n version: templateData.version,\n signature: templateData.ecdhSignature\n },\n \n // Reconstruct ECDSA key object\n ecdsaPublicKey: {\n keyType: \"ECDSA\",\n keyData: templateData.ecdsaKeyData,\n timestamp: templateData.timestamp - 999, // Approximate\n version: templateData.version,\n signature: templateData.ecdsaSignature\n },\n \n // Reconstruct auth challenge\n authChallenge: {\n challenge: templateData.authChallenge,\n timestamp: templateData.timestamp,\n nonce: templateData.authNonce,\n version: templateData.version\n },\n \n // Generate security level (can be recalculated)\n securityLevel: {\n level: \"CRITICAL\",\n score: 20,\n color: \"red\",\n verificationResults: {\n encryption: { passed: false, details: \"Encryption not working\", points: 0 },\n keyExchange: { passed: true, details: \"Simple key exchange verified\", points: 15 },\n messageIntegrity: { passed: false, details: \"Message integrity failed\", points: 0 },\n rateLimiting: { passed: true, details: \"Rate limiting active\", points: 5 },\n ecdsa: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n metadataProtection: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n pfs: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n nestedEncryption: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n packetPadding: { passed: false, details: \"Enhanced session required - feature not available\", points: 0 },\n advancedFeatures: { passed: false, details: \"Premium session required - feature not available\", points: 0 }\n },\n timestamp: templateData.timestamp,\n details: \"Real verification: 20/100 security checks passed (2/4 available)\",\n isRealData: true,\n passedChecks: 2,\n totalChecks: 4,\n sessionType: \"demo\",\n maxPossibleScore: 50\n }\n };\n \n return fullOffer;\n };\n\n const handleQRScan = async (scannedData) => {\n try {\n console.log('QR Code scanned:', scannedData.substring(0, 100) + '...');\n console.log('Current buffer state:', qrChunksBufferRef.current);\n \n // Check if this is a binary chunk (starts with SB1:bin: or is a raw binary chunk)\n if (scannedData.startsWith('SB1:bin:') || (qrChunksBufferRef.current && qrChunksBufferRef.current.id)) {\n console.log('Binary chunk detected:', scannedData.substring(0, 50) + '...');\n \n // This is a binary chunk - add to buffer\n if (!qrChunksBufferRef.current.id) {\n console.log('Initializing buffer for binary chunks');\n // Initialize buffer for binary chunks\n qrChunksBufferRef.current = { \n id: `bin_${Date.now()}`, \n total: 4, // We expect 4 chunks\n seen: new Set(), \n items: [],\n lastUpdateMs: Date.now()\n };\n }\n \n // Add chunk to buffer (use data hash as identifier)\n const chunkHash = scannedData.substring(0, 50); // Use first 50 chars as hash\n \n // Check if this chunk was already scanned\n if (qrChunksBufferRef.current.seen.has(chunkHash)) {\n console.log(`Chunk already scanned, ignoring...`);\n return Promise.resolve(false);\n }\n \n qrChunksBufferRef.current.seen.add(chunkHash);\n qrChunksBufferRef.current.items.push(scannedData);\n qrChunksBufferRef.current.lastUpdateMs = Date.now();\n \n // Emit progress and force re-render\n try {\n const uniqueCount = qrChunksBufferRef.current.seen.size;\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { \n detail: { \n id: qrChunksBufferRef.current.id, \n seq: uniqueCount, \n total: qrChunksBufferRef.current.total \n } \n }));\n \n // Force re-render to update progress indicator\n setQrFramesTotal(qrChunksBufferRef.current.total);\n setQrFrameIndex(uniqueCount);\n } catch {}\n \n // Check if we have all chunks\n const isComplete = qrChunksBufferRef.current.seen.size >= qrChunksBufferRef.current.total;\n console.log(`Chunks collected: ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, complete: ${isComplete}`);\n \n if (!isComplete) {\n // Keep scanner open for more chunks\n console.log(`Scanned chunk ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, waiting for more...`);\n return Promise.resolve(false);\n }\n \n // All chunks collected - reconstruct binary data\n try {\n const fullBinaryData = qrChunksBufferRef.current.items.join('');\n // Store the original binary data, not decoded JSON\n if (showOfferStep) {\n setAnswerInput(fullBinaryData);\n } else {\n setOfferInput(fullBinaryData);\n }\n \n setMessages(prev => [...prev, { \n message: 'All binary chunks captured. Payload reconstructed.', \n type: 'success' \n }]);\n \n // Clear buffer and close scanner\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('Binary chunks reconstruction failed:', e);\n return Promise.resolve(false);\n }\n }\n \n // Check if this might be a binary chunk (long string without JSON structure)\n if (scannedData.length > 100 && !scannedData.startsWith('{') && !scannedData.startsWith('[')) {\n console.log('Detected potential binary chunk (long non-JSON string):', scannedData.substring(0, 50) + '...');\n \n // Initialize buffer if not exists\n if (!qrChunksBufferRef.current.id) {\n console.log('Initializing buffer for potential binary chunks');\n qrChunksBufferRef.current = { \n id: `bin_${Date.now()}`, \n total: 4, // We expect 4 chunks\n seen: new Set(), \n items: [],\n lastUpdateMs: Date.now()\n };\n }\n \n // Add chunk to buffer (use data hash as identifier)\n const chunkHash = scannedData.substring(0, 50); // Use first 50 chars as hash\n \n // Check if this chunk was already scanned\n if (qrChunksBufferRef.current.seen.has(chunkHash)) {\n console.log(`Chunk already scanned, ignoring...`);\n return Promise.resolve(false);\n }\n \n qrChunksBufferRef.current.seen.add(chunkHash);\n qrChunksBufferRef.current.items.push(scannedData);\n qrChunksBufferRef.current.lastUpdateMs = Date.now();\n \n // Force re-render to update progress indicator\n try {\n const uniqueCount = qrChunksBufferRef.current.seen.size;\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { \n detail: { \n id: qrChunksBufferRef.current.id, \n seq: uniqueCount, \n total: qrChunksBufferRef.current.total \n } \n }));\n \n // Force re-render to update progress indicator\n setQrFramesTotal(qrChunksBufferRef.current.total);\n setQrFrameIndex(uniqueCount);\n } catch {}\n \n // Check if we have all chunks\n const isComplete = qrChunksBufferRef.current.seen.size >= qrChunksBufferRef.current.total;\n console.log(`Chunks collected: ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, complete: ${isComplete}`);\n \n if (!isComplete) {\n // Keep scanner open for more chunks\n console.log(`Scanned chunk ${qrChunksBufferRef.current.seen.size}/${qrChunksBufferRef.current.total}, waiting for more...`);\n return Promise.resolve(false);\n }\n \n // All chunks collected - reconstruct binary data\n try {\n const fullBinaryData = qrChunksBufferRef.current.items.join('');\n // Store the original binary data, not decoded JSON\n if (showOfferStep) {\n setAnswerInput(fullBinaryData);\n } else {\n setOfferInput(fullBinaryData);\n }\n \n setMessages(prev => [...prev, { \n message: 'All binary chunks captured. Payload reconstructed.', \n type: 'success' \n }]);\n \n // Clear buffer and close scanner\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('Binary chunks reconstruction failed:', e);\n return Promise.resolve(false);\n }\n }\n \n // Single QR code - try to decode directly\n // Removed verbose debug log\n let parsedData;\n if (typeof window.decodeAnyPayload === 'function') {\n const any = window.decodeAnyPayload(scannedData);\n if (typeof any === 'string') {\n parsedData = JSON.parse(any);\n } else {\n parsedData = any; // object from binary\n }\n } else {\n const maybeDecompressed = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(scannedData) : scannedData;\n parsedData = JSON.parse(maybeDecompressed);\n }\n console.log('Decoded data:', parsedData);\n \n // QR with hdr/body: COSE or RAW/BIN animated frames\n if (parsedData.hdr && parsedData.body) {\n const { hdr } = parsedData;\n // Initialize/rotate buffer by id\n if (!qrChunksBufferRef.current.id || qrChunksBufferRef.current.id !== hdr.id) {\n qrChunksBufferRef.current = { id: hdr.id, total: hdr.total || 1, seen: new Set(), items: [], lastUpdateMs: Date.now() };\n try {\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { detail: { id: hdr.id, seq: 0, total: hdr.total || 1 } }));\n } catch {}\n }\n // Deduplicate & record\n if (!qrChunksBufferRef.current.seen.has(hdr.seq)) {\n qrChunksBufferRef.current.seen.add(hdr.seq);\n qrChunksBufferRef.current.items.push(scannedData);\n qrChunksBufferRef.current.lastUpdateMs = Date.now();\n }\n // Emit progress based on unique frames captured\n try {\n const uniqueCount = qrChunksBufferRef.current.seen.size;\n document.dispatchEvent(new CustomEvent('qr-scan-progress', { detail: { id: hdr.id, seq: uniqueCount, total: qrChunksBufferRef.current.total || hdr.total || 0 } }));\n } catch {}\n const isComplete = qrChunksBufferRef.current.seen.size >= (qrChunksBufferRef.current.total || 1);\n if (!isComplete) {\n // Explicitly keep scanner open\n return Promise.resolve(false);\n }\n // Completed: decide RAW vs BIN vs COSE\n if (hdr.rt === 'raw') {\n try {\n // Sort by seq and concatenate bodies\n const parts = qrChunksBufferRef.current.items\n .map(s => JSON.parse(s))\n .sort((a, b) => (a.hdr.seq || 0) - (b.hdr.seq || 0))\n .map(p => p.body || '');\n const fullText = parts.join('');\n const payloadObj = JSON.parse(fullText);\n if (showOfferStep) {\n setAnswerInput(JSON.stringify(payloadObj, null, 2));\n } else {\n setOfferInput(JSON.stringify(payloadObj, null, 2));\n }\n setMessages(prev => [...prev, { message: 'All frames captured. RAW payload reconstructed.', type: 'success' }]);\n try { document.dispatchEvent(new CustomEvent('qr-scan-complete', { detail: { id: hdr.id } })); } catch {}\n // Close scanner from caller by returning true\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('RAW multi-frame reconstruction failed:', e);\n return Promise.resolve(false);\n }\n } else if (hdr.rt === 'bin') {\n try {\n const parts = qrChunksBufferRef.current.items\n .map(s => JSON.parse(s))\n .sort((a, b) => (a.hdr.seq || 0) - (b.hdr.seq || 0))\n .map(p => p.body || '');\n const fullText = parts.join(''); // SB1:bin:...\n let payloadObj;\n if (typeof window.decodeAnyPayload === 'function') {\n const any = window.decodeAnyPayload(fullText);\n payloadObj = (typeof any === 'string') ? JSON.parse(any) : any;\n } else {\n payloadObj = JSON.parse(fullText);\n }\n if (showOfferStep) {\n setAnswerInput(JSON.stringify(payloadObj, null, 2));\n } else {\n setOfferInput(JSON.stringify(payloadObj, null, 2));\n }\n setMessages(prev => [...prev, { message: 'All frames captured. BIN payload reconstructed.', type: 'success' }]);\n try { document.dispatchEvent(new CustomEvent('qr-scan-complete', { detail: { id: hdr.id } })); } catch {}\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n } catch (e) {\n console.warn('BIN multi-frame reconstruction failed:', e);\n return Promise.resolve(false);\n }\n } else if (window.receiveAndProcess) {\n try {\n const results = await window.receiveAndProcess(qrChunksBufferRef.current.items);\n if (results.length > 0) {\n const { payloadObj } = results[0];\n if (showOfferStep) {\n setAnswerInput(JSON.stringify(payloadObj, null, 2));\n } else {\n setOfferInput(JSON.stringify(payloadObj, null, 2));\n }\n setMessages(prev => [...prev, { message: 'All frames captured. COSE payload reconstructed.', type: 'success' }]);\n try { document.dispatchEvent(new CustomEvent('qr-scan-complete', { detail: { id: hdr.id } })); } catch {}\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n setShowQRScannerModal(false);\n return Promise.resolve(true);\n }\n } catch (e) {\n console.warn('COSE multi-chunk processing failed:', e);\n }\n return Promise.resolve(false);\n } else {\n return Promise.resolve(false);\n }\n }\n \n // Check if this is a template-based QR code\n if (parsedData.type === 'enhanced_secure_offer_template') {\n console.log('QR scan: Template-based offer detected, reconstructing...');\n const fullOffer = reconstructFromTemplate(parsedData);\n \n // Determine which input to populate based on current mode\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n setAnswerInput(JSON.stringify(fullOffer, null, 2));\n console.log('\uD83D\uDCF1 Template data populated to answerInput (waiting for response mode)');\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n setOfferInput(JSON.stringify(fullOffer, null, 2));\n console.log('\uD83D\uDCF1 Template data populated to offerInput (paste invitation mode)');\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully! Full offer reconstructed from template.',\n type: 'success'\n }]);\n setShowQRScannerModal(false); // Close QR scanner modal\n return true;\n }\n // Check if this is a reference-based QR code\n else if (parsedData.type === 'secure_offer_reference' && parsedData.referenceId) {\n // Try to get the full offer data from localStorage\n const fullOfferData = localStorage.getItem(`qr_offer_${parsedData.referenceId}`);\n if (fullOfferData) {\n const fullOffer = JSON.parse(fullOfferData);\n // Determine which input to populate based on current mode\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n setAnswerInput(JSON.stringify(fullOffer, null, 2));\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n setOfferInput(JSON.stringify(fullOffer, null, 2));\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully! Full offer data retrieved.',\n type: 'success'\n }]);\n setShowQRScannerModal(false); // Close QR scanner modal\n return true;\n } else {\n setMessages(prev => [...prev, {\n message: 'QR code reference found but full data not available. Please use copy/paste.',\n type: 'error'\n }]);\n return false;\n }\n } else {\n // If payload was compressed, it's already decompressed above; keep legacy warning only when clearly incomplete\n if (!parsedData.sdp && parsedData.type === 'enhanced_secure_offer') {\n setMessages(prev => [...prev, {\n message: 'Compressed QR may omit SDP for brevity. Use copy/paste if connection fails.',\n type: 'warning'\n }]);\n }\n \n // Determine which input to populate based on current mode\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n console.log('QR scan: Populating answerInput with:', parsedData);\n setAnswerInput(JSON.stringify(parsedData, null, 2));\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n console.log('QR scan: Populating offerInput with:', parsedData);\n setOfferInput(JSON.stringify(parsedData, null, 2));\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully!',\n type: 'success'\n }]);\n setShowQRScannerModal(false);\n return true;\n }\n } catch (error) {\n // If not JSON, use as plain text\n if (showOfferStep) {\n // In \"Waiting for peer's response\" mode - populate answerInput\n setAnswerInput(scannedData);\n } else {\n // In \"Paste secure invitation\" mode - populate offerInput\n setOfferInput(scannedData);\n }\n setMessages(prev => [...prev, {\n message: '\uD83D\uDCF1 QR code scanned successfully!',\n type: 'success'\n }]);\n setShowQRScannerModal(false);\n return true;\n }\n };\n\n\n\n \n const handleCreateOffer = async () => {\n try {\n // All security features are enabled by default\n setIsGeneratingKeys(true);\n setOfferData('');\n setShowOfferStep(false);\n setShowQRCode(false);\n setQrCodeUrl('');\n \n const offer = await webrtcManagerRef.current.createSecureOffer();\n \n // Store offer data directly (no encryption needed with SAS)\n setOfferData(offer);\n setShowOfferStep(true);\n \n // Generate QR code with binary format and chunking\n const offerString = typeof offer === 'object' ? JSON.stringify(offer) : offer;\n try {\n if (typeof window.encodeBinaryToPrefixed === 'function') {\n const bin = window.encodeBinaryToPrefixed(offerString);\n // Force chunking into 4 parts - split binary data directly\n const TARGET_CHUNKS = 4;\n let total = TARGET_CHUNKS;\n let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));\n if (FRAME_MAX <= 0) FRAME_MAX = 200;\n \n // Ensure we don't exceed TARGET_CHUNKS\n if (bin.length <= FRAME_MAX) {\n total = 1;\n FRAME_MAX = bin.length;\n } else {\n // Recalculate to ensure exactly TARGET_CHUNKS parts\n FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);\n total = TARGET_CHUNKS;\n }\n \n const id = `bin_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const chunks = [];\n for (let i = 0; i < total; i++) {\n const seq = i + 1;\n const part = bin.slice(i * FRAME_MAX, (i + 1) * FRAME_MAX);\n // Store binary chunks directly without JSON wrapper\n chunks.push(part);\n }\n \n // Seed first frame and start auto-advance immediately\n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n if (window.generateQRCode && chunks.length > 0) {\n const firstUrl = await window.generateQRCode(chunks[0], { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (firstUrl) setQrCodeUrl(firstUrl);\n }\n \n // Store precomputed chunks to ref, ready for animation\n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks, idx: 0, active: true };\n setQrFramesTotal(chunks.length);\n setQrFrameIndex(1);\n setQrManualMode(false);\n \n // Start auto-advance loop for Offer immediately\n const intervalMs = 3000;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n \n // Show QR immediately for Offer flow\n try { setShowQRCode(true); } catch {}\n } else {\n // Fallback to single QR\n await generateQRCode(offer);\n try { setShowQRCode(true); } catch {}\n }\n } catch (e) {\n console.warn('Offer QR generation failed:', e);\n }\n \n const existingMessages = messages.filter(m => \n m.type === 'system' && \n (m.message.includes('Secure invitation created') || m.message.includes('Send the encrypted code'))\n );\n \n if (existingMessages.length === 0) {\n setMessages(prev => [...prev, { \n message: 'Secure invitation created and encrypted!', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n setMessages(prev => [...prev, { \n message: 'Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.).',\n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n }\n \n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } catch (error) {\n setMessages(prev => [...prev, { \n message: `Error creating invitation: ${error.message}`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n } finally {\n setIsGeneratingKeys(false);\n }\n };\n \n const handleCreateAnswer = async () => {\n try {\n \n if (!offerInput.trim()) {\n setMessages(prev => [...prev, { \n message: 'You need to insert the invitation code from your interlocutor.', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n return;\n }\n \n try {\n setMessages(prev => [...prev, { \n message: 'Processing the secure invitation...', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n let offer;\n try {\n // Prefer binary decode first, then gzip JSON\n if (typeof window.decodeAnyPayload === 'function') {\n const any = window.decodeAnyPayload(offerInput.trim());\n offer = (typeof any === 'string') ? JSON.parse(any) : any;\n } else {\n const rawText = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(offerInput.trim()) : offerInput.trim();\n offer = JSON.parse(rawText);\n }\n } catch (parseError) {\n throw new Error(`Invalid invitation format: ${parseError.message}`);\n }\n \n if (!offer || typeof offer !== 'object') {\n throw new Error('The invitation must be an object');\n }\n \n // Support both compact and legacy offer formats\n const isValidOfferType = (offer.t === 'offer') || (offer.type === 'enhanced_secure_offer');\n if (!isValidOfferType) {\n throw new Error('Invalid invitation type. Expected offer or enhanced_secure_offer');\n }\n\n const answer = await webrtcManagerRef.current.createSecureAnswer(offer);\n \n // Store answer data directly (no encryption needed with SAS)\n setAnswerData(answer);\n setShowAnswerStep(true);\n \n // Generate QR code with binary format and chunking\n const answerString = typeof answer === 'object' ? JSON.stringify(answer) : answer;\n try {\n if (typeof window.encodeBinaryToPrefixed === 'function') {\n const bin = window.encodeBinaryToPrefixed(answerString);\n // Force chunking into 4 parts - split binary data directly\n const TARGET_CHUNKS = 4;\n let total = TARGET_CHUNKS;\n let FRAME_MAX = Math.max(200, Math.ceil(bin.length / TARGET_CHUNKS));\n if (FRAME_MAX <= 0) FRAME_MAX = 200;\n \n // Ensure we don't exceed TARGET_CHUNKS\n if (bin.length <= FRAME_MAX) {\n total = 1;\n FRAME_MAX = bin.length;\n } else {\n // Recalculate to ensure exactly TARGET_CHUNKS parts\n FRAME_MAX = Math.ceil(bin.length / TARGET_CHUNKS);\n total = TARGET_CHUNKS;\n }\n \n const id = `ans_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const chunks = [];\n for (let i = 0; i < total; i++) {\n const seq = i + 1;\n const part = bin.slice(i * FRAME_MAX, (i + 1) * FRAME_MAX);\n // Store binary chunks directly without JSON wrapper\n chunks.push(part);\n }\n \n const isDesktop = (typeof window !== 'undefined') && ((window.innerWidth || 0) >= 1024);\n const QR_SIZE = isDesktop ? 720 : 512;\n if (window.generateQRCode && chunks.length > 0) {\n const firstUrl = await window.generateQRCode(chunks[0], { errorCorrectionLevel: 'M', size: QR_SIZE, margin: 2 });\n if (firstUrl) setQrCodeUrl(firstUrl);\n }\n \n try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {}\n qrAnimationRef.current = { timer: null, chunks, idx: 0, active: true };\n setQrFramesTotal(chunks.length);\n setQrFrameIndex(1);\n setQrManualMode(false);\n \n const intervalMs = 3000;\n qrAnimationRef.current.timer = setInterval(renderAndAdvance, intervalMs);\n \n // Show QR immediately for Answer flow\n try { setShowQRCode(true); } catch {}\n } else {\n // Fallback to single QR\n await generateQRCode(answer);\n try { setShowQRCode(true); } catch {}\n }\n } catch (e) {\n console.warn('Answer QR generation failed:', e);\n }\n \n // Mark generated answers as active immediately.\n // `answerInput` is empty on the joiner path\n // because the response was created locally,\n // not pasted by the user.\n if (typeof markAnswerCreated === 'function') {\n markAnswerCreated();\n }\n\n \n const existingResponseMessages = messages.filter(m => \n m.type === 'system' && \n (m.message.includes('Secure response created') || m.message.includes('Send the response'))\n );\n \n if (existingResponseMessages.length === 0) {\n setMessages(prev => [...prev, { \n message: 'Secure response created!', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n setMessages(prev => [...prev, { \n message: 'Send the response code to the initiator via a secure channel or let them scan the QR code below.', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n }\n \n // Update security level after creating answer\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } catch (error) {\n console.error('Error in handleCreateAnswer:', error);\n setMessages(prev => [...prev, { \n message: `Error processing the invitation: ${error.message}`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n } catch (error) {\n console.error('Error in handleCreateAnswer:', error);\n setMessages(prev => [...prev, { \n message: `Invitation processing error: ${error.message}`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n };\n \n const handleConnect = async () => {\n try {\n if (!answerInput.trim()) {\n setMessages(prev => [...prev, { \n message: 'You need to insert the response code from your interlocutor.', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n return;\n }\n \n try {\n setMessages(prev => [...prev, { \n message: 'Processing the secure response...', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n let answer;\n try {\n // Prefer binary decode first, then gzip JSON\n if (typeof window.decodeAnyPayload === 'function') {\n const anyAnswer = window.decodeAnyPayload(answerInput.trim());\n answer = (typeof anyAnswer === 'string') ? JSON.parse(anyAnswer) : anyAnswer;\n } else {\n const rawText = (typeof window.decompressIfNeeded === 'function') ? window.decompressIfNeeded(answerInput.trim()) : answerInput.trim();\n answer = JSON.parse(rawText);\n }\n } catch (parseError) {\n throw new Error(`Invalid response format: ${parseError.message}`);\n }\n \n if (!answer || typeof answer !== 'object') {\n throw new Error('The response must be an object');\n }\n \n // Support both compact and legacy formats\n const answerType = answer.t || answer.type;\n if (!answerType || (answerType !== 'answer' && answerType !== 'enhanced_secure_answer')) {\n throw new Error('Invalid response type. Expected answer or enhanced_secure_answer');\n }\n \n await webrtcManagerRef.current.handleSecureAnswer(answer);\n \n // All security features are enabled by default - no session activation needed\n if (pendingSession) {\n setPendingSession(null);\n setMessages(prev => [...prev, { \n message: `All security features enabled by default`, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n \n setMessages(prev => [...prev, { \n message: 'Finalizing the secure connection...', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n // Update security level after handling answer\n if (!window.isUpdatingSecurity) {\n updateSecurityLevel().catch(console.error);\n }\n } catch (error) {\n console.error('Error in handleConnect inner try:', error);\n \n // \u0411\u043E\u043B\u0435\u0435 \u0434\u0435\u0442\u0430\u043B\u044C\u043D\u0430\u044F \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0430 \u043E\u0448\u0438\u0431\u043E\u043A\n let errorMessage = 'Connection setup error';\n if (error.message.includes('CRITICAL SECURITY FAILURE')) {\n if (error.message.includes('ECDH public key structure')) {\n errorMessage = 'Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.';\n } else if (error.message.includes('ECDSA public key structure')) {\n errorMessage = 'Invalid response code - missing signature verification key. Please check the code and try again.';\n } else {\n errorMessage = 'Security validation failed - possible attack detected';\n }\n } else if (error.message.includes('too old') || error.message.includes('replay')) {\n errorMessage = 'Response data is outdated - please use a fresh invitation';\n } else if (error.message.includes('MITM') || error.message.includes('signature')) {\n errorMessage = 'Security breach detected - connection rejected';\n } else if (error.message.includes('Invalid') || error.message.includes('format')) {\n errorMessage = 'Invalid response format - please check the code';\n } else {\n errorMessage = ` ${error.message}`;\n }\n \n setMessages(prev => [...prev, { \n message: errorMessage, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now(),\n showRetryButton: true\n }]);\n\n if (!error.message.includes('too old') && !error.message.includes('replay')) {\n setPendingSession(null);\n setSessionTimeLeft(0);\n }\n\n setConnectionStatus('failed');\n \n } \n } catch (error) {\n console.error('Error in handleConnect outer try:', error);\n\n let errorMessage = 'Connection setup error';\n if (error.message.includes('CRITICAL SECURITY FAILURE')) {\n if (error.message.includes('ECDH public key structure')) {\n errorMessage = 'Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.';\n } else if (error.message.includes('ECDSA public key structure')) {\n errorMessage = 'Invalid response code - missing signature verification key. Please check the code and try again.';\n } else {\n errorMessage = 'Security validation failed - possible attack detected';\n }\n } else if (error.message.includes('too old') || error.message.includes('replay')) {\n errorMessage = 'Response data is outdated - please use a fresh invitation';\n } else if (error.message.includes('MITM') || error.message.includes('signature')) {\n errorMessage = 'Security breach detected - connection rejected';\n } else if (error.message.includes('Invalid') || error.message.includes('format')) {\n errorMessage = 'Invalid response format - please check the code';\n } else {\n errorMessage = `${error.message}`;\n }\n \n setMessages(prev => [...prev, { \n message: errorMessage, \n type: 'system',\n id: Date.now(),\n timestamp: Date.now(),\n showRetryButton: true\n }]);\n\n if (!error.message.includes('too old') && !error.message.includes('replay')) {\n setPendingSession(null);\n setSessionTimeLeft(0);\n }\n\n setConnectionStatus('failed');\n }\n };\n \n const handleVerifyConnection = async (userCode, isValid = true) => {\n if (isValid) {\n webrtcManagerRef.current.confirmVerification(userCode);\n // Mark local verification as confirmed\n setLocalVerificationConfirmed(true);\n \n // Initialize notification integration if permission was granted\n try {\n if (window.NotificationIntegration && webrtcManagerRef.current && !notificationIntegrationRef.current) {\n const integration = new window.NotificationIntegration(webrtcManagerRef.current);\n await integration.init();\n \n // Store reference for cleanup\n notificationIntegrationRef.current = integration;\n \n \n // Check if permission was already granted\n const status = integration.getStatus();\n if (status.permission === 'granted') {\n setMessages(prev => [...prev, { \n message: '\u2713 Notifications enabled - you will receive alerts when the tab is inactive', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n } else {\n setMessages(prev => [...prev, { \n message: '\u2139 Notifications disabled - you can enable them using the button on the main page', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n }\n } else if (notificationIntegrationRef.current) {\n } else {\n // Handle error silently\n }\n } catch (error) {\n console.warn('Failed to initialize notifications:', error);\n // Don't show error to user, notifications are optional\n }\n } else {\n setMessages(prev => [...prev, { \n message: ' Verification rejected. The connection is unsafe! Session reset..', \n type: 'system',\n id: Date.now(),\n timestamp: Date.now()\n }]);\n \n // Clear verification states\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n setShowVerification(false);\n setVerificationCode('');\n \n // Reset UI to initial state\n setConnectionStatus('disconnected');\n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setShowAnswerStep(false);\n setKeyFingerprint('');\n setSecurityLevel(null);\n setIsVerified(false);\n setMessages([]);\n \n setSessionTimeLeft(0);\n setPendingSession(null);\n \n // Dispatch disconnected event for SessionTimer\n document.dispatchEvent(new CustomEvent('disconnected'));\n \n handleDisconnect();\n }\n };\n \n const handleSendMessage = async () => {\n if (!messageInput.trim()) {\n return;\n }\n \n if (!webrtcManagerRef.current) {\n return;\n }\n\n const baseTextEarly = messageInput.trim();\n const midEarly = `m_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n\n // Offline guard: a P2P data channel can stay \"open\" after the\n // browser loses connectivity, so isConnected() isn't enough \u2014 show\n // the bubble as \"not sent\" (\u2717) instead of silently transmitting.\n // Uses the live offline state (catches console-simulated offline too).\n const offlineNow = isOffline\n || (typeof navigator !== 'undefined' && navigator.onLine === false)\n || (window.pwaOfflineManager && window.pwaOfflineManager.isOnline === false);\n if (offlineNow) {\n // Store-and-forward: show one check (sent), keep it in the\n // conversation at its original time, and transmit on reconnect.\n const outTextOff = codeMode ? '```\\n' + baseTextEarly + '\\n```' : baseTextEarly;\n const tsOff = Date.now();\n const metaOff = { mid: midEarly, ts: tsOff };\n if (viewOnceMode) { metaOff.once = true; metaOff.onceTtl = viewOnceTtl; }\n if (disappearTtl > 0) metaOff.ttl = disappearTtl;\n const echoOpts = { mid: midEarly, status: 'sent', timestamp: tsOff };\n if (disappearTtl > 0) echoOpts.expiresAt = tsOff + disappearTtl * 1000;\n addMessageWithAutoScroll(outTextOff, 'sent', echoOpts);\n // Queue on the ACTIVE session's outgoing queue (created in createSession);\n // guard in case the entry is missing so the echo never gets lost.\n const q = queuesRef.current.get(activeIdRef.current);\n if (q) q.outgoing.push({ outText: outTextOff, meta: metaOff, mid: midEarly });\n setMessageInput('');\n if (codeMode) setCodeMode(false);\n if (viewOnceMode) setViewOnceMode(false);\n return;\n }\n\n // Online but the channel isn't ready (e.g. dropped/not yet established) \u2014\n // can't transmit. The setup screen is shown for re-establishment in that case.\n if (!webrtcManagerRef.current.isConnected()) {\n return;\n }\n\n try {\n const baseText = baseTextEarly;\n // Code mode wraps the text in a fenced block so both sides render\n // a code window with a copy button (the marker travels as text).\n const outText = codeMode ? '```\\n' + baseText + '\\n```' : baseText;\n\n // Shared id lets unsend/disappearing/receipts reference the\n // same message on both peers.\n const mid = midEarly;\n const meta = { mid, ts: Date.now() }; // ts \u2192 recipient shows the original send time\n if (viewOnceMode) { // applies to the recipient\n meta.once = true;\n meta.onceTtl = viewOnceTtl; // seconds visible after opening\n }\n if (disappearTtl > 0) meta.ttl = disappearTtl; // applies to both sides\n\n // Local echo: sender sees their own text normally (view-once is a\n // recipient-side control), but disappearing also expires our copy.\n // Starts as \"sending\"; flips to \"sent\" once the channel accepts it\n // and \"delivered\" when the peer's receipt arrives.\n const localOpts = { mid, status: 'sending' };\n if (disappearTtl > 0) localOpts.expiresAt = Date.now() + disappearTtl * 1000;\n addMessageWithAutoScroll(outText, 'sent', localOpts);\n\n try {\n await webrtcManagerRef.current.sendMessage(outText, meta);\n // Reliable ordered data channel: a resolved send means the peer's\n // device received it \u2192 two grey ticks. The peer's read receipt\n // (onMessageDelivered) upgrades it to two green ticks ('read').\n updateMessageStatus(mid, 'delivered');\n } catch (sendErr) {\n updateMessageStatus(mid, 'failed');\n throw sendErr;\n }\n setMessageInput('');\n // Per-message toggles reset; disappearing stays as a sticky setting.\n if (codeMode) setCodeMode(false);\n if (viewOnceMode) setViewOnceMode(false);\n } catch (error) {\n const msg = String(error?.message || error);\n if (!/queued for sending|Data channel not ready/i.test(msg)) {\n addMessageWithAutoScroll(`Sending error: ${msg}`,'system');\n }\n }\n };\n\n // Unsend: remove locally and ask the peer to drop it too.\n const handleUnsendMessage = React.useCallback((mid) => {\n if (!mid) return;\n setMessages(prev => prev.filter(m => String(m.mid) !== String(mid)));\n try { webrtcManagerRef.current?.sendMessageDelete?.(String(mid)); } catch (_) {}\n }, []);\n\n // View-once reveal timeout / disappearing expiry: wipe the content and\n // leave a \"This message has expired\" tombstone (per the design).\n const handleMessageExpire = React.useCallback((id) => {\n setMessages(prev => prev.map(m => m.id === id ? { ...m, expired: true, message: '', expiresAt: undefined } : m));\n }, []);\n\n // Panic wipe: clear the conversation, tear down the session and wipe keys.\n const handleClearData = () => {\n setOfferData('');\n setAnswerData('');\n setOfferInput('');\n setAnswerInput('');\n setShowOfferStep(false);\n setIsGeneratingKeys(false);\n\n if (!shouldPreserveAnswerData()) {\n setShowAnswerStep(false);\n }\n \n setShowVerification(false);\n setShowQRCode(false);\n setShowQRScanner(false);\n setShowQRScannerModal(false);\n // Clear QR scanner buffer\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n\n if (!shouldPreserveAnswerData()) {\n setQrCodeUrl('');\n }\n \n setVerificationCode('');\n setIsVerified(false);\n setKeyFingerprint('');\n setSecurityLevel(null);\n setConnectionStatus('disconnected');\n setMessages([]);\n setMessageInput('');\n \n // Clear verification states\n setLocalVerificationConfirmed(false);\n setRemoteVerificationConfirmed(false);\n setBothVerificationsConfirmed(false);\n \n // PAKE passwords removed - using SAS verification instead \n\n if (typeof console.clear === 'function') {\n console.clear();\n }\n \n // Cleanup session state\n setSessionTimeLeft(0);\n \n setPendingSession(null);\n document.dispatchEvent(new CustomEvent('peer-disconnect'));\n // Session manager removed - all features enabled by default\n };\n \n const handleIncomingDecision = React.useCallback(async (fileId, accepted) => {\n try {\n if (accepted) {\n await webrtcManagerRef.current?.acceptIncomingFile(fileId);\n } else {\n await webrtcManagerRef.current?.rejectIncomingFile(fileId);\n }\n } finally {\n setPendingIncomingFiles(prev => prev.filter(f => f.fileId !== fileId));\n }\n }, []);\n\n // Disconnect tears down ONLY the active session \u2014 its peerConnection, its\n // manager (with the manager's own key-wipe logic) and its notification\n // integration. Every other session keeps running untouched. If it was the\n // last session we immediately open a fresh blank one so the UI isn't empty.\n const handleDisconnect = () => {\n try {\n const id = activeIdRef.current;\n setSessionTimeLeft(0);\n // Global lifecycle events (consumed by any remaining listeners).\n document.dispatchEvent(new CustomEvent('peer-disconnect'));\n document.dispatchEvent(new CustomEvent('disconnected'));\n document.dispatchEvent(new CustomEvent('session-cleanup', {\n detail: { timestamp: Date.now(), reason: 'manual_disconnect' }\n }));\n // Manual disconnect always wipes this chat's data and removes it; the\n // ensure-at-least-one-session effect re-opens the landing if it was the\n // last one. Siblings keep running untouched.\n destroySession(id);\n if (typeof console.clear === 'function') console.clear();\n } catch (error) {\n console.error('Error during disconnect:', error);\n }\n };\n \n const handleSessionActivated = (session) => {\n let message;\n if (session.type === 'demo') {\n message = ` Demo session activated for 6 minutes. You can create invitations!`;\n } else {\n message = ` All security features enabled by default. You can create invitations!`;\n }\n \n addMessageWithAutoScroll(message, 'system');\n \n };\n \n React.useEffect(() => {\n if (connectionStatus === 'connected' && isVerified) {\n addMessageWithAutoScroll(' Secure connection successfully established and verified! You can now communicate safely with full protection against MITM attacks and Perfect Forward Secrecy..', 'system');\n \n }\n }, [connectionStatus, isVerified]);\n \n // Chat view requires an ACTIVE verified connection. On a drop the manager\n // clears its verification state (it must be re-established \u2014 there is no\n // \"keep chatting while disconnected\" in this P2P design), so we fall back to\n // the setup screen, which is the re-establish path. Note: this means a dropped\n // chat shows the connect screen; the conversation history stays in the session.\n const isConnectedAndVerified = (connectionStatus === 'connected' || connectionStatus === 'verified') && isVerified;\n\n // The PWA \"Install app\" pill is a landing-page affordance \u2014 hide it once\n // we're inside the chat (CSS: body.sb-in-chat #pwa-install-button).\n React.useEffect(() => {\n document.body.classList.toggle('sb-in-chat', isConnectedAndVerified);\n return () => document.body.classList.remove('sb-in-chat');\n }, [isConnectedAndVerified]);\n \n React.useEffect(() => {\n // All security features are enabled by default - no session activation needed\n if (isConnectedAndVerified && pendingSession && connectionStatus !== 'failed') {\n setPendingSession(null);\n setSessionTimeLeft(0); \n addMessageWithAutoScroll(' All security features enabled by default', 'system');\n }\n }, [isConnectedAndVerified, pendingSession, connectionStatus]);\n\n // QR Scanner initialization\n React.useEffect(() => {\n if (showQRScannerModal && window.Html5Qrcode) {\n const html5Qrcode = new window.Html5Qrcode(\"qr-reader\");\n const config = { \n fps: 10\n // \u0423\u0431\u0438\u0440\u0430\u0435\u043C qrbox \u0447\u0442\u043E\u0431\u044B \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0432\u0441\u044E \u043E\u0431\u043B\u0430\u0441\u0442\u044C\n };\n\n let isScanning = true;\n\n html5Qrcode.start(\n { facingMode: \"environment\" }, // Use back camera\n config,\n (decodedText, decodedResult) => {\n if (!isScanning) {\n console.log('Scanner stopped, ignoring scan');\n return;\n }\n \n console.log('QR Code scanned:', decodedText);\n console.log('Current buffer state:', qrChunksBufferRef.current);\n \n handleQRScan(decodedText).then((success) => {\n console.log('QR scan result:', success);\n if (success) {\n // Successfully processed - stop scanner and close modal\n console.log('Closing scanner and modal');\n isScanning = false;\n \n // Stop scanner first, then clear\n try {\n console.log('Stopping scanner...');\n html5Qrcode.stop().then(() => {\n console.log('Scanner stopped, clearing...');\n html5Qrcode.clear();\n setShowQRScannerModal(false);\n }).catch((err) => {\n console.log('Error stopping scanner:', err);\n // Try to clear anyway\n try {\n html5Qrcode.clear();\n } catch (clearErr) {\n console.log('Error clearing scanner:', clearErr);\n }\n setShowQRScannerModal(false);\n });\n } catch (err) {\n console.log('Error in scanner cleanup:', err);\n setShowQRScannerModal(false);\n }\n } else {\n console.log('Continuing to scan for more chunks...');\n }\n }).catch((error) => {\n console.error('QR scan processing error:', error);\n // Continue scanning on error\n });\n },\n (error) => {\n // Ignore scanning errors - continue scanning\n if (isScanning) {\n console.log('QR scan error (ignored):', error);\n }\n }\n ).catch((err) => {\n console.error('QR Scanner start error:', err);\n // Close modal on start error\n setShowQRScannerModal(false);\n });\n\n return () => {\n isScanning = false;\n try {\n // Try to stop scanner, but don't worry if it's already stopped\n html5Qrcode.stop().then(() => {\n html5Qrcode.clear();\n }).catch((err) => {\n // Scanner might already be stopped, just clear it\n console.log('Scanner already stopped or error stopping:', err);\n try {\n html5Qrcode.clear();\n } catch (clearErr) {\n console.log('Error clearing scanner in cleanup:', clearErr);\n }\n });\n } catch (err) {\n console.log('Error in cleanup:', err);\n // Just try to clear, don't worry about stopping\n try {\n html5Qrcode.clear();\n } catch (clearErr) {\n console.log('Error clearing scanner in cleanup:', clearErr);\n }\n }\n };\n }\n }, [showQRScannerModal]);\n \n const sessionChats = decorateSessions(sessionsState);\n // The multi-session chrome (left rail + chat column) appears only once there\n // is a genuinely WORKING conversation \u2014 a session whose SAS is verified \u2014 or\n // when more than one session exists. The whole offer/answer/verification flow\n // of the first session (which the manager reports as 'connecting'/'verifying')\n // stays on the original single-column \"Start Secure\" screen, exactly as before;\n // the rail only shows up after the first secure channel is actually established.\n const showSidebar = sessionsState.order.length > 1 || sessionsState.order.some((id) => {\n const s = sessionsState.sessions[id];\n return s && s.sas && s.sas.isVerified;\n });\n\n return React.createElement('div', {\n className: \"minimal-bg\",\n // With the rail visible the app is a fixed-height shell (rail + column\n // fill the viewport, design-style). Otherwise it's the scrollable landing.\n // flexDirection:'row' is explicit \u2014 the .minimal-bg class forces\n // flex-direction:column, which would otherwise stack the rail ABOVE the chat.\n style: showSidebar ? { display: 'flex', flexDirection: 'row', height: '100vh', width: '100%', overflow: 'hidden' } : { minHeight: '100vh' }\n }, [\n showSidebar && React.createElement(SessionsSidebar, {\n key: 'sessions-sidebar',\n chats: sessionChats,\n collapsed: sidebarCollapsed,\n drawerOpen: sidebarDrawerOpen,\n onToggleCollapse: () => setSidebarCollapsed(v => !v),\n onSelect: handleSelectSession,\n onNewChat: handleNewChat,\n onRename: handleRenameSession,\n onCloseDrawer: () => setSidebarDrawerOpen(false),\n myStatus: myStatus,\n onSetStatus: setMyStatus\n }),\n // Mobile-only hamburger that opens the drawer (hidden on desktop via CSS).\n showSidebar && React.createElement('button', {\n key: 'sb-burger',\n className: 'sb-burger',\n onClick: () => setSidebarDrawerOpen(true),\n style: { display: 'none', position: 'fixed', top: '13px', left: '13px', zIndex: 55, width: '38px', height: '38px', borderRadius: '10px', placeItems: 'center', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(18,18,20,0.9)', color: '#cfcfd4', cursor: 'pointer' },\n dangerouslySetInnerHTML: { __html: SB_SVG.burger }\n }),\n React.createElement('div', {\n key: 'app-column',\n className: showSidebar ? 'minimal-bg' : 'minimal-bg min-h-screen',\n style: showSidebar ? { flex: 1, minWidth: 0, height: '100vh', overflow: 'hidden', display: 'flex', flexDirection: 'column' } : {}\n }, [\n // Advanced network settings now render inside the connection\n // screen's right panel (see EnhancedConnectionSetup), matching\n // the design's slide-up-within-the-right-column behavior.\n // The verified chat renders its own in-chat header (SecureBit Chat\n // design); the shared header is shown only on the landing/setup view.\n (!isConnectedAndVerified && !showSidebar && window.EnhancedMinimalHeader) && React.createElement(window.EnhancedMinimalHeader, {\n key: 'header',\n status: connectionStatus,\n fingerprint: keyFingerprint,\n verificationCode: verificationCode,\n onDisconnect: handleDisconnect,\n isConnected: isConnectedAndVerified,\n securityLevel: securityLevel,\n // sessionManager removed - all features enabled by default\n webrtcManager: webrtcManagerRef.current\n }),\n \n React.createElement('main', {\n key: 'main'\n }, \n (() => {\n return isConnectedAndVerified;\n })()\n ? (() => {\n return React.createElement(EnhancedChatInterface, {\n title: active ? active.peerLabel : '',\n isOffline: isOffline,\n peerPresence: active ? active.peerPresence : null,\n onRenameTitle: (label) => { if (activeSessionId) dispatch({ type: SA.RENAME, id: activeSessionId, label }); },\n messages: messages,\n messageInput: messageInput,\n setMessageInput: setMessageInput,\n onSendMessage: handleSendMessage,\n onDisconnect: handleDisconnect,\n keyFingerprint: keyFingerprint,\n isVerified: isVerified,\n chatMessagesRef: chatMessagesRef,\n scrollToBottom: scrollToBottom,\n webrtcManager: webrtcManagerRef.current,\n status: connectionStatus,\n pendingIncomingFiles: pendingIncomingFiles,\n onIncomingDecision: handleIncomingDecision,\n // Secure chat extras\n codeMode: codeMode,\n setCodeMode: setCodeMode,\n viewOnceMode: viewOnceMode,\n setViewOnceMode: setViewOnceMode,\n viewOnceTtl: viewOnceTtl,\n setViewOnceTtl: setViewOnceTtl,\n disappearTtl: disappearTtl,\n setDisappearTtl: setDisappearTtl,\n nowTick: nowTick,\n onUnsendMessage: handleUnsendMessage,\n onMessageExpire: handleMessageExpire\n });\n })()\n : React.createElement(EnhancedConnectionSetup, {\n onCreateOffer: handleCreateOffer,\n onCreateAnswer: handleCreateAnswer,\n onConnect: handleConnect,\n onClearData: handleClearData,\n onVerifyConnection: handleVerifyConnection,\n connectionStatus: connectionStatus,\n offerData: offerData,\n answerData: answerData,\n offerInput: offerInput,\n setOfferInput: setOfferInput,\n answerInput: answerInput,\n setAnswerInput: setAnswerInput,\n showOfferStep: showOfferStep,\n showAnswerStep: showAnswerStep,\n verificationCode: verificationCode,\n showVerification: showVerification,\n showQRCode: showQRCode,\n qrCodeUrl: qrCodeUrl,\n showQRScanner: showQRScanner,\n setShowQRCode: setShowQRCode,\n setShowQRScanner: setShowQRScanner,\n setShowQRScannerModal: setShowQRScannerModal,\n messages: messages,\n localVerificationConfirmed: localVerificationConfirmed,\n remoteVerificationConfirmed: remoteVerificationConfirmed,\n bothVerificationsConfirmed: bothVerificationsConfirmed,\n // QR control props\n qrFramesTotal: qrFramesTotal,\n qrFrameIndex: qrFrameIndex,\n qrManualMode: qrManualMode,\n toggleQrManualMode: toggleQrManualMode,\n nextQrFrame: nextQrFrame,\n prevQrFrame: prevQrFrame,\n // PAKE passwords removed - using SAS verification instead\n markAnswerCreated: markAnswerCreated,\n notificationIntegrationRef: notificationIntegrationRef,\n isGeneratingKeys: isGeneratingKeys,\n setIsGeneratingKeys: setIsGeneratingKeys,\n handleCreateOffer: handleCreateOffer,\n relayOnlyMode: relayOnlyMode,\n setRelayOnlyMode: setRelayOnlyMode,\n webrtcManagerRef: webrtcManagerRef,\n showIceSettings: showIceSettings,\n setShowIceSettings: setShowIceSettings,\n iceServersText: iceServersText,\n iceSettingsPersisted: iceSettingsPersisted,\n customIceServers: customIceServers,\n handleApplyIceSettings: handleApplyIceSettings,\n handleForgetIceSettings: handleForgetIceSettings,\n // Render only the create/connect card inside the chat column\n // (an additional session), instead of the full landing.\n compact: showSidebar\n })\n ),\n \n // QR Scanner Modal \u2014 camera scan (design import: \"Start Secure\" / Camera scan modal)\n showQRScannerModal && (() => {\n const closeScanner = () => {\n setShowQRScannerModal(false);\n // Clear QR scanner buffer\n qrChunksBufferRef.current = { id: null, total: 0, seen: new Set(), items: [] };\n };\n const buf = qrChunksBufferRef.current;\n const hasParts = !!(buf && buf.id && buf.total > 1);\n const framesText = hasParts\n ? `Scanning frames\u2026 ${buf.seen.size} / ${buf.total}`\n : 'Scanning\u2026';\n const corner = (k, st) => React.createElement('span', {\n key: k,\n style: Object.assign({ position: 'absolute', width: '34px', height: '34px', zIndex: 3 }, st)\n });\n return React.createElement('div', {\n key: 'qr-scanner-modal',\n onClick: closeScanner,\n style: { position: 'fixed', inset: 0, zIndex: 50, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', background: 'rgba(6,6,8,0.82)', backdropFilter: 'blur(10px)', WebkitBackdropFilter: 'blur(10px)', animation: 'sbUp .2s ease' }\n }, [\n React.createElement('div', {\n key: 'scanner-container',\n onClick: (e) => e.stopPropagation(),\n style: { width: '100%', maxWidth: '420px', borderRadius: '22px', border: '1px solid rgba(255,255,255,0.1)', background: '#111113', boxShadow: '0 30px 90px rgba(0,0,0,0.6)', overflow: 'hidden' }\n }, [\n // Header\n React.createElement('div', {\n key: 'scanner-header',\n style: { display: 'flex', alignItems: 'center', gap: '11px', padding: '18px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)' }\n }, [\n React.createElement('span', {\n key: 'scanner-icon',\n style: { display: 'flex' },\n dangerouslySetInnerHTML: { __html: '' }\n }),\n React.createElement('div', {\n key: 'scanner-titles',\n style: { flex: 1, lineHeight: 1.2 }\n }, [\n React.createElement('div', {\n key: 'scanner-title',\n style: { fontSize: '15.5px', fontWeight: 800, color: '#f4f4f6' }\n }, 'Scan QR code'),\n React.createElement('div', {\n key: 'scanner-hint',\n style: { fontSize: '12px', color: '#7b7b83' }\n }, \"Point your camera at their QR\")\n ]),\n React.createElement('button', {\n key: 'close-btn',\n onClick: closeScanner,\n style: { width: '32px', height: '32px', display: 'grid', placeItems: 'center', borderRadius: '9px', border: 'none', background: 'rgba(255,255,255,0.05)', color: '#9a9aa2', cursor: 'pointer' }\n }, React.createElement('i', { className: 'fas fa-times' }))\n ]),\n // Body\n React.createElement('div', {\n key: 'scanner-body',\n style: { padding: '22px 24px 24px' }\n }, [\n React.createElement('div', {\n key: 'viewfinder',\n style: { position: 'relative', aspectRatio: '1', borderRadius: '18px', overflow: 'hidden', background: '#000', border: '1px solid rgba(255,255,255,0.1)' }\n }, [\n React.createElement('div', {\n key: 'vf-bg',\n style: { position: 'absolute', inset: 0, background: 'radial-gradient(circle at 50% 45%, #1a1a1f, #000)' }\n }),\n // Camera video is injected here by Html5Qrcode\n React.createElement('div', {\n key: 'qr-reader',\n id: 'qr-reader',\n style: { position: 'absolute', inset: 0, zIndex: 1 }\n }),\n corner('c-tl', { top: '18px', left: '18px', borderTop: '2.5px solid #3ecf8e', borderLeft: '2.5px solid #3ecf8e', borderRadius: '8px 0 0 0' }),\n corner('c-tr', { top: '18px', right: '18px', borderTop: '2.5px solid #3ecf8e', borderRight: '2.5px solid #3ecf8e', borderRadius: '0 8px 0 0' }),\n corner('c-bl', { bottom: '18px', left: '18px', borderBottom: '2.5px solid #3ecf8e', borderLeft: '2.5px solid #3ecf8e', borderRadius: '0 0 0 8px' }),\n corner('c-br', { bottom: '18px', right: '18px', borderBottom: '2.5px solid #3ecf8e', borderRight: '2.5px solid #3ecf8e', borderRadius: '0 0 8px 0' }),\n React.createElement('span', {\n key: 'scan-line',\n style: { position: 'absolute', left: '18px', right: '18px', height: '2.5px', zIndex: 2, background: 'linear-gradient(90deg, transparent, #3ecf8e, transparent)', boxShadow: '0 0 16px #3ecf8e', animation: 'sbScan 1.5s ease-in-out infinite alternate' }\n }),\n React.createElement('div', {\n key: 'scan-status',\n style: { position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 3, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', padding: '14px', background: 'linear-gradient(transparent, rgba(0,0,0,0.6))' }\n }, [\n React.createElement('span', {\n key: 'spinner',\n style: { display: 'flex', animation: 'sbSpin 1.4s linear infinite' },\n dangerouslySetInnerHTML: { __html: '' }\n }),\n React.createElement('span', {\n key: 'scan-frames',\n style: { fontSize: '12.5px', fontWeight: 600, color: '#cfcfd4' }\n }, framesText)\n ])\n ]),\n React.createElement('p', {\n key: 'scanner-note',\n style: { margin: '16px 0 0', textAlign: 'center', fontSize: '12px', lineHeight: 1.5, color: '#6b6b73' }\n }, 'Hold steady until all parts are captured. Camera access is local \u2014 nothing is uploaded.')\n ])\n ])\n ]);\n })()\n ]) // end app-column\n ]);\n };\n // UpdateChecker \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442 \u0434\u043B\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0439 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439\n const UpdateCheckerWrapper = ({ children }) => {\n // \u041F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C UpdateChecker\n if (typeof window !== 'undefined' && window.UpdateChecker) {\n return React.createElement(window.UpdateChecker, {\n debug: false\n }, children);\n }\n // Fallback \u0435\u0441\u043B\u0438 UpdateChecker \u043D\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\n return children;\n };\n\n function initializeApp() {\n if (window.EnhancedSecureCryptoUtils && window.EnhancedSecureWebRTCManager) {\n // \u041E\u0431\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0432 UpdateChecker \u0434\u043B\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u043E\u0439 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439\n const AppWithUpdateChecker = React.createElement(UpdateCheckerWrapper, null,\n React.createElement(EnhancedSecureP2PChat)\n );\n ReactDOM.render(AppWithUpdateChecker, document.getElementById('root'));\n } else {\n console.error('\u041C\u043E\u0434\u0443\u043B\u0438 \u043D\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u044B:', {\n hasCrypto: !!window.EnhancedSecureCryptoUtils,\n hasWebRTC: !!window.EnhancedSecureWebRTCManager\n });\n }\n }\n\n if (typeof window !== 'undefined') {\n\n window.addEventListener('unhandledrejection', (event) => {\n console.error('Unhandled promise rejection:', event.reason);\n event.preventDefault(); \n });\n \n\n window.addEventListener('error', (event) => {\n console.error('Global error:', event.error);\n event.preventDefault(); \n });\n \n if (!window.initializeApp) {\n window.initializeApp = initializeApp;\n }\n };\n \n // Render Enhanced Application with UpdateChecker\n if (window.EnhancedSecureCryptoUtils && window.EnhancedSecureWebRTCManager) {\n const UpdateCheckerWrapper = ({ children }) => {\n if (typeof window !== 'undefined' && window.UpdateChecker) {\n return React.createElement(window.UpdateChecker, {\n debug: false\n }, children);\n }\n return children;\n };\n const AppWithUpdateChecker = React.createElement(UpdateCheckerWrapper, null,\n React.createElement(EnhancedSecureP2PChat)\n );\n ReactDOM.render(AppWithUpdateChecker, document.getElementById('root'));\n } else {\n ReactDOM.render(React.createElement(EnhancedSecureP2PChat), document.getElementById('root'));\n }\n"], + "mappings": "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", + "names": ["setMessages", "addMessageWithAutoScroll", "updateMessageStatus", "setConnectionStatus", "setKeyFingerprint", "setVerificationCode", "setIsVerified", "setLocalVerificationConfirmed", "setRemoteVerificationConfirmed", "setBothVerificationsConfirmed", "setShowVerification", "setShowOfferStep", "setShowAnswerStep", "setShowQRCode", "setQrCodeUrl", "setOfferData", "setAnswerData", "setOfferInput", "setAnswerInput", "setPendingIncomingFiles", "opts", "offerData", "payload", "UpdateCheckerWrapper"] } diff --git a/index.html b/index.html index 1de523f..514b00e 100644 --- a/index.html +++ b/index.html @@ -23,7 +23,7 @@ - + @@ -89,7 +89,7 @@ - + @@ -98,7 +98,7 @@ - + @@ -182,32 +182,36 @@ - - + + + + + - - - - + + + + - - - - + + + +
- - + + - - - - - + + + + + \ No newline at end of file diff --git a/libs/prism/prism.css b/libs/prism/prism.css new file mode 100644 index 0000000..8fce550 --- /dev/null +++ b/libs/prism/prism.css @@ -0,0 +1 @@ +code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green} \ No newline at end of file diff --git a/libs/prism/prism.js b/libs/prism/prism.js new file mode 100644 index 0000000..294414c --- /dev/null +++ b/libs/prism/prism.js @@ -0,0 +1,18 @@ +window.Prism=window.Prism||{};window.Prism.manual=true; +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(l){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,e={},j={manual:l.Prism&&l.Prism.manual,disableWorkerMessageHandler:l.Prism&&l.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof C?new C(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=i.reach);y+=b.value.length,b=b.next){var v=b.value;if(n.length>t.length)return;if(!(v instanceof C)){var F,x=1;if(m){if(!(F=L(f,y,t,p))||F.index>=t.length)break;var k=F.index,w=F.index+F[0].length,A=y;for(A+=b.value.length;A<=k;)b=b.next,A+=b.value.length;if(A-=b.value.length,y=A,b.value instanceof C)continue;for(var P=b;P!==n.tail&&(Ai.reach&&(i.reach=v),b.prev),S=(S&&(_=z(n,_,S),y+=S.length),O(n,_,x),new C(o,d?j.tokenize($,d):$,h,$));b=z(n,_,S),E&&z(n,b,E),1i.reach&&(i.reach=$.reach))}}}}}(e,r,t,r.head,0),r),i=[],o=s.head.next;o!==s.tail;)i.push(o.value),o=o.next;return i},hooks:{all:{},add:function(e,t){var n=j.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=j.hooks.all[e];if(n&&n.length)for(var a,r=0;a=n[r++];)a(t)}},Token:C};function C(e,t,n,a){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length}function L(e,t,n,a){e.lastIndex=t;t=e.exec(n);return t&&a&&t[1]&&(e=t[1].length,t.index+=e,t[0]=t[0].slice(e)),t}function u(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function z(e,t,n){var a=t.next,n={value:n,prev:t,next:a};return t.next=n,a.prev=n,e.length++,n}function O(e,t,n){for(var a=t.next,r=0;r"+s.content+""},!l.document)return l.addEventListener&&(j.disableWorkerMessageHandler||l.addEventListener("message",function(e){var e=JSON.parse(e.data),t=e.language,n=e.code,e=e.immediateClose;l.postMessage(j.highlight(n,j.languages[t],t)),e&&l.close()},!1)),j;var a,e=j.util.currentScript();function r(){j.manual||j.highlightAll()}return e&&(j.filename=e.src,e.hasAttribute("data-manual")&&(j.manual=!0)),j.manual||("loading"===(a=document.readyState)||"interactive"===a&&e&&e.defer?document.addEventListener("DOMContentLoaded",r):window.requestAnimationFrame?window.requestAnimationFrame(r):window.setTimeout(r,16)),j}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism),Prism.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(e,t){var n={},n=(n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[t]},n.cdata=/^$/i,{"included-cdata":{pattern://i,inside:n}}),t=(n["language-"+t]={pattern:/[\s\S]+/,inside:Prism.languages[t]},{});t[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",t)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(e,t){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:Prism.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml,function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/,t=(e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+t.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css,e.languages.markup);t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism),Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),Prism.languages.js=Prism.languages.javascript,function(){var l,u,g,c,e;void 0!==Prism&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},c="pre[data-src]:not(["+(u="data-src-status")+'="loaded"]):not(['+u+'="'+(g="loading")+'"])',Prism.hooks.add("before-highlightall",function(e){e.selector+=", "+c}),Prism.hooks.add("before-sanity-check",function(e){var r,t,n,a,s,i,o=e.element;o.matches(c)&&(e.code="",o.setAttribute(u,g),(r=o.appendChild(document.createElement("CODE"))).textContent="Loading…",t=o.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(t)||[,"none"])[1],e=l[n]||n),Prism.util.setLanguage(r,e),Prism.util.setLanguage(o,e),(n=Prism.plugins.autoloader)&&n.loadLanguages(e),n=t,a=function(e){o.setAttribute(u,"loaded");var t,n,a=function(e){var t,n;if(e=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||""))return t=Number(e[1]),n=e[2],e=e[3],n?e?[t,Number(e)]:[t,void 0]:[t,t]}(o.getAttribute("data-range"));a&&(t=e.split(/\r\n?|\n/g),n=a[0],a=null==a[1]?t.length:a[1],n<0&&(n+=t.length),n=Math.max(0,Math.min(n-1,t.length)),a<0&&(a+=t.length),a=Math.max(0,Math.min(a,t.length)),e=t.slice(n,a).join("\n"),o.hasAttribute("data-start")||o.setAttribute("data-start",String(n+1))),r.textContent=e,Prism.highlightElement(r)},s=function(e){o.setAttribute(u,"failed"),r.textContent=e},(i=new XMLHttpRequest).open("GET",n,!0),i.onreadystatechange=function(){4==i.readyState&&(i.status<400&&i.responseText?a(i.responseText):400<=i.status?s("✖ Error "+i.status+" while fetching file: "+i.statusText):s("✖ Error: File does not exist or is empty"))},i.send(null))}),e=!(Prism.plugins.fileHighlight={highlight:function(e){for(var t,n=(e||document).querySelectorAll(c),a=0;t=n[a++];)Prism.highlightElement(t)}}),Prism.fileHighlight=function(){e||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),e=!0),Prism.plugins.fileHighlight.highlight.apply(this,arguments)})}(); +Prism.languages.c=Prism.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),Prism.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],char:Prism.languages.c.char,comment:Prism.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}}}),Prism.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete Prism.languages.c.boolean; +;!function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n="\\b(?!)\\w+(?:\\s*\\.\\s*\\w+)*\\b".replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp("(\\b(?:class|concept|enum|struct|typename)\\s+)(?!)\\w+".replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp('(\\b(?:import|module)\\s+)(?:"(?:\\\\(?:\r\n|[^])|[^"\\\\\r\n])*"|<[^<>\r\n]*>|'+"(?:\\s*:\\s*)?|:\\s*".replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(Prism); +;!function(e){var n=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,t="(?:[a-z]\\w*\\s*\\.\\s*)*(?:[A-Z]\\w*\\s*\\.\\s*)*",s={pattern:RegExp("(^|[^\\w.])"+t+"[A-Z](?:[\\d_A-Z]*[a-z]\\w*)?\\b"),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};e.languages.java=e.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[s,{pattern:RegExp("(^|[^\\w.])"+t+"[A-Z]\\w*(?=\\s+\\w+\\s*[;,=()]|\\s*(?:\\[[\\s,]*\\]\\s*)?::\\s*new\\b)"),lookbehind:!0,inside:s.inside},{pattern:RegExp("(\\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\\s+)"+t+"[A-Z]\\w*\\b"),lookbehind:!0,inside:s.inside}],keyword:n,function:[e.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),e.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":s,keyword:n,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp("(\\bimport\\s+)"+t+"(?:[A-Z]\\w*|\\*)(?=\\s*;)"),lookbehind:!0,inside:{namespace:s.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp("(\\bimport\\s+static\\s+)"+t+"(?:\\w+|\\*)(?=\\s*;)"),lookbehind:!0,alias:"static",inside:{namespace:s.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp("(\\b(?:exports|import(?:\\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\\s+)(?!)[a-z]\\w*(?:\\.[a-z]\\w*)*\\.?".replace(//g,(function(){return n.source}))),lookbehind:!0,inside:{punctuation:/\./}}})}(Prism); +;!function(e){function n(e,n){return e.replace(/<<(\d+)>>/g,(function(e,s){return"(?:"+n[+s]+")"}))}function s(e,s,a){return RegExp(n(e,s),a||"")}function a(e,n){for(var s=0;s>/g,(function(){return"(?:"+e+")"}));return e.replace(/<>/g,"[^\\s\\S]")}var t="bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void",r="class enum interface record struct",i="add alias and ascending async await by descending from(?=\\s*(?:\\w|$)) get global group into init(?=\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\s*{)",o="abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield";function l(e){return"\\b(?:"+e.trim().replace(/ /g,"|")+")\\b"}var d=l(r),p=RegExp(l(t+" "+r+" "+i+" "+o)),c=l(r+" "+i+" "+o),u=l(t+" "+r+" "+o),g=a("<(?:[^<>;=+\\-*/%&|^]|<>)*>",2),b=a("\\((?:[^()]|<>)*\\)",2),h="@?\\b[A-Za-z_]\\w*\\b",f=n("<<0>>(?:\\s*<<1>>)?",[h,g]),m=n("(?!<<0>>)<<1>>(?:\\s*\\.\\s*<<1>>)*",[c,f]),k="\\[\\s*(?:,\\s*)*\\]",y=n("<<0>>(?:\\s*(?:\\?\\s*)?<<1>>)*(?:\\s*\\?)?",[m,k]),w=n("[^,()<>[\\];=+\\-*/%&|^]|<<0>>|<<1>>|<<2>>",[g,b,k]),v=n("\\(<<0>>+(?:,<<0>>+)+\\)",[w]),x=n("(?:<<0>>|<<1>>)(?:\\s*(?:\\?\\s*)?<<2>>)*(?:\\s*\\?)?",[v,m,k]),$={keyword:p,punctuation:/[<>()?,.:[\]]/},_="'(?:[^\r\n'\\\\]|\\\\.|\\\\[Uux][\\da-fA-F]{1,8})'",B='"(?:\\\\.|[^\\\\"\r\n])*"';e.languages.csharp=e.languages.extend("clike",{string:[{pattern:s("(^|[^$\\\\])<<0>>",['@"(?:""|\\\\[^]|[^\\\\"])*"(?!")']),lookbehind:!0,greedy:!0},{pattern:s("(^|[^@$\\\\])<<0>>",[B]),lookbehind:!0,greedy:!0}],"class-name":[{pattern:s("(\\busing\\s+static\\s+)<<0>>(?=\\s*;)",[m]),lookbehind:!0,inside:$},{pattern:s("(\\busing\\s+<<0>>\\s*=\\s*)<<1>>(?=\\s*;)",[h,x]),lookbehind:!0,inside:$},{pattern:s("(\\busing\\s+)<<0>>(?=\\s*=)",[h]),lookbehind:!0},{pattern:s("(\\b<<0>>\\s+)<<1>>",[d,f]),lookbehind:!0,inside:$},{pattern:s("(\\bcatch\\s*\\(\\s*)<<0>>",[m]),lookbehind:!0,inside:$},{pattern:s("(\\bwhere\\s+)<<0>>",[h]),lookbehind:!0},{pattern:s("(\\b(?:is(?:\\s+not)?|as)\\s+)<<0>>",[y]),lookbehind:!0,inside:$},{pattern:s("\\b<<0>>(?=\\s+(?!<<1>>|with\\s*\\{)<<2>>(?:\\s*[=,;:{)\\]]|\\s+(?:in|when)\\b))",[x,u,h]),inside:$}],keyword:p,number:/(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i,operator:/>>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,punctuation:/\?\.?|::|[{}[\];(),.:]/}),e.languages.insertBefore("csharp","number",{range:{pattern:/\.\./,alias:"operator"}}),e.languages.insertBefore("csharp","punctuation",{"named-parameter":{pattern:s("([(,]\\s*)<<0>>(?=\\s*:)",[h]),lookbehind:!0,alias:"punctuation"}}),e.languages.insertBefore("csharp","class-name",{namespace:{pattern:s("(\\b(?:namespace|using)\\s+)<<0>>(?:\\s*\\.\\s*<<0>>)*(?=\\s*[;{])",[h]),lookbehind:!0,inside:{punctuation:/\./}},"type-expression":{pattern:s("(\\b(?:default|sizeof|typeof)\\s*\\(\\s*(?!\\s))(?:[^()\\s]|\\s(?!\\s)|<<0>>)*(?=\\s*\\))",[b]),lookbehind:!0,alias:"class-name",inside:$},"return-type":{pattern:s("<<0>>(?=\\s+(?:<<1>>\\s*(?:=>|[({]|\\.\\s*this\\s*\\[)|this\\s*\\[))",[x,m]),inside:$,alias:"class-name"},"constructor-invocation":{pattern:s("(\\bnew\\s+)<<0>>(?=\\s*[[({])",[x]),lookbehind:!0,inside:$,alias:"class-name"},"generic-method":{pattern:s("<<0>>\\s*<<1>>(?=\\s*\\()",[h,g]),inside:{function:s("^<<0>>",[h]),generic:{pattern:RegExp(g),alias:"class-name",inside:$}}},"type-list":{pattern:s("\\b((?:<<0>>\\s+<<1>>|record\\s+<<1>>\\s*<<5>>|where\\s+<<2>>)\\s*:\\s*)(?:<<3>>|<<4>>|<<1>>\\s*<<5>>|<<6>>)(?:\\s*,\\s*(?:<<3>>|<<4>>|<<6>>))*(?=\\s*(?:where|[{;]|=>|$))",[d,f,h,x,p.source,b,"\\bnew\\s*\\(\\s*\\)"]),lookbehind:!0,inside:{"record-arguments":{pattern:s("(^(?!new\\s*\\()<<0>>\\s*)<<1>>",[f,b]),lookbehind:!0,greedy:!0,inside:e.languages.csharp},keyword:p,"class-name":{pattern:RegExp(x),greedy:!0,inside:$},punctuation:/[,()]/}},preprocessor:{pattern:/(^[\t ]*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/,lookbehind:!0,alias:"keyword"}}}});var E=B+"|"+_,R=n("/(?![*/])|//[^\r\n]*[\r\n]|/\\*(?:[^*]|\\*(?!/))*\\*/|<<0>>",[E]),z=a(n("[^\"'/()]|<<0>>|\\(<>*\\)",[R]),2),S="\\b(?:assembly|event|field|method|module|param|property|return|type)\\b",j=n("<<0>>(?:\\s*\\(<<1>>*\\))?",[m,z]);e.languages.insertBefore("csharp","class-name",{attribute:{pattern:s("((?:^|[^\\s\\w>)?])\\s*\\[\\s*)(?:<<0>>\\s*:\\s*)?<<1>>(?:\\s*,\\s*<<1>>)*(?=\\s*\\])",[S,j]),lookbehind:!0,greedy:!0,inside:{target:{pattern:s("^<<0>>(?=\\s*:)",[S]),alias:"keyword"},"attribute-arguments":{pattern:s("\\(<<0>>*\\)",[z]),inside:e.languages.csharp},"class-name":{pattern:RegExp(m),inside:{punctuation:/\./}},punctuation:/[:,]/}}});var A=":[^}\r\n]+",F=a(n("[^\"'/()]|<<0>>|\\(<>*\\)",[R]),2),P=n("\\{(?!\\{)(?:(?![}:])<<0>>)*<<1>>?\\}",[F,A]),U=a(n("[^\"'/()]|/(?!\\*)|/\\*(?:[^*]|\\*(?!/))*\\*/|<<0>>|\\(<>*\\)",[E]),2),Z=n("\\{(?!\\{)(?:(?![}:])<<0>>)*<<1>>?\\}",[U,A]);function q(n,a){return{interpolation:{pattern:s("((?:^|[^{])(?:\\{\\{)*)<<0>>",[n]),lookbehind:!0,inside:{"format-string":{pattern:s("(^\\{(?:(?![}:])<<0>>)*)<<1>>(?=\\}$)",[a,A]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-csharp",inside:e.languages.csharp}}},string:/[\s\S]+/}}e.languages.insertBefore("csharp","string",{"interpolation-string":[{pattern:s('(^|[^\\\\])(?:\\$@|@\\$)"(?:""|\\\\[^]|\\{\\{|<<0>>|[^\\\\{"])*"',[P]),lookbehind:!0,greedy:!0,inside:q(P,F)},{pattern:s('(^|[^@\\\\])\\$"(?:\\\\.|\\{\\{|<<0>>|[^\\\\"{])*"',[Z]),lookbehind:!0,greedy:!0,inside:q(Z,U)}],char:{pattern:RegExp(_),greedy:!0}}),e.languages.dotnet=e.languages.cs=e.languages.csharp}(Prism); +;Prism.languages.go=Prism.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),Prism.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete Prism.languages.go["class-name"]; +;!function(e){for(var a="/\\*(?:[^*/]|\\*(?!/)|/(?!\\*)|)*\\*/",t=0;t<2;t++)a=a.replace(//g,(function(){return a}));a=a.replace(//g,(function(){return"[^\\s\\S]"})),e.languages.rust={comment:[{pattern:RegExp("(^|[^\\\\])"+a),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|trait|type|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(Prism); +;Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python; +;!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,t="(?:"+r.source+"(?:[ \t]+"+n.source+")?|"+n.source+"(?:[ \t]+"+r.source+")?)",a="(?:[^\\s\\x00-\\x08\\x0e-\\x1f!\"#%&'*,\\-:>?@[\\]`{|}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*".replace(//g,(function(){return"[^\\s\\x00-\\x08\\x0e-\\x1f,[\\]{}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]"})),d="\"(?:[^\"\\\\\r\n]|\\\\.)*\"|'(?:[^'\\\\\r\n]|\\\\.)*'";function o(e,n){n=(n||"").replace(/m/g,"")+"m";var r="([:\\-,[{]\\s*(?:\\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\\]|\\}|(?:[\r\n]\\s*)?#))".replace(/<>/g,(function(){return t})).replace(/<>/g,(function(){return e}));return RegExp(r,n)}e.languages.yaml={scalar:{pattern:RegExp("([\\-:]\\s*(?:\\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\\S[^\r\n]*(?:\\2[^\r\n]+)*)".replace(/<>/g,(function(){return t}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp("((?:^|[:\\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\\s*:\\s)".replace(/<>/g,(function(){return t})).replace(/<>/g,(function(){return"(?:"+a+"|"+d+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:o("\\d{4}-\\d\\d?-\\d\\d?(?:[tT]|[ \t]+)\\d\\d?:\\d{2}:\\d{2}(?:\\.\\d*)?(?:[ \t]*(?:Z|[-+]\\d\\d?(?::\\d{2})?))?|\\d{4}-\\d{2}-\\d{2}|\\d\\d?:\\d{2}(?::\\d{2}(?:\\.\\d*)?)?"),lookbehind:!0,alias:"number"},boolean:{pattern:o("false|true","i"),lookbehind:!0,alias:"important"},null:{pattern:o("null|~","i"),lookbehind:!0,alias:"important"},string:{pattern:o(d),lookbehind:!0,greedy:!0},number:{pattern:o("[+-]?(?:0x[\\da-f]+|0o[0-7]+|(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?|\\.inf|\\.nan)","i"),lookbehind:!0},tag:r,important:n,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(Prism); +;Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/}; +;!function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var s=e.languages.extend("typescript",{});delete s["class-name"],e.languages.typescript["class-name"].inside=s,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism); +;!function(t){var n=t.util.clone(t.languages.javascript),e="(?:\\{*\\.{3}(?:[^{}]|)*\\})";function a(t,n){return t=t.replace(//g,(function(){return"(?:\\s|//.*(?!.)|/\\*(?:[^*]|\\*(?!/))\\*/)"})).replace(//g,(function(){return"(?:\\{(?:\\{(?:\\{[^{}]*\\}|[^{}])*\\}|[^{}])*\\})"})).replace(//g,(function(){return e})),RegExp(t,n)}e=a(e).source,t.languages.jsx=t.languages.extend("markup",n),t.languages.jsx.tag.pattern=a("+(?:[\\w.:$-]+(?:=(?:\"(?:\\\\[^]|[^\\\\\"])*\"|'(?:\\\\[^]|[^\\\\'])*'|[^\\s{'\"/>=]+|))?|))**/?)?>"),t.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,t.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,t.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,t.languages.jsx.tag.inside.comment=n.comment,t.languages.insertBefore("inside","attr-name",{spread:{pattern:a(""),inside:t.languages.jsx}},t.languages.jsx.tag),t.languages.insertBefore("inside","special-attr",{script:{pattern:a("="),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:t.languages.jsx}}},t.languages.jsx.tag);var s=function(t){return t?"string"==typeof t?t:"string"==typeof t.content?t.content:t.content.map(s).join(""):""},g=function(n){for(var e=[],a=0;a0&&e[e.length-1].tagName===s(o.content[0].content[1])&&e.pop():"/>"===o.content[o.content.length-1].content||e.push({tagName:s(o.content[0].content[1]),openedBraces:0}):e.length>0&&"punctuation"===o.type&&"{"===o.content?e[e.length-1].openedBraces++:e.length>0&&e[e.length-1].openedBraces>0&&"punctuation"===o.type&&"}"===o.content?e[e.length-1].openedBraces--:i=!0),(i||"string"==typeof o)&&e.length>0&&0===e[e.length-1].openedBraces){var r=s(o);a0&&("string"==typeof n[a-1]||"plain-text"===n[a-1].type)&&(r=s(n[a-1])+r,n.splice(a-1,1),a--),n[a]=new t.Token("plain-text",r,null,r)}o.content&&"string"!=typeof o.content&&g(o.content)}};t.hooks.add("after-tokenize",(function(t){"jsx"!==t.language&&"tsx"!==t.language||g(t.tokens)}))}(Prism); +;!function(e){var a=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",a),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var t=e.languages.tsx.tag;t.pattern=RegExp("(^|[^\\w$]|(?=/g,(function(){return"(?:\\\\.|[^\\\\\n\r]|(?:\n|\r\n?)(?![\r\n]))"})),RegExp("((?:^|[^\\\\])(?:\\\\{2})*)(?:"+n+")")}var t="(?:\\\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\\\|\r\n`])+",a="\\|?__(?:\\|__)+\\|?(?:(?:\n|\r\n?)|(?![^]))".replace(/__/g,(function(){return t})),i="\\|?[ \t]*:?-{3,}:?[ \t]*(?:\\|[ \t]*:?-{3,}:?[ \t]*)+\\|?(?:\n|\r\n?)";n.languages.markdown=n.languages.extend("markup",{}),n.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:n.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+i+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+i+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(t),inside:n.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+i+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(t),alias:"important",inside:n.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:e("\\b__(?:(?!_)|_(?:(?!_))+_)+__\\b|\\*\\*(?:(?!\\*)|\\*(?:(?!\\*))+\\*)+\\*\\*"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:e("\\b_(?:(?!_)|__(?:(?!_))+__)+_\\b|\\*(?:(?!\\*)|\\*\\*(?:(?!\\*))+\\*\\*)+\\*"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:e("(~~?)(?:(?!~))+\\2"),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:e('!?\\[(?:(?!\\]))+\\](?:\\([^\\s)]+(?:[\t ]+"(?:\\\\.|[^"\\\\])*")?\\)|[ \t]?\\[(?:(?!\\]))+\\])'),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(e){["url","bold","italic","strike","code-snippet"].forEach((function(t){e!==t&&(n.languages.markdown[e].inside.content.inside[t]=n.languages.markdown[t])}))})),n.hooks.add("after-tokenize",(function(n){"markdown"!==n.language&&"md"!==n.language||function n(e){if(e&&"string"!=typeof e)for(var t=0,a=e.length;t",quot:'"'},l=String.fromCodePoint||String.fromCharCode;n.languages.md=n.languages.markdown}(Prism); +; \ No newline at end of file diff --git a/manifest.json b/manifest.json index e728458..e3f7471 100644 --- a/manifest.json +++ b/manifest.json @@ -1,5 +1,5 @@ { - "name": "SecureBit.chat v4.9.1 - ECDH + DTLS + SAS", + "name": "SecureBit.chat — Private, Encrypted Messenger", "short_name": "SecureBit", "description": "P2P messenger with ECDH + DTLS + SAS security, military-grade cryptography and Lightning Network payments", "start_url": "./", diff --git a/meta.json b/meta.json index 89dbd7d..e3fce28 100644 --- a/meta.json +++ b/meta.json @@ -1,10 +1,10 @@ { - "version": "1782332255248", - "buildVersion": "1782332255248", - "appVersion": "4.9.1", - "buildTime": "2026-06-24T20:17:35.295Z", - "buildId": "1782332255248-ef2f13d", - "gitHash": "ef2f13d", + "version": "1782446255208", + "buildVersion": "1782446255208", + "appVersion": "4.10.0", + "buildTime": "2026-06-26T03:57:35.595Z", + "buildId": "1782446255208-db5d6e4", + "gitHash": "db5d6e4", "generated": true, - "generatedAt": "2026-06-24T20:17:35.303Z" + "generatedAt": "2026-06-26T03:57:35.602Z" } \ No newline at end of file diff --git a/package.json b/package.json index acbc000..a8b144f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "securebit-chat", - "version": "4.9.1", + "version": "4.10.0", "description": "Secure P2P Communication Application with End-to-End Encryption", "main": "index.html", "scripts": { @@ -11,7 +11,7 @@ "dev": "npm run build && python -m http.server 8000", "watch": "npx tailwindcss -i src/styles/tw-input.css -o assets/tailwind.css --watch", "serve": "npx http-server -p 8000", - "test": "node tests/sas-verification.test.mjs && node tests/file-transfer-consent.test.mjs && node tests/incoming-message-sanitization.test.mjs && node tests/outgoing-message-integrity.test.mjs && node tests/secure-chat-features.test.mjs && node tests/notification-meta-forwarding.test.mjs && node tests/file-type-allowlist.test.mjs && node tests/webrtc-privacy-mode.test.mjs && node tests/indexeddb-metadata-encryption.test.mjs && node tests/disconnect-cleanup.test.mjs && node tests/timer-lifecycle.test.mjs && node tests/file-transfer-cleanup.test.mjs && node tests/file-transfer-ui-cleanup.test.mjs && node tests/file-transfer-callback-propagation.test.mjs && node tests/debug-window-hooks.test.mjs && node tests/inbound-message-rate-limit.test.mjs && node tests/file-transfer-chunk-rate-limit.test.mjs && node tests/ice-servers-validation.test.mjs" + "test": "node tests/sas-verification.test.mjs && node tests/file-transfer-consent.test.mjs && node tests/incoming-message-sanitization.test.mjs && node tests/outgoing-message-integrity.test.mjs && node tests/secure-chat-features.test.mjs && node tests/notification-meta-forwarding.test.mjs && node tests/file-type-allowlist.test.mjs && node tests/webrtc-privacy-mode.test.mjs && node tests/indexeddb-metadata-encryption.test.mjs && node tests/disconnect-cleanup.test.mjs && node tests/timer-lifecycle.test.mjs && node tests/file-transfer-cleanup.test.mjs && node tests/file-transfer-ui-cleanup.test.mjs && node tests/file-transfer-callback-propagation.test.mjs && node tests/debug-window-hooks.test.mjs && node tests/inbound-message-rate-limit.test.mjs && node tests/file-transfer-chunk-rate-limit.test.mjs && node tests/ice-servers-validation.test.mjs && node tests/sessions-reducer.test.mjs" }, "keywords": [ "p2p", diff --git a/scripts/post-build.js b/scripts/post-build.js index deb4183..79cb280 100644 --- a/scripts/post-build.js +++ b/scripts/post-build.js @@ -141,12 +141,37 @@ function updateIndexHtmlVersions(buildVersion) { fs.writeFileSync(indexHtmlPath, indexHtml, 'utf-8'); console.log('✅ index.html versions updated'); - + } catch (error) { console.warn('⚠️ Failed to update index.html versions:', error.message); } } +/** + * Stamp the build version into sw.js so the file's bytes change every deploy. The browser + * only reinstalls a Service Worker when sw.js differs byte-for-byte; without this the SW + * (and its caches) stay frozen and clients never pick up new releases automatically. + */ +function updateServiceWorkerVersion(buildVersion) { + try { + const swPath = path.join(CONFIG.publicDir, 'sw.js'); + if (!fs.existsSync(swPath)) { + console.warn('⚠️ sw.js not found, skipping SW version stamp'); + return; + } + let sw = fs.readFileSync(swPath, 'utf-8'); + if (/const SW_BUILD_VERSION = '[^']*';/.test(sw)) { + sw = sw.replace(/const SW_BUILD_VERSION = '[^']*';/, `const SW_BUILD_VERSION = '${buildVersion}';`); + fs.writeFileSync(swPath, sw, 'utf-8'); + console.log(`✅ sw.js build stamp updated → ${buildVersion}`); + } else { + console.warn('⚠️ SW_BUILD_VERSION marker not found in sw.js'); + } + } catch (error) { + console.warn('⚠️ Failed to stamp sw.js version:', error.message); + } +} + /** * Validate generated meta.json */ @@ -184,7 +209,10 @@ function main() { // Update versions in index.html updateIndexHtmlVersions(meta.version); - + + // Stamp the build version into sw.js so the Service Worker reinstalls each deploy. + updateServiceWorkerVersion(meta.version); + console.log('✅ Build metadata generation completed'); } diff --git a/src/app.jsx b/src/app.jsx index 637cd28..0f5a4b0 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -1,5 +1,17 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js'; import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/iceSettingsStore.js'; +import { + sessionsReducer, + createInitialState, + createSessionEntry, + decorateSessions, + monoInitials, + statusDot, + MY_STATUS_OPTIONS, + PRESENCE_DOT, + PRESENCE_WORD, + SESSION_ACTIONS as SA +} from './state/sessionsStore.js'; // ── Secure chat extras: code blocks, clipboard hygiene ────────────── // Copy text to the clipboard and (optionally) wipe it after a delay so @@ -102,7 +114,23 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic return nodes; }; + // Fenced-language → Prism grammar id (with common aliases). + const PRISM_ALIAS = { + js: 'javascript', mjs: 'javascript', javascript: 'javascript', node: 'javascript', + ts: 'typescript', typescript: 'typescript', jsx: 'jsx', tsx: 'tsx', + py: 'python', python: 'python', sh: 'bash', shell: 'bash', zsh: 'bash', bash: 'bash', + 'c++': 'cpp', cpp: 'cpp', cc: 'cpp', cxx: 'cpp', c: 'c', h: 'c', + cs: 'csharp', csharp: 'csharp', java: 'java', go: 'go', golang: 'go', + rs: 'rust', rust: 'rust', json: 'json', yml: 'yaml', yaml: 'yaml', sql: 'sql', + md: 'markdown', markdown: 'markdown', html: 'markup', xml: 'markup', svg: 'markup', css: 'css' + }; + // Monospace code window with a copy button (clipboard auto-clears in 30s). + // Syntax highlighting is done by Prism when available. SECURITY: Prism only + // TOKENIZES the snippet — it never executes it. Prism.highlight() HTML-escapes + // the input before tokenizing, so the highlighted markup contains no live code + // and can't inject scripts (CSP also blocks inline execution). When Prism or the + // grammar is missing we fall back to the dependency-free React-node highlighter. const CodeBlock = ({ code, lang }) => { const [copied, setCopied] = React.useState(false); const handleCopy = async () => { @@ -112,6 +140,20 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic setTimeout(() => setCopied(false), 2000); } }; + const norm = PRISM_ALIAS[(lang || '').toLowerCase()] || (lang || '').toLowerCase(); + const prism = (typeof window !== 'undefined') ? window.Prism : null; + const grammar = (prism && prism.languages) ? prism.languages[norm] : null; + const usePrism = !!(prism && grammar && typeof prism.highlight === 'function'); + let highlightedHtml = null; + if (usePrism) { + try { highlightedHtml = prism.highlight(code, grammar, norm); } catch (_) { highlightedHtml = null; } + } + const displayLang = (usePrism ? norm : (lang || 'code')); + const codeEl = (usePrism && highlightedHtml != null) + // Prism-escaped highlight markup — safe to inject (no live code). + ? React.createElement('code', { className: 'language-' + norm, dangerouslySetInnerHTML: { __html: highlightedHtml } }) + // Fallback: React text nodes, never HTML. + : React.createElement('code', null, highlightCode(code)); return React.createElement('div', { className: "my-1 rounded-lg overflow-hidden", style: { backgroundColor: '#1b1c1b', border: '0 solid #e5e7eb' } @@ -124,7 +166,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic React.createElement('span', { key: 'lang', className: "text-[11px] uppercase tracking-wide text-gray-500 font-mono" - }, lang || 'code'), + }, displayLang), React.createElement('button', { key: 'copy', onClick: handleCopy, @@ -142,7 +184,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic key: 'pre', className: "px-3 py-2 overflow-x-auto text-xs leading-relaxed text-gray-200 custom-scrollbar", style: { whiteSpace: 'pre', fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', margin: 0 } - }, React.createElement('code', null, highlightCode(code))) + }, codeEl) ]); }; @@ -584,7 +626,10 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic const stCfg = ({ sending: { icon: 'fa-clock', color: '#6b6b73', label: 'Sending' }, sent: { icon: 'fa-check', color: '#8a8a92', label: 'Sent' }, - delivered: { icon: 'fa-check-double', color: '#3ecf8e', label: 'Delivered' }, + // Two GREY ticks = delivered to the peer's device but not yet read. + delivered: { icon: 'fa-check-double', color: '#8a8a92', label: 'Delivered' }, + // Two GREEN ticks = the peer actually opened the chat and read it. + read: { icon: 'fa-check-double', color: '#3ecf8e', label: 'Read' }, failed: { icon: 'fa-triangle-exclamation', color: '#e5727a', label: 'Not sent' } })[status || 'sent'] || { icon: 'fa-check', color: '#8a8a92', label: 'Sent' }; metaLeft.push(React.createElement('span', { @@ -687,7 +732,10 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic iceSettingsPersisted, customIceServers, handleApplyIceSettings, - handleForgetIceSettings + handleForgetIceSettings, + // When true, render ONLY the create/connect card (no marketing landing, + // no hero) so it slots into the chat column for an additional session. + compact = false }) => { const [mode, setMode] = React.useState('create'); const [notificationPermissionRequested, setNotificationPermissionRequested] = React.useState(false); @@ -1242,7 +1290,9 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic }) : null; - const rightPanel = h('div', { key: 'right', style: { flex: '0.95 1 460px', minWidth: 'min(100%, 320px)', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', height: '100vh' } }, [ + const rightPanel = h('div', { key: 'right', style: compact + ? { flex: 1, minWidth: 0, width: '100%', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', height: '100%' } + : { flex: '0.95 1 460px', minWidth: 'min(100%, 320px)', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column', height: '100vh' } }, [ h('div', { key: 'scroll', className: 'custom-scrollbar', style: { flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', padding: '42px 44px' } }, h('div', { style: { maxWidth: '430px', width: '100%', margin: 'auto' } }, [ h('div', { key: 'kicker', style: { fontFamily: MONO, fontSize: '11px', fontWeight: 600, color: '#6b6b73', textTransform: 'uppercase', letterSpacing: '1px', marginBottom: '10px' } }, kicker), @@ -1304,6 +1354,12 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic '@keyframes sbBlink{0%,100%{opacity:1}50%{opacity:.35}}' } }); + // Embedded in the chat column (additional session): just the create/connect + // card filling the area — no hero, no marketing landing. + if (compact) { + return h('div', { className: 'sb-start', style: { flex: 1, minHeight: 0, width: '100%', display: 'flex', flexDirection: 'column', background: '#0f0f11', color: '#e8e8eb' } }, [keyframeStyle, rightPanel, qrModal]); + } + return h('div', { className: 'sb-start', style: { width: '100%' } }, [keyframeStyle, hero, uniqueSection, partnersSection, roadmapSection, communitySection, qrModal]); }; @@ -1455,10 +1511,12 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic // In-chat header matching the SecureBit Chat design: logo + version, // a "Secure" pill (click = run the security verification report; the chevron // toggles the network/crypto detail panel), a connection indicator, Disconnect. - const SecureBitChatHeader = ({ status, onDisconnect, webrtcManager }) => { + const SecureBitChatHeader = ({ status, onDisconnect, webrtcManager, title, isOffline, peerPresence, onRenameTitle }) => { const MONO = "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace"; const [showNetwork, setShowNetwork] = React.useState(false); const [sec, setSec] = React.useState(null); + const [editingName, setEditingName] = React.useState(false); + const [nameDraft, setNameDraft] = React.useState(''); React.useEffect(() => { let alive = true; @@ -1479,7 +1537,25 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic return () => { alive = false; clearInterval(iv); document.removeEventListener('real-security-calculated', onCalc); }; }, [webrtcManager]); - const connected = status === 'connected' || status === 'verified'; + // Offline (our device lost connectivity) takes precedence over the P2P state — + // the data channel can stay "open" after the network drops, so without this the + // header would keep saying "Connected" while the user is actually offline. + const onlineConnected = status === 'connected' || status === 'verified'; + const dropped = status === 'disconnected' || status === 'peer_disconnected'; + const connected = onlineConnected && !isOffline; + const connDot = (isOffline || dropped) ? '#e5727a' : (onlineConnected ? '#3ecf8e' : '#e3c84e'); + const connLabel = isOffline + ? 'Offline' + : (onlineConnected ? 'Connected' + : (status === 'peer_disconnected' ? 'Peer disconnected' + : (status === 'disconnected' ? 'Disconnected' : 'Connecting…'))); + const connGlow = (isOffline || dropped) ? '0 0 0 3px rgba(229,114,122,0.16)' : (onlineConnected ? '0 0 0 3px rgba(62,207,142,0.16)' : '0 0 0 3px rgba(227,200,78,0.16)'); + // The avatar dot + subtitle reflect the PEER's advertised availability while connected. + const peerDot = (onlineConnected && !isOffline) ? (PRESENCE_DOT[peerPresence] || '#3ecf8e') : connDot; + const peerPresenceWord = (onlineConnected && !isOffline && peerPresence) ? (PRESENCE_WORD[peerPresence] || null) : null; + const startRename = () => { setNameDraft(title || ''); setEditingName(true); }; + const commitRename = () => { if (typeof onRenameTitle === 'function') onRenameTitle(nameDraft); setEditingName(false); }; + const renameKey = (e) => { if (e.key === 'Enter') { e.preventDefault(); commitRename(); } else if (e.key === 'Escape') { setEditingName(false); } }; const passed = sec && Number.isFinite(sec.passedChecks) ? sec.passedChecks : null; const total = sec && Number.isFinite(sec.totalChecks) ? sec.totalChecks : null; const scoreLabel = (passed != null && total) ? (passed + '/' + total) : (sec ? (sec.score + '%') : '—'); @@ -1494,9 +1570,9 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic style: { display: 'flex', alignItems: 'center', gap: '9px', padding: '7px 13px', borderRadius: '9px', border: '1px solid ' + (showNetwork ? 'rgba(255,255,255,0.16)' : 'rgba(255,255,255,0.07)'), background: showNetwork ? 'rgba(255,255,255,0.05)' : 'rgba(255,255,255,0.02)', cursor: 'pointer', fontFamily: 'inherit', transition: 'all .15s' } }, [ React.createElement('i', { key: 'i', className: 'fas fa-shield-halved', style: { color: accent, fontSize: '13px' } }), - React.createElement('span', { key: 'l', style: { fontSize: '13px', fontWeight: 600, color: '#e8e8eb' } }, sec ? (sec.level || 'Secure') : 'Secure'), - React.createElement('span', { key: 'd', style: { width: '1px', height: '13px', background: 'rgba(255,255,255,0.12)' } }), - React.createElement('span', { key: 's', style: { fontFamily: MONO, fontSize: '11.5px', fontWeight: 500, color: '#8a8a92' } }, scoreLabel), + React.createElement('span', { key: 'l', className: 'sb-sec-label', style: { fontSize: '13px', fontWeight: 600, color: '#e8e8eb' } }, sec ? (sec.level || 'Secure') : 'Secure'), + React.createElement('span', { key: 'd', className: 'sb-sec-div', style: { width: '1px', height: '13px', background: 'rgba(255,255,255,0.12)' } }), + React.createElement('span', { key: 's', className: 'sb-sec-score', style: { fontFamily: MONO, fontSize: '11.5px', fontWeight: 500, color: '#8a8a92' } }, scoreLabel), React.createElement('button', { key: 'c', type: 'button', title: 'Network & crypto details', onClick: (e) => { e.stopPropagation(); setShowNetwork(v => !v); }, @@ -1504,26 +1580,51 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic }, React.createElement('i', { className: 'fas fa-chevron-down', style: { color: '#6b6b73', fontSize: '11px', transform: showNetwork ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } })) ]); + const headerResponsiveCss = React.createElement('style', { key: 'hdr-css', dangerouslySetInnerHTML: { __html: + // Mobile: leave room for the drawer hamburger and shed non-essential header + // chrome so avatar + name + status + Disconnect fit a narrow screen. + '@media (max-width:768px){' + + '.sb-chat-header{padding-left:60px !important;gap:10px !important;}' + + '.sb-chat-header .sb-sec-score,.sb-chat-header .sb-sec-label,.sb-chat-header .sb-sec-div{display:none !important;}' + + '.sb-chat-header .sb-secpill{padding:8px !important;gap:6px !important;}' + + '.sb-chat-header .sb-conn-text{display:none !important;}' + + '.sb-chat-header .sb-conn{padding:9px !important;}' + + '.sb-chat-header .sb-hdr-sub{display:none !important;}' + + '}' + + '@media (max-width:480px){.sb-chat-header{padding-right:12px !important;gap:8px !important;}}' + } }); const header = React.createElement('header', { - key: 'hdr', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '24px', padding: '0 20px', height: '64px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(18,18,20,0.72)', backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)' } + key: 'hdr', className: 'sb-chat-header', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '24px', padding: '0 20px', height: '64px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(18,18,20,0.72)', backdropFilter: 'blur(14px)', WebkitBackdropFilter: 'blur(14px)' } }, [ + headerResponsiveCss, + // The SecureBit brand/logo lives in the left rail; this header identifies the + // ACTIVE conversation — avatar monogram + local label + connection status. React.createElement('div', { key: 'left', style: { display: 'flex', alignItems: 'center', gap: '12px', minWidth: 0 } }, [ - React.createElement('div', { key: 'logo', style: { width: '36px', height: '36px', flex: 'none', display: 'grid', placeItems: 'center' } }, - React.createElement('img', { src: '/logo/securebit-mark.svg', alt: 'SecureBit', style: { width: '100%', height: '100%', objectFit: 'contain', display: 'block' } }) - ), - React.createElement('div', { key: 'txt', style: { lineHeight: 1.2, minWidth: 0 } }, [ - React.createElement('div', { key: 'r1', style: { display: 'flex', alignItems: 'baseline', gap: '7px' } }, [ - React.createElement('span', { key: 'n', style: { fontSize: '16px', fontWeight: 800, letterSpacing: '-0.3px', color: '#e8e8eb' } }, 'SecureBit'), - React.createElement('span', { key: 'v', style: { fontFamily: MONO, fontSize: '10px', fontWeight: 500, color: '#56565e' } }, 'v4.9.0') - ]), - React.createElement('div', { key: 'r2', style: { fontSize: '11px', color: '#6b6b73', fontWeight: 500 } }, 'End-to-end encrypted') - ]) + React.createElement('div', { key: 'avatar', style: { position: 'relative', flex: 'none', width: '36px', height: '36px', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.09)', fontSize: '13px', fontWeight: 700, letterSpacing: '-0.3px', color: '#e8e8eb' } }, [ + monoInitials(title || 'Chat'), + React.createElement('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: peerDot, border: '2px solid #121214' } }) + ]), + editingName + ? React.createElement('div', { key: 'edit', style: { display: 'flex', flexDirection: 'column', gap: '4px', minWidth: 0 } }, [ + React.createElement('div', { key: 'row', style: { display: 'flex', alignItems: 'center', gap: '6px' } }, [ + React.createElement('input', { key: 'in', autoFocus: true, value: nameDraft, maxLength: 32, placeholder: 'Name this chat', onChange: (e) => setNameDraft(e.target.value), onKeyDown: renameKey, onBlur: commitRename, style: { width: '210px', padding: '5px 10px', borderRadius: '8px', border: '1px solid rgba(240,137,42,0.55)', background: '#0f0f11', color: '#f4f4f6', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, outline: 'none' } }), + React.createElement('button', { key: 'ok', onMouseDown: (e) => e.preventDefault(), onClick: commitRename, title: 'Save', style: { flex: 'none', width: '28px', height: '28px', borderRadius: '8px', display: 'grid', placeItems: 'center', border: 'none', background: '#f0892a', color: '#1a0f04', cursor: 'pointer' } }, React.createElement('i', { className: 'fas fa-check', style: { fontSize: '12px' } })) + ]), + React.createElement('div', { key: 'hint', style: { fontSize: '11px', color: '#56565e' } }, 'Local label · stored only on this device') + ]) + : React.createElement('div', { key: 'txt', style: { lineHeight: 1.2, minWidth: 0 } }, [ + React.createElement('div', { key: 'r1', style: { display: 'flex', alignItems: 'center', gap: '7px' } }, [ + React.createElement('span', { key: 'n', style: { fontSize: '15px', fontWeight: 800, letterSpacing: '-0.3px', color: '#f4f4f6', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, title || 'Secure chat'), + React.createElement('button', { key: 'edit', onClick: startRename, title: 'Rename chat (local only)', style: { flex: 'none', width: '24px', height: '24px', borderRadius: '7px', display: 'grid', placeItems: 'center', border: 'none', background: 'transparent', color: '#56565e', cursor: 'pointer' } }, React.createElement('i', { className: 'fas fa-pen', style: { fontSize: '11px' } })) + ]), + React.createElement('div', { key: 'r2', className: 'sb-hdr-sub', style: { fontSize: '11px', color: '#6b6b73', fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, isOffline ? 'No network · reconnecting' : (peerPresenceWord || (onlineConnected ? 'P2P · end-to-end encrypted' : (status === 'peer_disconnected' ? 'Peer disconnected' : (status === 'disconnected' ? 'Disconnected' : 'Connecting…'))))) + ]) ]), secBtn, - React.createElement('div', { key: 'right', style: { display: 'flex', alignItems: 'center', gap: '9px' } }, [ - React.createElement('div', { key: 'conn', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 13px', borderRadius: '9px', border: '1px solid rgba(255,255,255,0.07)', background: 'rgba(255,255,255,0.02)' } }, [ - React.createElement('span', { key: 'dot', style: { width: '7px', height: '7px', borderRadius: '50%', background: connected ? '#3ecf8e' : '#e3c84e', boxShadow: connected ? '0 0 0 3px rgba(62,207,142,0.16)' : '0 0 0 3px rgba(227,200,78,0.16)' } }), - React.createElement('span', { key: 't', style: { fontSize: '13px', fontWeight: 600, color: '#cfcfd4' } }, connected ? 'Connected' : 'Connecting…') + React.createElement('div', { key: 'right', className: 'sb-hdr-right', style: { display: 'flex', alignItems: 'center', gap: '9px' } }, [ + React.createElement('div', { key: 'conn', className: 'sb-conn', style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 13px', borderRadius: '9px', border: '1px solid rgba(255,255,255,0.07)', background: 'rgba(255,255,255,0.02)' } }, [ + React.createElement('span', { key: 'dot', style: { flex: 'none', width: '7px', height: '7px', borderRadius: '50%', background: connDot, boxShadow: connGlow } }), + React.createElement('span', { key: 't', className: 'sb-conn-text', style: { fontSize: '13px', fontWeight: 600, color: '#cfcfd4' } }, connLabel) ]), React.createElement('button', { key: 'dc', onClick: onDisconnect, className: 'sb-disconnect', style: { display: 'flex', alignItems: 'center', gap: '7px', padding: '8px 14px', borderRadius: '9px', border: '1px solid rgba(255,255,255,0.08)', background: 'transparent', color: '#9a9aa2', fontFamily: 'inherit', fontSize: '13px', fontWeight: 600, cursor: 'pointer', transition: 'all .15s' } }, [ React.createElement('i', { key: 'i', className: 'fas fa-power-off', style: { fontSize: '12px' } }), @@ -1551,6 +1652,10 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic const EnhancedChatInterface = ({ + title, + isOffline, + peerPresence, + onRenameTitle, messages, messageInput, setMessageInput, @@ -1878,7 +1983,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic }, React.createElement('i', { className: 'fas fa-arrow-down', style: { fontSize: '15px' } })); const chatHeader = React.createElement(SecureBitChatHeader, { - key: 'chat-header', status: status, onDisconnect: onDisconnect, webrtcManager: webrtcManager + key: 'chat-header', status: status, onDisconnect: onDisconnect, webrtcManager: webrtcManager, title: title, isOffline: isOffline, peerPresence: peerPresence, onRenameTitle: onRenameTitle }); return React.createElement('div', { @@ -1888,17 +1993,358 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic }; + // Build a chat message in the shape addMessageWithAutoScroll produces, so the + // per-session callbacks can dispatch ADD_MESSAGE without an active-view setter. + const buildSessionMessage = (message, type, opts = {}) => ({ + message, + type, + id: Date.now() + Math.random(), + timestamp: (typeof opts.timestamp === 'number') ? opts.timestamp : Date.now(), + mid: opts.mid, + status: opts.status, + viewOnce: opts.viewOnce === true, + viewOnceTtl: (typeof opts.viewOnceTtl === 'number') ? opts.viewOnceTtl : 15, + expiresAt: (typeof opts.expiresAt === 'number') ? opts.expiresAt : undefined + }); + + // Left rail listing every open session (design import: "Multi Session + // Concepts"). Two desktop states — expanded (292px) and a collapsed icon dock + // (72px) — plus a mobile slide-out drawer. Pure presentational: all data comes + // from decorated session objects, all actions are callbacks. + const SB_SVG = { + chevL: '', + chevR: '', + plus: '', + users: '', + burger: '' + }; + + const SessionsSidebar = ({ chats, collapsed, drawerOpen, onToggleCollapse, onSelect, onNewChat, onRename, onCloseDrawer, myStatus, onSetStatus }) => { + const h = React.createElement; + const [editingId, setEditingId] = React.useState(null); + const [draft, setDraft] = React.useState(''); + const [presenceOpen, setPresenceOpen] = React.useState(false); + const startEdit = (c) => (e) => { e.stopPropagation(); setEditingId(c.id); setDraft(c.name); }; + const commitEdit = () => { if (editingId) { onRename(editingId, draft); setEditingId(null); } }; + const editKey = (e) => { + if (e.key === 'Enter') { e.preventDefault(); commitEdit(); } + else if (e.key === 'Escape') { setEditingId(null); } + }; + const renameInput = (extra = {}) => h('input', { + autoFocus: true, + value: draft, + onChange: (e) => setDraft(e.target.value), + onKeyDown: editKey, + onBlur: commitEdit, + onClick: (e) => e.stopPropagation(), + style: Object.assign({ width: '100%', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(240,137,42,0.5)', borderRadius: '6px', color: '#f4f4f6', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, padding: '2px 6px', outline: 'none' }, extra) + }); + const icon = (svg, style) => h('span', { style: Object.assign({ display: 'grid', placeItems: 'center' }, style || {}), dangerouslySetInnerHTML: { __html: svg } }); + const avatar = (c, size, ring) => h('div', { + style: { position: 'relative', flex: 'none', width: size + 'px', height: size + 'px', borderRadius: (size >= 44 ? 12 : 11) + 'px', display: 'grid', placeItems: 'center', background: c.active ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.035)', border: '1px solid rgba(255,255,255,' + (c.active ? '0.14' : '0.07') + ')', fontSize: '13px', fontWeight: 700, letterSpacing: '-0.3px', color: c.active ? '#f4f4f6' : '#9a9aa2' } + }, [c.mono, h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: c.dot, border: '2px solid ' + ring } })]); + + // ---- Expanded list row ---- + const expandedRow = (c) => h('div', { + key: c.id, + onClick: () => onSelect(c.id), + style: { position: 'relative', display: 'flex', alignItems: 'center', gap: '12px', padding: '11px 12px', marginBottom: '4px', borderRadius: '11px', background: c.active ? '#161618' : 'transparent', border: '1px solid ' + (c.active ? 'rgba(255,255,255,0.08)' : 'transparent'), cursor: 'pointer' } + }, [ + c.active && h('span', { key: 'bar', style: { position: 'absolute', left: 0, top: '12px', bottom: '12px', width: '3px', borderRadius: '0 3px 3px 0', background: '#f0892a' } }), + avatar(c, 38, c.active ? '#161618' : '#0c0c0e'), + h('div', { key: 'body', style: { flex: 1, minWidth: 0 } }, [ + h('div', { key: 'top', style: { display: 'flex', alignItems: 'center', gap: '7px' } }, [ + editingId === c.id + ? renameInput() + : h('span', { + key: 'name', + onDoubleClick: startEdit(c), + title: 'Double-click to rename', + style: { flex: 1, minWidth: 0, fontSize: '14px', fontWeight: c.active ? 700 : 600, letterSpacing: '-0.2px', color: c.active ? '#f4f4f6' : '#cfcfd4', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } + }, c.name), + c.unread && editingId !== c.id && h('span', { key: 'u', style: { flex: 'none', minWidth: '18px', height: '18px', padding: '0 5px', borderRadius: '9px', display: 'grid', placeItems: 'center', background: '#f0892a', color: '#1a0f04', fontFamily: "'JetBrains Mono',monospace", fontSize: '10px', fontWeight: 700 } }, c.unread) + ]), + h('div', { key: 'prev', style: { fontSize: '12px', color: c.active ? '#8a8a92' : '#6b6b73', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, c.preview) + ]) + ]); + + // ---- Collapsed dock item ---- + const dockItem = (c) => h('div', { key: c.id, style: { position: 'relative' } }, [ + c.active && h('span', { key: 'bar', style: { position: 'absolute', left: '-13px', top: '9px', bottom: '9px', width: '3px', borderRadius: '0 3px 3px 0', background: '#f0892a' } }), + h('div', { + key: 'tile', + onClick: () => onSelect(c.id), + title: c.name, + style: { position: 'relative', width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', cursor: 'pointer', background: c.active ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,' + (c.active ? '0.14' : '0.07') + ')', fontSize: '13px', fontWeight: 700, letterSpacing: '-0.3px', color: c.active ? '#f4f4f6' : '#9a9aa2' } + }, [ + c.mono, + h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: c.dot, border: '2.5px solid #0c0c0e' } }), + c.unread && h('span', { key: 'u', style: { position: 'absolute', left: '-5px', top: '-5px', minWidth: '17px', height: '17px', padding: '0 4px', borderRadius: '9px', display: 'grid', placeItems: 'center', background: '#f0892a', color: '#1a0f04', fontFamily: "'JetBrains Mono',monospace", fontSize: '9.5px', fontWeight: 700, border: '2px solid #0c0c0e' } }, c.unread) + ]) + ]); + + // Same logo treatment as the landing header (Header.jsx): the mark on a + // transparent background — no black tile. + const brandMark = (size) => h('div', { style: { width: size + 'px', height: size + 'px', flex: 'none', display: 'grid', placeItems: 'center' } }, + h('img', { src: '/logo/securebit-mark.svg', alt: 'SecureBit', style: { width: '100%', height: '100%', objectFit: 'contain', display: 'block' } })); + const collapseBtn = (svg, title) => h('button', { onClick: onToggleCollapse, title, style: { width: '30px', height: '30px', borderRadius: '8px', display: 'grid', placeItems: 'center', border: '1px solid rgba(255,255,255,0.07)', background: 'transparent', color: '#8a8a92', cursor: 'pointer' }, dangerouslySetInnerHTML: { __html: svg } }); + + // ---- Expanded rail content ---- + // ---- Presence ("You" status) panel ---- + const myMeta = MY_STATUS_OPTIONS.find((o) => o.key === myStatus) || MY_STATUS_OPTIONS[0]; + const PRES_SVG = { + user: '', + check: '', + chevUp: '', + lock: '' + }; + const presenceMenu = (pos) => (presenceOpen ? h('div', { + key: 'pmenu', + style: Object.assign({ position: 'absolute', zIndex: 30, borderRadius: '14px', background: '#161618', border: '1px solid rgba(255,255,255,0.1)', boxShadow: '0 16px 40px rgba(0,0,0,0.55)', padding: '6px' }, pos) + }, [ + h('div', { key: 'h', style: { padding: '9px 10px 7px', fontFamily: "'JetBrains Mono',monospace", fontSize: '10px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '1.2px' } }, 'Set your status'), + ...MY_STATUS_OPTIONS.map((o) => h('button', { + key: o.key, + onClick: () => { onSetStatus(o.key); setPresenceOpen(false); }, + style: { width: '100%', display: 'flex', alignItems: 'center', gap: '11px', padding: '9px 10px', borderRadius: '9px', border: 'none', background: 'transparent', cursor: 'pointer', textAlign: 'left' } + }, [ + h('span', { key: 'd', style: { flex: 'none', width: '10px', height: '10px', borderRadius: '50%', background: o.dot } }), + h('span', { key: 't', style: { flex: 1, minWidth: 0 } }, [ + h('span', { key: 'w', style: { display: 'block', fontSize: '13.5px', fontWeight: 600, color: '#e8e8eb' } }, o.word), + h('span', { key: 'de', style: { display: 'block', fontSize: '11.5px', color: '#6b6b73' } }, o.desc) + ]), + o.key === myStatus && h('span', { key: 'c', style: { flex: 'none', display: 'grid', placeItems: 'center' }, dangerouslySetInnerHTML: { __html: PRES_SVG.check } }) + ])), + h('div', { key: 'note', style: { display: 'flex', alignItems: 'flex-start', gap: '8px', margin: '6px 6px 4px', padding: '9px 10px', borderRadius: '9px', background: 'rgba(62,207,142,0.06)', border: '1px solid rgba(62,207,142,0.16)' } }, [ + h('span', { key: 'i', style: { flex: 'none', marginTop: '1px', display: 'grid' }, dangerouslySetInnerHTML: { __html: PRES_SVG.lock } }), + h('span', { key: 't', style: { fontSize: '11px', lineHeight: 1.45, color: '#8a8a92' } }, 'Sent end-to-end to connected peers only — never stored on a server.') + ]) + ]) : null); + const presencePanelExpanded = h('div', { key: 'you', style: { flex: 'none', position: 'relative', marginTop: '10px', borderTop: '1px solid rgba(255,255,255,0.06)', padding: '10px 12px 12px' } }, [ + presenceMenu({ left: '12px', right: '12px', bottom: '64px' }), + h('button', { key: 'btn', onClick: () => setPresenceOpen((v) => !v), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '11px', padding: '7px 8px', borderRadius: '11px', border: '1px solid rgba(255,255,255,0.06)', background: 'rgba(255,255,255,0.02)', cursor: 'pointer' } }, [ + h('div', { key: 'av', style: { position: 'relative', flex: 'none', width: '36px', height: '36px', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(240,137,42,0.12)', border: '1px solid rgba(240,137,42,0.24)', color: '#f0892a' } }, [ + h('span', { key: 'i', style: { display: 'grid' }, dangerouslySetInnerHTML: { __html: PRES_SVG.user } }), + h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '11px', height: '11px', borderRadius: '50%', background: myMeta.dot, border: '2px solid #0c0c0e' } }) + ]), + h('div', { key: 'tx', style: { flex: 1, minWidth: 0, textAlign: 'left' } }, [ + h('div', { key: 'y', style: { fontSize: '13.5px', fontWeight: 700, color: '#f4f4f6' } }, 'You'), + h('div', { key: 'w', style: { fontSize: '12px', color: '#8a8a92' } }, myMeta.word) + ]), + h('span', { key: 'ch', style: { display: 'grid', placeItems: 'center' }, dangerouslySetInnerHTML: { __html: PRES_SVG.chevUp } }) + ]) + ]); + const presencePanelCollapsed = h('div', { key: 'you', style: { flex: 'none', position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '0 0 13px' } }, [ + presenceMenu({ left: '60px', bottom: '8px', width: '248px' }), + h('button', { key: 'btn', onClick: () => setPresenceOpen((v) => !v), title: 'Your status — ' + myMeta.word, style: { position: 'relative', width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', cursor: 'pointer', background: 'rgba(240,137,42,0.12)', border: '1px solid rgba(240,137,42,0.24)', color: '#f0892a' } }, [ + h('span', { key: 'i', style: { display: 'grid' }, dangerouslySetInnerHTML: { __html: PRES_SVG.user } }), + h('span', { key: 'dot', style: { position: 'absolute', right: '-2px', bottom: '-2px', width: '12px', height: '12px', borderRadius: '50%', background: myMeta.dot, border: '2.5px solid #0c0c0e' } }) + ]) + ]); + + const expandedInner = [ + h('div', { key: 'head', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 12px 0 16px', height: '64px', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [ + h('div', { key: 'brand', style: { display: 'flex', alignItems: 'center', gap: '10px' } }, [brandMark(30), h('span', { key: 't', style: { fontSize: '15px', fontWeight: 800, letterSpacing: '-0.3px', color: '#f4f4f6' } }, 'SecureBit')]), + collapseBtn(SB_SVG.chevL, 'Collapse') + ]), + h('div', { key: 'label', style: { flex: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '16px 16px 9px' } }, [ + h('span', { key: 'l', style: { fontFamily: "'JetBrains Mono',monospace", fontSize: '10px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '1.3px' } }, 'Chats'), + h('span', { key: 'c', style: { fontFamily: "'JetBrains Mono',monospace", fontSize: '10px', fontWeight: 600, color: '#6b6b73' } }, String(chats.length)) + ]), + h('div', { key: 'list', className: 'msc-scroll', style: { flex: 1, overflowY: 'auto', padding: '0 10px' } }, [ + ...chats.map(expandedRow), + h('div', { key: 'gh', style: { marginTop: '14px', padding: '0 2px 6px' } }, h('span', { style: { fontFamily: "'JetBrains Mono',monospace", fontSize: '10px', fontWeight: 600, color: '#56565e', textTransform: 'uppercase', letterSpacing: '1.3px' } }, 'Group chats')), + h('div', { key: 'gph', title: 'Coming in v6.0', style: { display: 'flex', alignItems: 'center', gap: '12px', padding: '11px 12px', borderRadius: '11px', background: 'transparent', border: '1px dashed rgba(255,255,255,0.09)', cursor: 'not-allowed' } }, [ + h('div', { key: 'i', style: { flex: 'none', width: '38px', height: '38px', borderRadius: '11px', display: 'grid', placeItems: 'center', background: 'rgba(255,255,255,0.025)', border: '1px solid rgba(255,255,255,0.06)', color: '#56565e' }, dangerouslySetInnerHTML: { __html: SB_SVG.users } }), + h('div', { key: 'b', style: { flex: 1, minWidth: 0 } }, [ + h('div', { key: 't', style: { fontSize: '14px', fontWeight: 600, color: '#8a8a92' } }, 'Group chats'), + h('div', { key: 's', style: { fontSize: '11.5px', color: '#56565e' } }, 'Up to 8 peers · P2P mesh') + ]), + h('span', { key: 'soon', style: { flex: 'none', padding: '4px 9px', borderRadius: '7px', background: 'rgba(240,137,42,0.1)', border: '1px solid rgba(240,137,42,0.24)', fontFamily: "'JetBrains Mono',monospace", fontSize: '9.5px', fontWeight: 700, color: '#f0892a', textTransform: 'uppercase', letterSpacing: '0.8px' } }, 'Soon') + ]) + ]), + h('div', { key: 'new', style: { flex: 'none', padding: '12px' } }, h('button', { + onClick: onNewChat, + style: { width: '100%', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '9px', padding: '12px', borderRadius: '11px', border: 'none', background: '#f0892a', color: '#1a0f04', fontFamily: 'inherit', fontSize: '14px', fontWeight: 700, cursor: 'pointer', boxShadow: '0 8px 24px rgba(240,137,42,0.28)' } + }, [icon(SB_SVG.plus, { key: 'p' }), 'New chat'])), + presencePanelExpanded + ]; + + // ---- Collapsed dock content ---- + const collapsedInner = [ + h('div', { key: 'head', style: { flex: 'none', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px', padding: '13px 0', width: '100%', borderBottom: '1px solid rgba(255,255,255,0.06)' } }, [brandMark(32), collapseBtn(SB_SVG.chevR, 'Expand')]), + h('div', { key: 'list', className: 'msc-scroll', style: { flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '10px', padding: '14px 0', width: '100%' } }, [ + ...chats.map(dockItem), + h('div', { key: 'sep', style: { width: '30px', height: '1px', background: 'rgba(255,255,255,0.07)', margin: '2px 0' } }), + h('div', { key: 'gph', title: 'Group chats — coming in v6.0', style: { position: 'relative', width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', cursor: 'not-allowed', background: 'transparent', border: '1px dashed rgba(255,255,255,0.1)', color: '#56565e' }, dangerouslySetInnerHTML: { __html: SB_SVG.users } }) + ]), + h('div', { key: 'new', style: { flex: 'none', padding: '13px 0' } }, h('button', { + onClick: onNewChat, title: 'New chat', + style: { width: '44px', height: '44px', borderRadius: '12px', display: 'grid', placeItems: 'center', border: 'none', background: '#f0892a', color: '#1a0f04', cursor: 'pointer', boxShadow: '0 8px 24px rgba(240,137,42,0.28)' }, dangerouslySetInnerHTML: { __html: SB_SVG.plus } + })), + presencePanelCollapsed + ]; + + const railWidth = collapsed ? '72px' : '292px'; + const railStyle = { flex: 'none', width: railWidth, display: 'flex', flexDirection: 'column', alignItems: collapsed ? 'center' : 'stretch', background: '#0c0c0e', borderRight: '1px solid rgba(255,255,255,0.06)' }; + const inner = collapsed ? collapsedInner : expandedInner; + + return h(React.Fragment, null, [ + // Responsive behaviour (inline styles can't express media queries). + h('style', { key: 'css', dangerouslySetInnerHTML: { __html: '@media (max-width:1023px){.sb-rail{display:none !important;}.sb-burger{display:grid !important;}}@media (min-width:1024px){.sb-drawer-overlay{display:none !important;}}' } }), + // Desktop rail + h('aside', { key: 'rail', className: 'sb-rail', style: railStyle }, inner), + // Mobile drawer overlay + h('div', { + key: 'drawer', className: 'sb-drawer-overlay', + onClick: onCloseDrawer, + style: { position: 'fixed', inset: 0, zIndex: 60, background: 'rgba(6,6,8,0.6)', backdropFilter: 'blur(4px)', WebkitBackdropFilter: 'blur(4px)', display: drawerOpen ? 'block' : 'none' } + }, h('aside', { onClick: (e) => e.stopPropagation(), style: { position: 'absolute', left: 0, top: 0, bottom: 0, width: '292px', display: 'flex', flexDirection: 'column', background: '#0c0c0e', borderRight: '1px solid rgba(255,255,255,0.06)', boxShadow: '0 0 60px rgba(0,0,0,0.6)' } }, expandedInner)) + ]); + }; + // Main Enhanced Application Component const EnhancedSecureP2PChat = () => { - const [messages, setMessages] = React.useState([]); + // ============================================ + // MULTI-SESSION REGISTRY + // Each conversation is an independent session with its OWN + // EnhancedSecureWebRTCManager (full key/ratchet/SAS isolation). The reducer + // holds serializable per-session state; non-serializable objects (managers, + // notification integrations, offline queues) live in ref-held Maps keyed by + // sessionId and are NEVER shared between sessions. sessionId is local-only. + // ============================================ + const [sessionsState, dispatch] = React.useReducer(sessionsReducer, undefined, createInitialState); + const activeSessionId = sessionsState.activeSessionId; + const activeIdRef = React.useRef(null); + activeIdRef.current = activeSessionId; + const active = activeSessionId ? sessionsState.sessions[activeSessionId] : null; + const EMPTY_ARR = React.useRef([]).current; + + const managersRef = React.useRef(new Map()); // id -> EnhancedSecureWebRTCManager + const integrationsRef = React.useRef(new Map()); // id -> NotificationIntegration + const queuesRef = React.useRef(new Map()); // id -> { incoming:[], outgoing:[] } + + // Active-session VIEW. The rest of the component (and the child setup/chat + // components) read these names unchanged; the setters dispatch to the active + // session read from activeIdRef at call time, so even memoized callbacks stay + // correct across session switches. + const dispatchActive = React.useCallback((build) => { + const id = activeIdRef.current; if (!id) return; + dispatch(build(id)); + }, []); + + const messages = active ? active.messages : EMPTY_ARR; + const setMessages = React.useCallback((updaterOrArr) => { + const id = activeIdRef.current; if (!id) return; + if (typeof updaterOrArr === 'function') dispatch({ type: SA.SET_MESSAGES, id, updater: updaterOrArr }); + else dispatch({ type: SA.SET_MESSAGES, id, messages: updaterOrArr }); + }, []); + + const connectionStatus = active ? active.status : 'disconnected'; + const setConnectionStatus = React.useCallback((status) => dispatchActive((id) => ({ type: SA.SET_STATUS, id, status })), [dispatchActive]); + + const keyFingerprint = active ? active.keyFingerprint : ''; + const setKeyFingerprint = React.useCallback((fingerprint) => dispatchActive((id) => ({ type: SA.SET_FINGERPRINT, id, fingerprint })), [dispatchActive]); + + const verificationCode = active ? active.verificationCode : ''; + const setVerificationCode = React.useCallback((code) => dispatchActive((id) => ({ type: SA.SET_VERIFICATION, id, code })), [dispatchActive]); + + const isVerified = active ? active.sas.isVerified : false; + const setIsVerified = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { isVerified: !!v } })), [dispatchActive]); + const localVerificationConfirmed = active ? active.sas.localConfirmed : false; + const setLocalVerificationConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { localConfirmed: !!v } })), [dispatchActive]); + const remoteVerificationConfirmed = active ? active.sas.remoteConfirmed : false; + const setRemoteVerificationConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { remoteConfirmed: !!v } })), [dispatchActive]); + const bothVerificationsConfirmed = active ? active.sas.bothConfirmed : false; + const setBothVerificationsConfirmed = React.useCallback((v) => dispatchActive((id) => ({ type: SA.SET_SAS, id, sas: { bothConfirmed: !!v } })), [dispatchActive]); + + const pendingIncomingFiles = active ? active.pendingIncomingFiles : EMPTY_ARR; + const setPendingIncomingFiles = React.useCallback((updaterOrArr) => { + const id = activeIdRef.current; if (!id) return; + if (typeof updaterOrArr === 'function') dispatch({ type: SA.SET_PENDING_FILES, id, updater: updaterOrArr }); + else dispatch({ type: SA.SET_PENDING_FILES, id, files: updaterOrArr }); + }, []); + + // Per-session offer/answer setup flow (preserved when switching chats). + const setupField = (name, fallback) => (active ? active.setup[name] : fallback); + const setSetupField = (name) => React.useCallback((value) => dispatchActive((id) => ({ type: SA.PATCH_SETUP, id, patch: { [name]: value } })), [dispatchActive]); + const offerData = setupField('offerData', ''); + const setOfferData = setSetupField('offerData'); + const answerData = setupField('answerData', ''); + const setAnswerData = setSetupField('answerData'); + const offerInput = setupField('offerInput', ''); + const setOfferInput = setSetupField('offerInput'); + const answerInput = setupField('answerInput', ''); + const setAnswerInput = setSetupField('answerInput'); + const showOfferStep = setupField('showOfferStep', false); + const setShowOfferStep = setSetupField('showOfferStep'); + const showAnswerStep = setupField('showAnswerStep', false); + const setShowAnswerStep = setSetupField('showAnswerStep'); + const showVerification = setupField('showVerification', false); + const setShowVerification = setSetupField('showVerification'); + const showQRCode = setupField('showQRCode', false); + const setShowQRCode = setSetupField('showQRCode'); + const qrCodeUrl = setupField('qrCodeUrl', ''); + const setQrCodeUrl = setSetupField('qrCodeUrl'); + const isGeneratingKeys = setupField('isGeneratingKeys', false); + const setIsGeneratingKeys = setSetupField('isGeneratingKeys'); + + // Accessor over the ACTIVE session's manager / notification integration. + // Existing `webrtcManagerRef.current.X()` call sites keep working against the + // active session; per-session callbacks use their own captured manager instead. + const webrtcManagerRef = React.useMemo(() => ({ + get current() { return managersRef.current.get(activeIdRef.current) || null; }, + set current(v) { + const id = activeIdRef.current; if (!id) return; + if (v) managersRef.current.set(id, v); else managersRef.current.delete(id); + } + }), []); + const notificationIntegrationRef = React.useMemo(() => ({ + get current() { return integrationsRef.current.get(activeIdRef.current) || null; }, + set current(v) { + const id = activeIdRef.current; if (!id) return; + if (v) integrationsRef.current.set(id, v); else integrationsRef.current.delete(id); + } + }), []); + + // ---- Presence / availability ---- + // My status is broadcast E2E over each connected session's data channel + // (sendSystemMessage) and never stored on a server. 'invisible' goes on the + // wire as 'offline' so peers cannot tell. myStatusRef lets the per-session + // callbacks (captured at create time) read the current status. + const [myStatus, setMyStatusState] = React.useState(() => { + try { return localStorage.getItem('securebit_my_status') || 'available'; } catch { return 'available'; } + }); + const myStatusRef = React.useRef(myStatus); + myStatusRef.current = myStatus; + const wirePresence = (s) => (s === 'invisible' ? 'offline' : s); + // Presence travels as a normal ENCRYPTED chat message (type 'message') — that is + // the only inbound path the manager delivers to onMessage; unknown raw system + // types are dropped. handleMessage recognises the {type:'presence'} payload and + // consumes it without displaying it. + const sendPresenceTo = React.useCallback((mgr, s) => { + if (!mgr || typeof mgr.sendMessage !== 'function') return; + try { + if (mgr.isConnected && mgr.isConnected()) { + const p = mgr.sendMessage(JSON.stringify({ type: 'presence', status: wirePresence(s) })); + if (p && typeof p.catch === 'function') p.catch(() => {}); + } + } catch (_) {} + }, []); + const setMyStatus = React.useCallback((key) => { + setMyStatusState(key); + try { localStorage.setItem('securebit_my_status', key); } catch {} + for (const mgr of managersRef.current.values()) sendPresenceTo(mgr, key); + }, [sendPresenceTo]); + // Secure chat extras: per-message send modes + 1s tick for countdowns. const [codeMode, setCodeMode] = React.useState(false); const [viewOnceMode, setViewOnceMode] = React.useState(false); const [viewOnceTtl, setViewOnceTtl] = React.useState(15); // seconds visible after the peer opens it const [disappearTtl, setDisappearTtl] = React.useState(0); // seconds; 0 = off (sticky) const [nowTick, setNowTick] = React.useState(() => Date.now()); - const [connectionStatus, setConnectionStatus] = React.useState('disconnected'); + // connectionStatus → per-session status (active-session view, above). // Offline awareness — tracks the real online/offline events (which is // also what a console-simulated `dispatchEvent(new Event('offline'))` // fires, even when navigator.onLine stays true). @@ -1908,8 +2354,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic // over the still-live P2P channel: outgoing waits until WE reconnect, // incoming waits until WE reconnect before being shown/acked. const offlineRef = React.useRef(isOffline); - const outgoingQueueRef = React.useRef([]); - const incomingQueueRef = React.useRef([]); + // Offline store-and-forward queues are now per-session (queuesRef, above). React.useEffect(() => { offlineRef.current = isOffline; }, [isOffline]); React.useEffect(() => { const goOffline = () => setIsOffline(true); @@ -1976,39 +2421,23 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic // Moved scrollToBottom logic to be available globally const [messageInput, setMessageInput] = React.useState(''); - const [offerData, setOfferData] = React.useState(''); - const [answerData, setAnswerData] = React.useState(''); - const [offerInput, setOfferInput] = React.useState(''); - const [answerInput, setAnswerInput] = React.useState(''); - const [keyFingerprint, setKeyFingerprint] = React.useState(''); - const [verificationCode, setVerificationCode] = React.useState(''); - const [showOfferStep, setShowOfferStep] = React.useState(false); - const [showAnswerStep, setShowAnswerStep] = React.useState(false); - const [showVerification, setShowVerification] = React.useState(false); - const [showQRCode, setShowQRCode] = React.useState(false); - const [qrCodeUrl, setQrCodeUrl] = React.useState(''); + // offerData/answerData/offerInput/answerInput, showOfferStep/showAnswerStep, + // showVerification/showQRCode/qrCodeUrl, isGeneratingKeys → per-session setup + // slice (declared in the active-session view block above). const [showQRScanner, setShowQRScanner] = React.useState(false); const [showQRScannerModal, setShowQRScannerModal] = React.useState(false); - const [isGeneratingKeys, setIsGeneratingKeys] = React.useState(false); - - const [isVerified, setIsVerified] = React.useState(false); + // isVerified + mutual-verification flags → per-session SAS slice (above). const [securityLevel, setSecurityLevel] = React.useState(null); const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0); - - // Mutual verification states - const [localVerificationConfirmed, setLocalVerificationConfirmed] = React.useState(false); - const [remoteVerificationConfirmed, setRemoteVerificationConfirmed] = React.useState(false); - const [bothVerificationsConfirmed, setBothVerificationsConfirmed] = React.useState(false); - + // PAKE password states removed - using SAS verification instead - + // Session state - all security features enabled by default const [pendingSession, setPendingSession] = React.useState(null); - - // All security features are enabled by default - no payment required - const [pendingIncomingFiles, setPendingIncomingFiles] = React.useState([]); + // All security features are enabled by default - no payment required + // pendingIncomingFiles → per-session slice (above). @@ -2062,8 +2491,8 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic }); }; - const webrtcManagerRef = React.useRef(null); - const notificationIntegrationRef = React.useRef(null); + // webrtcManagerRef / notificationIntegrationRef → accessors over the active + // session's manager / integration (active-session view block, above). // Development-only debug helpers. Production never exposes // manager internals or cleanup controls on `window`. @@ -2125,32 +2554,37 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic setMessages(prev => prev.map(m => (String(m.mid) === String(mid) && m.type === 'sent') ? { ...m, status } : m)); }, []); - // When WE come back online: transmit anything we queued while offline, - // and surface (and acknowledge) anything that arrived while we were offline. + // When WE come back online: for EVERY session, transmit anything queued while + // offline and surface (and acknowledge) anything that arrived meanwhile. Each + // session flushes against its own manager and into its own slice. const flushOfflineQueues = React.useCallback(() => { - const out = outgoingQueueRef.current; - outgoingQueueRef.current = []; - for (const item of out) { - const send = webrtcManagerRef.current?.sendMessage?.(item.outText, item.meta); - if (send && typeof send.then === 'function') { - send.then(() => updateMessageStatus(item.mid, 'sent')).catch(() => updateMessageStatus(item.mid, 'failed')); + for (const [id, q] of queuesRef.current.entries()) { + const mgr = managersRef.current.get(id); + const out = q.outgoing; q.outgoing = []; + for (const item of out) { + const send = mgr?.sendMessage?.(item.outText, item.meta); + if (send && typeof send.then === 'function') { + send.then(() => dispatch({ type: SA.UPDATE_MESSAGE_STATUS, id, mid: item.mid, status: 'delivered' })) + .catch(() => dispatch({ type: SA.UPDATE_MESSAGE_STATUS, id, mid: item.mid, status: 'failed' })); + } + } + const inc = q.incoming; q.incoming = []; + if (inc.length > 0) { + dispatch({ type: SA.ADD_MESSAGE, id, message: buildSessionMessage( + `Connection restored — ${inc.length} message${inc.length === 1 ? '' : 's'} received while you were offline.`, + 'notice' + ) }); + } + const viewing = id === activeIdRef.current && (typeof document === 'undefined' || document.visibilityState === 'visible'); + for (const item of inc) { + dispatch({ type: SA.ADD_MESSAGE, id, message: buildSessionMessage(item.message, item.type, item.opts) }); + if (item.opts && item.opts.mid && item.type === 'received') { + if (viewing) { try { mgr?.sendDeliveryReceipt?.(item.opts.mid); } catch (_) {} } + else if (q.pendingReadAcks) q.pendingReadAcks.push(item.opts.mid); + } } } - const inc = incomingQueueRef.current; - incomingQueueRef.current = []; - if (inc.length > 0) { - addMessageWithAutoScroll( - `Connection restored — ${inc.length} message${inc.length === 1 ? '' : 's'} received while you were offline.`, - 'notice' - ); - } - for (const item of inc) { - addMessageWithAutoScroll(item.message, item.type, item.opts); - if (item.opts && item.opts.mid && (item.type === 'received' || item.type === 'sent')) { - try { webrtcManagerRef.current?.sendDeliveryReceipt?.(item.opts.mid); } catch (_) {} - } - } - }, [addMessageWithAutoScroll, updateMessageStatus]); + }, []); React.useEffect(() => { if (isOffline) return; // only act on the offline → online edge @@ -2231,44 +2665,103 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic } }, [messages]); - // Disappearing-message clock: tick every second (only while some - // message has an expiry) and prune anything past its deadline. - const hasExpiring = messages.some(m => typeof m.expiresAt === 'number'); + // Disappearing-message clock: tick every second (while ANY session has an + // expiry) and prune past-deadline messages — independently per session, so + // a background conversation's timers keep running too. + const anyExpiring = sessionsState.order.some((id) => (sessionsState.sessions[id]?.messages || []).some((m) => typeof m.expiresAt === 'number')); + const sessionsStateRef = React.useRef(sessionsState); + sessionsStateRef.current = sessionsState; React.useEffect(() => { - if (!hasExpiring) return; + if (!anyExpiring) return; + // Disappearing messages leave a tombstone (content wiped) instead of vanishing. + const expireFn = (prev) => { + const now = Date.now(); + let changed = false; + const next = prev.map((m) => { + if (typeof m.expiresAt === 'number' && m.expiresAt <= now && !m.expired) { + changed = true; + return { ...m, expired: true, message: '', expiresAt: undefined }; + } + return m; + }); + return changed ? next : prev; + }; const interval = setInterval(() => { const now = Date.now(); setNowTick(now); - setMessages(prev => { - // Disappearing messages leave a tombstone ("This message has - // expired") instead of vanishing; the content is wiped. - let changed = false; - const next = prev.map(m => { - if (typeof m.expiresAt === 'number' && m.expiresAt <= now && !m.expired) { - changed = true; - return { ...m, expired: true, message: '', expiresAt: undefined }; - } - return m; - }); - return changed ? next : prev; - }); + const st = sessionsStateRef.current; + for (const id of st.order) { + const msgs = st.sessions[id]?.messages || []; + if (msgs.some((m) => typeof m.expiresAt === 'number' && m.expiresAt <= now && !m.expired)) { + dispatch({ type: SA.SET_MESSAGES, id, updater: expireFn }); + } + } }, 1000); return () => clearInterval(interval); - }, [hasExpiring]); + }, [anyExpiring]); // PAKE password functions removed - using SAS verification instead - React.useEffect(() => { - // Prevent multiple initializations - if (webrtcManagerRef.current) { - console.log('⚠️ WebRTC Manager already initialized, skipping...'); - return; - } - + // Build a brand-new, fully-isolated session: its own manager, its own + // id-bound callbacks, its own notification integration and offline queues. + // The callback bodies below are the original single-session handlers; the + // id-bound shadow setters declared here redirect every set*/dispatch into + // THIS session's slice, so nothing leaks into another conversation. + const createSession = (opts = {}) => { + const role = opts.role || 'offer'; + const entry = createSessionEntry({ role }); + const id = entry.id; + dispatch({ type: SA.CREATE_SESSION, entry, activate: opts.activate !== false }); + // pendingReadAcks: mids received while this chat was NOT being viewed; the + // read receipt is held back until the user actually opens the conversation. + queuesRef.current.set(id, { incoming: [], outgoing: [], pendingReadAcks: [] }); + + // --- id-bound shadow setters (override the active-session view names) --- + const setMessages = (u) => { + if (typeof u === 'function') dispatch({ type: SA.SET_MESSAGES, id, updater: u }); + else dispatch({ type: SA.SET_MESSAGES, id, messages: u }); + }; + const addMessageWithAutoScroll = (message, type, opts2 = {}) => { + dispatch({ type: SA.ADD_MESSAGE, id, message: buildSessionMessage(message, type, opts2) }); + // Background session received a chat message → bump its unread badge. + if (type === 'received' && id !== activeIdRef.current) { + dispatch({ type: SA.INCREMENT_UNREAD, id }); + } + }; + const updateMessageStatus = (mid, status) => { if (mid) dispatch({ type: SA.UPDATE_MESSAGE_STATUS, id, mid, status }); }; + const setConnectionStatus = (status) => dispatch({ type: SA.SET_STATUS, id, status }); + const setKeyFingerprint = (fingerprint) => dispatch({ type: SA.SET_FINGERPRINT, id, fingerprint }); + const setVerificationCode = (code) => dispatch({ type: SA.SET_VERIFICATION, id, code }); + const setIsVerified = (v) => dispatch({ type: SA.SET_SAS, id, sas: { isVerified: !!v } }); + const setLocalVerificationConfirmed = (v) => dispatch({ type: SA.SET_SAS, id, sas: { localConfirmed: !!v } }); + const setRemoteVerificationConfirmed = (v) => dispatch({ type: SA.SET_SAS, id, sas: { remoteConfirmed: !!v } }); + const setBothVerificationsConfirmed = (v) => dispatch({ type: SA.SET_SAS, id, sas: { bothConfirmed: !!v } }); + const setShowVerification = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showVerification: !!v } }); + const setShowOfferStep = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showOfferStep: !!v } }); + const setShowAnswerStep = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showAnswerStep: !!v } }); + const setShowQRCode = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { showQRCode: !!v } }); + const setQrCodeUrl = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { qrCodeUrl: v } }); + const setOfferData = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { offerData: v } }); + const setAnswerData = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { answerData: v } }); + const setOfferInput = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { offerInput: v } }); + const setAnswerInput = (v) => dispatch({ type: SA.PATCH_SETUP, id, patch: { answerInput: v } }); + const setPendingIncomingFiles = (u) => { + if (typeof u === 'function') dispatch({ type: SA.SET_PENDING_FILES, id, updater: u }); + else dispatch({ type: SA.SET_PENDING_FILES, id, files: u }); + }; + const sessionQueues = () => queuesRef.current.get(id) || { incoming: [], outgoing: [] }; + const handleMessage = (message, type, meta) => { if (typeof message === 'string' && message.trim().startsWith('{')) { try { const parsedMessage = JSON.parse(message); + // Peer availability update (E2E control message) — store it for THIS + // session and never show it in the chat. + if (parsedMessage.type === 'presence') { + const st = (parsedMessage.data && parsedMessage.data.status) || parsedMessage.status; + if (st) dispatch({ type: SA.SET_PEER_PRESENCE, id, presence: st }); + return; + } const blockedTypes = [ 'file_transfer_start', 'file_transfer_response', @@ -2290,7 +2783,14 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic ]; if (parsedMessage.type && blockedTypes.includes(parsedMessage.type)) { console.log(`Blocked system/file message from chat: ${parsedMessage.type}`); - return; + return; + } + // Defensive unwrap: a plain-message envelope can leak through as + // raw JSON for some payloads (e.g. fenced code blocks). Show the + // real text, not {"type":"message","data":"```..."}. + if (parsedMessage.type === 'message' && typeof parsedMessage.data === 'string') { + message = parsedMessage.data; + if (parsedMessage.meta && typeof parsedMessage.meta === 'object') meta = parsedMessage.meta; } } catch (parseError) { @@ -2315,15 +2815,25 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic // and don't acknowledge it yet, so the sender stays at one check. // It's surfaced (and acked → ✓✓) the moment we reconnect. if (offlineRef.current && type === 'received') { - incomingQueueRef.current.push({ message, type, opts }); + sessionQueues().incoming.push({ message, type, opts }); return; } addMessageWithAutoScroll(message, type, opts); - // Acknowledge receipt so the sender's bubble shows "delivered" (✓✓). - if (opts.mid && (type === 'received' || type === 'sent')) { - try { webrtcManagerRef.current?.sendDeliveryReceipt?.(opts.mid); } catch (_) {} + // Read receipt: only ack once the user is actually looking at THIS + // conversation (active session + visible tab). Otherwise hold the mid + // back so a message read in a background chat stays "delivered" (grey + // ✓✓) on the sender's side — it flips to green only when this chat is + // opened (see flushReadAcks). + if (opts.mid && type === 'received') { + const beingViewed = id === activeIdRef.current && (typeof document === 'undefined' || document.visibilityState === 'visible'); + if (beingViewed) { + try { manager?.sendDeliveryReceipt?.(opts.mid); } catch (_) {} + } else { + const q = sessionQueues(); + if (q.pendingReadAcks) q.pendingReadAcks.push(opts.mid); + } } }; @@ -2353,6 +2863,17 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic setTimeout(() => { setIsVerified(true); }, 0); + // Tell the newly-connected peer my current availability (E2E, via + // the normal encrypted message path — see sendPresenceTo). + try { + const s = myStatusRef.current === 'invisible' ? 'offline' : myStatusRef.current; + setTimeout(() => { + try { + const p = manager.sendMessage?.(JSON.stringify({ type: 'presence', status: s })); + if (p && typeof p.catch === 'function') p.catch(() => {}); + } catch (_) {} + }, 400); + } catch (_) {} if (!window.isUpdatingSecurity) { updateSecurityLevel().catch(console.error); } @@ -2361,86 +2882,30 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic updateSecurityLevel().catch(console.error); } } else if (status === 'disconnected') { - updateConnectionState({ status: 'disconnected' }); + // Drop: the manager has cleared its verification (re-establishment + // required), so reset this session's verification flags too — the + // setup/connect screen then shows so the user can reconnect. We do + // NOT remove the session (that create→destroy→create churn flashed + // the console) and we keep the message history; closing a chat is + // done only via the Disconnect button. setConnectionStatus('disconnected'); - - if (shouldPreserveAnswerData()) { - setIsVerified(false); - setShowVerification(false); - return; - } - setIsVerified(false); setShowVerification(false); - - // Dispatch disconnected event for SessionTimer - document.dispatchEvent(new CustomEvent('disconnected')); - - // Clear verification states setLocalVerificationConfirmed(false); setRemoteVerificationConfirmed(false); setBothVerificationsConfirmed(false); - - // Clear connection data - setOfferData(''); - setAnswerData(''); - setOfferInput(''); - setAnswerInput(''); - setShowOfferStep(false); - setShowAnswerStep(false); - setKeyFingerprint(''); - setVerificationCode(''); - setSecurityLevel(null); - - - // Return to main page after a short delay - setTimeout(() => { - setConnectionStatus('disconnected'); - setShowVerification(false); - - setOfferData(''); - setAnswerData(''); - setOfferInput(''); - setAnswerInput(''); - setShowOfferStep(false); - setShowAnswerStep(false); - setMessages([]); - }, 1000); - + if (id === activeIdRef.current) document.dispatchEvent(new CustomEvent('disconnected')); } else if (status === 'peer_disconnected') { + if (id === activeIdRef.current) { setSessionTimeLeft(0); - - document.dispatchEvent(new CustomEvent('peer-disconnect')); - - // A short delay before clearing to display the status - setTimeout(() => { - setKeyFingerprint(''); - setVerificationCode(''); - setSecurityLevel(null); - setIsVerified(false); - setShowVerification(false); - setConnectionStatus('disconnected'); - - // Clear verification states - setLocalVerificationConfirmed(false); - setRemoteVerificationConfirmed(false); - setBothVerificationsConfirmed(false); - - // Clear connection data - setOfferData(''); - setAnswerData(''); - setOfferInput(''); - setAnswerInput(''); - setShowOfferStep(false); - setShowAnswerStep(false); - setMessages([]); - - - if (typeof console.clear === 'function') { - console.clear(); - } - - }, 2000); + document.dispatchEvent(new CustomEvent('peer-disconnect')); + } + setConnectionStatus('peer_disconnected'); + setIsVerified(false); + setShowVerification(false); + setLocalVerificationConfirmed(false); + setRemoteVerificationConfirmed(false); + setBothVerificationsConfirmed(false); } }; @@ -2498,9 +2963,9 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic console.clear(); } - webrtcManagerRef.current = new EnhancedSecureWebRTCManager( - handleMessage, - handleStatusChange, + const manager = new EnhancedSecureWebRTCManager( + handleMessage, + handleStatusChange, handleKeyExchange, handleVerificationRequired, handleAnswerError, @@ -2515,24 +2980,28 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic } } ); + managersRef.current.set(id, manager); // Unsend / delete-for-everyone: peer asked us to drop a message. - webrtcManagerRef.current.onMessageDelete = (mid) => { + manager.onMessageDelete = (mid) => { if (!mid) return; setMessages(prev => prev.filter(m => String(m.mid) !== String(mid))); }; - // Delivery receipt: peer confirmed they got our message → "delivered". - webrtcManagerRef.current.onMessageDelivered = (mid) => { - updateMessageStatus(mid, 'delivered'); + // Read receipt: the peer actually opened the chat and read it → green. + // (A receipt is only sent once the recipient is viewing the conversation, + // so background-read messages stay at two grey 'delivered' ticks.) + manager.onMessageDelivered = (mid) => { + updateMessageStatus(mid, 'read'); }; - // Initialize notification integration if permission was already granted - if (typeof Notification !== 'undefined' && Notification && Notification.permission === 'granted' && window.NotificationIntegration && !notificationIntegrationRef.current) { + // Per-session notification integration (raises OS notifications when the + // tab is hidden; the unread badge handles the in-app, focused case). + if (typeof Notification !== 'undefined' && Notification && Notification.permission === 'granted' && window.NotificationIntegration && !integrationsRef.current.get(id)) { try { - const integration = new window.NotificationIntegration(webrtcManagerRef.current); + const integration = new window.NotificationIntegration(manager); integration.init().then(() => { - notificationIntegrationRef.current = integration; + integrationsRef.current.set(id, integration); }).catch((error) => { // Handle error silently }); @@ -2540,73 +3009,16 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic // Handle error silently } } - - handleMessage(' SecureBit.chat Enhanced Security Edition v4.9.0 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system'); - - const handleBeforeUnload = (event) => { - if (event.type === 'beforeunload' && !isTabSwitching) { - - if (webrtcManagerRef.current && webrtcManagerRef.current.isConnected()) { - try { - webrtcManagerRef.current.sendSystemMessage({ - type: 'peer_disconnect', - reason: 'user_disconnect', - timestamp: Date.now() - }); - } catch (error) { - } - - setTimeout(() => { - if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); - } - }, 100); - } else if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); - } - } else if (isTabSwitching) { - event.preventDefault(); - event.returnValue = ''; - } - }; - - window.addEventListener('beforeunload', handleBeforeUnload); - - let isTabSwitching = false; - let tabSwitchTimeout = null; - - const handleVisibilityChange = () => { - if (document.visibilityState === 'hidden') { - isTabSwitching = true; - - if (tabSwitchTimeout) { - clearTimeout(tabSwitchTimeout); - } - - tabSwitchTimeout = setTimeout(() => { - isTabSwitching = false; - }, 5000); - - } else if (document.visibilityState === 'visible') { - isTabSwitching = false; - - if (tabSwitchTimeout) { - clearTimeout(tabSwitchTimeout); - tabSwitchTimeout = null; - } - } - }; - - document.addEventListener('visibilitychange', handleVisibilityChange); - - // Setup file transfer callbacks - if (webrtcManagerRef.current) { - webrtcManagerRef.current.setFileTransferCallbacks( + + handleMessage(' SecureBit.chat Enhanced Security Edition v4.10.0 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system'); + + // Setup file transfer callbacks (id-bound to THIS session's manager). + manager.setFileTransferCallbacks( // Progress callback (progress) => { console.log('File progress:', progress); }, - + // File received callback — auto-save to disk, no button press needed. (fileData) => { const sizeMb = Math.max(1, Math.round((fileData.fileSize || 0) / (1024 * 1024))); @@ -2622,10 +3034,6 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic setTimeout(() => fileData.revokeObjectURL(url), 15000); }; - // Trigger the download automatically once assembly completes. - // (Plain-string notice — system messages render as text in the - // connection log, so a React button here would show "[object - // Object]". The file panel keeps a manual Download as fallback.) saveToDisk() .then(() => { addMessageWithAutoScroll(`File received & saved: ${fileData.fileName} (${sizeMb} MB)`, 'system'); @@ -2635,7 +3043,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic addMessageWithAutoScroll(`File received: ${fileData.fileName} (${sizeMb} MB). Open the file panel to download it.`, 'system'); }); }, - + // Error callback (error) => { console.error('File transfer error:', error); @@ -2657,23 +3065,130 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic }); } ); - } - - return () => { - window.removeEventListener('beforeunload', handleBeforeUnload); - document.removeEventListener('visibilitychange', handleVisibilityChange); - - if (tabSwitchTimeout) { - clearTimeout(tabSwitchTimeout); - tabSwitchTimeout = null; - } - - if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); - webrtcManagerRef.current = null; - } + + return id; }; - }, []); // Empty dependency array to run only once + + // Keep createSession reachable from non-render callers (new-chat button etc.). + const createSessionRef = React.useRef(createSession); + createSessionRef.current = createSession; + + // Tear down ONE session completely: its manager (existing key-wipe logic), + // its notification integration and its queues. Other sessions are untouched. + // Re-entrancy guarded: mgr.disconnect() can synchronously re-fire the + // 'disconnected' status callback, which would otherwise recurse back in here. + const destroyingRef = React.useRef(new Set()); + const destroySession = React.useCallback((id) => { + if (!id || destroyingRef.current.has(id)) return; + destroyingRef.current.add(id); + try { + const mgr = managersRef.current.get(id); + if (mgr) { try { mgr.disconnect(); } catch (_) {} managersRef.current.delete(id); } + const integ = integrationsRef.current.get(id); + if (integ) { try { integ.cleanup?.(); } catch (_) {} integrationsRef.current.delete(id); } + queuesRef.current.delete(id); + dispatch({ type: SA.REMOVE_SESSION, id }); + } finally { + destroyingRef.current.delete(id); + } + }, []); + + // Always keep at least one session around: when the last chat is removed the + // user lands back on the single-column "Start Secure" page with a fresh session. + React.useEffect(() => { + if (sessionsState.order.length === 0) createSessionRef.current({ role: 'offer' }); + }, [sessionsState.order.length]); + + // ---- Sidebar (session list) state + actions ---- + const [sidebarCollapsed, setSidebarCollapsed] = React.useState(() => { + try { return localStorage.getItem('securebit_sidebar_collapsed') === 'true'; } catch { return false; } + }); + React.useEffect(() => { try { localStorage.setItem('securebit_sidebar_collapsed', String(sidebarCollapsed)); } catch {} }, [sidebarCollapsed]); + const [sidebarDrawerOpen, setSidebarDrawerOpen] = React.useState(false); + const handleSelectSession = React.useCallback((id) => { + dispatch({ type: SA.SET_ACTIVE, id }); + dispatch({ type: SA.CLEAR_UNREAD, id }); + setSidebarDrawerOpen(false); + }, []); + const handleNewChat = React.useCallback(() => { + createSessionRef.current({ role: 'offer' }); + setSidebarDrawerOpen(false); + }, []); + const handleRenameSession = React.useCallback((id, label) => { dispatch({ type: SA.RENAME, id, label }); }, []); + // Send any held-back read receipts for a session (call when the user opens it). + const flushReadAcks = React.useCallback((id) => { + if (!id) return; + const q = queuesRef.current.get(id); + const mgr = managersRef.current.get(id); + if (!q || !mgr || !q.pendingReadAcks || q.pendingReadAcks.length === 0) return; + const acks = q.pendingReadAcks; q.pendingReadAcks = []; + for (const mid of acks) { try { mgr.sendDeliveryReceipt?.(mid); } catch (_) {} } + }, []); + // Opening a session clears its unread badge and releases its read receipts. + React.useEffect(() => { + if (!activeSessionId) return; + dispatch({ type: SA.CLEAR_UNREAD, id: activeSessionId }); + if (typeof document === 'undefined' || document.visibilityState === 'visible') flushReadAcks(activeSessionId); + }, [activeSessionId, flushReadAcks]); + + // App-level lifecycle: create the first session on mount, wire the global + // tab/unload guards, and on unmount disconnect EVERY live manager. + const didInitRef = React.useRef(false); + React.useEffect(() => { + if (didInitRef.current) return; + didInitRef.current = true; + + // (The first session is created by the ensure-at-least-one-session effect, + // which also handles re-landing after the last chat is closed.) + + let isTabSwitching = false; + let tabSwitchTimeout = null; + + const handleBeforeUnload = (event) => { + if (event.type === 'beforeunload' && !isTabSwitching) { + for (const mgr of managersRef.current.values()) { + try { + if (mgr.isConnected && mgr.isConnected()) { + try { mgr.sendSystemMessage({ type: 'peer_disconnect', reason: 'user_disconnect', timestamp: Date.now() }); } catch (_) {} + setTimeout(() => { try { mgr.disconnect(); } catch (_) {} }, 100); + } else { + mgr.disconnect(); + } + } catch (_) {} + } + } else if (isTabSwitching) { + event.preventDefault(); + event.returnValue = ''; + } + }; + + const handleVisibilityChange = () => { + if (document.visibilityState === 'hidden') { + isTabSwitching = true; + if (tabSwitchTimeout) clearTimeout(tabSwitchTimeout); + tabSwitchTimeout = setTimeout(() => { isTabSwitching = false; }, 5000); + } else if (document.visibilityState === 'visible') { + isTabSwitching = false; + if (tabSwitchTimeout) { clearTimeout(tabSwitchTimeout); tabSwitchTimeout = null; } + // Tab regained focus → release held read receipts for the open chat. + flushReadAcks(activeIdRef.current); + } + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + document.addEventListener('visibilitychange', handleVisibilityChange); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + document.removeEventListener('visibilitychange', handleVisibilityChange); + if (tabSwitchTimeout) { clearTimeout(tabSwitchTimeout); tabSwitchTimeout = null; } + for (const mgr of managersRef.current.values()) { try { mgr.disconnect(); } catch (_) {} } + managersRef.current.clear(); + for (const integ of integrationsRef.current.values()) { try { integ.cleanup?.(); } catch (_) {} } + integrationsRef.current.clear(); + queuesRef.current.clear(); + }; + }, []); // run once // All security features are enabled by default - no session purchase needed @@ -2759,6 +3274,11 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic // Animated QR state (for multi-chunk COSE) const qrAnimationRef = React.useRef({ timer: null, chunks: [], idx: 0, active: false }); + // Stop any running QR auto-advance when the active session changes, so frames + // are never pushed into the wrong session's setup slice. + React.useEffect(() => () => { + try { if (qrAnimationRef.current && qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); qrAnimationRef.current.timer = null; } } catch {} + }, [activeSessionId]); const stopQrAnimation = () => { try { if (qrAnimationRef.current.timer) { clearInterval(qrAnimationRef.current.timer); } } catch {} qrAnimationRef.current = { timer: null, chunks: [], idx: 0, active: false }; @@ -3518,7 +4038,7 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic }]); setMessages(prev => [...prev, { - message: '📤 Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.)..', + message: 'Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.).', type: 'system', id: Date.now(), timestamp: Date.now() @@ -3937,10 +4457,6 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic if (!webrtcManagerRef.current) { return; } - - if (!webrtcManagerRef.current.isConnected()) { - return; - } const baseTextEarly = messageInput.trim(); const midEarly = `m_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`; @@ -3963,13 +4479,22 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic const echoOpts = { mid: midEarly, status: 'sent', timestamp: tsOff }; if (disappearTtl > 0) echoOpts.expiresAt = tsOff + disappearTtl * 1000; addMessageWithAutoScroll(outTextOff, 'sent', echoOpts); - outgoingQueueRef.current.push({ outText: outTextOff, meta: metaOff, mid: midEarly }); + // Queue on the ACTIVE session's outgoing queue (created in createSession); + // guard in case the entry is missing so the echo never gets lost. + const q = queuesRef.current.get(activeIdRef.current); + if (q) q.outgoing.push({ outText: outTextOff, meta: metaOff, mid: midEarly }); setMessageInput(''); if (codeMode) setCodeMode(false); if (viewOnceMode) setViewOnceMode(false); return; } + // Online but the channel isn't ready (e.g. dropped/not yet established) — + // can't transmit. The setup screen is shown for re-establishment in that case. + if (!webrtcManagerRef.current.isConnected()) { + return; + } + try { const baseText = baseTextEarly; // Code mode wraps the text in a fenced block so both sides render @@ -3996,7 +4521,10 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic try { await webrtcManagerRef.current.sendMessage(outText, meta); - updateMessageStatus(mid, 'sent'); + // Reliable ordered data channel: a resolved send means the peer's + // device received it → two grey ticks. The peer's read receipt + // (onMessageDelivered) upgrades it to two green ticks ('read'). + updateMessageStatus(mid, 'delivered'); } catch (sendErr) { updateMessageStatus(mid, 'failed'); throw sendErr; @@ -4089,82 +4617,25 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic } }, []); + // Disconnect tears down ONLY the active session — its peerConnection, its + // manager (with the manager's own key-wipe logic) and its notification + // integration. Every other session keeps running untouched. If it was the + // last session we immediately open a fresh blank one so the UI isn't empty. const handleDisconnect = () => { try { + const id = activeIdRef.current; setSessionTimeLeft(0); - - // Mark as user-initiated disconnect - updateConnectionState({ - status: 'disconnected', - isUserInitiatedDisconnect: true - }); - - // Cleanup WebRTC connection - if (webrtcManagerRef.current) { - webrtcManagerRef.current.disconnect(); - } - - // Cleanup notification integration - if (notificationIntegrationRef.current) { - notificationIntegrationRef.current.cleanup(); - notificationIntegrationRef.current = null; - } - - // Clear all connection-related states - setKeyFingerprint(''); - setVerificationCode(''); - setSecurityLevel(null); - setIsVerified(false); - setShowVerification(false); - setConnectionStatus('disconnected'); - - // Clear verification states - setLocalVerificationConfirmed(false); - setRemoteVerificationConfirmed(false); - setBothVerificationsConfirmed(false); - - // Reset UI to initial state - setOfferData(''); - setAnswerData(''); - setOfferInput(''); - setAnswerInput(''); - setShowOfferStep(false); - setShowAnswerStep(false); - setIsGeneratingKeys(false); - setShowQRCode(false); - setQrCodeUrl(''); - setShowQRScanner(false); - setShowQRScannerModal(false); - - // Clear messages - setMessages([]); - setPendingIncomingFiles([]); - - // Clear console - if (typeof console.clear === 'function') { - console.clear(); - } - - // Dispatch disconnect events - document.dispatchEvent(new CustomEvent('peer-disconnect')); - document.dispatchEvent(new CustomEvent('disconnected')); - - // Dispatch session cleanup event - document.dispatchEvent(new CustomEvent('session-cleanup', { - detail: { - timestamp: Date.now(), - reason: 'manual_disconnect' - } - })); - - // Clear data and reset session timer - handleClearData(); - - setTimeout(() => { - setSessionTimeLeft(0); - }, 500); - - console.log('Disconnect completed successfully'); + // Global lifecycle events (consumed by any remaining listeners). + document.dispatchEvent(new CustomEvent('peer-disconnect')); + document.dispatchEvent(new CustomEvent('disconnected')); + document.dispatchEvent(new CustomEvent('session-cleanup', { + detail: { timestamp: Date.now(), reason: 'manual_disconnect' } + })); + // Manual disconnect always wipes this chat's data and removes it; the + // ensure-at-least-one-session effect re-opens the landing if it was the + // last one. Siblings keep running untouched. + destroySession(id); + if (typeof console.clear === 'function') console.clear(); } catch (error) { console.error('Error during disconnect:', error); } @@ -4189,6 +4660,11 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic } }, [connectionStatus, isVerified]); + // Chat view requires an ACTIVE verified connection. On a drop the manager + // clears its verification state (it must be re-established — there is no + // "keep chatting while disconnected" in this P2P design), so we fall back to + // the setup screen, which is the re-establish path. Note: this means a dropped + // chat shows the connect screen; the conversation history stays in the session. const isConnectedAndVerified = (connectionStatus === 'connected' || connectionStatus === 'verified') && isVerified; // The PWA "Install app" pill is a landing-page affordance — hide it once @@ -4306,15 +4782,58 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic } }, [showQRScannerModal]); + const sessionChats = decorateSessions(sessionsState); + // The multi-session chrome (left rail + chat column) appears only once there + // is a genuinely WORKING conversation — a session whose SAS is verified — or + // when more than one session exists. The whole offer/answer/verification flow + // of the first session (which the manager reports as 'connecting'/'verifying') + // stays on the original single-column "Start Secure" screen, exactly as before; + // the rail only shows up after the first secure channel is actually established. + const showSidebar = sessionsState.order.length > 1 || sessionsState.order.some((id) => { + const s = sessionsState.sessions[id]; + return s && s.sas && s.sas.isVerified; + }); + return React.createElement('div', { - className: "minimal-bg min-h-screen" + className: "minimal-bg", + // With the rail visible the app is a fixed-height shell (rail + column + // fill the viewport, design-style). Otherwise it's the scrollable landing. + // flexDirection:'row' is explicit — the .minimal-bg class forces + // flex-direction:column, which would otherwise stack the rail ABOVE the chat. + style: showSidebar ? { display: 'flex', flexDirection: 'row', height: '100vh', width: '100%', overflow: 'hidden' } : { minHeight: '100vh' } }, [ + showSidebar && React.createElement(SessionsSidebar, { + key: 'sessions-sidebar', + chats: sessionChats, + collapsed: sidebarCollapsed, + drawerOpen: sidebarDrawerOpen, + onToggleCollapse: () => setSidebarCollapsed(v => !v), + onSelect: handleSelectSession, + onNewChat: handleNewChat, + onRename: handleRenameSession, + onCloseDrawer: () => setSidebarDrawerOpen(false), + myStatus: myStatus, + onSetStatus: setMyStatus + }), + // Mobile-only hamburger that opens the drawer (hidden on desktop via CSS). + showSidebar && React.createElement('button', { + key: 'sb-burger', + className: 'sb-burger', + onClick: () => setSidebarDrawerOpen(true), + style: { display: 'none', position: 'fixed', top: '13px', left: '13px', zIndex: 55, width: '38px', height: '38px', borderRadius: '10px', placeItems: 'center', border: '1px solid rgba(255,255,255,0.1)', background: 'rgba(18,18,20,0.9)', color: '#cfcfd4', cursor: 'pointer' }, + dangerouslySetInnerHTML: { __html: SB_SVG.burger } + }), + React.createElement('div', { + key: 'app-column', + className: showSidebar ? 'minimal-bg' : 'minimal-bg min-h-screen', + style: showSidebar ? { flex: 1, minWidth: 0, height: '100vh', overflow: 'hidden', display: 'flex', flexDirection: 'column' } : {} + }, [ // Advanced network settings now render inside the connection // screen's right panel (see EnhancedConnectionSetup), matching // the design's slide-up-within-the-right-column behavior. // The verified chat renders its own in-chat header (SecureBit Chat // design); the shared header is shown only on the landing/setup view. - (!isConnectedAndVerified && window.EnhancedMinimalHeader) && React.createElement(window.EnhancedMinimalHeader, { + (!isConnectedAndVerified && !showSidebar && window.EnhancedMinimalHeader) && React.createElement(window.EnhancedMinimalHeader, { key: 'header', status: connectionStatus, fingerprint: keyFingerprint, @@ -4334,6 +4853,10 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic })() ? (() => { return React.createElement(EnhancedChatInterface, { + title: active ? active.peerLabel : '', + isOffline: isOffline, + peerPresence: active ? active.peerPresence : null, + onRenameTitle: (label) => { if (activeSessionId) dispatch({ type: SA.RENAME, id: activeSessionId, label }); }, messages: messages, messageInput: messageInput, setMessageInput: setMessageInput, @@ -4410,7 +4933,10 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic iceSettingsPersisted: iceSettingsPersisted, customIceServers: customIceServers, handleApplyIceSettings: handleApplyIceSettings, - handleForgetIceSettings: handleForgetIceSettings + handleForgetIceSettings: handleForgetIceSettings, + // Render only the create/connect card inside the chat column + // (an additional session), instead of the full landing. + compact: showSidebar }) ), @@ -4518,9 +5044,8 @@ import { loadIceSettings, saveIceSettings, clearIceSettings } from './network/ic ]) ]) ]); - })(), - - + })() + ]) // end app-column ]); }; // UpdateChecker компонент для автоматической проверки обновлений diff --git a/src/state/sessionsStore.js b/src/state/sessionsStore.js new file mode 100644 index 0000000..a0dd60d --- /dev/null +++ b/src/state/sessionsStore.js @@ -0,0 +1,336 @@ +// Sessions registry for SecureBit.chat multi-session support. +// +// Pure, framework-free reducer + helpers. The root React component drives it via +// React.useReducer; non-serializable per-session objects (the EnhancedSecureWebRTCManager +// instance, its NotificationIntegration, offline queues, QR-animation timers) live OUTSIDE +// this state in ref-held Maps keyed by sessionId — never in here, never shared between +// sessions. Every reducer case returns fresh objects for the touched session only, so a +// change to one session can never mutate another (full isolation). +// +// sessionId is LOCAL ONLY (crypto.randomUUID). It is never sent to the peer as identity. + +export const SESSION_ACTIONS = Object.freeze({ + CREATE_SESSION: 'CREATE_SESSION', + REMOVE_SESSION: 'REMOVE_SESSION', + SET_ACTIVE: 'SET_ACTIVE', + SET_STATUS: 'SET_STATUS', + SET_FINGERPRINT: 'SET_FINGERPRINT', + SET_VERIFICATION: 'SET_VERIFICATION', + SET_SAS: 'SET_SAS', + ADD_MESSAGE: 'ADD_MESSAGE', + SET_MESSAGES: 'SET_MESSAGES', + UPDATE_MESSAGE_STATUS: 'UPDATE_MESSAGE_STATUS', + DELETE_MESSAGE: 'DELETE_MESSAGE', + EXPIRE_MESSAGE: 'EXPIRE_MESSAGE', + INCREMENT_UNREAD: 'INCREMENT_UNREAD', + CLEAR_UNREAD: 'CLEAR_UNREAD', + SET_PENDING_FILES: 'SET_PENDING_FILES', + PATCH_SETUP: 'PATCH_SETUP', + RENAME: 'RENAME', + SET_PEER_PRESENCE: 'SET_PEER_PRESENCE' +}); + +// Availability presence the PEER advertises to us (sent E2E over the data channel, never +// stored on a server). 'invisible' is sent on the wire as 'offline' so peers can't tell. +export const PRESENCE_DOT = { available: '#3ecf8e', away: '#e3b341', busy: '#e5727a', offline: '#6b6b73' }; +export const PRESENCE_WORD = { available: 'Available', away: 'Away', busy: 'Busy', offline: 'Offline' }; +// The statuses the local user can pick for themselves (design: Set your status). +export const MY_STATUS_OPTIONS = [ + { key: 'available', word: 'Available', desc: 'Online and reachable', dot: '#3ecf8e' }, + { key: 'away', word: 'Away', desc: 'Idle · stepped away', dot: '#e3b341' }, + { key: 'busy', word: 'Busy', desc: 'Do not disturb', dot: '#e5727a' }, + { key: 'invisible', word: 'Invisible', desc: 'Appear offline to peers', dot: '#6b6b73' } +]; + +// Short, human-friendly default label derived from the local sessionId. Never the peer's +// identity — just something stable to show before the SAS-derived label is available. +export function shortLabelFromId(id) { + const hex = String(id || '').replace(/[^a-z0-9]/gi, ''); + return 'Chat ' + (hex.slice(0, 4) || '0000').toUpperCase(); +} + +// Two-letter monogram for the avatar tile (mirrors the design's `mono()` helper). +export function monoInitials(label) { + const words = String(label || '').trim().split(/\s+/).filter(Boolean); + const a = words[0]?.[0] || ''; + const b = words[1]?.[0] || words[0]?.[1] || ''; + return (a + b).toUpperCase() || '··'; +} + +// Status → dot colour (mirrors the design's DOT map). +export function statusDot(status) { + switch (status) { + case 'connected': + case 'verified': + return '#3ecf8e'; + case 'connecting': + case 'verifying': + case 'new': + return '#e3b341'; + default: + return '#e5727a'; // disconnected / peer_disconnected / lost + } +} + +// Status → header sub-text (mirrors the design's SUB map). +export function statusSub(status) { + switch (status) { + case 'connected': + case 'verified': + return 'P2P · connected'; + case 'verifying': + return 'Verifying…'; + case 'connecting': + case 'new': + return 'Connecting…'; + case 'peer_disconnected': + return 'Peer disconnected'; + default: + return 'Disconnected'; + } +} + +function emptySetup() { + return { + offerData: '', + answerData: '', + offerInput: '', + answerInput: '', + showOfferStep: false, + showAnswerStep: false, + showVerification: false, + showQRCode: false, + qrCodeUrl: '', + isGeneratingKeys: false, + qrFramesTotal: 0, + qrFrameIndex: 0, + qrManualMode: false + }; +} + +export function createSessionEntry(opts = {}) { + const id = opts.id || (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : String(Date.now()) + Math.random()); + return { + id, + peerLabel: opts.peerLabel || shortLabelFromId(id), + labelIsCustom: false, // becomes true once the user renames; blocks SAS auto-relabel + createdAt: opts.createdAt || Date.now(), + role: opts.role || 'offer', // 'offer' | 'answer' + status: opts.status || 'new', + keyFingerprint: '', + verificationCode: '', + sas: { localConfirmed: false, remoteConfirmed: false, bothConfirmed: false, isVerified: false }, + messages: [], + unreadCount: 0, + pendingIncomingFiles: [], + peerPresence: null, // peer's advertised availability ('available'|'away'|'busy'|'offline'); null = unknown + setup: emptySetup() + }; +} + +export function createInitialState() { + return { sessions: {}, order: [], activeSessionId: null }; +} + +// Apply a partial patch to one session, returning a new state with only that session's +// object replaced. Other sessions keep their identity (referential isolation). +function patchSession(state, id, patch) { + const session = state.sessions[id]; + if (!session) return state; + return { + ...state, + sessions: { ...state.sessions, [id]: { ...session, ...patch } } + }; +} + +export function sessionsReducer(state, action) { + const A = SESSION_ACTIONS; + switch (action.type) { + case A.CREATE_SESSION: { + const entry = action.entry || createSessionEntry(action); + if (state.sessions[entry.id]) return state; + return { + sessions: { ...state.sessions, [entry.id]: entry }, + order: [...state.order, entry.id], + activeSessionId: action.activate === false ? state.activeSessionId : entry.id + }; + } + + case A.REMOVE_SESSION: { + const { id } = action; + if (!state.sessions[id]) return state; + const sessions = { ...state.sessions }; + delete sessions[id]; + const order = state.order.filter((x) => x !== id); + let activeSessionId = state.activeSessionId; + if (activeSessionId === id) { + // Re-point to the previous sibling in display order, else the first remaining. + const removedIdx = state.order.indexOf(id); + activeSessionId = order[Math.max(0, removedIdx - 1)] || order[0] || null; + } + return { sessions, order, activeSessionId }; + } + + case A.SET_ACTIVE: { + if (!state.sessions[action.id]) return state; + if (state.activeSessionId === action.id) return state; + return { ...state, activeSessionId: action.id }; + } + + case A.SET_STATUS: { + const session = state.sessions[action.id]; + if (!session || session.status === action.status) return state; // no-op if unchanged + return patchSession(state, action.id, { status: action.status }); + } + + case A.SET_FINGERPRINT: + return patchSession(state, action.id, { keyFingerprint: action.fingerprint }); + + case A.SET_VERIFICATION: + return patchSession(state, action.id, { verificationCode: action.code }); + + case A.SET_SAS: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { sas: { ...session.sas, ...action.sas } }); + } + + case A.ADD_MESSAGE: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { messages: [...session.messages, action.message] }); + } + + case A.SET_MESSAGES: { + const session = state.sessions[action.id]; + if (!session) return state; + const next = typeof action.updater === 'function' + ? action.updater(session.messages) + : action.messages; + return patchSession(state, action.id, { messages: Array.isArray(next) ? next : [] }); + } + + case A.UPDATE_MESSAGE_STATUS: { + const session = state.sessions[action.id]; + if (!session) return state; + let changed = false; + const messages = session.messages.map((m) => { + if (String(m.mid) === String(action.mid) && m.status !== action.status) { + changed = true; + return { ...m, status: action.status }; + } + return m; + }); + return changed ? patchSession(state, action.id, { messages }) : state; + } + + case A.DELETE_MESSAGE: { + const session = state.sessions[action.id]; + if (!session) return state; + const messages = session.messages.filter((m) => String(m.mid) !== String(action.mid)); + if (messages.length === session.messages.length) return state; + return patchSession(state, action.id, { messages }); + } + + case A.EXPIRE_MESSAGE: { + const session = state.sessions[action.id]; + if (!session) return state; + let changed = false; + const messages = session.messages.map((m) => { + if (String(m.id) === String(action.messageId) && !m.expired) { + changed = true; + return { ...m, expired: true, message: '', expiresAt: undefined }; + } + return m; + }); + return changed ? patchSession(state, action.id, { messages }) : state; + } + + case A.INCREMENT_UNREAD: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { unreadCount: session.unreadCount + 1 }); + } + + case A.CLEAR_UNREAD: { + const session = state.sessions[action.id]; + if (!session || session.unreadCount === 0) return state; + return patchSession(state, action.id, { unreadCount: 0 }); + } + + case A.SET_PENDING_FILES: { + const session = state.sessions[action.id]; + if (!session) return state; + const next = typeof action.updater === 'function' + ? action.updater(session.pendingIncomingFiles) + : action.files; + return patchSession(state, action.id, { pendingIncomingFiles: Array.isArray(next) ? next : [] }); + } + + case A.PATCH_SETUP: { + const session = state.sessions[action.id]; + if (!session) return state; + return patchSession(state, action.id, { setup: { ...session.setup, ...action.patch } }); + } + + case A.RENAME: { + const session = state.sessions[action.id]; + if (!session) return state; + const label = String(action.label || '').trim() || session.peerLabel; + return patchSession(state, action.id, { peerLabel: label, labelIsCustom: true }); + } + + case A.SET_PEER_PRESENCE: { + const session = state.sessions[action.id]; + if (!session || session.peerPresence === action.presence) return state; + return patchSession(state, action.id, { peerPresence: action.presence }); + } + + default: + return state; + } +} + +// Decorate a session into the shape the sidebar/header rendering consumes (avatar monogram, +// status dot, sub-text, last-message preview, unread badge). Pure derivation — no state. +export function decorateSession(session, activeSessionId) { + const lastMessage = [...session.messages].reverse().find((m) => !m.expired && typeof m.message === 'string' && m.message.trim()); + const s = session.status; + const isUp = s === 'connected' || s === 'verified'; + const isPending = s === 'connecting' || s === 'verifying' || s === 'new'; + // Avatar dot + sub-text: while a session is up, reflect the PEER's advertised presence; + // otherwise reflect the connection state (amber = connecting, red = dropped). + let dot, headerSub; + if (isPending) { + dot = '#e3b341'; + headerSub = statusSub(s); + } else if (isUp) { + dot = session.peerPresence ? (PRESENCE_DOT[session.peerPresence] || '#6b6b73') : '#3ecf8e'; + headerSub = session.peerPresence ? (PRESENCE_WORD[session.peerPresence] || 'Online') : 'P2P · connected'; + } else { + dot = '#e5727a'; + headerSub = statusSub(s); + } + const preview = lastMessage ? lastMessage.message : headerSub; + return { + id: session.id, + name: session.peerLabel, + mono: monoInitials(session.peerLabel), + dot, + headerSub, + status: session.status, + peerPresence: session.peerPresence, + preview, + unread: session.unreadCount > 0 ? (session.unreadCount > 99 ? '99+' : String(session.unreadCount)) : null, + verified: !!session.sas.isVerified, + active: session.id === activeSessionId, + inactive: session.id !== activeSessionId + }; +} + +export function decorateSessions(state) { + return state.order + .map((id) => state.sessions[id]) + .filter(Boolean) + .map((s) => decorateSession(s, state.activeSessionId)); +} diff --git a/src/utils/updateManager.js b/src/utils/updateManager.js index 2d35ec4..d468f6b 100644 --- a/src/utils/updateManager.js +++ b/src/utils/updateManager.js @@ -155,8 +155,20 @@ class UpdateManager { } const meta = await response.json(); + + // The service worker returns a fallback meta { error: 'Network unavailable', + // version: } when it can't reach the network. That default ("v4.7.56") + // must NOT be treated as a real server version — otherwise a transient drop pops a + // bogus "Update available → v4.7.56". Ignore any error-tagged response. + if (meta && meta.error) { + if (this.options.debug) { + console.warn('⚠️ meta.json came from offline fallback — skipping update check:', meta.error); + } + return { hasUpdate: false, error: meta.error }; + } + this.serverVersion = meta.version || meta.buildVersion || null; - + if (!this.serverVersion) { throw new Error('Version not found in meta.json'); } @@ -249,47 +261,55 @@ class UpdateManager { } this.isUpdating = true; - + + // Step logging (always on) so we can see exactly where an update stalls. + const log = (m) => { try { console.log('🔧 [update] ' + m); } catch (_) {} }; + // Run a cleanup step but never let it block the reload: it still executes fully, we just + // stop AWAITING it past `ms`. This keeps all the security cleanup (SW caches, SW + // unregister, storage wipe) while guaranteeing the page reloads. + const capped = (label, promise, ms) => Promise.race([ + Promise.resolve(promise).then(() => log(label + ' done')).catch((e) => log(label + ' error: ' + (e && e.message))), + new Promise((resolve) => setTimeout(() => { log(label + ' still running after ' + ms + 'ms — continuing'); resolve(); }, ms)) + ]); + const navigate = () => { + log('navigating to new version…'); + try { window.location.href = `${window.location.pathname}?v=${Date.now()}&_update=true`; } + catch (_) { try { window.location.reload(); } catch (__) {} } + }; + try { - if (this.options.debug) { - console.log('🚀 Starting force update...'); - } - + log('start (online=' + (typeof navigator !== 'undefined' ? navigator.onLine : 'n/a') + ')'); + // Step 1: Preserve critical data const preservedData = this.preserveCriticalData(); - - // Step 2: Clear Service Worker caches - await this.clearServiceWorkerCaches(); - - // Step 3: Unregister Service Workers - await this.unregisterServiceWorkers(); - + + // Step 2: Clear Service Worker caches (time-boxed, still runs fully) + await capped('clearServiceWorkerCaches', this.clearServiceWorkerCaches(), 3000); + + // Step 3: Unregister Service Workers (time-boxed, still runs fully) + await capped('unregisterServiceWorkers', this.unregisterServiceWorkers(), 3000); + // Step 4: Clear browser cache (localStorage, sessionStorage) this.clearBrowserCaches(); - + log('browser caches cleared'); + // Step 5: Update version if (this.serverVersion) { this.setLocalVersion(this.serverVersion); } - + // Step 6: Restore critical data this.restoreCriticalData(preservedData); - + // Step 7: Force reload with cache-busting - if (this.options.debug) { - console.log('🔄 Reloading page with new version...'); - } - - // Small delay to complete operations - await new Promise(resolve => setTimeout(resolve, 500)); - - // Reload with full cache bypass - window.location.href = `${window.location.pathname}?v=${Date.now()}&_update=true`; - + await new Promise(resolve => setTimeout(resolve, 300)); + navigate(); + } catch (error) { this.handleError('Force update failed', error); this.isUpdating = false; - throw error; + // The new build loads from the network regardless — never leave the user stuck. + navigate(); } } diff --git a/sw.js b/sw.js index 2f8d34f..95a5ec6 100644 --- a/sw.js +++ b/sw.js @@ -8,6 +8,11 @@ let CACHE_NAME = 'securebit-pwa-v4.7.56'; let STATIC_CACHE = 'securebit-pwa-static-v4.7.56'; let DYNAMIC_CACHE = 'securebit-pwa-dynamic-v4.7.56'; +// Build stamp — rewritten by scripts/post-build.js on every release so this file's +// bytes change each deploy. That is what makes the browser detect a new Service Worker, +// reinstall it, drop stale caches and (via controllerchange) prompt the page to update. +const SW_BUILD_VERSION = '1782446255208'; + // Load version from meta.json on install async function getAppVersion() { try { diff --git a/tests/sessions-reducer.test.mjs b/tests/sessions-reducer.test.mjs new file mode 100644 index 0000000..8ca80d1 --- /dev/null +++ b/tests/sessions-reducer.test.mjs @@ -0,0 +1,139 @@ +// Verifies the multi-session reducer keeps sessions fully isolated: a change to one +// session never mutates another, unread only grows for non-active received traffic, and +// removing a session re-points the active pointer without disturbing siblings. +import assert from 'node:assert/strict'; + +const { + sessionsReducer, + createInitialState, + createSessionEntry, + SESSION_ACTIONS: A, + decorateSession, + monoInitials, + statusDot +} = await import('../src/state/sessionsStore.js'); + +function withTwoSessions() { + let state = createInitialState(); + state = sessionsReducer(state, { type: A.CREATE_SESSION, entry: createSessionEntry({ id: 'a', peerLabel: 'work laptop' }) }); + state = sessionsReducer(state, { type: A.CREATE_SESSION, entry: createSessionEntry({ id: 'b', peerLabel: 'atlas repo' }) }); + return state; +} + +// CREATE_SESSION activates the new session and preserves order. +{ + const state = withTwoSessions(); + assert.deepEqual(state.order, ['a', 'b']); + assert.equal(state.activeSessionId, 'b', 'newest session becomes active'); + assert.equal(Object.keys(state.sessions).length, 2); +} + +// Isolation: mutating session B leaves session A's object referentially untouched. +{ + const before = withTwoSessions(); + const aRef = before.sessions.a; + const after = sessionsReducer(before, { type: A.ADD_MESSAGE, id: 'b', message: { id: 1, message: 'hi', type: 'sent' } }); + assert.equal(after.sessions.a, aRef, 'session A object must be the same reference after editing B'); + assert.equal(after.sessions.b.messages.length, 1); + assert.equal(after.sessions.a.messages.length, 0, 'A transcript untouched'); + // And the original state object was not mutated in place. + assert.equal(before.sessions.b.messages.length, 0, 'reducer is immutable'); +} + +// SET_STATUS / SET_FINGERPRINT / SET_SAS are scoped to one session. +{ + let state = withTwoSessions(); + state = sessionsReducer(state, { type: A.SET_STATUS, id: 'a', status: 'verified' }); + state = sessionsReducer(state, { type: A.SET_SAS, id: 'a', sas: { isVerified: true, bothConfirmed: true } }); + state = sessionsReducer(state, { type: A.SET_FINGERPRINT, id: 'a', fingerprint: 'AB:CD' }); + assert.equal(state.sessions.a.status, 'verified'); + assert.equal(state.sessions.a.sas.isVerified, true); + assert.equal(state.sessions.a.keyFingerprint, 'AB:CD'); + assert.equal(state.sessions.b.status, 'new', 'sibling status untouched'); + assert.equal(state.sessions.b.sas.isVerified, false, 'sibling SAS untouched'); + assert.equal(state.sessions.b.keyFingerprint, '', 'sibling fingerprint untouched'); +} + +// UPDATE_MESSAGE_STATUS and DELETE_MESSAGE only touch the named session/message. +{ + let state = withTwoSessions(); + state = sessionsReducer(state, { type: A.ADD_MESSAGE, id: 'a', message: { id: 1, mid: 'm1', message: 'x', type: 'sent', status: 'sending' } }); + state = sessionsReducer(state, { type: A.UPDATE_MESSAGE_STATUS, id: 'a', mid: 'm1', status: 'delivered' }); + assert.equal(state.sessions.a.messages[0].status, 'delivered'); + state = sessionsReducer(state, { type: A.DELETE_MESSAGE, id: 'a', mid: 'm1' }); + assert.equal(state.sessions.a.messages.length, 0); + assert.equal(state.sessions.b.messages.length, 0); +} + +// Unread bookkeeping. +{ + let state = withTwoSessions(); // active = b + state = sessionsReducer(state, { type: A.INCREMENT_UNREAD, id: 'a' }); + state = sessionsReducer(state, { type: A.INCREMENT_UNREAD, id: 'a' }); + assert.equal(state.sessions.a.unreadCount, 2); + assert.equal(state.sessions.b.unreadCount, 0); + state = sessionsReducer(state, { type: A.SET_ACTIVE, id: 'a' }); + state = sessionsReducer(state, { type: A.CLEAR_UNREAD, id: 'a' }); + assert.equal(state.sessions.a.unreadCount, 0); + assert.equal(state.activeSessionId, 'a'); +} + +// PATCH_SETUP merges, scoped per session. +{ + let state = withTwoSessions(); + state = sessionsReducer(state, { type: A.PATCH_SETUP, id: 'a', patch: { offerData: 'OFFER', showOfferStep: true } }); + assert.equal(state.sessions.a.setup.offerData, 'OFFER'); + assert.equal(state.sessions.a.setup.showOfferStep, true); + assert.equal(state.sessions.a.setup.answerData, '', 'untouched setup field keeps default'); + assert.equal(state.sessions.b.setup.offerData, '', 'sibling setup untouched'); +} + +// RENAME marks the label custom. +{ + let state = withTwoSessions(); + state = sessionsReducer(state, { type: A.RENAME, id: 'a', label: 'Alice' }); + assert.equal(state.sessions.a.peerLabel, 'Alice'); + assert.equal(state.sessions.a.labelIsCustom, true); + assert.equal(state.sessions.b.labelIsCustom, false); +} + +// REMOVE_SESSION re-points active to the previous sibling and leaves the rest intact. +{ + let state = withTwoSessions(); // order [a,b], active b + const bRef = state.sessions.b; + state = sessionsReducer(state, { type: A.SET_ACTIVE, id: 'a' }); + state = sessionsReducer(state, { type: A.REMOVE_SESSION, id: 'a' }); + assert.equal(state.sessions.a, undefined, 'a removed'); + assert.equal(state.sessions.b, bRef, 'sibling b object untouched'); + assert.deepEqual(state.order, ['b']); + assert.equal(state.activeSessionId, 'b', 'active re-pointed to remaining session'); +} + +// REMOVE_SESSION on the last session leaves no active. +{ + let state = createInitialState(); + state = sessionsReducer(state, { type: A.CREATE_SESSION, entry: createSessionEntry({ id: 'solo' }) }); + state = sessionsReducer(state, { type: A.REMOVE_SESSION, id: 'solo' }); + assert.equal(state.activeSessionId, null); + assert.deepEqual(state.order, []); +} + +// Decorators mirror the design helpers. +{ + assert.equal(monoInitials('work laptop'), 'WL'); + assert.equal(monoInitials('atlas'), 'AT'); + assert.equal(statusDot('verified'), '#3ecf8e'); + assert.equal(statusDot('connecting'), '#e3b341'); + assert.equal(statusDot('disconnected'), '#e5727a'); + + const entry = createSessionEntry({ id: 'a', peerLabel: 'work laptop' }); + entry.unreadCount = 3; + entry.status = 'connecting'; + const d = decorateSession(entry, 'b'); + assert.equal(d.mono, 'WL'); + assert.equal(d.unread, '3'); + assert.equal(d.active, false); + assert.equal(d.inactive, true); +} + +console.log('sessions-reducer.test.mjs: all assertions passed');