update docker
This commit is contained in:
128
www/app.js
128
www/app.js
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user