v6.8.8: added a section to support the project
No files matched your search
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v6.8.8 — Project support section
|
||||||
|
|
||||||
|
Added a section to support the project.
|
||||||
|
|
||||||
## v6.8.6 — Desktop 1.0.4 downloads, and a simpler roadmap
|
## v6.8.6 — Desktop 1.0.4 downloads, and a simpler roadmap
|
||||||
|
|
||||||
The download buttons now give you desktop version 1.0.4, which fetches its relay
|
The download buttons now give you desktop version 1.0.4, which fetches its relay
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
No accounts. No servers storing your messages. No installation required.
|
No accounts. No servers storing your messages. No installation required.
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](CHANGELOG.md)
|
[](CHANGELOG.md)
|
||||||
[](https://snapcraft.io/securebit-chat)
|
[](https://snapcraft.io/securebit-chat)
|
||||||
[](#install-as-an-app)
|
[](#install-as-an-app)
|
||||||
[](#security-model)
|
[](#security-model)
|
||||||
|
|||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/ar/manifest.json">
|
<link rel="manifest" href="/ar/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/ar.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/ar.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="16" cy="16" r="16" fill="#F7931A"/><path fill="#FFF" fill-rule="nonzero" d="M23.189 14.02c.314-2.096-1.283-3.223-3.465-3.975l.708-2.84-1.728-.43-.69 2.765c-.454-.114-.92-.22-1.385-.326l.695-2.783L15.596 6l-.708 2.839c-.376-.086-.746-.17-1.104-.26l.002-.009-2.384-.595-.46 1.846s1.283.294 1.256.312c.7.175.826.638.805 1.006l-.806 3.235c.048.012.11.03.18.057l-.183-.045-1.13 4.532c-.086.212-.303.531-.793.41.018.025-1.256-.313-1.256-.313l-.858 1.978 2.25.561c.418.105.828.215 1.231.318l-.715 2.872 1.727.43.708-2.84c.472.127.93.245 1.378.357l-.706 2.828 1.728.43.715-2.866c2.948.558 5.164.333 6.097-2.333.752-2.146-.037-3.385-1.588-4.192 1.13-.26 1.98-1.003 2.207-2.538zm-3.95 5.538c-.533 2.147-4.148.986-5.32.695l.95-3.805c1.172.293 4.929.872 4.37 3.11zm.535-5.569c-.487 1.953-3.495.96-4.47.717l.86-3.45c.975.243 4.118.696 3.61 2.733z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 953 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="16" cy="16" r="16" fill="#627EEA"/><g fill="#FFF" fill-rule="nonzero"><path fill-opacity=".602" d="M16.498 4v8.87l7.497 3.35z"/><path d="M16.498 4L9 16.22l7.498-3.35z"/><path fill-opacity=".602" d="M16.498 21.968v6.027L24 17.616z"/><path d="M16.498 27.995v-6.028L9 17.616z"/><path fill-opacity=".2" d="M16.498 20.573l7.497-4.353-7.497-3.348z"/><path fill-opacity=".602" d="M9 16.22l7.498 4.353v-7.701z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 525 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37 37" shape-rendering="crispEdges"><path fill="#111114" d="M0 0h7v1h-7zM8 0h2v1h-2zM11 0h2v1h-2zM14 0h1v1h-1zM16 0h1v1h-1zM18 0h1v1h-1zM20 0h6v1h-6zM28 0h1v1h-1zM30 0h7v1h-7zM0 1h1v1h-1zM6 1h1v1h-1zM8 1h1v1h-1zM11 1h1v1h-1zM14 1h2v1h-2zM18 1h2v1h-2zM25 1h3v1h-3zM30 1h1v1h-1zM36 1h1v1h-1zM0 2h1v1h-1zM2 2h3v1h-3zM6 2h1v1h-1zM8 2h1v1h-1zM10 2h2v1h-2zM13 2h1v1h-1zM15 2h1v1h-1zM20 2h1v1h-1zM22 2h1v1h-1zM28 2h1v1h-1zM30 2h1v1h-1zM32 2h3v1h-3zM36 2h1v1h-1zM0 3h1v1h-1zM2 3h3v1h-3zM6 3h1v1h-1zM14 3h1v1h-1zM16 3h8v1h-8zM25 3h4v1h-4zM30 3h1v1h-1zM32 3h3v1h-3zM36 3h1v1h-1zM0 4h1v1h-1zM2 4h3v1h-3zM6 4h1v1h-1zM10 4h2v1h-2zM18 4h7v1h-7zM26 4h1v1h-1zM30 4h1v1h-1zM32 4h3v1h-3zM36 4h1v1h-1zM0 5h1v1h-1zM6 5h1v1h-1zM8 5h3v1h-3zM12 5h1v1h-1zM15 5h4v1h-4zM20 5h1v1h-1zM23 5h1v1h-1zM27 5h2v1h-2zM30 5h1v1h-1zM36 5h1v1h-1zM0 6h7v1h-7zM8 6h1v1h-1zM10 6h1v1h-1zM12 6h1v1h-1zM14 6h1v1h-1zM16 6h1v1h-1zM18 6h1v1h-1zM20 6h1v1h-1zM22 6h1v1h-1zM24 6h1v1h-1zM26 6h1v1h-1zM28 6h1v1h-1zM30 6h7v1h-7zM8 7h1v1h-1zM11 7h3v1h-3zM15 7h2v1h-2zM18 7h1v1h-1zM22 7h1v1h-1zM24 7h1v1h-1zM2 8h3v1h-3zM6 8h1v1h-1zM8 8h3v1h-3zM12 8h1v1h-1zM17 8h1v1h-1zM22 8h1v1h-1zM25 8h7v1h-7zM34 8h3v1h-3zM0 9h1v1h-1zM2 9h2v1h-2zM5 9h1v1h-1zM7 9h3v1h-3zM11 9h3v1h-3zM15 9h3v1h-3zM20 9h2v1h-2zM24 9h1v1h-1zM26 9h1v1h-1zM28 9h2v1h-2zM33 9h3v1h-3zM0 10h4v1h-4zM6 10h2v1h-2zM11 10h2v1h-2zM15 10h3v1h-3zM21 10h1v1h-1zM24 10h2v1h-2zM27 10h1v1h-1zM30 10h2v1h-2zM34 10h3v1h-3zM1 11h3v1h-3zM5 11h1v1h-1zM7 11h1v1h-1zM9 11h2v1h-2zM12 11h2v1h-2zM15 11h4v1h-4zM20 11h2v1h-2zM27 11h1v1h-1zM29 11h2v1h-2zM3 12h2v1h-2zM6 12h1v1h-1zM8 12h1v1h-1zM11 12h1v1h-1zM13 12h2v1h-2zM16 12h1v1h-1zM20 12h2v1h-2zM25 12h2v1h-2zM29 12h6v1h-6zM36 12h1v1h-1zM1 13h2v1h-2zM4 13h2v1h-2zM8 13h1v1h-1zM11 13h9v1h-9zM21 13h1v1h-1zM23 13h4v1h-4zM28 13h1v1h-1zM33 13h1v1h-1zM35 13h1v1h-1zM0 14h3v1h-3zM6 14h4v1h-4zM11 14h3v1h-3zM17 14h2v1h-2zM26 14h3v1h-3zM32 14h5v1h-5zM1 15h2v1h-2zM4 15h1v1h-1zM7 15h1v1h-1zM13 15h4v1h-4zM19 15h1v1h-1zM21 15h3v1h-3zM28 15h1v1h-1zM30 15h2v1h-2zM35 15h1v1h-1zM0 16h7v1h-7zM9 16h2v1h-2zM12 16h2v1h-2zM17 16h3v1h-3zM21 16h1v1h-1zM23 16h1v1h-1zM25 16h12v1h-12zM0 17h1v1h-1zM3 17h3v1h-3zM7 17h1v1h-1zM11 17h2v1h-2zM14 17h1v1h-1zM20 17h1v1h-1zM23 17h1v1h-1zM26 17h1v1h-1zM31 17h1v1h-1zM33 17h1v1h-1zM0 18h1v1h-1zM2 18h1v1h-1zM4 18h1v1h-1zM6 18h1v1h-1zM8 18h2v1h-2zM11 18h1v1h-1zM13 18h1v1h-1zM20 18h2v1h-2zM23 18h3v1h-3zM27 18h4v1h-4zM33 18h1v1h-1zM35 18h2v1h-2zM0 19h4v1h-4zM5 19h1v1h-1zM7 19h1v1h-1zM11 19h4v1h-4zM16 19h2v1h-2zM20 19h2v1h-2zM26 19h2v1h-2zM29 19h2v1h-2zM33 19h1v1h-1zM2 20h2v1h-2zM5 20h3v1h-3zM11 20h2v1h-2zM16 20h3v1h-3zM20 20h2v1h-2zM23 20h4v1h-4zM30 20h3v1h-3zM34 20h1v1h-1zM2 21h2v1h-2zM5 21h1v1h-1zM7 21h3v1h-3zM11 21h3v1h-3zM16 21h1v1h-1zM18 21h2v1h-2zM22 21h2v1h-2zM28 21h2v1h-2zM31 21h1v1h-1zM2 22h1v1h-1zM4 22h1v1h-1zM6 22h2v1h-2zM9 22h2v1h-2zM14 22h3v1h-3zM19 22h7v1h-7zM27 22h2v1h-2zM31 22h1v1h-1zM33 22h1v1h-1zM35 22h2v1h-2zM3 23h1v1h-1zM5 23h1v1h-1zM7 23h1v1h-1zM10 23h2v1h-2zM13 23h2v1h-2zM16 23h1v1h-1zM19 23h1v1h-1zM23 23h1v1h-1zM27 23h4v1h-4zM1 24h2v1h-2zM4 24h4v1h-4zM9 24h1v1h-1zM11 24h2v1h-2zM15 24h3v1h-3zM20 24h2v1h-2zM28 24h5v1h-5zM34 24h1v1h-1zM0 25h1v1h-1zM2 25h1v1h-1zM9 25h1v1h-1zM11 25h2v1h-2zM14 25h1v1h-1zM16 25h1v1h-1zM19 25h2v1h-2zM24 25h2v1h-2zM28 25h1v1h-1zM31 25h1v1h-1zM35 25h1v1h-1zM0 26h1v1h-1zM3 26h4v1h-4zM8 26h1v1h-1zM10 26h1v1h-1zM13 26h3v1h-3zM18 26h1v1h-1zM21 26h1v1h-1zM27 26h3v1h-3zM31 26h1v1h-1zM34 26h1v1h-1zM36 26h1v1h-1zM0 27h1v1h-1zM2 27h1v1h-1zM4 27h1v1h-1zM9 27h1v1h-1zM12 27h4v1h-4zM19 27h1v1h-1zM21 27h2v1h-2zM24 27h1v1h-1zM0 28h1v1h-1zM4 28h4v1h-4zM10 28h4v1h-4zM15 28h4v1h-4zM20 28h1v1h-1zM25 28h2v1h-2zM28 28h9v1h-9zM8 29h2v1h-2zM12 29h1v1h-1zM14 29h6v1h-6zM23 29h1v1h-1zM25 29h1v1h-1zM28 29h1v1h-1zM32 29h2v1h-2zM35 29h1v1h-1zM0 30h7v1h-7zM9 30h2v1h-2zM12 30h1v1h-1zM14 30h1v1h-1zM17 30h4v1h-4zM25 30h1v1h-1zM28 30h1v1h-1zM30 30h1v1h-1zM32 30h1v1h-1zM34 30h1v1h-1zM36 30h1v1h-1zM0 31h1v1h-1zM6 31h1v1h-1zM12 31h6v1h-6zM19 31h2v1h-2zM22 31h2v1h-2zM28 31h1v1h-1zM32 31h2v1h-2zM36 31h1v1h-1zM0 32h1v1h-1zM2 32h3v1h-3zM6 32h1v1h-1zM8 32h1v1h-1zM10 32h3v1h-3zM15 32h3v1h-3zM20 32h2v1h-2zM24 32h9v1h-9zM34 32h2v1h-2zM0 33h1v1h-1zM2 33h3v1h-3zM6 33h1v1h-1zM8 33h7v1h-7zM18 33h1v1h-1zM25 33h1v1h-1zM27 33h4v1h-4zM32 33h2v1h-2zM0 34h1v1h-1zM2 34h3v1h-3zM6 34h1v1h-1zM8 34h2v1h-2zM12 34h1v1h-1zM16 34h1v1h-1zM21 34h4v1h-4zM26 34h2v1h-2zM31 34h1v1h-1zM33 34h1v1h-1zM36 34h1v1h-1zM0 35h1v1h-1zM6 35h1v1h-1zM9 35h2v1h-2zM12 35h3v1h-3zM16 35h1v1h-1zM19 35h2v1h-2zM22 35h2v1h-2zM28 35h1v1h-1zM30 35h2v1h-2zM36 35h1v1h-1zM0 36h7v1h-7zM12 36h1v1h-1zM14 36h1v1h-1zM19 36h1v1h-1zM22 36h5v1h-5zM29 36h1v1h-1zM32 36h1v1h-1zM34 36h3v1h-3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37 37" shape-rendering="crispEdges"><path fill="#111114" d="M0 0h7v1h-7zM8 0h3v1h-3zM16 0h4v1h-4zM22 0h3v1h-3zM27 0h2v1h-2zM30 0h7v1h-7zM0 1h1v1h-1zM6 1h1v1h-1zM8 1h1v1h-1zM11 1h1v1h-1zM15 1h1v1h-1zM17 1h1v1h-1zM19 1h1v1h-1zM22 1h1v1h-1zM25 1h1v1h-1zM27 1h2v1h-2zM30 1h1v1h-1zM36 1h1v1h-1zM0 2h1v1h-1zM2 2h3v1h-3zM6 2h1v1h-1zM8 2h1v1h-1zM11 2h1v1h-1zM13 2h2v1h-2zM16 2h1v1h-1zM18 2h2v1h-2zM21 2h3v1h-3zM26 2h1v1h-1zM28 2h1v1h-1zM30 2h1v1h-1zM32 2h3v1h-3zM36 2h1v1h-1zM0 3h1v1h-1zM2 3h3v1h-3zM6 3h1v1h-1zM9 3h1v1h-1zM13 3h1v1h-1zM15 3h2v1h-2zM21 3h2v1h-2zM25 3h1v1h-1zM27 3h1v1h-1zM30 3h1v1h-1zM32 3h3v1h-3zM36 3h1v1h-1zM0 4h1v1h-1zM2 4h3v1h-3zM6 4h1v1h-1zM17 4h1v1h-1zM19 4h1v1h-1zM23 4h2v1h-2zM26 4h1v1h-1zM28 4h1v1h-1zM30 4h1v1h-1zM32 4h3v1h-3zM36 4h1v1h-1zM0 5h1v1h-1zM6 5h1v1h-1zM8 5h2v1h-2zM11 5h3v1h-3zM16 5h4v1h-4zM21 5h3v1h-3zM25 5h3v1h-3zM30 5h1v1h-1zM36 5h1v1h-1zM0 6h7v1h-7zM8 6h1v1h-1zM10 6h1v1h-1zM12 6h1v1h-1zM14 6h1v1h-1zM16 6h1v1h-1zM18 6h1v1h-1zM20 6h1v1h-1zM22 6h1v1h-1zM24 6h1v1h-1zM26 6h1v1h-1zM28 6h1v1h-1zM30 6h7v1h-7zM8 7h2v1h-2zM11 7h2v1h-2zM15 7h2v1h-2zM19 7h3v1h-3zM28 7h1v1h-1zM2 8h3v1h-3zM6 8h1v1h-1zM8 8h1v1h-1zM12 8h1v1h-1zM14 8h2v1h-2zM17 8h2v1h-2zM24 8h2v1h-2zM27 8h1v1h-1zM29 8h3v1h-3zM34 8h3v1h-3zM0 9h2v1h-2zM3 9h3v1h-3zM7 9h2v1h-2zM14 9h1v1h-1zM18 9h2v1h-2zM22 9h2v1h-2zM26 9h1v1h-1zM28 9h2v1h-2zM36 9h1v1h-1zM1 10h2v1h-2zM5 10h2v1h-2zM8 10h3v1h-3zM13 10h1v1h-1zM17 10h2v1h-2zM20 10h2v1h-2zM23 10h2v1h-2zM27 10h4v1h-4zM32 10h1v1h-1zM35 10h2v1h-2zM4 11h2v1h-2zM7 11h2v1h-2zM11 11h1v1h-1zM13 11h2v1h-2zM20 11h2v1h-2zM24 11h1v1h-1zM26 11h2v1h-2zM30 11h2v1h-2zM33 11h1v1h-1zM0 12h4v1h-4zM5 12h2v1h-2zM8 12h2v1h-2zM11 12h1v1h-1zM15 12h2v1h-2zM18 12h1v1h-1zM23 12h2v1h-2zM26 12h2v1h-2zM29 12h2v1h-2zM34 12h1v1h-1zM36 12h1v1h-1zM1 13h1v1h-1zM3 13h3v1h-3zM10 13h4v1h-4zM19 13h2v1h-2zM22 13h1v1h-1zM28 13h1v1h-1zM31 13h1v1h-1zM33 13h1v1h-1zM36 13h1v1h-1zM0 14h1v1h-1zM6 14h1v1h-1zM10 14h4v1h-4zM15 14h4v1h-4zM20 14h2v1h-2zM23 14h3v1h-3zM27 14h1v1h-1zM30 14h2v1h-2zM33 14h1v1h-1zM35 14h2v1h-2zM0 15h2v1h-2zM5 15h1v1h-1zM8 15h5v1h-5zM17 15h1v1h-1zM19 15h2v1h-2zM22 15h1v1h-1zM26 15h1v1h-1zM29 15h3v1h-3zM35 15h2v1h-2zM1 16h2v1h-2zM5 16h3v1h-3zM9 16h4v1h-4zM16 16h4v1h-4zM22 16h1v1h-1zM25 16h1v1h-1zM27 16h2v1h-2zM30 16h1v1h-1zM32 16h1v1h-1zM34 16h3v1h-3zM0 17h2v1h-2zM5 17h1v1h-1zM7 17h3v1h-3zM13 17h2v1h-2zM16 17h4v1h-4zM26 17h1v1h-1zM29 17h1v1h-1zM31 17h1v1h-1zM33 17h1v1h-1zM35 17h1v1h-1zM1 18h2v1h-2zM4 18h1v1h-1zM6 18h4v1h-4zM11 18h2v1h-2zM16 18h1v1h-1zM18 18h4v1h-4zM23 18h1v1h-1zM27 18h4v1h-4zM32 18h1v1h-1zM34 18h1v1h-1zM36 18h1v1h-1zM0 19h1v1h-1zM3 19h3v1h-3zM7 19h1v1h-1zM11 19h4v1h-4zM17 19h1v1h-1zM21 19h4v1h-4zM26 19h3v1h-3zM31 19h1v1h-1zM33 19h1v1h-1zM0 20h2v1h-2zM3 20h4v1h-4zM8 20h1v1h-1zM11 20h1v1h-1zM18 20h10v1h-10zM29 20h2v1h-2zM34 20h1v1h-1zM0 21h1v1h-1zM2 21h2v1h-2zM9 21h1v1h-1zM13 21h2v1h-2zM17 21h2v1h-2zM23 21h1v1h-1zM28 21h1v1h-1zM31 21h1v1h-1zM33 21h1v1h-1zM35 21h1v1h-1zM1 22h1v1h-1zM3 22h4v1h-4zM8 22h2v1h-2zM11 22h3v1h-3zM15 22h1v1h-1zM18 22h2v1h-2zM23 22h5v1h-5zM30 22h3v1h-3zM36 22h1v1h-1zM1 23h1v1h-1zM4 23h1v1h-1zM8 23h2v1h-2zM11 23h2v1h-2zM14 23h4v1h-4zM20 23h1v1h-1zM24 23h1v1h-1zM26 23h5v1h-5zM33 23h1v1h-1zM35 23h1v1h-1zM1 24h1v1h-1zM3 24h4v1h-4zM8 24h1v1h-1zM10 24h2v1h-2zM13 24h3v1h-3zM18 24h2v1h-2zM23 24h1v1h-1zM27 24h1v1h-1zM29 24h2v1h-2zM32 24h1v1h-1zM34 24h3v1h-3zM0 25h2v1h-2zM5 25h1v1h-1zM7 25h3v1h-3zM11 25h3v1h-3zM16 25h1v1h-1zM21 25h2v1h-2zM24 25h1v1h-1zM27 25h2v1h-2zM31 25h1v1h-1zM33 25h1v1h-1zM35 25h1v1h-1zM0 26h1v1h-1zM2 26h1v1h-1zM6 26h1v1h-1zM8 26h1v1h-1zM10 26h1v1h-1zM12 26h1v1h-1zM15 26h6v1h-6zM22 26h1v1h-1zM26 26h2v1h-2zM29 26h2v1h-2zM32 26h1v1h-1zM36 26h1v1h-1zM0 27h1v1h-1zM2 27h3v1h-3zM9 27h1v1h-1zM12 27h3v1h-3zM16 27h1v1h-1zM18 27h1v1h-1zM24 27h1v1h-1zM27 27h2v1h-2zM36 27h1v1h-1zM0 28h1v1h-1zM2 28h1v1h-1zM4 28h1v1h-1zM6 28h1v1h-1zM13 28h2v1h-2zM16 28h1v1h-1zM19 28h1v1h-1zM21 28h2v1h-2zM25 28h1v1h-1zM27 28h10v1h-10zM8 29h1v1h-1zM14 29h2v1h-2zM17 29h1v1h-1zM19 29h1v1h-1zM22 29h3v1h-3zM26 29h3v1h-3zM32 29h3v1h-3zM36 29h1v1h-1zM0 30h7v1h-7zM9 30h1v1h-1zM14 30h6v1h-6zM24 30h2v1h-2zM28 30h1v1h-1zM30 30h1v1h-1zM32 30h2v1h-2zM35 30h2v1h-2zM0 31h1v1h-1zM6 31h1v1h-1zM13 31h1v1h-1zM16 31h1v1h-1zM19 31h1v1h-1zM21 31h4v1h-4zM26 31h1v1h-1zM28 31h1v1h-1zM32 31h1v1h-1zM36 31h1v1h-1zM0 32h1v1h-1zM2 32h3v1h-3zM6 32h1v1h-1zM8 32h4v1h-4zM14 32h3v1h-3zM19 32h1v1h-1zM21 32h1v1h-1zM24 32h10v1h-10zM35 32h2v1h-2zM0 33h1v1h-1zM2 33h3v1h-3zM6 33h1v1h-1zM8 33h3v1h-3zM12 33h1v1h-1zM14 33h1v1h-1zM18 33h3v1h-3zM29 33h2v1h-2zM33 33h1v1h-1zM0 34h1v1h-1zM2 34h3v1h-3zM6 34h1v1h-1zM8 34h1v1h-1zM12 34h1v1h-1zM15 34h1v1h-1zM18 34h1v1h-1zM20 34h1v1h-1zM22 34h4v1h-4zM27 34h1v1h-1zM30 34h2v1h-2zM36 34h1v1h-1zM0 35h1v1h-1zM6 35h1v1h-1zM12 35h1v1h-1zM15 35h2v1h-2zM18 35h1v1h-1zM20 35h4v1h-4zM26 35h1v1h-1zM28 35h5v1h-5zM36 35h1v1h-1zM0 36h7v1h-7zM10 36h5v1h-5zM16 36h1v1h-1zM19 36h1v1h-1zM22 36h6v1h-6zM31 36h1v1h-1zM33 36h4v1h-4z"/></svg>
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33 33" shape-rendering="crispEdges"><path fill="#111114" d="M0 0h7v1h-7zM9 0h1v1h-1zM11 0h1v1h-1zM14 0h2v1h-2zM17 0h1v1h-1zM21 0h2v1h-2zM26 0h7v1h-7zM0 1h1v1h-1zM6 1h1v1h-1zM8 1h1v1h-1zM10 1h1v1h-1zM12 1h2v1h-2zM15 1h1v1h-1zM17 1h1v1h-1zM19 1h2v1h-2zM24 1h1v1h-1zM26 1h1v1h-1zM32 1h1v1h-1zM0 2h1v1h-1zM2 2h3v1h-3zM6 2h1v1h-1zM8 2h1v1h-1zM10 2h3v1h-3zM14 2h1v1h-1zM17 2h2v1h-2zM21 2h1v1h-1zM26 2h1v1h-1zM28 2h3v1h-3zM32 2h1v1h-1zM0 3h1v1h-1zM2 3h3v1h-3zM6 3h1v1h-1zM8 3h2v1h-2zM12 3h1v1h-1zM14 3h2v1h-2zM18 3h2v1h-2zM21 3h4v1h-4zM26 3h1v1h-1zM28 3h3v1h-3zM32 3h1v1h-1zM0 4h1v1h-1zM2 4h3v1h-3zM6 4h1v1h-1zM8 4h1v1h-1zM10 4h1v1h-1zM12 4h1v1h-1zM14 4h3v1h-3zM19 4h2v1h-2zM23 4h1v1h-1zM26 4h1v1h-1zM28 4h3v1h-3zM32 4h1v1h-1zM0 5h1v1h-1zM6 5h1v1h-1zM8 5h2v1h-2zM11 5h4v1h-4zM17 5h2v1h-2zM20 5h2v1h-2zM24 5h1v1h-1zM26 5h1v1h-1zM32 5h1v1h-1zM0 6h7v1h-7zM8 6h1v1h-1zM10 6h1v1h-1zM12 6h1v1h-1zM14 6h1v1h-1zM16 6h1v1h-1zM18 6h1v1h-1zM20 6h1v1h-1zM22 6h1v1h-1zM24 6h1v1h-1zM26 6h7v1h-7zM9 7h6v1h-6zM17 7h1v1h-1zM20 7h1v1h-1zM2 8h1v1h-1zM5 8h5v1h-5zM11 8h1v1h-1zM16 8h1v1h-1zM18 8h1v1h-1zM23 8h3v1h-3zM27 8h5v1h-5zM1 9h1v1h-1zM3 9h1v1h-1zM5 9h1v1h-1zM7 9h1v1h-1zM9 9h2v1h-2zM14 9h2v1h-2zM17 9h1v1h-1zM19 9h1v1h-1zM21 9h1v1h-1zM24 9h4v1h-4zM30 9h1v1h-1zM0 10h3v1h-3zM4 10h3v1h-3zM8 10h4v1h-4zM14 10h2v1h-2zM18 10h6v1h-6zM25 10h1v1h-1zM30 10h1v1h-1zM32 10h1v1h-1zM2 11h4v1h-4zM7 11h1v1h-1zM9 11h1v1h-1zM11 11h2v1h-2zM14 11h2v1h-2zM17 11h4v1h-4zM22 11h1v1h-1zM24 11h1v1h-1zM26 11h4v1h-4zM31 11h1v1h-1zM0 12h3v1h-3zM5 12h3v1h-3zM12 12h2v1h-2zM15 12h1v1h-1zM19 12h2v1h-2zM23 12h1v1h-1zM26 12h1v1h-1zM28 12h1v1h-1zM32 12h1v1h-1zM0 13h1v1h-1zM2 13h1v1h-1zM11 13h1v1h-1zM13 13h1v1h-1zM17 13h1v1h-1zM19 13h2v1h-2zM22 13h1v1h-1zM24 13h1v1h-1zM27 13h1v1h-1zM29 13h1v1h-1zM31 13h1v1h-1zM1 14h2v1h-2zM4 14h1v1h-1zM6 14h1v1h-1zM10 14h2v1h-2zM14 14h3v1h-3zM18 14h1v1h-1zM20 14h1v1h-1zM22 14h2v1h-2zM25 14h1v1h-1zM27 14h3v1h-3zM32 14h1v1h-1zM0 15h2v1h-2zM3 15h2v1h-2zM7 15h3v1h-3zM11 15h1v1h-1zM13 15h1v1h-1zM16 15h1v1h-1zM22 15h1v1h-1zM29 15h1v1h-1zM32 15h1v1h-1zM0 16h1v1h-1zM3 16h1v1h-1zM6 16h3v1h-3zM10 16h1v1h-1zM14 16h2v1h-2zM17 16h2v1h-2zM23 16h2v1h-2zM26 16h2v1h-2zM31 16h2v1h-2zM0 17h1v1h-1zM2 17h2v1h-2zM9 17h2v1h-2zM12 17h4v1h-4zM17 17h3v1h-3zM23 17h2v1h-2zM26 17h2v1h-2zM29 17h1v1h-1zM31 17h2v1h-2zM0 18h3v1h-3zM4 18h4v1h-4zM9 18h1v1h-1zM12 18h3v1h-3zM17 18h4v1h-4zM23 18h3v1h-3zM28 18h3v1h-3zM32 18h1v1h-1zM1 19h2v1h-2zM5 19h1v1h-1zM8 19h2v1h-2zM11 19h1v1h-1zM14 19h3v1h-3zM20 19h1v1h-1zM24 19h1v1h-1zM27 19h1v1h-1zM29 19h1v1h-1zM31 19h1v1h-1zM1 20h1v1h-1zM3 20h1v1h-1zM5 20h2v1h-2zM8 20h2v1h-2zM13 20h2v1h-2zM16 20h2v1h-2zM21 20h1v1h-1zM23 20h1v1h-1zM26 20h5v1h-5zM2 21h2v1h-2zM5 21h1v1h-1zM7 21h9v1h-9zM17 21h4v1h-4zM22 21h1v1h-1zM24 21h1v1h-1zM27 21h1v1h-1zM29 21h1v1h-1zM31 21h1v1h-1zM0 22h2v1h-2zM3 22h1v1h-1zM6 22h5v1h-5zM15 22h3v1h-3zM19 22h1v1h-1zM21 22h1v1h-1zM23 22h4v1h-4zM30 22h1v1h-1zM32 22h1v1h-1zM5 23h1v1h-1zM7 23h5v1h-5zM15 23h2v1h-2zM18 23h1v1h-1zM20 23h1v1h-1zM23 23h2v1h-2zM27 23h1v1h-1zM29 23h1v1h-1zM31 23h1v1h-1zM0 24h2v1h-2zM4 24h5v1h-5zM10 24h1v1h-1zM16 24h1v1h-1zM18 24h2v1h-2zM21 24h1v1h-1zM24 24h5v1h-5zM31 24h2v1h-2zM8 25h1v1h-1zM10 25h1v1h-1zM17 25h2v1h-2zM20 25h1v1h-1zM23 25h2v1h-2zM28 25h1v1h-1zM31 25h2v1h-2zM0 26h7v1h-7zM8 26h1v1h-1zM10 26h12v1h-12zM23 26h2v1h-2zM26 26h1v1h-1zM28 26h1v1h-1zM31 26h2v1h-2zM0 27h1v1h-1zM6 27h1v1h-1zM8 27h2v1h-2zM14 27h2v1h-2zM18 27h3v1h-3zM22 27h3v1h-3zM28 27h2v1h-2zM31 27h2v1h-2zM0 28h1v1h-1zM2 28h3v1h-3zM6 28h1v1h-1zM9 28h1v1h-1zM14 28h3v1h-3zM18 28h1v1h-1zM20 28h9v1h-9zM0 29h1v1h-1zM2 29h3v1h-3zM6 29h1v1h-1zM10 29h2v1h-2zM13 29h1v1h-1zM15 29h3v1h-3zM20 29h1v1h-1zM24 29h2v1h-2zM28 29h1v1h-1zM30 29h1v1h-1zM32 29h1v1h-1zM0 30h1v1h-1zM2 30h3v1h-3zM6 30h1v1h-1zM8 30h1v1h-1zM10 30h4v1h-4zM15 30h1v1h-1zM19 30h5v1h-5zM25 30h2v1h-2zM28 30h2v1h-2zM31 30h2v1h-2zM0 31h1v1h-1zM6 31h1v1h-1zM10 31h2v1h-2zM13 31h1v1h-1zM15 31h1v1h-1zM19 31h2v1h-2zM23 31h2v1h-2zM27 31h1v1h-1zM29 31h1v1h-1zM0 32h7v1h-7zM18 32h1v1h-1zM23 32h3v1h-3zM30 32h1v1h-1zM32 32h1v1h-1z"/></svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><g fill="none"><circle fill="#EF0027" cx="16" cy="16" r="16"/><path d="M21.932 9.913L7.5 7.257l7.595 19.112 10.583-12.894-3.746-3.562zm-.232 1.17l2.208 2.099-6.038 1.093 3.83-3.192zm-5.142 2.973l-6.364-5.278 10.402 1.914-4.038 3.364zm-.453.934l-1.038 8.58L9.472 9.487l6.633 5.502zm.96.455l6.687-1.21-7.67 9.343.983-8.133z" fill="#FFF"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 429 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="16" cy="16" r="16" fill="#26A17B"/><path fill="#FFF" d="M17.922 17.383v-.002c-.11.008-.677.042-1.942.042-1.01 0-1.721-.03-1.971-.042v.003c-3.888-.171-6.79-.848-6.79-1.658 0-.809 2.902-1.486 6.79-1.66v2.644c.254.018.982.061 1.988.061 1.207 0 1.812-.05 1.925-.06v-2.643c3.88.173 6.775.85 6.775 1.658 0 .81-2.895 1.485-6.775 1.657m0-3.59v-2.366h5.414V7.819H8.595v3.608h5.414v2.365c-4.4.202-7.709 1.074-7.709 2.118 0 1.044 3.309 1.915 7.709 2.118v7.582h3.913v-7.584c4.393-.202 7.694-1.073 7.694-2.116 0-1.043-3.301-1.914-7.694-2.117"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 648 B |
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/de/manifest.json">
|
<link rel="manifest" href="/de/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/de.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/de.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
var en=Object.create;var fi=Object.defineProperty;var tn=Object.getOwnPropertyDescriptor;var rn=Object.getOwnPropertyNames;var sn=Object.getPrototypeOf,nn=Object.prototype.hasOwnProperty;var pi=(n,e)=>()=>{try{return e||n((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}};var an=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of rn(e))!nn.call(n,i)&&i!==t&&fi(n,i,{get:()=>e[i],enumerable:!(r=tn(e,i))||r.enumerable});return n};var yi=(n,e,t)=>(t=n!=null?en(sn(n)):{},an(e||!n||!n.__esModule?fi(t,"default",{value:n,enumerable:!0}):t,n));var Cs=pi((xo,qt)=>{var wt=class{constructor(e={}){this.permission=typeof Notification<"u"&&Notification&&typeof Notification.permission=="string"?Notification.permission:"denied",this.isTabActive=this.checkTabActive(),this.unreadCount=0,this.originalTitle=document.title,this.notificationQueue=[],this.maxQueueSize=e.maxQueueSize||5,this.rateLimitMs=e.rateLimitMs||2e3,this.lastNotificationTime=0,this.trustedOrigins=e.trustedOrigins||[],this.isSecureContext=window.isSecureContext,this.hidden=this.getHiddenProperty(),this.visibilityChange=this.getVisibilityChangeEvent(),this.initVisibilityTracking(),this.initSecurityChecks()}initSecurityChecks(){}getHiddenProperty(){return typeof document.hidden<"u"?"hidden":typeof document.msHidden<"u"?"msHidden":typeof document.webkitHidden<"u"?"webkitHidden":"hidden"}getVisibilityChangeEvent(){return typeof document.hidden<"u"?"visibilitychange":typeof document.msHidden<"u"?"msvisibilitychange":typeof document.webkitHidden<"u"?"webkitvisibilitychange":"visibilitychange"}checkTabActive(){return this.hidden&&typeof document[this.hidden]<"u"?!document[this.hidden]:typeof document.hasFocus=="function"?document.hasFocus():!0}initVisibilityTracking(){typeof document.addEventListener<"u"&&typeof document[this.hidden]<"u"&&document.addEventListener(this.visibilityChange,()=>{this.isTabActive=this.checkTabActive(),this.isTabActive&&(this.resetUnreadCount(),this.clearNotificationQueue())}),window.addEventListener("focus",()=>{this.isTabActive=this.checkTabActive(),this.isTabActive&&this.resetUnreadCount()}),window.addEventListener("blur",()=>{this.isTabActive=this.checkTabActive()}),window.addEventListener("beforeunload",()=>{this.clearNotificationQueue()})}async requestPermission(){if(!this.isSecureContext||!("Notification"in window))return!1;if(this.permission==="granted")return!0;if(this.permission==="denied")return!1;try{return this.permission=await Notification.requestPermission(),this.permission==="granted"}catch{return!1}}updateTitle(){this.unreadCount>0?document.title=`(${this.unreadCount}) ${this.originalTitle}`:document.title=this.originalTitle}sanitizeText(e){if(typeof e!="string")return"";let t=document.createElement("div");return t.textContent=e,t.innerHTML.replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").substring(0,500)}validateIconUrl(e){if(!e)return null;try{let t=new URL(e,window.location.origin);return t.protocol==="https:"||t.protocol==="data:"?this.trustedOrigins.length>0?this.trustedOrigins.some(i=>t.origin===i)?t.href:null:t.href:null}catch{return null}}checkRateLimit(){let e=Date.now();return e-this.lastNotificationTime<this.rateLimitMs?!1:(this.lastNotificationTime=e,!0)}notify(e,t,r={}){if(typeof Notification>"u"||(this.isTabActive=this.checkTabActive(),this.isTabActive)||this.permission!=="granted"||!this.checkRateLimit())return null;let i=this.sanitizeText(e||"Unknown"),s=this.sanitizeText(t||""),a=this.validateIconUrl(r.icon)||"/logo/icon-192x192.png";this.notificationQueue.length>=this.maxQueueSize&&this.clearNotificationQueue();try{let o=new Notification(`${i}`,{body:s.substring(0,200),icon:a,badge:a,tag:`chat-${r.senderId||"unknown"}`,requireInteraction:!1,silent:r.silent||!1,vibrate:navigator.vibrate?[200,100,200]:void 0,data:{senderId:this.sanitizeText(r.senderId),timestamp:Date.now()}});this.unreadCount++,this.updateTitle(),this.notificationQueue.push(o),o.onclick=d=>{if(d.preventDefault(),window.focus(),o.close(),typeof r.onClick=="function")try{r.onClick(r.senderId)}catch(u){console.error("[Notifications] Error in onClick handler:",u)}},o.onerror=d=>{console.error("[Notifications] Error showing notification:",d)};let c=Math.min(r.autoClose||5e3,1e4);return setTimeout(()=>{o.close(),this.removeFromQueue(o)},c),o}catch(o){return console.error("[Notifications] Failed to create notification:",o),null}}removeFromQueue(e){let t=this.notificationQueue.indexOf(e);t>-1&&this.notificationQueue.splice(t,1)}clearNotificationQueue(){this.notificationQueue.forEach(e=>{try{e.close()}catch{}}),this.notificationQueue=[]}resetUnreadCount(){this.unreadCount=0,this.updateTitle()}getStatus(){return{permission:this.permission,isTabActive:this.isTabActive,unreadCount:this.unreadCount,isSecureContext:this.isSecureContext,queueSize:this.notificationQueue.length}}},jt=class{constructor(){this.notificationManager=new wt({maxQueueSize:5,rateLimitMs:2e3,trustedOrigins:[window.Line truncated
|
var os=Object.create;var bi=Object.defineProperty;var cs=Object.getOwnPropertyDescriptor;var ls=Object.getOwnPropertyNames;var ds=Object.getPrototypeOf,us=Object.prototype.hasOwnProperty;var _i=(s,e)=>()=>{try{return e||s((e={exports:{}}).exports,e),e.exports}catch(t){throw e=0,t}};var hs=(s,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ls(e))!us.call(s,i)&&i!==t&&bi(s,i,{get:()=>e[i],enumerable:!(r=cs(e,i))||r.enumerable});return s};var wi=(s,e,t)=>(t=s!=null?os(ds(s)):{},hs(e||!s||!s.__esModule?bi(t,"default",{value:s,enumerable:!0}):t,s));var Ln=_i((Po,Wt)=>{var wt=class{constructor(e={}){this.permission=typeof Notification<"u"&&Notification&&typeof Notification.permission=="string"?Notification.permission:"denied",this.isTabActive=this.checkTabActive(),this.unreadCount=0,this.originalTitle=document.title,this.notificationQueue=[],this.maxQueueSize=e.maxQueueSize||5,this.rateLimitMs=e.rateLimitMs||2e3,this.lastNotificationTime=0,this.trustedOrigins=e.trustedOrigins||[],this.isSecureContext=window.isSecureContext,this.hidden=this.getHiddenProperty(),this.visibilityChange=this.getVisibilityChangeEvent(),this.initVisibilityTracking(),this.initSecurityChecks()}initSecurityChecks(){}getHiddenProperty(){return typeof document.hidden<"u"?"hidden":typeof document.msHidden<"u"?"msHidden":typeof document.webkitHidden<"u"?"webkitHidden":"hidden"}getVisibilityChangeEvent(){return typeof document.hidden<"u"?"visibilitychange":typeof document.msHidden<"u"?"msvisibilitychange":typeof document.webkitHidden<"u"?"webkitvisibilitychange":"visibilitychange"}checkTabActive(){return this.hidden&&typeof document[this.hidden]<"u"?!document[this.hidden]:typeof document.hasFocus=="function"?document.hasFocus():!0}initVisibilityTracking(){typeof document.addEventListener<"u"&&typeof document[this.hidden]<"u"&&document.addEventListener(this.visibilityChange,()=>{this.isTabActive=this.checkTabActive(),this.isTabActive&&(this.resetUnreadCount(),this.clearNotificationQueue())}),window.addEventListener("focus",()=>{this.isTabActive=this.checkTabActive(),this.isTabActive&&this.resetUnreadCount()}),window.addEventListener("blur",()=>{this.isTabActive=this.checkTabActive()}),window.addEventListener("beforeunload",()=>{this.clearNotificationQueue()})}async requestPermission(){if(!this.isSecureContext||!("Notification"in window))return!1;if(this.permission==="granted")return!0;if(this.permission==="denied")return!1;try{return this.permission=await Notification.requestPermission(),this.permission==="granted"}catch{return!1}}updateTitle(){this.unreadCount>0?document.title=`(${this.unreadCount}) ${this.originalTitle}`:document.title=this.originalTitle}sanitizeText(e){if(typeof e!="string")return"";let t=document.createElement("div");return t.textContent=e,t.innerHTML.replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").substring(0,500)}validateIconUrl(e){if(!e)return null;try{let t=new URL(e,window.location.origin);return t.protocol==="https:"||t.protocol==="data:"?this.trustedOrigins.length>0?this.trustedOrigins.some(i=>t.origin===i)?t.href:null:t.href:null}catch{return null}}checkRateLimit(){let e=Date.now();return e-this.lastNotificationTime<this.rateLimitMs?!1:(this.lastNotificationTime=e,!0)}notify(e,t,r={}){if(typeof Notification>"u"||(this.isTabActive=this.checkTabActive(),this.isTabActive)||this.permission!=="granted"||!this.checkRateLimit())return null;let i=this.sanitizeText(e||"Unknown"),n=this.sanitizeText(t||""),a=this.validateIconUrl(r.icon)||"/logo/icon-192x192.png";this.notificationQueue.length>=this.maxQueueSize&&this.clearNotificationQueue();try{let o=new Notification(`${i}`,{body:n.substring(0,200),icon:a,badge:a,tag:`chat-${r.senderId||"unknown"}`,requireInteraction:!1,silent:r.silent||!1,vibrate:navigator.vibrate?[200,100,200]:void 0,data:{senderId:this.sanitizeText(r.senderId),timestamp:Date.now()}});this.unreadCount++,this.updateTitle(),this.notificationQueue.push(o),o.onclick=d=>{if(d.preventDefault(),window.focus(),o.close(),typeof r.onClick=="function")try{r.onClick(r.senderId)}catch(u){console.error("[Notifications] Error in onClick handler:",u)}},o.onerror=d=>{console.error("[Notifications] Error showing notification:",d)};let c=Math.min(r.autoClose||5e3,1e4);return setTimeout(()=>{o.close(),this.removeFromQueue(o)},c),o}catch(o){return console.error("[Notifications] Failed to create notification:",o),null}}removeFromQueue(e){let t=this.notificationQueue.indexOf(e);t>-1&&this.notificationQueue.splice(t,1)}clearNotificationQueue(){this.notificationQueue.forEach(e=>{try{e.close()}catch{}}),this.notificationQueue=[]}resetUnreadCount(){this.unreadCount=0,this.updateTitle()}getStatus(){return{permission:this.permission,isTabActive:this.isTabActive,unreadCount:this.unreadCount,isSecureContext:this.isSecureContext,queueSize:this.notificationQueue.length}}},Yt=class{constructor(){this.notificationManager=new wt({maxQueueSize:5,rateLimitMs:2e3,trustedOrigins:[window.Line truncated
|
||||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function dn(n,e){return on(n)||cn(n,e)||un(n,e)||ln()}function un(n,e){if(n){if(typeof n=="string")return gi(n,e);var t={}.toString.call(n).slice(8,-1);return t==="Object"&&n.constructor&&(t=n.constructor.name),t==="Map"||t==="Set"?Array.from(n):t==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?gi(n,e):void 0}}var Ri=Object.entries,mi=Object.setPrototypeOf,hn=Object.isFrozen,fn=Object.getPrototypeOf,pn=Object.getOwnPropertyDescriptor,ne=Object.freeze,ae=Object.seal,rt=Object.create,Mi=typeof Reflect<"u"&&Reflect,_r=Mi.apply,br=Mi.construct;ne||(ne=function(e){return e});ae||(ae=function(e){return e});_r||(_r=function(e,t){for(var r=arguments.length,i=new Array(r>2?r-2:0),s=2;s<r;s++)i[s-2]=arguments[s];return e.apply(t,i)});br||(br=function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i<t;i++)r[i-1]=arguments[i];return new e(...r)});var ht=te(Array.prototype.forEach),yn=te(Array.prototype.lastIndexOf),Si=te(Array.prototype.pop),tt=te(Array.prototype.push),gn=te(Array.prototype.splice),Fe=Array.isArray,yt=te(String.prototype.toLowerCase),pr=te(String.prototype.toString),_i=te(String.prototype.match),ft=te(String.prototype.replace),bi=te(String.prototype.indexOf),mn=te(String.prototype.trim),Sn=te(Number.prototype.toString),_n=te(Boolean.prototype.toString),wi=typeof BigInt>"u"?null:te(BigInt.prototype.toString),Ei=typeof Symbol>"u"?null:te(Symbol.prototype.toString),he=te(Object.prototype.hasOwnProperty),pt=te(Object.prototype.toString),se=te(RegExp.prototype.test),$e=bn(TypeError);function te(n){return function(e){e instanceof RegExp&&(e.lastIndex=0);for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i<t;i++)r[i-1]=arguments[i];return _r(n,e,r)}}function bn(n){return function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return br(n,t)}}function V(n,e){let t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:yt;if(mi&&mi(n,null),!Fe(e))return n;let r=e.length;for(;r--;){let i=e[r];if(typeof i=="string"){let s=t(i);s!==i&&(hn(e)||(e[r]=s),i=s)}n[i]=!0}return n}function wn(n){for(let e=0;e<n.length;e++)he(n,e)||(n[e]=null);return n}function ue(n){let e=rt(null);for(let r of Ri(n)){var t=dn(r,2);let i=t[0],s=t[1];he(n,i)&&(Fe(s)?e[i]=wn(s):s&&typeof s=="object"&&s.constructor===Object?e[i]=ue(s):e[i]=s)}return e}function En(n){switch(typeof n){case"string":return n;case"number":return Sn(n);case"boolean":return _n(n);case"bigint":return wi?wi(n):"0";case"symbol":return Ei?Ei(n):"Symbol()";case"undefined":return pt(n);case"function":case"object":{if(n===null)return pt(n);let e=n,t=Ae(e,"toString");if(typeof t=="function"){let r=t(e);return typeof r=="string"?r:pt(r)}return pt(n)}default:return pt(n)}}function Ae(n,e){for(;n!==null;){let r=pn(n,e);if(r){if(r.get)return te(r.get);if(typeof r.value=="function")return te(r.value)}n=fn(n)}function t(){return null}return t}function vn(n){try{return se(n,""),!0}catch{return!1}}var vi=ne(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","search","section","select","shadow","slot","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),yr=ne(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","enterkeyhint","exportparts","filter","font","g","glyph","glyphref","hkern","image","inputmode","line","lineargradient","marker","mask","metadata","mpath","part","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),gr=ne(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),Tn=ne(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor"
Line truncated
|
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function gs(s,e){return fs(s)||ps(s,e)||ms(s,e)||ys()}function ms(s,e){if(s){if(typeof s=="string")return Ei(s,e);var t={}.toString.call(s).slice(8,-1);return t==="Object"&&s.constructor&&(t=s.constructor.name),t==="Map"||t==="Set"?Array.from(s):t==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?Ei(s,e):void 0}}var Ki=Object.entries,vi=Object.setPrototypeOf,Ss=Object.isFrozen,bs=Object.getPrototypeOf,_s=Object.getOwnPropertyDescriptor,ce=Object.freeze,le=Object.seal,rt=Object.create,Ni=typeof Reflect<"u"&&Reflect,wr=Ni.apply,Er=Ni.construct;ce||(ce=function(e){return e});le||(le=function(e){return e});wr||(wr=function(e,t){for(var r=arguments.length,i=new Array(r>2?r-2:0),n=2;n<r;n++)i[n-2]=arguments[n];return e.apply(t,i)});Er||(Er=function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i<t;i++)r[i-1]=arguments[i];return new e(...r)});var ht=se(Array.prototype.forEach),ws=se(Array.prototype.lastIndexOf),Ti=se(Array.prototype.pop),tt=se(Array.prototype.push),Es=se(Array.prototype.splice),Fe=Array.isArray,yt=se(String.prototype.toLowerCase),gr=se(String.prototype.toString),Ci=se(String.prototype.match),ft=se(String.prototype.replace),ki=se(String.prototype.indexOf),vs=se(String.prototype.trim),Ts=se(Number.prototype.toString),Cs=se(Boolean.prototype.toString),Ai=typeof BigInt>"u"?null:se(BigInt.prototype.toString),xi=typeof Symbol>"u"?null:se(Symbol.prototype.toString),ye=se(Object.prototype.hasOwnProperty),pt=se(Object.prototype.toString),oe=se(RegExp.prototype.test),$e=ks(TypeError);function se(s){return function(e){e instanceof RegExp&&(e.lastIndex=0);for(var t=arguments.length,r=new Array(t>1?t-1:0),i=1;i<t;i++)r[i-1]=arguments[i];return wr(s,e,r)}}function ks(s){return function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return Er(s,t)}}function H(s,e){let t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:yt;if(vi&&vi(s,null),!Fe(e))return s;let r=e.length;for(;r--;){let i=e[r];if(typeof i=="string"){let n=t(i);n!==i&&(Ss(e)||(e[r]=n),i=n)}s[i]=!0}return s}function As(s){for(let e=0;e<s.length;e++)ye(s,e)||(s[e]=null);return s}function pe(s){let e=rt(null);for(let r of Ki(s)){var t=gs(r,2);let i=t[0],n=t[1];ye(s,i)&&(Fe(n)?e[i]=As(n):n&&typeof n=="object"&&n.constructor===Object?e[i]=pe(n):e[i]=n)}return e}function xs(s){switch(typeof s){case"string":return s;case"number":return Ts(s);case"boolean":return Cs(s);case"bigint":return Ai?Ai(s):"0";case"symbol":return xi?xi(s):"Symbol()";case"undefined":return pt(s);case"function":case"object":{if(s===null)return pt(s);let e=s,t=Ae(e,"toString");if(typeof t=="function"){let r=t(e);return typeof r=="string"?r:pt(r)}return pt(s)}default:return pt(s)}}function Ae(s,e){for(;s!==null;){let r=_s(s,e);if(r){if(r.get)return se(r.get);if(typeof r.value=="function")return se(r.value)}s=bs(s)}function t(){return null}return t}function Is(s){try{return oe(s,""),!0}catch{return!1}}var Ii=ce(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","search","section","select","shadow","slot","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),mr=ce(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","enterkeyhint","exportparts","filter","font","g","glyph","glyphref","hkern","image","inputmode","line","lineargradient","marker","mask","metadata","mpath","part","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),Sr=ce(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),Rs=ce(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor"
Line truncated
|
||||||
`+ee),Le&&(ee=kt(ee)),b&&vt?R(ee):ee},e.setConfig=function(){let C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};dr(C),er=!0},e.clearConfig=function(){Ze=null,er=!1,b=x,A=""},e.isValidAttribute=function(C,l,y){Ze||dr({});let E=J(C),L=J(l);return di(E,L,y)},e.addHook=function(C,l){typeof l=="function"&&tt(I[C],l)},e.removeHook=function(C,l){if(l!==void 0){let y=yn(I[C],l);return y===-1?void 0:gn(I[C],y,1)[0]}return Si(I[C])},e.removeHooks=function(C){I[C]=[]},e.removeAllHooks=function(){I=Ii()},e}var Di=Li();var Rt=class n{static _keyMetadata=new WeakMap;static _messageSanitizer=null;static sortObjectKeys(e){if(typeof e!="object"||e===null)return e;if(Array.isArray(e))return e.map(n.sortObjectKeys);let t={};return Object.keys(e).sort().forEach(r=>{t[r]=n.sortObjectKeys(e[r])}),t}static assertCryptoKey(e,t=null,r=[]){if(!(e instanceof CryptoKey))throw new Error("Expected CryptoKey");if(t&&e.algorithm?.name!==t)throw new Error(`Expected algorithm ${t}, got ${e.algorithm?.name}`);for(let i of r)if(!e.usages||!e.usages.includes(i))throw new Error(`Missing required key usage: ${i}`)}static arrayBufferToBase64(e){let t="",r=new Uint8Array(e),i=r.byteLength;for(let s=0;s<i;s++)t+=String.fromCharCode(r[s]);return btoa(t)}static base64ToArrayBuffer(e){try{if(typeof e!="string"||!e)throw new Error("Invalid base64 input: must be a non-empty string");let t=e.trim();if(!/^[A-Za-z0-9+/]*={0,2}$/.test(t))throw new Error("Invalid base64 format");if(t==="")return new ArrayBuffer(0);let r=atob(t),i=r.length,s=new Uint8Array(i);for(let a=0;a<i;a++)s[a]=r.charCodeAt(a);return s.buffer}catch(t){throw console.error("Base64 to ArrayBuffer conversion failed:",t.message),new Error(`Base64 conversion error: ${t.message}`)}}static hexToUint8Array(e){try{if(!e||typeof e!="string")throw new Error("Invalid hex string input: must be a non-empty string");let t=e.replace(/:/g,"").replace(/\s/g,"");if(!/^[0-9a-fA-F]*$/.test(t))throw new Error("Invalid hex format: contains non-hex characters");if(t.length%2!==0)throw new Error("Invalid hex format: odd length");let r=new Uint8Array(t.length/2);for(let i=0;i<t.length;i+=2)r[i/2]=parseInt(t.substr(i,2),16);return r}catch(t){throw console.error("Hex to Uint8Array conversion failed:",t.message),new Error(`Hex conversion error: ${t.message}`)}}static zeroizeBuffer(e){try{if(!e)return;let t=e instanceof Uint8Array?e:e instanceof ArrayBuffer?new Uint8Array(e):null;if(!t||t.length===0)return;crypto.getRandomValues(t),t.fill(0)}catch{}}static async encryptData(e,t){try{let r=typeof e=="string"?e:JSON.stringify(e),i=crypto.getRandomValues(new Uint8Array(16)),s=new TextEncoder,a=s.encode(t),o=await crypto.subtle.importKey("raw",a,{name:"PBKDF2"},!1,["deriveKey"]),c=await crypto.subtle.deriveKey({name:"PBKDF2",salt:i,iterations:31e4,hash:"SHA-256"},o,{name:"AES-GCM",length:256},!1,["encrypt"]),d=crypto.getRandomValues(new Uint8Array(12)),u=s.encode(r),h=await crypto.subtle.encrypt({name:"AES-GCM",iv:d},c,u),m={version:"1.0",salt:Array.from(i),iv:Array.from(d),data:Array.from(new Uint8Array(h)),timestamp:Date.now()},p=JSON.stringify(m);return n.arrayBufferToBase64(new TextEncoder().encode(p).buffer)}catch(r){throw console.error("Encryption failed:",r.message),new Error(`Encryption error: ${r.message}`)}}static async decryptData(e,t){try{let r=n.base64ToArrayBuffer(e),i=new TextDecoder().decode(r),s=JSON.parse(i);if(!s.version||!s.salt||!s.iv||!s.data)throw new Error("Invalid encrypted data format");let a=new Uint8Array(s.salt),o=new Uint8Array(s.iv),c=new Uint8Array(s.data),u=new TextEncoder().encode(t),h=await crypto.subtle.importKey("raw",u,{name:"PBKDF2"},!1,["deriveKey"]),m=await crypto.subtle.deriveKey({name:"PBKDF2",salt:a,iterations:31e4,hash:"SHA-256"},h,{name:"AES-GCM",length:256},!1,["decrypt"]),p=await crypto.subtle.decrypt({name:"AES-GCM",iv:o},m,c),S=new TextDecoder().decode(p);try{return JSON.parse(S)}catch{return S}}catch(r){throw console.error("Decryption failed:",r.message),new Error(`Decryption error: ${r.message}`)}}static generateSecurePassword(){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?",t=e.length,r=32,i="";for(let s=0;s<r;s++){let a;do a=crypto.getRandomValues(new Uint32Array(1))[0];while(a>=4294967296-4294967296%t);i+=e[a%t]}return i}static async calculateSecurityLevel(e){let t=0,r=100,i={};try{if(!e||!e.securityFeatures)return console.warn("Security manager not fully initialized, using fallback calculation"),{level:"INITIALIZING",score:0,color:"gray",verificationResults:{},timestamp:Date.now(),details:"Security system initializing...",isRealData:!1};let s="full",a=!1;try{let h=await n.verifyEncryption(e);h.passed?(t+=20,i.verifyEncryption={passed:!0,details:h.details,points:20}):i.verifyEncryption={passed:!1,details:h.details,points:0}}catch(h){i.verifyEncryption={passed:!1,details:`Encryption check failed: ${h.message}`,points:0}}try{let h=await n.verifyECDHKeyExchange(e);h.passed?(t+=15,i.verifyECDHKeyExchange={pa
Line truncated
|
`+ne),Le&&(ne=At(ne)),_&&Tt?R(ne):ne},e.setConfig=function(){let C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};hr(C),rr=!0},e.clearConfig=function(){Ze=null,rr=!1,_=x,A=""},e.isValidAttribute=function(C,l,y){Ze||hr({});let v=re(C),L=re(l);return gi(v,L,y)},e.addHook=function(C,l){typeof l=="function"&&tt(I[C],l)},e.removeHook=function(C,l){if(l!==void 0){let y=ws(I[C],l);return y===-1?void 0:Es(I[C],y,1)[0]}return Ti(I[C])},e.removeHooks=function(C){I[C]=[]},e.removeAllHooks=function(){I=Fi()},e}var Ui=Oi();var Mt=class s{static _keyMetadata=new WeakMap;static _messageSanitizer=null;static sortObjectKeys(e){if(typeof e!="object"||e===null)return e;if(Array.isArray(e))return e.map(s.sortObjectKeys);let t={};return Object.keys(e).sort().forEach(r=>{t[r]=s.sortObjectKeys(e[r])}),t}static assertCryptoKey(e,t=null,r=[]){if(!(e instanceof CryptoKey))throw new Error("Expected CryptoKey");if(t&&e.algorithm?.name!==t)throw new Error(`Expected algorithm ${t}, got ${e.algorithm?.name}`);for(let i of r)if(!e.usages||!e.usages.includes(i))throw new Error(`Missing required key usage: ${i}`)}static arrayBufferToBase64(e){let t="",r=new Uint8Array(e),i=r.byteLength;for(let n=0;n<i;n++)t+=String.fromCharCode(r[n]);return btoa(t)}static base64ToArrayBuffer(e){try{if(typeof e!="string"||!e)throw new Error("Invalid base64 input: must be a non-empty string");let t=e.trim();if(!/^[A-Za-z0-9+/]*={0,2}$/.test(t))throw new Error("Invalid base64 format");if(t==="")return new ArrayBuffer(0);let r=atob(t),i=r.length,n=new Uint8Array(i);for(let a=0;a<i;a++)n[a]=r.charCodeAt(a);return n.buffer}catch(t){throw console.error("Base64 to ArrayBuffer conversion failed:",t.message),new Error(`Base64 conversion error: ${t.message}`)}}static hexToUint8Array(e){try{if(!e||typeof e!="string")throw new Error("Invalid hex string input: must be a non-empty string");let t=e.replace(/:/g,"").replace(/\s/g,"");if(!/^[0-9a-fA-F]*$/.test(t))throw new Error("Invalid hex format: contains non-hex characters");if(t.length%2!==0)throw new Error("Invalid hex format: odd length");let r=new Uint8Array(t.length/2);for(let i=0;i<t.length;i+=2)r[i/2]=parseInt(t.substr(i,2),16);return r}catch(t){throw console.error("Hex to Uint8Array conversion failed:",t.message),new Error(`Hex conversion error: ${t.message}`)}}static zeroizeBuffer(e){try{if(!e)return;let t=e instanceof Uint8Array?e:e instanceof ArrayBuffer?new Uint8Array(e):null;if(!t||t.length===0)return;crypto.getRandomValues(t),t.fill(0)}catch{}}static async encryptData(e,t){try{let r=typeof e=="string"?e:JSON.stringify(e),i=crypto.getRandomValues(new Uint8Array(16)),n=new TextEncoder,a=n.encode(t),o=await crypto.subtle.importKey("raw",a,{name:"PBKDF2"},!1,["deriveKey"]),c=await crypto.subtle.deriveKey({name:"PBKDF2",salt:i,iterations:31e4,hash:"SHA-256"},o,{name:"AES-GCM",length:256},!1,["encrypt"]),d=crypto.getRandomValues(new Uint8Array(12)),u=n.encode(r),h=await crypto.subtle.encrypt({name:"AES-GCM",iv:d},c,u),m={version:"1.0",salt:Array.from(i),iv:Array.from(d),data:Array.from(new Uint8Array(h)),timestamp:Date.now()},p=JSON.stringify(m);return s.arrayBufferToBase64(new TextEncoder().encode(p).buffer)}catch(r){throw console.error("Encryption failed:",r.message),new Error(`Encryption error: ${r.message}`)}}static async decryptData(e,t){try{let r=s.base64ToArrayBuffer(e),i=new TextDecoder().decode(r),n=JSON.parse(i);if(!n.version||!n.salt||!n.iv||!n.data)throw new Error("Invalid encrypted data format");let a=new Uint8Array(n.salt),o=new Uint8Array(n.iv),c=new Uint8Array(n.data),u=new TextEncoder().encode(t),h=await crypto.subtle.importKey("raw",u,{name:"PBKDF2"},!1,["deriveKey"]),m=await crypto.subtle.deriveKey({name:"PBKDF2",salt:a,iterations:31e4,hash:"SHA-256"},h,{name:"AES-GCM",length:256},!1,["decrypt"]),p=await crypto.subtle.decrypt({name:"AES-GCM",iv:o},m,c),S=new TextDecoder().decode(p);try{return JSON.parse(S)}catch{return S}}catch(r){throw console.error("Decryption failed:",r.message),new Error(`Decryption error: ${r.message}`)}}static generateSecurePassword(){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?",t=e.length,r=32,i="";for(let n=0;n<r;n++){let a;do a=crypto.getRandomValues(new Uint32Array(1))[0];while(a>=4294967296-4294967296%t);i+=e[a%t]}return i}static async calculateSecurityLevel(e){let t=0,r=100,i={};try{if(!e||!e.securityFeatures)return console.warn("Security manager not fully initialized, using fallback calculation"),{level:"INITIALIZING",score:0,color:"gray",verificationResults:{},timestamp:Date.now(),details:"Security system initializing...",isRealData:!1};let n="full",a=!1;try{let h=await s.verifyEncryption(e);h.passed?(t+=20,i.verifyEncryption={passed:!0,details:h.details,points:20}):i.verifyEncryption={passed:!1,details:h.details,points:0}}catch(h){i.verifyEncryption={passed:!1,details:`Encryption check failed: ${h.message}`,points:0}}try{let h=await s.verifyECDHKeyExchange(e);h.passed?(t+=15,i.verifyECDHKeyExchange={
Line truncated
|
||||||
Please wait for real-time cryptographic verification to complete.`,"sec.verificationUnavailable":"Security verification not available","chat.placeholder":"Type an encrypted message\u2026","chat.send":"Send message","chat.sendFiles":"Send files","chat.hideFiles":"Hide files","chat.recordVoice":"Record voice message","chat.sendVoice":"Send voice message","chat.discard":"Discard","chat.codeBlock":"Send as a code block (expands the input)","chat.codeHint":"Code snippet \xB7 formatting preserved \xB7 \u2318\u21B5 to send","chat.viewOnce":"View once","chat.viewOnceTitle":"View once - vanishes after the peer reads it","chat.viewOncePrefix":"View once \xB7 ","chat.viewOnceTap":"View once \xB7 tap to reveal","chat.viewedOnce":"Viewed once","chat.disappearing":"Disappearing message - deletes on both sides","chat.disappearAfter":"Disappear after","chat.visibleFor":"Visible for","chat.timerPrefix":"Timer \xB7 ","chat.expired":"This message has expired","chat.deleteForEveryone":"Delete for everyone","chat.sending":"Sending","chat.delivered":"Delivered","chat.notSent":"Not sent","chat.notSentReason":"Not sent - the secure channel is not ready. Reconnect to continue.","chat.encrypted":"Encrypted","chat.decrypted":"Decrypted","chat.encryptedOnDevice":"Encrypted on your device","chat.uploading":"Uploading","chat.downloading":"Downloading","chat.transferring":"Transferring\u2026","chat.copied":"Copied!","chat.collapse":"Collapse","chat.closeMenu":"Close menu","chat.newChat":"New chat","chat.newGroup":"New group","chat.createGroup":"Create a group","chat.groupChats":"Group chats","chat.secureChat":"Secure chat","chat.nameThis":"Name this chat","chat.rename":"Rename chat (local only)","chat.renameHint":"Double-click to rename","chat.localLabel":"Local label \xB7 stored only on this device","chat.setStatus":"Set your status","chat.yourStatusPrefix":"Your status - ","chat.meshHint":"Up to 8 peers \xB7 P2P mesh","chat.offline":"Offline","chat.noNetwork":"No network \xB7 reconnecting","chat.startVoiceCall":"Start encrypted voice call","chat.startVideoCall":"Start encrypted video call","chat.verifyForCalls":"Verify the session to enable calls","chat.e2eeNote":"Every message is end-to-end encrypted on your device before it leaves.","chat.peersOnly":"Sent end-to-end to connected peers only - never stored on a server.","flow.invitationCreated":"Secure invitation created","flow.invitationCreatedBang":"Secure invitation created and encrypted!","flow.responseCreated":"Secure response created","flow.responseCreatedBang":"Secure response created!","flow.sendInvitation":"Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.).","flow.sendResponse":"Send the response code to the initiator via a secure channel or let them scan the QR code below.","flow.sendEncryptedCode":"Send the encrypted code","flow.sendTheResponse":"Send the response","flow.pasteOrWrite":"Paste or write code\u2026","flow.processingInvitation":"Processing the secure invitation...","flow.processingResponse":"Processing the secure response...","flow.finalizing":"Finalizing the secure connection...","flow.channelEstablished":"Secure channel established","flow.channelReady":"Secure channel is ready","flow.restoring":"Restoring connection\u2026","flow.invitationCaptured":"Invitation captured.","err.needInvitation":"You need to insert the invitation code from your interlocutor.","err.needResponse":"You need to insert the response code from your interlocutor.","err.responseFormat":"Invalid response format - please check the code","err.responseNoKey":"Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.","err.responseNoSignKey":"Invalid response code - missing signature verification key. Please check the code and try again.","err.responseOutdated":"Response data is outdated - please use a fresh invitation","err.setupError":"Connection setup error","err.notReady":"Connection not ready","err.securityBreach":"Security breach detected - connection rejected","err.securityValidation":"Security validation failed - possible attack detected","err.retiredQr":"This QR code uses a retired format that could not transfer the invitation. Ask your peer to generate a new one, or use copy/paste.","err.compressedQrNote":"Compressed QR may omit SDP for brevity. Use copy/paste if connection fails.","err.inviteNotConnected":"The invitation could not be sent. That chat is not connected right now.","err.inviteNotConnectedRetry":"The invitation could not be sent. That chat is not connected right now - reopen it and try again.","err.nobodyAccepted":"Nobody accepted the invitation. The group is unchanged.","err.noAudio":"No audio captured - check microphone permission and try again.","err.voiceNeedsConnection":"Voice message needs an active secure connection. Reconnect and try again.","err.voiceRestoring":"Restoring the connection - try sending the voice message again in a moment.","err.fileTooLarge":"File too large","sas.sa
Line truncated
|
Please wait for real-time cryptographic verification to complete.`,"sec.verificationUnavailable":"Security verification not available","chat.placeholder":"Type an encrypted message\u2026","chat.send":"Send message","chat.sendFiles":"Send files","chat.hideFiles":"Hide files","chat.recordVoice":"Record voice message","chat.sendVoice":"Send voice message","chat.discard":"Discard","chat.codeBlock":"Send as a code block (expands the input)","chat.codeHint":"Code snippet \xB7 formatting preserved \xB7 \u2318\u21B5 to send","chat.viewOnce":"View once","chat.viewOnceTitle":"View once - vanishes after the peer reads it","chat.viewOncePrefix":"View once \xB7 ","chat.viewOnceTap":"View once \xB7 tap to reveal","chat.viewedOnce":"Viewed once","chat.disappearing":"Disappearing message - deletes on both sides","chat.disappearAfter":"Disappear after","chat.visibleFor":"Visible for","chat.timerPrefix":"Timer \xB7 ","chat.expired":"This message has expired","chat.deleteForEveryone":"Delete for everyone","chat.sending":"Sending","chat.delivered":"Delivered","chat.notSent":"Not sent","chat.notSentReason":"Not sent - the secure channel is not ready. Reconnect to continue.","chat.encrypted":"Encrypted","chat.decrypted":"Decrypted","chat.encryptedOnDevice":"Encrypted on your device","chat.uploading":"Uploading","chat.downloading":"Downloading","chat.transferring":"Transferring\u2026","chat.copied":"Copied!","chat.collapse":"Collapse","chat.closeMenu":"Close menu","chat.newChat":"New chat","chat.newGroup":"New group","chat.createGroup":"Create a group","chat.groupChats":"Group chats","chat.secureChat":"Secure chat","chat.nameThis":"Name this chat","chat.rename":"Rename chat (local only)","chat.renameHint":"Double-click to rename","chat.localLabel":"Local label \xB7 stored only on this device","chat.setStatus":"Set your status","chat.yourStatusPrefix":"Your status - ","chat.meshHint":"Up to 8 peers \xB7 P2P mesh","chat.offline":"Offline","chat.noNetwork":"No network \xB7 reconnecting","chat.startVoiceCall":"Start encrypted voice call","chat.startVideoCall":"Start encrypted video call","chat.verifyForCalls":"Verify the session to enable calls","chat.e2eeNote":"Every message is end-to-end encrypted on your device before it leaves.","chat.peersOnly":"Sent end-to-end to connected peers only - never stored on a server.","flow.invitationCreated":"Secure invitation created","flow.invitationCreatedBang":"Secure invitation created and encrypted!","flow.responseCreated":"Secure response created","flow.responseCreatedBang":"Secure response created!","flow.sendInvitation":"Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.).","flow.sendResponse":"Send the response code to the initiator via a secure channel or let them scan the QR code below.","flow.sendEncryptedCode":"Send the encrypted code","flow.sendTheResponse":"Send the response","flow.pasteOrWrite":"Paste or write code\u2026","flow.processingInvitation":"Processing the secure invitation...","flow.processingResponse":"Processing the secure response...","flow.finalizing":"Finalizing the secure connection...","flow.channelEstablished":"Secure channel established","flow.channelReady":"Secure channel is ready","flow.restoring":"Restoring connection\u2026","flow.invitationCaptured":"Invitation captured.","err.needInvitation":"You need to insert the invitation code from your interlocutor.","err.needResponse":"You need to insert the response code from your interlocutor.","err.responseFormat":"Invalid response format - please check the code","err.responseNoKey":"Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.","err.responseNoSignKey":"Invalid response code - missing signature verification key. Please check the code and try again.","err.responseOutdated":"Response data is outdated - please use a fresh invitation","err.setupError":"Connection setup error","err.notReady":"Connection not ready","err.securityBreach":"Security breach detected - connection rejected","err.securityValidation":"Security validation failed - possible attack detected","err.retiredQr":"This QR code uses a retired format that could not transfer the invitation. Ask your peer to generate a new one, or use copy/paste.","err.compressedQrNote":"Compressed QR may omit SDP for brevity. Use copy/paste if connection fails.","err.inviteNotConnected":"The invitation could not be sent. That chat is not connected right now.","err.inviteNotConnectedRetry":"The invitation could not be sent. That chat is not connected right now - reopen it and try again.","err.nobodyAccepted":"Nobody accepted the invitation. The group is unchanged.","err.noAudio":"No audio captured - check microphone permission and try again.","err.voiceNeedsConnection":"Voice message needs an active secure connection. Reconnect and try again.","err.voiceRestoring":"Restoring the connection - try sending the voice message again in a moment.","err.fileTooLarge":"File too large","sas.sa
Line truncated
|
||||||
`)!==-1?`\r
|
`)!==-1?`\r
|
||||||
`:`
|
`:`
|
||||||
`}function Bi(n){let e=Xn(n),t=n.split(/\r\n|\n/),r=[],i=[],s=null;for(let a of t)a.startsWith("m=")?(s={lines:[a]},i.push(s)):s?s.lines.push(a):r.push(a);return{eol:e,session:r,media:i}}function Hi(n){let e=[...n.session];for(let t of n.media)e.push(...t.lines);return e.join(n.eol)}function $i(n){let e=n.lines[0].match(/^m=(\w+)/);return e?e[1]:null}function Qn(n,e){let t=new RegExp("^a=rtpmap:(\\d+)\\s+"+e+"\\/","i"),r=[];for(let i of n.lines){let s=i.match(t);s&&r.push(s[1])}return r}function Jn(n){let e=new Map;for(let t of n.split(";")){let r=t.trim();if(!r)continue;let i=r.indexOf("=");i===-1?e.set(r,void 0):e.set(r.slice(0,i).trim(),r.slice(i+1).trim())}return e}function Vi(n){let e=[];for(let[t,r]of n)e.push(r===void 0?t:`${t}=${r}`);return e.join(";")}function Zn(n,e,t){let r=n.lines.findIndex(o=>o.startsWith(`a=fmtp:${e} `)||o===`a=fmtp:${e}`);if(r!==-1){let o=n.lines[r].slice(`a=fmtp:${e} `.length),c=Jn(o);for(let[d,u]of Object.entries(t))c.set(d,String(u));n.lines[r]=`a=fmtp:${e} ${Vi(c)}`;return}let i=new Map;for(let[o,c]of Object.entries(t))i.set(o,String(c));let s=`a=fmtp:${e} ${Vi(i)}`,a=n.lines.findIndex(o=>o.startsWith(`a=rtpmap:${e} `));a!==-1?n.lines.splice(a+1,0,s):n.lines.push(s)}function vr(n,e){if(!n||typeof n!="string")return n;let t=Bi(n),r=!1;for(let i of t.media)if($i(i)==="audio")for(let s of Qn(i,"opus"))Zn(i,s,e),r=!0;return r?Hi(t):n}var ea=/^(rtx|red|ulpfec|flexfec-03|telephone-event|CN)$/i;function ta(n){let e=[];for(let t of n.lines){let r=t.match(/^a=rtpmap:(\d+)\s+([^/]+)\//);r&&!ea.test(r[2])&&e.push(r[1])}return e}function ra(n,e){for(let t of ta(n))for(let r of e){let i=`a=rtcp-fb:${t} ${r}`;if(n.lines.includes(i))continue;let s=-1;for(let a=0;a<n.lines.length;a++){let o=n.lines[a];(o.startsWith(`a=rtpmap:${t} `)||o.startsWith(`a=fmtp:${t} `)||o.startsWith(`a=rtcp-fb:${t} `))&&(s=a)}s===-1&&(s=n.lines.length-1),n.lines.splice(s+1,0,i)}}function ia(n,e){if(n.lines.some(i=>i.startsWith("a=extmap:")&&i.includes(e)))return;let t=0,r=-1;for(let i=0;i<n.lines.length;i++){let s=n.lines[i].match(/^a=extmap:(\d+)/);s&&(t=Math.max(t,Number(s[1])),r=i)}r===-1&&(r=n.lines.findIndex(i=>i.startsWith("a=mid:")),r===-1&&(r=n.lines.length-1)),n.lines.splice(r+1,0,`a=extmap:${t+1} ${e}`)}function Gi(n,e){if(!n||typeof n!="string"||!e)return n;let t=Bi(n),r=!1;for(let i of t.media){let s=$i(i),a=s==="video"?e.video:s==="audio"?e.audio:null;a&&(Array.isArray(a.rtcpFb)&&(ra(i,a.rtcpFb),r=!0),a.twcc&&e.twccUri&&(ia(i,e.twccUri),r=!0))}return r?Hi(t):n}function qi(n){try{if(Ue||!n||typeof n.setCodecPreferences!="function"||!nt.preferRed)return!1;let e=typeof RTCRtpSender<"u"&&RTCRtpSender.getCapabilities?RTCRtpSender.getCapabilities("audio"):null;if(!e||!Array.isArray(e.codecs))return!1;let t=o=>/red$/i.test(o.mimeType),r=o=>/opus$/i.test(o.mimeType);if(!e.codecs.some(t))return!1;let i=e.codecs.filter(t),s=e.codecs.filter(r),a=e.codecs.filter(o=>!t(o)&&!r(o));return n.setCodecPreferences([...i,...s,...a]),!0}catch{return!1}}async function ji(n,e={}){try{if(Ue||!n||typeof n.getParameters!="function")return!1;let t={...nt.sender,...e},r=n.getParameters();(!r.encodings||r.encodings.length===0)&&(r.encodings=[{}]);for(let i of r.encodings)i.maxBitrate=t.maxBitrate,i.priority=t.priority,i.networkPriority=t.networkPriority;return await n.setParameters(r),!0}catch{return!1}}var Wi={VP9:0,AV1:1,H264:2,VP8:3};function Yi(n){return(String(n||"").split("/")[1]||"").toUpperCase()}function sa(n){let e=t=>{let r=Yi(t.mimeType);return Object.prototype.hasOwnProperty.call(Wi,r)?Wi[r]:99};return n.map((t,r)=>({c:t,i:r})).sort((t,r)=>e(t.c)-e(r.c)||t.i-r.i).map(t=>t.c)}function Xi(n){if(!n||!Array.isArray(n.codecs))return null;let e=new Set(n.codecs.map(t=>Yi(t.mimeType)));for(let t of Re.codecPreferenceOrder)if(e.has(t))return t;return null}function Qi(){return typeof RTCRtpSender<"u"&&RTCRtpSender.getCapabilities?RTCRtpSender.getCapabilities("video"):null}function Ji(n){try{if(Ue||!n||typeof n.setCodecPreferences!="function")return!1;let e=Qi();return!e||!Array.isArray(e.codecs)?!1:(n.setCodecPreferences(sa(e.codecs)),Xi(e))}catch{return!1}}function na(n){return n==="VP9"?{scalabilityMode:Re.vp9.preferredScalabilityMode,maxBitrate:15e5,degradationPreference:Re.vp9.degradationPreference}:n==="AV1"?{scalabilityMode:Re.av1.scalabilityMode,maxBitrate:Re.av1.maxBitrate,degradationPreference:Re.av1.degradationPreference}:{scalabilityMode:void 0,maxBitrate:15e5,degradationPreference:"balanced"}}async function Zi(n,e={}){try{if(Ue||!n||typeof n.getParameters!="function")return!1;let t=Xi(Qi())||"VP8",r={...na(t),...e},i=n.getParameters();(!i.encodings||i.encodings.length===0)&&(i.encodings=[{}]);let s=i.encodings.length>1;if(s)for(let a of i.encodings)a.networkPriority=Re.networkPriority;else{let a=i.encodings[0];a.maxBitrate=r.maxBitrate,a.networkPriority=Re.networkPriority,r.scalabilityMode&&(a.scalabilityMode=r.scalabilityMode)}r.degradationPreference&&(i.degradationPreference=r.degradationPreference);try{retu
Line truncated
|
`}function Wi(s){let e=ta(s),t=s.split(/\r\n|\n/),r=[],i=[],n=null;for(let a of t)a.startsWith("m=")?(n={lines:[a]},i.push(n)):n?n.lines.push(a):r.push(a);return{eol:e,session:r,media:i}}function Yi(s){let e=[...s.session];for(let t of s.media)e.push(...t.lines);return e.join(s.eol)}function Xi(s){let e=s.lines[0].match(/^m=(\w+)/);return e?e[1]:null}function ra(s,e){let t=new RegExp("^a=rtpmap:(\\d+)\\s+"+e+"\\/","i"),r=[];for(let i of s.lines){let n=i.match(t);n&&r.push(n[1])}return r}function ia(s){let e=new Map;for(let t of s.split(";")){let r=t.trim();if(!r)continue;let i=r.indexOf("=");i===-1?e.set(r,void 0):e.set(r.slice(0,i).trim(),r.slice(i+1).trim())}return e}function ji(s){let e=[];for(let[t,r]of s)e.push(r===void 0?t:`${t}=${r}`);return e.join(";")}function na(s,e,t){let r=s.lines.findIndex(o=>o.startsWith(`a=fmtp:${e} `)||o===`a=fmtp:${e}`);if(r!==-1){let o=s.lines[r].slice(`a=fmtp:${e} `.length),c=ia(o);for(let[d,u]of Object.entries(t))c.set(d,String(u));s.lines[r]=`a=fmtp:${e} ${ji(c)}`;return}let i=new Map;for(let[o,c]of Object.entries(t))i.set(o,String(c));let n=`a=fmtp:${e} ${ji(i)}`,a=s.lines.findIndex(o=>o.startsWith(`a=rtpmap:${e} `));a!==-1?s.lines.splice(a+1,0,n):s.lines.push(n)}function Cr(s,e){if(!s||typeof s!="string")return s;let t=Wi(s),r=!1;for(let i of t.media)if(Xi(i)==="audio")for(let n of ra(i,"opus"))na(i,n,e),r=!0;return r?Yi(t):s}var sa=/^(rtx|red|ulpfec|flexfec-03|telephone-event|CN)$/i;function aa(s){let e=[];for(let t of s.lines){let r=t.match(/^a=rtpmap:(\d+)\s+([^/]+)\//);r&&!sa.test(r[2])&&e.push(r[1])}return e}function oa(s,e){for(let t of aa(s))for(let r of e){let i=`a=rtcp-fb:${t} ${r}`;if(s.lines.includes(i))continue;let n=-1;for(let a=0;a<s.lines.length;a++){let o=s.lines[a];(o.startsWith(`a=rtpmap:${t} `)||o.startsWith(`a=fmtp:${t} `)||o.startsWith(`a=rtcp-fb:${t} `))&&(n=a)}n===-1&&(n=s.lines.length-1),s.lines.splice(n+1,0,i)}}function ca(s,e){if(s.lines.some(i=>i.startsWith("a=extmap:")&&i.includes(e)))return;let t=0,r=-1;for(let i=0;i<s.lines.length;i++){let n=s.lines[i].match(/^a=extmap:(\d+)/);n&&(t=Math.max(t,Number(n[1])),r=i)}r===-1&&(r=s.lines.findIndex(i=>i.startsWith("a=mid:")),r===-1&&(r=s.lines.length-1)),s.lines.splice(r+1,0,`a=extmap:${t+1} ${e}`)}function Qi(s,e){if(!s||typeof s!="string"||!e)return s;let t=Wi(s),r=!1;for(let i of t.media){let n=Xi(i),a=n==="video"?e.video:n==="audio"?e.audio:null;a&&(Array.isArray(a.rtcpFb)&&(oa(i,a.rtcpFb),r=!0),a.twcc&&e.twccUri&&(ca(i,e.twccUri),r=!0))}return r?Yi(t):s}function Ji(s){try{if(Ue||!s||typeof s.setCodecPreferences!="function"||!st.preferRed)return!1;let e=typeof RTCRtpSender<"u"&&RTCRtpSender.getCapabilities?RTCRtpSender.getCapabilities("audio"):null;if(!e||!Array.isArray(e.codecs))return!1;let t=o=>/red$/i.test(o.mimeType),r=o=>/opus$/i.test(o.mimeType);if(!e.codecs.some(t))return!1;let i=e.codecs.filter(t),n=e.codecs.filter(r),a=e.codecs.filter(o=>!t(o)&&!r(o));return s.setCodecPreferences([...i,...n,...a]),!0}catch{return!1}}async function Zi(s,e={}){try{if(Ue||!s||typeof s.getParameters!="function")return!1;let t={...st.sender,...e},r=s.getParameters();(!r.encodings||r.encodings.length===0)&&(r.encodings=[{}]);for(let i of r.encodings)i.maxBitrate=t.maxBitrate,i.priority=t.priority,i.networkPriority=t.networkPriority;return await s.setParameters(r),!0}catch{return!1}}var en={VP9:0,AV1:1,H264:2,VP8:3};function tn(s){return(String(s||"").split("/")[1]||"").toUpperCase()}function la(s){let e=t=>{let r=tn(t.mimeType);return Object.prototype.hasOwnProperty.call(en,r)?en[r]:99};return s.map((t,r)=>({c:t,i:r})).sort((t,r)=>e(t.c)-e(r.c)||t.i-r.i).map(t=>t.c)}function rn(s){if(!s||!Array.isArray(s.codecs))return null;let e=new Set(s.codecs.map(t=>tn(t.mimeType)));for(let t of Re.codecPreferenceOrder)if(e.has(t))return t;return null}function nn(){return typeof RTCRtpSender<"u"&&RTCRtpSender.getCapabilities?RTCRtpSender.getCapabilities("video"):null}function sn(s){try{if(Ue||!s||typeof s.setCodecPreferences!="function")return!1;let e=nn();return!e||!Array.isArray(e.codecs)?!1:(s.setCodecPreferences(la(e.codecs)),rn(e))}catch{return!1}}function da(s){return s==="VP9"?{scalabilityMode:Re.vp9.preferredScalabilityMode,maxBitrate:15e5,degradationPreference:Re.vp9.degradationPreference}:s==="AV1"?{scalabilityMode:Re.av1.scalabilityMode,maxBitrate:Re.av1.maxBitrate,degradationPreference:Re.av1.degradationPreference}:{scalabilityMode:void 0,maxBitrate:15e5,degradationPreference:"balanced"}}async function an(s,e={}){try{if(Ue||!s||typeof s.getParameters!="function")return!1;let t=rn(nn())||"VP8",r={...da(t),...e},i=s.getParameters();(!i.encodings||i.encodings.length===0)&&(i.encodings=[{}]);let n=i.encodings.length>1;if(n)for(let a of i.encodings)a.networkPriority=Re.networkPriority;else{let a=i.encodings[0];a.maxBitrate=r.maxBitrate,a.networkPriority=Re.networkPriority,r.scalabilityMode&&(a.scalabilityMode=r.scalabilityMode)}r.degradationPreference&&(i.degradationPreference=r.degradationPreference);try{retu
Line truncated
|
||||||
`)+`\r
|
`)+`\r
|
||||||
`}}var Fr=new TextEncoder;function zt(...n){let e=n.reduce((i,s)=>i+s.length,0),t=new Uint8Array(e),r=0;for(let i of n)t.set(i,r),r+=i.length;return t}async function Kr(n,e){return(await n(zt(Fr.encode("sbq2/bind\0"),e))).slice(0,$.BINDING_BYTES)}async function Vt(n,e){return(await n(zt(Fr.encode("sbq2/blob\0"),e))).slice(0,$.COMMITMENT_BYTES)}function ys(n,e,t,r){let i=s=>{let a=new Uint8Array(4);return new DataView(a.buffer).setUint32(0,s.length),zt(a,s)};return zt(Fr.encode("sbq2/sas/v1\0"),i(n),i(e),i(t),i(r))}var mt="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function va(n){let e="";for(let t=0;t<n.length;t+=3){let r=n[t],i=n[t+1],s=n[t+2];if(e+=mt[r>>2],e+=mt[(r&3)<<4|(i??0)>>4],i===void 0||(e+=mt[(i&15)<<2|(s??0)>>6],s===void 0))break;e+=mt[s&63]}return e}function Ta(n){typeof n!="string"&&P("payload must be a string");let e=n.replace(/\s+/g,"");e.length>Math.ceil($.MAX_PAYLOAD_BYTES*4/3)+4&&P("payload is too long"),/^[A-Za-z0-9_-]*$/.test(e)||P("payload contains characters outside base64url"),e.length%4===1&&P("payload has an impossible length");let t=new Uint8Array(Math.floor(e.length*3/4)),r=0,i=0,s=0;for(let a of e)i=i<<6|mt.indexOf(a),s+=6,s>=8&&(s-=8,t[r++]=i>>s&255);return i&(1<<s)-1&&P("payload has non-zero padding bits"),t.subarray(0,r)}var Ir="SB2:";function gs(n){return Ir+va(n)}function Nr(n){typeof n!="string"&&P("payload must be a string");let e=n.trim();return e.startsWith(Ir)||P("not an SB2 descriptor"),Ta(e.slice(Ir.length))}var ms=2,Ee=Object.freeze({OFFER:0,ANSWER:1}),We=Object.freeze({MAX_SPKI:256,MIN_SPKI:40,MAX_SIG:160,MIN_SIG:48,MAX_BLOB_BYTES:1024}),Or=class extends Error{constructor(e,t="key_exchange"){super(e),this.name="KeyExchangeError",this.code=t}},le=(n,e)=>{throw new Or(n,e)};function Ss({role:n,ecdhSpki:e,ecdsaSpki:t}){n!==Ee.OFFER&&n!==Ee.ANSWER&&le("invalid role");for(let[a,o]of[["ecdh",e],["ecdsa",t]])o instanceof Uint8Array||le(`${a} SPKI must be a Uint8Array`),(o.length<We.MIN_SPKI||o.length>We.MAX_SPKI)&&le(`${a} SPKI length out of range`);let r=new Uint8Array(4+e.length+2+t.length),i=new DataView(r.buffer),s=0;return r[s++]=ms,r[s++]=n,i.setUint16(s,e.length),s+=2,r.set(e,s),s+=e.length,i.setUint16(s,t.length),s+=2,r.set(t,s),r}function _s(n){n instanceof Uint8Array||le("key blob must be a Uint8Array"),n.length===0&&le("key blob is empty"),n.length>We.MAX_BLOB_BYTES&&le("key blob exceeds the size limit");let e=new DataView(n.buffer,n.byteOffset,n.byteLength),t=0,r=u=>{t+u>n.length&&le("key blob is truncated")};r(1);let i=n[t++];i!==ms&&le(`unsupported key blob version 0x${i.toString(16)}`,"version"),r(1);let s=n[t++];s!==Ee.OFFER&&s!==Ee.ANSWER&&le("reserved key blob role"),r(2);let a=e.getUint16(t);t+=2,(a<We.MIN_SPKI||a>We.MAX_SPKI)&&le("ECDH SPKI length out of range"),r(a);let o=n.slice(t,t+a);t+=a,r(2);let c=e.getUint16(t);t+=2,(c<We.MIN_SPKI||c>We.MAX_SPKI)&&le("ECDSA SPKI length out of range"),r(c);let d=n.slice(t,t+c);return t+=c,t!==n.length&&le(`${n.length-t} trailing byte(s) after the key blob`),{version:i,role:s,ecdhSpki:o,ecdsaSpki:d}}function bs({offerDescriptor:n,answerDescriptor:e,offerBlob:t,answerBlob:r}){for(let[i,s]of Object.entries({offerDescriptor:n,answerDescriptor:e,offerBlob:t,answerBlob:r}))(!(s instanceof Uint8Array)||s.length===0)&&le(`transcript component ${i} is missing`);return ys(n,e,t,r)}async function ws(n,e){let t=await n.digest("SHA-512",e);return Array.from(new Uint8Array(t))}var Es=new TextEncoder;function Ur(n){let e=Es.encode("sbq2/proof/v1\0"),t=new Uint8Array(e.length+n.length);return t.set(e,0),t.set(n,e.length),t}async function vs(n,{ecdhPrivateKey:e,peerEcdhPublicKey:t,transcript:r,digits:i=7}){let s=await n.deriveBits({name:"ECDH",public:t},e,256),a=null;try{a=await n.importKey("raw",s,"HKDF",!1,["deriveBits"]);let o=new Uint8Array(await n.digest("SHA-256",r)),c=await n.deriveBits({name:"HKDF",hash:"SHA-256",salt:o,info:Es.encode("sbq2-sas-v1")},a,64),d=new DataView(c),u=(d.getUint32(0)^d.getUint32(4))>>>0,h=10**i;return String(u%h).padStart(i,"0")}finally{try{new Uint8Array(s).fill(0)}catch{}}}async function Ts(n,e,t){(!(t instanceof Uint8Array)||t.length!==$.COMMITMENT_BYTES)&&le("descriptor carried no usable commitment","commitment_missing");let i=await Vt(async a=>new Uint8Array(await n.digest("SHA-256",a)),e);i.length!==t.length&&le("commitment length mismatch","commitment_mismatch");let s=0;for(let a=0;a<i.length;a++)s|=i[a]^t[a];return s!==0&&le("the key material does not match the commitment in the invitation","commitment_mismatch"),!0}var Bt=class n{static TIMEOUTS={KEY_ROTATION_INTERVAL:3e5,CONNECTION_TIMEOUT:1e4,HEARTBEAT_INTERVAL:1e4,SECURITY_CALC_DELAY:1e3,SECURITY_CALC_RETRY_DELAY:3e3,CLEANUP_INTERVAL:3e5,CLEANUP_CHECK_INTERVAL:6e4,ICE_GATHERING_TIMEOUT:1e4,ICE_GATHERING_HARD_TIMEOUT:25e3,DISCONNECT_CLEANUP_DELAY:500,PEER_DISCONNECT_CLEANUP:2e3,STAGE2_ACTIVATION_DELAY:1e4,STAGE3_ACTIVATION_DELAY:15e3,STAGE4_ACTIVATION_DELAY:2e4,FILE_TRANSFER_INIT_DELAY:1e3,FAKE_TRAFFIC_MIN_INTERV
Line truncated
|
`}}var Nr=new TextEncoder;function Bt(...s){let e=s.reduce((i,n)=>i+n.length,0),t=new Uint8Array(e),r=0;for(let i of s)t.set(i,r),r+=i.length;return t}async function Or(s,e){return(await s(Bt(Nr.encode("sbq2/bind\0"),e))).slice(0,j.BINDING_BYTES)}async function Ht(s,e){return(await s(Bt(Nr.encode("sbq2/blob\0"),e))).slice(0,j.COMMITMENT_BYTES)}function En(s,e,t,r){let i=n=>{let a=new Uint8Array(4);return new DataView(a.buffer).setUint32(0,n.length),Bt(a,n)};return Bt(Nr.encode("sbq2/sas/v1\0"),i(s),i(e),i(t),i(r))}var mt="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function xa(s){let e="";for(let t=0;t<s.length;t+=3){let r=s[t],i=s[t+1],n=s[t+2];if(e+=mt[r>>2],e+=mt[(r&3)<<4|(i??0)>>4],i===void 0||(e+=mt[(i&15)<<2|(n??0)>>6],n===void 0))break;e+=mt[n&63]}return e}function Ia(s){typeof s!="string"&&P("payload must be a string");let e=s.replace(/\s+/g,"");e.length>Math.ceil(j.MAX_PAYLOAD_BYTES*4/3)+4&&P("payload is too long"),/^[A-Za-z0-9_-]*$/.test(e)||P("payload contains characters outside base64url"),e.length%4===1&&P("payload has an impossible length");let t=new Uint8Array(Math.floor(e.length*3/4)),r=0,i=0,n=0;for(let a of e)i=i<<6|mt.indexOf(a),n+=6,n>=8&&(n-=8,t[r++]=i>>n&255);return i&(1<<n)-1&&P("payload has non-zero padding bits"),t.subarray(0,r)}var Mr="SB2:";function vn(s){return Mr+xa(s)}function Ur(s){typeof s!="string"&&P("payload must be a string");let e=s.trim();return e.startsWith(Mr)||P("not an SB2 descriptor"),Ia(e.slice(Mr.length))}var Tn=2,ve=Object.freeze({OFFER:0,ANSWER:1}),We=Object.freeze({MAX_SPKI:256,MIN_SPKI:40,MAX_SIG:160,MIN_SIG:48,MAX_BLOB_BYTES:1024}),zr=class extends Error{constructor(e,t="key_exchange"){super(e),this.name="KeyExchangeError",this.code=t}},he=(s,e)=>{throw new zr(s,e)};function Cn({role:s,ecdhSpki:e,ecdsaSpki:t}){s!==ve.OFFER&&s!==ve.ANSWER&&he("invalid role");for(let[a,o]of[["ecdh",e],["ecdsa",t]])o instanceof Uint8Array||he(`${a} SPKI must be a Uint8Array`),(o.length<We.MIN_SPKI||o.length>We.MAX_SPKI)&&he(`${a} SPKI length out of range`);let r=new Uint8Array(4+e.length+2+t.length),i=new DataView(r.buffer),n=0;return r[n++]=Tn,r[n++]=s,i.setUint16(n,e.length),n+=2,r.set(e,n),n+=e.length,i.setUint16(n,t.length),n+=2,r.set(t,n),r}function kn(s){s instanceof Uint8Array||he("key blob must be a Uint8Array"),s.length===0&&he("key blob is empty"),s.length>We.MAX_BLOB_BYTES&&he("key blob exceeds the size limit");let e=new DataView(s.buffer,s.byteOffset,s.byteLength),t=0,r=u=>{t+u>s.length&&he("key blob is truncated")};r(1);let i=s[t++];i!==Tn&&he(`unsupported key blob version 0x${i.toString(16)}`,"version"),r(1);let n=s[t++];n!==ve.OFFER&&n!==ve.ANSWER&&he("reserved key blob role"),r(2);let a=e.getUint16(t);t+=2,(a<We.MIN_SPKI||a>We.MAX_SPKI)&&he("ECDH SPKI length out of range"),r(a);let o=s.slice(t,t+a);t+=a,r(2);let c=e.getUint16(t);t+=2,(c<We.MIN_SPKI||c>We.MAX_SPKI)&&he("ECDSA SPKI length out of range"),r(c);let d=s.slice(t,t+c);return t+=c,t!==s.length&&he(`${s.length-t} trailing byte(s) after the key blob`),{version:i,role:n,ecdhSpki:o,ecdsaSpki:d}}function An({offerDescriptor:s,answerDescriptor:e,offerBlob:t,answerBlob:r}){for(let[i,n]of Object.entries({offerDescriptor:s,answerDescriptor:e,offerBlob:t,answerBlob:r}))(!(n instanceof Uint8Array)||n.length===0)&&he(`transcript component ${i} is missing`);return En(s,e,t,r)}async function xn(s,e){let t=await s.digest("SHA-512",e);return Array.from(new Uint8Array(t))}var In=new TextEncoder;function Vr(s){let e=In.encode("sbq2/proof/v1\0"),t=new Uint8Array(e.length+s.length);return t.set(e,0),t.set(s,e.length),t}async function Rn(s,{ecdhPrivateKey:e,peerEcdhPublicKey:t,transcript:r,digits:i=7}){let n=await s.deriveBits({name:"ECDH",public:t},e,256),a=null;try{a=await s.importKey("raw",n,"HKDF",!1,["deriveBits"]);let o=new Uint8Array(await s.digest("SHA-256",r)),c=await s.deriveBits({name:"HKDF",hash:"SHA-256",salt:o,info:In.encode("sbq2-sas-v1")},a,64),d=new DataView(c),u=(d.getUint32(0)^d.getUint32(4))>>>0,h=10**i;return String(u%h).padStart(i,"0")}finally{try{new Uint8Array(n).fill(0)}catch{}}}async function Mn(s,e,t){(!(t instanceof Uint8Array)||t.length!==j.COMMITMENT_BYTES)&&he("descriptor carried no usable commitment","commitment_missing");let i=await Ht(async a=>new Uint8Array(await s.digest("SHA-256",a)),e);i.length!==t.length&&he("commitment length mismatch","commitment_mismatch");let n=0;for(let a=0;a<i.length;a++)n|=i[a]^t[a];return n!==0&&he("the key material does not match the commitment in the invitation","commitment_mismatch"),!0}var $t=class s{static TIMEOUTS={KEY_ROTATION_INTERVAL:3e5,CONNECTION_TIMEOUT:1e4,HEARTBEAT_INTERVAL:1e4,SECURITY_CALC_DELAY:1e3,SECURITY_CALC_RETRY_DELAY:3e3,CLEANUP_INTERVAL:3e5,CLEANUP_CHECK_INTERVAL:6e4,ICE_GATHERING_TIMEOUT:1e4,ICE_GATHERING_HARD_TIMEOUT:25e3,DISCONNECT_CLEANUP_DELAY:500,PEER_DISCONNECT_CLEANUP:2e3,STAGE2_ACTIVATION_DELAY:1e4,STAGE3_ACTIVATION_DELAY:15e3,STAGE4_ACTIVATION_DELAY:2e4,FILE_TRANSFER_INIT_DELAY:1e3,FAKE_TRAFFIC_MIN_INTERV
Line truncated
|
||||||
self.onmessage = function(e) {
|
self.onmessage = function(e) {
|
||||||
const { type, data } = e.data;
|
const { type, data } = e.data;
|
||||||
|
|
||||||
@@ -39,10 +39,10 @@ Please wait for real-time cryptographic verification to complete.`,"sec.verifica
|
|||||||
self.postMessage({ success: false, error: error.message });
|
self.postMessage({ success: false, error: error.message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
`,s=new Blob([i],{type:"application/javascript"}),a=new Worker(URL.createObjectURL(s)),o=setTimeout(()=>{a.terminate(),r(new Error("Worker cleanup timeout"))},5e3);a.onmessage=c=>{clearTimeout(o),a.terminate(),URL.revokeObjectURL(s),c.data.success?t(c.data):r(new Error(c.data.error))},a.onerror=c=>{clearTimeout(o),a.terminate(),URL.revokeObjectURL(s),r(c)},a.postMessage(e)})}async _cleanupInMainThread(e){let{type:t,data:r}=e;switch(t){case"cleanup_arrays":let i=0,s=100;for(;i<r.count;){let d=Math.min(i+s,r.count);for(let u=i;u<d;u++);i=d,await this._asyncSleep(1)}return{success:!0,processed:i};case"cleanup_objects":let a=r.objects||[],o=[];for(let d=0;d<a.length;d+=50)o.push(a.slice(d,d+50));let c=0;for(let d of o)d.forEach(()=>c++),await this._asyncSleep(1);return{success:!0,cleaned:c};default:return{success:!0,message:"Unknown cleanup type"}}}_initializeMutexSystem(){this._keyOperationMutex={locked:!1,queue:[],lockId:null,lockTimeout:null,lockTime:null,operationCount:0},this._cryptoOperationMutex={locked:!1,queue:[],lockId:null,lockTimeout:null,lockTime:null,operationCount:0},this._connectionOperationMutex={locked:!1,queue:[],lockId:null,lockTimeout:null,lockTime:null,operationCount:0},this._keySystemState={isInitializing:!1,isRotating:!1,isDestroying:!1,lastOperation:null,lastOperationTime:Date.now(),operationId:null,concurrentOperations:0,maxConcurrentOperations:1},this._operationCounters={keyOperations:0,cryptoOperations:0,connectionOperations:0,totalOperations:0,failedOperations:0},this._secureLog("info","\u{1F512} Enhanced mutex system initialized with atomic protection",{mutexes:["keyOperation","cryptoOperation","connectionOperation"],timestamp:Date.now(),features:["atomic_operations","race_condition_protection","enhanced_state_tracking"]})}_hardenDebugModeReferences(){this._secureLog("info","\u{1F512} XSS Hardening: Debug mode references already replaced")}_initializeUnifiedScheduler(){this._maintenanceScheduler=setInterval(()=>{this._executeMaintenanceCycle()},3e5),this._secureLog("info","\u{1F527} Unified maintenance scheduler initialized (5-minute cycle)"),this._activeTimers=new Set([this._maintenanceScheduler])}_trackActiveTimer(e){return e&&(this._activeTimers||(this._activeTimers=new Set),this._activeTimers.add(e),e)}_untrackActiveTimer(e){e&&this._activeTimers&&this._activeTimers.delete(e)}_setSASMaterialReady(e,t){this._sasLocalFingerprint=e,this._sasRemoteFingerprint=t}_isVerificationReady(){let e=!!(this.peerConnection?.localDescription&&this.peerConnection?.remoteDescription),t=this.dataChannel?.readyState==="open",r=typeof this.verificationCode=="string"&&this.verificationCode.trim().length>0,i=typeof this._sasLocalFingerprint=="string"&&this._sasLocalFingerprint.trim().length>0&&typeof this._sasRemoteFingerprint=="string"&&this._sasRemoteFingerprint.trim().length>0;return e&&t&&r&&i}_notifyVerificationReadyIfPossible(){return this._isVerificationReady()?(this._verificationUiOpened||(this._verificationUiOpened=!0,this.onStatusChange?.("verifying"),this.onVerificationRequired?.(this.verificationCode)),!0):!1}_countIceCandidatesInSDP(e){return typeof e!="string"?0:(e.match(/^a=candidate:/gm)||[]).length}_summarizeIceCandidatesInSDP(e){let t={total:0,host:0,srflx:0,relay:0,prflx:0,unknown:0};if(typeof e!="string")return t;for(let r of e.match(/^a=candidate:.*$/gm)||[]){t.total+=1;let s=r.match(/\btyp\s+(host|srflx|relay|prflx)\b/i)?.[1]?.toLowerCase();s&&Object.prototype.hasOwnProperty.call(t,s)?t[s]+=1:t.unknown+=1}return t}_describeIceCandidatesInSDP(e){return typeof e!="string"?[]:(e.match(/^a=candidate:.*$/gm)||[]).map(t=>{let r=t.slice(12).trim().split(/\s+/),i=r.findIndex(u=>u.toLowerCase()==="typ"),s=r[4]||"",a=r[5]||"",o=i>=0&&r[i+1]||"unknown",c=(r[2]||"unknown").toLowerCase(),d="unknown";return/\.local$/i.test(s)?d="mdns":/^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.)/.test(s)?d="private-ipv4":/^\d{1,3}(\.\d{1,3}){3}$/.test(s)?d="public-ipv4":s.includes(":")&&(d="ipv6"),{candidateType:o,protocol:c,addressKind:d,portPresent:!!a,tcpType:(()=>{let u=r.findIndex(h=>h.toLowerCase()==="tcptype");return u>=0&&r[u+1]||null})()}})}_logIceCandidateDiagnostics(e,t,r={}){let i=this._summarizeIceCandidatesInSDP(t),s=this._describeIceCandidatesInSDP(t);return console.info(`[SecureBit ICE] ${e}`,{candidateSummary:i,candidateDetails:s,candidateDetailsJson:JSON.stringify(s),...r}),{candidateSummary:i,candidateDetails:s}}_hasOnlyMdnsHostCandidates(e){let t=this._summarizeIceCandidatesInSDP(e),r=this._describeIceCandidatesInSDP(e);return t.total>0&&t.srflx===0&&t.relay===0&&t.prflx===0&&r.every(i=>i.candidateType==="host"&&i.addressKind==="mdns")}_warnIfRemoteCandidatesNeedRelay(e,t){if(!this._hasOnlyMdnsHostCandidates(t))return!1;let r=e==="answer"?"Connection warning: the response contains only browser-masked mDNS host candidates and no server-reflexive or TURN relay candidates. This network/browser combination may not connect until TURN is configured.":"Connection warning: the i
Line truncated
|
`,n=new Blob([i],{type:"application/javascript"}),a=new Worker(URL.createObjectURL(n)),o=setTimeout(()=>{a.terminate(),r(new Error("Worker cleanup timeout"))},5e3);a.onmessage=c=>{clearTimeout(o),a.terminate(),URL.revokeObjectURL(n),c.data.success?t(c.data):r(new Error(c.data.error))},a.onerror=c=>{clearTimeout(o),a.terminate(),URL.revokeObjectURL(n),r(c)},a.postMessage(e)})}async _cleanupInMainThread(e){let{type:t,data:r}=e;switch(t){case"cleanup_arrays":let i=0,n=100;for(;i<r.count;){let d=Math.min(i+n,r.count);for(let u=i;u<d;u++);i=d,await this._asyncSleep(1)}return{success:!0,processed:i};case"cleanup_objects":let a=r.objects||[],o=[];for(let d=0;d<a.length;d+=50)o.push(a.slice(d,d+50));let c=0;for(let d of o)d.forEach(()=>c++),await this._asyncSleep(1);return{success:!0,cleaned:c};default:return{success:!0,message:"Unknown cleanup type"}}}_initializeMutexSystem(){this._keyOperationMutex={locked:!1,queue:[],lockId:null,lockTimeout:null,lockTime:null,operationCount:0},this._cryptoOperationMutex={locked:!1,queue:[],lockId:null,lockTimeout:null,lockTime:null,operationCount:0},this._connectionOperationMutex={locked:!1,queue:[],lockId:null,lockTimeout:null,lockTime:null,operationCount:0},this._keySystemState={isInitializing:!1,isRotating:!1,isDestroying:!1,lastOperation:null,lastOperationTime:Date.now(),operationId:null,concurrentOperations:0,maxConcurrentOperations:1},this._operationCounters={keyOperations:0,cryptoOperations:0,connectionOperations:0,totalOperations:0,failedOperations:0},this._secureLog("info","\u{1F512} Enhanced mutex system initialized with atomic protection",{mutexes:["keyOperation","cryptoOperation","connectionOperation"],timestamp:Date.now(),features:["atomic_operations","race_condition_protection","enhanced_state_tracking"]})}_hardenDebugModeReferences(){this._secureLog("info","\u{1F512} XSS Hardening: Debug mode references already replaced")}_initializeUnifiedScheduler(){this._maintenanceScheduler=setInterval(()=>{this._executeMaintenanceCycle()},3e5),this._secureLog("info","\u{1F527} Unified maintenance scheduler initialized (5-minute cycle)"),this._activeTimers=new Set([this._maintenanceScheduler])}_trackActiveTimer(e){return e&&(this._activeTimers||(this._activeTimers=new Set),this._activeTimers.add(e),e)}_untrackActiveTimer(e){e&&this._activeTimers&&this._activeTimers.delete(e)}_setSASMaterialReady(e,t){this._sasLocalFingerprint=e,this._sasRemoteFingerprint=t}_isVerificationReady(){let e=!!(this.peerConnection?.localDescription&&this.peerConnection?.remoteDescription),t=this.dataChannel?.readyState==="open",r=typeof this.verificationCode=="string"&&this.verificationCode.trim().length>0,i=typeof this._sasLocalFingerprint=="string"&&this._sasLocalFingerprint.trim().length>0&&typeof this._sasRemoteFingerprint=="string"&&this._sasRemoteFingerprint.trim().length>0;return e&&t&&r&&i}_notifyVerificationReadyIfPossible(){return this._isVerificationReady()?(this._verificationUiOpened||(this._verificationUiOpened=!0,this.onStatusChange?.("verifying"),this.onVerificationRequired?.(this.verificationCode)),!0):!1}_countIceCandidatesInSDP(e){return typeof e!="string"?0:(e.match(/^a=candidate:/gm)||[]).length}_summarizeIceCandidatesInSDP(e){let t={total:0,host:0,srflx:0,relay:0,prflx:0,unknown:0};if(typeof e!="string")return t;for(let r of e.match(/^a=candidate:.*$/gm)||[]){t.total+=1;let n=r.match(/\btyp\s+(host|srflx|relay|prflx)\b/i)?.[1]?.toLowerCase();n&&Object.prototype.hasOwnProperty.call(t,n)?t[n]+=1:t.unknown+=1}return t}_describeIceCandidatesInSDP(e){return typeof e!="string"?[]:(e.match(/^a=candidate:.*$/gm)||[]).map(t=>{let r=t.slice(12).trim().split(/\s+/),i=r.findIndex(u=>u.toLowerCase()==="typ"),n=r[4]||"",a=r[5]||"",o=i>=0&&r[i+1]||"unknown",c=(r[2]||"unknown").toLowerCase(),d="unknown";return/\.local$/i.test(n)?d="mdns":/^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.)/.test(n)?d="private-ipv4":/^\d{1,3}(\.\d{1,3}){3}$/.test(n)?d="public-ipv4":n.includes(":")&&(d="ipv6"),{candidateType:o,protocol:c,addressKind:d,portPresent:!!a,tcpType:(()=>{let u=r.findIndex(h=>h.toLowerCase()==="tcptype");return u>=0&&r[u+1]||null})()}})}_logIceCandidateDiagnostics(e,t,r={}){let i=this._summarizeIceCandidatesInSDP(t),n=this._describeIceCandidatesInSDP(t);return console.info(`[SecureBit ICE] ${e}`,{candidateSummary:i,candidateDetails:n,candidateDetailsJson:JSON.stringify(n),...r}),{candidateSummary:i,candidateDetails:n}}_hasOnlyMdnsHostCandidates(e){let t=this._summarizeIceCandidatesInSDP(e),r=this._describeIceCandidatesInSDP(e);return t.total>0&&t.srflx===0&&t.relay===0&&t.prflx===0&&r.every(i=>i.candidateType==="host"&&i.addressKind==="mdns")}_warnIfRemoteCandidatesNeedRelay(e,t){if(!this._hasOnlyMdnsHostCandidates(t))return!1;let r=e==="answer"?"Connection warning: the response contains only browser-masked mDNS host candidates and no server-reflexive or TURN relay candidates. This network/browser combination may not connect until TURN is configured.":"Connection warning: the i
Line truncated
|
||||||
`),e=e.replace(/\n{3,}/g,`
|
`),e=e.replace(/\n{3,}/g,`
|
||||||
|
|
||||||
`),e=e.trim()),e}_sanitizeInputObject(e){if(e===null||typeof e!="object")return e;if(Array.isArray(e))return e.map(r=>this._sanitizeInputObject(r));let t={};for(let r in e)if(e.hasOwnProperty(r)){let i=e[r];typeof i=="string"?t[r]=this._sanitizeInputString(i):typeof i=="object"?t[r]=this._sanitizeInputObject(i):t[r]=i}return t}_checkRateLimit(e="message"){let t=Date.now();return this._rateLimiter||(this._rateLimiter={messageCount:0,lastReset:t,burstCount:0,lastBurstReset:t}),t-this._rateLimiter.lastReset>6e4&&(this._rateLimiter.messageCount=0,this._rateLimiter.lastReset=t),t-this._rateLimiter.lastBurstReset>1e3&&(this._rateLimiter.burstCount=0,this._rateLimiter.lastBurstReset=t),this._rateLimiter.burstCount>=this._inputValidationLimits.rateLimitBurstSize?(this._secureLog("warn","\u26A0\uFE0F Rate limit burst exceeded",{context:e}),!1):this._rateLimiter.messageCount>=this._inputValidationLimits.rateLimitMessagesPerMinute?(this._secureLog("warn","\u26A0\uFE0F Rate limit exceeded",{context:e}),!1):(this._rateLimiter.messageCount++,this._rateLimiter.burstCount++,!0)}_checkInboundRateLimit(e="incoming_message"){let t=Date.now();return this._inboundRateLimiter||(this._inboundRateLimiter={messageCount:0,lastReset:t,burstCount:0,lastBurstReset:t}),t-this._inboundRateLimiter.lastReset>6e4&&(this._inboundRateLimiter.messageCount=0,this._inboundRateLimiter.lastReset=t),t-this._inboundRateLimiter.lastBurstReset>1e3&&(this._inboundRateLimiter.burstCount=0,this._inboundRateLimiter.lastBurstReset=t),this._inboundRateLimiter.burstCount>=this._inputValidationLimits.rateLimitBurstSize?(this._secureLog("warn","\u26A0\uFE0F Inbound message burst limit exceeded; dropping message",{context:e}),!1):this._inboundRateLimiter.messageCount>=this._inputValidationLimits.rateLimitMessagesPerMinute?(this._secureLog("warn","\u26A0\uFE0F Inbound message rate limit exceeded; dropping message",{context:e}),!1):(this._inboundRateLimiter.messageCount++,this._inboundRateLimiter.burstCount++,!0)}_initializeSecureKeyStorage(){this._masterKeyManager=new Gt,this._secureKeyStorage=new Ht(this._masterKeyManager),this._keyStorageStats={totalKeys:0,activeKeys:0,lastAccess:null,lastRotation:null},this._secureLog("info","\u{1F510} Enhanced secure key storage initialized")}setMasterKeyPasswordCallback(e){this._masterKeyManager&&this._masterKeyManager.setPasswordRequiredCallback(e)}setMasterKeySessionExpiredCallback(e){this._masterKeyManager&&this._masterKeyManager.setSessionExpiredCallback(e)}lockMasterKey(){this._masterKeyManager&&this._masterKeyManager.lock()}isMasterKeyUnlocked(){return this._masterKeyManager?this._masterKeyManager.isUnlocked():!1}getMasterKeySessionStatus(){return this._masterKeyManager?this._masterKeyManager.getSessionStatus():null}async _ensureFileTransferReady(){try{if(this.fileTransferSystem)return!0;if(!this.dataChannel||this.dataChannel.readyState!=="open")throw new Error("Data channel not open");if(!this.isVerified)throw new Error("Connection not verified");this.initializeFileTransfer();let e=0,t=50;for(;!this.fileTransferSystem&&e<t;)await new Promise(r=>setTimeout(r,100)),e++;if(!this.fileTransferSystem)throw new Error("File transfer system initialization timeout");return!0}catch(e){return this._secureLog("error","\u274C _ensureFileTransferReady failed",{errorType:e?.constructor?.name||"Unknown",hasMessage:!!e?.message}),!1}}_getSecureKey(e){return this._secureKeyStorage.retrieveKey(e)}async _setSecureKey(e,t){if(!(t instanceof CryptoKey))return this._secureLog("error","\u274C Attempt to store non-CryptoKey"),!1;let r=await this._secureKeyStorage.storeKey(e,t,{version:this.currentKeyVersion,type:t.algorithm.name});return r&&this._secureLog("info",`\u{1F511} Key ${e} stored securely with encryption`),r}_validateKeyValue(e){return e instanceof CryptoKey&&e.algorithm&&e.usages&&e.usages.length>0}_secureWipeKeys(){this._secureKeyStorage.secureWipeAll(),this._masterKeyManager&&this._masterKeyManager.lock(),this._secureLog("info","\u{1F9F9} All keys securely wiped and encrypted storage cleared")}_validateKeyStorage(){return this._secureKeyStorage instanceof Ht}_getKeyStorageStats(){let e=this._secureKeyStorage.getStorageStats();return{totalKeysCount:e.totalKeys,activeKeysCount:e.totalKeys,hasLastAccess:e.metadata.some(t=>t.lastAccessed),hasLastRotation:!!this._keyStorageStats.lastRotation,storageType:"SecureKeyStorage",timestamp:Date.now()}}_rotateKeys(){let e=Array.from(this._secureKeyStorage.keys());this._secureKeyStorage.clear(),this._keyStorageStats.lastRotation=Date.now(),this._keyStorageStats.activeKeys=0,this._secureLog("info",`\u{1F504} Key rotation completed. ${e.length} keys rotated`)}_emergencyKeyWipe(){this._secureWipeKeys(),this._secureLog("error","\u{1F6A8} EMERGENCY: All keys wiped due to security threat")}_startKeySecurityMonitoring(){this._secureLog("info","\u{1F527} Key security monitoring moved to unified scheduler")}_validateKeyConstantTime(e){let t=0;try{let r=e instanceof CryptoKey;t+=r?1:0}catch{t+=0}try{let
Line truncated
|
`),e=e.trim()),e}_sanitizeInputObject(e){if(e===null||typeof e!="object")return e;if(Array.isArray(e))return e.map(r=>this._sanitizeInputObject(r));let t={};for(let r in e)if(e.hasOwnProperty(r)){let i=e[r];typeof i=="string"?t[r]=this._sanitizeInputString(i):typeof i=="object"?t[r]=this._sanitizeInputObject(i):t[r]=i}return t}_checkRateLimit(e="message"){let t=Date.now();return this._rateLimiter||(this._rateLimiter={messageCount:0,lastReset:t,burstCount:0,lastBurstReset:t}),t-this._rateLimiter.lastReset>6e4&&(this._rateLimiter.messageCount=0,this._rateLimiter.lastReset=t),t-this._rateLimiter.lastBurstReset>1e3&&(this._rateLimiter.burstCount=0,this._rateLimiter.lastBurstReset=t),this._rateLimiter.burstCount>=this._inputValidationLimits.rateLimitBurstSize?(this._secureLog("warn","\u26A0\uFE0F Rate limit burst exceeded",{context:e}),!1):this._rateLimiter.messageCount>=this._inputValidationLimits.rateLimitMessagesPerMinute?(this._secureLog("warn","\u26A0\uFE0F Rate limit exceeded",{context:e}),!1):(this._rateLimiter.messageCount++,this._rateLimiter.burstCount++,!0)}_checkInboundRateLimit(e="incoming_message"){let t=Date.now();return this._inboundRateLimiter||(this._inboundRateLimiter={messageCount:0,lastReset:t,burstCount:0,lastBurstReset:t}),t-this._inboundRateLimiter.lastReset>6e4&&(this._inboundRateLimiter.messageCount=0,this._inboundRateLimiter.lastReset=t),t-this._inboundRateLimiter.lastBurstReset>1e3&&(this._inboundRateLimiter.burstCount=0,this._inboundRateLimiter.lastBurstReset=t),this._inboundRateLimiter.burstCount>=this._inputValidationLimits.rateLimitBurstSize?(this._secureLog("warn","\u26A0\uFE0F Inbound message burst limit exceeded; dropping message",{context:e}),!1):this._inboundRateLimiter.messageCount>=this._inputValidationLimits.rateLimitMessagesPerMinute?(this._secureLog("warn","\u26A0\uFE0F Inbound message rate limit exceeded; dropping message",{context:e}),!1):(this._inboundRateLimiter.messageCount++,this._inboundRateLimiter.burstCount++,!0)}_initializeSecureKeyStorage(){this._masterKeyManager=new jt,this._secureKeyStorage=new Gt(this._masterKeyManager),this._keyStorageStats={totalKeys:0,activeKeys:0,lastAccess:null,lastRotation:null},this._secureLog("info","\u{1F510} Enhanced secure key storage initialized")}setMasterKeyPasswordCallback(e){this._masterKeyManager&&this._masterKeyManager.setPasswordRequiredCallback(e)}setMasterKeySessionExpiredCallback(e){this._masterKeyManager&&this._masterKeyManager.setSessionExpiredCallback(e)}lockMasterKey(){this._masterKeyManager&&this._masterKeyManager.lock()}isMasterKeyUnlocked(){return this._masterKeyManager?this._masterKeyManager.isUnlocked():!1}getMasterKeySessionStatus(){return this._masterKeyManager?this._masterKeyManager.getSessionStatus():null}async _ensureFileTransferReady(){try{if(this.fileTransferSystem)return!0;if(!this.dataChannel||this.dataChannel.readyState!=="open")throw new Error("Data channel not open");if(!this.isVerified)throw new Error("Connection not verified");this.initializeFileTransfer();let e=0,t=50;for(;!this.fileTransferSystem&&e<t;)await new Promise(r=>setTimeout(r,100)),e++;if(!this.fileTransferSystem)throw new Error("File transfer system initialization timeout");return!0}catch(e){return this._secureLog("error","\u274C _ensureFileTransferReady failed",{errorType:e?.constructor?.name||"Unknown",hasMessage:!!e?.message}),!1}}_getSecureKey(e){return this._secureKeyStorage.retrieveKey(e)}async _setSecureKey(e,t){if(!(t instanceof CryptoKey))return this._secureLog("error","\u274C Attempt to store non-CryptoKey"),!1;let r=await this._secureKeyStorage.storeKey(e,t,{version:this.currentKeyVersion,type:t.algorithm.name});return r&&this._secureLog("info",`\u{1F511} Key ${e} stored securely with encryption`),r}_validateKeyValue(e){return e instanceof CryptoKey&&e.algorithm&&e.usages&&e.usages.length>0}_secureWipeKeys(){this._secureKeyStorage.secureWipeAll(),this._masterKeyManager&&this._masterKeyManager.lock(),this._secureLog("info","\u{1F9F9} All keys securely wiped and encrypted storage cleared")}_validateKeyStorage(){return this._secureKeyStorage instanceof Gt}_getKeyStorageStats(){let e=this._secureKeyStorage.getStorageStats();return{totalKeysCount:e.totalKeys,activeKeysCount:e.totalKeys,hasLastAccess:e.metadata.some(t=>t.lastAccessed),hasLastRotation:!!this._keyStorageStats.lastRotation,storageType:"SecureKeyStorage",timestamp:Date.now()}}_rotateKeys(){let e=Array.from(this._secureKeyStorage.keys());this._secureKeyStorage.clear(),this._keyStorageStats.lastRotation=Date.now(),this._keyStorageStats.activeKeys=0,this._secureLog("info",`\u{1F504} Key rotation completed. ${e.length} keys rotated`)}_emergencyKeyWipe(){this._secureWipeKeys(),this._secureLog("error","\u{1F6A8} EMERGENCY: All keys wiped due to security threat")}_startKeySecurityMonitoring(){this._secureLog("info","\u{1F527} Key security monitoring moved to unified scheduler")}_validateKeyConstantTime(e){let t=0;try{let r=e instanceof CryptoKey;t+=r?1:0}catch{t+=0}try{let
Line truncated
|
||||||
|
|
||||||
`;if(D+=`Security Level: ${I.level} (${I.score}%)
|
`;if(D+=`Security Level: ${I.level} (${I.score}%)
|
||||||
`,D+=`Verification Time: ${new Date(I.timestamp).toLocaleTimeString()}
|
`,D+=`Verification Time: ${new Date(I.timestamp).toLocaleTimeString()}
|
||||||
@@ -50,11 +50,11 @@ Please wait for real-time cryptographic verification to complete.`,"sec.verifica
|
|||||||
|
|
||||||
`,I.verificationResults){D+=`DETAILED CRYPTOGRAPHIC TESTS:
|
`,I.verificationResults){D+=`DETAILED CRYPTOGRAPHIC TESTS:
|
||||||
`,D+="="+"=".repeat(40)+`
|
`,D+="="+"=".repeat(40)+`
|
||||||
`;let Y=Object.entries(I.verificationResults).filter(([_e,ye])=>ye.passed),Se=Object.entries(I.verificationResults).filter(([_e,ye])=>!ye.passed);Y.length>0&&(D+=`PASSED TESTS:
|
`;let Z=Object.entries(I.verificationResults).filter(([_e,me])=>me.passed),be=Object.entries(I.verificationResults).filter(([_e,me])=>!me.passed);Z.length>0&&(D+=`PASSED TESTS:
|
||||||
`,Y.forEach(([_e,ye])=>{let Ye=_e.replace(/([A-Z])/g," $1").replace(/^./,W=>W.toUpperCase());D+=` ${Ye}: ${ye.details||f("sec.testPassed")}
|
`,Z.forEach(([_e,me])=>{let Ye=_e.replace(/([A-Z])/g," $1").replace(/^./,X=>X.toUpperCase());D+=` ${Ye}: ${me.details||f("sec.testPassed")}
|
||||||
`}),D+=`
|
`}),D+=`
|
||||||
`),Se.length>0&&(D+=`FAILED/UNAVAILABLE TESTS:
|
`),be.length>0&&(D+=`FAILED/UNAVAILABLE TESTS:
|
||||||
`,Se.forEach(([_e,ye])=>{let Ye=_e.replace(/([A-Z])/g," $1").replace(/^./,W=>W.toUpperCase());D+=` ${Ye}: ${ye.details||f("sec.testFailed")}
|
`,be.forEach(([_e,me])=>{let Ye=_e.replace(/([A-Z])/g," $1").replace(/^./,X=>X.toUpperCase());D+=` ${Ye}: ${me.details||f("sec.testFailed")}
|
||||||
`}),D+=`
|
`}),D+=`
|
||||||
`),D+=`SUMMARY:
|
`),D+=`SUMMARY:
|
||||||
`,D+=`Passed: ${I.passedChecks}/${I.totalChecks} tests
|
`,D+=`Passed: ${I.passedChecks}/${I.totalChecks} tests
|
||||||
@@ -62,7 +62,7 @@ Please wait for real-time cryptographic verification to complete.`,"sec.verifica
|
|||||||
|
|
||||||
`}if(D+=`SECURITY FEATURES STATUS:
|
`}if(D+=`SECURITY FEATURES STATUS:
|
||||||
`,D+="="+"=".repeat(40)+`
|
`,D+="="+"=".repeat(40)+`
|
||||||
`,I.verificationResults){let Y={"ECDSA Digital Signatures":I.verificationResults.verifyECDSASignatures?.passed||!1,"ECDH Key Exchange":I.verificationResults.verifyECDHKeyExchange?.passed||!1,"AES-GCM Encryption":I.verificationResults.verifyEncryption?.passed||!1,[f("sec.messageIntegrity")]:I.verificationResults.verifyMessageIntegrity?.passed||!1,[f("sec.forwardSecrecy")]:I.verificationResults.verifyPerfectForwardSecrecy?.passed||!1,[f("sec.replayProtection")]:I.verificationResults.verifyReplayProtection?.passed||!1,"DTLS Fingerprint":I.verificationResults.verifyDTLSFingerprint?.passed||!1,"SAS Verification":I.verificationResults.verifySASVerification?.passed||!1,[f("sec.metadataProtection")]:I.verificationResults.verifyMetadataProtection?.passed||!1,[f("sec.trafficObfuscation")]:I.verificationResults.verifyTrafficObfuscation?.passed||!1};Object.entries(Y).forEach(([Se,_e])=>{D+=`${_e?"\u2705":"\u274C"} ${Se}
|
`,I.verificationResults){let Z={"ECDSA Digital Signatures":I.verificationResults.verifyECDSASignatures?.passed||!1,"ECDH Key Exchange":I.verificationResults.verifyECDHKeyExchange?.passed||!1,"AES-GCM Encryption":I.verificationResults.verifyEncryption?.passed||!1,[f("sec.messageIntegrity")]:I.verificationResults.verifyMessageIntegrity?.passed||!1,[f("sec.forwardSecrecy")]:I.verificationResults.verifyPerfectForwardSecrecy?.passed||!1,[f("sec.replayProtection")]:I.verificationResults.verifyReplayProtection?.passed||!1,"DTLS Fingerprint":I.verificationResults.verifyDTLSFingerprint?.passed||!1,"SAS Verification":I.verificationResults.verifySASVerification?.passed||!1,[f("sec.metadataProtection")]:I.verificationResults.verifyMetadataProtection?.passed||!1,[f("sec.trafficObfuscation")]:I.verificationResults.verifyTrafficObfuscation?.passed||!1};Object.entries(Z).forEach(([be,_e])=>{D+=`${_e?"\u2705":"\u274C"} ${be}
|
||||||
`})}else D+=`\u2705 ECDSA Digital Signatures
|
`})}else D+=`\u2705 ECDSA Digital Signatures
|
||||||
`,D+=`\u2705 ECDH Key Exchange
|
`,D+=`\u2705 ECDH Key Exchange
|
||||||
`,D+=`\u2705 AES-GCM Encryption
|
`,D+=`\u2705 AES-GCM Encryption
|
||||||
@@ -78,7 +78,7 @@ ${I.details||f("sec.verificationDone")}`,I.isRealData?D+=`
|
|||||||
|
|
||||||
\u2705 This is REAL-TIME verification using actual cryptographic functions.`:D+=`
|
\u2705 This is REAL-TIME verification using actual cryptographic functions.`:D+=`
|
||||||
|
|
||||||
\u26A0\uFE0F Warning: This data may be simulated. Connection may not be fully established.`;let K=document.createElement("div");K.style.cssText=`
|
\u26A0\uFE0F Warning: This data may be simulated. Connection may not be fully established.`;let N=document.createElement("div");N.style.cssText=`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -90,7 +90,7 @@ ${I.details||f("sec.verificationDone")}`,I.isRealData?D+=`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
`;let O=document.createElement("div");O.style.cssText=`
|
`;let z=document.createElement("div");z.style.cssText=`
|
||||||
background: var(--sb-legacy-panel);
|
background: var(--sb-legacy-panel);
|
||||||
color: var(--sb-text-1);
|
color: var(--sb-text-1);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -100,11 +100,11 @@ ${I.details||f("sec.verificationDone")}`,I.isRealData?D+=`
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
border: 1px solid var(--sb-surface-5);
|
border: 1px solid var(--sb-surface-5);
|
||||||
`,O.textContent=D,K.appendChild(O),K.addEventListener("click",Y=>{Y.target===K&&document.body.removeChild(K)});let q=Y=>{Y.key==="Escape"&&(document.body.removeChild(K),document.removeEventListener("keydown",q))};document.addEventListener("keydown",q),document.body.appendChild(K)},F=(()=>{switch(n){case"connected":return{text:f("status.connected"),className:"status-connected",badgeClass:"bg-green-500/10 text-green-400 border-green-500/20"};case"verifying":return{text:f("status.verifying"),className:"status-verifying",badgeClass:"bg-purple-500/10 text-purple-400 border-purple-500/20"};case"connecting":return{text:f("status.connecting"),className:"status-connecting",badgeClass:"bg-blue-500/10 text-blue-400 border-blue-500/20"};case"retrying":return{text:f("status.retrying"),className:"status-connecting",badgeClass:"bg-yellow-500/10 text-yellow-400 border-yellow-500/20"};case"failed":return{text:f("status.error"),className:"status-failed",badgeClass:"bg-red-500/10 text-red-400 border-red-500/20"};case"reconnecting":return{text:f("status.reconnecting"),className:"status-connecting",badgeClass:"bg-yellow-500/10 text-yellow-400 border-yellow-500/20"};case"peer_disconnected":return{text:f("status.peerDisconnected"),className:"status-failed",badgeClass:"bg-orange-500/10 text-orange-400 border-orange-500/20"};default:return{text:f("status.notConnected"),className:"status-disconnected",badgeClass:"bg-gray-500/10 text-gray-400 border-gray-500/20"}}})(),k=i?o||s:null,b=(()=>{if(!k)return{tooltip:f("sec.verificationInProgress"),isVerified:!1,dataSource:"loading"};let G=k.isRealData!==!1,N=`${k.level} (${k.score}%)`;return G?{tooltip:`${N} - Real-time verification \u2705
|
`,z.textContent=D,N.appendChild(z),N.addEventListener("click",Z=>{Z.target===N&&document.body.removeChild(N)});let W=Z=>{Z.key==="Escape"&&(document.body.removeChild(N),document.removeEventListener("keydown",W))};document.addEventListener("keydown",W),document.body.appendChild(N)},F=(()=>{switch(s){case"connected":return{text:f("status.connected"),className:"status-connected",badgeClass:"bg-green-500/10 text-green-400 border-green-500/20"};case"verifying":return{text:f("status.verifying"),className:"status-verifying",badgeClass:"bg-purple-500/10 text-purple-400 border-purple-500/20"};case"connecting":return{text:f("status.connecting"),className:"status-connecting",badgeClass:"bg-blue-500/10 text-blue-400 border-blue-500/20"};case"retrying":return{text:f("status.retrying"),className:"status-connecting",badgeClass:"bg-yellow-500/10 text-yellow-400 border-yellow-500/20"};case"failed":return{text:f("status.error"),className:"status-failed",badgeClass:"bg-red-500/10 text-red-400 border-red-500/20"};case"reconnecting":return{text:f("status.reconnecting"),className:"status-connecting",badgeClass:"bg-yellow-500/10 text-yellow-400 border-yellow-500/20"};case"peer_disconnected":return{text:f("status.peerDisconnected"),className:"status-failed",badgeClass:"bg-orange-500/10 text-orange-400 border-orange-500/20"};default:return{text:f("status.notConnected"),className:"status-disconnected",badgeClass:"bg-gray-500/10 text-gray-400 border-gray-500/20"}}})(),k=i?o||n:null,_=(()=>{if(!k)return{tooltip:f("sec.verificationInProgress"),isVerified:!1,dataSource:"loading"};let G=k.isRealData!==!1,U=`${k.level} (${k.score}%)`;return G?{tooltip:`${U} - Real-time verification \u2705
|
||||||
Right-click or Ctrl+click to disconnect`,isVerified:!0,dataSource:"real"}:{tooltip:`${N} - Estimated (connection establishing...)
|
Right-click or Ctrl+click to disconnect`,isVerified:!0,dataSource:"real"}:{tooltip:`${U} - Estimated (connection establishing...)
|
||||||
Right-click or Ctrl+click to disconnect`,isVerified:!1,dataSource:"estimated"}})();React.useEffect(()=>(window.debugHeaderSecurity=void 0,()=>{delete window.debugHeaderSecurity}),[o,d,i,a,k,b]);let A=k?k.color==="green"?"var(--sb-green)":k.color==="orange"?"var(--sb-orange)":k.color==="yellow"?"var(--sb-yellow)":"var(--sb-red)":"var(--sb-green)",x=i?"var(--sb-green-solid)":["connecting","verifying","retrying","reconnecting"].includes(n)?"var(--sb-yellow-solid)":n==="failed"?"var(--sb-red-solid)":"var(--sb-text-9)",T=x==="var(--sb-green-solid)"?"rgba(var(--sb-green-rgb), 0.16)":x==="var(--sb-yellow-solid)"?"rgba(var(--sb-yellow-rgb), 0.16)":x==="var(--sb-red-solid)"?"rgba(var(--sb-red-rgb), 0.16)":"rgba(var(--sb-text-9-rgb), 0.16)",j="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",z=!i,[R,fe]=React.useState(!1);React.useEffect(()=>{let G=()=>fe((window.scrollY||window.pageYOffset||0)>8);return G(),window.addEventListener("scroll",G,{passive:!0}),()=>window.removeEventListener("scroll",G)},[]);let re="blur(20px) saturate(180%)",pe="background .25s ease, backdrop-filter .25s ease, -webkit-backdrop-filter .25s ease, border-color .25s ease",me={paddingTop:"var(--sb-safe-top, 0px)",paddingBottom:"var(--sb-bar-extra, 0px)"},ce={position:"fixed",top:0,left:0,right:0,...me},ve=z?R?{...ce,background:"rgba(var(--sb-bg-rgb), 0.72)",backdropFilter:re,WebkitBackdropFilter:re,borderBottom:"1px solid rgba(var(--sb-ink), 0.06)",transition:pe}:{...ce,background:"transparent",backdropFilter:"blur(0px) saturate(100%)",WebkitBackdropFilter:"blur(0px) saturate(100%)",borderBottom:"1px solid transparent",transition:pe}:{...me,background:"rgba(var(--sb-bg-rgb), 0.72)",backdropFilter:re,WebkitBackdropFilter:re,borderBottom:"1px solid rgba(var(--sb-ink), 0.06)"};return React.createElement("header",{className:z?"header-minimal z-50":"header-minimal sticky top-0 z-50",style:ve},[React.createElement("div",{key:"container",className:"max-w-7xl mx-auto",style:{padding:"0 20px"}},[React.createElement("div",{key:"content",className:"flex items-center justify-between",style:{height:"var(--sb-bar-h, 64px)",gap:"16px"}},[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:"var(--sb-text-2)"}},"SecureBit"),React.createElement("span",{key:"v",style:{fontFamily:j,fontSize:"10px",fontWeight:500,color:"var(--sb-text-faint)"}},ka)]),React.createElement("div",{key:"r2",className:"hidden sm:block",style:{fontSize:"11px",color:"var(--sb-text-9)",fontWeight:500}},f("hdr.tagline"))])]),React.createElement("div",{key:"right",style:{display:"flex",alignItems:"center",gap:"9px"}},[z&&React.createElement(Vr,{key:"lang"}),z&&React.createElement(Hr,{key:"theme"}),!z&&React.createElement("button",{key:"net",type:"button",onClick:()=>window.dispatchEvent(new CustomEvent("securebit:open-network-settings")),title:f("hdr.netSettingsTitle"),"aria-label":f("hdr.netSettings"),className:"sb-disconnect",style:{display:"grid",placeItems:"center",width:"38px",height:"38px",borderRadius:"9px",border:"1px solid rgba(var(--sb-ink), 0.07)",background:"rgba(var(--sb-ink), 0.02)",color:"var(--sb-text-6)",cursor:"pointer",transition:"all .15s"}},React.createElement("i",{className:"fas fa-network-wired",style:{fontSize:"13px"}})),!z&&k&&React.createElement("div",{key:"sec",onClick:w,onContextMenu:G=>{G.preventDefault(),typeof r=="function"&&r()},title:b.tooltip,className:"sb-secpill",style:{display:"flex",alignItems:"center",gap:"8px",padding:"7px 12px",borderRadius:"9px",border:"1px solid rgba(var(--sb-ink), 0.07)",background:"rgba(var(--sb-ink), 0.02)",cursor:"pointer"}},[React.createElement("i",{key:"i",className:"fas fa-shield-halved",style:{fontSize:"13px",color:A}}),React.createElement("span",{key:"l",className:"hidden sm:inline",style:{fontSize:"12.5px",fontWeight:600,color:"var(--sb-text-2)"}},f(`secLevel.${k.level}`)===`secLevel.${k.level}`?String(k.level):f(`secLevel.${k.level}`)),React.createElement("span",{key:"s",style:{fontFamily:j,fontSize:"11.5px",color:"var(--sb-text-7)"}},k.score+"%")]),!z&&React.createElement("div",{key:"status",style:{display:"flex",alignItems:"center",gap:"8px",padding:"8px 13px",borderRadius:"9px",border:"1px solid rgba(var(--sb-ink), 0.07)",background:"rgba(var(--sb-ink), 0.02)"}},[React.createElement("span",{key:"dot",style:{width:"7px",height:"7px",borderRadius:"50%",background:x,boxShadow:"0 0 0 3px "+T}}),React.createElement("span",{key
Line truncated
|
Right-click or Ctrl+click to disconnect`,isVerified:!1,dataSource:"estimated"}})();React.useEffect(()=>(window.debugHeaderSecurity=void 0,()=>{delete window.debugHeaderSecurity}),[o,d,i,a,k,_]);let A=k?k.color==="green"?"var(--sb-green)":k.color==="orange"?"var(--sb-orange)":k.color==="yellow"?"var(--sb-yellow)":"var(--sb-red)":"var(--sb-green)",x=i?"var(--sb-green-solid)":["connecting","verifying","retrying","reconnecting"].includes(s)?"var(--sb-yellow-solid)":s==="failed"?"var(--sb-red-solid)":"var(--sb-text-9)",T=x==="var(--sb-green-solid)"?"rgba(var(--sb-green-rgb), 0.16)":x==="var(--sb-yellow-solid)"?"rgba(var(--sb-yellow-rgb), 0.16)":x==="var(--sb-red-solid)"?"rgba(var(--sb-red-rgb), 0.16)":"rgba(var(--sb-text-9-rgb), 0.16)",Y="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",B=!i,[R,ue]=React.useState(!1);React.useEffect(()=>{let G=()=>ue((window.scrollY||window.pageYOffset||0)>8);return G(),window.addEventListener("scroll",G,{passive:!0}),()=>window.removeEventListener("scroll",G)},[]);let K="blur(20px) saturate(180%)",O="background .25s ease, backdrop-filter .25s ease, -webkit-backdrop-filter .25s ease, border-color .25s ease",Q={paddingTop:"var(--sb-safe-top, 0px)",paddingBottom:"var(--sb-bar-extra, 0px)"},J={position:"fixed",top:0,left:0,right:0,...Q},ge=B?R?{...J,background:"rgba(var(--sb-bg-rgb), 0.72)",backdropFilter:K,WebkitBackdropFilter:K,borderBottom:"1px solid rgba(var(--sb-ink), 0.06)",transition:O}:{...J,background:"transparent",backdropFilter:"blur(0px) saturate(100%)",WebkitBackdropFilter:"blur(0px) saturate(100%)",borderBottom:"1px solid transparent",transition:O}:{...Q,background:"rgba(var(--sb-bg-rgb), 0.72)",backdropFilter:K,WebkitBackdropFilter:K,borderBottom:"1px solid rgba(var(--sb-ink), 0.06)"};return React.createElement("header",{className:B?"header-minimal z-50":"header-minimal sticky top-0 z-50",style:ge},[React.createElement("div",{key:"container",className:"max-w-7xl mx-auto",style:{padding:"0 20px"}},[React.createElement("div",{key:"content",className:"flex items-center justify-between",style:{height:"var(--sb-bar-h, 64px)",gap:"16px"}},[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:"var(--sb-text-2)"}},"SecureBit"),React.createElement("span",{key:"v",style:{fontFamily:Y,fontSize:"10px",fontWeight:500,color:"var(--sb-text-faint)"}},Ma)]),React.createElement("div",{key:"r2",className:"hidden sm:block",style:{fontSize:"11px",color:"var(--sb-text-9)",fontWeight:500}},f("hdr.tagline"))])]),React.createElement("div",{key:"right",style:{display:"flex",alignItems:"center",gap:"9px"}},[B&&React.createElement(Hr,{key:"lang"}),B&&React.createElement(Gr,{key:"theme"}),!B&&React.createElement("button",{key:"net",type:"button",onClick:()=>window.dispatchEvent(new CustomEvent("securebit:open-network-settings")),title:f("hdr.netSettingsTitle"),"aria-label":f("hdr.netSettings"),className:"sb-disconnect",style:{display:"grid",placeItems:"center",width:"38px",height:"38px",borderRadius:"9px",border:"1px solid rgba(var(--sb-ink), 0.07)",background:"rgba(var(--sb-ink), 0.02)",color:"var(--sb-text-6)",cursor:"pointer",transition:"all .15s"}},React.createElement("i",{className:"fas fa-network-wired",style:{fontSize:"13px"}})),!B&&k&&React.createElement("div",{key:"sec",onClick:w,onContextMenu:G=>{G.preventDefault(),typeof r=="function"&&r()},title:_.tooltip,className:"sb-secpill",style:{display:"flex",alignItems:"center",gap:"8px",padding:"7px 12px",borderRadius:"9px",border:"1px solid rgba(var(--sb-ink), 0.07)",background:"rgba(var(--sb-ink), 0.02)",cursor:"pointer"}},[React.createElement("i",{key:"i",className:"fas fa-shield-halved",style:{fontSize:"13px",color:A}}),React.createElement("span",{key:"l",className:"hidden sm:inline",style:{fontSize:"12.5px",fontWeight:600,color:"var(--sb-text-2)"}},f(`secLevel.${k.level}`)===`secLevel.${k.level}`?String(k.level):f(`secLevel.${k.level}`)),React.createElement("span",{key:"s",style:{fontFamily:Y,fontSize:"11.5px",color:"var(--sb-text-7)"}},k.score+"%")]),!B&&React.createElement("div",{key:"status",style:{display:"flex",alignItems:"center",gap:"8px",padding:"8px 13px",borderRadius:"9px",border:"1px solid rgba(var(--sb-ink), 0.07)",background:"rgba(var(--sb-ink), 0.02)"}},[React.createElement("span",{key:"dot",style:{width:"7px",height:"7px",borderRadius:"50%",background:x,boxShadow:"0 0 0 3px "+T}}),React.createElement("span",{key:"t",className
Line truncated
|
||||||
`).map(r=>r.trim()).filter(Boolean).map(r=>({urls:r}));return Rs(t)}function Ps(n){return Array.isArray(n)?n.some(e=>(Array.isArray(e?.urls)?e.urls:[e?.urls]).some(Ls)):!1}var Ve=window.React,Oa=["# One URL per line, e.g.:","stun:stun.example.com:3478","turn:turn.example.com:3478?transport=udp","","# Or paste JSON for servers with credentials:",'[{"urls":"turns:turn.example.com:5349","username":"user","credential":"secret"}]'].join(`
|
`).map(r=>r.trim()).filter(Boolean).map(r=>({urls:r}));return Nn(t)}function Vn(s){return Array.isArray(s)?s.some(e=>(Array.isArray(e?.urls)?e.urls:[e?.urls]).some(Un)):!1}var Ve=window.React,$a=["# One URL per line, e.g.:","stun:stun.example.com:3478","turn:turn.example.com:3478?transport=udp","","# Or paste JSON for servers with credentials:",'[{"urls":"turns:turn.example.com:5349","username":"user","credential":"secret"}]'].join(`
|
||||||
`);async function Ua(n,e=6e3){let t={host:0,srflx:0,relay:0};if(typeof RTCPeerConnection>"u")return{...t,error:f("ice.errUnavailable")};let r;try{r=new RTCPeerConnection({iceServers:n})}catch(i){return{...t,error:i.message||f("ice.errInvalid")}}return new Promise(i=>{let s=!1,a=()=>{if(!s){s=!0,clearTimeout(o);try{r.close()}catch{}i(t)}},o=setTimeout(a,e);r.onicecandidate=c=>{if(!c.candidate){a();return}let d=c.candidate.candidate||"";/ typ host/.test(d)?t.host++:/ typ srflx/.test(d)?t.srflx++:/ typ relay/.test(d)&&t.relay++};try{r.createDataChannel("securebit-ice-test"),r.createOffer().then(c=>r.setLocalDescription(c)).catch(()=>a())}catch{a()}})}var za=({isOpen:n,onClose:e,initial:t,hasSaved:r,onApply:i,onForget:s,embedded:a})=>{if(!n)return null;let[o,c]=Ve.useState(t?.useCustom||!1),[d,u]=Ve.useState(t?.serversText||""),[h,m]=Ve.useState(t?.privacyMode==="relay-only"),[p,S]=Ve.useState(t?.persisted||!1),[g,_]=Ve.useState("idle"),[w,M]=Ve.useState(null),F=o?Ds(d):{servers:[],errors:[],warnings:[]},k=Ps(F.servers),v=!o||F.servers.length>0&&F.errors.length===0,b=async()=>{_("running"),M(null);let N=await Ua(F.servers);M(N),_("done")},A=()=>{v&&i({useCustom:o,servers:o?F.servers:[],privacyMode:h?"relay-only":"standard",serversText:d},p)},x=async()=>{s&&await s(),S(!1)},T=Ve.createElement,j="var(--sb-orange)",z="var(--sb-green)",R="var(--sb-orange-solid)",fe="var(--sb-green-solid)",re="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",pe=(N,I,D,K,O)=>T("button",{type:"button",onClick:I,style:Object.assign({width:"100%",textAlign:"start",display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 15px",borderRadius:"13px",border:`1px solid ${N?"rgba(var(--sb-orange-rgb), 0.45)":"rgba(var(--sb-ink), 0.07)"}`,background:N?"rgba(var(--sb-orange-rgb), 0.06)":"var(--sb-surface)",color:"inherit",fontFamily:"inherit",cursor:"pointer",transition:"all .15s",marginBottom:"10px"},O||{})},[T("span",{key:"ring",style:{flex:"none",width:"18px",height:"18px",marginTop:"1px",borderRadius:"50%",border:`1.5px solid ${N?j:"rgba(var(--sb-ink), 0.22)"}`,display:"grid",placeItems:"center"}},T("span",{style:{width:"8px",height:"8px",borderRadius:"50%",background:N?R:"transparent"}})),T("span",{key:"tx",style:{flex:1}},[T("span",{key:"t",style:{display:"block",fontSize:"14px",fontWeight:700,color:"var(--sb-text-1)"}},D),T("span",{key:"d",style:{display:"block",fontSize:"12.5px",color:"var(--sb-text-7)",marginTop:"2px"}},K)])]),me=(N,I,D,K,O,q)=>T("button",{type:"button",onClick:I,style:{width:"100%",textAlign:"start",display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 15px",borderRadius:"13px",border:`1px solid ${N?"rgba(var(--sb-green-rgb), 0.3)":"rgba(var(--sb-ink), 0.07)"}`,background:N?"rgba(var(--sb-green-rgb), 0.05)":"var(--sb-surface)",color:"inherit",fontFamily:"inherit",cursor:"pointer",transition:"all .15s",marginBottom:"10px"}},[T("span",{key:"tx",style:{flex:1}},[T("span",{key:"r1",style:{display:"flex",alignItems:"center",gap:"8px"}},[T("span",{key:"t",style:{fontSize:"14px",fontWeight:700,color:"var(--sb-text-1)"}},D),q&&T("span",{key:"b",style:{fontSize:"10px",fontWeight:700,color:z,padding:"2px 7px",borderRadius:"5px",background:"rgba(var(--sb-green-rgb), 0.1)",border:"1px solid rgba(var(--sb-green-rgb), 0.22)"}},q)]),T("span",{key:"d",style:{display:"block",fontSize:"12.5px",lineHeight:1.5,color:"var(--sb-text-7)",marginTop:"3px"}},K)]),T("span",{key:"tr",style:{flex:"none",width:"42px",height:"24px",borderRadius:"99px",background:N?O||fe:"rgba(var(--sb-ink), 0.08)",border:`1px solid ${N?O||z:"rgba(var(--sb-ink), 0.12)"}`,position:"relative",transition:"all .18s",marginTop:"1px"}},T("span",{style:{position:"absolute",top:"2px",insetInlineStart:"2px",width:"18px",height:"18px",borderRadius:"50%",background:"#fff",transform:N?`translateX(${18*Oi()}px)`:"translateX(0)",transition:"transform .18s"}}))]),ce=[];if(ce.push(T("p",{key:"intro",style:{margin:"0 0 18px",fontSize:"13.5px",lineHeight:1.6,color:"var(--sb-text-6)"}},f("ice.intro"))),ce.push(pe(!o,()=>c(!1),f("ice.publicTitle"),f("ice.publicDesc"))),ce.push(pe(o,()=>c(!0),f("ice.customTitle"),f("ice.customDesc",{max:ze.MAX_SERVERS}),o?{marginBottom:"14px"}:null)),o){let N=[];N.push(T("div",{key:"ta",style:{borderRadius:"13px",border:"1px solid rgba(var(--sb-ink), 0.08)",background:"var(--sb-bg-deep)",overflow:"hidden",marginBottom:"12px"}},T("textarea",{dir:"ltr",value:d,onChange:D=>u(D.target.value),rows:5,spellCheck:!1,autoComplete:"off",placeholder:Oa,style:{width:"100%",resize:"vertical",border:"none",outline:"none",background:"transparent",color:"var(--sb-text-code)",fontFamily:re,fontSize:"12px",lineHeight:1.65,padding:"13px 14px",minHeight:"104px"}}))),F.errors.length>0&&N.push(T("ul",{key:"err",style:{margin:"0 0 10px",paddingInlineStart:"18px",color:"var(--sb-red)",fontSize:"12.5px"}},F.errors.slice(0,6).map((D,K)=>T("li",{key:K},D)))),F.warnings.length>0&&N.push(T("ul",{key:"warn",style:{margin:"0 0 10px",paddingInlineStar
Line truncated
|
`);async function Ga(s,e=6e3){let t={host:0,srflx:0,relay:0};if(typeof RTCPeerConnection>"u")return{...t,error:f("ice.errUnavailable")};let r;try{r=new RTCPeerConnection({iceServers:s})}catch(i){return{...t,error:i.message||f("ice.errInvalid")}}return new Promise(i=>{let n=!1,a=()=>{if(!n){n=!0,clearTimeout(o);try{r.close()}catch{}i(t)}},o=setTimeout(a,e);r.onicecandidate=c=>{if(!c.candidate){a();return}let d=c.candidate.candidate||"";/ typ host/.test(d)?t.host++:/ typ srflx/.test(d)?t.srflx++:/ typ relay/.test(d)&&t.relay++};try{r.createDataChannel("securebit-ice-test"),r.createOffer().then(c=>r.setLocalDescription(c)).catch(()=>a())}catch{a()}})}var qa=({isOpen:s,onClose:e,initial:t,hasSaved:r,onApply:i,onForget:n,embedded:a})=>{if(!s)return null;let[o,c]=Ve.useState(t?.useCustom||!1),[d,u]=Ve.useState(t?.serversText||""),[h,m]=Ve.useState(t?.privacyMode==="relay-only"),[p,S]=Ve.useState(t?.persisted||!1),[g,b]=Ve.useState("idle"),[w,M]=Ve.useState(null),F=o?zn(d):{servers:[],errors:[],warnings:[]},k=Vn(F.servers),E=!o||F.servers.length>0&&F.errors.length===0,_=async()=>{b("running"),M(null);let U=await Ga(F.servers);M(U),b("done")},A=()=>{E&&i({useCustom:o,servers:o?F.servers:[],privacyMode:h?"relay-only":"standard",serversText:d},p)},x=async()=>{n&&await n(),S(!1)},T=Ve.createElement,Y="var(--sb-orange)",B="var(--sb-green)",R="var(--sb-orange-solid)",ue="var(--sb-green-solid)",K="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",O=(U,I,D,N,z)=>T("button",{type:"button",onClick:I,style:Object.assign({width:"100%",textAlign:"start",display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 15px",borderRadius:"13px",border:`1px solid ${U?"rgba(var(--sb-orange-rgb), 0.45)":"rgba(var(--sb-ink), 0.07)"}`,background:U?"rgba(var(--sb-orange-rgb), 0.06)":"var(--sb-surface)",color:"inherit",fontFamily:"inherit",cursor:"pointer",transition:"all .15s",marginBottom:"10px"},z||{})},[T("span",{key:"ring",style:{flex:"none",width:"18px",height:"18px",marginTop:"1px",borderRadius:"50%",border:`1.5px solid ${U?Y:"rgba(var(--sb-ink), 0.22)"}`,display:"grid",placeItems:"center"}},T("span",{style:{width:"8px",height:"8px",borderRadius:"50%",background:U?R:"transparent"}})),T("span",{key:"tx",style:{flex:1}},[T("span",{key:"t",style:{display:"block",fontSize:"14px",fontWeight:700,color:"var(--sb-text-1)"}},D),T("span",{key:"d",style:{display:"block",fontSize:"12.5px",color:"var(--sb-text-7)",marginTop:"2px"}},N)])]),Q=(U,I,D,N,z,W)=>T("button",{type:"button",onClick:I,style:{width:"100%",textAlign:"start",display:"flex",alignItems:"flex-start",gap:"12px",padding:"14px 15px",borderRadius:"13px",border:`1px solid ${U?"rgba(var(--sb-green-rgb), 0.3)":"rgba(var(--sb-ink), 0.07)"}`,background:U?"rgba(var(--sb-green-rgb), 0.05)":"var(--sb-surface)",color:"inherit",fontFamily:"inherit",cursor:"pointer",transition:"all .15s",marginBottom:"10px"}},[T("span",{key:"tx",style:{flex:1}},[T("span",{key:"r1",style:{display:"flex",alignItems:"center",gap:"8px"}},[T("span",{key:"t",style:{fontSize:"14px",fontWeight:700,color:"var(--sb-text-1)"}},D),W&&T("span",{key:"b",style:{fontSize:"10px",fontWeight:700,color:B,padding:"2px 7px",borderRadius:"5px",background:"rgba(var(--sb-green-rgb), 0.1)",border:"1px solid rgba(var(--sb-green-rgb), 0.22)"}},W)]),T("span",{key:"d",style:{display:"block",fontSize:"12.5px",lineHeight:1.5,color:"var(--sb-text-7)",marginTop:"3px"}},N)]),T("span",{key:"tr",style:{flex:"none",width:"42px",height:"24px",borderRadius:"99px",background:U?z||ue:"rgba(var(--sb-ink), 0.08)",border:`1px solid ${U?z||B:"rgba(var(--sb-ink), 0.12)"}`,position:"relative",transition:"all .18s",marginTop:"1px"}},T("span",{style:{position:"absolute",top:"2px",insetInlineStart:"2px",width:"18px",height:"18px",borderRadius:"50%",background:"#fff",transform:U?`translateX(${18*$i()}px)`:"translateX(0)",transition:"transform .18s"}}))]),J=[];if(J.push(T("p",{key:"intro",style:{margin:"0 0 18px",fontSize:"13.5px",lineHeight:1.6,color:"var(--sb-text-6)"}},f("ice.intro"))),J.push(O(!o,()=>c(!1),f("ice.publicTitle"),f("ice.publicDesc"))),J.push(O(o,()=>c(!0),f("ice.customTitle"),f("ice.customDesc",{max:ze.MAX_SERVERS}),o?{marginBottom:"14px"}:null)),o){let U=[];U.push(T("div",{key:"ta",style:{borderRadius:"13px",border:"1px solid rgba(var(--sb-ink), 0.08)",background:"var(--sb-bg-deep)",overflow:"hidden",marginBottom:"12px"}},T("textarea",{dir:"ltr",value:d,onChange:D=>u(D.target.value),rows:5,spellCheck:!1,autoComplete:"off",placeholder:$a,style:{width:"100%",resize:"vertical",border:"none",outline:"none",background:"transparent",color:"var(--sb-text-code)",fontFamily:K,fontSize:"12px",lineHeight:1.65,padding:"13px 14px",minHeight:"104px"}}))),F.errors.length>0&&U.push(T("ul",{key:"err",style:{margin:"0 0 10px",paddingInlineStart:"18px",color:"var(--sb-red)",fontSize:"12.5px"}},F.errors.slice(0,6).map((D,N)=>T("li",{key:N},D)))),F.warnings.length>0&&U.push(T("ul",{key:"warn",style:{margin:"0 0 10px",paddingInlineStart:"18px",c
Line truncated
|
||||||
/**
|
/**
|
||||||
* Secure and Reliable Notification Manager for P2P WebRTC Chat
|
* Secure and Reliable Notification Manager for P2P WebRTC Chat
|
||||||
* Follows best practices: OWASP, MDN, Chrome DevRel
|
* Follows best practices: OWASP, MDN, Chrome DevRel
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 3,
|
"version": 3,
|
||||||
"sources": ["../src/notifications/SecureNotificationManager.js", "../src/notifications/NotificationIntegration.js", "../node_modules/dompurify/src/utils.ts", "../node_modules/dompurify/src/tags.ts", "../node_modules/dompurify/src/attrs.ts", "../node_modules/dompurify/src/regexp.ts", "../node_modules/dompurify/src/purify.ts", "../src/crypto/EnhancedSecureCryptoUtils.js", "../src/i18n/generated.js", "../src/i18n/dict/en.js", "../src/i18n/index.js", "../src/transfer/EnhancedSecureFileTransfer.js", "../src/network/webrtc/config.js", "../src/network/webrtc/sdp.js", "../src/network/webrtc/audio.js", "../src/network/webrtc/video.js", "../src/network/webrtc/adaptation/metrics.js", "../src/network/webrtc/adaptation/controller.js", "../src/crypto/DoubleRatchet.js", "../src/network/descriptor/sbq2.js", "../src/network/descriptor/keyexchange.js", "../src/network/EnhancedSecureWebRTCManager.js", "../src/scripts/app-boot.js", "../package.json", "../src/components/ui/LanguageSwitcher.jsx", "../src/components/ui/ThemeSwitcher.jsx", "../src/components/ui/Header.jsx", "../src/components/ui/DownloadApps.jsx", "../src/components/ui/BecomePartner.jsx", "../src/components/ui/UniqueFeatureSlider.jsx", "../src/components/ui/Roadmap.jsx", "../src/components/ui/CommunityCTA.jsx", "../src/components/ui/FileTransfer.jsx", "../src/network/iceServers.js", "../src/components/ui/IceServerSettings.jsx", "../src/components/ui/CallUI.jsx"],
|
"sources": ["../src/notifications/SecureNotificationManager.js", "../src/notifications/NotificationIntegration.js", "../node_modules/dompurify/src/utils.ts", "../node_modules/dompurify/src/tags.ts", "../node_modules/dompurify/src/attrs.ts", "../node_modules/dompurify/src/regexp.ts", "../node_modules/dompurify/src/purify.ts", "../src/crypto/EnhancedSecureCryptoUtils.js", "../src/i18n/generated.js", "../src/i18n/dict/en.js", "../src/i18n/index.js", "../src/transfer/EnhancedSecureFileTransfer.js", "../src/network/webrtc/config.js", "../src/network/webrtc/sdp.js", "../src/network/webrtc/audio.js", "../src/network/webrtc/video.js", "../src/network/webrtc/adaptation/metrics.js", "../src/network/webrtc/adaptation/controller.js", "../src/crypto/DoubleRatchet.js", "../src/network/descriptor/sbq2.js", "../src/network/descriptor/keyexchange.js", "../src/network/EnhancedSecureWebRTCManager.js", "../src/scripts/app-boot.js", "../package.json", "../src/components/ui/LanguageSwitcher.jsx", "../src/components/ui/ThemeSwitcher.jsx", "../src/components/ui/Header.jsx", "../src/components/ui/DownloadApps.jsx", "../src/components/ui/BecomePartner.jsx", "../src/components/ui/UniqueFeatureSlider.jsx", "../src/components/ui/Roadmap.jsx", "../src/components/ui/DonateCrypto.jsx", "../src/components/ui/CommunityCTA.jsx", "../src/components/ui/FileTransfer.jsx", "../src/network/iceServers.js", "../src/components/ui/IceServerSettings.jsx", "../src/components/ui/CallUI.jsx"],
|
||||||
"sourcesContent": ["/**\n * Secure and Reliable Notification Manager for P2P WebRTC Chat\n * Follows best practices: OWASP, MDN, Chrome DevRel\n * \n * @version 1.0.0\n * @author SecureBit Team\n * @license MIT\n */\n\nclass SecureChatNotificationManager {\n constructor(config = {}) {\n // Safely read Notification permission (iOS Safari may not define Notification)\n this.permission = (typeof Notification !== 'undefined' && Notification && typeof Notification.permission === 'string')\n ? Notification.permission\n : 'denied';\n this.isTabActive = this.checkTabActive(); // Initialize with proper check\n this.unreadCount = 0;\n this.originalTitle = document.title;\n this.notificationQueue = [];\n this.maxQueueSize = config.maxQueueSize || 5;\n this.rateLimitMs = config.rateLimitMs || 2000; // Spam protection\n this.lastNotificationTime = 0;\n this.trustedOrigins = config.trustedOrigins || [];\n \n // Secure context flag\n this.isSecureContext = window.isSecureContext;\n \n // Cross-browser compatibility for Page Visibility API\n this.hidden = this.getHiddenProperty();\n this.visibilityChange = this.getVisibilityChangeEvent();\n \n this.initVisibilityTracking();\n this.initSecurityChecks();\n }\n\n /**\n * Initialize security checks and validation\n * @private\n */\n initSecurityChecks() {\n // Security checks are performed silently\n }\n\n /**\n * Get hidden property name for cross-browser compatibility\n * @returns {string} Hidden property name\n * @private\n */\n getHiddenProperty() {\n if (typeof document.hidden !== \"undefined\") {\n return \"hidden\";\n } else if (typeof document.msHidden !== \"undefined\") {\n return \"msHidden\";\n } else if (typeof document.webkitHidden !== \"undefined\") {\n return \"webkitHidden\";\n }\n return \"hidden\"; // fallback\n }\n\n /**\n * Get visibility change event name for cross-browser compatibility\n * @returns {string} Visibility change event name\n * @private\n */\n getVisibilityChangeEvent() {\n if (typeof document.hidden !== \"undefined\") {\n return \"visibilitychange\";\n } else if (typeof document.msHidden !== \"undefined\") {\n return \"msvisibilitychange\";\n } else if (typeof document.webkitHidden !== \"undefined\") {\n return \"webkitvisibilitychange\";\n }\n return \"visibilitychange\"; // fallback\n }\n\n /**\n * Check if tab is currently active using multiple methods\n * @returns {boolean} True if tab is active\n * @private\n */\n checkTabActive() {\n // Primary method: Page Visibility API\n if (this.hidden && typeof document[this.hidden] !== \"undefined\") {\n return !document[this.hidden];\n }\n \n // Fallback method: document.hasFocus()\n if (typeof document.hasFocus === \"function\") {\n return document.hasFocus();\n }\n \n // Ultimate fallback: assume active\n return true;\n }\n\n /**\n * Initialize page visibility tracking (Page Visibility API)\n * @private\n */\n initVisibilityTracking() {\n // Primary method: Page Visibility API with cross-browser support\n if (typeof document.addEventListener !== \"undefined\" && typeof document[this.hidden] !== \"undefined\") {\n document.addEventListener(this.visibilityChange, () => {\n this.isTabActive = this.checkTabActive();\n \n if (this.isTabActive) {\n this.resetUnreadCount();\n this.clearNotificationQueue();\n }\n });\n }\n\n // Fallback method: Window focus/blur events\n window.addEventListener('focus', () => {\n this.isTabActive = this.checkTabActive();\n if (this.isTabActive) {\n this.resetUnreadCount();\n }\n });\n\n window.addEventListener('blur', () => {\n this.isTabActive = this.checkTabActive();\n });\n\n // Page unload cleanup\n window.addEventListener('beforeunload', () => {\n this.clearNotificationQueue();\n });\n }\n\n /**\n * Request notification permission (BEST PRACTICE: Only call in response to user action)\n * Never call on page load!\n * @returns {Promise<boolean>} Permission granted status\n */\n async requestPermission() {\n // Secure context check\n if (!this.isSecureContext || !('Notification' in window)) {\n return false;\n }\n\n if (this.permission === 'granted') {\n return true;\n }\n\n if (this.permission === 'denied') {\n return false;\n }\n\n try {\n this.permission = await Notification.requestPermission();\n return this.permission === 'granted';\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Update page title with unread count\n * @private\n */\n updateTitle() {\n if (this.unreadCount > 0) {\n document.title = `(${this.unreadCount}) ${this.originalTitle}`;\n } else {\n document.title = this.originalTitle;\n }\n }\n\n /**\n Line truncated
|
"sourcesContent": ["/**\n * Secure and Reliable Notification Manager for P2P WebRTC Chat\n * Follows best practices: OWASP, MDN, Chrome DevRel\n * \n * @version 1.0.0\n * @author SecureBit Team\n * @license MIT\n */\n\nclass SecureChatNotificationManager {\n constructor(config = {}) {\n // Safely read Notification permission (iOS Safari may not define Notification)\n this.permission = (typeof Notification !== 'undefined' && Notification && typeof Notification.permission === 'string')\n ? Notification.permission\n : 'denied';\n this.isTabActive = this.checkTabActive(); // Initialize with proper check\n this.unreadCount = 0;\n this.originalTitle = document.title;\n this.notificationQueue = [];\n this.maxQueueSize = config.maxQueueSize || 5;\n this.rateLimitMs = config.rateLimitMs || 2000; // Spam protection\n this.lastNotificationTime = 0;\n this.trustedOrigins = config.trustedOrigins || [];\n \n // Secure context flag\n this.isSecureContext = window.isSecureContext;\n \n // Cross-browser compatibility for Page Visibility API\n this.hidden = this.getHiddenProperty();\n this.visibilityChange = this.getVisibilityChangeEvent();\n \n this.initVisibilityTracking();\n this.initSecurityChecks();\n }\n\n /**\n * Initialize security checks and validation\n * @private\n */\n initSecurityChecks() {\n // Security checks are performed silently\n }\n\n /**\n * Get hidden property name for cross-browser compatibility\n * @returns {string} Hidden property name\n * @private\n */\n getHiddenProperty() {\n if (typeof document.hidden !== \"undefined\") {\n return \"hidden\";\n } else if (typeof document.msHidden !== \"undefined\") {\n return \"msHidden\";\n } else if (typeof document.webkitHidden !== \"undefined\") {\n return \"webkitHidden\";\n }\n return \"hidden\"; // fallback\n }\n\n /**\n * Get visibility change event name for cross-browser compatibility\n * @returns {string} Visibility change event name\n * @private\n */\n getVisibilityChangeEvent() {\n if (typeof document.hidden !== \"undefined\") {\n return \"visibilitychange\";\n } else if (typeof document.msHidden !== \"undefined\") {\n return \"msvisibilitychange\";\n } else if (typeof document.webkitHidden !== \"undefined\") {\n return \"webkitvisibilitychange\";\n }\n return \"visibilitychange\"; // fallback\n }\n\n /**\n * Check if tab is currently active using multiple methods\n * @returns {boolean} True if tab is active\n * @private\n */\n checkTabActive() {\n // Primary method: Page Visibility API\n if (this.hidden && typeof document[this.hidden] !== \"undefined\") {\n return !document[this.hidden];\n }\n \n // Fallback method: document.hasFocus()\n if (typeof document.hasFocus === \"function\") {\n return document.hasFocus();\n }\n \n // Ultimate fallback: assume active\n return true;\n }\n\n /**\n * Initialize page visibility tracking (Page Visibility API)\n * @private\n */\n initVisibilityTracking() {\n // Primary method: Page Visibility API with cross-browser support\n if (typeof document.addEventListener !== \"undefined\" && typeof document[this.hidden] !== \"undefined\") {\n document.addEventListener(this.visibilityChange, () => {\n this.isTabActive = this.checkTabActive();\n \n if (this.isTabActive) {\n this.resetUnreadCount();\n this.clearNotificationQueue();\n }\n });\n }\n\n // Fallback method: Window focus/blur events\n window.addEventListener('focus', () => {\n this.isTabActive = this.checkTabActive();\n if (this.isTabActive) {\n this.resetUnreadCount();\n }\n });\n\n window.addEventListener('blur', () => {\n this.isTabActive = this.checkTabActive();\n });\n\n // Page unload cleanup\n window.addEventListener('beforeunload', () => {\n this.clearNotificationQueue();\n });\n }\n\n /**\n * Request notification permission (BEST PRACTICE: Only call in response to user action)\n * Never call on page load!\n * @returns {Promise<boolean>} Permission granted status\n */\n async requestPermission() {\n // Secure context check\n if (!this.isSecureContext || !('Notification' in window)) {\n return false;\n }\n\n if (this.permission === 'granted') {\n return true;\n }\n\n if (this.permission === 'denied') {\n return false;\n }\n\n try {\n this.permission = await Notification.requestPermission();\n return this.permission === 'granted';\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Update page title with unread count\n * @private\n */\n updateTitle() {\n if (this.unreadCount > 0) {\n document.title = `(${this.unreadCount}) ${this.originalTitle}`;\n } else {\n document.title = this.originalTitle;\n }\n }\n\n /**\n Line truncated
|
||||||
"mappings": "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 truncated
|
"mappings": "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 truncated
|
||||||
"names": ["require_SecureNotificationManager", "__commonJSMin", "exports", "module", "SecureChatNotificationManager", "config", "text", "div", "url", "parsedUrl", "origin", "now", "senderName", "message", "options", "safeSenderName", "safeMessage", "safeIcon", "notification", "event", "error", "autoCloseTimeout", "index", "SecureP2PChat", "dataChannel", "data", "senderId", "container", "messageEl", "nameEl", "textEl", "timeEl", "audio", "require_NotificationIntegration", "__commonJSMin", "exports", "module", "import_SecureNotificationManager", "NotificationIntegration", "webrtcManager", "message", "type", "rest", "status", "meta", "messageKey", "messagesArray", "msg", "messageInfo", "notificationText", "notificationResult", "senderId", "messageData", "messagesContainer", "entries", "Object", "setPrototypeOf", "isFrozen", "getPrototypeOf", "getOwnPropertyDescriptor", "freeze", "seal", "create", "_ref", "Reflect", "apply", "construct", "x", "func", "thisArg", "_len", "arguments", "length", "args", "Array", "_key", "Func", "_len2", "_key2", "arrayForEach", "unapply", "prototype", "forEach", "arrayLastIndexOf", "lastIndexOf", "arrayPop", "pop", "arrayPush", "push", "arraySplice", "splice", "arrayIsArray", "isArray", "stringToLowerCase", "String", "toLowerCase", "stringToString", "toString", "stringMatch", "match", "stringReplace", "replace", "stringIndexOf", "indexOf", "stringTrim", "trim", "numberToString", "Number", "booleanToString", "Boolean", "bigintToString", "BigInt", "symbolToString", "Symbol", "objectHasOwnProperty", "hasOwnProperty", "objectToString", "regExpTest", "RegExp", "test", "typeErrorCreate", "unconstruct", "TypeError", "lastIndex", "_len3", "_key3", "_len4", "_key4", "addToSet", "set", "array", "transformCaseFunc", "l", "element", "lcElement", "cleanArray", "index", "clone", "object", "newObject", "_ref2", "_ref3", "_slicedToArray", "property", "value", "constructor", "stringifyValue", "valueAsRecord", "valueToString", "lookupGetter", "stringified", "prop", "desc", "get", "fallbackValue", "isRegex", "html", "svg", "svgFilters", "svgDisallowed", "mathMl", "mathMlDisallowed", "text", "xml", "MUSTACHE_EXPR", "ERB_EXPR", "TMPLIT_EXPR", "DATA_ATTR", "ARIA_ATTR", "IS_ALLOWED_URI", "IS_SCRIPT_OR_DATA", "ATTR_WHITESPACE", "DOCTYPE_NAME", "CUSTOM_ELEMENT", "ELEMENT_MARKUP_PROBE", "COMMENT_MARKUP_PROBE", "FALLBACK_TAG_CLOSE", "SELF_CLOSING_TAG", "NODE_TYPE", "attribute", "cdataSection", "entityReference", "entityNode", "processingInstruction", "comment", "document", "documentType", "documentFragment", "notation", "getGlobal", "window", "_createTrustedTypesPolicy", "trustedTypes", "purifyHostElement", "createPolicy", "suffix", "ATTR_NAME", "hasAttribute", "getAttribute", "policyName", "createHTML", "createScriptURL", "scriptUrl", "console", "warn", "_createHooksMap", "afterSanitizeAttributes", "afterSanitizeElements", "afterSanitizeShadowDOM", "beforeSanitizeAttributes", "beforeSanitizeElements", "beforeSanitizeShadowDOM", "uponSanitizeAttribute", "uponSanitizeElement", "uponSanitizeShadowNode", "_resolveSetOption", "cfg", "key", "fallback", "options", "base", "transform", "createDOMPurify", "undefined", "DOMPurify", "root", "version", "VERSION", "removed", "nodeType", "Element", "isSupported", "originalDocument", "currentScript", "DocumentFragment", "HTMLTemplateElement", "Node", "NodeFilter", "_window$NamedNodeMap", "NamedNodeMap", "MozNamedAttrMap", "HTMLFormElement", "DOMParser", "ElementPrototype", "cloneNode", "remove", "getNextSibling", "getChildNodes", "getParentNode", "getShadowRoot", "getAttributes", "getNodeType", "getNodeName", "template", "createElement", "content", "ownerDocument", "trustedTypesPolicy", "emptyHTML", "defaultTrustedTypesPolicy", "defaultTrustedTypesPolicyResolved", "IN_TRUSTED_TYPES_POLICY", "_assertNotInTrustedTypesPolicy", "_createTrustedHTML", "_createTrustedScriptURL", "_getDefaultTrustedTypesPolicy", "_document", "implementation", "createNodeIterator", "createDocumentFragment", "getElementsByTagName", "importNode", "hooks", "createHTMLDocument", "EXPRESSIONS", "ALLOWED_TAGS", "DEFAULT_ALLOWED_TAGS", "TAGS", "ALLOWED_ATTR", "DEFAULT_ALLOWED_ATTR", "ATTRS", "CUSTOM_ELEMENT_HANDLING", "tagNameCheck", "writable", "configurable", "enumerable", "attributeNameCheck", "allowCustomizedBuiltInElements", "FORBID_TAGS", "FORBID_ATTR", "EXTRA_ELEMENT_HANDLING", "tagCheck", "attributeCheck", "ALLOW_ARIA_ATTR", "ALLOW_DATA_ATTR", "ALLOW_UNKNOWN_PROTOCOLS", "ALLOW_SELF_CLOSE_IN_ATTR", "SAFE_FOR_TEMPLATES", "SAFE_FOR_XML", "WHOLE_DOCUMENT", "SET_CONFIG", "FORCE_BODY", "RETURN_DOM", "RETURN_DOM_FRAGMENT", "RETURN_TRUSTED_TYPE", "SANITIZE_DOM", "SANITIZE_NAMED_PROPS", "SANITIZE_NAMED_PROPS_PREFIX", "KEEP_CONTENT", "IN_PLACE", "USE_PROFILES", "FORBID_CONTENTS", "DEFAULT_FORBID_CONTENTS", "DATA_URI_TAGS", "DEFAULT_DATA_URI_TAGS", "URI_SAFE_ATTRIBUTES", "DEFAULT_URI_SAFE_ATTRIBUTES", "MATHML_NAMESPACE", "SVG_NAMESPACE", "HTML_NAMESPACE", "NAMESPACE", "IS_EMPTY_INPUT", "ALLOWED_NAMESPACELine truncated
|
"names": ["require_SecureNotificationManager", "__commonJSMin", "exports", "module", "SecureChatNotificationManager", "config", "text", "div", "url", "parsedUrl", "origin", "now", "senderName", "message", "options", "safeSenderName", "safeMessage", "safeIcon", "notification", "event", "error", "autoCloseTimeout", "index", "SecureP2PChat", "dataChannel", "data", "senderId", "container", "messageEl", "nameEl", "textEl", "timeEl", "audio", "require_NotificationIntegration", "__commonJSMin", "exports", "module", "import_SecureNotificationManager", "NotificationIntegration", "webrtcManager", "message", "type", "rest", "status", "meta", "messageKey", "messagesArray", "msg", "messageInfo", "notificationText", "notificationResult", "senderId", "messageData", "messagesContainer", "entries", "Object", "setPrototypeOf", "isFrozen", "getPrototypeOf", "getOwnPropertyDescriptor", "freeze", "seal", "create", "_ref", "Reflect", "apply", "construct", "x", "func", "thisArg", "_len", "arguments", "length", "args", "Array", "_key", "Func", "_len2", "_key2", "arrayForEach", "unapply", "prototype", "forEach", "arrayLastIndexOf", "lastIndexOf", "arrayPop", "pop", "arrayPush", "push", "arraySplice", "splice", "arrayIsArray", "isArray", "stringToLowerCase", "String", "toLowerCase", "stringToString", "toString", "stringMatch", "match", "stringReplace", "replace", "stringIndexOf", "indexOf", "stringTrim", "trim", "numberToString", "Number", "booleanToString", "Boolean", "bigintToString", "BigInt", "symbolToString", "Symbol", "objectHasOwnProperty", "hasOwnProperty", "objectToString", "regExpTest", "RegExp", "test", "typeErrorCreate", "unconstruct", "TypeError", "lastIndex", "_len3", "_key3", "_len4", "_key4", "addToSet", "set", "array", "transformCaseFunc", "l", "element", "lcElement", "cleanArray", "index", "clone", "object", "newObject", "_ref2", "_ref3", "_slicedToArray", "property", "value", "constructor", "stringifyValue", "valueAsRecord", "valueToString", "lookupGetter", "stringified", "prop", "desc", "get", "fallbackValue", "isRegex", "html", "svg", "svgFilters", "svgDisallowed", "mathMl", "mathMlDisallowed", "text", "xml", "MUSTACHE_EXPR", "ERB_EXPR", "TMPLIT_EXPR", "DATA_ATTR", "ARIA_ATTR", "IS_ALLOWED_URI", "IS_SCRIPT_OR_DATA", "ATTR_WHITESPACE", "DOCTYPE_NAME", "CUSTOM_ELEMENT", "ELEMENT_MARKUP_PROBE", "COMMENT_MARKUP_PROBE", "FALLBACK_TAG_CLOSE", "SELF_CLOSING_TAG", "NODE_TYPE", "attribute", "cdataSection", "entityReference", "entityNode", "processingInstruction", "comment", "document", "documentType", "documentFragment", "notation", "getGlobal", "window", "_createTrustedTypesPolicy", "trustedTypes", "purifyHostElement", "createPolicy", "suffix", "ATTR_NAME", "hasAttribute", "getAttribute", "policyName", "createHTML", "createScriptURL", "scriptUrl", "console", "warn", "_createHooksMap", "afterSanitizeAttributes", "afterSanitizeElements", "afterSanitizeShadowDOM", "beforeSanitizeAttributes", "beforeSanitizeElements", "beforeSanitizeShadowDOM", "uponSanitizeAttribute", "uponSanitizeElement", "uponSanitizeShadowNode", "_resolveSetOption", "cfg", "key", "fallback", "options", "base", "transform", "createDOMPurify", "undefined", "DOMPurify", "root", "version", "VERSION", "removed", "nodeType", "Element", "isSupported", "originalDocument", "currentScript", "DocumentFragment", "HTMLTemplateElement", "Node", "NodeFilter", "_window$NamedNodeMap", "NamedNodeMap", "MozNamedAttrMap", "HTMLFormElement", "DOMParser", "ElementPrototype", "cloneNode", "remove", "getNextSibling", "getChildNodes", "getParentNode", "getShadowRoot", "getAttributes", "getNodeType", "getNodeName", "template", "createElement", "content", "ownerDocument", "trustedTypesPolicy", "emptyHTML", "defaultTrustedTypesPolicy", "defaultTrustedTypesPolicyResolved", "IN_TRUSTED_TYPES_POLICY", "_assertNotInTrustedTypesPolicy", "_createTrustedHTML", "_createTrustedScriptURL", "_getDefaultTrustedTypesPolicy", "_document", "implementation", "createNodeIterator", "createDocumentFragment", "getElementsByTagName", "importNode", "hooks", "createHTMLDocument", "EXPRESSIONS", "ALLOWED_TAGS", "DEFAULT_ALLOWED_TAGS", "TAGS", "ALLOWED_ATTR", "DEFAULT_ALLOWED_ATTR", "ATTRS", "CUSTOM_ELEMENT_HANDLING", "tagNameCheck", "writable", "configurable", "enumerable", "attributeNameCheck", "allowCustomizedBuiltInElements", "FORBID_TAGS", "FORBID_ATTR", "EXTRA_ELEMENT_HANDLING", "tagCheck", "attributeCheck", "ALLOW_ARIA_ATTR", "ALLOW_DATA_ATTR", "ALLOW_UNKNOWN_PROTOCOLS", "ALLOW_SELF_CLOSE_IN_ATTR", "SAFE_FOR_TEMPLATES", "SAFE_FOR_XML", "WHOLE_DOCUMENT", "SET_CONFIG", "FORCE_BODY", "RETURN_DOM", "RETURN_DOM_FRAGMENT", "RETURN_TRUSTED_TYPE", "SANITIZE_DOM", "SANITIZE_NAMED_PROPS", "SANITIZE_NAMED_PROPS_PREFIX", "KEEP_CONTENT", "IN_PLACE", "USE_PROFILES", "FORBID_CONTENTS", "DEFAULT_FORBID_CONTENTS", "DATA_URI_TAGS", "DEFAULT_DATA_URI_TAGS", "URI_SAFE_ATTRIBUTES", "DEFAULT_URI_SAFE_ATTRIBUTES", "MATHML_NAMESPACE", "SVG_NAMESPACE", "HTML_NAMESPACE", "NAMESPACE", "IS_EMPTY_INPUT", "ALLOWED_NAMESPACELine truncated
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
function Wa(t=globalThis.window){return t?.SECUREBIT_DEBUG===!0}function Bs({targetWindow:t=globalThis.window,webrtcManagerRef:e,onClearData:n,clearConsole:r=()=>{typeof console.clear=="function"&&console.clear()}}){return Wa(t)?(t.forceCleanup=()=>{n(),e.current&&e.current.disconnect()},t.clearLogs=r,t.webrtcManagerRef=e,()=>{delete t.forceCleanup,delete t.clearLogs,delete t.webrtcManagerRef}):()=>{}}var Va="securebit-net";var Hr="ice-device-key",Vr="ice-settings";function jr(){return typeof indexedDB<"u"&&typeof crypto<"u"&&!!crypto.subtle}function $r(){return new Promise((t,e)=>{let n;try{n=indexedDB.open(Va,1)}catch(r){e(r);return}n.onupgradeneeded=()=>{let r=n.result;r.objectStoreNames.contains("kv")||r.createObjectStore("kv")},n.onsuccess=()=>t(n.result),n.onerror=()=>e(n.error)})}function Wr(t,e){return new Promise((n,r)=>{let c=t.transaction("kv","readonly").objectStore("kv").get(e);c.onsuccess=()=>n(c.result),c.onerror=()=>r(c.error)})}function zs(t,e,n){return new Promise((r,i)=>{let c=t.transaction("kv","readwrite");c.objectStore("kv").put(n,e),c.oncomplete=()=>r(),c.onerror=()=>i(c.error)})}function ja(t,e){return new Promise((n,r)=>{let i=t.transaction("kv","readwrite");i.objectStore("kv").delete(e),i.oncomplete=()=>n(),i.onerror=()=>r(i.error)})}async function $a(t){let e=await Wr(t,Hr);if(e instanceof CryptoKey)return e;let n=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return await zs(t,Hr,n),n}async function Us(t){if(!jr())throw new Error("Persistent storage is not available in this browser");let e=await $r(),n=await $a(e),r=JSON.stringify({version:1,servers:Array.isArray(t?.servers)?t.servers:[],privacyMode:t?.privacyMode==="relay-only"?"relay-only":"standard"}),i=crypto.getRandomValues(new Uint8Array(12)),c=await crypto.subtle.encrypt({name:"AES-GCM",iv:i},n,new TextEncoder().encode(r));await zs(e,Vr,{iv:Array.from(i),data:Array.from(new Uint8Array(c))})}async function Gs(){if(!jr())return null;try{let t=await $r(),e=await Wr(t,Vr);if(!e||!Array.isArray(e.iv)||!Array.isArray(e.data))return null;let n=await Wr(t,Hr);if(!(n instanceof CryptoKey))return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(e.iv)},n,new Uint8Array(e.data)),i=JSON.parse(new TextDecoder().decode(r));return{servers:Array.isArray(i.servers)?i.servers:[],privacyMode:i.privacyMode==="relay-only"?"relay-only":"standard"}}catch{return null}}async function qr(){if(jr())try{let t=await $r();await ja(t,Vr)}catch{}}var qa="/api/turn-credentials",Vs=["turn.securebit.chat","144.172.96.126"];var Hs=[6e4,3e5,9e5];function js(t){let e=/^turns?:([^:?\s]+)/i.exec(String(t||"").trim());return e?e[1].toLowerCase():null}function Ya(t){return!t||typeof t!="object"?!1:(Array.isArray(t.urls)?t.urls:[t.urls]).some(n=>Vs.includes(js(n)))}function Gn(t){if(typeof t!="string"||t.length===0||t.length>512)return!1;for(let e=0;e<t.length;e++){let n=t.charCodeAt(e);if(n<32||n===127)return!1}return!0}function Ka(t,e){if(!Array.isArray(t)||!e||!Gn(e.username)||!Gn(e.credential))return!1;let n=!1;for(let r of t)Ya(r)&&(r.username=e.username,r.credential=e.credential,n=!0);if(!n&&Array.isArray(e.urls)){let r=e.urls.filter(i=>Gn(i)&&Vs.includes(js(i)));r.length>0&&(t.push({urls:r,username:e.username,credential:e.credential}),n=!0)}return n}function Qa(t,e){let n=t&&Array.isArray(t.iceServers)?t.iceServers[0]:null;if(!n||!Gn(n.username)||!Gn(n.credential))return null;let r=Number(String(n.username).split(":")[0]);if(!Number.isFinite(r)||r<=e)return null;let i=Number(t.ttl);return{urls:Array.isArray(n.urls)?n.urls.slice(0,8):[],username:n.username,credential:n.credential,expiry:r,ttl:Number.isFinite(i)&&i>0?i:r-e}}var Ws=!1;function $s(t={}){if(Ws||typeof window>"u"||typeof fetch!="function")return;Ws=!0;let e=t.getList||(()=>window.SECUREBIT_ICE_SERVERS),n=0,r=0,i=null,c=0,d=m=>{i&&clearTimeout(i),i=setTimeout(h,m)};async function h(){i=null;try{let m=await fetch(qa,{method:"POST",cache:"no-store",credentials:"omit"});if(!m.ok)throw new Error(`HTTP ${m.status}`);let v=Qa(await m.json(),Date.now()/1e3);if(!v)throw new Error("unusable response");Ka(e(),v),n=v.expiry,r=v.ttl,c=0,d(Math.max(60,r/2)*1e3)}catch{let v=Hs[Math.min(c,Hs.length-1)];c++,d(v)}}document.addEventListener("visibilitychange",()=>{if(document.visibilityState!=="visible")return;let m=Date.now()/1e3;(!n||n-m<r/2)&&h()}),h()}var Tn=["en","de","fr","es","uk","ru","zh","ko","hi","ar","he","fa","ur"],or={en:{htmlLang:"en",nativeName:"English",abbr:"EN",dir:"ltr",path:"/"},de:{htmlLang:"de",nativeName:"Deutsch",abbr:"DE",dir:"ltr",path:"/de/"},fr:{htmlLang:"fr",nativeName:"Fran\xE7ais",abbr:"FR",dir:"ltr",path:"/fr/"},es:{htmlLang:"es",nativeName:"Espa\xF1ol",abbr:"ES",dir:"ltr",path:"/es/"},uk:{htmlLang:"uk",nativeName:"\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430",abbr:"UK",dir:"ltr",path:"/uk/"},ru:{htmlLang:"ru",nativeName:"\u0420\u0443\u0441\u0441\u043A\u0438\u0439",abbr:"RU",dir:"ltr",path:"/ru/"},zh:{htmlLine truncated
|
function ja(t=globalThis.window){return t?.SECUREBIT_DEBUG===!0}function Us({targetWindow:t=globalThis.window,webrtcManagerRef:e,onClearData:n,clearConsole:r=()=>{typeof console.clear=="function"&&console.clear()}}){return ja(t)?(t.forceCleanup=()=>{n(),e.current&&e.current.disconnect()},t.clearLogs=r,t.webrtcManagerRef=e,()=>{delete t.forceCleanup,delete t.clearLogs,delete t.webrtcManagerRef}):()=>{}}var $a="securebit-net";var Wr="ice-device-key",jr="ice-settings";function $r(){return typeof indexedDB<"u"&&typeof crypto<"u"&&!!crypto.subtle}function qr(){return new Promise((t,e)=>{let n;try{n=indexedDB.open($a,1)}catch(r){e(r);return}n.onupgradeneeded=()=>{let r=n.result;r.objectStoreNames.contains("kv")||r.createObjectStore("kv")},n.onsuccess=()=>t(n.result),n.onerror=()=>e(n.error)})}function Vr(t,e){return new Promise((n,r)=>{let c=t.transaction("kv","readonly").objectStore("kv").get(e);c.onsuccess=()=>n(c.result),c.onerror=()=>r(c.error)})}function Gs(t,e,n){return new Promise((r,i)=>{let c=t.transaction("kv","readwrite");c.objectStore("kv").put(n,e),c.oncomplete=()=>r(),c.onerror=()=>i(c.error)})}function qa(t,e){return new Promise((n,r)=>{let i=t.transaction("kv","readwrite");i.objectStore("kv").delete(e),i.oncomplete=()=>n(),i.onerror=()=>r(i.error)})}async function Ya(t){let e=await Vr(t,Wr);if(e instanceof CryptoKey)return e;let n=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return await Gs(t,Wr,n),n}async function Hs(t){if(!$r())throw new Error("Persistent storage is not available in this browser");let e=await qr(),n=await Ya(e),r=JSON.stringify({version:1,servers:Array.isArray(t?.servers)?t.servers:[],privacyMode:t?.privacyMode==="relay-only"?"relay-only":"standard"}),i=crypto.getRandomValues(new Uint8Array(12)),c=await crypto.subtle.encrypt({name:"AES-GCM",iv:i},n,new TextEncoder().encode(r));await Gs(e,jr,{iv:Array.from(i),data:Array.from(new Uint8Array(c))})}async function Ws(){if(!$r())return null;try{let t=await qr(),e=await Vr(t,jr);if(!e||!Array.isArray(e.iv)||!Array.isArray(e.data))return null;let n=await Vr(t,Wr);if(!(n instanceof CryptoKey))return null;let r=await crypto.subtle.decrypt({name:"AES-GCM",iv:new Uint8Array(e.iv)},n,new Uint8Array(e.data)),i=JSON.parse(new TextDecoder().decode(r));return{servers:Array.isArray(i.servers)?i.servers:[],privacyMode:i.privacyMode==="relay-only"?"relay-only":"standard"}}catch{return null}}async function Yr(){if($r())try{let t=await qr();await qa(t,jr)}catch{}}var Ka="/api/turn-credentials",$s=["turn.securebit.chat","144.172.96.126"];var Vs=[6e4,3e5,9e5];function qs(t){let e=/^turns?:([^:?\s]+)/i.exec(String(t||"").trim());return e?e[1].toLowerCase():null}function Qa(t){return!t||typeof t!="object"?!1:(Array.isArray(t.urls)?t.urls:[t.urls]).some(n=>$s.includes(qs(n)))}function Hn(t){if(typeof t!="string"||t.length===0||t.length>512)return!1;for(let e=0;e<t.length;e++){let n=t.charCodeAt(e);if(n<32||n===127)return!1}return!0}function Xa(t,e){if(!Array.isArray(t)||!e||!Hn(e.username)||!Hn(e.credential))return!1;let n=!1;for(let r of t)Qa(r)&&(r.username=e.username,r.credential=e.credential,n=!0);if(!n&&Array.isArray(e.urls)){let r=e.urls.filter(i=>Hn(i)&&$s.includes(qs(i)));r.length>0&&(t.push({urls:r,username:e.username,credential:e.credential}),n=!0)}return n}function Ja(t,e){let n=t&&Array.isArray(t.iceServers)?t.iceServers[0]:null;if(!n||!Hn(n.username)||!Hn(n.credential))return null;let r=Number(String(n.username).split(":")[0]);if(!Number.isFinite(r)||r<=e)return null;let i=Number(t.ttl);return{urls:Array.isArray(n.urls)?n.urls.slice(0,8):[],username:n.username,credential:n.credential,expiry:r,ttl:Number.isFinite(i)&&i>0?i:r-e}}var js=!1;function Ys(t={}){if(js||typeof window>"u"||typeof fetch!="function")return;js=!0;let e=t.getList||(()=>window.SECUREBIT_ICE_SERVERS),n=0,r=0,i=null,c=0,d=m=>{i&&clearTimeout(i),i=setTimeout(h,m)};async function h(){i=null;try{let m=await fetch(Ka,{method:"POST",cache:"no-store",credentials:"omit"});if(!m.ok)throw new Error(`HTTP ${m.status}`);let v=Ja(await m.json(),Date.now()/1e3);if(!v)throw new Error("unusable response");Xa(e(),v),n=v.expiry,r=v.ttl,c=0,d(Math.max(60,r/2)*1e3)}catch{let v=Vs[Math.min(c,Vs.length-1)];c++,d(v)}}document.addEventListener("visibilitychange",()=>{if(document.visibilityState!=="visible")return;let m=Date.now()/1e3;(!n||n-m<r/2)&&h()}),h()}var Nn=["en","de","fr","es","uk","ru","zh","ko","hi","ar","he","fa","ur"],or={en:{htmlLang:"en",nativeName:"English",abbr:"EN",dir:"ltr",path:"/"},de:{htmlLang:"de",nativeName:"Deutsch",abbr:"DE",dir:"ltr",path:"/de/"},fr:{htmlLang:"fr",nativeName:"Fran\xE7ais",abbr:"FR",dir:"ltr",path:"/fr/"},es:{htmlLang:"es",nativeName:"Espa\xF1ol",abbr:"ES",dir:"ltr",path:"/es/"},uk:{htmlLang:"uk",nativeName:"\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430",abbr:"UK",dir:"ltr",path:"/uk/"},ru:{htmlLang:"ru",nativeName:"\u0420\u0443\u0441\u0441\u043A\u0438\u0439",abbr:"RU",dir:"ltr",path:"/ru/"},zh:{htmlLine truncated
|
||||||
Please wait for real-time cryptographic verification to complete.`,"sec.verificationUnavailable":"Security verification not available","chat.placeholder":"Type an encrypted message\u2026","chat.send":"Send message","chat.sendFiles":"Send files","chat.hideFiles":"Hide files","chat.recordVoice":"Record voice message","chat.sendVoice":"Send voice message","chat.discard":"Discard","chat.codeBlock":"Send as a code block (expands the input)","chat.codeHint":"Code snippet \xB7 formatting preserved \xB7 \u2318\u21B5 to send","chat.viewOnce":"View once","chat.viewOnceTitle":"View once - vanishes after the peer reads it","chat.viewOncePrefix":"View once \xB7 ","chat.viewOnceTap":"View once \xB7 tap to reveal","chat.viewedOnce":"Viewed once","chat.disappearing":"Disappearing message - deletes on both sides","chat.disappearAfter":"Disappear after","chat.visibleFor":"Visible for","chat.timerPrefix":"Timer \xB7 ","chat.expired":"This message has expired","chat.deleteForEveryone":"Delete for everyone","chat.sending":"Sending","chat.delivered":"Delivered","chat.notSent":"Not sent","chat.notSentReason":"Not sent - the secure channel is not ready. Reconnect to continue.","chat.encrypted":"Encrypted","chat.decrypted":"Decrypted","chat.encryptedOnDevice":"Encrypted on your device","chat.uploading":"Uploading","chat.downloading":"Downloading","chat.transferring":"Transferring\u2026","chat.copied":"Copied!","chat.collapse":"Collapse","chat.closeMenu":"Close menu","chat.newChat":"New chat","chat.newGroup":"New group","chat.createGroup":"Create a group","chat.groupChats":"Group chats","chat.secureChat":"Secure chat","chat.nameThis":"Name this chat","chat.rename":"Rename chat (local only)","chat.renameHint":"Double-click to rename","chat.localLabel":"Local label \xB7 stored only on this device","chat.setStatus":"Set your status","chat.yourStatusPrefix":"Your status - ","chat.meshHint":"Up to 8 peers \xB7 P2P mesh","chat.offline":"Offline","chat.noNetwork":"No network \xB7 reconnecting","chat.startVoiceCall":"Start encrypted voice call","chat.startVideoCall":"Start encrypted video call","chat.verifyForCalls":"Verify the session to enable calls","chat.e2eeNote":"Every message is end-to-end encrypted on your device before it leaves.","chat.peersOnly":"Sent end-to-end to connected peers only - never stored on a server.","flow.invitationCreated":"Secure invitation created","flow.invitationCreatedBang":"Secure invitation created and encrypted!","flow.responseCreated":"Secure response created","flow.responseCreatedBang":"Secure response created!","flow.sendInvitation":"Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.).","flow.sendResponse":"Send the response code to the initiator via a secure channel or let them scan the QR code below.","flow.sendEncryptedCode":"Send the encrypted code","flow.sendTheResponse":"Send the response","flow.pasteOrWrite":"Paste or write code\u2026","flow.processingInvitation":"Processing the secure invitation...","flow.processingResponse":"Processing the secure response...","flow.finalizing":"Finalizing the secure connection...","flow.channelEstablished":"Secure channel established","flow.channelReady":"Secure channel is ready","flow.restoring":"Restoring connection\u2026","flow.invitationCaptured":"Invitation captured.","err.needInvitation":"You need to insert the invitation code from your interlocutor.","err.needResponse":"You need to insert the response code from your interlocutor.","err.responseFormat":"Invalid response format - please check the code","err.responseNoKey":"Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.","err.responseNoSignKey":"Invalid response code - missing signature verification key. Please check the code and try again.","err.responseOutdated":"Response data is outdated - please use a fresh invitation","err.setupError":"Connection setup error","err.notReady":"Connection not ready","err.securityBreach":"Security breach detected - connection rejected","err.securityValidation":"Security validation failed - possible attack detected","err.retiredQr":"This QR code uses a retired format that could not transfer the invitation. Ask your peer to generate a new one, or use copy/paste.","err.compressedQrNote":"Compressed QR may omit SDP for brevity. Use copy/paste if connection fails.","err.inviteNotConnected":"The invitation could not be sent. That chat is not connected right now.","err.inviteNotConnectedRetry":"The invitation could not be sent. That chat is not connected right now - reopen it and try again.","err.nobodyAccepted":"Nobody accepted the invitation. The group is unchanged.","err.noAudio":"No audio captured - check microphone permission and try again.","err.voiceNeedsConnection":"Voice message needs an active secure connection. Reconnect and try again.","err.voiceRestoring":"Restoring the connection - try sending the voice message again in a moment.","err.fileTooLarge":"File too large","sas.sa
Line truncated
|
Please wait for real-time cryptographic verification to complete.`,"sec.verificationUnavailable":"Security verification not available","chat.placeholder":"Type an encrypted message\u2026","chat.send":"Send message","chat.sendFiles":"Send files","chat.hideFiles":"Hide files","chat.recordVoice":"Record voice message","chat.sendVoice":"Send voice message","chat.discard":"Discard","chat.codeBlock":"Send as a code block (expands the input)","chat.codeHint":"Code snippet \xB7 formatting preserved \xB7 \u2318\u21B5 to send","chat.viewOnce":"View once","chat.viewOnceTitle":"View once - vanishes after the peer reads it","chat.viewOncePrefix":"View once \xB7 ","chat.viewOnceTap":"View once \xB7 tap to reveal","chat.viewedOnce":"Viewed once","chat.disappearing":"Disappearing message - deletes on both sides","chat.disappearAfter":"Disappear after","chat.visibleFor":"Visible for","chat.timerPrefix":"Timer \xB7 ","chat.expired":"This message has expired","chat.deleteForEveryone":"Delete for everyone","chat.sending":"Sending","chat.delivered":"Delivered","chat.notSent":"Not sent","chat.notSentReason":"Not sent - the secure channel is not ready. Reconnect to continue.","chat.encrypted":"Encrypted","chat.decrypted":"Decrypted","chat.encryptedOnDevice":"Encrypted on your device","chat.uploading":"Uploading","chat.downloading":"Downloading","chat.transferring":"Transferring\u2026","chat.copied":"Copied!","chat.collapse":"Collapse","chat.closeMenu":"Close menu","chat.newChat":"New chat","chat.newGroup":"New group","chat.createGroup":"Create a group","chat.groupChats":"Group chats","chat.secureChat":"Secure chat","chat.nameThis":"Name this chat","chat.rename":"Rename chat (local only)","chat.renameHint":"Double-click to rename","chat.localLabel":"Local label \xB7 stored only on this device","chat.setStatus":"Set your status","chat.yourStatusPrefix":"Your status - ","chat.meshHint":"Up to 8 peers \xB7 P2P mesh","chat.offline":"Offline","chat.noNetwork":"No network \xB7 reconnecting","chat.startVoiceCall":"Start encrypted voice call","chat.startVideoCall":"Start encrypted video call","chat.verifyForCalls":"Verify the session to enable calls","chat.e2eeNote":"Every message is end-to-end encrypted on your device before it leaves.","chat.peersOnly":"Sent end-to-end to connected peers only - never stored on a server.","flow.invitationCreated":"Secure invitation created","flow.invitationCreatedBang":"Secure invitation created and encrypted!","flow.responseCreated":"Secure response created","flow.responseCreatedBang":"Secure response created!","flow.sendInvitation":"Send the invitation code to your interlocutor via a secure channel (voice call, SMS, etc.).","flow.sendResponse":"Send the response code to the initiator via a secure channel or let them scan the QR code below.","flow.sendEncryptedCode":"Send the encrypted code","flow.sendTheResponse":"Send the response","flow.pasteOrWrite":"Paste or write code\u2026","flow.processingInvitation":"Processing the secure invitation...","flow.processingResponse":"Processing the secure response...","flow.finalizing":"Finalizing the secure connection...","flow.channelEstablished":"Secure channel established","flow.channelReady":"Secure channel is ready","flow.restoring":"Restoring connection\u2026","flow.invitationCaptured":"Invitation captured.","err.needInvitation":"You need to insert the invitation code from your interlocutor.","err.needResponse":"You need to insert the response code from your interlocutor.","err.responseFormat":"Invalid response format - please check the code","err.responseNoKey":"Invalid response code - missing or corrupted cryptographic key. Please check the code and try again.","err.responseNoSignKey":"Invalid response code - missing signature verification key. Please check the code and try again.","err.responseOutdated":"Response data is outdated - please use a fresh invitation","err.setupError":"Connection setup error","err.notReady":"Connection not ready","err.securityBreach":"Security breach detected - connection rejected","err.securityValidation":"Security validation failed - possible attack detected","err.retiredQr":"This QR code uses a retired format that could not transfer the invitation. Ask your peer to generate a new one, or use copy/paste.","err.compressedQrNote":"Compressed QR may omit SDP for brevity. Use copy/paste if connection fails.","err.inviteNotConnected":"The invitation could not be sent. That chat is not connected right now.","err.inviteNotConnectedRetry":"The invitation could not be sent. That chat is not connected right now - reopen it and try again.","err.nobodyAccepted":"Nobody accepted the invitation. The group is unchanged.","err.noAudio":"No audio captured - check microphone permission and try again.","err.voiceNeedsConnection":"Voice message needs an active secure connection. Reconnect and try again.","err.voiceRestoring":"Restoring the connection - try sending the voice message again in a moment.","err.fileTooLarge":"File too large","sas.sa
Line truncated
|
||||||
`),n.style.transform="translate3d(0,"+(t?Math.round(t.offsetTop):0)+"px,0)"};i();let c=setInterval(i,200);return()=>{clearInterval(c),n.remove()}}var Xo=t=>()=>{if(t&&t.current){let e=()=>{t.current&&t.current.scrollTo({top:t.current.scrollHeight,behavior:wr()})};e(),setTimeout(e,50),setTimeout(e,150),setTimeout(e,300),requestAnimationFrame(()=>{setTimeout(e,100)})}},Ji=async t=>{let e=null;try{t&&window.EnhancedSecureCryptoUtils&&(e=await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(t))}catch{}if(!e){alert(`Security verification in progress\u2026
|
`),n.style.transform="translate3d(0,"+(t?Math.round(t.offsetTop):0)+"px,0)"};i();let c=setInterval(i,200);return()=>{clearInterval(c),n.remove()}}var Zo=t=>()=>{if(t&&t.current){let e=()=>{t.current&&t.current.scrollTo({top:t.current.scrollHeight,behavior:wr()})};e(),setTimeout(e,50),setTimeout(e,150),setTimeout(e,300),requestAnimationFrame(()=>{setTimeout(e,100)})}},ta=async t=>{let e=null;try{t&&window.EnhancedSecureCryptoUtils&&(e=await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(t))}catch{}if(!e){alert(`Security verification in progress\u2026
|
||||||
Please wait for real-time cryptographic verification to complete.`);return}let n="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",r=O=>String(O).replace(/[&<>"]/g,$=>({"&":"&","<":"<",">":">",'"':"""})[$]),i=e.color==="orange"?"var(--sb-orange-solid)":e.color==="yellow"?"var(--sb-yellow-solid)":e.color==="red"?"var(--sb-red-solid)":"var(--sb-green-solid)",c=e.color==="orange"?"var(--sb-orange)":e.color==="yellow"?"var(--sb-yellow)":e.color==="red"?"var(--sb-red)":"var(--sb-green)",d=e.color==="orange"?"var(--sb-orange-rgb)":e.color==="yellow"?"var(--sb-yellow-rgb)":e.color==="red"?"var(--sb-red-rgb)":"var(--sb-green-rgb)",h=Math.max(0,Math.min(100,Math.round(e.score||0))),m=2*Math.PI*56,v=`${(m*Math.min(1,h/100)).toFixed(1)} ${m.toFixed(1)}`,S=String(e.level||"SECURE").toUpperCase(),p=e.isRealData!==!1,k=e.verificationResults?Object.entries(e.verificationResults):[],y=Number.isFinite(e.passedChecks)?e.passedChecks:k.filter(([,O])=>O&&O.passed).length,I=Number.isFinite(e.totalChecks)?e.totalChecks:k.length,B=new Date(e.timestamp||Date.now()).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}),N=O=>{let $=a(`secTest.${O}`);return $===`secTest.${O}`?ae(O):$},ae=O=>{let $=String(O).replace(/^verify/i,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").trim();return $=$.replace(/\b(ecdh|ecdsa|aes|gcm|hmac|pfs|sas|mitm|asn|dtls|hkdf|spki|oid|p384)\b/gi,Q=>Q.toUpperCase()),$.charAt(0).toUpperCase()+$.slice(1)},te='<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--sb-green-solid)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4 10-11"/></svg>',pe='<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--sb-red-solid)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg>',ve=k.map(([O,$],Q)=>{let H=!!($&&$.passed),re=$&&$.details||(H?a("sec.testPassed"):a("sec.testFailed")),ie=a(`secDetail.${re}`),ue=ie===`secDetail.${re}`?re:ie,L=H?"var(--sb-surface)":"var(--sb-bg)",j=H?"rgba(var(--sb-green-rgb), 0.16)":"rgba(var(--sb-red-rgb), 0.18)",ne=H?"rgba(var(--sb-green-rgb), 0.12)":"rgba(var(--sb-red-rgb), 0.1)",Te=H?"rgba(var(--sb-green-rgb), 0.26)":"rgba(var(--sb-red-rgb), 0.24)",q=H?"var(--sb-text-1)":"var(--sb-text-4)";return`<div style="display:flex; align-items:flex-start; gap:13px; padding:16px 18px; border-radius:14px; background:${L}; border:1px solid ${j}; animation:svIn .3s cubic-bezier(.2,.7,.3,1) both; animation-delay:${(Q*.04).toFixed(2)}s;">
|
Please wait for real-time cryptographic verification to complete.`);return}let n="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",r=O=>String(O).replace(/[&<>"]/g,$=>({"&":"&","<":"<",">":">",'"':"""})[$]),i=e.color==="orange"?"var(--sb-orange-solid)":e.color==="yellow"?"var(--sb-yellow-solid)":e.color==="red"?"var(--sb-red-solid)":"var(--sb-green-solid)",c=e.color==="orange"?"var(--sb-orange)":e.color==="yellow"?"var(--sb-yellow)":e.color==="red"?"var(--sb-red)":"var(--sb-green)",d=e.color==="orange"?"var(--sb-orange-rgb)":e.color==="yellow"?"var(--sb-yellow-rgb)":e.color==="red"?"var(--sb-red-rgb)":"var(--sb-green-rgb)",h=Math.max(0,Math.min(100,Math.round(e.score||0))),m=2*Math.PI*56,v=`${(m*Math.min(1,h/100)).toFixed(1)} ${m.toFixed(1)}`,S=String(e.level||"SECURE").toUpperCase(),p=e.isRealData!==!1,k=e.verificationResults?Object.entries(e.verificationResults):[],y=Number.isFinite(e.passedChecks)?e.passedChecks:k.filter(([,O])=>O&&O.passed).length,I=Number.isFinite(e.totalChecks)?e.totalChecks:k.length,B=new Date(e.timestamp||Date.now()).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}),N=O=>{let $=a(`secTest.${O}`);return $===`secTest.${O}`?ae(O):$},ae=O=>{let $=String(O).replace(/^verify/i,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").trim();return $=$.replace(/\b(ecdh|ecdsa|aes|gcm|hmac|pfs|sas|mitm|asn|dtls|hkdf|spki|oid|p384)\b/gi,Q=>Q.toUpperCase()),$.charAt(0).toUpperCase()+$.slice(1)},te='<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--sb-green-solid)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4 10-11"/></svg>',pe='<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--sb-red-solid)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg>',ve=k.map(([O,$],Q)=>{let H=!!($&&$.passed),re=$&&$.details||(H?a("sec.testPassed"):a("sec.testFailed")),ie=a(`secDetail.${re}`),ue=ie===`secDetail.${re}`?re:ie,L=H?"var(--sb-surface)":"var(--sb-bg)",j=H?"rgba(var(--sb-green-rgb), 0.16)":"rgba(var(--sb-red-rgb), 0.18)",ne=H?"rgba(var(--sb-green-rgb), 0.12)":"rgba(var(--sb-red-rgb), 0.1)",Te=H?"rgba(var(--sb-green-rgb), 0.26)":"rgba(var(--sb-red-rgb), 0.24)",q=H?"var(--sb-text-1)":"var(--sb-text-4)";return`<div style="display:flex; align-items:flex-start; gap:13px; padding:16px 18px; border-radius:14px; background:${L}; border:1px solid ${j}; animation:svIn .3s cubic-bezier(.2,.7,.3,1) both; animation-delay:${(Q*.04).toFixed(2)}s;">
|
||||||
<span style="flex:none; width:34px; height:34px; border-radius:9px; display:grid; place-items:center; background:${ne}; border:1px solid ${Te};">${H?te:pe}</span>
|
<span style="flex:none; width:34px; height:34px; border-radius:9px; display:grid; place-items:center; background:${ne}; border:1px solid ${Te};">${H?te:pe}</span>
|
||||||
<div style="flex:1; min-width:0;">
|
<div style="flex:1; min-width:0;">
|
||||||
@@ -47,5 +47,5 @@ Please wait for real-time cryptographic verification to complete.`);return}let n
|
|||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="${i}" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" style="flex:none;"><path d="M12 3l8 4v5c0 4.5-3.2 7.8-8 9-4.8-1.2-8-4.5-8-9V7l8-4z"/><path d="M9.2 12.2l2 2 3.6-3.8"/></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="${i}" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" style="flex:none;"><path d="M12 3l8 4v5c0 4.5-3.2 7.8-8 9-4.8-1.2-8-4.5-8-9V7l8-4z"/><path d="M9.2 12.2l2 2 3.6-3.8"/></svg>
|
||||||
<span style="font-size:14px; font-weight:600; color:var(--sb-text-2);">${p?a("sec.realTimeNote"):a("sec.simulatedWarning")}</span>
|
<span style="font-size:14px; font-weight:600; color:var(--sb-text-2);">${p?a("sec.realTimeNote"):a("sec.simulatedWarning")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>`;let F=O=>{O.key==="Escape"&&V()},V=()=>{C.parentNode&&C.remove(),document.removeEventListener("keydown",F)};C.querySelector(".sv-close").addEventListener("click",V),C.addEventListener("click",O=>{O.target===C&&V()}),document.addEventListener("keydown",F);let P=C.querySelector(".sv-rerun");P.addEventListener("mouseenter",()=>{P.style.borderColor="rgba(var(--sb-orange-rgb), 0.45)",P.style.color="var(--sb-orange)"}),P.addEventListener("mouseleave",()=>{P.style.borderColor="rgba(var(--sb-ink), 0.1)",P.style.color="var(--sb-text-4)"}),P.addEventListener("click",()=>{V(),Ji(t)}),document.body.appendChild(C)},Jo=({status:t,onDisconnect:e,webrtcManager:n,title:r,isOffline:i,peerPresence:c,onRenameTitle:d})=>{let h="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",[m,v]=React.useState(!1),[S,p]=React.useState(null),[k,y]=React.useState(!1),[I,B]=React.useState("");React.useEffect(()=>{let q=!0,me=async()=>{try{if(!n)return;let Re=null;typeof n.getRealSecurityLevel=="function"?Re=await n.getRealSecurityLevel():typeof n.calculateAndReportSecurityLevel=="function"?Re=await n.calculateAndReportSecurityLevel():window.EnhancedSecureCryptoUtils&&(Re=await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(n)),q&&Re&&Re.isRealData!==!1&&p(Re)}catch{}};me();let Be=Re=>{q&&Re.detail&&Re.detail.securityData&&p(Re.detail.securityData)};document.addEventListener("real-security-calculated",Be);let rt=setInterval(me,15e3);return()=>{q=!1,clearInterval(rt),document.removeEventListener("real-security-calculated",Be)}},[n]);let N=t==="connected"||t==="verified",ae=t==="disconnected"||t==="peer_disconnected",te=N&&!i,pe=i||ae?"var(--sb-red-solid)":N?"var(--sb-green-solid)":"var(--sb-yellow-solid)",ve=i?a("chat.offline"):N?a("status.connected"):t==="peer_disconnected"?a("status.peerDisconnected"):t==="disconnected"?a("chat.disconnected"):a("status.connecting"),C=i||ae?"0 0 0 3px rgba(var(--sb-red-rgb), 0.16)":N?"0 0 0 3px rgba(var(--sb-green-rgb), 0.16)":"0 0 0 3px rgba(var(--sb-yellow-rgb), 0.16)",F=N&&!i?Xr[c]||"var(--sb-green-solid)":pe,V=N&&!i&&c&&Jr[c]||null,P=()=>{B(r||""),y(!0)},O=()=>{typeof d=="function"&&d(I),y(!1)},$=q=>{q.key==="Enter"?(q.preventDefault(),O()):q.key==="Escape"&&y(!1)},Q=S&&Number.isFinite(S.passedChecks)?S.passedChecks:null,H=S&&Number.isFinite(S.totalChecks)?S.totalChecks:null,re=q=>{if(!q)return q;let me=a(`secLevel.${q}`);return me===`secLevel.${q}`?q:me},ie=Q!=null&&H?Q+"/"+H:S?S.score+"%":"\u2014",ue=S?S.color==="green"?"var(--sb-green)":S.color==="orange"?"var(--sb-orange)":S.color==="yellow"?"var(--sb-yellow)":"var(--sb-red)":"var(--sb-green)",L=React.createElement("div",{key:"sec",title:a("sas.runVerification"),onClick:()=>Ji(n),className:"sb-secpill",style:{display:"flex",alignItems:"center",gap:"9px",padding:"7px 13px",borderRadius:"9px",border:"1px solid "+(m?"rgba(var(--sb-ink), 0.16)":"rgba(var(--sb-ink), 0.07)"),background:m?"rgba(var(--sb-ink), 0.05)":"rgba(var(--sb-ink), 0.02)",cursor:"pointer",fontFamily:"inherit",transition:"all .15s"}},[React.createElement("i",{key:"i",className:"fas fa-shield-halved",style:{color:ue,fontSize:"13px"}}),React.createElement("span",{key:"l",className:"sb-sec-label",style:{fontSize:"13px",fontWeight:600,color:"var(--sb-text-2)"}},S?re(S.level)||a("chatHdr.secure"):a("chatHdr.secure")),React.createElement("span",{key:"d",className:"sb-sec-div",style:{width:"1px",height:"13px",background:"rgba(var(--sb-ink), 0.12)"}}),React.createElement("span",{key:"s",className:"sb-sec-score",style:{fontFamily:h,fontSize:"11.5px",fontWeight:500,color:"var(--sb-text-7)"}},ie),React.createElement("button",{key:"c",type:"button",title:a("sec.panelTitle"),onClick:q=>{q.stopPropagation(),v(me=>!me)},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:"var(--sb-text-9)",fontSize:"11px",transform:m?"rotate(180deg)":"rotate(0deg)",transition:"transform .2s"}}))]),j=React.createElement("style",{key:"hdr-css",dangerouslySetInnerHTML:{__html:".sb-call-btn:not(.sb-call-off):hover{border-color:rgba(var(--sb-green-rgb), 0.45) !important;color:var(--sb-green) !important;background:rgba(var(--sb-green-rgb), 0.07) !important;}@media (max-width:768px){.sb-chat-header{padding-inline-start:58px !important;gap:8px !important;}.sb-chat-header .sb-secpill{display:none !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;}.sb-chat-header .sb-disconnect{width:40px !important;height:40px !important;padding:0 !important;gap:0 !important;justify-content:center !important;border-radius:9px !important;color:var(--sb-red) !important;border-color:rgba(var(--sb-red-rgb), 0.28) !important;}.sb-chat-header .sb-disconnect .sb-hide-sm{display:none !important;}}@media (max-width:600px){.sb-chips{flex-wrap:nowrap !important;jLine truncated
|
</div>`;let F=O=>{O.key==="Escape"&&V()},V=()=>{C.parentNode&&C.remove(),document.removeEventListener("keydown",F)};C.querySelector(".sv-close").addEventListener("click",V),C.addEventListener("click",O=>{O.target===C&&V()}),document.addEventListener("keydown",F);let P=C.querySelector(".sv-rerun");P.addEventListener("mouseenter",()=>{P.style.borderColor="rgba(var(--sb-orange-rgb), 0.45)",P.style.color="var(--sb-orange)"}),P.addEventListener("mouseleave",()=>{P.style.borderColor="rgba(var(--sb-ink), 0.1)",P.style.color="var(--sb-text-4)"}),P.addEventListener("click",()=>{V(),ta(t)}),document.body.appendChild(C)},ec=({status:t,onDisconnect:e,webrtcManager:n,title:r,isOffline:i,peerPresence:c,onRenameTitle:d})=>{let h="'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",[m,v]=React.useState(!1),[S,p]=React.useState(null),[k,y]=React.useState(!1),[I,B]=React.useState("");React.useEffect(()=>{let q=!0,ye=async()=>{try{if(!n)return;let Re=null;typeof n.getRealSecurityLevel=="function"?Re=await n.getRealSecurityLevel():typeof n.calculateAndReportSecurityLevel=="function"?Re=await n.calculateAndReportSecurityLevel():window.EnhancedSecureCryptoUtils&&(Re=await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(n)),q&&Re&&Re.isRealData!==!1&&p(Re)}catch{}};ye();let Be=Re=>{q&&Re.detail&&Re.detail.securityData&&p(Re.detail.securityData)};document.addEventListener("real-security-calculated",Be);let rt=setInterval(ye,15e3);return()=>{q=!1,clearInterval(rt),document.removeEventListener("real-security-calculated",Be)}},[n]);let N=t==="connected"||t==="verified",ae=t==="disconnected"||t==="peer_disconnected",te=N&&!i,pe=i||ae?"var(--sb-red-solid)":N?"var(--sb-green-solid)":"var(--sb-yellow-solid)",ve=i?a("chat.offline"):N?a("status.connected"):t==="peer_disconnected"?a("status.peerDisconnected"):t==="disconnected"?a("chat.disconnected"):a("status.connecting"),C=i||ae?"0 0 0 3px rgba(var(--sb-red-rgb), 0.16)":N?"0 0 0 3px rgba(var(--sb-green-rgb), 0.16)":"0 0 0 3px rgba(var(--sb-yellow-rgb), 0.16)",F=N&&!i?Jr[c]||"var(--sb-green-solid)":pe,V=N&&!i&&c&&Zr[c]||null,P=()=>{B(r||""),y(!0)},O=()=>{typeof d=="function"&&d(I),y(!1)},$=q=>{q.key==="Enter"?(q.preventDefault(),O()):q.key==="Escape"&&y(!1)},Q=S&&Number.isFinite(S.passedChecks)?S.passedChecks:null,H=S&&Number.isFinite(S.totalChecks)?S.totalChecks:null,re=q=>{if(!q)return q;let ye=a(`secLevel.${q}`);return ye===`secLevel.${q}`?q:ye},ie=Q!=null&&H?Q+"/"+H:S?S.score+"%":"\u2014",ue=S?S.color==="green"?"var(--sb-green)":S.color==="orange"?"var(--sb-orange)":S.color==="yellow"?"var(--sb-yellow)":"var(--sb-red)":"var(--sb-green)",L=React.createElement("div",{key:"sec",title:a("sas.runVerification"),onClick:()=>ta(n),className:"sb-secpill",style:{display:"flex",alignItems:"center",gap:"9px",padding:"7px 13px",borderRadius:"9px",border:"1px solid "+(m?"rgba(var(--sb-ink), 0.16)":"rgba(var(--sb-ink), 0.07)"),background:m?"rgba(var(--sb-ink), 0.05)":"rgba(var(--sb-ink), 0.02)",cursor:"pointer",fontFamily:"inherit",transition:"all .15s"}},[React.createElement("i",{key:"i",className:"fas fa-shield-halved",style:{color:ue,fontSize:"13px"}}),React.createElement("span",{key:"l",className:"sb-sec-label",style:{fontSize:"13px",fontWeight:600,color:"var(--sb-text-2)"}},S?re(S.level)||a("chatHdr.secure"):a("chatHdr.secure")),React.createElement("span",{key:"d",className:"sb-sec-div",style:{width:"1px",height:"13px",background:"rgba(var(--sb-ink), 0.12)"}}),React.createElement("span",{key:"s",className:"sb-sec-score",style:{fontFamily:h,fontSize:"11.5px",fontWeight:500,color:"var(--sb-text-7)"}},ie),React.createElement("button",{key:"c",type:"button",title:a("sec.panelTitle"),onClick:q=>{q.stopPropagation(),v(ye=>!ye)},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:"var(--sb-text-9)",fontSize:"11px",transform:m?"rotate(180deg)":"rotate(0deg)",transition:"transform .2s"}}))]),j=React.createElement("style",{key:"hdr-css",dangerouslySetInnerHTML:{__html:".sb-call-btn:not(.sb-call-off):hover{border-color:rgba(var(--sb-green-rgb), 0.45) !important;color:var(--sb-green) !important;background:rgba(var(--sb-green-rgb), 0.07) !important;}@media (max-width:768px){.sb-chat-header{padding-inline-start:58px !important;gap:8px !important;}.sb-chat-header .sb-secpill{display:none !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;}.sb-chat-header .sb-disconnect{width:40px !important;height:40px !important;padding:0 !important;gap:0 !important;justify-content:center !important;border-radius:9px !important;color:var(--sb-red) !important;border-color:rgba(var(--sb-red-rgb), 0.28) !important;}.sb-chat-header .sb-disconnect .sb-hide-sm{display:none !important;}}@media (max-width:600px){.sb-chips{flex-wrap:nowrap !important;jLine truncated
|
||||||
//# sourceMappingURL=app.js.map
|
//# sourceMappingURL=app.js.map
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": 3,
|
"version": 3,
|
||||||
"sources": ["../src/utils/debugWindowHooks.js", "../src/network/iceSettingsStore.js", "../src/network/turnCredentials.js", "../src/i18n/generated.js", "../src/i18n/dict/en.js", "../src/i18n/index.js", "../src/state/sessionsStore.js", "../src/group/groupCrypto.js", "../src/state/groupsStore.js", "../src/group/GroupSession.js", "../src/group/groupSender.js", "../src/ui/motion.js", "../src/components/ui/LanguageSuggestion.jsx", "../src/components/ui/GroupChat.jsx", "../src/group/groupCallMedia.js", "../src/components/ui/callLayout.js", "../src/components/ui/GroupCallUI.jsx", "../src/app.jsx"],
|
"sources": ["../src/utils/debugWindowHooks.js", "../src/network/iceSettingsStore.js", "../src/network/turnCredentials.js", "../src/i18n/generated.js", "../src/i18n/dict/en.js", "../src/i18n/index.js", "../src/state/sessionsStore.js", "../src/group/groupCrypto.js", "../src/state/groupsStore.js", "../src/group/GroupSession.js", "../src/group/groupSender.js", "../src/ui/motion.js", "../src/components/ui/LanguageSuggestion.jsx", "../src/components/ui/GroupChat.jsx", "../src/group/groupCallMedia.js", "../src/components/ui/callLayout.js", "../src/components/ui/GroupCallUI.jsx", "../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 keLine truncated
|
"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 keLine truncated
|
||||||
"mappings": "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 truncated
|
"mappings": "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 truncated
|
||||||
"names": ["isSecureBitDebugEnabled", "targetWindow", "installDebugWindowHooks", "webrtcManagerRef", "onClearData", "clearConsole", "DB_NAME", "KEY_RECORD", "SETTINGS_RECORD", "isSupported", "openDb", "resolve", "reject", "request", "DB_NAME", "error", "db", "idbGet", "key", "req", "idbPut", "value", "tx", "idbDelete", "getOrCreateDeviceKey", "existing", "KEY_RECORD", "saveIceSettings", "settings", "payload", "iv", "ciphertext", "SETTINGS_RECORD", "loadIceSettings", "record", "plaintext", "parsed", "clearIceSettings", "ENDPOINT", "OWN_RELAY_HOSTS", "RETRY_DELAYS_MS", "relayHost", "url", "m", "isOwnRelayEntry", "entry", "u", "OWN_RELAY_HOSTS", "isCleanField", "value", "i", "c", "applyTurnCredentials", "list", "cred", "updated", "urls", "parseCredentialResponse", "body", "nowSeconds", "server", "expiry", "ttl", "started", "startTurnCredentialRefresh", "opts", "getList", "timer", "failures", "schedule", "ms", "refresh", "res", "ENDPOINT", "delay", "now", "SUPPORTED_LOCALES", "LOCALE_META", "CROSS_LOCALE_STRINGS", "DICTIONARY", "registry", "REGISTRY", "dictionary", "code", "STORAGE_KEY", "localeFromPathname", "pathname", "segment", "SUPPORTED_LOCALES", "localeFromLanguages", "languages", "tag", "lower", "exact", "base", "partial", "detectLocale", "stored", "fromPath", "isLocaleRoot", "localeHref", "current", "tail", "rememberLocale", "storedLocale", "resolvedLocale", "currentLocale", "w", "localeDir", "LOCALE_META", "isRTL", "direction", "SUGGEST_KEY", "localeSuggestionDismissed", "dismissLocaleSuggestion", "suggestedLocale", "pathname", "languages", "stored", "shown", "localeFromPathname", "preferred", "SUPPORTED_LOCALES", "localeFromLanguages", "t", "key", "vars", "locale", "currentLocale", "template", "dictionary", "CROSS_LOCALE_STRINGS", "match", "name", "SESSION_ACTIONS", "PRESENCE_DOT", "PRESENCE_WORD", "t", "MY_STATUS_OPTIONS", "shortLabelFromId", "id", "hex", "monoInitials", "label", "words", "a", "b", "statusSub", "status", "t", "emptySetup", "createSessionEntry", "opts", "id", "shortLabelFromId", "createInitialState", "patchSession", "state", "patch", "session", "sessionsReducer", "action", "A", "SESSION_ACTIONS", "entry", "sessions", "order", "x", "activeSessionId", "removedIdx", "next", "changed", "messages", "m", "byId", "byMid", "byFileId", "label", "decorateSession", "lastMessage", "s", "isUp", "isPending", "dot", "headerSub", "PRESENCE_DOT", "PRESENCE_WORD", "preview", "monoInitials", "decorateSessions", "GROUP_LIMITS", "CALL_ACTIONS", "MESH_KINDS", "MEMBER_OPS", "ENC", "GroupCryptoError", "message", "code", "fail", "msg", "toHex", "bytes", "view", "out", "i", "fromHex", "hex", "toB64", "binary", "fromB64", "b64", "max", "randomBytes", "n", "newGroupId", "lp", "label", "parts", "chunks", "total", "part", "header", "o", "c", "u32", "b", "equalBytes", "a", "diff", "assertGroupId", "groupId", "assertFingerprint", "fp", "assertEpoch", "epoch", "assertName", "name", "value", "canonicalFingerprints", "fps", "seen", "generateGroupIdentity", "subtle", "keyPair", "spki", "fingerprint", "fingerprintSpki", "importMemberIdentity", "publicKey", "buildCommitment", "nonce", "payload", "verifyCommitment", "commitment", "fields", "expected", "computeGroupSas", "contributions", "digits", "ordered", "ikm", "salt", "key", "bits", "dv", "GroupSasCeremony", "selfFingerprint", "memberFingerprints", "existing", "memberOpPayload", "op", "memberFps", "signMemberOp", "privateKey", "sig", "verifyMemberOp", "signature", "hashBody", "body", "groupMessagePayload", "seq", "senderFp", "bodyHash", "signGroupMessage", "verifyGroupMessage", "meshDescriptorPayload", "kind", "fromFp", "toFp", "descriptor", "signMeshDescriptor", "verifyMeshDescriptor", "linkProbePayload", "linkFp", "signLinkProbe", "verifyLinkProbe", "groupCallPayload", "callId", "action", "withVideo", "assertCallId", "newCallId", "signGroupCall", "verifyGroupCall", "GROUP_ACTIONS", "GROUP_PHASE", "MEMBER_STATE", "GROUP_PHASE_WORD", "t", "groupInitials", "name", "words", "a", "b", "createGroupEntry", "opts", "byFingerprint", "NAME_ENCODER", "clampNameBytes", "value", "out", "GROUP_LIMITS", "createInitialGroupState", "patchGroup", "state", "id", "patch", "group", "groupsReducer", "action", "A", "entry", "groups", "order", "x", "activeGroupId", "removedIdx", "changed", "members", "m", "next", "k", "messages", "linkedCount", "groupSub", "total", "linked", "groupDot", "decorateGroup", "lastMessage", "sub", "decorateGroups", "g", "GROUP_FRAMES", "GROUP_ENVELOPE", "GROUP_FRAME_TYPES", "isGroupFrame", "parsed", "groupFrameType", "encodeEnvelope", "frame", "json", "encoded", "toB64", "GROUP_LIMITS", "GroupSessionError", "decodeEnvelope", "envelope", "raw", "bytes", "fromB64", "TIMEOUTS", "MESH_MAX_CONCURRENT_DIALS", "MESH_MAX_ATTEMPTS", "message", "code", "GroupSession", "groupId", "name", "isAdmin", "subtle", "send", "emit", "mesh", "log", "assertGroupId", "assertName", "GROUP_PHASE", "newGroupId", "generateGroupIdentity", "MEMBER_STATE", "t", "sessionId", "fn", "ms", "phLine truncated
|
"names": ["isSecureBitDebugEnabled", "targetWindow", "installDebugWindowHooks", "webrtcManagerRef", "onClearData", "clearConsole", "DB_NAME", "KEY_RECORD", "SETTINGS_RECORD", "isSupported", "openDb", "resolve", "reject", "request", "DB_NAME", "error", "db", "idbGet", "key", "req", "idbPut", "value", "tx", "idbDelete", "getOrCreateDeviceKey", "existing", "KEY_RECORD", "saveIceSettings", "settings", "payload", "iv", "ciphertext", "SETTINGS_RECORD", "loadIceSettings", "record", "plaintext", "parsed", "clearIceSettings", "ENDPOINT", "OWN_RELAY_HOSTS", "RETRY_DELAYS_MS", "relayHost", "url", "m", "isOwnRelayEntry", "entry", "u", "OWN_RELAY_HOSTS", "isCleanField", "value", "i", "c", "applyTurnCredentials", "list", "cred", "updated", "urls", "parseCredentialResponse", "body", "nowSeconds", "server", "expiry", "ttl", "started", "startTurnCredentialRefresh", "opts", "getList", "timer", "failures", "schedule", "ms", "refresh", "res", "ENDPOINT", "delay", "now", "SUPPORTED_LOCALES", "LOCALE_META", "CROSS_LOCALE_STRINGS", "DICTIONARY", "registry", "REGISTRY", "dictionary", "code", "STORAGE_KEY", "localeFromPathname", "pathname", "segment", "SUPPORTED_LOCALES", "localeFromLanguages", "languages", "tag", "lower", "exact", "base", "partial", "detectLocale", "stored", "fromPath", "isLocaleRoot", "localeHref", "current", "tail", "rememberLocale", "storedLocale", "resolvedLocale", "currentLocale", "w", "localeDir", "LOCALE_META", "isRTL", "direction", "SUGGEST_KEY", "localeSuggestionDismissed", "dismissLocaleSuggestion", "suggestedLocale", "pathname", "languages", "stored", "shown", "localeFromPathname", "preferred", "SUPPORTED_LOCALES", "localeFromLanguages", "t", "key", "vars", "locale", "currentLocale", "template", "dictionary", "CROSS_LOCALE_STRINGS", "match", "name", "SESSION_ACTIONS", "PRESENCE_DOT", "PRESENCE_WORD", "t", "MY_STATUS_OPTIONS", "shortLabelFromId", "id", "hex", "monoInitials", "label", "words", "a", "b", "statusSub", "status", "t", "emptySetup", "createSessionEntry", "opts", "id", "shortLabelFromId", "createInitialState", "patchSession", "state", "patch", "session", "sessionsReducer", "action", "A", "SESSION_ACTIONS", "entry", "sessions", "order", "x", "activeSessionId", "removedIdx", "next", "changed", "messages", "m", "byId", "byMid", "byFileId", "label", "decorateSession", "lastMessage", "s", "isUp", "isPending", "dot", "headerSub", "PRESENCE_DOT", "PRESENCE_WORD", "preview", "monoInitials", "decorateSessions", "GROUP_LIMITS", "CALL_ACTIONS", "MESH_KINDS", "MEMBER_OPS", "ENC", "GroupCryptoError", "message", "code", "fail", "msg", "toHex", "bytes", "view", "out", "i", "fromHex", "hex", "toB64", "binary", "fromB64", "b64", "max", "randomBytes", "n", "newGroupId", "lp", "label", "parts", "chunks", "total", "part", "header", "o", "c", "u32", "b", "equalBytes", "a", "diff", "assertGroupId", "groupId", "assertFingerprint", "fp", "assertEpoch", "epoch", "assertName", "name", "value", "canonicalFingerprints", "fps", "seen", "generateGroupIdentity", "subtle", "keyPair", "spki", "fingerprint", "fingerprintSpki", "importMemberIdentity", "publicKey", "buildCommitment", "nonce", "payload", "verifyCommitment", "commitment", "fields", "expected", "computeGroupSas", "contributions", "digits", "ordered", "ikm", "salt", "key", "bits", "dv", "GroupSasCeremony", "selfFingerprint", "memberFingerprints", "existing", "memberOpPayload", "op", "memberFps", "signMemberOp", "privateKey", "sig", "verifyMemberOp", "signature", "hashBody", "body", "groupMessagePayload", "seq", "senderFp", "bodyHash", "signGroupMessage", "verifyGroupMessage", "meshDescriptorPayload", "kind", "fromFp", "toFp", "descriptor", "signMeshDescriptor", "verifyMeshDescriptor", "linkProbePayload", "linkFp", "signLinkProbe", "verifyLinkProbe", "groupCallPayload", "callId", "action", "withVideo", "assertCallId", "newCallId", "signGroupCall", "verifyGroupCall", "GROUP_ACTIONS", "GROUP_PHASE", "MEMBER_STATE", "GROUP_PHASE_WORD", "t", "groupInitials", "name", "words", "a", "b", "createGroupEntry", "opts", "byFingerprint", "NAME_ENCODER", "clampNameBytes", "value", "out", "GROUP_LIMITS", "createInitialGroupState", "patchGroup", "state", "id", "patch", "group", "groupsReducer", "action", "A", "entry", "groups", "order", "x", "activeGroupId", "removedIdx", "changed", "members", "m", "next", "k", "messages", "linkedCount", "groupSub", "total", "linked", "groupDot", "decorateGroup", "lastMessage", "sub", "decorateGroups", "g", "GROUP_FRAMES", "GROUP_ENVELOPE", "GROUP_FRAME_TYPES", "isGroupFrame", "parsed", "groupFrameType", "encodeEnvelope", "frame", "json", "encoded", "toB64", "GROUP_LIMITS", "GroupSessionError", "decodeEnvelope", "envelope", "raw", "bytes", "fromB64", "TIMEOUTS", "MESH_MAX_CONCURRENT_DIALS", "MESH_MAX_ATTEMPTS", "message", "code", "GroupSession", "groupId", "name", "isAdmin", "subtle", "send", "emit", "mesh", "log", "assertGroupId", "assertName", "GROUP_PHASE", "newGroupId", "generateGroupIdentity", "MEMBER_STATE", "t", "sessionId", "fn", "ms", "phLine truncated
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ this document describes.
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Release | v6.8.6 |
|
| Release | v6.8.8 |
|
||||||
| Protocol version | 4.1 |
|
| Protocol version | 4.1 |
|
||||||
| Ratchet wire version | 1 |
|
| Ratchet wire version | 1 |
|
||||||
|
|
||||||
@@ -347,5 +347,5 @@ no leg may stop a capture it borrowed.
|
|||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
This describes the browser implementation as it stands in v6.8.6. It is not a
|
This describes the browser implementation as it stands in v6.8.8. It is not a
|
||||||
substitute for independent cryptographic review.
|
substitute for independent cryptographic review.
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody><tr>
|
<tbody><tr>
|
||||||
<td>Release</td>
|
<td>Release</td>
|
||||||
<td>v6.8.6</td>
|
<td>v6.8.8</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Protocol version</td>
|
<td>Protocol version</td>
|
||||||
@@ -486,7 +486,7 @@
|
|||||||
shared across every leg, and stopped when the call, the group or the tab ends;
|
shared across every leg, and stopped when the call, the group or the tab ends;
|
||||||
no leg may stop a capture it borrowed.</p>
|
no leg may stop a capture it borrowed.</p>
|
||||||
<h2 id="scope">Scope</h2>
|
<h2 id="scope">Scope</h2>
|
||||||
<p>This describes the browser implementation as it stands in v6.8.6. It is not a
|
<p>This describes the browser implementation as it stands in v6.8.8. It is not a
|
||||||
substitute for independent cryptographic review.</p>
|
substitute for independent cryptographic review.</p>
|
||||||
|
|
||||||
<nav class="more">
|
<nav class="more">
|
||||||
|
|||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/es/manifest.json">
|
<link rel="manifest" href="/es/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/es.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/es.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/fa/manifest.json">
|
<link rel="manifest" href="/fa/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/fa.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/fa.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/fr/manifest.json">
|
<link rel="manifest" href="/fr/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/fr.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/fr.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/he/manifest.json">
|
<link rel="manifest" href="/he/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/he.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/he.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/hi/manifest.json">
|
<link rel="manifest" href="/hi/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/hi.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/hi.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,18 +287,18 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -486,12 +486,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/ko/manifest.json">
|
<link rel="manifest" href="/ko/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/ko.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/ko.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "النظام",
|
"theme.system": "النظام",
|
||||||
"theme.light": "فاتح",
|
"theme.light": "فاتح",
|
||||||
"theme.dark": "داكن",
|
"theme.dark": "داكن",
|
||||||
|
"donate.eyebrow": "تبرّع بالعملات المشفّرة",
|
||||||
|
"donate.title": "ساعدنا في إبقاء SecureBit مستقلًا",
|
||||||
|
"donate.description": "يذهب كل تبرّع إلى التطوير وعمليات التدقيق الأمني.",
|
||||||
|
"donate.copy": "نسخ",
|
||||||
|
"donate.copied": "تم النسخ",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "شبكة Bitcoin",
|
||||||
|
"donate.net.eth": "الشبكة الرئيسية لـ Ethereum",
|
||||||
|
"donate.warn.usdt": "أرسل USDT على شبكة Tron (TRC-20) فقط.",
|
||||||
|
"donate.warn.btc": "أرسل BTC على شبكة Bitcoin فقط.",
|
||||||
|
"donate.warn.eth": "أرسل ETH على الشبكة الرئيسية لـ Ethereum فقط.",
|
||||||
|
"donate.qrAlt": "رمز QR لعنوان التبرّع بـ {coin}",
|
||||||
"community.title": "انضمّ إلى مستقبل الخصوصية",
|
"community.title": "انضمّ إلى مستقبل الخصوصية",
|
||||||
"community.description": "ينمو SecureBit بفضل مجتمعه. أفكاركم وملاحظاتكم هي ما يصوغ مستقبل التواصل الآمن - مبنيّ في العلن، مع تحقّق ASN.1 كامل من طرف إلى طرف.",
|
"community.description": "ينمو SecureBit بفضل مجتمعه. أفكاركم وملاحظاتكم هي ما يصوغ مستقبل التواصل الآمن - مبنيّ في العلن، مع تحقّق ASN.1 كامل من طرف إلى طرف.",
|
||||||
"community.github": "مستودع GitHub",
|
"community.github": "مستودع GitHub",
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "System",
|
"theme.system": "System",
|
||||||
"theme.light": "Hell",
|
"theme.light": "Hell",
|
||||||
"theme.dark": "Dunkel",
|
"theme.dark": "Dunkel",
|
||||||
|
"donate.eyebrow": "Mit Krypto spenden",
|
||||||
|
"donate.title": "Halte SecureBit unabhängig",
|
||||||
|
"donate.description": "Jede Spende fließt in Entwicklung und Audits.",
|
||||||
|
"donate.copy": "Kopieren",
|
||||||
|
"donate.copied": "Kopiert",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin-Netzwerk",
|
||||||
|
"donate.net.eth": "Ethereum-Mainnet",
|
||||||
|
"donate.warn.usdt": "Sende nur USDT über Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Sende nur BTC über das Bitcoin-Netzwerk.",
|
||||||
|
"donate.warn.eth": "Sende nur ETH über das Ethereum-Mainnet.",
|
||||||
|
"donate.qrAlt": "QR-Code der {coin}-Spendenadresse",
|
||||||
"community.title": "Gestalten Sie die Zukunft der Privatsphäre mit",
|
"community.title": "Gestalten Sie die Zukunft der Privatsphäre mit",
|
||||||
"community.description": "SecureBit wächst durch seine Community. Ihre Ideen und Ihr Feedback prägen die Zukunft sicherer Kommunikation - offen entwickelt, mit vollständiger ASN.1-Validierung von Ende zu Ende.",
|
"community.description": "SecureBit wächst durch seine Community. Ihre Ideen und Ihr Feedback prägen die Zukunft sicherer Kommunikation - offen entwickelt, mit vollständiger ASN.1-Validierung von Ende zu Ende.",
|
||||||
"community.github": "GitHub-Repository",
|
"community.github": "GitHub-Repository",
|
||||||
|
|||||||
@@ -37,6 +37,18 @@
|
|||||||
"theme.system": "System",
|
"theme.system": "System",
|
||||||
"theme.light": "Light",
|
"theme.light": "Light",
|
||||||
"theme.dark": "Dark",
|
"theme.dark": "Dark",
|
||||||
|
"donate.eyebrow": "Donate in crypto",
|
||||||
|
"donate.title": "Keep SecureBit independent",
|
||||||
|
"donate.description": "Every donation goes to development and audits.",
|
||||||
|
"donate.copy": "Copy",
|
||||||
|
"donate.copied": "Copied",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin network",
|
||||||
|
"donate.net.eth": "Ethereum mainnet",
|
||||||
|
"donate.warn.usdt": "Send only USDT on Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Send only BTC on the Bitcoin network.",
|
||||||
|
"donate.warn.eth": "Send only ETH on Ethereum mainnet.",
|
||||||
|
"donate.qrAlt": "QR code of the {coin} donation address",
|
||||||
"community.title": "Join the future of privacy",
|
"community.title": "Join the future of privacy",
|
||||||
"community.description": "SecureBit grows thanks to its community. Your ideas and feedback shape the future of secure communication - built in the open, with complete ASN.1 validation end-to-end.",
|
"community.description": "SecureBit grows thanks to its community. Your ideas and feedback shape the future of secure communication - built in the open, with complete ASN.1 validation end-to-end.",
|
||||||
"community.github": "GitHub Repository",
|
"community.github": "GitHub Repository",
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "Sistema",
|
"theme.system": "Sistema",
|
||||||
"theme.light": "Claro",
|
"theme.light": "Claro",
|
||||||
"theme.dark": "Oscuro",
|
"theme.dark": "Oscuro",
|
||||||
|
"donate.eyebrow": "Dona en cripto",
|
||||||
|
"donate.title": "Mantén SecureBit independiente",
|
||||||
|
"donate.description": "Cada donación se destina al desarrollo y a las auditorías.",
|
||||||
|
"donate.copy": "Copiar",
|
||||||
|
"donate.copied": "Copiado",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Red Bitcoin",
|
||||||
|
"donate.net.eth": "Red principal de Ethereum",
|
||||||
|
"donate.warn.usdt": "Envía solo USDT en Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Envía solo BTC en la red Bitcoin.",
|
||||||
|
"donate.warn.eth": "Envía solo ETH en la red principal de Ethereum.",
|
||||||
|
"donate.qrAlt": "Código QR de la dirección de donación en {coin}",
|
||||||
"community.title": "Construye el futuro de la privacidad",
|
"community.title": "Construye el futuro de la privacidad",
|
||||||
"community.description": "SecureBit crece gracias a su comunidad. Tus ideas y comentarios dan forma al futuro de la comunicación segura, desarrollada de forma abierta y con validación ASN.1 completa de extremo a extremo.",
|
"community.description": "SecureBit crece gracias a su comunidad. Tus ideas y comentarios dan forma al futuro de la comunicación segura, desarrollada de forma abierta y con validación ASN.1 completa de extremo a extremo.",
|
||||||
"community.github": "Repositorio en GitHub",
|
"community.github": "Repositorio en GitHub",
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "سیستم",
|
"theme.system": "سیستم",
|
||||||
"theme.light": "روشن",
|
"theme.light": "روشن",
|
||||||
"theme.dark": "تیره",
|
"theme.dark": "تیره",
|
||||||
|
"donate.eyebrow": "اهدای کمک با رمزارز",
|
||||||
|
"donate.title": "به مستقل ماندن SecureBit کمک کنید",
|
||||||
|
"donate.description": "هر کمک صرف توسعه و ممیزیهای امنیتی میشود.",
|
||||||
|
"donate.copy": "کپی",
|
||||||
|
"donate.copied": "کپی شد",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "شبکه Bitcoin",
|
||||||
|
"donate.net.eth": "شبکه اصلی Ethereum",
|
||||||
|
"donate.warn.usdt": "فقط USDT را روی شبکه Tron (TRC-20) بفرستید.",
|
||||||
|
"donate.warn.btc": "فقط BTC را روی شبکه Bitcoin بفرستید.",
|
||||||
|
"donate.warn.eth": "فقط ETH را روی شبکه اصلی Ethereum بفرستید.",
|
||||||
|
"donate.qrAlt": "کد QR نشانی کمک با {coin}",
|
||||||
"community.title": "به آیندهٔ حریم خصوصی بپیوندید",
|
"community.title": "به آیندهٔ حریم خصوصی بپیوندید",
|
||||||
"community.description": "SecureBit بهلطف جامعهاش رشد میکند. ایدهها و بازخوردهای شما آیندهٔ ارتباط امن را شکل میدهد - ساختهشده در فضای باز، با اعتبارسنجی کامل ASN.1 از یک سر تا سر دیگر.",
|
"community.description": "SecureBit بهلطف جامعهاش رشد میکند. ایدهها و بازخوردهای شما آیندهٔ ارتباط امن را شکل میدهد - ساختهشده در فضای باز، با اعتبارسنجی کامل ASN.1 از یک سر تا سر دیگر.",
|
||||||
"community.github": "مخزن GitHub",
|
"community.github": "مخزن GitHub",
|
||||||
|
|||||||
@@ -37,6 +37,18 @@
|
|||||||
"theme.system": "Système",
|
"theme.system": "Système",
|
||||||
"theme.light": "Clair",
|
"theme.light": "Clair",
|
||||||
"theme.dark": "Sombre",
|
"theme.dark": "Sombre",
|
||||||
|
"donate.eyebrow": "Faire un don en crypto",
|
||||||
|
"donate.title": "Gardons SecureBit indépendant",
|
||||||
|
"donate.description": "Chaque don finance le développement et les audits.",
|
||||||
|
"donate.copy": "Copier",
|
||||||
|
"donate.copied": "Copié",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Réseau Bitcoin",
|
||||||
|
"donate.net.eth": "Réseau principal Ethereum",
|
||||||
|
"donate.warn.usdt": "Envoyez uniquement de l'USDT sur Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Envoyez uniquement du BTC sur le réseau Bitcoin.",
|
||||||
|
"donate.warn.eth": "Envoyez uniquement de l'ETH sur le réseau principal Ethereum.",
|
||||||
|
"donate.qrAlt": "QR code de l'adresse de don {coin}",
|
||||||
"community.title": "Construisez l'avenir de la vie privée",
|
"community.title": "Construisez l'avenir de la vie privée",
|
||||||
"community.description": "SecureBit grandit grâce à sa communauté. Vos idées et vos retours façonnent l'avenir des communications sécurisées, développées à ciel ouvert, avec une validation ASN.1 complète de bout en bout.",
|
"community.description": "SecureBit grandit grâce à sa communauté. Vos idées et vos retours façonnent l'avenir des communications sécurisées, développées à ciel ouvert, avec une validation ASN.1 complète de bout en bout.",
|
||||||
"community.github": "Dépôt GitHub",
|
"community.github": "Dépôt GitHub",
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "מערכת",
|
"theme.system": "מערכת",
|
||||||
"theme.light": "בהיר",
|
"theme.light": "בהיר",
|
||||||
"theme.dark": "כהה",
|
"theme.dark": "כהה",
|
||||||
|
"donate.eyebrow": "תרומה בקריפטו",
|
||||||
|
"donate.title": "עזרו לשמור על SecureBit עצמאית",
|
||||||
|
"donate.description": "כל תרומה מופנית לפיתוח ולביקורות אבטחה.",
|
||||||
|
"donate.copy": "העתקה",
|
||||||
|
"donate.copied": "הועתק",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "רשת Bitcoin",
|
||||||
|
"donate.net.eth": "הרשת הראשית של Ethereum",
|
||||||
|
"donate.warn.usdt": "שלחו USDT ברשת Tron (TRC-20) בלבד.",
|
||||||
|
"donate.warn.btc": "שלחו BTC ברשת Bitcoin בלבד.",
|
||||||
|
"donate.warn.eth": "שלחו ETH ברשת הראשית של Ethereum בלבד.",
|
||||||
|
"donate.qrAlt": "קוד QR של כתובת התרומה ב-{coin}",
|
||||||
"community.title": "הצטרפו לעתיד של הפרטיות",
|
"community.title": "הצטרפו לעתיד של הפרטיות",
|
||||||
"community.description": "SecureBit גדל בזכות הקהילה שלו. הרעיונות והמשוב שלכם מעצבים את עתיד התקשורת המאובטחת - נבנה בגלוי, עם אימות ASN.1 מלא מקצה לקצה.",
|
"community.description": "SecureBit גדל בזכות הקהילה שלו. הרעיונות והמשוב שלכם מעצבים את עתיד התקשורת המאובטחת - נבנה בגלוי, עם אימות ASN.1 מלא מקצה לקצה.",
|
||||||
"community.github": "מאגר GitHub",
|
"community.github": "מאגר GitHub",
|
||||||
|
|||||||
@@ -37,6 +37,18 @@
|
|||||||
"theme.system": "सिस्टम",
|
"theme.system": "सिस्टम",
|
||||||
"theme.light": "हल्की",
|
"theme.light": "हल्की",
|
||||||
"theme.dark": "गहरी",
|
"theme.dark": "गहरी",
|
||||||
|
"donate.eyebrow": "क्रिप्टो में दान करें",
|
||||||
|
"donate.title": "SecureBit को स्वतंत्र बनाए रखें",
|
||||||
|
"donate.description": "हर दान विकास और सुरक्षा ऑडिट पर खर्च होता है।",
|
||||||
|
"donate.copy": "कॉपी करें",
|
||||||
|
"donate.copied": "कॉपी हो गया",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin नेटवर्क",
|
||||||
|
"donate.net.eth": "Ethereum मेननेट",
|
||||||
|
"donate.warn.usdt": "केवल Tron (TRC-20) पर USDT भेजें।",
|
||||||
|
"donate.warn.btc": "केवल Bitcoin नेटवर्क पर BTC भेजें।",
|
||||||
|
"donate.warn.eth": "केवल Ethereum मेननेट पर ETH भेजें।",
|
||||||
|
"donate.qrAlt": "{coin} दान पते का QR कोड",
|
||||||
"community.title": "निजता के भविष्य में शामिल हों",
|
"community.title": "निजता के भविष्य में शामिल हों",
|
||||||
"community.description": "SecureBit अपने समुदाय से बढ़ता है। आपके विचार और सुझाव सुरक्षित संवाद का भविष्य गढ़ते हैं - खुले में बना, और पूरे रास्ते पूरी ASN.1 जाँच के साथ।",
|
"community.description": "SecureBit अपने समुदाय से बढ़ता है। आपके विचार और सुझाव सुरक्षित संवाद का भविष्य गढ़ते हैं - खुले में बना, और पूरे रास्ते पूरी ASN.1 जाँच के साथ।",
|
||||||
"community.github": "GitHub रिपॉज़िटरी",
|
"community.github": "GitHub रिपॉज़िटरी",
|
||||||
|
|||||||
@@ -37,6 +37,18 @@
|
|||||||
"theme.system": "시스템",
|
"theme.system": "시스템",
|
||||||
"theme.light": "밝게",
|
"theme.light": "밝게",
|
||||||
"theme.dark": "어둡게",
|
"theme.dark": "어둡게",
|
||||||
|
"donate.eyebrow": "암호화폐로 후원하기",
|
||||||
|
"donate.title": "SecureBit의 독립을 지켜 주세요",
|
||||||
|
"donate.description": "모든 후원금은 개발과 보안 감사에 쓰입니다.",
|
||||||
|
"donate.copy": "복사",
|
||||||
|
"donate.copied": "복사됨",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "비트코인 네트워크",
|
||||||
|
"donate.net.eth": "이더리움 메인넷",
|
||||||
|
"donate.warn.usdt": "USDT는 Tron(TRC-20) 네트워크로만 보내세요.",
|
||||||
|
"donate.warn.btc": "BTC는 비트코인 네트워크로만 보내세요.",
|
||||||
|
"donate.warn.eth": "ETH는 이더리움 메인넷으로만 보내세요.",
|
||||||
|
"donate.qrAlt": "{coin} 후원 주소 QR 코드",
|
||||||
"community.title": "프라이버시의 미래를 함께 만듭니다",
|
"community.title": "프라이버시의 미래를 함께 만듭니다",
|
||||||
"community.description": "SecureBit은 커뮤니티 덕분에 자랍니다. 여러분의 아이디어와 의견이 안전한 소통의 미래를 만듭니다 - 모든 과정이 공개되어 있고, 전 구간에서 ASN.1 검증을 수행합니다.",
|
"community.description": "SecureBit은 커뮤니티 덕분에 자랍니다. 여러분의 아이디어와 의견이 안전한 소통의 미래를 만듭니다 - 모든 과정이 공개되어 있고, 전 구간에서 ASN.1 검증을 수행합니다.",
|
||||||
"community.github": "GitHub 저장소",
|
"community.github": "GitHub 저장소",
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "Системная",
|
"theme.system": "Системная",
|
||||||
"theme.light": "Светлая",
|
"theme.light": "Светлая",
|
||||||
"theme.dark": "Тёмная",
|
"theme.dark": "Тёмная",
|
||||||
|
"donate.eyebrow": "Пожертвование в криптовалюте",
|
||||||
|
"donate.title": "Сохраним SecureBit независимым",
|
||||||
|
"donate.description": "Каждое пожертвование идёт на разработку и аудиты.",
|
||||||
|
"donate.copy": "Копировать",
|
||||||
|
"donate.copied": "Скопировано",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Сеть Bitcoin",
|
||||||
|
"donate.net.eth": "Основная сеть Ethereum",
|
||||||
|
"donate.warn.usdt": "Отправляйте только USDT в сети Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Отправляйте только BTC в сети Bitcoin.",
|
||||||
|
"donate.warn.eth": "Отправляйте только ETH в основной сети Ethereum.",
|
||||||
|
"donate.qrAlt": "QR-код адреса для пожертвований в {coin}",
|
||||||
"community.title": "Присоединяйтесь к будущему приватности",
|
"community.title": "Присоединяйтесь к будущему приватности",
|
||||||
"community.description": "SecureBit растёт благодаря сообществу. Ваши идеи и отзывы формируют будущее защищённого общения - разработка ведётся открыто, с полной проверкой ASN.1 на всём пути.",
|
"community.description": "SecureBit растёт благодаря сообществу. Ваши идеи и отзывы формируют будущее защищённого общения - разработка ведётся открыто, с полной проверкой ASN.1 на всём пути.",
|
||||||
"community.github": "Репозиторий на GitHub",
|
"community.github": "Репозиторий на GitHub",
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "Системна",
|
"theme.system": "Системна",
|
||||||
"theme.light": "Світла",
|
"theme.light": "Світла",
|
||||||
"theme.dark": "Темна",
|
"theme.dark": "Темна",
|
||||||
|
"donate.eyebrow": "Пожертва в криптовалюті",
|
||||||
|
"donate.title": "Збережімо SecureBit незалежним",
|
||||||
|
"donate.description": "Кожна пожертва йде на розробку та аудити.",
|
||||||
|
"donate.copy": "Копіювати",
|
||||||
|
"donate.copied": "Скопійовано",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Мережа Bitcoin",
|
||||||
|
"donate.net.eth": "Основна мережа Ethereum",
|
||||||
|
"donate.warn.usdt": "Надсилайте лише USDT у мережі Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Надсилайте лише BTC у мережі Bitcoin.",
|
||||||
|
"donate.warn.eth": "Надсилайте лише ETH в основній мережі Ethereum.",
|
||||||
|
"donate.qrAlt": "QR-код адреси для пожертв у {coin}",
|
||||||
"community.title": "Долучайтеся до майбутнього приватності",
|
"community.title": "Долучайтеся до майбутнього приватності",
|
||||||
"community.description": "SecureBit росте завдяки своїй спільноті. Ваші ідеї та відгуки формують майбутнє захищеного спілкування - розробка ведеться відкрито, з повною валідацією ASN.1 на всьому шляху.",
|
"community.description": "SecureBit росте завдяки своїй спільноті. Ваші ідеї та відгуки формують майбутнє захищеного спілкування - розробка ведеться відкрито, з повною валідацією ASN.1 на всьому шляху.",
|
||||||
"community.github": "Репозиторій на GitHub",
|
"community.github": "Репозиторій на GitHub",
|
||||||
|
|||||||
@@ -42,6 +42,18 @@
|
|||||||
"theme.system": "سسٹم",
|
"theme.system": "سسٹم",
|
||||||
"theme.light": "روشن",
|
"theme.light": "روشن",
|
||||||
"theme.dark": "گہرا",
|
"theme.dark": "گہرا",
|
||||||
|
"donate.eyebrow": "کرپٹو میں عطیہ دیں",
|
||||||
|
"donate.title": "SecureBit کو خودمختار رکھیں",
|
||||||
|
"donate.description": "ہر عطیہ ڈیولپمنٹ اور سیکیورٹی آڈٹ پر خرچ ہوتا ہے۔",
|
||||||
|
"donate.copy": "کاپی کریں",
|
||||||
|
"donate.copied": "کاپی ہو گیا",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin نیٹ ورک",
|
||||||
|
"donate.net.eth": "Ethereum مین نیٹ",
|
||||||
|
"donate.warn.usdt": "صرف Tron (TRC-20) پر USDT بھیجیں۔",
|
||||||
|
"donate.warn.btc": "صرف Bitcoin نیٹ ورک پر BTC بھیجیں۔",
|
||||||
|
"donate.warn.eth": "صرف Ethereum مین نیٹ پر ETH بھیجیں۔",
|
||||||
|
"donate.qrAlt": "{coin} عطیہ ایڈریس کا QR کوڈ",
|
||||||
"community.title": "نجیت کے مستقبل میں شامل ہوں",
|
"community.title": "نجیت کے مستقبل میں شامل ہوں",
|
||||||
"community.description": "SecureBit اپنی برادری کی بدولت بڑھتا ہے۔ آپ کے خیالات اور آراء محفوظ رابطے کا مستقبل تشکیل دیتے ہیں - کھلے عام بنایا گیا، سرے سے سرے تک مکمل ASN.1 توثیق کے ساتھ۔",
|
"community.description": "SecureBit اپنی برادری کی بدولت بڑھتا ہے۔ آپ کے خیالات اور آراء محفوظ رابطے کا مستقبل تشکیل دیتے ہیں - کھلے عام بنایا گیا، سرے سے سرے تک مکمل ASN.1 توثیق کے ساتھ۔",
|
||||||
"community.github": "GitHub مخزن",
|
"community.github": "GitHub مخزن",
|
||||||
|
|||||||
@@ -37,6 +37,18 @@
|
|||||||
"theme.system": "跟随系统",
|
"theme.system": "跟随系统",
|
||||||
"theme.light": "浅色",
|
"theme.light": "浅色",
|
||||||
"theme.dark": "深色",
|
"theme.dark": "深色",
|
||||||
|
"donate.eyebrow": "加密货币捐赠",
|
||||||
|
"donate.title": "让 SecureBit 保持独立",
|
||||||
|
"donate.description": "每一笔捐赠都用于开发和安全审计。",
|
||||||
|
"donate.copy": "复制",
|
||||||
|
"donate.copied": "已复制",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "比特币网络",
|
||||||
|
"donate.net.eth": "以太坊主网",
|
||||||
|
"donate.warn.usdt": "仅通过 Tron (TRC-20) 发送 USDT。",
|
||||||
|
"donate.warn.btc": "仅通过比特币网络发送 BTC。",
|
||||||
|
"donate.warn.eth": "仅通过以太坊主网发送 ETH。",
|
||||||
|
"donate.qrAlt": "{coin} 捐赠地址二维码",
|
||||||
"community.title": "一起构筑隐私的未来",
|
"community.title": "一起构筑隐私的未来",
|
||||||
"community.description": "SecureBit 因社区而成长。你的想法与反馈塑造着安全通讯的未来 —— 开放开发,全程完整的 ASN.1 校验。",
|
"community.description": "SecureBit 因社区而成长。你的想法与反馈塑造着安全通讯的未来 —— 开放开发,全程完整的 ASN.1 校验。",
|
||||||
"community.github": "GitHub 仓库",
|
"community.github": "GitHub 仓库",
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"version": "1790219928897",
|
"version": "1790225533889",
|
||||||
"buildVersion": "1790219928897",
|
"buildVersion": "1790225533889",
|
||||||
"appVersion": "6.8.6",
|
"appVersion": "6.8.8",
|
||||||
"buildTime": "2026-09-24T03:18:48.935Z",
|
"buildTime": "2026-09-24T04:52:13.935Z",
|
||||||
"buildId": "1790219928897-02380a7",
|
"buildId": "1790225533889-eac3009",
|
||||||
"gitHash": "02380a7",
|
"gitHash": "eac3009",
|
||||||
"generated": true,
|
"generated": true,
|
||||||
"generatedAt": "2026-09-24T03:18:48.936Z"
|
"generatedAt": "2026-09-24T04:52:13.936Z"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "securebit-chat",
|
"name": "securebit-chat",
|
||||||
"version": "6.8.6",
|
"version": "6.8.8",
|
||||||
"description": "Secure P2P Communication Application with End-to-End Encryption",
|
"description": "Secure P2P Communication Application with End-to-End Encryption",
|
||||||
"main": "index.html",
|
"main": "index.html",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/ru/manifest.json">
|
<link rel="manifest" href="/ru/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/ru.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/ru.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -38,6 +38,14 @@ const DIR = direction();
|
|||||||
// someone who asked for less motion does not want. The jump still happens and
|
// someone who asked for less motion does not want. The jump still happens and
|
||||||
// still lands in the same place — it just stops travelling to get there.
|
// still lands in the same place — it just stops travelling to get there.
|
||||||
const scrollBehavior = () => (prefersReducedMotion() ? 'auto' : 'smooth');
|
const scrollBehavior = () => (prefersReducedMotion() ? 'auto' : 'smooth');
|
||||||
|
|
||||||
|
// After a chat ends, the start page scrolls to the donation card. The flag covers the
|
||||||
|
// usual case where the card only mounts once the chat is gone; the event covers a card
|
||||||
|
// that is already on screen. See src/components/ui/DonateCrypto.jsx.
|
||||||
|
const requestDonateScroll = () => {
|
||||||
|
window.__sbScrollToDonate = Date.now();
|
||||||
|
document.dispatchEvent(new CustomEvent('securebit:scroll-to-donate'));
|
||||||
|
};
|
||||||
import { GroupChatView, GroupSasModal, CreateGroupModal, GroupInviteModal, GroupErrorModal, AddMembersModal } from './components/ui/GroupChat.jsx';
|
import { GroupChatView, GroupSasModal, CreateGroupModal, GroupInviteModal, GroupErrorModal, AddMembersModal } from './components/ui/GroupChat.jsx';
|
||||||
import { GroupCallUI } from './components/ui/GroupCallUI.jsx';
|
import { GroupCallUI } from './components/ui/GroupCallUI.jsx';
|
||||||
import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
|
import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
|
||||||
@@ -1825,6 +1833,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
|
|||||||
const uniqueSection = atIntro && h(UniqueFeatureSlider, { key: 'unique-features-slider' });
|
const uniqueSection = atIntro && h(UniqueFeatureSlider, { key: 'unique-features-slider' });
|
||||||
const partnersSection = atIntro && h(BecomePartner, { key: 'become-partner' });
|
const partnersSection = atIntro && h(BecomePartner, { key: 'become-partner' });
|
||||||
const roadmapSection = atIntro && h(Roadmap, { key: 'roadmap' });
|
const roadmapSection = atIntro && h(Roadmap, { key: 'roadmap' });
|
||||||
|
const donateSection = atIntro && h(DonateCrypto, { key: 'donate-crypto' });
|
||||||
const communitySection = atIntro && h(CommunityCTA, { key: 'community-cta' });
|
const communitySection = atIntro && h(CommunityCTA, { key: 'community-cta' });
|
||||||
|
|
||||||
// Ship the keyframes inside app.js so the animations can never go
|
// Ship the keyframes inside app.js so the animations can never go
|
||||||
@@ -1870,7 +1879,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
|
|||||||
return h('div', { className: 'sb-start', style: { flex: 1, minHeight: 0, width: '100%', display: 'flex', flexDirection: 'column', background: 'var(--sb-bg)', color: 'var(--sb-text-2)' } }, [keyframeStyle, rightPanel, qrModal]);
|
return h('div', { className: 'sb-start', style: { flex: 1, minHeight: 0, width: '100%', display: 'flex', flexDirection: 'column', background: 'var(--sb-bg)', color: 'var(--sb-text-2)' } }, [keyframeStyle, rightPanel, qrModal]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return h('div', { className: 'sb-start', style: { width: '100%' } }, [keyframeStyle, hero, uniqueSection, partnersSection, roadmapSection, communitySection, qrModal]);
|
return h('div', { className: 'sb-start', style: { width: '100%' } }, [keyframeStyle, hero, uniqueSection, partnersSection, roadmapSection, donateSection, communitySection, qrModal]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global scroll function - defined outside components to ensure availability
|
// Global scroll function - defined outside components to ensure availability
|
||||||
@@ -6880,6 +6889,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
|
|||||||
const handleDisconnect = () => {
|
const handleDisconnect = () => {
|
||||||
try {
|
try {
|
||||||
const id = activeIdRef.current;
|
const id = activeIdRef.current;
|
||||||
|
requestDonateScroll();
|
||||||
setSessionTimeLeft(0);
|
setSessionTimeLeft(0);
|
||||||
// Global lifecycle events (consumed by any remaining listeners).
|
// Global lifecycle events (consumed by any remaining listeners).
|
||||||
document.dispatchEvent(new CustomEvent('peer-disconnect'));
|
document.dispatchEvent(new CustomEvent('peer-disconnect'));
|
||||||
@@ -6942,6 +6952,17 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
|
|||||||
|
|
||||||
const isConnectedAndVerified = (connectionStatus === 'connected' || connectionStatus === 'verified' || connectionStatus === 'reconnecting') && isVerified;
|
const isConnectedAndVerified = (connectionStatus === 'connected' || connectionStatus === 'verified' || connectionStatus === 'reconnecting') && isVerified;
|
||||||
|
|
||||||
|
// A chat that ends — the peer leaves, the link drops for good — lands
|
||||||
|
// back on the start page; bring the donation card into view there. Only
|
||||||
|
// for the SAME session going from chatting to not: switching to another
|
||||||
|
// tab's blank session is not an ending.
|
||||||
|
const prevChatRef = React.useRef({ id: activeSessionId, live: isConnectedAndVerified });
|
||||||
|
React.useEffect(() => {
|
||||||
|
const prev = prevChatRef.current;
|
||||||
|
prevChatRef.current = { id: activeSessionId, live: isConnectedAndVerified };
|
||||||
|
if (prev.live && !isConnectedAndVerified && prev.id === activeSessionId) requestDonateScroll();
|
||||||
|
}, [isConnectedAndVerified, activeSessionId]);
|
||||||
|
|
||||||
// The PWA "Install app" pill is a landing-page affordance — hide it once
|
// The PWA "Install app" pill is a landing-page affordance — hide it once
|
||||||
// we're inside the chat (CSS: body.sb-in-chat #pwa-install-button).
|
// we're inside the chat (CSS: body.sb-in-chat #pwa-install-button).
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -0,0 +1,306 @@
|
|||||||
|
import { t, isRTL } from '../../i18n/index.js';
|
||||||
|
|
||||||
|
// "Keep SecureBit independent" — crypto donation card.
|
||||||
|
// Translated from the Claude Design component (Crypto Donate v3.dc.html): coin switcher,
|
||||||
|
// address with copy, and a QR on the right.
|
||||||
|
//
|
||||||
|
// Everything is served from this origin. The design pulled the coin icons from jsDelivr
|
||||||
|
// and built the QR with a CDN script; the site's CSP (`img-src 'self' data:`) would block
|
||||||
|
// the first, and both would hand a third party the address of every visitor. The
|
||||||
|
// addresses are fixed, so the QRs are pre-rendered SVGs in /assets/crypto/.
|
||||||
|
|
||||||
|
const WALLETS = [
|
||||||
|
{ id: 'usdt', tab: 'USDT', coin: 'Tether USDT', badge: 'trx', address: 'TMopDoco5EwJB1QvQYsuhjUJdMvzPXNQYu' },
|
||||||
|
{ id: 'btc', tab: 'BTC', coin: 'Bitcoin', badge: null, address: 'bc1qnmc2xl7p0pxcy6lzf2tyf52wnslgzkgaq9kxpr' },
|
||||||
|
{ id: 'eth', tab: 'ETH', coin: 'Ethereum', badge: null, address: '0xCDfDfaD4EC1fa657C3d2A7EFabc1cb751bf1dAf7' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const icon = (id) => `/assets/crypto/${id}.svg`;
|
||||||
|
|
||||||
|
// A disconnect asks for the page to be brought here (see handleDisconnect in app.jsx).
|
||||||
|
// The request is a timestamped flag rather than just an event, because the landing — and
|
||||||
|
// this card with it — usually mounts only after the chat has been torn down. It expires
|
||||||
|
// so that a request made while no landing was shown cannot fire minutes later.
|
||||||
|
const SCROLL_FLAG = '__sbScrollToDonate';
|
||||||
|
const SCROLL_EVENT = 'securebit:scroll-to-donate';
|
||||||
|
const SCROLL_TTL_MS = 5000;
|
||||||
|
|
||||||
|
const DonateCrypto = () => {
|
||||||
|
const [isMobile, setIsMobile] = React.useState(
|
||||||
|
typeof window !== 'undefined' && window.matchMedia('(max-width:767px)').matches
|
||||||
|
);
|
||||||
|
const [sel, setSel] = React.useState(0);
|
||||||
|
const [copied, setCopied] = React.useState(false);
|
||||||
|
// +1 when the new coin comes in from the end side, -1 from the start side; the QR
|
||||||
|
// slides in accordingly so a swipe feels like moving along the row of tabs.
|
||||||
|
const [slide, setSlide] = React.useState(0);
|
||||||
|
const touchStart = React.useRef(null);
|
||||||
|
const sectionRef = React.useRef(null);
|
||||||
|
const copyTimer = React.useRef(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const mq = window.matchMedia('(max-width:767px)');
|
||||||
|
const onChange = () => setIsMobile(mq.matches);
|
||||||
|
mq.addEventListener ? mq.addEventListener('change', onChange) : mq.addListener(onChange);
|
||||||
|
return () => {
|
||||||
|
mq.removeEventListener ? mq.removeEventListener('change', onChange) : mq.removeListener(onChange);
|
||||||
|
clearTimeout(copyTimer.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
let timer = null;
|
||||||
|
const scrollHere = () => {
|
||||||
|
const at = window[SCROLL_FLAG];
|
||||||
|
window[SCROLL_FLAG] = 0;
|
||||||
|
if (!at || Date.now() - at > SCROLL_TTL_MS) return;
|
||||||
|
clearTimeout(timer);
|
||||||
|
// Let the landing finish laying out first, or the target moves under the scroll.
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
const el = sectionRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
el.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'center' });
|
||||||
|
}, 350);
|
||||||
|
};
|
||||||
|
scrollHere();
|
||||||
|
document.addEventListener(SCROLL_EVENT, scrollHere);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener(SCROLL_EVENT, scrollHere);
|
||||||
|
clearTimeout(timer);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const cur = WALLETS[sel];
|
||||||
|
|
||||||
|
const pick = (i) => {
|
||||||
|
const next = (i + WALLETS.length) % WALLETS.length;
|
||||||
|
if (next === sel) return;
|
||||||
|
setSlide(i > sel ? 1 : -1);
|
||||||
|
setSel(next);
|
||||||
|
setCopied(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Swipe across the QR to change coin on a phone. Horizontal only, and only past a
|
||||||
|
// clear threshold, so a vertical scroll that starts on the QR still scrolls the page
|
||||||
|
// (touch-action: pan-y hands vertical pans to the browser). In a right-to-left locale
|
||||||
|
// the tab row runs the other way, and so does the swipe.
|
||||||
|
const onTouchStart = (e) => {
|
||||||
|
const p = e.touches[0];
|
||||||
|
touchStart.current = { x: p.clientX, y: p.clientY };
|
||||||
|
};
|
||||||
|
const onTouchEnd = (e) => {
|
||||||
|
const start = touchStart.current;
|
||||||
|
touchStart.current = null;
|
||||||
|
if (!start) return;
|
||||||
|
const p = e.changedTouches[0];
|
||||||
|
const dx = p.clientX - start.x;
|
||||||
|
const dy = p.clientY - start.y;
|
||||||
|
if (Math.abs(dx) < 40 || Math.abs(dx) < Math.abs(dy) * 1.5) return;
|
||||||
|
const forward = isRTL() ? dx > 0 : dx < 0;
|
||||||
|
pick(sel + (forward ? 1 : -1));
|
||||||
|
};
|
||||||
|
|
||||||
|
const copy = async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(cur.address);
|
||||||
|
} catch (e) {
|
||||||
|
const ta = document.createElement('textarea');
|
||||||
|
ta.value = cur.address;
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.select();
|
||||||
|
try { document.execCommand('copy'); } catch (_) {}
|
||||||
|
ta.remove();
|
||||||
|
}
|
||||||
|
setCopied(true);
|
||||||
|
clearTimeout(copyTimer.current);
|
||||||
|
copyTimer.current = setTimeout(() => setCopied(false), 1800);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SANS = "'Manrope', system-ui, -apple-system, sans-serif";
|
||||||
|
const MONO = "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace";
|
||||||
|
const FIELD = {
|
||||||
|
borderRadius: '14px', background: 'var(--sb-bg)', border: '1px solid rgba(var(--sb-ink), 0.07)'
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabs = React.createElement('div', {
|
||||||
|
key: 'tabs', role: 'tablist',
|
||||||
|
style: { ...FIELD, display: 'flex', gap: '6px', padding: '5px', marginBottom: '14px' }
|
||||||
|
}, WALLETS.map((w, i) => {
|
||||||
|
const on = i === sel;
|
||||||
|
return React.createElement('button', {
|
||||||
|
key: w.id, type: 'button', role: 'tab', 'aria-selected': on,
|
||||||
|
onClick: () => pick(i),
|
||||||
|
style: {
|
||||||
|
flex: '1 1 0', minWidth: 0, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
gap: '9px', padding: '11px 10px', borderRadius: '10px', border: 'none', cursor: 'pointer',
|
||||||
|
fontFamily: SANS, fontSize: '14px', fontWeight: 700, transition: 'all .18s',
|
||||||
|
background: on ? 'var(--sb-surface-2)' : 'transparent',
|
||||||
|
color: on ? 'var(--sb-text-1)' : 'var(--sb-text-7)',
|
||||||
|
boxShadow: on ? 'inset 0 0 0 1px rgba(var(--sb-orange-rgb), 0.45)' : 'none'
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
React.createElement('span', { key: 'ic', style: { position: 'relative', flex: 'none', width: '22px', height: '22px' } }, [
|
||||||
|
React.createElement('img', { key: 'logo', src: icon(w.id), alt: '', width: 22, height: 22, style: { display: 'block', width: '22px', height: '22px' } }),
|
||||||
|
w.badge && React.createElement('img', {
|
||||||
|
key: 'badge', src: icon(w.badge), alt: '', width: 11, height: 11,
|
||||||
|
style: {
|
||||||
|
position: 'absolute', right: '-3px', bottom: '-3px', width: '11px', height: '11px', borderRadius: '50%',
|
||||||
|
border: '1.5px solid var(--sb-surface-2)', background: 'var(--sb-surface-2)'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
w.tab
|
||||||
|
]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
const copyBtn = React.createElement('button', {
|
||||||
|
key: 'copy', type: 'button', onClick: copy,
|
||||||
|
style: {
|
||||||
|
flex: 'none', display: 'inline-flex', alignItems: 'center', gap: '8px', padding: '12px 16px',
|
||||||
|
borderRadius: '10px', fontFamily: SANS, fontSize: '13.5px', fontWeight: 700, cursor: 'pointer',
|
||||||
|
background: 'rgba(var(--sb-ink), 0.05)', color: 'var(--sb-text-2)',
|
||||||
|
border: '1px solid rgba(var(--sb-ink), 0.1)', transition: 'all .18s'
|
||||||
|
},
|
||||||
|
onMouseEnter: (e) => { e.currentTarget.style.background = 'rgba(var(--sb-ink), 0.09)'; },
|
||||||
|
onMouseLeave: (e) => { e.currentTarget.style.background = 'rgba(var(--sb-ink), 0.05)'; }
|
||||||
|
}, [
|
||||||
|
copied
|
||||||
|
? React.createElement('svg', {
|
||||||
|
key: 'ok', width: 15, height: 15, viewBox: '0 0 24 24', fill: 'none', stroke: 'var(--sb-green-solid)',
|
||||||
|
strokeWidth: 2.4, strokeLinecap: 'round', strokeLinejoin: 'round',
|
||||||
|
dangerouslySetInnerHTML: { __html: '<path d="M5 12.5l4.2 4.2L19 6.5"/>' }
|
||||||
|
})
|
||||||
|
: React.createElement('svg', {
|
||||||
|
key: 'cp', width: 15, height: 15, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor',
|
||||||
|
strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',
|
||||||
|
dangerouslySetInnerHTML: { __html: '<rect x="9" y="9" width="12" height="12" rx="2.5"/><path d="M5 15V5a2 2 0 0 1 2-2h8"/>' }
|
||||||
|
}),
|
||||||
|
React.createElement('span', { key: 'l', 'aria-live': 'polite' }, copied ? t('donate.copied') : t('donate.copy'))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const addressField = React.createElement('div', {
|
||||||
|
key: 'addr',
|
||||||
|
style: { ...FIELD, display: 'flex', alignItems: 'center', gap: '12px', padding: '8px 8px 8px 18px' }
|
||||||
|
}, [
|
||||||
|
React.createElement('div', { key: 'tx', style: { flex: 1, minWidth: 0 } }, [
|
||||||
|
React.createElement('div', { key: 'net', style: { fontSize: '11.5px', fontWeight: 600, color: 'var(--sb-text-9)', marginBottom: '2px' } }, t(`donate.net.${cur.id}`)),
|
||||||
|
React.createElement('div', {
|
||||||
|
key: 'a', title: cur.address, dir: 'ltr',
|
||||||
|
style: { fontFamily: MONO, fontSize: '14px', color: 'var(--sb-text-2)', letterSpacing: '0.3px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }
|
||||||
|
}, cur.address.slice(0, 8) + '…' + cur.address.slice(-8))
|
||||||
|
]),
|
||||||
|
copyBtn
|
||||||
|
]);
|
||||||
|
|
||||||
|
const warn = React.createElement('div', {
|
||||||
|
key: 'warn',
|
||||||
|
style: { display: 'flex', alignItems: 'center', gap: '7px', marginTop: '14px', fontSize: '12px', color: 'var(--sb-text-9)' }
|
||||||
|
}, [
|
||||||
|
React.createElement('svg', {
|
||||||
|
key: 'i', width: 13, height: 13, viewBox: '0 0 24 24', fill: 'none', stroke: 'var(--sb-orange-solid)',
|
||||||
|
strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', style: { flex: 'none' },
|
||||||
|
dangerouslySetInnerHTML: { __html: '<path d="M12 3l9.5 17h-19z"/><path d="M12 10v4M12 17.5v.01"/>' }
|
||||||
|
}),
|
||||||
|
t(`donate.warn.${cur.id}`)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const left = React.createElement('div', {
|
||||||
|
key: 'left',
|
||||||
|
style: { flex: '1 1 440px', minWidth: 0, padding: isMobile ? '32px 22px 8px' : '44px', display: 'flex', flexDirection: 'column' }
|
||||||
|
}, [
|
||||||
|
React.createElement('div', {
|
||||||
|
key: 'eyebrow',
|
||||||
|
style: { fontFamily: MONO, fontSize: '11px', fontWeight: 600, color: 'var(--sb-text-9)', textTransform: 'uppercase', letterSpacing: '1.8px', marginBottom: '14px' }
|
||||||
|
}, t('donate.eyebrow')),
|
||||||
|
React.createElement('h2', {
|
||||||
|
key: 'title',
|
||||||
|
style: { margin: '0 0 10px', fontSize: isMobile ? '28px' : '34px', fontWeight: 800, letterSpacing: '-1px', lineHeight: 1.08, color: 'var(--sb-text-1)' }
|
||||||
|
}, t('donate.title')),
|
||||||
|
React.createElement('p', {
|
||||||
|
key: 'desc',
|
||||||
|
style: { margin: '0 0 30px', fontSize: '15px', lineHeight: 1.6, color: 'var(--sb-text-7)' }
|
||||||
|
}, t('donate.description')),
|
||||||
|
tabs,
|
||||||
|
addressField,
|
||||||
|
warn
|
||||||
|
]);
|
||||||
|
|
||||||
|
// The QR plate is white in both themes: scanners want dark modules on a light ground.
|
||||||
|
const dirSign = isRTL() ? -1 : 1;
|
||||||
|
const slideAnim = slide === 0 ? 'none'
|
||||||
|
: `${slide * dirSign > 0 ? 'cdInEnd' : 'cdInStart'} .28s cubic-bezier(.2,.7,.3,1)`;
|
||||||
|
|
||||||
|
const plate = React.createElement('div', {
|
||||||
|
key: 'plate',
|
||||||
|
onTouchStart, onTouchEnd,
|
||||||
|
style: {
|
||||||
|
position: 'relative', width: '232px', height: '232px', padding: '14px', borderRadius: '20px', background: '#fff',
|
||||||
|
boxShadow: '0 24px 60px rgba(var(--sb-shadow-rgb), calc(0.45 * var(--sb-shadow-k)))',
|
||||||
|
border: '1px solid rgba(var(--sb-ink), 0.07)', overflow: 'hidden', touchAction: 'pan-y', userSelect: 'none'
|
||||||
|
}
|
||||||
|
}, React.createElement('div', {
|
||||||
|
key: cur.id,
|
||||||
|
style: { position: 'relative', width: '100%', height: '100%', animation: slideAnim }
|
||||||
|
}, [
|
||||||
|
React.createElement('img', {
|
||||||
|
key: 'qr', src: `/assets/crypto/qr-${cur.id}.svg`, alt: t('donate.qrAlt', { coin: cur.coin }),
|
||||||
|
width: 204, height: 204, draggable: false,
|
||||||
|
style: { display: 'block', width: '100%', height: '100%', imageRendering: 'pixelated' }
|
||||||
|
}),
|
||||||
|
React.createElement('div', {
|
||||||
|
key: 'logo',
|
||||||
|
style: { position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%,-50%)', width: '48px', height: '48px', borderRadius: '12px', background: '#fff', display: 'grid', placeItems: 'center' }
|
||||||
|
}, React.createElement('img', { src: icon(cur.id), alt: '', width: 34, height: 34, draggable: false, style: { display: 'block', width: '34px', height: '34px' } }))
|
||||||
|
]));
|
||||||
|
|
||||||
|
// Page dots under the QR on a phone — the hint that it can be swiped.
|
||||||
|
const dots = isMobile && React.createElement('div', {
|
||||||
|
key: 'dots', 'aria-hidden': true,
|
||||||
|
style: { display: 'flex', justifyContent: 'center', gap: '7px', marginTop: '18px' }
|
||||||
|
}, WALLETS.map((w, i) => React.createElement('span', {
|
||||||
|
key: w.id,
|
||||||
|
style: {
|
||||||
|
width: i === sel ? '18px' : '7px', height: '7px', borderRadius: '4px', transition: 'all .25s',
|
||||||
|
background: i === sel ? 'var(--sb-orange-solid)' : 'rgba(var(--sb-ink), 0.16)'
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
|
||||||
|
const right = React.createElement('div', {
|
||||||
|
key: 'right',
|
||||||
|
style: { flex: '0 0 auto', minWidth: isMobile ? 0 : '300px', margin: '0 auto', padding: isMobile ? '24px 22px 32px' : '44px', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }
|
||||||
|
}, [plate, dots]);
|
||||||
|
|
||||||
|
const card = React.createElement('div', {
|
||||||
|
key: 'card',
|
||||||
|
style: {
|
||||||
|
width: '100%', maxWidth: '1000px', position: 'relative', overflow: 'hidden', borderRadius: '24px',
|
||||||
|
background: 'radial-gradient(520px 420px at 100% 50%, rgba(var(--sb-orange-rgb), 0.09), transparent 70%), var(--sb-surface)',
|
||||||
|
border: '1px solid rgba(var(--sb-ink), 0.07)', display: 'flex', flexWrap: 'wrap', alignItems: 'stretch',
|
||||||
|
animation: 'cdUp .45s cubic-bezier(.2,.7,.3,1)'
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
left,
|
||||||
|
right
|
||||||
|
]);
|
||||||
|
|
||||||
|
return React.createElement('section', {
|
||||||
|
id: 'donate', ref: sectionRef,
|
||||||
|
style: {
|
||||||
|
width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
background: 'var(--sb-bg)', color: 'var(--sb-text-2)', fontFamily: SANS,
|
||||||
|
padding: isMobile ? '48px 18px' : '64px 32px'
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
React.createElement('style', { key: 'kf', dangerouslySetInnerHTML: { __html: '@keyframes cdUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}' +
|
||||||
|
'@keyframes cdInEnd{from{opacity:0;transform:translateX(36px)}to{opacity:1;transform:none}}' +
|
||||||
|
'@keyframes cdInStart{from{opacity:0;transform:translateX(-36px)}to{opacity:1;transform:none}}' +
|
||||||
|
'@media (prefers-reduced-motion: reduce){#donate [style*="cdIn"]{animation:none!important}}' } }),
|
||||||
|
card
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
DonateCrypto.SCROLL_FLAG = SCROLL_FLAG;
|
||||||
|
DonateCrypto.SCROLL_EVENT = SCROLL_EVENT;
|
||||||
|
|
||||||
|
window.DonateCrypto = DonateCrypto;
|
||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "النظام",
|
"theme.system": "النظام",
|
||||||
"theme.light": "فاتح",
|
"theme.light": "فاتح",
|
||||||
"theme.dark": "داكن",
|
"theme.dark": "داكن",
|
||||||
|
"donate.eyebrow": "تبرّع بالعملات المشفّرة",
|
||||||
|
"donate.title": "ساعدنا في إبقاء SecureBit مستقلًا",
|
||||||
|
"donate.description": "يذهب كل تبرّع إلى التطوير وعمليات التدقيق الأمني.",
|
||||||
|
"donate.copy": "نسخ",
|
||||||
|
"donate.copied": "تم النسخ",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "شبكة Bitcoin",
|
||||||
|
"donate.net.eth": "الشبكة الرئيسية لـ Ethereum",
|
||||||
|
"donate.warn.usdt": "أرسل USDT على شبكة Tron (TRC-20) فقط.",
|
||||||
|
"donate.warn.btc": "أرسل BTC على شبكة Bitcoin فقط.",
|
||||||
|
"donate.warn.eth": "أرسل ETH على الشبكة الرئيسية لـ Ethereum فقط.",
|
||||||
|
"donate.qrAlt": "رمز QR لعنوان التبرّع بـ {coin}",
|
||||||
"community.title": "انضمّ إلى مستقبل الخصوصية",
|
"community.title": "انضمّ إلى مستقبل الخصوصية",
|
||||||
"community.description": "ينمو SecureBit بفضل مجتمعه. أفكاركم وملاحظاتكم هي ما يصوغ مستقبل التواصل الآمن - مبنيّ في العلن، مع تحقّق ASN.1 كامل من طرف إلى طرف.",
|
"community.description": "ينمو SecureBit بفضل مجتمعه. أفكاركم وملاحظاتكم هي ما يصوغ مستقبل التواصل الآمن - مبنيّ في العلن، مع تحقّق ASN.1 كامل من طرف إلى طرف.",
|
||||||
"community.github": "مستودع GitHub",
|
"community.github": "مستودع GitHub",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "System",
|
"theme.system": "System",
|
||||||
"theme.light": "Hell",
|
"theme.light": "Hell",
|
||||||
"theme.dark": "Dunkel",
|
"theme.dark": "Dunkel",
|
||||||
|
"donate.eyebrow": "Mit Krypto spenden",
|
||||||
|
"donate.title": "Halte SecureBit unabhängig",
|
||||||
|
"donate.description": "Jede Spende fließt in Entwicklung und Audits.",
|
||||||
|
"donate.copy": "Kopieren",
|
||||||
|
"donate.copied": "Kopiert",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin-Netzwerk",
|
||||||
|
"donate.net.eth": "Ethereum-Mainnet",
|
||||||
|
"donate.warn.usdt": "Sende nur USDT über Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Sende nur BTC über das Bitcoin-Netzwerk.",
|
||||||
|
"donate.warn.eth": "Sende nur ETH über das Ethereum-Mainnet.",
|
||||||
|
"donate.qrAlt": "QR-Code der {coin}-Spendenadresse",
|
||||||
"community.title": "Gestalten Sie die Zukunft der Privatsphäre mit",
|
"community.title": "Gestalten Sie die Zukunft der Privatsphäre mit",
|
||||||
"community.description": "SecureBit wächst durch seine Community. Ihre Ideen und Ihr Feedback prägen die Zukunft sicherer Kommunikation - offen entwickelt, mit vollständiger ASN.1-Validierung von Ende zu Ende.",
|
"community.description": "SecureBit wächst durch seine Community. Ihre Ideen und Ihr Feedback prägen die Zukunft sicherer Kommunikation - offen entwickelt, mit vollständiger ASN.1-Validierung von Ende zu Ende.",
|
||||||
"community.github": "GitHub-Repository",
|
"community.github": "GitHub-Repository",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "System",
|
"theme.system": "System",
|
||||||
"theme.light": "Light",
|
"theme.light": "Light",
|
||||||
"theme.dark": "Dark",
|
"theme.dark": "Dark",
|
||||||
|
"donate.eyebrow": "Donate in crypto",
|
||||||
|
"donate.title": "Keep SecureBit independent",
|
||||||
|
"donate.description": "Every donation goes to development and audits.",
|
||||||
|
"donate.copy": "Copy",
|
||||||
|
"donate.copied": "Copied",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin network",
|
||||||
|
"donate.net.eth": "Ethereum mainnet",
|
||||||
|
"donate.warn.usdt": "Send only USDT on Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Send only BTC on the Bitcoin network.",
|
||||||
|
"donate.warn.eth": "Send only ETH on Ethereum mainnet.",
|
||||||
|
"donate.qrAlt": "QR code of the {coin} donation address",
|
||||||
"community.title": "Join the future of privacy",
|
"community.title": "Join the future of privacy",
|
||||||
"community.description": "SecureBit grows thanks to its community. Your ideas and feedback shape the future of secure communication - built in the open, with complete ASN.1 validation end-to-end.",
|
"community.description": "SecureBit grows thanks to its community. Your ideas and feedback shape the future of secure communication - built in the open, with complete ASN.1 validation end-to-end.",
|
||||||
"community.github": "GitHub Repository",
|
"community.github": "GitHub Repository",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "Sistema",
|
"theme.system": "Sistema",
|
||||||
"theme.light": "Claro",
|
"theme.light": "Claro",
|
||||||
"theme.dark": "Oscuro",
|
"theme.dark": "Oscuro",
|
||||||
|
"donate.eyebrow": "Dona en cripto",
|
||||||
|
"donate.title": "Mantén SecureBit independiente",
|
||||||
|
"donate.description": "Cada donación se destina al desarrollo y a las auditorías.",
|
||||||
|
"donate.copy": "Copiar",
|
||||||
|
"donate.copied": "Copiado",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Red Bitcoin",
|
||||||
|
"donate.net.eth": "Red principal de Ethereum",
|
||||||
|
"donate.warn.usdt": "Envía solo USDT en Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Envía solo BTC en la red Bitcoin.",
|
||||||
|
"donate.warn.eth": "Envía solo ETH en la red principal de Ethereum.",
|
||||||
|
"donate.qrAlt": "Código QR de la dirección de donación en {coin}",
|
||||||
"community.title": "Construye el futuro de la privacidad",
|
"community.title": "Construye el futuro de la privacidad",
|
||||||
"community.description": "SecureBit crece gracias a su comunidad. Tus ideas y comentarios dan forma al futuro de la comunicación segura, desarrollada de forma abierta y con validación ASN.1 completa de extremo a extremo.",
|
"community.description": "SecureBit crece gracias a su comunidad. Tus ideas y comentarios dan forma al futuro de la comunicación segura, desarrollada de forma abierta y con validación ASN.1 completa de extremo a extremo.",
|
||||||
"community.github": "Repositorio en GitHub",
|
"community.github": "Repositorio en GitHub",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "سیستم",
|
"theme.system": "سیستم",
|
||||||
"theme.light": "روشن",
|
"theme.light": "روشن",
|
||||||
"theme.dark": "تیره",
|
"theme.dark": "تیره",
|
||||||
|
"donate.eyebrow": "اهدای کمک با رمزارز",
|
||||||
|
"donate.title": "به مستقل ماندن SecureBit کمک کنید",
|
||||||
|
"donate.description": "هر کمک صرف توسعه و ممیزیهای امنیتی میشود.",
|
||||||
|
"donate.copy": "کپی",
|
||||||
|
"donate.copied": "کپی شد",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "شبکه Bitcoin",
|
||||||
|
"donate.net.eth": "شبکه اصلی Ethereum",
|
||||||
|
"donate.warn.usdt": "فقط USDT را روی شبکه Tron (TRC-20) بفرستید.",
|
||||||
|
"donate.warn.btc": "فقط BTC را روی شبکه Bitcoin بفرستید.",
|
||||||
|
"donate.warn.eth": "فقط ETH را روی شبکه اصلی Ethereum بفرستید.",
|
||||||
|
"donate.qrAlt": "کد QR نشانی کمک با {coin}",
|
||||||
"community.title": "به آیندهٔ حریم خصوصی بپیوندید",
|
"community.title": "به آیندهٔ حریم خصوصی بپیوندید",
|
||||||
"community.description": "SecureBit بهلطف جامعهاش رشد میکند. ایدهها و بازخوردهای شما آیندهٔ ارتباط امن را شکل میدهد - ساختهشده در فضای باز، با اعتبارسنجی کامل ASN.1 از یک سر تا سر دیگر.",
|
"community.description": "SecureBit بهلطف جامعهاش رشد میکند. ایدهها و بازخوردهای شما آیندهٔ ارتباط امن را شکل میدهد - ساختهشده در فضای باز، با اعتبارسنجی کامل ASN.1 از یک سر تا سر دیگر.",
|
||||||
"community.github": "مخزن GitHub",
|
"community.github": "مخزن GitHub",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "Système",
|
"theme.system": "Système",
|
||||||
"theme.light": "Clair",
|
"theme.light": "Clair",
|
||||||
"theme.dark": "Sombre",
|
"theme.dark": "Sombre",
|
||||||
|
"donate.eyebrow": "Faire un don en crypto",
|
||||||
|
"donate.title": "Gardons SecureBit indépendant",
|
||||||
|
"donate.description": "Chaque don finance le développement et les audits.",
|
||||||
|
"donate.copy": "Copier",
|
||||||
|
"donate.copied": "Copié",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Réseau Bitcoin",
|
||||||
|
"donate.net.eth": "Réseau principal Ethereum",
|
||||||
|
"donate.warn.usdt": "Envoyez uniquement de l'USDT sur Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Envoyez uniquement du BTC sur le réseau Bitcoin.",
|
||||||
|
"donate.warn.eth": "Envoyez uniquement de l'ETH sur le réseau principal Ethereum.",
|
||||||
|
"donate.qrAlt": "QR code de l'adresse de don {coin}",
|
||||||
"community.title": "Construisez l'avenir de la vie privée",
|
"community.title": "Construisez l'avenir de la vie privée",
|
||||||
"community.description": "SecureBit grandit grâce à sa communauté. Vos idées et vos retours façonnent l'avenir des communications sécurisées, développées à ciel ouvert, avec une validation ASN.1 complète de bout en bout.",
|
"community.description": "SecureBit grandit grâce à sa communauté. Vos idées et vos retours façonnent l'avenir des communications sécurisées, développées à ciel ouvert, avec une validation ASN.1 complète de bout en bout.",
|
||||||
"community.github": "Dépôt GitHub",
|
"community.github": "Dépôt GitHub",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "מערכת",
|
"theme.system": "מערכת",
|
||||||
"theme.light": "בהיר",
|
"theme.light": "בהיר",
|
||||||
"theme.dark": "כהה",
|
"theme.dark": "כהה",
|
||||||
|
"donate.eyebrow": "תרומה בקריפטו",
|
||||||
|
"donate.title": "עזרו לשמור על SecureBit עצמאית",
|
||||||
|
"donate.description": "כל תרומה מופנית לפיתוח ולביקורות אבטחה.",
|
||||||
|
"donate.copy": "העתקה",
|
||||||
|
"donate.copied": "הועתק",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "רשת Bitcoin",
|
||||||
|
"donate.net.eth": "הרשת הראשית של Ethereum",
|
||||||
|
"donate.warn.usdt": "שלחו USDT ברשת Tron (TRC-20) בלבד.",
|
||||||
|
"donate.warn.btc": "שלחו BTC ברשת Bitcoin בלבד.",
|
||||||
|
"donate.warn.eth": "שלחו ETH ברשת הראשית של Ethereum בלבד.",
|
||||||
|
"donate.qrAlt": "קוד QR של כתובת התרומה ב-{coin}",
|
||||||
"community.title": "הצטרפו לעתיד של הפרטיות",
|
"community.title": "הצטרפו לעתיד של הפרטיות",
|
||||||
"community.description": "SecureBit גדל בזכות הקהילה שלו. הרעיונות והמשוב שלכם מעצבים את עתיד התקשורת המאובטחת - נבנה בגלוי, עם אימות ASN.1 מלא מקצה לקצה.",
|
"community.description": "SecureBit גדל בזכות הקהילה שלו. הרעיונות והמשוב שלכם מעצבים את עתיד התקשורת המאובטחת - נבנה בגלוי, עם אימות ASN.1 מלא מקצה לקצה.",
|
||||||
"community.github": "מאגר GitHub",
|
"community.github": "מאגר GitHub",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "सिस्टम",
|
"theme.system": "सिस्टम",
|
||||||
"theme.light": "हल्की",
|
"theme.light": "हल्की",
|
||||||
"theme.dark": "गहरी",
|
"theme.dark": "गहरी",
|
||||||
|
"donate.eyebrow": "क्रिप्टो में दान करें",
|
||||||
|
"donate.title": "SecureBit को स्वतंत्र बनाए रखें",
|
||||||
|
"donate.description": "हर दान विकास और सुरक्षा ऑडिट पर खर्च होता है।",
|
||||||
|
"donate.copy": "कॉपी करें",
|
||||||
|
"donate.copied": "कॉपी हो गया",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin नेटवर्क",
|
||||||
|
"donate.net.eth": "Ethereum मेननेट",
|
||||||
|
"donate.warn.usdt": "केवल Tron (TRC-20) पर USDT भेजें।",
|
||||||
|
"donate.warn.btc": "केवल Bitcoin नेटवर्क पर BTC भेजें।",
|
||||||
|
"donate.warn.eth": "केवल Ethereum मेननेट पर ETH भेजें।",
|
||||||
|
"donate.qrAlt": "{coin} दान पते का QR कोड",
|
||||||
"community.title": "निजता के भविष्य में शामिल हों",
|
"community.title": "निजता के भविष्य में शामिल हों",
|
||||||
"community.description": "SecureBit अपने समुदाय से बढ़ता है। आपके विचार और सुझाव सुरक्षित संवाद का भविष्य गढ़ते हैं - खुले में बना, और पूरे रास्ते पूरी ASN.1 जाँच के साथ।",
|
"community.description": "SecureBit अपने समुदाय से बढ़ता है। आपके विचार और सुझाव सुरक्षित संवाद का भविष्य गढ़ते हैं - खुले में बना, और पूरे रास्ते पूरी ASN.1 जाँच के साथ।",
|
||||||
"community.github": "GitHub रिपॉज़िटरी",
|
"community.github": "GitHub रिपॉज़िटरी",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "시스템",
|
"theme.system": "시스템",
|
||||||
"theme.light": "밝게",
|
"theme.light": "밝게",
|
||||||
"theme.dark": "어둡게",
|
"theme.dark": "어둡게",
|
||||||
|
"donate.eyebrow": "암호화폐로 후원하기",
|
||||||
|
"donate.title": "SecureBit의 독립을 지켜 주세요",
|
||||||
|
"donate.description": "모든 후원금은 개발과 보안 감사에 쓰입니다.",
|
||||||
|
"donate.copy": "복사",
|
||||||
|
"donate.copied": "복사됨",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "비트코인 네트워크",
|
||||||
|
"donate.net.eth": "이더리움 메인넷",
|
||||||
|
"donate.warn.usdt": "USDT는 Tron(TRC-20) 네트워크로만 보내세요.",
|
||||||
|
"donate.warn.btc": "BTC는 비트코인 네트워크로만 보내세요.",
|
||||||
|
"donate.warn.eth": "ETH는 이더리움 메인넷으로만 보내세요.",
|
||||||
|
"donate.qrAlt": "{coin} 후원 주소 QR 코드",
|
||||||
"community.title": "프라이버시의 미래를 함께 만듭니다",
|
"community.title": "프라이버시의 미래를 함께 만듭니다",
|
||||||
"community.description": "SecureBit은 커뮤니티 덕분에 자랍니다. 여러분의 아이디어와 의견이 안전한 소통의 미래를 만듭니다 - 모든 과정이 공개되어 있고, 전 구간에서 ASN.1 검증을 수행합니다.",
|
"community.description": "SecureBit은 커뮤니티 덕분에 자랍니다. 여러분의 아이디어와 의견이 안전한 소통의 미래를 만듭니다 - 모든 과정이 공개되어 있고, 전 구간에서 ASN.1 검증을 수행합니다.",
|
||||||
"community.github": "GitHub 저장소",
|
"community.github": "GitHub 저장소",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "Системная",
|
"theme.system": "Системная",
|
||||||
"theme.light": "Светлая",
|
"theme.light": "Светлая",
|
||||||
"theme.dark": "Тёмная",
|
"theme.dark": "Тёмная",
|
||||||
|
"donate.eyebrow": "Пожертвование в криптовалюте",
|
||||||
|
"donate.title": "Сохраним SecureBit независимым",
|
||||||
|
"donate.description": "Каждое пожертвование идёт на разработку и аудиты.",
|
||||||
|
"donate.copy": "Копировать",
|
||||||
|
"donate.copied": "Скопировано",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Сеть Bitcoin",
|
||||||
|
"donate.net.eth": "Основная сеть Ethereum",
|
||||||
|
"donate.warn.usdt": "Отправляйте только USDT в сети Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Отправляйте только BTC в сети Bitcoin.",
|
||||||
|
"donate.warn.eth": "Отправляйте только ETH в основной сети Ethereum.",
|
||||||
|
"donate.qrAlt": "QR-код адреса для пожертвований в {coin}",
|
||||||
"community.title": "Присоединяйтесь к будущему приватности",
|
"community.title": "Присоединяйтесь к будущему приватности",
|
||||||
"community.description": "SecureBit растёт благодаря сообществу. Ваши идеи и отзывы формируют будущее защищённого общения - разработка ведётся открыто, с полной проверкой ASN.1 на всём пути.",
|
"community.description": "SecureBit растёт благодаря сообществу. Ваши идеи и отзывы формируют будущее защищённого общения - разработка ведётся открыто, с полной проверкой ASN.1 на всём пути.",
|
||||||
"community.github": "Репозиторий на GitHub",
|
"community.github": "Репозиторий на GitHub",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "Системна",
|
"theme.system": "Системна",
|
||||||
"theme.light": "Світла",
|
"theme.light": "Світла",
|
||||||
"theme.dark": "Темна",
|
"theme.dark": "Темна",
|
||||||
|
"donate.eyebrow": "Пожертва в криптовалюті",
|
||||||
|
"donate.title": "Збережімо SecureBit незалежним",
|
||||||
|
"donate.description": "Кожна пожертва йде на розробку та аудити.",
|
||||||
|
"donate.copy": "Копіювати",
|
||||||
|
"donate.copied": "Скопійовано",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Мережа Bitcoin",
|
||||||
|
"donate.net.eth": "Основна мережа Ethereum",
|
||||||
|
"donate.warn.usdt": "Надсилайте лише USDT у мережі Tron (TRC-20).",
|
||||||
|
"donate.warn.btc": "Надсилайте лише BTC у мережі Bitcoin.",
|
||||||
|
"donate.warn.eth": "Надсилайте лише ETH в основній мережі Ethereum.",
|
||||||
|
"donate.qrAlt": "QR-код адреси для пожертв у {coin}",
|
||||||
"community.title": "Долучайтеся до майбутнього приватності",
|
"community.title": "Долучайтеся до майбутнього приватності",
|
||||||
"community.description": "SecureBit росте завдяки своїй спільноті. Ваші ідеї та відгуки формують майбутнє захищеного спілкування - розробка ведеться відкрито, з повною валідацією ASN.1 на всьому шляху.",
|
"community.description": "SecureBit росте завдяки своїй спільноті. Ваші ідеї та відгуки формують майбутнє захищеного спілкування - розробка ведеться відкрито, з повною валідацією ASN.1 на всьому шляху.",
|
||||||
"community.github": "Репозиторій на GitHub",
|
"community.github": "Репозиторій на GitHub",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "سسٹم",
|
"theme.system": "سسٹم",
|
||||||
"theme.light": "روشن",
|
"theme.light": "روشن",
|
||||||
"theme.dark": "گہرا",
|
"theme.dark": "گہرا",
|
||||||
|
"donate.eyebrow": "کرپٹو میں عطیہ دیں",
|
||||||
|
"donate.title": "SecureBit کو خودمختار رکھیں",
|
||||||
|
"donate.description": "ہر عطیہ ڈیولپمنٹ اور سیکیورٹی آڈٹ پر خرچ ہوتا ہے۔",
|
||||||
|
"donate.copy": "کاپی کریں",
|
||||||
|
"donate.copied": "کاپی ہو گیا",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "Bitcoin نیٹ ورک",
|
||||||
|
"donate.net.eth": "Ethereum مین نیٹ",
|
||||||
|
"donate.warn.usdt": "صرف Tron (TRC-20) پر USDT بھیجیں۔",
|
||||||
|
"donate.warn.btc": "صرف Bitcoin نیٹ ورک پر BTC بھیجیں۔",
|
||||||
|
"donate.warn.eth": "صرف Ethereum مین نیٹ پر ETH بھیجیں۔",
|
||||||
|
"donate.qrAlt": "{coin} عطیہ ایڈریس کا QR کوڈ",
|
||||||
"community.title": "نجیت کے مستقبل میں شامل ہوں",
|
"community.title": "نجیت کے مستقبل میں شامل ہوں",
|
||||||
"community.description": "SecureBit اپنی برادری کی بدولت بڑھتا ہے۔ آپ کے خیالات اور آراء محفوظ رابطے کا مستقبل تشکیل دیتے ہیں - کھلے عام بنایا گیا، سرے سے سرے تک مکمل ASN.1 توثیق کے ساتھ۔",
|
"community.description": "SecureBit اپنی برادری کی بدولت بڑھتا ہے۔ آپ کے خیالات اور آراء محفوظ رابطے کا مستقبل تشکیل دیتے ہیں - کھلے عام بنایا گیا، سرے سے سرے تک مکمل ASN.1 توثیق کے ساتھ۔",
|
||||||
"community.github": "GitHub مخزن",
|
"community.github": "GitHub مخزن",
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ export const DICTIONARY = {
|
|||||||
"theme.system": "跟随系统",
|
"theme.system": "跟随系统",
|
||||||
"theme.light": "浅色",
|
"theme.light": "浅色",
|
||||||
"theme.dark": "深色",
|
"theme.dark": "深色",
|
||||||
|
"donate.eyebrow": "加密货币捐赠",
|
||||||
|
"donate.title": "让 SecureBit 保持独立",
|
||||||
|
"donate.description": "每一笔捐赠都用于开发和安全审计。",
|
||||||
|
"donate.copy": "复制",
|
||||||
|
"donate.copied": "已复制",
|
||||||
|
"donate.net.usdt": "USDT · Tron (TRC-20)",
|
||||||
|
"donate.net.btc": "比特币网络",
|
||||||
|
"donate.net.eth": "以太坊主网",
|
||||||
|
"donate.warn.usdt": "仅通过 Tron (TRC-20) 发送 USDT。",
|
||||||
|
"donate.warn.btc": "仅通过比特币网络发送 BTC。",
|
||||||
|
"donate.warn.eth": "仅通过以太坊主网发送 ETH。",
|
||||||
|
"donate.qrAlt": "{coin} 捐赠地址二维码",
|
||||||
"community.title": "一起构筑隐私的未来",
|
"community.title": "一起构筑隐私的未来",
|
||||||
"community.description": "SecureBit 因社区而成长。你的想法与反馈塑造着安全通讯的未来 —— 开放开发,全程完整的 ASN.1 校验。",
|
"community.description": "SecureBit 因社区而成长。你的想法与反馈塑造着安全通讯的未来 —— 开放开发,全程完整的 ASN.1 校验。",
|
||||||
"community.github": "GitHub 仓库",
|
"community.github": "GitHub 仓库",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import '../components/ui/DownloadApps.jsx';
|
|||||||
import '../components/ui/BecomePartner.jsx';
|
import '../components/ui/BecomePartner.jsx';
|
||||||
import '../components/ui/UniqueFeatureSlider.jsx';
|
import '../components/ui/UniqueFeatureSlider.jsx';
|
||||||
import '../components/ui/Roadmap.jsx';
|
import '../components/ui/Roadmap.jsx';
|
||||||
|
import '../components/ui/DonateCrypto.jsx';
|
||||||
import '../components/ui/CommunityCTA.jsx';
|
import '../components/ui/CommunityCTA.jsx';
|
||||||
import '../components/ui/FileTransfer.jsx';
|
import '../components/ui/FileTransfer.jsx';
|
||||||
import '../components/ui/IceServerSettings.jsx';
|
import '../components/ui/IceServerSettings.jsx';
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ let DYNAMIC_CACHE = 'securebit-pwa-dynamic-v4.7.56';
|
|||||||
// Build stamp — rewritten by scripts/post-build.js on every release so this file's
|
// 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,
|
// 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.
|
// reinstall it, drop stale caches and (via controllerchange) prompt the page to update.
|
||||||
const SW_BUILD_VERSION = '1790219928897';
|
const SW_BUILD_VERSION = '1790225533889';
|
||||||
|
|
||||||
// Locale subdirectories, rewritten by scripts/build-i18n.js. Each localized page is a
|
// Locale subdirectories, rewritten by scripts/build-i18n.js. Each localized page is a
|
||||||
// separate document at its own URL, so the shell has to be cached and served per
|
// separate document at its own URL, so the shell has to be cached and served per
|
||||||
|
|||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/uk/manifest.json">
|
<link rel="manifest" href="/uk/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/uk.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/uk.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/ur/manifest.json">
|
<link rel="manifest" href="/ur/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/ur.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/ur.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -30,18 +30,18 @@
|
|||||||
|
|
||||||
<!-- PWA Manifest -->
|
<!-- PWA Manifest -->
|
||||||
<link rel="manifest" href="/zh/manifest.json">
|
<link rel="manifest" href="/zh/manifest.json">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
|
|
||||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||||
from localStorage while the parser is still in <head>, so the page paints in the
|
from localStorage while the parser is still in <head>, so the page paints in the
|
||||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||||
<script src="/src/scripts/theme-boot.js?v=1790219928897"></script>
|
<script src="/src/scripts/theme-boot.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||||
not replayed, so the listener has to exist before Chrome decides the page
|
not replayed, so the listener has to exist before Chrome decides the page
|
||||||
is installable. Deferred/module scripts are already too late. -->
|
is installable. Deferred/module scripts are already too late. -->
|
||||||
<script src="/src/scripts/pwa-install-capture.js?v=1790219928897"></script>
|
<script src="/src/scripts/pwa-install-capture.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<!-- PWA Meta Tags -->
|
<!-- PWA Meta Tags -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||||
|
|
||||||
<!-- Apple Touch Icons -->
|
<!-- Apple Touch Icons -->
|
||||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790219928897">
|
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790225533889">
|
||||||
|
|
||||||
<!-- Microsoft Tiles -->
|
<!-- Microsoft Tiles -->
|
||||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||||
both execute in document order after parsing, so React still runs before the
|
both execute in document order after parsing, so React still runs before the
|
||||||
app modules below, but the parser / first paint is no longer blocked. -->
|
app modules below, but the parser / first paint is no longer blocked. -->
|
||||||
<script defer src="/config/ice-servers.js?v=1790219928897"></script>
|
<script defer src="/config/ice-servers.js?v=1790225533889"></script>
|
||||||
<script defer src="/libs/react/react.production.min.js"></script>
|
<script defer src="/libs/react/react.production.min.js"></script>
|
||||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||||
@@ -278,8 +278,8 @@
|
|||||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||||
connection. Bytes were never the problem; requests were. -->
|
connection. Bytes were never the problem; requests were. -->
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=1790219928897">
|
<link rel="stylesheet" href="/assets/app.css?v=1790225533889">
|
||||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790219928897">
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790225533889">
|
||||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||||
answers for every weight, so there is one to preload rather than the two of five
|
answers for every weight, so there is one to preload rather than the two of five
|
||||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||||
@@ -287,19 +287,19 @@
|
|||||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||||
<script defer src="/src/scripts/load-async-css.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/load-async-css.js?v=1790225533889"></script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||||
</noscript>
|
</noscript>
|
||||||
<script defer src="/src/scripts/fa-check.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/fa-check.js?v=1790225533889"></script>
|
||||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||||
falls back to it); every other page loads its own here instead of all thirteen
|
falls back to it); every other page loads its own here instead of all thirteen
|
||||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||||
<script type="module" src="/src/i18n/dict/zh.js?v=1790219928897"></script>
|
<script type="module" src="/src/i18n/dict/zh.js?v=1790225533889"></script>
|
||||||
<!-- Update Manager - система принудительного обновления -->
|
<!-- Update Manager - система принудительного обновления -->
|
||||||
<script defer src="/src/utils/updateManager.js?v=1790219928897"></script>
|
<script defer src="/src/utils/updateManager.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790219928897"></script>
|
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790225533889"></script>
|
||||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||||
first load. src/components/QRScanner.js used to be here too and registered
|
first load. src/components/QRScanner.js used to be here too and registered
|
||||||
@@ -487,12 +487,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/dist/app-boot.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app-boot.js?v=1790225533889"></script>
|
||||||
<script type="module" src="/dist/app.js?v=1790219928897"></script>
|
<script type="module" src="/dist/app.js?v=1790225533889"></script>
|
||||||
|
|
||||||
<script defer src="/src/scripts/pwa-register.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-register.js?v=1790225533889"></script>
|
||||||
<script src="/src/pwa/install-prompt.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/install-prompt.js?v=1790225533889" type="module"></script>
|
||||||
<script src="/src/pwa/pwa-manager.js?v=1790219928897" type="module"></script>
|
<script src="/src/pwa/pwa-manager.js?v=1790225533889" type="module"></script>
|
||||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790219928897"></script>
|
<script defer src="/src/scripts/pwa-offline-test.js?v=1790225533889"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||