A part redrew on new elements each time a count was answered or a run ended, so a press on the page lost its click and a count being typed lost its focus. settle moves to ui_dom.js for both; a card is kept for its save, and a picture line stands while its pictures do. A press on a picture just as a new one reaches that line is still lost: the line shifts, and another picture is under the pointer by the mouseup. Tested with a real mouse on an isolated ComfyUI. tests/snapshot_bar.html now covers the page and fails 5 checks on the previous code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
45 lines
2.1 KiB
JavaScript
45 lines
2.1 KiB
JavaScript
export function text(parent, tag, value, className = '') {
|
|
const element = document.createElement(tag);
|
|
if (className) element.className = className;
|
|
element.textContent = value == null ? '' : String(value);
|
|
parent.appendChild(element);
|
|
return element;
|
|
}
|
|
|
|
export async function jsonResponse(response, fallbackMessage) {
|
|
let payload = {};
|
|
try { payload = await response.json(); } catch { /* A server error may not be JSON. */ }
|
|
if (!response.ok) throw new Error(payload.message || fallbackMessage);
|
|
return payload;
|
|
}
|
|
|
|
/**
|
|
* `parent` comes to show `fresh` on the elements it has. The browser sends no click to an element that left
|
|
* the page during the press, and a redraw can come during one: ComfyUI's graphChanged at the mouseup, the
|
|
* answer to a request, a run ending. A box being typed in would lose its digits. An element with a `data-key`
|
|
* is kept only for the same key; with `data-fills` too it draws its own content and is left as it stands.
|
|
* Handlers move over to the kept element, so they use the event's element, not the one made with them.
|
|
*/
|
|
export function settle(parent, fresh) {
|
|
const there = [...parent.children];
|
|
fresh.forEach((node, at) => {
|
|
const old = there[at];
|
|
if (!old) return void parent.append(node);
|
|
if (old.tagName !== node.tagName || old.dataset.key !== node.dataset.key) return void old.replaceWith(node);
|
|
if ('fills' in node.dataset) return;
|
|
for (const name of old.getAttributeNames()) {
|
|
if (!node.hasAttribute(name)) old.removeAttribute(name);
|
|
}
|
|
for (const { name, value } of node.attributes) {
|
|
if (old.getAttribute(name) !== value) old.setAttribute(name, value);
|
|
}
|
|
old.onclick = node.onclick;
|
|
old.onchange = node.onchange;
|
|
if (node.tagName === 'INPUT') {
|
|
if (old !== document.activeElement) old.value = node.value;
|
|
} else if (!node.children.length && old.textContent !== node.textContent) old.textContent = node.textContent;
|
|
settle(old, [...node.children]);
|
|
});
|
|
for (const extra of there.slice(fresh.length)) extra.remove();
|
|
}
|