diff --git a/pigway_frame_flow/web/app.js b/pigway_frame_flow/web/app.js index 07b345e..49b01d7 100644 --- a/pigway_frame_flow/web/app.js +++ b/pigway_frame_flow/web/app.js @@ -44,7 +44,7 @@ async function accepted(path,payload){const key='web-'+clientUUID();const result async function waitOperation(id){for(let n=0;n<120;n++){const operation=await api('/api/v1/operations/'+id);if(operation.status==='COMPLETED')return operation;if(['FAILED','UNKNOWN'].includes(operation.status))throw new Error(operation.status==='UNKNOWN'?t('unknown'):(operation.error?.message||t('error')));await new Promise(resolve=>setTimeout(resolve,2000));}throw new Error(t('syncing'));} function showModal(html){ const dialog=$('#modal'),content=$('#modal-content');content.innerHTML=html; - const image=content.querySelector('img');dialog.classList.toggle('image-viewer',!!image); + const image=content.querySelector('img');dialog.classList.toggle('image-viewer',!!image);document.body.classList.toggle('image-viewer-open',!!image);dialog.onclose=()=>document.body.classList.remove('image-viewer-open'); if(image){ const viewport=document.createElement('div');viewport.className='image-viewer-viewport';image.before(viewport);viewport.append(image); const toolbar=document.createElement('div');toolbar.className='actions image-viewer-toolbar'; diff --git a/pigway_frame_flow/web/style.css b/pigway_frame_flow/web/style.css index 4d9574d..a039691 100644 --- a/pigway_frame_flow/web/style.css +++ b/pigway_frame_flow/web/style.css @@ -55,11 +55,17 @@ input[type="file"]::file-selector-button{margin-inline-end:12px} .history-table [data-reconcile]{white-space:nowrap;overflow-wrap:normal;margin-top:8px} -/* Image dialogs use the viewport; forms keep their existing compact width. */ -dialog.image-viewer{width:calc(100vw - 32px);max-width:1600px;max-height:calc(100dvh - 32px);padding:20px;overflow:auto} +/* Reserve actual header/footer space rather than guessing image height. */ +body.image-viewer-open{overflow:hidden} +dialog.image-viewer{width:calc(100vw - 32px);max-width:1600px;height:calc(100dvh - 32px);max-height:calc(100dvh - 32px);padding:20px;overflow:hidden} +dialog.image-viewer[open]{display:flex} +dialog.image-viewer #modal-content{display:flex;flex-direction:column;flex:1;min-width:0;min-height:0} +dialog.image-viewer #modal-content> :not(.image-viewer-viewport){flex-shrink:0} +dialog.image-viewer h2{margin:0;overflow-wrap:anywhere} .image-viewer-toolbar{margin:12px 0} .image-viewer-toolbar button[aria-pressed="true"]{background:var(--accent);color:var(--panel)} -.image-viewer-viewport{max-height:calc(100dvh - 220px);overflow:auto;text-align:center;background:var(--soft);border-radius:12px} -.image-viewer-viewport img{display:block;width:auto;height:auto;max-width:100%;max-height:calc(100dvh - 220px);object-fit:contain;margin:0 auto} +.image-viewer-viewport{display:flex;align-items:center;justify-content:center;flex:1;min-height:0;min-width:0;overflow:hidden;background:var(--soft);border-radius:12px} +.image-viewer-viewport img{display:block;width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;margin:0} +.image-viewer-viewport.actual-size{display:block;overflow:auto} .image-viewer-viewport.actual-size img{width:auto;height:auto;max-width:none;max-height:none;margin:0} -@media(max-width:600px){dialog.image-viewer{width:calc(100vw - 16px);padding:12px}.image-viewer-viewport,.image-viewer-viewport img{max-height:calc(100dvh - 240px)}.image-viewer-viewport.actual-size img{max-height:none}} +@media(max-width:600px){dialog.image-viewer{width:calc(100vw - 16px);height:calc(100dvh - 16px);max-height:calc(100dvh - 16px);padding:12px}}