Enlarge image viewer with fit and actual-size modes

This commit is contained in:
Pigway
2026-10-08 19:40:05 +08:00
parent 0b9b548d20
commit dafcf8ecb4
3 changed files with 25 additions and 1 deletions
+13 -1
View File
@@ -42,7 +42,19 @@ function login(){ $('#main').innerHTML=`<div class="card" style="max-width:560px
function clientUUID(){if(typeof crypto.randomUUID==='function')return crypto.randomUUID();const bytes=crypto.getRandomValues(new Uint8Array(16));bytes[6]=(bytes[6]&15)|64;bytes[8]=(bytes[8]&63)|128;const hex=Array.from(bytes,b=>b.toString(16).padStart(2,'0')).join('');return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`;}
async function accepted(path,payload){const key='web-'+clientUUID();const result=await api(path,{method:'POST',body:payload,headers:{'Idempotency-Key':key}});toast(t('accepted'));return result.operation_id;}
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){$('#modal-content').innerHTML=html;$('#modal').showModal();bind('modal-cancel',()=>$('#modal').close());}
function showModal(html){
const dialog=$('#modal'),content=$('#modal-content');content.innerHTML=html;
const image=content.querySelector('img');dialog.classList.toggle('image-viewer',!!image);
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';
const fit=document.createElement('button'),original=document.createElement('button');fit.textContent=t('fitScreen');original.textContent=t('actualSize');
fit.type=original.type='button';fit.setAttribute('aria-pressed','true');original.setAttribute('aria-pressed','false');toolbar.append(fit,original);viewport.before(toolbar);
const setSize=actual=>{viewport.classList.toggle('actual-size',actual);fit.setAttribute('aria-pressed',String(!actual));original.setAttribute('aria-pressed',String(actual));};
fit.onclick=()=>setSize(false);original.onclick=()=>setSize(true);
}
dialog.showModal();bind('modal-cancel',()=>dialog.close());
}
async function confirmDialog(message,fn){showModal(`<h2>${t('confirm')}</h2><p>${esc(message)}</p><div class="modal-actions">${buttons('modal-cancel','cancel')}${buttons('modal-confirm','confirm',true)}</div>`);bind('modal-confirm',async()=>{await fn();$('#modal').close();});}
function detail(label,value){return `<div><small>${t(label)}</small><strong>${esc(value)}</strong></div>`;}
function liveWriteAllowed(){return state.settings?.mock||state.settings?.write_enabled;}
+3
View File
@@ -40,3 +40,6 @@ Object.assign(window.MESSAGES.zh,{cloudKeepLatest:'随机任务云端仅保留
Object.assign(window.MESSAGES.en,{cloudKeepLatest:'Each random task keeps its latest confirmed cloud image. Uploaded local favorites stay. Failed replacements never evict the last good image.',taskOrigin:'Task generated',originFilter:'Filter by origin',origin_all:'All origins',origin_manual:'Manual upload',origin_task:'Task generated',origin_external:'External push',origin_template:'Template generated',origin_unknown:'Unknown origin',allTasks:'All tasks',selectImage:'Select image',selectAll:'Select / deselect all',deleteSelected:'Delete selected',selectImagesFirst:'Select images first',deleteSelectedConfirm:'Delete selected local images? NAS originals remain untouched.',batchFailed:'Failed items:'});
Object.assign(window.MESSAGES.zh,{previousPage:'上一页',nextPage:'下一页'});Object.assign(window.MESSAGES.en,{previousPage:'Previous page',nextPage:'Next page'});
Object.assign(window.MESSAGES.zh,{fitScreen:'适应屏幕',actualSize:'原始尺寸(1:1)'});
Object.assign(window.MESSAGES.en,{fitScreen:'Fit to screen',actualSize:'Actual size (1:1)'});
+9
View File
@@ -54,3 +54,12 @@ input[type="file"]::file-selector-button{margin-inline-end:12px}
.source-items button small{display:block;margin-top:8px;color:var(--muted)}
.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}
.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.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}}