update docker

This commit is contained in:
2026-06-13 10:17:26 +07:00
parent 4f8d8148f7
commit 8c111f2406
11 changed files with 431 additions and 19 deletions

View File

@@ -3,6 +3,15 @@ const el = (id) => document.getElementById(id);
const statusEl = el("status");
const listEl = el("lidarList");
const lidarFormHintEl = el("lidarFormHint");
const pageTitleEl = document.querySelector(".pageTitle");
const navItemEls = Array.from(document.querySelectorAll(".navItem[data-page]"));
const pageOverviewEl = el("pageOverview");
const pageConfigEl = el("pageConfig");
const overviewBackendEl = el("overviewBackend");
const overviewActiveLayoutEl = el("overviewActiveLayout");
const overviewActiveModelEl = el("overviewActiveModel");
const overviewActiveSensorsEl = el("overviewActiveSensors");
const configSplitterEl = el("configSplitter");
const canvasWrap = el("canvasWrap");
const robotModelEl = el("robotModel");
@@ -107,6 +116,111 @@ const state = {
pendingFootprintClick: null, // { sx, sy } when Shift+click may add a vertex
};
function setActivePage(page) {
const p = page === "overview" ? "overview" : "config";
navItemEls.forEach((a) => {
const on = (a.dataset.page || "") === p;
a.classList.toggle("active", on);
if (on) a.setAttribute("aria-current", "page");
else a.removeAttribute("aria-current");
});
if (pageTitleEl) pageTitleEl.textContent = p === "overview" ? "Tổng quan" : "Cấu Hình";
if (pageOverviewEl) pageOverviewEl.hidden = p !== "overview";
if (pageConfigEl) pageConfigEl.hidden = p !== "config";
try {
localStorage.setItem("activePage", p);
} catch {
/* ignore */
}
}
function initNavigation() {
navItemEls.forEach((a) => {
a.addEventListener("click", (evt) => {
evt.preventDefault();
setActivePage(a.dataset.page || "config");
});
});
// Restore last page, default to config (màn hình chính).
let initial = "config";
try {
const saved = localStorage.getItem("activePage");
if (saved === "overview" || saved === "config") initial = saved;
} catch {
/* ignore */
}
setActivePage(initial);
}
function setLeftPaneWidth(px) {
const v = Math.round(clamp(Number(px), 320, 720));
document.documentElement.style.setProperty("--leftPaneW", `${v}px`);
try {
localStorage.setItem("leftPaneW", String(v));
} catch {
/* ignore */
}
}
function initSplitPane() {
if (!configSplitterEl) return;
try {
const saved = Number(localStorage.getItem("leftPaneW"));
if (Number.isFinite(saved) && saved > 0) setLeftPaneWidth(saved);
else setLeftPaneWidth(460);
} catch {
setLeftPaneWidth(460);
}
let dragging = false;
let startX = 0;
let startW = 0;
const onMove = (evt) => {
if (!dragging) return;
const x = evt.clientX ?? (evt.touches && evt.touches[0] ? evt.touches[0].clientX : startX);
setLeftPaneWidth(startW + (x - startX));
};
const onUp = () => {
if (!dragging) return;
dragging = false;
configSplitterEl.classList.remove("dragging");
window.removeEventListener("mousemove", onMove);
window.removeEventListener("mouseup", onUp);
window.removeEventListener("touchmove", onMove);
window.removeEventListener("touchend", onUp);
};
configSplitterEl.addEventListener("mousedown", (evt) => {
evt.preventDefault();
dragging = true;
configSplitterEl.classList.add("dragging");
startX = evt.clientX;
startW = Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--leftPaneW")) || 460;
window.addEventListener("mousemove", onMove);
window.addEventListener("mouseup", onUp);
});
configSplitterEl.addEventListener("touchstart", (evt) => {
if (!evt.touches || !evt.touches[0]) return;
dragging = true;
configSplitterEl.classList.add("dragging");
startX = evt.touches[0].clientX;
startW = Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--leftPaneW")) || 460;
window.addEventListener("touchmove", onMove, { passive: true });
window.addEventListener("touchend", onUp);
}, { passive: false });
// Keyboard resize (focus splitter, use arrows)
configSplitterEl.addEventListener("keydown", (evt) => {
if (evt.key !== "ArrowLeft" && evt.key !== "ArrowRight") return;
evt.preventDefault();
const cur = Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--leftPaneW")) || 460;
setLeftPaneWidth(cur + (evt.key === "ArrowLeft" ? -20 : 20));
});
}
const DIFF_DEFAULTS = {
frame_id: "base_footprint",
wheel_separation_m: 1.0,
@@ -2906,6 +3020,7 @@ async function loadAll() {
loadAllInFlight = (async () => {
const st = await api("/api/state");
if (overviewBackendEl) overviewBackendEl.textContent = "OK";
state.activeLayoutId = st.active_layout_id || null;
state.activeLayoutName = st.active_layout_name || "";
state.layoutCatalog = st.layouts || [];
@@ -2980,6 +3095,16 @@ async function loadAll() {
setSelectedRelText();
renderList();
renderImuList();
if (overviewActiveLayoutEl) {
const name = state.activeLayoutName || state.activeLayoutId || "—";
overviewActiveLayoutEl.textContent = name;
}
if (overviewActiveModelEl) {
overviewActiveModelEl.textContent = state.layout?.robot?.model || "diff";
}
if (overviewActiveSensorsEl) {
overviewActiveSensorsEl.textContent = `${state.lidars.length} LiDAR • ${state.imus.length} IMU`;
}
if (!state.viewInitialized) {
fitViewToWorld();
state.viewInitialized = true;
@@ -3203,6 +3328,8 @@ function initRobotModelPanelCollapse() {
}
initLayoutManagerEvents();
initNavigation();
initSplitPane();
initLidarForm();
initMotorWheelsEvents();
initBicycleMotorWheelsEvents();
@@ -3253,6 +3380,7 @@ saveLayoutBtn.addEventListener("click", async () => {
setStatus("Sẵn sàng");
} catch (e) {
const msg = String(e.message || e);
if (overviewBackendEl) overviewBackendEl.textContent = `Lỗi: ${msg}`;
if (msg.includes("stack") || msg.includes("Maximum call")) {
setStatus(`Lỗi JavaScript: ${msg}`);
} else {