diff --git a/Web/index.html b/Web/index.html
index d06054f8b..c0ea35434 100644
--- a/Web/index.html
+++ b/Web/index.html
@@ -10,8 +10,6 @@
- Not an official GR tool.
-
@@ -120,7 +118,7 @@
GRCAN Viewer
-
+
diff --git a/Web/viewer.js b/Web/viewer.js
index a093ecdc3..1d4ea23dc 100644
--- a/Web/viewer.js
+++ b/Web/viewer.js
@@ -20,6 +20,25 @@ window.addEventListener("DOMContentLoaded", function () {
const searchInput = document.getElementById("viewer-search");
let nodeIdMap = new Map();
let currentRef = "";
+ const requestedQueryKey = (() => {
+ try {
+ const params = new URLSearchParams(window.location.search);
+ if (params.has("ref")) return "ref";
+ if (params.has("branch")) return "branch";
+ return "ref";
+ } catch (_err) {
+ return "ref";
+ }
+ })();
+ const requestedRefFromUrl = (() => {
+ try {
+ const params = new URLSearchParams(window.location.search);
+ const ref = params.get("ref") || params.get("branch");
+ return ref ? ref.trim() : "";
+ } catch (_err) {
+ return "";
+ }
+ })();
let _allNodes = []; // persisted node→bus→messages index for search
let _searchDropdown = null;
@@ -63,6 +82,31 @@ window.addEventListener("DOMContentLoaded", function () {
container.appendChild(d);
}
+ function updateLocationState(ref) {
+ const url = new URL(window.location.href);
+ const isCustomFile = !!window.GrcanApi.isLocalMode();
+ const hasEdits =
+ !isCustomFile &&
+ !!editor &&
+ !!editor.hasUnsavedEdits &&
+ editor.hasUnsavedEdits();
+
+ if (isCustomFile) {
+ url.search = "";
+ url.hash = "custom";
+ } else {
+ if (ref) {
+ url.searchParams.set(requestedQueryKey, ref);
+ } else {
+ url.searchParams.delete("ref");
+ url.searchParams.delete("branch");
+ }
+ url.hash = hasEdits ? "edited" : "";
+ }
+
+ window.history.replaceState(null, "", url);
+ }
+
function messageChangeState(msgName, deviceName, busCanonical) {
const busPort = busCanonical
? window.GrcanApi.busToPort(busCanonical)
@@ -1113,6 +1157,7 @@ window.addEventListener("DOMContentLoaded", function () {
await renderBusNode(null, text);
}
restoreSelection(snapshot || navSnapshot());
+ updateLocationState(currentRef);
}
if (editor) {
@@ -1179,6 +1224,7 @@ window.addEventListener("DOMContentLoaded", function () {
}
await renderHierarchy(ref);
currentRef = ref;
+ updateLocationState(currentRef);
if (typeof window.regenerateAndDrawBg === "function") {
window.regenerateAndDrawBg();
}
@@ -1210,12 +1256,21 @@ window.addEventListener("DOMContentLoaded", function () {
refSelect.appendChild(opt);
});
- if (branches.includes("main")) {
- refSelect.value = "main";
- await renderHierarchy("main");
- currentRef = "main";
+ const availableRefs = new Set([...branches, ...tags]);
+ const initialRef = availableRefs.has(requestedRefFromUrl)
+ ? requestedRefFromUrl
+ : branches.includes("main")
+ ? "main"
+ : "";
+
+ if (initialRef) {
+ refSelect.value = initialRef;
+ await renderHierarchy(initialRef);
+ currentRef = initialRef;
+ updateLocationState(currentRef);
} else {
setPlaceholder(firstList, "Select a ref");
+ updateLocationState("");
}
}
@@ -1235,6 +1290,7 @@ window.addEventListener("DOMContentLoaded", function () {
localFileInput.value = "";
window.GrcanApi.setLocalCandoText(null);
refSelect.disabled = false;
+ updateLocationState(currentRef);
if (currentRef) renderHierarchy(currentRef);
}
});
@@ -1246,6 +1302,7 @@ window.addEventListener("DOMContentLoaded", function () {
localFileInput.style.display = "none";
window.GrcanApi.setLocalCandoText(null);
refSelect.disabled = false;
+ updateLocationState(currentRef);
return;
}
const reader = new FileReader();
@@ -1253,6 +1310,7 @@ window.addEventListener("DOMContentLoaded", function () {
window.GrcanApi.setLocalCandoText(e.target.result);
refSelect.disabled = true;
renderHierarchy(currentRef || "local");
+ updateLocationState(currentRef);
};
reader.readAsText(file);
});
@@ -1260,6 +1318,9 @@ window.addEventListener("DOMContentLoaded", function () {
localFileInput.addEventListener("cancel", function () {
localToggle.checked = false;
localFileInput.style.display = "none";
+ window.GrcanApi.setLocalCandoText(null);
+ refSelect.disabled = false;
+ updateLocationState(currentRef);
});
}