v6.8.1: the downloads point at desktop 1.0.3
CodeQL Analysis / Analyze CodeQL (push) Canceled after 0s
Deploy Application / deploy (push) Canceled after 0s
Mirror to Codeberg / mirror (push) Canceled after 0s
Mirror to PrivacyGuides / mirror (push) Canceled after 0s

Desktop 1.0.3 fixes video on a call placed after a group call — the two shared
one peer connection, the second call opened a second video section, and the two
H264 profiles collided, so the call carried sound and no picture. It also ships
the STUN and TURN list this client uses, which is what lets a browser and a
desktop agree on a path instead of one of them offering no relay at all.

The version lives in two places on this side (the landing page and the download
grid), and the consistency test also reads the README badge, the changelog and
the cryptography document, so all of them move together.

Riding along: the group status dot now takes its colour from the theme in the
call and group views, which is what the desktop's copy of groupsStore.js was
compared against.

Claude-Session: https://claude.ai/code/session_019RCW2Fm7dkAaU2zeHDksei
This commit is contained in:
lockbitchat
2026-09-11 22:58:20 -04:00
parent 9e63cf65a4
commit 4000bde857
31 changed files with 381 additions and 328 deletions
+11
View File
@@ -1,5 +1,16 @@
# Changelog
## v6.8.1 — Desktop 1.0.3
The download buttons point at desktop 1.0.3. That release fixes video on a call
placed after a group call — the two shared one connection, opened a second video
section, and the call carried sound with no picture — and it carries the same
STUN and TURN servers this web client uses, so a desktop and a browser can agree
on a path instead of one of them offering no relay at all.
Light theme colours: the group status dot now reads its colour from the theme in
the call and group views as well.
## v6.8.0 — Light theme
Added a light theme. The switch is in the header next to the language menu, with three
+1 -1
View File
@@ -9,7 +9,7 @@
No accounts. No servers storing your messages. No installation required.
[![License: MIT](https://img.shields.io/badge/License-MIT-f0892a.svg)](LICENSE)
[![Version](https://img.shields.io/badge/version-6.8.0-3ecf8e.svg)](CHANGELOG.md)
[![Version](https://img.shields.io/badge/version-6.8.1-3ecf8e.svg)](CHANGELOG.md)
[![Get it from the Snap Store](https://snapcraft.io/securebit-chat/badge.svg)](https://snapcraft.io/securebit-chat)
[![PWA](https://img.shields.io/badge/PWA-installable-3ecf8e.svg)](#install-as-an-app)
[![Encryption](https://img.shields.io/badge/crypto-ECDH%20P--384%20%C2%B7%20AES--256--GCM-blue.svg)](#security-model)
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/ar/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/ar.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/ar.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/de/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/de.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/de.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+3 -3
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
Vendored
+6 -6
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -7,7 +7,7 @@ this document describes.
| | |
| --- | --- |
| Release | v6.8.0 |
| Release | v6.8.1 |
| Protocol version | 4.1 |
| Ratchet wire version | 1 |
@@ -347,5 +347,5 @@ no leg may stop a capture it borrowed.
## Scope
This describes the browser implementation as it stands in v6.8.0. It is not a
This describes the browser implementation as it stands in v6.8.1. It is not a
substitute for independent cryptographic review.
+2 -2
View File
@@ -153,7 +153,7 @@
</thead>
<tbody><tr>
<td>Release</td>
<td>v6.8.0</td>
<td>v6.8.1</td>
</tr>
<tr>
<td>Protocol version</td>
@@ -486,7 +486,7 @@
shared across every leg, and stopped when the call, the group or the tab ends;
no leg may stop a capture it borrowed.</p>
<h2 id="scope">Scope</h2>
<p>This describes the browser implementation as it stands in v6.8.0. It is not a
<p>This describes the browser implementation as it stands in v6.8.1. It is not a
substitute for independent cryptographic review.</p>
<nav class="more">
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/es/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/es.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/es.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/fa/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/fa.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/fa.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/fr/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/fr.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/fr.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/he/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/he.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/he.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/hi/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/hi.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/hi.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+18 -18
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -486,12 +486,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/ko/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/ko.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/ko.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+7 -7
View File
@@ -1,10 +1,10 @@
{
"version": "1788557187846",
"buildVersion": "1788557187846",
"appVersion": "6.8.0",
"buildTime": "2026-09-04T21:26:27.911Z",
"buildId": "1788557187846-1c153b2",
"gitHash": "1c153b2",
"version": "1789181644117",
"buildVersion": "1789181644117",
"appVersion": "6.8.1",
"buildTime": "2026-09-12T02:54:04.184Z",
"buildId": "1789181644117-9e63cf6",
"gitHash": "9e63cf6",
"generated": true,
"generatedAt": "2026-09-04T21:26:27.912Z"
"generatedAt": "2026-09-12T02:54:04.185Z"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "securebit-chat",
"version": "6.8.0",
"version": "6.8.1",
"description": "Secure P2P Communication Application with End-to-End Encryption",
"main": "index.html",
"scripts": {
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/ru/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/ru.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/ru.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+23 -23
View File
@@ -17,7 +17,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -37,7 +37,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -57,7 +57,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -77,7 +77,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -97,7 +97,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -117,7 +117,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -137,7 +137,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -157,7 +157,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -177,7 +177,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -197,7 +197,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -217,7 +217,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -237,7 +237,7 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -257,67 +257,67 @@
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://securebit.chat/docs/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/faq/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/architecture/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/cryptography/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/descriptor-sbq2/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/configuration/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/calls/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/api/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/contributing/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://securebit.chat/docs/use-policy/</loc>
<lastmod>2026-09-04</lastmod>
<lastmod>2026-09-12</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
+12 -10
View File
@@ -1351,6 +1351,8 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
const isAnswerCred = !isCreate && showAnswerStep && !showVerification;
const atIntro = !showVerification && !isGenerating && !isOfferCred && !isAnswerCred;
const accent = isCreate ? C_ORANGE : C_GREEN;
// Same pair as a mark: fa() puts `color` on the icon's stroke.
const accentSolid = isCreate ? C_ORANGE_SOLID : C_GREEN_SOLID;
const kicker = showVerification
? t('step.verification')
: ((isOfferCred || isAnswerCred) ? t('step.exchange') : t('step.open'));
@@ -1552,7 +1554,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
]);
const showQrButton = qrCodeUrl && h('button', { key: 'showqr', onClick: () => setQrModalOpen(true), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '15px 16px', borderRadius: '14px', border: `1px solid ${isCreate ? 'rgba(var(--sb-orange-rgb), 0.3)' : 'rgba(var(--sb-green-rgb), 0.3)'}`, background: isCreate ? 'rgba(var(--sb-orange-rgb), 0.06)' : 'rgba(var(--sb-green-rgb), 0.06)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'start', marginBottom: '14px' } }, [
h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: isCreate ? 'rgba(var(--sb-orange-rgb), 0.12)' : 'rgba(var(--sb-green-rgb), 0.12)', border: `1px solid ${isCreate ? 'rgba(var(--sb-orange-rgb), 0.28)' : 'rgba(var(--sb-green-rgb), 0.28)'}` } }, fa('fa-qrcode', { color: accent, fontSize: '18px' })),
h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: isCreate ? 'rgba(var(--sb-orange-rgb), 0.12)' : 'rgba(var(--sb-green-rgb), 0.12)', border: `1px solid ${isCreate ? 'rgba(var(--sb-orange-rgb), 0.28)' : 'rgba(var(--sb-green-rgb), 0.28)'}` } }, fa('fa-qrcode', { color: accentSolid, fontSize: '18px' })),
h('span', { key: 'tx', style: { flex: 1 } }, [
h('span', { key: 't', style: { display: 'block', fontSize: '14.5px', fontWeight: 700, color: 'var(--sb-text-1)' } }, t('qr.showTitle')),
h('span', { key: 's', style: { display: 'block', fontSize: '12.5px', color: 'var(--sb-text-7)', marginTop: '1px' } }, (qrFramesTotal || 0) > 1 ? t('qr.showSubtitleFrames', { frames: qrFramesTotal }) : t('qr.showSubtitle'))
@@ -1568,14 +1570,14 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
inner = h('div', { key: 'verify', style: { animation: 'sbUp .3s ease' } }, [
!verified && backButton('vback'),
h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '11px', marginBottom: '8px' } }, [
h('div', { key: 'i', style: { width: '34px', height: '34px', flex: 'none', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(var(--sb-green-rgb), 0.1)', border: '1px solid rgba(var(--sb-green-rgb), 0.25)' } }, fa('fa-shield-alt', { color: C_GREEN })),
h('div', { key: 'i', style: { width: '34px', height: '34px', flex: 'none', borderRadius: '10px', display: 'grid', placeItems: 'center', background: 'rgba(var(--sb-green-rgb), 0.1)', border: '1px solid rgba(var(--sb-green-rgb), 0.25)' } }, fa('fa-shield-alt', { color: C_GREEN_SOLID })),
h('h2', { key: 't', style: { margin: 0, fontSize: '21px', fontWeight: 800, letterSpacing: '-0.4px', color: 'var(--sb-text-1)' } }, t('verify.title'))
]),
h('p', { key: 'sub', style: { margin: '0 0 18px', fontSize: '13.5px', lineHeight: 1.55, color: 'var(--sb-text-7)' } }, t('verify.desc')),
h('div', { key: 'cells', dir: 'ltr', style: { display: 'flex', gap: '6px', justifyContent: 'center', marginBottom: '20px', flexWrap: 'wrap' } }, cells),
verified
? h('div', { key: 'ok', style: { display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', padding: '24px 16px', borderRadius: '16px', border: '1px solid rgba(var(--sb-green-rgb), 0.25)', background: 'rgba(var(--sb-green-rgb), 0.06)', animation: 'sbUp .3s ease' } }, [
h('div', { key: 'i', style: { width: '54px', height: '54px', borderRadius: '16px', display: 'grid', placeItems: 'center', background: 'rgba(var(--sb-green-rgb), 0.14)', border: '1px solid rgba(var(--sb-green-rgb), 0.35)', marginBottom: '14px' } }, fa('fa-check', { color: C_GREEN, fontSize: '24px' })),
h('div', { key: 'i', style: { width: '54px', height: '54px', borderRadius: '16px', display: 'grid', placeItems: 'center', background: 'rgba(var(--sb-green-rgb), 0.14)', border: '1px solid rgba(var(--sb-green-rgb), 0.35)', marginBottom: '14px' } }, fa('fa-check', { color: C_GREEN_SOLID, fontSize: '24px' })),
h('div', { key: 't', style: { fontSize: '18px', fontWeight: 800, color: 'var(--sb-text-1)' } }, t('verify.verified')),
h('div', { key: 's', style: { fontSize: '13.5px', color: 'var(--sb-text-7)', marginTop: '5px' } }, t('verify.bothConfirmed'))
])
@@ -1603,7 +1605,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
const genSteps = [t('handshake.step1'), t('handshake.step2'), t('handshake.step3')];
inner = h('div', { key: 'gen', style: { animation: 'sbUp .28s ease' } }, [
h('div', { key: 'head', style: { display: 'flex', alignItems: 'center', gap: '13px', marginBottom: '22px' } }, [
h('div', { key: 'sp', style: { width: '44px', height: '44px', flex: 'none', display: 'grid', placeItems: 'center' } }, fa('fa-circle-notch', { color: C_ORANGE, fontSize: '32px', animation: 'sbSpin 1s linear infinite' })),
h('div', { key: 'sp', style: { width: '44px', height: '44px', flex: 'none', display: 'grid', placeItems: 'center' } }, fa('fa-circle-notch', { color: C_ORANGE_SOLID, fontSize: '32px', animation: 'sbSpin 1s linear infinite' })),
h('div', { key: 'tx' }, [
h('h2', { key: 't', style: { margin: 0, fontSize: '20px', fontWeight: 800, letterSpacing: '-0.4px', color: 'var(--sb-text-1)' } }, isCreate ? t('handshake.securingTitle') : t('handshake.answerTitle')),
h('p', { key: 's', style: { margin: '3px 0 0', fontSize: '13px', color: 'var(--sb-text-7)' } }, t('handshake.securingDesc'))
@@ -1616,7 +1618,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
return h('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: '12px', padding: '13px 15px', borderTop: i ? '1px solid rgba(var(--sb-ink), 0.05)' : 'none', transition: 'background .3s', background: done ? 'rgba(var(--sb-green-rgb), 0.04)' : 'transparent' } }, [
h('div', { key: 'd', style: { flex: 'none', width: '20px', height: '20px', borderRadius: '50%', display: 'grid', placeItems: 'center', background: done ? 'rgba(var(--sb-green-rgb), 0.12)' : (active ? 'rgba(var(--sb-orange-rgb), 0.12)' : 'rgba(var(--sb-ink), 0.04)'), border: `1px solid ${done ? 'rgba(var(--sb-green-rgb), 0.3)' : (active ? 'rgba(var(--sb-orange-rgb), 0.3)' : 'rgba(var(--sb-ink), 0.1)')}`, transition: 'all .3s' } },
done
? fa('fa-check', { color: C_GREEN, fontSize: '11px' })
? fa('fa-check', { color: C_GREEN_SOLID, fontSize: '11px' })
: h('span', { style: { width: '6px', height: '6px', borderRadius: '50%', background: active ? C_ORANGE_SOLID : 'var(--sb-text-faint)', animation: active ? 'sbBlink 1s ease-in-out infinite' : 'none' } })),
h('span', { key: 'l', style: { fontSize: '13.5px', color: done ? 'var(--sb-text-4)' : (active ? 'var(--sb-text-2)' : 'var(--sb-text-9)'), transition: 'color .3s' } }, label)
]);
@@ -1640,7 +1642,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
])
]),
isAnswerCred && h('div', { key: 'answerextra', style: { marginTop: '4px', display: 'flex', alignItems: 'center', gap: '10px', padding: '12px 14px', borderRadius: '12px', border: '1px solid rgba(var(--sb-green-rgb), 0.18)', background: 'rgba(var(--sb-green-rgb), 0.05)' } }, [
fa('fa-circle-notch', { key: 'i', color: C_GREEN, animation: 'sbSpin 1.4s linear infinite' }),
fa('fa-circle-notch', { key: 'i', color: C_GREEN_SOLID, animation: 'sbSpin 1.4s linear infinite' }),
h('span', { key: 't', style: { fontSize: '13px', color: 'var(--sb-text-4)', fontWeight: 500 } }, t('handshake.answerSentNote'))
])
]);
@@ -1657,7 +1659,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
h('h2', { key: 'h', style: { margin: '0 0 6px', fontSize: '23px', fontWeight: 800, letterSpacing: '-0.5px', color: 'var(--sb-text-1)' } }, t('intro.joinTitle')),
h('p', { key: 'p', style: { margin: '0 0 16px', fontSize: '14px', lineHeight: 1.55, color: 'var(--sb-text-7)' } }, "Scan your peer's QR with your camera, or paste their invitation code."),
h('button', { key: 'scan', className: 'sb-scan-btn', onClick: () => { requestNotificationPermissionOnInteraction(); setShowQRScannerModal(true); }, style: { width: '100%', display: 'flex', alignItems: 'center', gap: '13px', padding: '15px 16px', borderRadius: '14px', border: '1px solid rgba(var(--sb-green-rgb), 0.3)', background: 'rgba(var(--sb-green-rgb), 0.06)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'start', marginBottom: '14px' } }, [
h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: 'rgba(var(--sb-green-rgb), 0.12)', border: '1px solid rgba(var(--sb-green-rgb), 0.28)' } }, fa('fa-camera', { color: C_GREEN, fontSize: '18px' })),
h('span', { key: 'ic', style: { flex: 'none', width: '42px', height: '42px', borderRadius: '12px', display: 'grid', placeItems: 'center', background: 'rgba(var(--sb-green-rgb), 0.12)', border: '1px solid rgba(var(--sb-green-rgb), 0.28)' } }, fa('fa-camera', { color: C_GREEN_SOLID, fontSize: '18px' })),
h('span', { key: 'tx', style: { flex: 1 } }, [
h('span', { key: 't', style: { display: 'block', fontSize: '14.5px', fontWeight: 700, color: 'var(--sb-text-1)' } }, t('intro.scanTitle')),
h('span', { key: 's', style: { display: 'block', fontSize: '12.5px', color: 'var(--sb-text-7)', marginTop: '1px' } }, t('intro.scanSubtitle'))
@@ -1687,7 +1689,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
// /download/v0.3.0/SecureBit.Chat_0.1.0_x64-setup.exe, a file that never
// existed, and the download 404s. A pinned tag keeps serving a real
// installer instead.
const SB_DESKTOP_VERSION = '1.0.1';
const SB_DESKTOP_VERSION = '1.0.3';
const SB_DESKTOP_RELEASE = `https://github.com/SecureBitChat/securebit-desktop/releases/download/v${SB_DESKTOP_VERSION}`;
const DOWNLOADS = {
mac: { name: 'macOS', format: '.dmg · Apple Silicon & Intel', icon: 'fab fa-apple', url: `${SB_DESKTOP_RELEASE}/SecureBit.Chat_${SB_DESKTOP_VERSION}_x64.dmg` },
@@ -1720,7 +1722,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
h('span', { key: 'n', style: { display: 'block', fontSize: '13.5px', fontWeight: 700, color: 'var(--sb-text-1)' } }, DOWNLOADS[detectedOS].name),
h('span', { key: 'f', style: { display: 'block', fontSize: '11px', color: 'var(--sb-orange-3)', marginTop: '1px' } }, `Recommended for this device · ${DOWNLOADS[detectedOS].format}`)
]),
fa('fa-download', { color: C_ORANGE })
fa('fa-download', { color: C_ORANGE_SOLID })
])),
h('div', { key: 'others', style: { padding: '0 12px 8px', display: 'flex', flexDirection: 'column', gap: '2px' } },
otherOS.map((k) => h('button', { key: k, onClick: () => dlLink(DOWNLOADS[k].url), style: { width: '100%', display: 'flex', alignItems: 'center', gap: '12px', padding: '11px 14px', borderRadius: '11px', border: 'none', background: 'transparent', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'start' } }, [
@@ -1739,7 +1741,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
const footer = h('div', { key: 'footer', className: 'sb-conn-footer', style: { position: 'relative', marginTop: '30px', paddingTop: '18px', borderTop: '1px solid rgba(var(--sb-ink), 0.06)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '12px', flexWrap: 'wrap' } }, [
h('button', { key: 'dl', onClick: () => setPlatformsOpen((v) => !v), style: { display: 'inline-flex', alignItems: 'center', gap: '9px', paddingBlock: '8px', paddingInlineStart: '9px', paddingInlineEnd: '13px', borderRadius: '10px', border: `1px solid ${platformsOpen ? 'rgba(var(--sb-orange-rgb), 0.4)' : 'rgba(var(--sb-ink), 0.08)'}`, background: platformsOpen ? 'rgba(var(--sb-orange-rgb), 0.06)' : 'rgba(var(--sb-ink), 0.02)', color: 'inherit', fontFamily: 'inherit', cursor: 'pointer', transition: 'all .15s' } }, [
fa('fa-download', { key: 'i', color: C_ORANGE }),
fa('fa-download', { key: 'i', color: C_ORANGE_SOLID }),
h('span', { key: 't', style: { fontSize: '12.5px', fontWeight: 700, color: 'var(--sb-text-2)' } }, t('action.downloadDesktop')),
fa('fa-chevron-down', { key: 'c', color: 'var(--sb-text-9)', style: { fontSize: '11px', transform: platformsOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform .2s' } })
]),
+7 -5
View File
@@ -124,18 +124,20 @@ const CallUIComponent = ({ webrtcManager, peerTitle }) => {
// Connection-quality indicator driven by the adaptation controller's
// getStats (loss + RTT). Signal bars + label; hidden until there's data.
// `color` labels the quality in words; `solid` fills the signal bars. A bar is a
// mark, so it keeps the brand colour in both themes see src/styles/theme.css.
const QUALITY = {
excellent: { bars: 4, color: 'var(--sb-green)', label: t('call.qualityExcellent') },
good: { bars: 3, color: 'var(--sb-green)', label: t('call.qualityGood') },
fair: { bars: 2, color: 'var(--sb-yellow)', label: t('call.qualityFair') },
poor: { bars: 1, color: 'var(--sb-red)', label: t('call.qualityWeak') },
excellent: { bars: 4, color: 'var(--sb-green)', solid: 'var(--sb-green-solid)', label: t('call.qualityExcellent') },
good: { bars: 3, color: 'var(--sb-green)', solid: 'var(--sb-green-solid)', label: t('call.qualityGood') },
fair: { bars: 2, color: 'var(--sb-yellow)', solid: 'var(--sb-yellow-solid)', label: t('call.qualityFair') },
poor: { bars: 1, color: 'var(--sb-red)', solid: 'var(--sb-red-solid)', label: t('call.qualityWeak') },
};
const qualityIndicator = (compact) => {
const q = QUALITY[call.quality];
if (!q) return null;
const bars = h('span', { key: 'bars', style: { display: 'inline-flex', alignItems: 'flex-end', gap: '2px', height: '14px' } },
[0, 1, 2, 3].map(i => h('span', {
key: i, style: { width: '3px', height: (5 + i * 3) + 'px', borderRadius: '1px', background: i < q.bars ? q.color : 'rgba(var(--sb-ink), 0.18)' }
key: i, style: { width: '3px', height: (5 + i * 3) + 'px', borderRadius: '1px', background: i < q.bars ? q.solid : 'rgba(var(--sb-ink), 0.18)' }
})));
if (compact) return bars;
return h('span', { key: 'q', title: t('call.quality'), style: { display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: '11.5px', fontWeight: 600, color: q.color } }, [bars, q.label]);
+1 -1
View File
@@ -5,7 +5,7 @@
// The tag is pinned rather than using /releases/latest/download/: the filenames
// carry the version, so a `latest` link 404s the moment a newer release exists,
// whereas a pinned tag keeps serving a working (if older) installer.
const DESKTOP_VERSION = '1.0.1';
const DESKTOP_VERSION = '1.0.3';
const DESKTOP_RELEASE = `https://github.com/SecureBitChat/securebit-desktop/releases/download/v${DESKTOP_VERSION}`;
const DownloadApps = () => {
+7 -5
View File
@@ -55,11 +55,13 @@ const minimizeBtn = {
color: '#fff', cursor: 'pointer', transition: 'all .15s',
};
// `color` labels the quality in words; `solid` fills the signal bars. A bar is a
// mark, so it keeps the brand colour in both themes see src/styles/theme.css.
const QUALITY = {
excellent: { bars: 4, color: 'var(--sb-green)' },
good: { bars: 3, color: 'var(--sb-green)' },
fair: { bars: 2, color: 'var(--sb-yellow)' },
poor: { bars: 1, color: 'var(--sb-red)' },
excellent: { bars: 4, color: 'var(--sb-green)', solid: 'var(--sb-green-solid)' },
good: { bars: 3, color: 'var(--sb-green)', solid: 'var(--sb-green-solid)' },
fair: { bars: 2, color: 'var(--sb-yellow)', solid: 'var(--sb-yellow-solid)' },
poor: { bars: 1, color: 'var(--sb-red)', solid: 'var(--sb-red-solid)' },
};
function qualityBars(quality) {
@@ -70,7 +72,7 @@ function qualityBars(quality) {
key: i,
style: {
width: '2.5px', height: (4 + i * 2.6) + 'px', borderRadius: '1px',
background: i < q.bars ? q.color : 'rgba(var(--sb-ink), 0.18)',
background: i < q.bars ? q.solid : 'rgba(var(--sb-ink), 0.18)',
},
})));
}
+17 -8
View File
@@ -23,10 +23,19 @@ const C = {
ink: 'var(--sb-text-1)',
ink2: 'var(--sb-text-5b)',
ink3: 'var(--sb-text-9)',
// Two forms of each accent. The plain one is for text and darkens in the light
// theme so it clears 4.5:1 on white; the -Solid one is for anything painted a
// button's ground, a checkbox, a dot, the safety code and is the brand colour in
// both themes, because what sits on it is near-black either way. Reaching for the
// wrong one is how every accent button in this file came out brown.
accent: 'var(--sb-orange)',
good: 'var(--sb-green)',
warn: 'var(--sb-yellow-2)',
bad: 'var(--sb-red)',
accentSolid: 'var(--sb-orange-solid)',
goodSolid: 'var(--sb-green-solid)',
warnSolid: 'var(--sb-yellow-2-solid)',
badSolid: 'var(--sb-red-solid)',
mono: "'JetBrains Mono', ui-monospace, monospace",
};
@@ -52,7 +61,7 @@ const btn = (accent = false) => ({
padding: '11px 18px', borderRadius: '10px', cursor: 'pointer',
fontFamily: 'inherit', fontSize: '14px', fontWeight: 700,
border: accent ? 'none' : `1px solid ${C.line2}`,
background: accent ? C.accent : 'transparent',
background: accent ? C.accentSolid : 'transparent',
color: accent ? 'var(--sb-on-accent)' : C.ink2,
});
@@ -175,7 +184,7 @@ export function GroupSasModal({ group, onConfirm, onCancel }) {
dir: 'ltr',
style: {
fontFamily: C.mono, fontSize: 'clamp(30px, 9vw, 42px)', fontWeight: 700,
letterSpacing: '9px', color: C.accent, unicodeBidi: 'isolate',
letterSpacing: '9px', color: C.accentSolid, unicodeBidi: 'isolate',
},
}, group.sasCode)),
@@ -302,8 +311,8 @@ export function CreateGroupModal({ candidates, relayOnly, onCreate, onCancel })
key: 'tick',
style: {
flex: 'none', width: '18px', height: '18px', borderRadius: '5px',
background: on ? C.accent : 'transparent',
border: `1px solid ${on ? C.accent : C.line2}`,
background: on ? C.accentSolid : 'transparent',
border: `1px solid ${on ? C.accentSolid : C.line2}`,
},
}),
]);
@@ -430,8 +439,8 @@ export function AddMembersModal({ candidates, remaining, onAdd, onCancel }) {
key: 'tick',
style: {
flex: 'none', width: '18px', height: '18px', borderRadius: '5px',
background: on ? C.accent : 'transparent',
border: `1px solid ${on ? C.accent : C.line2}`,
background: on ? C.accentSolid : 'transparent',
border: `1px solid ${on ? C.accentSolid : C.line2}`,
},
}),
]);
@@ -504,8 +513,8 @@ function MemberStrip({ group, onRemove, isAdmin }) {
}, group.members.map((m) => {
const self = m.state === MEMBER_STATE.SELF;
const lost = m.state === MEMBER_STATE.LOST;
const dot = self || m.state === MEMBER_STATE.LINKED ? C.good
: m.state === MEMBER_STATE.PENDING ? C.warn : C.bad;
const dot = self || m.state === MEMBER_STATE.LINKED ? C.goodSolid
: m.state === MEMBER_STATE.PENDING ? C.warnSolid : C.badSolid;
const via = m.state === MEMBER_STATE.PENDING;
return h('span', {
key: m.fp,
+1 -1
View File
@@ -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
// bytes change each deploy. That is what makes the browser detect a new Service Worker,
// reinstall it, drop stale caches and (via controllerchange) prompt the page to update.
const SW_BUILD_VERSION = '1788557187846';
const SW_BUILD_VERSION = '1789181644117';
// 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
+29 -2
View File
@@ -141,8 +141,21 @@ const template = read('templates/index.template.html');
// a helper argument: svg(icon, size, ACCENT, width) puts its third argument on
// `stroke`, so the colour reaches a mark without ever naming a property.
/\bsvg\([^)]*?\b(C_ORANGE|C_GREEN|ACCENT)\b(?!_SOLID)/g,
// a variable whose name says it is a dot. A dot is always a mark.
/\b\w*[Dd]ot\w*\s*=\s*[^;]{0,120}var\(--sb-(orange|green|red|red-strong|yellow|yellow-2)\)/g,
// a variable whose name says it is a dot. A dot is always a mark, and it can be
// assigned either a token or a palette member, so both spellings are checked.
/\b\w*[Dd]ot\w*\s*=\s*[^;]{0,140}var\(--sb-(orange|green|red|red-strong|yellow|yellow-2)\)/g,
/\b\w*[Dd]ot\w*\s*=\s*[^;]{0,140}\.(accent|good|warn|bad)\b(?!Solid)/g,
// a member of a palette or lookup object — `background: C.accent`,
// `background: q.color`. GroupChat keeps its whole palette this way, and every
// accent button in it came out brown because no property named a token and no
// constant carried one either.
// `opts` is excluded: it is the parameter of the generic fa() icon helper, which
// paints whatever its caller hands it. Checking the helper would say nothing —
// the call sites are checked instead, just below.
/(background|backgroundColor|borderColor|fill|stroke)[a-zA-Z-]*\s*:\s*[^,;}]{0,50}\b(?!opts\b)\w+\.(accent|good|warn|bad|color)\b(?!Solid)/g,
// fa('name', { color: X }) puts X on the icon's stroke, so an accent passed here
// is a mark and must be the fill form.
/fa\('[a-z0-9-]+',\s*\{[^{}]{0,80}color:\s*(C_GREEN|C_ORANGE|accent)(?![_A-Za-z])/g,
];
const offenders = [];
for (const rel of sources) {
@@ -163,6 +176,20 @@ const template = read('templates/index.template.html');
`${rel} only paints dots; every accent there must be the -solid form`);
}
// A palette object that names an accent must publish both forms, so a call site can
// pick the right one instead of having only the wrong one available.
{
const groupChat = read('src/components/ui/GroupChat.jsx');
for (const key of ['accent', 'good', 'warn', 'bad']) {
assert.match(groupChat, new RegExp(`${key}Solid: 'var\\(--sb-[a-z0-9-]+-solid\\)'`),
`GroupChat's palette needs a ${key}Solid for anything it paints`);
}
for (const rel of ['src/components/ui/CallUI.jsx', 'src/components/ui/GroupCallUI.jsx']) {
assert.match(read(rel), /solid: 'var\(--sb-[a-z0-9-]+-solid\)'/,
`${rel}'s quality table needs the fill form for its signal bars`);
}
}
// A constant that feeds a fill has to be the solid one. Both forms must exist
// wherever the file declares either, so the two are never confused for each other.
for (const rel of ['src/app.jsx', 'src/components/ui/IceServerSettings.jsx']) {
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/uk/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/uk.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/uk.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/ur/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/ur.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/ur.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest -->
<link rel="manifest" href="/zh/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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). -->
<script src="/src/scripts/theme-boot.js?v=1788557187846"></script>
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1788557187846"></script>
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script>
<!-- PWA Meta Tags -->
<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">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1788557187846">
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117">
<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="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="144x144" href="/logo/icon-144x144.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=1788557187846">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117">
<!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
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. -->
<script defer src="/config/ice-servers.js?v=1788557187846"></script>
<script defer src="/config/ice-servers.js?v=1789181644117"></script>
<script defer src="/libs/react/react.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 —
@@ -278,8 +278,8 @@
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
connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1788557187846">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1788557187846">
<link rel="stylesheet" href="/assets/app.css?v=1789181644117">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117">
<!-- 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
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/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1788557187846"></script>
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script>
<noscript>
<link rel="stylesheet" href="/libs/prism/prism.css">
</noscript>
<script defer src="/src/scripts/fa-check.js?v=1788557187846"></script>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script>
<!-- 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()
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. -->
<script type="module" src="/src/i18n/dict/zh.js?v=1788557187846"></script>
<script type="module" src="/src/i18n/dict/zh.js?v=1789181644117"></script>
<!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1788557187846"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1788557187846"></script>
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script>
<!-- /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
first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div>
</div>
</div>
<script type="module" src="/dist/app-boot.js?v=1788557187846"></script>
<script type="module" src="/dist/app.js?v=1788557187846"></script>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script>
<script defer src="/src/scripts/pwa-register.js?v=1788557187846"></script>
<script src="/src/pwa/install-prompt.js?v=1788557187846" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1788557187846" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1788557187846"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script>
</body>
</html>