Skip to content

Commit 4b929e8

Browse files
gguf-viewer: vanilla layout mobile-compatible with no code exceptions (1/2)
1 parent 7db97d8 commit 4b929e8

File tree

5 files changed

+189
-70
lines changed

5 files changed

+189
-70
lines changed

tools/gguf-viewer/public/architecture.js

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -577,30 +577,6 @@
577577
note.textContent = options.note;
578578
li.appendChild(note);
579579
}
580-
if (tensor.name) {
581-
const actions = document.createElement("div");
582-
actions.className = "architecture-tensor__actions";
583-
584-
const encodedName = encodeURIComponent(tensor.name);
585-
586-
const heatmapButton = document.createElement("button");
587-
heatmapButton.type = "button";
588-
heatmapButton.className = "viewer-button";
589-
heatmapButton.dataset.action = "heatmap";
590-
heatmapButton.dataset.name = encodedName;
591-
heatmapButton.textContent = "Heatmap";
592-
actions.appendChild(heatmapButton);
593-
594-
const statisticsButton = document.createElement("button");
595-
statisticsButton.type = "button";
596-
statisticsButton.className = "viewer-button";
597-
statisticsButton.dataset.action = "statistics";
598-
statisticsButton.dataset.name = encodedName;
599-
statisticsButton.textContent = "Statistics";
600-
actions.appendChild(statisticsButton);
601-
602-
header.appendChild(actions);
603-
}
604580
return li;
605581
}
606582

tools/gguf-viewer/public/common.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@ const HEATMAP_STEP_FALLBACK = 0.001;
534534
const HEATMAP_MIN_STEP = 1e-6;
535535
const HEATMAP_SCALE_HTML_STEP = 0.0001;
536536
const HEATMAP_SCALE_DECIMALS = 4;
537-
const VIEWPORT_MARGIN = 64;
538537

539538
let tensorData = [];
540539
let modelMetadataEntries = [];
@@ -618,6 +617,7 @@ const heatmapDragState = {
618617
offsetX: 0,
619618
offsetY: 0,
620619
moved: false,
620+
touchId: null,
621621
};
622622

623623
syncPageFromLocation();
@@ -955,7 +955,16 @@ function syncHeatmapControls() {
955955
function getViewportDimensions() {
956956
const doc = document.documentElement;
957957
const width = Math.max(1, Math.floor(doc ? doc.clientWidth : window.innerWidth || 1));
958-
const height = Math.max(1, Math.floor(window.innerHeight || (doc ? doc.clientHeight : 1)));
958+
const candidates = [
959+
doc && Number.isFinite(doc.clientHeight) ? doc.clientHeight : 0,
960+
typeof window.visualViewport === "object" && Number.isFinite(window.visualViewport.height)
961+
? window.visualViewport.height
962+
: 0,
963+
Number.isFinite(window.innerHeight) ? window.innerHeight : 0,
964+
].filter((value) => value > 0);
965+
const height = candidates.length > 0
966+
? Math.max(1, Math.floor(Math.min(...candidates)))
967+
: 1;
959968
return { width, height };
960969
}
961970

tools/gguf-viewer/public/heatmap.js

Lines changed: 152 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,14 @@ function resetHeatmap(message = HEATMAP_DEFAULT_STREAM_MESSAGE) {
183183
heatmapState.viewHeight = heatmapCanvas.height;
184184
heatmapState.pendingScale = null;
185185
heatmapState.sliceRequestId = 0;
186+
heatmapDragState.active = false;
187+
heatmapDragState.touchId = null;
188+
heatmapDragState.offsetX = 0;
189+
heatmapDragState.offsetY = 0;
190+
heatmapDragState.moved = false;
191+
if (heatmapCanvas) {
192+
heatmapCanvas.classList.remove("heatmap-canvas--dragging");
193+
}
186194
if (heatmapGainOffsetToggle) {
187195
heatmapGainOffsetToggle.checked = false;
188196
}
@@ -304,20 +312,48 @@ function ensureHeatmapBuffer(width, height) {
304312
}
305313
}
306314

315+
function getPaddingBottomPx(element) {
316+
if (!element || typeof window.getComputedStyle !== "function") {
317+
return 0;
318+
}
319+
const styles = window.getComputedStyle(element);
320+
if (!styles) {
321+
return 0;
322+
}
323+
const value = Number.parseFloat(styles.paddingBottom);
324+
if (!Number.isFinite(value)) {
325+
return 0;
326+
}
327+
return Math.max(0, value);
328+
}
329+
307330
function syncHeatmapToViewport(fetchAfterResize) {
308331
if (heatmapCanvasWrapper && heatmapCanvasWrapper.offsetParent === null) {
309332
return;
310333
}
311334

312335
const viewport = getViewportDimensions();
313-
const availableHeight = Math.max(1, viewport.height - VIEWPORT_MARGIN);
336+
const viewportHeight = Math.max(1, Math.floor(viewport.height));
337+
let availableHeight = viewportHeight;
314338
if (heatmapCanvasWrapper) {
339+
const wrapperRect = heatmapCanvasWrapper.getBoundingClientRect();
340+
const topOffset = Math.max(0, Math.floor(wrapperRect.top));
341+
let paddingOffset = 1;
342+
const parentSection = heatmapCanvasWrapper.closest("section");
343+
if (parentSection) {
344+
paddingOffset += Math.ceil(getPaddingBottomPx(parentSection));
345+
}
346+
const mainElement = heatmapCanvasWrapper.closest("main");
347+
if (mainElement) {
348+
paddingOffset += Math.ceil(getPaddingBottomPx(mainElement));
349+
}
350+
availableHeight = Math.max(1, viewportHeight - topOffset - paddingOffset);
315351
heatmapCanvasWrapper.style.height = `${availableHeight}px`;
316352
}
317353

318354
const wrapperWidth = heatmapCanvasWrapper
319355
? Math.max(1, Math.floor(heatmapCanvasWrapper.clientWidth || heatmapCanvasWrapper.offsetWidth || 1))
320-
: Math.max(1, viewport.width - 64);
356+
: Math.max(1, viewport.width);
321357

322358
const hasTensor = !!heatmapState.tensor;
323359
const layoutWidth = hasTensor && heatmapState.layout && heatmapState.layout.width > 0
@@ -340,6 +376,11 @@ function syncHeatmapToViewport(fetchAfterResize) {
340376
heatmapDragState.offsetX = 0;
341377
heatmapDragState.offsetY = 0;
342378
heatmapDragState.moved = false;
379+
heatmapDragState.active = false;
380+
heatmapDragState.touchId = null;
381+
if (heatmapCanvas) {
382+
heatmapCanvas.classList.remove("heatmap-canvas--dragging");
383+
}
343384
if (hasTensor) {
344385
heatmapState.windowX = clampWindowX(heatmapState.windowX);
345386
heatmapState.windowY = clampWindowY(heatmapState.windowY);
@@ -1100,21 +1141,93 @@ function heatmapCanvasScale() {
11001141
return { scaleX, scaleY };
11011142
}
11021143

1103-
heatmapCanvas.addEventListener("mousedown", (event) => {
1104-
if (!heatmapState.tensor) {
1105-
return;
1144+
function beginHeatmapDrag(clientX, clientY, options = {}) {
1145+
if (!heatmapState.tensor || heatmapDragState.active) {
1146+
return false;
11061147
}
1107-
event.preventDefault();
11081148
hideHeatmapTooltip();
11091149
heatmapDragState.active = true;
1110-
heatmapDragState.lastX = event.clientX;
1111-
heatmapDragState.lastY = event.clientY;
1150+
heatmapDragState.lastX = clientX;
1151+
heatmapDragState.lastY = clientY;
11121152
heatmapDragState.offsetX = 0;
11131153
heatmapDragState.offsetY = 0;
11141154
heatmapDragState.moved = false;
1155+
heatmapDragState.touchId = typeof options.touchId === "number" ? options.touchId : null;
11151156
heatmapCanvas.classList.add("heatmap-canvas--dragging");
1157+
return true;
1158+
}
1159+
1160+
function updateHeatmapDragPosition(clientX, clientY) {
1161+
if (!heatmapDragState.active) {
1162+
return;
1163+
}
1164+
const { scaleX, scaleY } = heatmapCanvasScale();
1165+
const dx = (clientX - heatmapDragState.lastX) * scaleX;
1166+
const dy = (clientY - heatmapDragState.lastY) * scaleY;
1167+
heatmapDragState.lastX = clientX;
1168+
heatmapDragState.lastY = clientY;
1169+
1170+
if (dx === 0 && dy === 0) {
1171+
return;
1172+
}
1173+
1174+
const beforeX = heatmapDragState.offsetX;
1175+
const beforeY = heatmapDragState.offsetY;
1176+
heatmapDragState.offsetX += dx;
1177+
heatmapDragState.offsetY += dy;
1178+
clampDragOffsets();
1179+
if (heatmapDragState.offsetX !== beforeX || heatmapDragState.offsetY !== beforeY) {
1180+
heatmapDragState.moved = true;
1181+
}
1182+
1183+
if (heatmapState.imageReady) {
1184+
drawHeatmap(heatmapDragState.offsetX, heatmapDragState.offsetY);
1185+
}
1186+
}
1187+
1188+
function findTouchById(touchList, identifier) {
1189+
if (!touchList || typeof touchList.length !== "number") {
1190+
return null;
1191+
}
1192+
for (let i = 0; i < touchList.length; i += 1) {
1193+
const touch = typeof touchList.item === "function" ? touchList.item(i) : touchList[i];
1194+
if (touch && touch.identifier === identifier) {
1195+
return touch;
1196+
}
1197+
}
1198+
return null;
1199+
}
1200+
1201+
function getTrackedTouch(event) {
1202+
if (heatmapDragState.touchId === null) {
1203+
return null;
1204+
}
1205+
const { touchId } = heatmapDragState;
1206+
return findTouchById(event.touches, touchId) || findTouchById(event.changedTouches, touchId);
1207+
}
1208+
1209+
heatmapCanvas.addEventListener("mousedown", (event) => {
1210+
if (!beginHeatmapDrag(event.clientX, event.clientY)) {
1211+
return;
1212+
}
1213+
event.preventDefault();
11161214
});
11171215

1216+
heatmapCanvas.addEventListener("touchstart", (event) => {
1217+
const touch = event.changedTouches && event.changedTouches.length > 0
1218+
? event.changedTouches[0]
1219+
: event.touches && event.touches.length > 0
1220+
? event.touches[0]
1221+
: null;
1222+
if (!touch) {
1223+
return;
1224+
}
1225+
if (!beginHeatmapDrag(touch.clientX, touch.clientY, { touchId: touch.identifier })) {
1226+
return;
1227+
}
1228+
event.preventDefault();
1229+
}, { passive: false });
1230+
11181231
heatmapCanvas.addEventListener("mousemove", (event) => {
11191232
if (!heatmapState.tensor || !heatmapState.imageReady || heatmapDragState.active) {
11201233
return;
@@ -1158,38 +1271,27 @@ heatmapCanvas.addEventListener("mousemove", (event) => {
11581271
});
11591272

11601273
window.addEventListener("mousemove", (event) => {
1274+
updateHeatmapDragPosition(event.clientX, event.clientY);
1275+
});
1276+
1277+
window.addEventListener("touchmove", (event) => {
11611278
if (!heatmapDragState.active) {
11621279
return;
11631280
}
1164-
const { scaleX, scaleY } = heatmapCanvasScale();
1165-
const dx = (event.clientX - heatmapDragState.lastX) * scaleX;
1166-
const dy = (event.clientY - heatmapDragState.lastY) * scaleY;
1167-
heatmapDragState.lastX = event.clientX;
1168-
heatmapDragState.lastY = event.clientY;
1169-
1170-
if (dx === 0 && dy === 0) {
1281+
const touch = getTrackedTouch(event);
1282+
if (!touch) {
11711283
return;
11721284
}
1173-
1174-
const beforeX = heatmapDragState.offsetX;
1175-
const beforeY = heatmapDragState.offsetY;
1176-
heatmapDragState.offsetX += dx;
1177-
heatmapDragState.offsetY += dy;
1178-
clampDragOffsets();
1179-
if (heatmapDragState.offsetX !== beforeX || heatmapDragState.offsetY !== beforeY) {
1180-
heatmapDragState.moved = true;
1181-
}
1182-
1183-
if (heatmapState.imageReady) {
1184-
drawHeatmap(heatmapDragState.offsetX, heatmapDragState.offsetY);
1185-
}
1186-
});
1285+
event.preventDefault();
1286+
updateHeatmapDragPosition(touch.clientX, touch.clientY);
1287+
}, { passive: false });
11871288

11881289
function endHeatmapDrag() {
11891290
if (!heatmapDragState.active) {
11901291
return;
11911292
}
11921293
heatmapDragState.active = false;
1294+
heatmapDragState.touchId = null;
11931295
heatmapCanvas.classList.remove("heatmap-canvas--dragging");
11941296
clampDragOffsets();
11951297
if (!heatmapDragState.moved) {
@@ -1218,6 +1320,27 @@ function endHeatmapDrag() {
12181320
}
12191321

12201322
window.addEventListener("mouseup", endHeatmapDrag);
1323+
window.addEventListener("touchend", (event) => {
1324+
if (!heatmapDragState.active) {
1325+
return;
1326+
}
1327+
const touch = getTrackedTouch(event);
1328+
if (!touch) {
1329+
return;
1330+
}
1331+
event.preventDefault();
1332+
endHeatmapDrag();
1333+
}, { passive: false });
1334+
window.addEventListener("touchcancel", (event) => {
1335+
if (!heatmapDragState.active) {
1336+
return;
1337+
}
1338+
const touch = getTrackedTouch(event);
1339+
if (!touch) {
1340+
return;
1341+
}
1342+
endHeatmapDrag();
1343+
});
12211344
heatmapCanvas.addEventListener("mouseleave", () => {
12221345
hideHeatmapTooltip();
12231346
if (heatmapDragState.active) {

tools/gguf-viewer/public/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<html lang="en">
33
<head>
44
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
56
<title>llama-gguf-viewer</title>
67
<link rel="stylesheet" href="viewer.css" />
78
</head>

0 commit comments

Comments
 (0)