universal-runtime: replace status badge with centered overlay Подготовка приложения...

This commit is contained in:
2026-07-20 15:45:50 +00:00
parent f61953f32a
commit 19a9764d8e
+55 -56
View File
@@ -20,25 +20,26 @@ cat > /opt/portal/index.html <<'HTML'
<title>Universal Session</title>
<style>
html,body,#screen{margin:0;height:100%;background:#111}
.status{
position:fixed;
left:12px;
top:12px;
z-index:50;
padding:8px 10px;
border-radius:8px;
background:rgba(16,22,32,.86);
border:1px solid rgba(255,255,255,.18);
color:#dce8f5;
font:600 13px/1.25 sans-serif;
max-width:min(92vw,560px);
#co{
position:fixed;inset:0;z-index:50;background:rgba(8,14,24,.95);
display:flex;align-items:center;justify-content:center;
transition:opacity .5s;
}
.status.error{
background:rgba(85,20,20,.9);
border-color:rgba(255,130,130,.36);
color:#ffe3e3;
#co.hidden{opacity:0;pointer-events:none}
.co-inner{display:flex;flex-direction:column;align-items:center;gap:20px;padding:40px}
.co-ring{
width:64px;height:64px;border-radius:50%;
border:4px solid rgba(220,232,245,.12);
border-top-color:#4a9de0;
animation:spin .9s linear infinite;
}
.co-title{color:#dce8f5;font:600 1.1rem/1.4 sans-serif;text-align:center;letter-spacing:.01em}
@keyframes spin{to{transform:rotate(360deg)}}
#status{
position:fixed;left:12px;top:12px;z-index:60;padding:8px 12px;border-radius:8px;
background:rgba(85,20,20,.9);border:1px solid rgba(255,130,130,.36);
color:#ffe3e3;font:600 13px/1.25 sans-serif;max-width:min(92vw,560px);display:none;
}
.status.hidden{display:none}
.nav-panel{
position:fixed;left:16px;top:64px;z-index:99;display:flex;gap:10px;
background:linear-gradient(180deg,rgba(15,24,36,.78),rgba(9,14,22,.86));border:1px solid rgba(255,255,255,.22);backdrop-filter: blur(5px);
@@ -56,7 +57,13 @@ cat > /opt/portal/index.html <<'HTML'
</head>
<body>
<div id="screen"></div>
<div id="status" class="status">Подключение к слоту...</div>
<div id="co">
<div class="co-inner">
<div class="co-ring"></div>
<div class="co-title">Подготовка приложения...</div>
</div>
</div>
<div id="status"></div>
<div class="nav-panel">
<button class="nav-btn" id="btn-back" type="button" title="Назад">←</button>
<button class="nav-btn" id="btn-home" type="button" title="Главная">⌂</button>
@@ -67,8 +74,9 @@ cat > /opt/portal/index.html <<'HTML'
const basePath = location.pathname.replace(/\/+$/, '');
const wsUrl = (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + basePath + '/websockify';
const statusEl = document.getElementById('status');
const coEl = document.getElementById('co');
const XK_ALT_L = 0xffe9;
const XK_LEFT = 0xff51;
const XK_LEFT = 0xff51;
let rfb = null;
let connected = false;
@@ -79,41 +87,40 @@ cat > /opt/portal/index.html <<'HTML'
const RECONNECT_DELAYS_MS = [1000, 2000, 3000, 5000, 8000];
let manualDisconnect = false;
function showStatus(text, isError = false) {
statusEl.textContent = text;
statusEl.classList.toggle('error', !!isError);
statusEl.classList.remove('hidden');
function showOverlay() {
coEl.style.opacity = '1';
coEl.style.display = 'flex';
coEl.classList.remove('hidden');
statusEl.style.display = 'none';
}
function hideStatus() {
statusEl.classList.add('hidden');
function hideOverlay() {
coEl.classList.add('hidden');
setTimeout(() => { coEl.style.display = 'none'; }, 520);
}
function showError(text) {
hideOverlay();
statusEl.textContent = text;
statusEl.style.display = 'block';
}
function clearConnectTimer() {
if (connectTimer) {
clearTimeout(connectTimer);
connectTimer = null;
}
if (connectTimer) { clearTimeout(connectTimer); connectTimer = null; }
}
function clearReconnectTimer() {
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
}
function reconnectDelay(attemptNumber) {
const idx = Math.min(Math.max(attemptNumber - 1, 0), RECONNECT_DELAYS_MS.length - 1);
return RECONNECT_DELAYS_MS[idx];
function reconnectDelay(n) {
return RECONNECT_DELAYS_MS[Math.min(Math.max(n - 1, 0), RECONNECT_DELAYS_MS.length - 1)];
}
function scheduleConnectTimeout() {
clearConnectTimer();
connectTimer = setTimeout(() => {
if (!connected && !manualDisconnect) {
scheduleReconnect('Нет подключения к экрану слота. Пробуем переподключиться...');
}
if (!connected && !manualDisconnect) scheduleReconnect('Нет подключения. Пробуем переподключиться...');
}, 8000);
}
@@ -121,51 +128,43 @@ cat > /opt/portal/index.html <<'HTML'
if (manualDisconnect) return;
clearConnectTimer();
clearReconnectTimer();
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
showStatus('Соединение со слотом потеряно. Переподключение не удалось. Откройте сервис заново.', true);
showError('Соединение потеряно. Переподключение не удалось. Откройте сервис заново.');
return;
}
const nextAttempt = reconnectAttempts + 1;
const delayMs = reconnectDelay(nextAttempt);
const delaySec = Math.ceil(delayMs / 1000);
showStatus(`${reasonText} Повтор ${nextAttempt}/${MAX_RECONNECT_ATTEMPTS} через ${delaySec} сек.`, true);
showError(\`\${reasonText} Повтор \${nextAttempt}/\${MAX_RECONNECT_ATTEMPTS} через \${Math.ceil(delayMs/1000)} сек.\`);
reconnectTimer = setTimeout(() => {
reconnectAttempts = nextAttempt;
connectRfb('Переподключение к слоту...');
connectRfb();
}, delayMs);
}
function attachRfb() {
if (rfb) {
try { rfb.disconnect(); } catch (e) {}
}
if (rfb) { try { rfb.disconnect(); } catch (e) {} }
rfb = new RFB(document.getElementById('screen'), wsUrl);
rfb.viewOnly = false;
rfb.scaleViewport = true;
rfb.resizeSession = true;
rfb.addEventListener('connect', () => {
connected = true;
reconnectAttempts = 0;
clearConnectTimer();
clearReconnectTimer();
hideStatus();
hideOverlay();
});
rfb.addEventListener('disconnect', () => {
connected = false;
if (manualDisconnect) return;
scheduleReconnect('Соединение со слотом потеряно.');
scheduleReconnect('Соединение потеряно.');
});
}
function connectRfb(statusText) {
function connectRfb() {
if (manualDisconnect) return;
connected = false;
showStatus(statusText || 'Подключение к слоту...');
showOverlay();
attachRfb();
scheduleConnectTimeout();
}
@@ -291,7 +290,7 @@ cat > /opt/portal/index.html <<'HTML'
});
})();
connectRfb('Подключение к слоту...');
connectRfb();
</script>
</body>
</html>