universal-runtime: replace status badge with centered overlay Подготовка приложения...
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user