reworked zooming

This commit is contained in:
John Lancaster
2026-06-29 18:00:22 -05:00
parent e35a8ec060
commit 67b0980664
@@ -60,16 +60,16 @@ def _register_panzoom_assets() -> None:
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
align-items: flex-start;
justify-content: flex-start;
}
.document-panzoom-media {
width: auto;
height: auto;
display: block;
max-width: none;
max-height: none;
max-width: 100%;
max-height: 100%;
user-select: none;
-webkit-user-drag: none;
}
@@ -99,79 +99,76 @@ def _attach_panzoom(host_id: str) -> None:
const media = host.querySelector('[data-panzoom-media]');
if (!target) return;
const cleanup = () => {{
const existing = window.__transcriptionPanzoom[{host_id!r}];
if (existing?.resizeObserver) existing.resizeObserver.disconnect();
if (existing?.wheelHandler) host.removeEventListener('wheel', existing.wheelHandler);
if (existing?.instance) existing.instance.destroy();
}};
const computeFitScale = () => {{
const hostRect = host.getBoundingClientRect();
if (hostRect.width <= 0 || hostRect.height <= 0) return 1;
if (media && media.tagName === 'IMG') {{
if (media.naturalWidth <= 0 || media.naturalHeight <= 0) return 1;
return Math.min(
hostRect.width / media.naturalWidth,
hostRect.height / media.naturalHeight
);
}}
if (target.tagName === 'IFRAME') {{
const targetRect = target.getBoundingClientRect();
if (targetRect.width <= 0 || targetRect.height <= 0) return 1;
return hostRect.height / targetRect.height;
}}
if (hostRect.width <= 0 || hostRect.height <= 0) return null;
return 1;
}};
const initPanzoom = () => {{
if (window.__transcriptionPanzoom[{host_id!r}]) {{
window.__transcriptionPanzoom[{host_id!r}].destroy();
}}
const buildInstance = () => {{
cleanup();
if (host.__transcriptionWheelHandler) {{
host.removeEventListener('wheel', host.__transcriptionWheelHandler);
host.__transcriptionWheelHandler = null;
}}
const fitScale = computeFitScale();
if (fitScale === null) return false;
let fitScale = computeFitScale();
if (!Number.isFinite(fitScale) || fitScale <= 0) {{
fitScale = 1;
}}
const startScale = fitScale;
const minScale = 0.01;
const minScale = Math.min(fitScale, 0.01);
const instance = Panzoom(target, {{
maxScale: 32,
startX: 0,
startY: 0,
startScale: fitScale,
minScale: minScale,
startScale: startScale,
step: 0.18,
contain: 'inside',
roundPixels: true,
maxScale: 256,
step: 0.2,
roundPixels: false,
panOnlyWhenZoomed: true,
overflow: 'hidden',
}});
window.__transcriptionPanzoom[{host_id!r}] = instance;
const wheelHandler = (event) => instance.zoomWithWheel(event);
host.addEventListener('wheel', wheelHandler, {{ passive: false }});
requestAnimationFrame(() => {{
instance.reset({{ animate: false, force: true }});
instance.setOptions({{ contain: undefined }});
instance.reset({{ animate: false }});
}});
const wheelHandler = (event) => instance.zoomWithWheel(event);
host.__transcriptionWheelHandler = wheelHandler;
host.addEventListener('wheel', wheelHandler, {{ passive: false }});
const resizeObserver = new ResizeObserver(() => {{
const nextFitScale = computeFitScale();
if (nextFitScale === null) return;
instance.setOptions({{
startScale: nextFitScale,
minScale: Math.min(nextFitScale, 0.01),
}});
instance.reset({{ animate: false }});
}});
resizeObserver.observe(host);
window.__transcriptionPanzoom[{host_id!r}] = {{
instance,
wheelHandler,
resizeObserver,
}};
return true;
}};
const initWhenReady = (retries = 15) => {{
if (buildInstance()) return;
if (retries <= 0) return;
requestAnimationFrame(() => initWhenReady(retries - 1));
}};
if (media && media.tagName === 'IMG' && !media.complete) {{
media.addEventListener('load', initPanzoom, {{ once: true }});
media.addEventListener('load', () => initWhenReady(), {{ once: true }});
return;
}}
if ((media && media.tagName === 'IMG' && (host.clientWidth <= 0 || host.clientHeight <= 0)) ||
(target.tagName === 'IFRAME' && (host.clientWidth <= 0 || host.clientHeight <= 0))) {{
requestAnimationFrame(initPanzoom);
return;
}}
initPanzoom();
initWhenReady();
}})();
"""
)
@@ -186,10 +183,11 @@ def render_document_panzoom(*, document: Document, height: str = "640px") -> Non
document_kind = _document_kind(document)
with ui.card().classes("w-full q-pa-md"):
with ui.column().classes("q-gutter-none"):
with ui.row().classes("w-full items-center justify-between q-gutter-sm"):
ui.label("Document preview").classes("text-subtitle1 text-weight-medium")
ui.label(document.filename).classes("text-caption text-grey-4")
with ui.row().classes("w-full items-center justify-between no-wrap"):
ui.label("Document preview").classes("text-subtitle1 text-weight-medium")
ui.label(document.filename).classes("text-caption text-grey-4 ellipsis").style(
"max-width: 60%; text-align: right;"
)
with (
ui.element("div")