v6.8.2: the keyboard no longer pushes the chat off the screen on iOS

Reported from an iPhone, with a screenshot: tap the message box, the keyboard
comes up, and the whole app has slid off the top — header gone, only the bottom
edge of the composer left, black underneath it down to the keyboard.

That is the visual viewport being panned. WebKit shrinks it for the keyboard and
then moves it down inside the layout viewport (which it does not shrink) to
reveal the focused field. The shell had shrunk correctly, to the top --sb-vh of
the layout viewport; the window had simply moved off it. No CSS declines that
pan, and it is not a document scroll, so there is no scrollTop to put back.

So the shell is pinned to the visible rect: fixed to the layout viewport and
translated by visualViewport.offsetTop. The earlier objection to pinning is kept
where it was right — HEIGHT still moves on resize only, which is what stopped the
layout twitching under the finger, and the offset rides a transform rather than
`top`.

The first cut of this had a guard that silently disabled it: the pan was only
followed when window.innerHeight was much taller than the visual viewport, i.e.
"a keyboard is covering us". That assumes iOS never resizes the layout viewport,
which stopped being safe once WebKit started honouring interactive-widget. Only a
pinch-zoomed page switches the pan off now, by scale, and offsetTop is believed
whenever it is non-zero. The offset is also re-read across the keyboard's
animation on focusin/focusout: on iOS 26 the viewport is documented not to settle
back immediately on dismissal.

The message list stays at the newest message across a keyboard open or close,
instead of holding a scroll position from the taller layout and leaving the last
few messages under the composer.

Two smaller things on the same screen:

The QR code closes itself once the safety number appears. It was only dismissed
when both exchange flags cleared, and those stay set through verification, so a
one-time credential sat on top of the code the user was meant to be reading.

The disconnect button's label is hidden wherever the button becomes a 40px
square. Its own rule stopped at 560px while the square starts at 768px, so
between the two the word was still inside the box, pushing the icon off centre.

?vvdebug=1 paints the viewport numbers over the app — visible rect height,
offset, scale, what the shell resolved to. A phone cannot be put on a debugger
and none of those numbers show up in a screenshot of the result; this one is how
the guard above was caught. Off unless named in the URL.

Claude-Session: https://claude.ai/code/session_01R63J7dexujFQCJpDAeKxr9
This commit is contained in:
lockbitchat
2026-09-12 01:25:45 -04:00
parent 4000bde857
commit 12e62db76f
27 changed files with 496 additions and 301 deletions
+16
View File
@@ -1,5 +1,21 @@
# Changelog # Changelog
## v6.8.2 — The keyboard no longer pushes the chat off the screen
On iPhone, tapping the message box used to shove the whole chat off the top of the
screen: the header disappeared, only the bottom edge of the message box was left, and
everything under it was empty black down to the keyboard. Safari moves the visible part
of the page up to show the field you tapped, and the app was not following it. Now it
is, so the conversation stays where it is and the message box sits just above the
keyboard. The chat also stays scrolled to the newest message when the keyboard opens and
closes, instead of leaving you a screen behind.
The QR code now closes by itself once the other side has scanned it and the security
code appears — it used to stay on top of the code you were meant to be checking.
The disconnect button in the chat header is a clean square again on small screens; its
label was still taking up room inside it and pushing the icon off centre.
## v6.8.1 — Desktop 1.0.3 ## v6.8.1 — Desktop 1.0.3
The download buttons point at desktop 1.0.3. That release fixes video on a call The download buttons point at desktop 1.0.3. That release fixes video on a call
+1 -1
View File
@@ -9,7 +9,7 @@
No accounts. No servers storing your messages. No installation required. No accounts. No servers storing your messages. No installation required.
[![License: MIT](https://img.shields.io/badge/License-MIT-f0892a.svg)](LICENSE) [![License: MIT](https://img.shields.io/badge/License-MIT-f0892a.svg)](LICENSE)
[![Version](https://img.shields.io/badge/version-6.8.1-3ecf8e.svg)](CHANGELOG.md) [![Version](https://img.shields.io/badge/version-6.8.2-3ecf8e.svg)](CHANGELOG.md)
[![Get it from the Snap Store](https://snapcraft.io/securebit-chat/badge.svg)](https://snapcraft.io/securebit-chat) [![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) [![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) [![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 --> <!-- PWA Manifest -->
<link rel="manifest" href="/ar/manifest.json"> <link rel="manifest" href="/ar/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/ar.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/ar.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/de/manifest.json"> <link rel="manifest" href="/de/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/de.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/de.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
Vendored
+11 -10
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.1 | | Release | v6.8.2 |
| Protocol version | 4.1 | | Protocol version | 4.1 |
| Ratchet wire version | 1 | | Ratchet wire version | 1 |
@@ -347,5 +347,5 @@ no leg may stop a capture it borrowed.
## Scope ## Scope
This describes the browser implementation as it stands in v6.8.1. It is not a This describes the browser implementation as it stands in v6.8.2. It is not a
substitute for independent cryptographic review. substitute for independent cryptographic review.
+2 -2
View File
@@ -153,7 +153,7 @@
</thead> </thead>
<tbody><tr> <tbody><tr>
<td>Release</td> <td>Release</td>
<td>v6.8.1</td> <td>v6.8.2</td>
</tr> </tr>
<tr> <tr>
<td>Protocol version</td> <td>Protocol version</td>
@@ -486,7 +486,7 @@
shared across every leg, and stopped when the call, the group or the tab ends; shared across every leg, and stopped when the call, the group or the tab ends;
no leg may stop a capture it borrowed.</p> no leg may stop a capture it borrowed.</p>
<h2 id="scope">Scope</h2> <h2 id="scope">Scope</h2>
<p>This describes the browser implementation as it stands in v6.8.1. It is not a <p>This describes the browser implementation as it stands in v6.8.2. It is not a
substitute for independent cryptographic review.</p> substitute for independent cryptographic review.</p>
<nav class="more"> <nav class="more">
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/es/manifest.json"> <link rel="manifest" href="/es/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/es.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/es.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/fa/manifest.json"> <link rel="manifest" href="/fa/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/fa.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/fa.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/fr/manifest.json"> <link rel="manifest" href="/fr/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/fr.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/fr.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/he/manifest.json"> <link rel="manifest" href="/he/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/he.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/he.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/hi/manifest.json"> <link rel="manifest" href="/hi/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/hi.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/hi.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+18 -18
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,18 +287,18 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -486,12 +486,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/ko/manifest.json"> <link rel="manifest" href="/ko/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/ko.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/ko.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+7 -7
View File
@@ -1,10 +1,10 @@
{ {
"version": "1789181644117", "version": "1789190079159",
"buildVersion": "1789181644117", "buildVersion": "1789190079159",
"appVersion": "6.8.1", "appVersion": "6.8.2",
"buildTime": "2026-09-12T02:54:04.184Z", "buildTime": "2026-09-12T05:14:39.203Z",
"buildId": "1789181644117-9e63cf6", "buildId": "1789190079159-4000bde",
"gitHash": "9e63cf6", "gitHash": "4000bde",
"generated": true, "generated": true,
"generatedAt": "2026-09-12T02:54:04.185Z" "generatedAt": "2026-09-12T05:14:39.204Z"
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "securebit-chat", "name": "securebit-chat",
"version": "6.8.1", "version": "6.8.2",
"description": "Secure P2P Communication Application with End-to-End Encryption", "description": "Secure P2P Communication Application with End-to-End Encryption",
"main": "index.html", "main": "index.html",
"scripts": { "scripts": {
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/ru/manifest.json"> <link rel="manifest" href="/ru/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/ru.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/ru.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+165 -8
View File
@@ -1169,10 +1169,16 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
React.useEffect(() => { setSasInput(''); setSasError(''); }, [verificationCode]); React.useEffect(() => { setSasInput(''); setSasError(''); }, [verificationCode]);
// Close the QR popup and re-hide (blur) the code whenever the // Close the QR popup and re-hide (blur) the code whenever the
// exchange step changes — every new offer/answer starts concealed. // exchange step changes — every new offer/answer starts concealed.
// Verification is also a step change, and the one that matters most:
// the peer has scanned the code and the channel is up, so a QR still
// covering the screen is a one-time credential left on display over
// the safety number the user is now supposed to be reading. The
// offer/answer flags stay set underneath, which is why this cannot
// be left to the !showOfferStep && !showAnswerStep case alone.
React.useEffect(() => { React.useEffect(() => {
if (!showOfferStep && !showAnswerStep) setQrModalOpen(false); if (showVerification || (!showOfferStep && !showAnswerStep)) setQrModalOpen(false);
setCodeRevealed(false); setCodeRevealed(false);
}, [showOfferStep, showAnswerStep]); }, [showOfferStep, showAnswerStep, showVerification]);
// Animate the "Securing your channel" steps while keys generate. // Animate the "Securing your channel" steps while keys generate.
React.useEffect(() => { React.useEffect(() => {
const generating = isGeneratingKeys && !showOfferStep && !showAnswerStep && !showVerification; const generating = isGeneratingKeys && !showOfferStep && !showAnswerStep && !showVerification;
@@ -1867,6 +1873,48 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
}; };
// Global scroll function - defined outside components to ensure availability // Global scroll function - defined outside components to ensure availability
// ── viewport read-out (?vvdebug=1) ───────────────────────────────
// A phone cannot be attached to a debugger the way a laptop can, and
// the numbers that decide this layout — how tall the visible rect is,
// where it has been moved to, whether the shell followed — are not
// visible in a screenshot of the result. This paints them on top of
// the app so one screenshot answers the question. Off unless asked
// for by name in the URL; it costs nothing when it is not.
function startViewportDebug(vv) {
let on = false;
try { on = new URLSearchParams(window.location.search).get('vvdebug') === '1'; } catch (_) { return null; }
if (!on) return null;
const box = document.createElement('div');
box.setAttribute('data-sb-vvdebug', '');
box.style.cssText = 'position:fixed;top:0;left:0;z-index:2147483647;max-width:100vw;padding:6px 8px;' +
'font:600 10.5px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre;' +
'color:#fff;background:#000;pointer-events:none;';
document.body.appendChild(box);
const src = (() => {
const el = document.querySelector('script[src*="dist/app.js"]');
return el ? String(el.getAttribute('src') || '').split('?v=')[1] || '?' : '?';
})();
const paint = () => {
const shell = document.querySelector('.sb-app-shell');
const cs = shell ? getComputedStyle(shell) : null;
const r = shell ? shell.getBoundingClientRect() : null;
const root = getComputedStyle(document.documentElement);
box.textContent = [
'build ' + src,
'innerH ' + window.innerHeight + ' scrollY ' + Math.round(window.scrollY),
'vv h ' + (vv ? Math.round(vv.height) : '—') + ' top ' + (vv ? Math.round(vv.offsetTop) : '—') + ' scale ' + (vv ? vv.scale.toFixed(2) : '—'),
'var vh ' + root.getPropertyValue('--sb-vh').trim() + ' vvtop ' + root.getPropertyValue('--sb-vv-top').trim(),
'shell ' + (shell ? (cs.position + ' h' + Math.round(r.height) + ' y' + Math.round(r.top)) : 'ABSENT')
].join('\n');
// The read-out must stay readable even when the thing it is
// reporting on has been moved off the screen.
box.style.transform = 'translate3d(0,' + (vv ? Math.round(vv.offsetTop) : 0) + 'px,0)';
};
paint();
const iv = setInterval(paint, 200);
return () => { clearInterval(iv); box.remove(); };
}
const createScrollToBottomFunction = (chatMessagesRef) => { const createScrollToBottomFunction = (chatMessagesRef) => {
return () => { return () => {
if (chatMessagesRef && chatMessagesRef.current) { if (chatMessagesRef && chatMessagesRef.current) {
@@ -2124,6 +2172,11 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
'.sb-chat-header .sb-hdr-sub{display:none !important;}' + '.sb-chat-header .sb-hdr-sub{display:none !important;}' +
// Disconnect: compact icon-only square, red-tinted, matching the call buttons. // Disconnect: compact icon-only square, red-tinted, matching the call buttons.
'.sb-chat-header .sb-disconnect{width:40px !important;height:40px !important;padding:0 !important;gap:0 !important;justify-content:center !important;border-radius:9px !important;color:var(--sb-red) !important;border-color:rgba(var(--sb-red-rgb), 0.28) !important;}' + '.sb-chat-header .sb-disconnect{width:40px !important;height:40px !important;padding:0 !important;gap:0 !important;justify-content:center !important;border-radius:9px !important;color:var(--sb-red) !important;border-color:rgba(var(--sb-red-rgb), 0.28) !important;}' +
// The label has to go wherever the button becomes a square. Its own
// rule (.sb-hide-sm, components.css) stops at 560px, so between there
// and 768 the word "Disconnect" was still inside a 40px box, shouldering
// the icon off-centre — the button read as crooked rather than icon-only.
'.sb-chat-header .sb-disconnect .sb-hide-sm{display:none !important;}' +
'}' + '}' +
// Composer mode chips: one horizontally-scrollable row instead of wrapping // Composer mode chips: one horizontally-scrollable row instead of wrapping
// to two ugly rows on narrow screens. // to two ugly rows on narrow screens.
@@ -2291,6 +2344,33 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
} }
}, [pendingIncomingFiles.length]); }, [pendingIncomingFiles.length]);
// Whether the conversation was sitting at its bottom, remembered from the
// last scroll. It has to be remembered rather than measured on demand:
// by the time the keyboard has opened the list is already shorter, and
// "how far from the bottom" then reads as the height of the keyboard for
// a view that had not moved at all.
const atBottomRef = React.useRef(true);
// Follow the conversation when the visual viewport resizes — the keyboard
// opening or closing. The list keeps its scrollTop while its height drops
// by a few hundred pixels, which leaves whoever tapped the composer
// looking at messages from a minute ago.
React.useEffect(() => {
const vv = (typeof window !== 'undefined') ? window.visualViewport : null;
if (!vv) return;
const stick = () => {
if (!atBottomRef.current) return;
const el = chatMessagesRef.current;
if (!el) return;
el.scrollTop = el.scrollHeight;
};
// Twice: once for this frame, once after iOS has finished animating
// the keyboard in and settled on a final height.
const onResize = () => { requestAnimationFrame(stick); setTimeout(stick, 300); };
vv.addEventListener('resize', onResize);
return () => vv.removeEventListener('resize', onResize);
}, [chatMessagesRef]);
React.useEffect(() => { React.useEffect(() => {
if (chatMessagesRef.current && messages.length > 0) { if (chatMessagesRef.current && messages.length > 0) {
const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current; const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;
@@ -2315,11 +2395,13 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
if (chatMessagesRef.current) { if (chatMessagesRef.current) {
const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current; const { scrollTop, scrollHeight, clientHeight } = chatMessagesRef.current;
const isNearBottom = scrollHeight - scrollTop - clientHeight < 100; const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;
atBottomRef.current = isNearBottom;
setShowScrollButton(!isNearBottom); setShowScrollButton(!isNearBottom);
} }
}; };
const handleScrollToBottom = () => { const handleScrollToBottom = () => {
atBottomRef.current = true;
if (typeof scrollToBottom === 'function') { if (typeof scrollToBottom === 'function') {
scrollToBottom(); scrollToBottom();
setShowScrollButton(false); setShowScrollButton(false);
@@ -3042,7 +3124,32 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
// viewport tracking, the dvh fallback — was inert until this // viewport tracking, the dvh fallback — was inert until this
// floor was removed: --sb-vh could shrink all it liked and the // floor was removed: --sb-vh could shrink all it liked and the
// shell would not follow it below 100vh. // shell would not follow it below 100vh.
'.sb-app-shell{height:var(--sb-vh,100dvh) !important;min-height:0 !important;overflow:hidden;}' + //
// Sizing alone turned out not to be enough on iOS, and the
// screenshot of the failure is worth writing down: keyboard up,
// the whole app had slid off the top of the screen, only the
// bottom sliver of the composer still showing, and black — the
// body, past the end of a 400px-tall shell — filling everything
// down to the keyboard.
//
// That is the *visual* viewport being panned. When the keyboard
// opens, WebKit shrinks the visual viewport and moves it down
// inside the layout viewport (which it never shrinks) to reveal
// the focused field. The shell had correctly shrunk to the top
// var(--sb-vh) of the layout viewport — and the visible window
// had moved off it. Nothing in CSS can decline that pan, and it
// is not a document scroll, so there is no scrollTop to reset.
//
// So the shell is pinned to the visible rect after all: fixed to
// the layout viewport and translated by visualViewport.offsetTop.
// The earlier objection to pinning stands for what it described —
// chasing the offset with `top`, and recomputing HEIGHT on every
// scroll event, which twitched under the finger. Height still
// moves on resize only. The offset rides a transform on the
// compositor, which is the cheap half, and only it follows the
// pan. --sb-vv-top is 0 everywhere that does not pan (desktop,
// Chrome on Android), so this is inert off iOS.
'.sb-app-shell{height:var(--sb-vh,100dvh) !important;min-height:0 !important;overflow:hidden;position:fixed;top:0;inset-inline:0;transform:translate3d(0,var(--sb-vv-top, 0px),0);}' +
// Pin the header, and nothing else. // Pin the header, and nothing else.
// //
// Sticky is the whole fix: if an ancestor scrolls — which is what // Sticky is the whole fix: if an ancestor scrolls — which is what
@@ -3519,7 +3626,7 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
React.useEffect(() => { React.useEffect(() => {
const vv = (typeof window !== 'undefined') ? window.visualViewport : null; const vv = (typeof window !== 'undefined') ? window.visualViewport : null;
const root = document.documentElement; const root = document.documentElement;
let lastH = -1, lastInset = ''; let lastH = -1, lastInset = '', lastY = -1, raf = 0;
// HEIGHT changes only on resize. It deliberately does NOT follow // HEIGHT changes only on resize. It deliberately does NOT follow
// `scroll`: on iOS the visual viewport also moves while the URL bar // `scroll`: on iOS the visual viewport also moves while the URL bar
@@ -3530,25 +3637,75 @@ import { GroupCallMedia, mediaErrorCode } from './group/groupCallMedia.js';
const h = Math.round(vv ? vv.height : (window.innerHeight || 0)); const h = Math.round(vv ? vv.height : (window.innerHeight || 0));
if (h && h !== lastH) { lastH = h; root.style.setProperty('--sb-vh', h + 'px'); } if (h && h !== lastH) { lastH = h; root.style.setProperty('--sb-vh', h + 'px'); }
}; };
// True while something the browser drew — in practice the on-screen
// keyboard — is covering a large part of the window.
const keyboardUp = () => !!vv && (window.innerHeight - vv.height) > 120;
// The home-indicator inset must collapse while the keyboard is up. // The home-indicator inset must collapse while the keyboard is up.
// Once the shell has shrunk to the visible area, that padding is no // Once the shell has shrunk to the visible area, that padding is no
// longer clearing the indicator — it is just dead space between the // longer clearing the indicator — it is just dead space between the
// composer and the keyboard. // composer and the keyboard.
const applyInset = () => { const applyInset = () => {
const covered = !!vv && (window.innerHeight - vv.height) > 120; const covered = keyboardUp();
const v = covered ? '0px' : 'env(safe-area-inset-bottom, 0px)'; const v = covered ? '0px' : 'env(safe-area-inset-bottom, 0px)';
if (v !== lastInset) { lastInset = v; root.style.setProperty('--sb-safe-bottom', v); } if (v !== lastInset) { lastInset = v; root.style.setProperty('--sb-safe-bottom', v); }
}; };
const apply = () => { applyHeight(); applyInset(); }; // WHERE the visible rect is, as opposed to how tall it is. iOS pans
// the visual viewport down inside the layout viewport to reveal a
// focused field, and a shell that only knows its height is left
// behind — off the top of the screen, which is the bug this fixes.
// Only a transform follows this (see --sb-vv-top in the shell rule):
// it is the half cheap enough to run on every scroll event, and rAF
// collapses a burst of them into one write per frame.
//
// The one case that must NOT be followed is a pinch-zoomed page,
// which pans too — chasing that drags the shell out from under
// whoever is reading. `scale` says so directly. An earlier version
// of this guard asked instead whether the window was much taller
// than the visual viewport, i.e. "is a keyboard covering us", which
// assumes iOS never shrinks the layout viewport — true until it
// isn't, and when it isn't the guard silently turns the whole fix
// off. offsetTop is only ever non-zero when something moved us, so
// it needs no permission to be believed.
const applyOffset = () => {
const zoomed = !!vv && vv.scale > 1.01;
const y = (vv && !zoomed) ? Math.max(0, Math.round(vv.offsetTop)) : 0;
if (y !== lastY) { lastY = y; root.style.setProperty('--sb-vv-top', y + 'px'); }
};
const apply = () => { applyHeight(); applyInset(); applyOffset(); };
const onPan = () => {
if (raf) return;
raf = requestAnimationFrame(() => { raf = 0; applyOffset(); });
};
// Focus is the other notice we get, and on iOS 26 it is the more
// reliable one: the pan that reveals a focused field can land after
// the last resize event, and on dismissal the viewport is documented
// not to settle back for a moment. So the offset is re-read a few
// times across the keyboard's own animation rather than once.
const timers = [];
const resettle = () => {
applyOffset();
[60, 180, 360, 600].forEach((ms) => timers.push(setTimeout(() => { applyHeight(); applyInset(); applyOffset(); }, ms)));
};
apply(); apply();
if (vv) vv.addEventListener('resize', apply); if (vv) { vv.addEventListener('resize', apply); vv.addEventListener('scroll', onPan); }
window.addEventListener('resize', apply); window.addEventListener('resize', apply);
window.addEventListener('orientationchange', apply); window.addEventListener('orientationchange', apply);
window.addEventListener('scroll', onPan, { passive: true });
document.addEventListener('focusin', resettle);
document.addEventListener('focusout', resettle);
const stopDebug = startViewportDebug(vv);
return () => { return () => {
if (vv) vv.removeEventListener('resize', apply); if (raf) cancelAnimationFrame(raf);
timers.forEach(clearTimeout);
if (vv) { vv.removeEventListener('resize', apply); vv.removeEventListener('scroll', onPan); }
window.removeEventListener('resize', apply); window.removeEventListener('resize', apply);
window.removeEventListener('orientationchange', apply); window.removeEventListener('orientationchange', apply);
window.removeEventListener('scroll', onPan);
document.removeEventListener('focusin', resettle);
document.removeEventListener('focusout', resettle);
if (stopDebug) stopDebug();
}; };
}, []); }, []);
const [relayOnlyMode, setRelayOnlyMode] = React.useState(() => { const [relayOnlyMode, setRelayOnlyMode] = React.useState(() => {
+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 // Build stamp — rewritten by scripts/post-build.js on every release so this file's
// bytes change each deploy. That is what makes the browser detect a new Service Worker, // bytes change each deploy. That is what makes the browser detect a new Service Worker,
// reinstall it, drop stale caches and (via controllerchange) prompt the page to update. // reinstall it, drop stale caches and (via controllerchange) prompt the page to update.
const SW_BUILD_VERSION = '1789181644117'; const SW_BUILD_VERSION = '1789190079159';
// Locale subdirectories, rewritten by scripts/build-i18n.js. Each localized page is a // Locale subdirectories, rewritten by scripts/build-i18n.js. Each localized page is a
// separate document at its own URL, so the shell has to be cached and served per // separate document at its own URL, so the shell has to be cached and served per
+38 -17
View File
@@ -38,12 +38,24 @@ assert.ok(app.includes('env(safe-area-inset-bottom'),
'the composer must pad for the home indicator'); 'the composer must pad for the home indicator');
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// the shell is sized, not pinned // the shell is sized AND pinned to the visible rect
// //
// An earlier fix pinned the shell with position:fixed and chased the visual // Sizing came first, and on its own it was not enough. Reported from iOS Safari,
// viewport offset from JS. That is the wrong instrument — the reference iOS chat // with a screenshot: tap the composer, the keyboard comes up, and the entire app
// implementations drive HEIGHT from visualViewport and leave positioning alone — // has slid off the top of the screen — the bottom sliver of the composer at the
// and it made the layout feel nailed down instead of laid out. // very top, black underneath it all the way down to the keyboard, header gone.
//
// That is the visual viewport being PANNED. WebKit shrinks it for the keyboard
// and then moves it down inside the layout viewport (which it never shrinks) to
// reveal the focused field. The shell had shrunk correctly, to the top --sb-vh of
// the layout viewport; the window had simply moved off it. No CSS declines that
// pan, and it is not a document scroll, so there is no scrollTop to put back.
//
// So the shell is fixed to the layout viewport and translated by
// visualViewport.offsetTop. The earlier objection to pinning is still honoured
// where it was right: HEIGHT never moves on a scroll event (that is what made the
// layout twitch under the finger), and the offset rides a transform rather than
// `top`. Off iOS nothing pans, --sb-vv-top stays 0, and this is inert.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
assert.match(app, /\.sb-app-shell\{height:var\(--sb-vh,100dvh\) !important/, assert.match(app, /\.sb-app-shell\{height:var\(--sb-vh,100dvh\) !important/,
'the shell must be sized from the visual viewport, with a dvh fallback'); 'the shell must be sized from the visual viewport, with a dvh fallback');
@@ -62,13 +74,12 @@ assert.match(app, /\.sb-app-shell\{[^}]*min-height:0 !important/,
'--sb-vh can never shrink it and the whole viewport-tracking path is inert'); '--sb-vh can never shrink it and the whole viewport-tracking path is inert');
assert.match(app, /\.sb-app-col\{[^}]*min-height:0 !important/, assert.match(app, /\.sb-app-col\{[^}]*min-height:0 !important/,
'the column carries .minimal-bg too and needs the same floor removed'); 'the column carries .minimal-bg too and needs the same floor removed');
assert.ok(!/\.sb-app-shell\{position:fixed/.test(app), assert.match(app, /\.sb-app-shell\{[^}]*position:fixed/,
'the shell must not be position:fixed: a fixed box is laid out against the ' + 'the shell must be pinned to the layout viewport, or an iOS pan leaves it ' +
'layout viewport, which iOS never shrinks for the keyboard, so it has to be ' + 'off the top of the screen with the keyboard up');
'chased with JS forever'); assert.match(app, /\.sb-app-shell\{[^}]*transform:translate3d\(0,var\(--sb-vv-top, 0px\),0\)/,
assert.ok(!/--sb-vv-top/.test(app), 'the pin must follow visualViewport.offsetTop, and it must do so with a ' +
'chasing visualViewport.offsetTop belongs to the pinned approach and should ' + 'transform — `top` on a fixed box relayouts the whole shell every frame');
'be gone with it');
assert.ok(!/sb-scroll-locked/.test(app), assert.ok(!/sb-scroll-locked/.test(app),
'the body scroll lock existed only to stop rubber-banding behind a pinned ' + 'the body scroll lock existed only to stop rubber-banding behind a pinned ' +
'shell; without the pin it just breaks normal scrolling'); 'shell; without the pin it just breaks normal scrolling');
@@ -122,14 +133,24 @@ assert.match(app, /var\(--sb-safe-bottom, env\(safe-area-inset-bottom, 0px\)\)/,
{ {
const i = app.indexOf('const applyHeight = '); const i = app.indexOf('const applyHeight = ');
assert.notEqual(i, -1, 'height tracking must exist'); assert.notEqual(i, -1, 'height tracking must exist');
const block = app.slice(i, i + 2500); const block = app.slice(i, i + 6000);
assert.match(block, /vv\.addEventListener\('resize', apply\)/, assert.match(block, /vv\.addEventListener\('resize', apply\)/,
'height must be recomputed on resize'); 'height must be recomputed on resize');
assert.ok(!/vv\.addEventListener\('scroll'/.test(block), assert.ok(!/vv\.addEventListener\('scroll', apply\)/.test(block),
'nothing may be recomputed on visualViewport scroll: on iOS that fires ' + 'HEIGHT must not be recomputed on visualViewport scroll: on iOS that ' +
'while the URL bar collapses and during rubber-banding, and resizing the ' + 'fires while the URL bar collapses and during rubber-banding, and ' +
'shell there is what made the layout twitch under the finger'); 'resizing the shell there is what made the layout twitch under the finger');
assert.match(block, /vv\.addEventListener\('scroll', onPan\)/,
'the pan, on the other hand, must be followed on scroll — that event is ' +
'the only notice iOS gives that the visible rect has moved');
assert.match(block, /requestAnimationFrame\(\(\) => \{ raf = 0; applyOffset\(\); \}\)/,
'a burst of scroll events must collapse into one style write per frame');
assert.match(block, /const zoomed = !!vv && vv\.scale > 1\.01/,
'the only thing that may switch the pan off is a pinch-zoomed page. It ' +
'must not be gated on the window being taller than the visual viewport: ' +
'that assumes iOS never shrinks the layout viewport, and the day it does ' +
'the guard turns the whole fix off without a word');
// Redundant writes cause a style recalculation on every event. // Redundant writes cause a style recalculation on every event.
assert.match(block, /h !== lastH/, 'skip no-op height writes'); assert.match(block, /h !== lastH/, 'skip no-op height writes');
+1 -1
View File
@@ -212,7 +212,7 @@ const template = read('templates/index.template.html');
// Layout variables that predate this file and are declared elsewhere. // Layout variables that predate this file and are declared elsewhere.
const elsewhere = new Set([ const elsewhere = new Set([
'--sb-press', '--sb-settle', '--sb-bar-h', '--sb-bar-extra', '--sb-press', '--sb-settle', '--sb-bar-h', '--sb-bar-extra',
'--sb-safe-top', '--sb-safe-bottom', '--sb-vh', '--sb-safe-top', '--sb-safe-bottom', '--sb-vh', '--sb-vv-top',
]); ]);
const sources = [ const sources = [
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/uk/manifest.json"> <link rel="manifest" href="/uk/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/uk.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/uk.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/ur/manifest.json"> <link rel="manifest" href="/ur/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/ur.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/ur.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -30,18 +30,18 @@
<!-- PWA Manifest --> <!-- PWA Manifest -->
<link rel="manifest" href="/zh/manifest.json"> <link rel="manifest" href="/zh/manifest.json">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html> <!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
from localStorage while the parser is still in <head>, so the page paints in the from localStorage while the parser is still in <head>, so the page paints in the
chosen theme on the first frame instead of flashing dark and correcting itself. chosen theme on the first frame instead of flashing dark and correcting itself.
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). --> Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
<script src="/src/scripts/theme-boot.js?v=1789181644117"></script> <script src="/src/scripts/theme-boot.js?v=1789190079159"></script>
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is <!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
not replayed, so the listener has to exist before Chrome decides the page not replayed, so the listener has to exist before Chrome decides the page
is installable. Deferred/module scripts are already too late. --> is installable. Deferred/module scripts are already too late. -->
<script src="/src/scripts/pwa-install-capture.js?v=1789181644117"></script> <script src="/src/scripts/pwa-install-capture.js?v=1789190079159"></script>
<!-- PWA Meta Tags --> <!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
@@ -116,7 +116,7 @@
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
<!-- Apple Touch Icons --> <!-- Apple Touch Icons -->
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1789190079159">
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
@@ -125,7 +125,7 @@
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789181644117"> <link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1789190079159">
<!-- Microsoft Tiles --> <!-- Microsoft Tiles -->
<meta name="msapplication-TileColor" content="#ff6b35"> <meta name="msapplication-TileColor" content="#ff6b35">
@@ -264,7 +264,7 @@
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts <!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
both execute in document order after parsing, so React still runs before the both execute in document order after parsing, so React still runs before the
app modules below, but the parser / first paint is no longer blocked. --> app modules below, but the parser / first paint is no longer blocked. -->
<script defer src="/config/ice-servers.js?v=1789181644117"></script> <script defer src="/config/ice-servers.js?v=1789190079159"></script>
<script defer src="/libs/react/react.production.min.js"></script> <script defer src="/libs/react/react.production.min.js"></script>
<script defer src="/libs/react-dom/react-dom.production.min.js"></script> <script defer src="/libs/react-dom/react-dom.production.min.js"></script>
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only — <!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
@@ -278,8 +278,8 @@
the end of <body>: 21 KB of CSS in total, but eight round trips before the the end of <body>: 21 KB of CSS in total, but eight round trips before the
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
connection. Bytes were never the problem; requests were. --> connection. Bytes were never the problem; requests were. -->
<link rel="stylesheet" href="/assets/app.css?v=1789181644117"> <link rel="stylesheet" href="/assets/app.css?v=1789190079159">
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789181644117"> <link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1789190079159">
<!-- Preload only the fonts needed for first paint. Inter is one variable file that <!-- Preload only the fonts needed for first paint. Inter is one variable file that
answers for every weight, so there is one to preload rather than the two of five answers for every weight, so there is one to preload rather than the two of five
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
@@ -287,19 +287,19 @@
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. --> <!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
<script defer src="/src/scripts/load-async-css.js?v=1789181644117"></script> <script defer src="/src/scripts/load-async-css.js?v=1789190079159"></script>
<noscript> <noscript>
<link rel="stylesheet" href="/libs/prism/prism.css"> <link rel="stylesheet" href="/libs/prism/prism.css">
</noscript> </noscript>
<script defer src="/src/scripts/fa-check.js?v=1789181644117"></script> <script defer src="/src/scripts/fa-check.js?v=1789190079159"></script>
<!-- This locale's dictionary, registered before anything can ask for a string. <!-- This locale's dictionary, registered before anything can ask for a string.
Only the default locale's is bundled (src/i18n/index.js imports it, because t() Only the default locale's is bundled (src/i18n/index.js imports it, because t()
falls back to it); every other page loads its own here instead of all thirteen falls back to it); every other page loads its own here instead of all thirteen
riding along inside dist/app.js. Absent on the default locale's own page. --> riding along inside dist/app.js. Absent on the default locale's own page. -->
<script type="module" src="/src/i18n/dict/zh.js?v=1789181644117"></script> <script type="module" src="/src/i18n/dict/zh.js?v=1789190079159"></script>
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script defer src="/src/utils/updateManager.js?v=1789181644117"></script> <script defer src="/src/utils/updateManager.js?v=1789190079159"></script>
<script type="module" src="/src/components/UpdateChecker.jsx?v=1789181644117"></script> <script type="module" src="/src/components/UpdateChecker.jsx?v=1789190079159"></script>
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the <!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
app has mounted and the browser is idle, so the 142 KB it weighs is off the app has mounted and the browser is idle, so the 142 KB it weighs is off the
first load. src/components/QRScanner.js used to be here too and registered first load. src/components/QRScanner.js used to be here too and registered
@@ -487,12 +487,12 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/dist/app-boot.js?v=1789181644117"></script> <script type="module" src="/dist/app-boot.js?v=1789190079159"></script>
<script type="module" src="/dist/app.js?v=1789181644117"></script> <script type="module" src="/dist/app.js?v=1789190079159"></script>
<script defer src="/src/scripts/pwa-register.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-register.js?v=1789190079159"></script>
<script src="/src/pwa/install-prompt.js?v=1789181644117" type="module"></script> <script src="/src/pwa/install-prompt.js?v=1789190079159" type="module"></script>
<script src="/src/pwa/pwa-manager.js?v=1789181644117" type="module"></script> <script src="/src/pwa/pwa-manager.js?v=1789190079159" type="module"></script>
<script defer src="/src/scripts/pwa-offline-test.js?v=1789181644117"></script> <script defer src="/src/scripts/pwa-offline-test.js?v=1789190079159"></script>
</body> </body>
</html> </html>