From 18b04135abc613ddfc43f7e65407f48d124d5ab5 Mon Sep 17 00:00:00 2001 From: "Anton T." <61037544+Anton778@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:37:30 +0700 Subject: [PATCH 01/10] v8.1: add guided gateway setup, compact layout and ID roster --- js/v8.1-ui.js | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 js/v8.1-ui.js diff --git a/js/v8.1-ui.js b/js/v8.1-ui.js new file mode 100644 index 0000000..7bcf3cf --- /dev/null +++ b/js/v8.1-ui.js @@ -0,0 +1,366 @@ +"use strict"; + +/* ЦУП Альтаир v8.1 — компоновка рабочего экрана и сценарий первого запуска. */ +(() => { + const VERSION = "0.8.1"; + const GATEWAY_READY_KEY = "altair-v81-gateway-ready"; + const EXPECTED_IDS = Array.from({ length: 10 }, (_, index) => String(index + 1).padStart(2, "0")); + const seen = new Map(); + let gatewayReady = false; + let dashboardObserver = null; + let flasherObserver = null; + + function log(message, type = "info") { + window.OpenMCCLogger?.write?.(message, type, "V8.1"); + } + + function safeStorageGet(key) { + try { return localStorage.getItem(key); } catch { return null; } + } + + function safeStorageSet(key, value) { + try { localStorage.setItem(key, value); } catch {} + } + + function normalizeId(value) { + const raw = String(value ?? "").trim().toUpperCase(); + if (!raw) return "UNKNOWN"; + if (/^\d{1,2}$/.test(raw)) return raw.padStart(2, "0"); + return raw.slice(0, 16); + } + + function setBranding() { + document.title = "ЦУП Альтаир v8.1 — Миссия на Луну"; + const badge = document.querySelector(".buildBadge"); + if (badge) badge.textContent = `v8 · ${VERSION}`; + + const aboutVersion = document.querySelector("#v8AboutDialog .aboutHeader div > div"); + if (aboutVersion && /версия/i.test(aboutVersion.textContent || "")) { + aboutVersion.textContent = `Центр управления полётами · версия ${VERSION}`; + } + + const eventLogFirst = document.querySelector("#eventLog > div:first-child"); + if (eventLogFirst && /ЦУП Альтаир v8/i.test(eventLogFirst.textContent || "")) { + eventLogFirst.textContent = "ЦУП Альтаир v8.1 запущен."; + } + } + + function setGatewayReady(value, source = "manual") { + gatewayReady = Boolean(value); + if (gatewayReady) safeStorageSet(GATEWAY_READY_KEY, VERSION); + + const step = document.querySelector('[data-v81-step="flash"]'); + const state = document.getElementById("v81FlashState"); + const acknowledge = document.getElementById("v81AlreadyFlashed"); + if (step) step.classList.toggle("done", gatewayReady); + if (state) state.textContent = gatewayReady ? "шлюз готов" : "требуется прошивка или подтверждение"; + if (acknowledge) acknowledge.textContent = gatewayReady ? "Подтверждено" : "Шлюз уже прошит"; + + refreshConnectGate(); + if (gatewayReady && source !== "restore") log("Шлюз ESP32 отмечен как готовый к подключению.", "success"); + } + + function getConnected() { + return Boolean(window.OpenMCCSerial?.getState?.().connected); + } + + function refreshConnectGate() { + const original = document.getElementById("connectButton"); + const shortcut = document.getElementById("v81ConnectShortcut"); + const status = document.getElementById("v81ConnectionStepState"); + const connected = getConnected(); + + if (original) { + original.disabled = !gatewayReady && !connected; + original.title = gatewayReady || connected ? "" : "Сначала прошейте ESP32-шлюз или подтвердите, что он уже прошит."; + } + if (shortcut) { + shortcut.disabled = !gatewayReady && !connected; + shortcut.textContent = connected ? "Отключить" : "Подключить"; + } + if (status) { + if (connected) status.textContent = "соединение установлено"; + else status.textContent = gatewayReady ? "готово к подключению" : "ожидает шага 1"; + } + + const step = document.querySelector('[data-v81-step="connect"]'); + if (step) { + step.classList.toggle("ready", gatewayReady && !connected); + step.classList.toggle("done", connected); + } + } + + function installSetupFlow() { + const dashboard = document.getElementById("dashboard"); + if (!dashboard) return null; + + let flow = document.getElementById("v81SetupFlow"); + if (!flow) { + flow = document.createElement("section"); + flow.id = "v81SetupFlow"; + flow.className = "panel wide v81SetupFlow"; + flow.innerHTML = ` +
Серый — связи ещё не было. Цвет — ID хотя бы раз принят. «ПЕРЕДАЁТ» означает пакет за последние 5 с.
+ `; + layout.appendChild(aside); + } + EXPECTED_IDS.forEach(rosterEntry); + updateRoster(); + } + + function recordId(telemetry) { + if (!telemetry || typeof telemetry !== "object" || telemetry.ID === undefined) return; + const id = normalizeId(telemetry.ID); + const item = seen.get(id) || { count: 0, firstSeen: Date.now(), lastSeen: 0, color: null }; + item.count += 1; + item.lastSeen = Date.now(); + item.color = window.OpenMCCCharts?.colorForId?.(id) || item.color || "#00d9ff"; + seen.set(id, item); + rosterEntry(id); + updateRoster(); + } + + function updateRoster() { + const list = document.getElementById("v81IdRosterList"); + if (!list) return; + const now = Date.now(); + list.querySelectorAll(".v81RosterItem").forEach(item => { + const id = item.dataset.rosterId; + const info = seen.get(id); + const dot = item.querySelector(".v81RosterDot"); + const status = item.querySelector(".v81RosterStatus"); + const small = item.querySelector("small"); + item.classList.remove("never", "seen", "active"); + if (!info) { + item.classList.add("never"); + if (dot) dot.style.background = ""; + if (status) status.textContent = "НЕТ СВЯЗИ"; + if (small) small.textContent = "0 пак."; + return; + } + const active = now - info.lastSeen <= 5000; + item.classList.add("seen"); + if (active) item.classList.add("active"); + if (dot) dot.style.background = info.color; + if (status) status.textContent = active ? "ПЕРЕДАЁТ" : "БЫЛ НА СВЯЗИ"; + if (small) small.textContent = `${info.count} пак.`; + }); + const expectedSeen = EXPECTED_IDS.filter(id => seen.has(id)).length; + const extra = [...seen.keys()].filter(id => !EXPECTED_IDS.includes(id)).length; + const count = document.getElementById("v81RosterCount"); + if (count) count.textContent = extra ? `${expectedSeen}/${EXPECTED_IDS.length} +${extra}` : `${expectedSeen} / ${EXPECTED_IDS.length}`; + } + + function resetRoster() { + seen.clear(); + const list = document.getElementById("v81IdRosterList"); + if (list) { + list.innerHTML = ""; + EXPECTED_IDS.forEach(rosterEntry); + } + updateRoster(); + } + + function reorganizeDashboard() { + const dashboard = document.getElementById("dashboard"); + if (!dashboard) return; + const flow = installSetupFlow(); + const telemetry = document.getElementById("telemetryPanel"); + const charts = document.getElementById("chartPanel"); + const command = document.getElementById("commandPanel"); + const system = document.getElementById("systemPanel"); + const radio = document.getElementById("radioPanel"); + const firmware = document.getElementById("firmwarePanel"); + const serial = document.getElementById("serialPanel"); + const raw = document.getElementById("rawSerialPanel"); + const logPanel = document.getElementById("logPanel"); + + if (flow && telemetry) flow.insertAdjacentElement("afterend", telemetry); + if (telemetry && charts) telemetry.insertAdjacentElement("afterend", charts); + if (charts && command) charts.insertAdjacentElement("afterend", command); + if (command && system) command.insertAdjacentElement("afterend", system); + if (system && radio) system.insertAdjacentElement("afterend", radio); + if (radio && firmware) radio.insertAdjacentElement("afterend", firmware); + if (firmware && serial) firmware.insertAdjacentElement("afterend", serial); + if (logPanel && raw) logPanel.parentNode.insertBefore(raw, logPanel); + + makeSerialCompact(); + compactFirmwarePanel(); + installIdRoster(); + + const serialNav = document.querySelector('#quickNav [data-scroll-target="serialPanel"]'); + if (serialNav) { + serialNav.dataset.scrollTarget = "v81SetupFlow"; + serialNav.textContent = "Подготовка шлюза"; + serialNav.onclick = () => document.getElementById("v81SetupFlow")?.scrollIntoView({ behavior: "smooth", block: "start" }); + } + } + + function installObservers() { + const dashboard = document.getElementById("dashboard"); + if (!dashboard || dashboardObserver) return; + let timer = null; + dashboardObserver = new MutationObserver(() => { + clearTimeout(timer); + timer = setTimeout(reorganizeDashboard, 60); + }); + dashboardObserver.observe(dashboard, { childList: true }); + } + + function installEvents() { + window.addEventListener("openmcc:telemetry", event => recordId(event.detail)); + window.addEventListener("openmcc:v8-reset", resetRoster); + window.addEventListener("openmcc:serial-connected", () => { + refreshConnectGate(); + document.getElementById("serialPanel")?.classList.add("v8-collapsed"); + }); + window.addEventListener("openmcc:serial-disconnected", refreshConnectGate); + setInterval(updateRoster, 1000); + } + + function initialize() { + setBranding(); + reorganizeDashboard(); + installObservers(); + installEvents(); + setTimeout(() => { setBranding(); reorganizeDashboard(); }, 900); + log("Интерфейс v8.1 готов: прошивка → соединение, телеметрия и графики наверху, контроль ID активен.", "success"); + } + + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", () => setTimeout(initialize, 600), { once: true }); + else setTimeout(initialize, 600); +})(); From c8f9aee255127215acf1131e6b46e39ecf2dfcbe Mon Sep 17 00:00:00 2001 From: "Anton T." <61037544+Anton778@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:38:07 +0700 Subject: [PATCH 02/10] v8.1: compact header, guided setup and full-width telemetry layout --- css/v8.css | 290 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 185 insertions(+), 105 deletions(-) diff --git a/css/v8.css b/css/v8.css index 50b1d0e..43b50c5 100644 --- a/css/v8.css +++ b/css/v8.css @@ -1,16 +1,30 @@ -/* ЦУП Альтаир v8 */ +/* ЦУП Альтаир v8.1 */ .altairLogo .logoMark.v8LogoMark { - width: 42px; - height: 42px; + width: 46px; + height: 46px; border: 0; - background: rgba(255,255,255,.96); - border-radius: 10px; + background: transparent; + border-radius: 0; display: grid; place-items: center; - overflow: hidden; - box-shadow: 0 0 24px rgba(44,108,255,.25); + overflow: visible; + box-shadow: none; } -.altairLogo .logoMark.v8LogoMark img { width: 36px; height: 36px; object-fit: contain; } +.altairLogo .logoMark.v8LogoMark img { + width: 44px; + height: 44px; + object-fit: contain; + background: transparent; +} + +#topBar { + grid-template-columns: minmax(300px, 1.8fr) minmax(180px, .82fr) minmax(235px, 1fr) minmax(110px, .52fr) minmax(150px, .68fr) auto; + gap: 12px; + min-height: 70px; + padding-block: 10px; +} +#topBar .topActions { grid-row: 1; align-self: center; flex-wrap: nowrap; } +.buildBadge { white-space: nowrap; } .v8FrequencyMetric #v8FrequencyValue { color: #73e8ff; @@ -19,36 +33,136 @@ white-space: nowrap; } -.v8PanelToggle { +.v8PanelToggle, +.v81UtilityToggle { margin-left: auto; - min-width: 118px; + min-width: 106px; border: 1px solid rgba(79,180,255,.34); border-radius: 9px; background: rgba(8,29,52,.78); color: #a9dfff; - padding: 7px 10px; + padding: 6px 9px; cursor: pointer; } -.v8PanelToggle:hover { border-color: rgba(79,180,255,.7); color: #e3f7ff; } +.v8PanelToggle:hover, +.v81UtilityToggle:hover { border-color: rgba(79,180,255,.7); color: #e3f7ff; } #serialPanel.v8-collapsed > :not(.panelTitleRow):not(.v8ConnectionSummary) { display: none !important; } -.v8ConnectionSummary { - display: none; - gap: 8px; - flex-wrap: wrap; - margin-top: 8px; -} +#serialPanel.v8-collapsed { min-height: 0; padding-block: 9px; } +#serialPanel.v8-collapsed .panelTitleRow { margin: 0; align-items: center; } +#serialPanel.v8-collapsed .panelTitleRow h2 { margin: 1px 0 0; font-size: 15px; } +#serialPanel.v8-collapsed .panelEyebrow { font-size: 9px; } +.v8ConnectionSummary { display: none; gap: 6px; flex-wrap: wrap; margin-top: 6px; } #serialPanel.v8-collapsed .v8ConnectionSummary { display: flex; } .v8ConnectionSummary span { - border: 1px solid rgba(77,116,157,.34); - border-radius: 999px; - padding: 6px 10px; - color: #9fb6cf; - background: rgba(4,17,31,.65); - font-size: .78rem; + border: 0; + border-radius: 0; + padding: 0 8px 0 0; + color: #8198b2; + background: transparent; + font-size: .72rem; } .v8ConnectionSummary strong { color: #d9f5ff; } +#v81SetupFlow { + grid-column: span 12 !important; + padding: 12px 14px; + background: linear-gradient(90deg, rgba(0,217,255,.055), rgba(8,18,32,.82)); +} +.v81SetupTitle { display: flex; justify-content: space-between; align-items: end; gap: 16px; margin-bottom: 9px; } +.v81SetupTitle strong { display:block; margin-top:2px; color:#eaf8ff; font-size:16px; } +.v81SetupTitle > span { color:#6f91b1; font: 700 11px/1.2 Consolas,monospace; } +.v81SetupSteps { display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,1fr); gap: 10px; align-items: stretch; } +.v81SetupStep { + min-width: 0; + display: grid; + grid-template-columns: 34px minmax(140px,1fr) auto auto; + align-items: center; + gap: 9px; + padding: 9px 10px; + border: 1px solid rgba(69,105,143,.42); + border-radius: 11px; + background: rgba(4,14,26,.72); +} +.v81SetupStep.ready { border-color: rgba(0,217,255,.42); } +.v81SetupStep.done { border-color: rgba(58,219,143,.5); box-shadow: inset 0 0 0 1px rgba(58,219,143,.05); } +.v81StepNo { + width: 28px; + height: 28px; + display:grid; + place-items:center; + border-radius:50%; + background:rgba(0,217,255,.12); + color:#7deaff; + font:800 12px Consolas,monospace; +} +.v81SetupStep.done .v81StepNo { background:rgba(58,219,143,.15); color:#75efb0; } +.v81StepText { min-width:0; display:flex; flex-direction:column; gap:2px; } +.v81StepText strong { color:#dff6ff; font-size:13px; } +.v81StepText span { color:#8299b3; font-size:11px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } +.v81SetupArrow { display:grid; place-items:center; color:#4e789d; font-size:20px; } +.v81StepButton { + width:auto; + margin:0; + min-height:32px; + padding:6px 9px; + border-radius:8px; + border:1px solid rgba(65,103,142,.55); + background:#081a2c; + color:#a9bfd5; + cursor:pointer; + white-space:nowrap; +} +.v81StepButton.primary { border-color:rgba(0,217,255,.45); color:#b9f5ff; background:rgba(0,108,144,.18); } +.v81StepButton:disabled { opacity:.38; cursor:not-allowed; } + +#telemetryPanel, +#chartPanel { grid-column: span 12 !important; } +#telemetryPanel .altairTelemetryGrid { grid-template-columns: repeat(5, minmax(0,1fr)); } +#telemetryPanel .telemetryCard { min-height: 96px; } + +.v81ChartsLayout { display: grid; grid-template-columns: minmax(0,1fr) 238px; gap: 12px; align-items: stretch; } +.v81ChartsLayout .altairChartGrid { grid-template-columns: repeat(2, minmax(0,1fr)); } +.v81ChartsLayout .chartSummaryCard { grid-column: 1 / -1; } +#v81IdRoster { + min-width: 0; + border: 1px solid rgba(58,94,132,.45); + border-radius: 12px; + background: rgba(4,13,24,.78); + padding: 11px; +} +.v81RosterHeader { display:flex; align-items:flex-end; justify-content:space-between; gap:8px; margin-bottom:7px; } +.v81RosterHeader strong { display:block; color:#e4f6ff; font-size:14px; } +#v81RosterCount { color:#73e8ff; font:800 12px Consolas,monospace; } +#v81IdRoster > p { margin:0 0 9px; color:#7089a3; font-size:10px; line-height:1.4; } +#v81IdRosterList { display:grid; gap:4px; } +.v81RosterItem { + display:grid; + grid-template-columns: 9px 48px minmax(0,1fr) auto; + gap:7px; + align-items:center; + min-height:27px; + padding:4px 6px; + border-radius:7px; + background:rgba(10,24,40,.45); +} +.v81RosterDot { width:8px; height:8px; border-radius:50%; background:#43556a; box-shadow:none; } +.v81RosterItem strong { color:#9fb2c6; font:700 11px Consolas,monospace; } +.v81RosterStatus { color:#667d95; font-size:9px; font-weight:800; letter-spacing:.04em; } +.v81RosterItem small { color:#657b91; font:9px Consolas,monospace; white-space:nowrap; } +.v81RosterItem.seen strong { color:#d7e8f5; } +.v81RosterItem.seen .v81RosterStatus { color:#9eb5ca; } +.v81RosterItem.active { background:rgba(10,38,43,.62); } +.v81RosterItem.active .v81RosterDot { box-shadow:0 0 8px currentColor; } +.v81RosterItem.active .v81RosterStatus { color:#70e8a8; } + +#firmwarePanel, +#serialPanel, +#rawSerialPanel { grid-column: span 12 !important; } +#firmwarePanel.v81-utility-collapsed > :not(.panelTitleRow) { display:none !important; } +#firmwarePanel.v81-utility-collapsed { min-height:0; padding-block:9px; } +#firmwarePanel.v81-utility-collapsed .panelTitleRow { margin:0; } + .v8ResetButton { border: 1px solid rgba(255,170,70,.38); background: rgba(80,42,8,.22); @@ -59,94 +173,60 @@ } .v8ResetButton:hover { border-color: rgba(255,170,70,.75); } -#rawSerialPanel .rawToolbar { - display: flex; - gap: 8px; - align-items: center; - flex-wrap: wrap; - margin-bottom: 10px; -} +#rawSerialPanel .rawToolbar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px; } #rawSerialConsole { - background: #020811; - border: 1px solid rgba(72,114,160,.35); - border-radius: 10px; - min-height: 180px; - max-height: 360px; - overflow: auto; - padding: 10px 12px; - color: #a7c8e8; - font: 12px/1.45 "Cascadia Mono","Consolas",monospace; - white-space: pre-wrap; - word-break: break-word; -} -#rawSerialConsole .rx { color: #9de9ff; } -#rawSerialConsole .tx { color: #ffd479; } -#rawSerialConsole .err { color: #ff8e9a; } -#rawSerialConsole .info { color: #8cf0b4; } - -.v8CommandTarget { - display: grid; - grid-template-columns: minmax(100px, 180px) 1fr; - gap: 10px; - align-items: center; - margin: 0 0 14px; -} -.v8CommandTarget input { - width: 100%; - box-sizing: border-box; - background: rgba(4,17,31,.8); - color: #d9f5ff; - border: 1px solid rgba(77,116,157,.42); - border-radius: 8px; - padding: 9px 10px; -} -.v8ProtocolNote { - margin-top: 10px; - color: #8299b4; - font-size: .8rem; - line-height: 1.45; + background:#020811; + border:1px solid rgba(72,114,160,.35); + border-radius:10px; + min-height:180px; + max-height:360px; + overflow:auto; + padding:10px 12px; + color:#a7c8e8; + font:12px/1.45 "Cascadia Mono","Consolas",monospace; + white-space:pre-wrap; + word-break:break-word; } -.v8ProtocolNote code { color: #bfeeff; } +#rawSerialConsole .rx { color:#9de9ff; } +#rawSerialConsole .tx { color:#ffd479; } +#rawSerialConsole .err { color:#ff8e9a; } +#rawSerialConsole .info { color:#8cf0b4; } -.v8AboutButton { font-size: 14px !important; } -#v8AboutDialog { - position: fixed; - inset: 0; - z-index: 9998; - display: none; - align-items: center; - justify-content: center; - background: rgba(0,6,14,.78); - backdrop-filter: blur(7px); - padding: 24px; -} -#v8AboutDialog.open { display: flex; } -#v8AboutDialog .aboutCard { - width: min(680px, 94vw); - max-height: 88vh; - overflow: auto; - background: #071523; - border: 1px solid rgba(77,157,216,.45); - border-radius: 16px; - box-shadow: 0 22px 80px rgba(0,0,0,.55); - padding: 24px; -} +.v8CommandTarget { display:grid; grid-template-columns:minmax(100px,180px) 1fr; gap:10px; align-items:center; margin:0 0 14px; } +.v8CommandTarget input { width:100%; box-sizing:border-box; background:rgba(4,17,31,.8); color:#d9f5ff; border:1px solid rgba(77,116,157,.42); border-radius:8px; padding:9px 10px; } +.v8ProtocolNote { margin-top:10px; color:#8299b4; font-size:.8rem; line-height:1.45; } +.v8ProtocolNote code { color:#bfeeff; } + +.v8AboutButton { font-size:14px !important; } +#v8AboutDialog { position:fixed; inset:0; z-index:9998; display:none; align-items:center; justify-content:center; background:rgba(0,6,14,.78); backdrop-filter:blur(7px); padding:24px; } +#v8AboutDialog.open { display:flex; } +#v8AboutDialog .aboutCard { width:min(680px,94vw); max-height:88vh; overflow:auto; background:#071523; border:1px solid rgba(77,157,216,.45); border-radius:16px; box-shadow:0 22px 80px rgba(0,0,0,.55); padding:24px; } #v8AboutDialog .aboutHeader { display:flex; align-items:center; gap:14px; } -#v8AboutDialog .aboutHeader img { width:56px; height:56px; background:#fff; border-radius:12px; padding:5px; } +#v8AboutDialog .aboutHeader img { width:56px; height:56px; background:transparent; border-radius:0; padding:0; } #v8AboutDialog h2 { margin:0; color:#e8f8ff; } #v8AboutDialog p, #v8AboutDialog li { color:#a9bdcf; line-height:1.55; } #v8AboutDialog code { color:#7de7ff; } -#v8AboutDialog .closeAbout { - float:right; - border:1px solid rgba(105,142,177,.4); - background:#0b2137; - color:#dceeff; - border-radius:8px; - padding:7px 10px; - cursor:pointer; -} +#v8AboutDialog .closeAbout { float:right; border:1px solid rgba(105,142,177,.4); background:#0b2137; color:#dceeff; border-radius:8px; padding:7px 10px; cursor:pointer; } +@media (max-width: 1450px) { + #topBar { grid-template-columns:minmax(250px,1.55fr) minmax(170px,.85fr) minmax(210px,1fr) minmax(105px,.52fr) auto; } + .topMetric.connection { display:none; } + #telemetryPanel .altairTelemetryGrid { grid-template-columns:repeat(5,minmax(0,1fr)); } +} +@media (max-width: 1180px) { + #topBar { grid-template-columns:minmax(240px,1.5fr) minmax(205px,1fr) minmax(100px,.55fr) auto; } + .topMetric.mission { display:none; } + .v81SetupSteps { grid-template-columns:1fr; } + .v81SetupArrow { display:none; } + .v81SetupStep { grid-template-columns:34px minmax(130px,1fr) auto auto; } + #telemetryPanel .altairTelemetryGrid { grid-template-columns:repeat(3,minmax(0,1fr)); } + .v81ChartsLayout { grid-template-columns:1fr; } + #v81IdRosterList { grid-template-columns:repeat(2,minmax(0,1fr)); } +} @media (max-width: 900px) { - .v8FrequencyMetric { display: none; } - .v8CommandTarget { grid-template-columns: 1fr; } + .v8FrequencyMetric { display:none; } + .v8CommandTarget { grid-template-columns:1fr; } + #telemetryPanel .altairTelemetryGrid { grid-template-columns:repeat(2,minmax(0,1fr)); } + .v81SetupStep { grid-template-columns:34px 1fr; } + .v81SetupStep .v81StepButton { grid-column:2; } } From 016d690fc894708c08b0a8498afdbc1fac1d2ea3 Mon Sep 17 00:00:00 2001 From: "Anton T." <61037544+Anton778@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:38:32 +0700 Subject: [PATCH 03/10] v8.1: load guided layout runtime --- js/v8-final.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/js/v8-final.js b/js/v8-final.js index 22d3e37..0eaac43 100644 --- a/js/v8-final.js +++ b/js/v8-final.js @@ -73,8 +73,6 @@ const grid = document.querySelector("#telemetryPanel .altairTelemetryGrid"); if (!grid) return; - // Канонический порядок v8. RSSI и SNR всегда находятся в самом конце, - // после состояния раскрытия антенны. const order = [ "ID", "PACKET", @@ -96,7 +94,7 @@ function cleanLegacyUi() { const badge = document.querySelector(".buildBadge"); - if (badge) badge.textContent = "v8 · 0.8.0"; + if (badge) badge.textContent = "v8 · 0.8.1"; const projectEyebrow = document.querySelector("#projectPanel .panelEyebrow"); if (projectEyebrow) projectEyebrow.textContent = "RELEASE V8"; @@ -121,11 +119,9 @@ const eventLogFirst = document.querySelector("#eventLog > div:first-child"); if (eventLogFirst && /v5/i.test(eventLogFirst.textContent || "")) { - eventLogFirst.textContent = "ЦУП Альтаир v8 запущен."; + eventLogFirst.textContent = "ЦУП Альтаир v8.1 запущен."; } - // Старый набор готовых команд из v5 отправлял команды в обход нового RF-протокола. - // В v8 используются основные кнопки PING/INFO/TM_START/TM_STOP и произвольная команда. document.querySelectorAll(".commandPresetGroup").forEach(group => group.remove()); const help = document.getElementById("helpDialog"); @@ -135,7 +131,7 @@ const sections = help.querySelectorAll("section"); if (sections[0]) { - sections[0].innerHTML = 'Подключите ESP32-радиошлюз на 115200 бод. Рабочий радиоканал: 435.000 МГц, 4.8 kbps, 2-FSK, девиация 5 кГц, RX BW 203 кГц.
'; + sections[0].innerHTML = 'Сначала подготовьте ESP32-радиошлюз, затем подключите его на 115200 бод. Рабочий радиоканал: 435.000 МГц, 4.8 kbps, 2-FSK, девиация 5 кГц, RX BW 203 кГц.
'; } if (sections[1]) { sections[1].innerHTML = 'Принимаются позиционные пакеты с 7 полями и расширенные пакеты с полем ANTENNA. Поле контрольной суммы сохраняется, но прикладная XOR-проверка отключена: изменение ID не приводит к отбрасыванию пакета.
Xt13zo z=?wtG@vaJ4qbR8|uS_W}0Gy_l*82$9;^5zZLuj8(eP*Rholcg3ceV^}!_At$LDxIO za1DSi#!qbq-6qc5wEz5jlo=$)GRAHK1)(U5v5Xy2aivj6+5v28#{@~9X}kttmg&nN z^QJnP-mC=J>?xkjf}=Kq=!!~!EVo%6Zg&Pi^GPe=i=xmy+f2-1T%M>lf~~ukPPnwu zWR>C346DJ>#U`OlE$#q%13={N0ANE(dFxvDKaozQYda$w>obyS)yFx;+gq0Iant zQ@IC#b|K2WeO=K;{dnv}`pU3)`qrZgE9=9C*)<~CsuAx1U~|I38ry>vfV%(bG^=oJ z@QbWtYL6rR$@BN+bl}RB`e3_ECy;BM?Z8U4Qo}l6^o0FYb^}U z)1cEzT-&~m#c8mE9fILU17KVJyiED|eC7=REmh8r3n0kiIS(X6C02D7kxwE-!nbdS zy64%~Jfdxu }0+3x4UAl|r=kM2~10x0f@X=Y|>&jHm z8`O=$Ljb1ne2$Up;iUX5Tg)}AoR2!>cC|TlgRDvK^Eao&S$J zkt&4VoDLHiGuy159tDtxV_-Vqe`NK=5l{m#Vlax>+;=G4)HSARV{C1qMkTo6VF2WC zY^ZzdI<$ec)kGafHK!{vI=8%`hOPm~ihxShzKxRlHXOs382b#iM(UgV2sSz~4fp!f z-vl7bq14rB{N;B3?4Sns>`NZ^_Nfy8f<>AQlHLP=W1npKq4MnP+9@!E0RgFN8WVtY zRG}o|__WM3f70UsGAlFySli)<21HA;wr^ =#AmHL-p1FwN56X5R%xt<8b2@0FxW*(s-*&sdiRJgI$* vBPxARnxpbAwe|IT&k;>a)qGu7N6xFQpftgzDtv07 z_OAd~@&^twi6fY6Z5zAp9ivjPtLJNw|NW{om lTM)UkqkKYiS*uG4SmLeP z&et821|b+~7(NMru90d>!V+6&ilnLxInhER$$<^DAbF%Qf6|#!Uz#AZHQBP%i4u|$ zx7QJ)Y{&9_XIdX9$0P+&{7%^5t8#UFX!P2a7ttmU9BsZ0fW8-*c|A)IPl5c=&ibrv zL+$Q}`-cI@G3Donu~;+m&u85mxAjds&ibhUmOM=_8I=pQd$K8siS(qb{NVM#!RCQI zG0LGN1olAnG$l?a6i0FfZoD1#?*%Z