v6.8.5: the relay password is no longer built into the app
This commit is contained in:
+9
-14
@@ -1,21 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## v6.8.4 — The relay password now changes every day
|
||||
## v6.8.5 — The relay password is no longer built into the app
|
||||
|
||||
When two people cannot connect directly, the app routes the call through our own
|
||||
relay server. Until now every copy of the app carried the same password for that
|
||||
server, valid for years, and anyone could copy it and push their own traffic
|
||||
through it. The site now hands each app a fresh password that runs out after a
|
||||
day, and only gives them out at a limited pace. Nothing changes for you: the app
|
||||
picks up a new one on its own, including during long conversations.
|
||||
When two people cannot connect directly, calls and chats go through our relay
|
||||
server. Until now every copy of the app carried the same password for it, valid
|
||||
for years, so anyone could copy it and send their own traffic through our server.
|
||||
That password is gone. The app now asks the site for a new one that works for a
|
||||
day and renews it by itself, including during long conversations. The site hands
|
||||
them out only at a limited pace. You do not need to do anything.
|
||||
|
||||
## v6.8.3 — Private project files are no longer published with the site
|
||||
|
||||
A few files meant only for the developer's own machine — local editor settings and
|
||||
tool notes — were being uploaded along with the website and could be opened by anyone
|
||||
who knew the address. They held no passwords or keys, but they had no business being
|
||||
public. They are now left out of every deploy, along with anything else kept private
|
||||
in the project.
|
||||
A few files meant only for the developer's own computer were being published
|
||||
along with the site. They held no passwords, but they are no longer published.
|
||||
|
||||
## v6.8.2 — The keyboard no longer pushes the chat off the screen
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
No accounts. No servers storing your messages. No installation required.
|
||||
|
||||
[](LICENSE)
|
||||
[](CHANGELOG.md)
|
||||
[](CHANGELOG.md)
|
||||
[](https://snapcraft.io/securebit-chat)
|
||||
[](#install-as-an-app)
|
||||
[](#security-model)
|
||||
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/ar/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/ar.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/ar.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
// Production ICE override baked into the Fly.io image (no secrets — public STUN
|
||||
// only). The Dockerfile copies this to config/ice-servers.js, which is otherwise
|
||||
// only; the relay is added at runtime with a short-lived password). The Dockerfile copies this to config/ice-servers.js, which is otherwise
|
||||
// git-ignored. Users who want a TURN relay can add one via "Advanced network
|
||||
// settings"; to ship an operator TURN here, add it below (TURN credentials are
|
||||
// visible to every browser, so rotate them if exposed).
|
||||
@@ -12,37 +12,9 @@ window.SECUREBIT_ICE_SERVERS = [
|
||||
// hostnames inside their WebRTC layer even though normal page DNS works, so they
|
||||
// gather zero srflx/relay candidates and can't connect. Reaching the server by IP
|
||||
// bypasses that. Harmless to other browsers.
|
||||
{ urls: 'stun:144.172.96.126:3478' },
|
||||
{
|
||||
// SecureBit self-hosted coturn relay (turn.securebit.chat) — used only when
|
||||
// direct P2P fails (strict NAT / STUN limits). Media stays E2E-encrypted over
|
||||
// DTLS; the relay forwards only ciphertext. Multiple transports are offered so
|
||||
// every browser finds one that works:
|
||||
// - turn:3478?transport=udp — plain UDP, the most universally compatible
|
||||
// transport (notably the one Safari reliably gathers relay candidates on).
|
||||
// - turn:3478?transport=tcp — TCP fallback when UDP is blocked.
|
||||
// - turns:443?transport=tcp — TURN over TLS on 443, bypasses restrictive
|
||||
// firewalls that only allow HTTPS.
|
||||
//
|
||||
// The credential below is a long-lived token derived from the server's TURN
|
||||
// REST API secret (static-auth-secret) — username is "<expiry-unix-ts>:label".
|
||||
// Expiry 2147483647 = 2038-01-19 (max value coturn accepts; it parses the
|
||||
// timestamp as int32). The master secret itself never leaves the server; only
|
||||
// this derived username/credential pair is public (by design, like any
|
||||
// browser-side TURN credential). To revoke/renew, rotate the server's
|
||||
// static-auth-secret and re-issue with generate_turn_credentials.py.
|
||||
urls: [
|
||||
'turn:turn.securebit.chat:3478?transport=udp',
|
||||
'turn:turn.securebit.chat:3478?transport=tcp',
|
||||
// Raw-IP TURN relay — the DNS-bypass fallback that lets Safari (and any client
|
||||
// whose WebRTC layer can't resolve the hostname) obtain a relay candidate.
|
||||
// Plain turn: (no TLS) so no cert/hostname check is needed; the REST-API
|
||||
// credential is host-independent, and relayed traffic is already E2E ciphertext.
|
||||
'turn:144.172.96.126:3478?transport=udp',
|
||||
'turn:144.172.96.126:3478?transport=tcp',
|
||||
'turns:turn.securebit.chat:443?transport=tcp'
|
||||
],
|
||||
username: '2147483647:securebit',
|
||||
credential: 'tNrw3h2p/+OE0uLMLVc+ech7T6o='
|
||||
}
|
||||
{ urls: 'stun:144.172.96.126:3478' }
|
||||
// SecureBit's own TURN relay (turn.securebit.chat) is deliberately not listed
|
||||
// here. It no longer has a built-in password: the app asks the site for one
|
||||
// that expires after a day (POST /api/turn-credentials) and adds the relay to
|
||||
// this list itself — see src/network/turnCredentials.js.
|
||||
];
|
||||
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/de/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/de.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/de.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+4
-4
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
+2
-2
@@ -7,7 +7,7 @@ this document describes.
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| Release | v6.8.4 |
|
||||
| Release | v6.8.5 |
|
||||
| Protocol version | 4.1 |
|
||||
| Ratchet wire version | 1 |
|
||||
|
||||
@@ -347,5 +347,5 @@ no leg may stop a capture it borrowed.
|
||||
|
||||
## Scope
|
||||
|
||||
This describes the browser implementation as it stands in v6.8.4. It is not a
|
||||
This describes the browser implementation as it stands in v6.8.5. It is not a
|
||||
substitute for independent cryptographic review.
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td>Release</td>
|
||||
<td>v6.8.4</td>
|
||||
<td>v6.8.5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Protocol version</td>
|
||||
@@ -486,7 +486,7 @@
|
||||
shared across every leg, and stopped when the call, the group or the tab ends;
|
||||
no leg may stop a capture it borrowed.</p>
|
||||
<h2 id="scope">Scope</h2>
|
||||
<p>This describes the browser implementation as it stands in v6.8.4. It is not a
|
||||
<p>This describes the browser implementation as it stands in v6.8.5. It is not a
|
||||
substitute for independent cryptographic review.</p>
|
||||
|
||||
<nav class="more">
|
||||
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/es/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/es.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/es.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/fa/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/fa.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/fa.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/fr/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/fr.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/fr.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/he/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/he.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/he.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/hi/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/hi.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/hi.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+18
-18
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,18 +287,18 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -486,12 +486,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/ko/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/ko.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/ko.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"version": "1790206279080",
|
||||
"buildVersion": "1790206279080",
|
||||
"appVersion": "6.8.4",
|
||||
"buildTime": "2026-09-23T23:31:19.119Z",
|
||||
"buildId": "1790206279080-99da907",
|
||||
"gitHash": "99da907",
|
||||
"version": "1790208243441",
|
||||
"buildVersion": "1790208243441",
|
||||
"appVersion": "6.8.5",
|
||||
"buildTime": "2026-09-24T00:04:03.480Z",
|
||||
"buildId": "1790208243441-f39e902",
|
||||
"gitHash": "f39e902",
|
||||
"generated": true,
|
||||
"generatedAt": "2026-09-23T23:31:19.120Z"
|
||||
"generatedAt": "2026-09-24T00:04:03.481Z"
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "securebit-chat",
|
||||
"version": "6.8.4",
|
||||
"version": "6.8.5",
|
||||
"description": "Secure P2P Communication Application with End-to-End Encryption",
|
||||
"main": "index.html",
|
||||
"scripts": {
|
||||
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/ru/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/ru.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/ru.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+23
-23
@@ -17,7 +17,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -37,7 +37,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -57,7 +57,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -77,7 +77,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -97,7 +97,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -117,7 +117,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -137,7 +137,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -157,7 +157,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -177,7 +177,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -197,7 +197,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -217,7 +217,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -237,7 +237,7 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
@@ -257,67 +257,67 @@
|
||||
<xhtml:link rel="alternate" hreflang="fa" href="https://securebit.chat/fa/"/>
|
||||
<xhtml:link rel="alternate" hreflang="ur" href="https://securebit.chat/ur/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://securebit.chat/"/>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/faq/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/architecture/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/cryptography/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/descriptor-sbq2/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/configuration/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/calls/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/api/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/contributing/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://securebit.chat/docs/use-policy/</loc>
|
||||
<lastmod>2026-09-23</lastmod>
|
||||
<lastmod>2026-09-24</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
// connection — and the next in-band ICE restart, which re-reads the list — picks
|
||||
// up the new credential without anything else having to know it changed.
|
||||
//
|
||||
// If the endpoint cannot be reached, nothing is changed and the entry keeps the
|
||||
// credential it shipped with. Only our relay's entries are touched; a TURN server
|
||||
// the user configured themselves is never modified.
|
||||
// The web build ships no relay entry at all: a TURN entry without a credential
|
||||
// is refused by RTCPeerConnection, and a built-in credential is exactly what this
|
||||
// replaced. So the first successful fetch ADDS the entry, and later ones renew it.
|
||||
// Until then — or if the site cannot be reached — connections use STUN only.
|
||||
// Only our relay's entries are touched; a TURN server the user configured
|
||||
// themselves is never modified.
|
||||
|
||||
const ENDPOINT = '/api/turn-credentials';
|
||||
const OWN_RELAY_HOSTS = ['turn.securebit.chat', '144.172.96.126'];
|
||||
@@ -42,7 +45,8 @@ function isCleanField(value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a fresh credential into every own-relay entry of `list`, in place.
|
||||
* Write a fresh credential into every own-relay entry of `list`, in place. When
|
||||
* the list has no such entry yet and `cred.urls` names our relay, one is added.
|
||||
* @returns {boolean} whether anything was updated
|
||||
*/
|
||||
export function applyTurnCredentials(list, cred) {
|
||||
@@ -56,6 +60,15 @@ export function applyTurnCredentials(list, cred) {
|
||||
entry.credential = cred.credential;
|
||||
updated = true;
|
||||
}
|
||||
if (!updated && Array.isArray(cred.urls)) {
|
||||
// Only our relay's addresses may come from the response: the endpoint
|
||||
// is trusted to renew a password, not to point connections elsewhere.
|
||||
const urls = cred.urls.filter((u) => isCleanField(u) && OWN_RELAY_HOSTS.includes(relayHost(u)));
|
||||
if (urls.length > 0) {
|
||||
list.push({ urls, username: cred.username, credential: cred.credential });
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -68,6 +81,7 @@ export function parseCredentialResponse(body, nowSeconds) {
|
||||
if (!Number.isFinite(expiry) || expiry <= nowSeconds) return null;
|
||||
const ttl = Number(body.ttl);
|
||||
return {
|
||||
urls: Array.isArray(server.urls) ? server.urls.slice(0, 8) : [],
|
||||
username: server.username,
|
||||
credential: server.credential,
|
||||
expiry,
|
||||
|
||||
@@ -11,7 +11,7 @@ let DYNAMIC_CACHE = 'securebit-pwa-dynamic-v4.7.56';
|
||||
// Build stamp — rewritten by scripts/post-build.js on every release so this file's
|
||||
// bytes change each deploy. That is what makes the browser detect a new Service Worker,
|
||||
// reinstall it, drop stale caches and (via controllerchange) prompt the page to update.
|
||||
const SW_BUILD_VERSION = '1790206279080';
|
||||
const SW_BUILD_VERSION = '1790208243441';
|
||||
|
||||
// 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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import crypto from 'node:crypto';
|
||||
import endpoint from '../deploy/turn-credentials.js';
|
||||
import { applyTurnCredentials, isOwnRelayEntry } from '../src/network/turnCredentials.js';
|
||||
import { applyTurnCredentials, isOwnRelayEntry, parseCredentialResponse } from '../src/network/turnCredentials.js';
|
||||
|
||||
const SECRET = 'test-secret';
|
||||
const NOW = 1_800_000_000;
|
||||
@@ -89,4 +89,43 @@ const ownEntry = () => ({
|
||||
assert.equal(own.credential, 'old');
|
||||
}
|
||||
|
||||
{
|
||||
// The web build ships no relay entry: the first fetch adds it.
|
||||
const stun = { urls: 'stun:stun.l.google.com:19302' };
|
||||
const list = [stun];
|
||||
const body = endpoint.decide({ method: 'POST', origin: 'https://securebit.chat', secret: SECRET, nowSeconds: NOW }).body;
|
||||
const cred = parseCredentialResponse(body, NOW);
|
||||
assert.ok(cred, 'a real endpoint response parses');
|
||||
assert.equal(applyTurnCredentials(list, cred), true);
|
||||
assert.equal(list.length, 2, 'the relay is added to the same array');
|
||||
assert.equal(list[0], stun);
|
||||
assert.deepEqual(list[1].urls, endpoint.TURN_URLS);
|
||||
assert.equal(list[1].credential, cred.credential);
|
||||
|
||||
// A second fetch renews that entry instead of adding another.
|
||||
const next = parseCredentialResponse(endpoint.decide({ method: 'POST', origin: undefined, secret: SECRET, nowSeconds: NOW + 60 }).body, NOW + 60);
|
||||
applyTurnCredentials(list, next);
|
||||
assert.equal(list.length, 2);
|
||||
assert.equal(list[1].username, next.username);
|
||||
|
||||
// The response may renew a password; it may not point us at another server.
|
||||
const hijack = [stun];
|
||||
assert.equal(applyTurnCredentials(hijack, { username: 'u:securebit', credential: 'c', urls: ['turn:relay.evil.example:3478'] }), false);
|
||||
assert.equal(hijack.length, 1);
|
||||
const mixed = [];
|
||||
applyTurnCredentials(mixed, { username: 'u:securebit', credential: 'c', urls: ['turn:relay.evil.example:3478', 'turn:turn.securebit.chat:3478'] });
|
||||
assert.deepEqual(mixed[0].urls, ['turn:turn.securebit.chat:3478']);
|
||||
|
||||
// Expired credentials are refused outright.
|
||||
assert.equal(parseCredentialResponse(body, NOW + endpoint.TTL_SECONDS + 1), null);
|
||||
}
|
||||
|
||||
// The shipped production config carries no relay password any more.
|
||||
{
|
||||
const fs = await import('node:fs');
|
||||
const prod = fs.readFileSync(new URL('../config/ice-servers.prod.js', import.meta.url), 'utf8');
|
||||
assert.doesNotMatch(prod, /credential\s*:/, 'config/ice-servers.prod.js must not ship a TURN password');
|
||||
assert.doesNotMatch(prod, /urls:\s*\[?\s*'turns?:/, 'the relay is added at runtime, not listed statically');
|
||||
}
|
||||
|
||||
console.log('turn-credentials.test.mjs: all assertions passed');
|
||||
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/uk/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/uk.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/uk.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/ur/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/ur.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/ur.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
+19
-19
@@ -30,18 +30,18 @@
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/zh/manifest.json">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
|
||||
<!-- Blocking, and above the stylesheet on purpose: this stamps data-theme on <html>
|
||||
from localStorage while the parser is still in <head>, so the page paints in the
|
||||
chosen theme on the first frame instead of flashing dark and correcting itself.
|
||||
Cannot be inline (CSP is script-src 'self') and cannot be a module (deferred). -->
|
||||
<script src="/src/scripts/theme-boot.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/theme-boot.js?v=1790208243441"></script>
|
||||
|
||||
<!-- Blocking, and in <head> on purpose: beforeinstallprompt fires once and is
|
||||
not replayed, so the listener has to exist before Chrome decides the page
|
||||
is installable. Deferred/module scripts are already too late. -->
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790206279080"></script>
|
||||
<script src="/src/scripts/pwa-install-capture.js?v=1790208243441"></script>
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@@ -116,7 +116,7 @@
|
||||
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="/logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/logo/icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/logo/icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/logo/icon-72x72.png">
|
||||
@@ -125,7 +125,7 @@
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/logo/icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/logo/icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/logo/icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790206279080">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/logo/icon-180x180.png?v=1790208243441">
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#ff6b35">
|
||||
@@ -264,7 +264,7 @@
|
||||
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
||||
both execute in document order after parsing, so React still runs before the
|
||||
app modules below, but the parser / first paint is no longer blocked. -->
|
||||
<script defer src="/config/ice-servers.js?v=1790206279080"></script>
|
||||
<script defer src="/config/ice-servers.js?v=1790208243441"></script>
|
||||
<script defer src="/libs/react/react.production.min.js"></script>
|
||||
<script defer src="/libs/react-dom/react-dom.production.min.js"></script>
|
||||
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
||||
@@ -278,8 +278,8 @@
|
||||
the end of <body>: 21 KB of CSS in total, but eight round trips before the
|
||||
browser could paint, which GTmetrix measured at 441 ms on a throttled mobile
|
||||
connection. Bytes were never the problem; requests were. -->
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790206279080">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790206279080">
|
||||
<link rel="stylesheet" href="/assets/app.css?v=1790208243441">
|
||||
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1790208243441">
|
||||
<!-- Preload only the fonts needed for first paint. Inter is one variable file that
|
||||
answers for every weight, so there is one to preload rather than the two of five
|
||||
copies this used to name. fa-solid covers the bulk of UI icons; fa-regular and
|
||||
@@ -287,19 +287,19 @@
|
||||
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.subset.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="/assets/fonts/inter/files/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/load-async-css.js?v=1790208243441"></script>
|
||||
<noscript>
|
||||
<link rel="stylesheet" href="/libs/prism/prism.css">
|
||||
</noscript>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/fa-check.js?v=1790208243441"></script>
|
||||
<!-- This locale's dictionary, registered before anything can ask for a string.
|
||||
Only the default locale's is bundled (src/i18n/index.js imports it, because t()
|
||||
falls back to it); every other page loads its own here instead of all thirteen
|
||||
riding along inside dist/app.js. Absent on the default locale's own page. -->
|
||||
<script type="module" src="/src/i18n/dict/zh.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/i18n/dict/zh.js?v=1790208243441"></script>
|
||||
<!-- Update Manager - система принудительного обновления -->
|
||||
<script defer src="/src/utils/updateManager.js?v=1790206279080"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790206279080"></script>
|
||||
<script defer src="/src/utils/updateManager.js?v=1790208243441"></script>
|
||||
<script type="module" src="/src/components/UpdateChecker.jsx?v=1790208243441"></script>
|
||||
<!-- /dist/qr-local.js is deliberately absent: app-boot.js fetches it once the
|
||||
app has mounted and the browser is idle, so the 142 KB it weighs is off the
|
||||
first load. src/components/QRScanner.js used to be here too and registered
|
||||
@@ -487,12 +487,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790206279080"></script>
|
||||
<script type="module" src="/dist/app-boot.js?v=1790208243441"></script>
|
||||
<script type="module" src="/dist/app.js?v=1790208243441"></script>
|
||||
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790206279080"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790206279080" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790206279080" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790206279080"></script>
|
||||
<script defer src="/src/scripts/pwa-register.js?v=1790208243441"></script>
|
||||
<script src="/src/pwa/install-prompt.js?v=1790208243441" type="module"></script>
|
||||
<script src="/src/pwa/pwa-manager.js?v=1790208243441" type="module"></script>
|
||||
<script defer src="/src/scripts/pwa-offline-test.js?v=1790208243441"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user