fix: zoom-aware dropdown positioning in cookbook hamburger menus
Some checks are pending
ci / docker publish / build (amd64) (push) Waiting to run
ci / docker publish / build (arm64) (push) Waiting to run
ci / docker publish / merge manifest + tag (push) Blocked by required conditions

getBoundingClientRect() returns zoom-scaled coordinates but
window.innerWidth does not. With ui-scale-125 active, the right-edge
calculation (window.innerWidth - rect.right) mixed coordinate spaces,
causing dropdowns to appear displaced from their hamburger buttons.

Divide window.innerWidth by the computed zoom factor to match the
rect coordinate space. Fixes both cookbookRunning.js (Active tab)
and cookbookServe.js (Serve tab).
This commit is contained in:
Lukas Parsons 2026-07-07 11:30:22 -04:00
parent 185667954c
commit 4d3cf1a5f9
2 changed files with 7 additions and 2 deletions

View file

@ -408,6 +408,9 @@ export function _parseServePhase(snapshot) {
if (/Ollama API ready on port\s+\d+/i.test(flat)) { if (/Ollama API ready on port\s+\d+/i.test(flat)) {
return { phase: 'ready', status: 'ready' }; return { phase: 'ready', status: 'ready' };
} }
if (/(?:server is |^|\s)listening on http:\/\//i.test(flat)) {
return { phase: 'ready', status: 'ready' };
}
const llamaBuildMatches = [...flat.matchAll(/\[\s*(\d{1,3})%\]\s*(?:Building|Linking)/gi)]; const llamaBuildMatches = [...flat.matchAll(/\[\s*(\d{1,3})%\]\s*(?:Building|Linking)/gi)];
if (llamaBuildMatches.length) { if (llamaBuildMatches.length) {
const pct = Math.min(100, parseInt(llamaBuildMatches[llamaBuildMatches.length - 1][1], 10)); const pct = Math.min(100, parseInt(llamaBuildMatches[llamaBuildMatches.length - 1][1], 10));
@ -2525,9 +2528,10 @@ export function _renderRunningTab() {
} }
const rect = menuBtn.getBoundingClientRect(); const rect = menuBtn.getBoundingClientRect();
const z = parseFloat(getComputedStyle(document.documentElement).zoom) || 1;
dropdown.style.position = 'fixed'; dropdown.style.position = 'fixed';
dropdown.style.top = rect.bottom + 2 + 'px'; dropdown.style.top = rect.bottom + 2 + 'px';
dropdown.style.right = (window.innerWidth - rect.right) + 'px'; dropdown.style.right = ((window.innerWidth / z) - rect.right) + 'px';
document.body.appendChild(dropdown); document.body.appendChild(dropdown);
// Clamp into the *visible* area. On mobile (esp. Firefox) window.innerHeight // Clamp into the *visible* area. On mobile (esp. Firefox) window.innerHeight
// includes the strip hidden under the dynamic toolbar, so a menu that "fits" // includes the strip hidden under the dynamic toolbar, so a menu that "fits"

View file

@ -1077,7 +1077,8 @@ function _rerenderCachedModels() {
cancelDiv.addEventListener('click', () => { closeDropdown(); }); cancelDiv.addEventListener('click', () => { closeDropdown(); });
dropdown.appendChild(cancelDiv); dropdown.appendChild(cancelDiv);
const rect = btn.getBoundingClientRect(); const rect = btn.getBoundingClientRect();
dropdown.style.cssText = `position:fixed;z-index:${topPortalZ()};visibility:hidden;top:0;right:${window.innerWidth-rect.right}px;background:var(--panel);border:1px solid var(--border);border-radius:8px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,0.3);font-size:12px;`; const z = parseFloat(getComputedStyle(document.documentElement).zoom) || 1;
dropdown.style.cssText = `position:fixed;z-index:${topPortalZ()};visibility:hidden;top:0;right:${(window.innerWidth / z) - rect.right}px;background:var(--panel);border:1px solid var(--border);border-radius:8px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,0.3);font-size:12px;`;
document.body.appendChild(dropdown); document.body.appendChild(dropdown);
// Clamp into the VISIBLE area (visualViewport, not innerHeight — they differ // Clamp into the VISIBLE area (visualViewport, not innerHeight — they differ
// on mobile under the dynamic toolbar). Flip above the button if there's no // on mobile under the dynamic toolbar). Flip above the button if there's no