panel: refine domain detail layout and drop section index
test / test (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-12 22:16:03 +03:00
parent 44e79c4120
commit f9e259a66d
6 changed files with 299 additions and 384 deletions
+23 -63
View File
@@ -72,6 +72,28 @@
});
}
// --- Custom DMARC rua address shown only for "custom" mode ------------
// Same idea as the address list: the email field only applies when the
// operator picks Custom address. With JavaScript blocked the field stays
// visible and the server still ignores it for inherit/none.
function syncCustomAddressField(select) {
var form = select.closest("form");
var field = form && form.querySelector("[data-custom-address]");
if (!field) {
return;
}
field.hidden = select.value !== select.dataset.customMode;
}
function initCustomAddressFields(root) {
root.querySelectorAll("select[data-custom-mode]").forEach(function (select) {
syncCustomAddressField(select);
select.addEventListener("change", function () {
syncCustomAddressField(select);
});
});
}
// --- Domain pick shown only for domain administrators ------------------
// Global administrators manage every domain, so the assignment checkboxes
// are irrelevant for that role. The toggle runs on load too, because the
@@ -157,74 +179,12 @@
});
}
// --- Section index follows the page -----------------------------------
// The long pages list their own sections in the navigation column (the
// "sections" template). Marking the one currently in view turns that list
// from an index into a position, which is the whole point of it on a page
// nine cards tall. The links work without any of this; only the highlight
// depends on it.
//
// Each pass looks its targets up by id rather than holding on to elements
// found once: the status page replaces its cards wholesale every five
// seconds (adaptive polling on #status-body), and anything remembered here would be
// measuring boxes that had left the document.
var sectionLinks = [];
function markCurrentSection() {
var current = null;
sectionLinks.forEach(function (link) {
var target = document.getElementById(link.hash.slice(1));
// The section in view is the last one whose top has passed the reading
// line; the links are in document order, so the last match wins.
if (target && target.getBoundingClientRect().top <= 100) {
current = link;
}
});
if (window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 2) {
// At the foot of the page there is no scroll left to bring the last
// cards up to the reading line, so without this they could never be
// marked however far down you are — and the last card of the domain
// page is the one that deletes it.
current = sectionLinks[sectionLinks.length - 1];
} else if (!current) {
// Above the first heading nothing has been passed yet, and the page is
// still on its first section.
current = sectionLinks[0];
}
sectionLinks.forEach(function (link) {
link.classList.toggle("current", link === current);
});
}
function initSectionIndex() {
sectionLinks = Array.prototype.slice.call(
document.querySelectorAll(".sections a[href^='#']")
);
if (!sectionLinks.length) {
return;
}
var pending = false;
// Scroll fires far more often than the highlight can change, so the work
// is collapsed onto the next frame.
window.addEventListener("scroll", function () {
if (pending) {
return;
}
pending = true;
window.requestAnimationFrame(function () {
pending = false;
markCurrentSection();
});
}, { passive: true });
markCurrentSection();
}
document.addEventListener("DOMContentLoaded", function () {
initAddressFields(document);
initCustomAddressFields(document);
initDomainPickFields(document);
initEncryptFields(document);
initImportPasswordField(document);
initSectionIndex();
});
// --- Adaptive monitoring polling ---------------------------------------