fix: inspector reload hoepfully?

This commit is contained in:
Your Name 2026-08-06 16:46:48 -04:00
parent ffba038b35
commit e9f235505f

View file

@ -97,9 +97,99 @@
}
}
async function replaceBody(html) {
const ui = [...document.body.querySelectorAll(":scope > [data-bliss-ui]")];
function nodeKey(node) {
if (node.nodeType !== Node.ELEMENT_NODE) return null;
if (node.id) return `id:${node.id}`;
if (node.dataset.messageId) return `message:${node.dataset.messageId}`;
if (node.dataset.blissRouteId) {
return `route:${node.dataset.blissRouteId}:${node.dataset.blissRequestUrl || ""}`;
}
return null;
}
function sameNodeType(left, right) {
return (
left.nodeType === right.nodeType &&
(left.nodeType !== Node.ELEMENT_NODE || left.tagName === right.tagName)
);
}
function syncAttributes(element, next) {
for (const attribute of [...element.attributes]) {
if (!next.hasAttribute(attribute.name)) element.removeAttribute(attribute.name);
}
for (const attribute of [...next.attributes]) {
if (element.getAttribute(attribute.name) !== attribute.value) {
element.setAttribute(attribute.name, attribute.value);
}
}
}
// Live regions can contain data that arrived after the template was first
// rendered (a WebSocket chat message is the important case). Those nodes
// are not present in the saved render context, so retain them during a
// template patch instead of deleting them with the old tree.
function keepLiveNode(node) {
return (
node.nodeType === Node.ELEMENT_NODE &&
(node.matches("[data-bliss-ui]") ||
node.hasAttribute("data-message-id") ||
node.hasAttribute("data-bliss-route-id"))
);
}
function morphNode(current, next) {
if (!sameNodeType(current, next)) {
current.replaceWith(next.cloneNode(true));
return;
}
if (current.nodeType !== Node.ELEMENT_NODE) {
if (current.nodeValue !== next.nodeValue) current.nodeValue = next.nodeValue;
return;
}
syncAttributes(current, next);
const currentChildren = [...current.childNodes];
const keyed = new Map();
for (const child of currentChildren) {
const key = nodeKey(child);
if (key && !keyed.has(key)) keyed.set(key, child);
}
const used = new Set();
let cursor = current.firstChild;
for (const nextChild of [...next.childNodes]) {
const key = nodeKey(nextChild);
let child = key ? keyed.get(key) : null;
if (child && used.has(child)) child = null;
if (!child) {
child = currentChildren.find(
(candidate) =>
!used.has(candidate) &&
!nodeKey(candidate) &&
sameNodeType(candidate, nextChild),
);
}
if (!child) child = nextChild.cloneNode(false);
if (child !== cursor) current.insertBefore(child, cursor);
used.add(child);
morphNode(child, nextChild);
cursor = child.nextSibling;
}
for (const child of currentChildren) {
if (!used.has(child) && child.isConnected && !keepLiveNode(child)) child.remove();
}
}
async function replaceBody(html, { morph = false } = {}) {
const next = new DOMParser().parseFromString(html, "text/html");
if (morph) {
morphNode(document.body, next.body);
htmx.process(document.body);
return;
}
const ui = [...document.body.querySelectorAll(":scope > [data-bliss-ui]")];
for (const attribute of [...document.body.attributes]) {
document.body.removeAttribute(attribute.name);
}
@ -154,7 +244,7 @@
throw new Error(`Template ${templateId} returned ${response.status}`);
}
const html = await response.text();
if (element === document.body) return replaceBody(html);
if (element === document.body) return replaceBody(html, { morph: true });
const fragment = document.createElement("template");
fragment.innerHTML = html;
// A template may legally have several top-level nodes. They share one