fix thumbnails

This commit is contained in:
KingKDot
2026-05-18 09:55:05 -05:00
parent 4129fc2164
commit 95f7db5dd5
8 changed files with 192 additions and 23 deletions
+31 -3
View File
@@ -8,6 +8,7 @@ import {
monitorsBadge,
shortId,
} from "./viewUtils.js";
import { applyImageSrcSmooth } from "./thumbnail-loader.js";
function escapeHtml(text) {
const div = document.createElement("div");
@@ -433,6 +434,23 @@ export function createRenderer({
return `${currentLayout}|${c.id}|${!!c.online}|${c.lastSeen}|${c.pingMs}|${c.host}|${c.user}|${c.os}|${c.arch}|${c.version}|${c.monitors}|${c.country}|${c.nickname}|${c.customTag}|${c.customTagNote}|${!!c.bookmarked}|${!!c.isAdmin}|${c.elevation}|${c.cpu}|${c.gpu}|${c.ram}|${c.hwid}|${c.disconnectReason}|${c.disconnectDetail}|${c.groupId}|${c.groupName}|${c.groupColor}|${!!c.notificationsMuted}`;
}
function cardThumbDigest(c) {
return `${!!c.hasThumbnail}|${Number(c.thumbnailVersion) || 0}`;
}
function softThumbUpdate(card, client) {
const host = card.querySelector("[data-thumb-host]");
if (!host) return;
const newVersion = Number(client.thumbnailVersion) || 0;
host.dataset.thumbVersion = String(newVersion);
host.dataset.thumbOnline = client.online ? "1" : "0";
if (!client.hasThumbnail) return;
const img = host.querySelector("img[data-thumb-img]");
if (!img) return;
const url = `/api/clients/${encodeURIComponent(client.id)}/thumbnail${newVersion ? `?v=${newVersion}` : ""}`;
applyImageSrcSmooth(img, url);
}
function renderMerge(data, options = {}) {
setupGridDelegation();
ensureLayoutScaffold();
@@ -463,9 +481,18 @@ export function createRenderer({
if (existing) {
if (existing.classList.contains("card-uninstalling")) return;
const digest = cardDigest(client);
if (existing._cardDigest === digest) return;
existing._cardDigest = digest;
updateCard(existing, client);
const thumbDigest = cardThumbDigest(client);
const structureChanged = existing._cardDigest !== digest;
const thumbChanged = existing._thumbDigest !== thumbDigest;
if (!structureChanged && !thumbChanged) return;
if (structureChanged) {
existing._cardDigest = digest;
existing._thumbDigest = thumbDigest;
updateCard(existing, client);
} else {
existing._thumbDigest = thumbDigest;
softThumbUpdate(existing, client);
}
return;
}
newClients.push(client);
@@ -550,6 +577,7 @@ export function createRenderer({
: buildRowA(client, options);
node._cardDigest = cardDigest(client);
node._thumbDigest = cardThumbDigest(client);
if (options.animate && currentLayout !== "table") {
node.classList.add("card-animate");
+35 -2
View File
@@ -794,7 +794,7 @@ if (window.CodeMirror && scriptEditor) {
tabSize: 2,
lineWrapping: true,
});
editorInstance.setSize(null, "100%");
editorInstance.setSize(null, "24rem");
window._vbCodeMirror = editorInstance;
}
@@ -803,6 +803,36 @@ const modeToggleVisual = document.getElementById("mode-toggle-visual");
const codeEditorSection = document.getElementById("code-editor-section");
const visualBuilderSection = document.getElementById("visual-builder-section");
const rightColumn = codeEditorSection?.parentElement?.querySelector(".lg\\:col-span-1");
const lgMediaQuery = window.matchMedia("(min-width: 1024px)");
function syncEditorHeight() {
if (!editorInstance || !codeEditorSection || !rightColumn) return;
if (codeEditorSection.classList.contains("hidden")) return;
if (!lgMediaQuery.matches) {
editorInstance.setSize(null, "24rem");
return;
}
const rightHeight = rightColumn.getBoundingClientRect().height;
const card = codeEditorSection.querySelector(":scope > div");
if (!card) return;
const cs = getComputedStyle(card);
const padY = (parseFloat(cs.paddingTop) || 0) + (parseFloat(cs.paddingBottom) || 0);
const header = card.querySelector(":scope > .flex.items-center.justify-between");
const headerH = header ? header.getBoundingClientRect().height : 0;
const headerStyle = header ? getComputedStyle(header) : null;
const headerMb = headerStyle ? parseFloat(headerStyle.marginBottom) || 0 : 0;
const target = Math.max(384, rightHeight - padY - headerH - headerMb);
editorInstance.setSize(null, `${target}px`);
}
if (editorInstance && rightColumn) {
syncEditorHeight();
if (typeof ResizeObserver === "function") {
const ro = new ResizeObserver(() => syncEditorHeight());
ro.observe(rightColumn);
}
window.addEventListener("resize", syncEditorHeight);
lgMediaQuery.addEventListener?.("change", syncEditorHeight);
}
let visualBuilderInited = false;
function setMode(mode) {
@@ -825,7 +855,10 @@ function setMode(mode) {
visualBuilderSection?.classList.add("hidden");
modeToggleCode.className = "flex-1 flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-semibold text-sm transition-colors bg-emerald-600 text-white";
modeToggleVisual.className = "flex-1 flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-semibold text-sm transition-colors bg-slate-800 text-slate-300 hover:bg-slate-700";
if (editorInstance) editorInstance.refresh();
if (editorInstance) {
editorInstance.refresh();
syncEditorHeight();
}
}
}
@@ -49,6 +49,24 @@ function buildThumbnailUrl(clientId, version) {
return version > 0 ? `${base}?v=${version}` : base;
}
export function applyImageSrcSmooth(img, url) {
if (!img || !url) return;
if (img.dataset.thumbUrl === url) return;
const newImg = img.cloneNode(false);
newImg.dataset.thumbUrl = url;
newImg.style.display = "block";
const swap = () => {
if (img.parentNode && img.dataset.thumbUrl !== url) img.replaceWith(newImg);
};
newImg.addEventListener("error", () => {}, { once: true });
newImg.src = url;
if (typeof newImg.decode === "function") {
newImg.decode().then(swap).catch(() => {});
} else {
newImg.addEventListener("load", swap, { once: true });
}
}
export class ThumbnailLoader {
constructor(options = {}) {
this.refreshIntervalMs = Math.max(500, Number(options.refreshIntervalMs) || DEFAULT_REFRESH_MS);
@@ -147,10 +165,7 @@ export class ThumbnailLoader {
const img = element.querySelector("img[data-thumb-img]");
if (!img) return;
const url = buildThumbnailUrl(rec.clientId, rec.version);
if (img.dataset.thumbUrl !== url) {
img.dataset.thumbUrl = url;
img.src = url;
}
applyImageSrcSmooth(img, url);
}
handleIntersection(entries) {