caterium-app/public/core/performance.js
2026-09-07 17:58:03 +03:00

82 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(()=>{
'use strict';
const critical=img=>img.closest('header,.brand,#sunCloudAuthGate,.sun-auth-gate')||img.id==='sunLoginLogo'||img.classList.contains('sun-live-catalog-logo');
const tune=img=>{
if(!(img instanceof HTMLImageElement)||critical(img))return;
if(!img.hasAttribute('loading'))img.loading='lazy';
if(!img.hasAttribute('decoding'))img.decoding='async';
if(!img.hasAttribute('fetchpriority'))img.setAttribute('fetchpriority','low');
};
const scan=root=>{
if(root instanceof HTMLImageElement)tune(root);
root?.querySelectorAll?.('img').forEach(tune);
};
const MAX_FILE=15*1024*1024,TARGET=2*1024*1024,MAX_SIDE=2048;
const guardedInputs=new WeakSet();
const toast=(text,type='info',ms=4500)=>{try{return window.SunEnterprise?.toast?.(text,type,ms)}catch(_){}};
const jpegName=name=>String(name||'photo').replace(/\.[^.]+$/,'')+'.jpg';
const canvasBlob=(canvas,type,quality)=>new Promise((resolve,reject)=>canvas.toBlob(b=>b?resolve(b):reject(new Error('Не удалось сжать изображение.')),type,quality));
async function decodeImage(file){
if('createImageBitmap' in window){const bitmap=await createImageBitmap(file);return {source:bitmap,width:bitmap.width,height:bitmap.height,close:()=>bitmap.close?.()};}
const url=URL.createObjectURL(file);try{const img=new Image();await new Promise((resolve,reject)=>{img.onload=resolve;img.onerror=reject;img.src=url});return {source:img,width:img.naturalWidth,height:img.naturalHeight,close:()=>URL.revokeObjectURL(url)};}catch(e){URL.revokeObjectURL(url);throw e;}
}
async function compressImage(file){
if(!(file instanceof File)||!String(file.type||'').startsWith('image/')||/gif|svg/i.test(file.type||''))return file;
let decoded;try{decoded=await decodeImage(file);}catch(_){return file;}
try{
const {width,height,source}=decoded;if(!width||!height)return file;
if(file.size<=TARGET&&Math.max(width,height)<=MAX_SIDE)return file;
let scale=Math.min(1,MAX_SIDE/Math.max(width,height)),quality=.84,best=null;
for(let pass=0;pass<5;pass++){
const w=Math.max(1,Math.round(width*scale)),h=Math.max(1,Math.round(height*scale));
const canvas=document.createElement('canvas');canvas.width=w;canvas.height=h;
const ctx=canvas.getContext('2d',{alpha:false});if(!ctx)return file;ctx.fillStyle='#fff';ctx.fillRect(0,0,w,h);ctx.drawImage(source,0,0,w,h);
const blob=await canvasBlob(canvas,'image/jpeg',quality);if(!best||blob.size<best.size)best=blob;if(blob.size<=TARGET)break;
quality=Math.max(.60,quality-.08);scale*=.86;
}
if(!best||best.size>=file.size)return file;
return new File([best],jpegName(file.name),{type:'image/jpeg',lastModified:file.lastModified||Date.now()});
}finally{decoded.close?.();}
}
async function prepareAttachment(file){const prepared=await compressImage(file);if(prepared.size>MAX_FILE)throw new Error(`Файл «${file.name}» больше 15 МБ даже после оптимизации.`);return {file:prepared,originalSize:file.size,savedBytes:Math.max(0,file.size-prepared.size)};}
async function guardChatFiles(input){
const raw=[...(input.files||[])];if(!raw.length)return;
const prepared=[];let saved=0;
for(const f of raw){try{const p=await prepareAttachment(f);prepared.push(p.file);saved+=p.savedBytes;}catch(e){toast(e.message||String(e),'warn',6000);}}
if(!prepared.length){input.value='';return;}
const dt=new DataTransfer();prepared.forEach(f=>dt.items.add(f));input.files=dt.files;
guardedInputs.add(input);input.dispatchEvent(new Event('change',{bubbles:true}));guardedInputs.delete(input);
if(saved>256*1024)toast(`Фото оптимизированы: сэкономлено ${(saved/1024/1024).toFixed(1)} МБ.`,'success',4200);
}
document.addEventListener('change',e=>{
const input=e.target;if(!(input instanceof HTMLInputElement)||input.id!=='sunChatFilesV29'||guardedInputs.has(input)||typeof DataTransfer==='undefined')return;
e.preventDefault();e.stopImmediatePropagation();guardChatFiles(input).catch(err=>{console.error('[Caterium photo compression]',err);guardedInputs.add(input);input.dispatchEvent(new Event('change',{bubbles:true}));guardedInputs.delete(input);});
},true);
window.SunAttachmentGuard={VERSION:'17.6.1',MAX_FILE,TARGET,MAX_SIDE,compressImage,prepareAttachment};
let memoryData=null,memoryAt=0,memoryLoading=false;
const esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??'');
async function loadMemory(){
const now=Date.now();if(memoryLoading)return memoryData;if(memoryData&&now-memoryAt<5000)return memoryData;
const c=window.SunCloudV2?.getClient?.();if(!c)return null;memoryLoading=true;
try{const r=await c.rpc('sun_dev_dashboard');if(r.error)throw r.error;memoryData=r.data||null;memoryAt=Date.now();return memoryData;}catch(_){return null}finally{memoryLoading=false;}
}
async function enhanceDeveloperMemory(){
const view=document.getElementById('sun-developer-console-v22');if(!view?.classList.contains('on'))return;
const body=document.getElementById('sunDevBody');if(!body)return;
const d=await loadMemory();if(!d||!view.classList.contains('on'))return;
let box=document.getElementById('sunDevMemoryV1761');if(!box){box=document.createElement('div');box.id='sunDevMemoryV1761';box.className='sun-dev-grid';box.style.marginBottom='12px';body.prepend(box);}
box.innerHTML=`<div class="sun-dev-kpi"><small>Память сервера</small><b>${esc(d.server_size||d.database_size||'—')}</b></div><div class="sun-dev-kpi"><small>База PostgreSQL</small><b>${esc(d.database_size||'—')}</b></div><div class="sun-dev-kpi"><small>Файлы Storage</small><b>${esc(d.storage_size||'—')}</b></div><div class="sun-dev-kpi"><small>Объектов Storage</small><b>${Number(d.storage_objects||0)}</b></div>`;
}
const start=()=>{
scan(document);
const mo=new MutationObserver(records=>{records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));enhanceDeveloperMemory();});
mo.observe(document.documentElement,{childList:true,subtree:true});
document.addEventListener('click',e=>{if(e.target.closest('#sunDeveloperNavV22,[data-dev-tab],#sunDevRefresh'))setTimeout(enhanceDeveloperMemory,100)},true);
window.addEventListener('sun:cloud-state-applied',()=>setTimeout(enhanceDeveloperMemory,150));
setInterval(()=>{if(document.getElementById('sun-developer-console-v22')?.classList.contains('on'))enhanceDeveloperMemory();},5000);
window.SunPerformance={scanImages:()=>scan(document),refreshDeveloperMemory:enhanceDeveloperMemory,disconnect:()=>mo.disconnect()};
};
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start,{once:true});else start();
})();