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;}