Deploy msgboard
Browse files- src/msgboard/dashboard.html +24 -7
src/msgboard/dashboard.html
CHANGED
|
@@ -1150,24 +1150,41 @@ const avatar = (s, cls = "", link = true) => {
|
|
| 1150 |
(s.identity_type === "system" ? dashedBorderSvg(S) : "") + `</div>`;
|
| 1151 |
};
|
| 1152 |
const pictures = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1153 |
function hydrateBoardImages(root) {
|
| 1154 |
root.querySelectorAll(".board-img[data-file]:not([data-done])").forEach(async (el) => {
|
| 1155 |
el.dataset.done = "1";
|
| 1156 |
const id = el.dataset.file;
|
|
|
|
| 1157 |
try {
|
| 1158 |
const info = await getInfo(id), blob = await getBlob(id, info);
|
| 1159 |
if (!info || kindOf(info.filename, info.content_type) !== "image") throw new Error("not an image");
|
| 1160 |
-
const
|
| 1161 |
-
|
| 1162 |
-
|
| 1163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1164 |
});
|
| 1165 |
}
|
| 1166 |
// Safari (macOS overlay scrollbars) draws a code block's sideways scrollbar over its last line;
|
| 1167 |
-
// blocks that actually scroll get room below the text for it
|
|
|
|
| 1168 |
function markScrollingCode(root) {
|
| 1169 |
-
|
| 1170 |
-
pre.classList.toggle("hscroll", pre.scrollWidth > pre.clientWidth + 1)));
|
| 1171 |
}
|
| 1172 |
let codeResizeTimer = null;
|
| 1173 |
window.addEventListener("resize", () => { clearTimeout(codeResizeTimer); codeResizeTimer = setTimeout(() => markScrollingCode(document), 150); });
|
|
|
|
| 1150 |
(s.identity_type === "system" ? dashedBorderSvg(S) : "") + `</div>`;
|
| 1151 |
};
|
| 1152 |
const pictures = new Map();
|
| 1153 |
+
// Inline images, once loaded, are remembered with their size: every re-render of a feed puts them back at full
|
| 1154 |
+
// size right away instead of a placeholder that grows later and pushes the messages around.
|
| 1155 |
+
const boardImgs = new Map(); // file id -> { url, w, h, alt }
|
| 1156 |
+
function boardImgEl(id, r) {
|
| 1157 |
+
const img = document.createElement("img"); img.alt = r.alt; img.src = r.url;
|
| 1158 |
+
img.style.cssText = `aspect-ratio: ${r.w} / ${r.h}; width: min(100%, 640px, ${r.w}px, ${Math.floor(420 * r.w / r.h)}px); height: auto;`;
|
| 1159 |
+
img.onclick = () => openViewer([id], 0);
|
| 1160 |
+
return img;
|
| 1161 |
+
}
|
| 1162 |
function hydrateBoardImages(root) {
|
| 1163 |
root.querySelectorAll(".board-img[data-file]:not([data-done])").forEach(async (el) => {
|
| 1164 |
el.dataset.done = "1";
|
| 1165 |
const id = el.dataset.file;
|
| 1166 |
+
if (boardImgs.has(id)) { el.replaceChildren(boardImgEl(id, boardImgs.get(id))); el.classList.add("ready"); return; }
|
| 1167 |
try {
|
| 1168 |
const info = await getInfo(id), blob = await getBlob(id, info);
|
| 1169 |
if (!info || kindOf(info.filename, info.content_type) !== "image") throw new Error("not an image");
|
| 1170 |
+
const url = URL.createObjectURL(typed(blob, info)), probe = new Image();
|
| 1171 |
+
probe.src = url; await probe.decode();
|
| 1172 |
+
const r = { url, w: probe.naturalWidth || 1, h: probe.naturalHeight || 1, alt: el.title || info.filename };
|
| 1173 |
+
boardImgs.set(id, r);
|
| 1174 |
+
if (!el.isConnected) return;
|
| 1175 |
+
// first load: the placeholder grows into the image, so keep the reader's place (or the bottom) by hand;
|
| 1176 |
+
// Safari has no built-in scroll anchoring
|
| 1177 |
+
const sc = el.closest(".scroll"), atBottom = sc && nearBottom(sc), restore = sc && !atBottom ? keepAnchor(sc) : null;
|
| 1178 |
+
el.replaceChildren(boardImgEl(id, r)); el.classList.add("ready");
|
| 1179 |
+
if (atBottom) sc.scrollTop = sc.scrollHeight; else if (restore) restore();
|
| 1180 |
+
} catch { if (el.isConnected) el.innerHTML = `<a href="#" data-board-file="${esc(id)}">📎 ${esc(id.split("/").pop())}</a>`; }
|
| 1181 |
});
|
| 1182 |
}
|
| 1183 |
// Safari (macOS overlay scrollbars) draws a code block's sideways scrollbar over its last line;
|
| 1184 |
+
// blocks that actually scroll get room below the text for it. Done right away, not on the next frame, so a
|
| 1185 |
+
// re-render has its final height before the scroll position is restored.
|
| 1186 |
function markScrollingCode(root) {
|
| 1187 |
+
root.querySelectorAll(".body pre").forEach((pre) => pre.classList.toggle("hscroll", pre.scrollWidth > pre.clientWidth + 1));
|
|
|
|
| 1188 |
}
|
| 1189 |
let codeResizeTimer = null;
|
| 1190 |
window.addEventListener("resize", () => { clearTimeout(codeResizeTimer); codeResizeTimer = setTimeout(() => markScrollingCode(document), 150); });
|