1(function(){ 2 'use strict'; 3 const U=window.LocalFlowProcessorUtils,P=window.LocalFlowProcessors.handlers; 4 const out=(text,name,extra)=>U.result(text,name,extra); 5 const files=env=>U.ensureFiles(env.state); 6 const imageFiles=env=>files(env).filter(file=>file.type.startsWith('image/')||/\.(?:png|jpe?g|webp|gif|bmp|heic|heif)$/i.test(file.name)); 7 const mimeFrom=mode=>mode==='jpg'||mode==='jpeg'?'image/jpeg':mode==='webp'?'image/webp':'image/png'; 8 const canvasResult=async(canvas,name,type='image/png',quality=.9)=>{const blob=await U.canvasBlob(canvas,type,quality);return U.blob(blob,name,'å¾çå¤ç宿ã',{previewUrl:canvas.toDataURL(type,quality)});}; 9 const processImages=async(env,transform,name='images.zip')=>{const list=imageFiles(env);if(!list.length)throw new Error('è¯·å æå¼å¾çã');const entries=[];for(let i=0;i<list.length;i++){const result=await transform(list[i],i,list),ext=result.type==='image/jpeg'?'jpg':result.type==='image/webp'?'webp':'png';entries.push({name:result.name||`${list[i].name.replace(/\.[^.]+$/,'')}.${ext}`,data:result.blob});env.progress((i+1)/list.length*100,`æ£å¨å¤ç ${i+1}/${list.length}`);}if(entries.length===1)return U.blob(entries[0].data,entries[0].name,'å¾çå¤ç宿ã',{previewUrl:URL.createObjectURL(entries[0].data)});return U.zip(entries,name);}; 10 11 P['image-compress']=async env=>processImages(env,async file=>{const canvas=await U.canvasFromFile(file),type=mimeFrom(env.mode==='original'?U.ext(file):env.mode),quality=Math.max(.05,Math.min(1,Number(env.option)||.78));return {blob:await U.canvasBlob(canvas,type,quality),type};},'compressed-images.zip'); 12 P['image-resize']=async env=>processImages(env,async file=>{const source=await U.canvasFromFile(file),parts=String(env.option||'').split(/[xÃ,ï¼\s]+/).map(Number),width=Math.max(1,parts[0]||Math.round(source.width*(Number(env.option2)||50)/100)),height=Math.max(1,parts[1]||Math.round(source.height*width/source.width)),canvas=document.createElement('canvas');canvas.width=width;canvas.height=height;canvas.getContext('2d').drawImage(source,0,0,width,height);return {blob:await U.canvasBlob(canvas,'image/png'),type:'image/png'};},'resized-images.zip'); 13 P['image-background-white']=async env=>processImages(env,async file=>{const source=await U.canvasFromFile(file),canvas=document.createElement('canvas');canvas.width=source.width;canvas.height=source.height;const ctx=canvas.getContext('2d');ctx.fillStyle=env.option||'#ffffff';ctx.fillRect(0,0,canvas.width,canvas.height);ctx.drawImage(source,0,0);const type=env.mode==='png'?'image/png':'image/jpeg';return {blob:await U.canvasBlob(canvas,type,Number(env.option2)||.92),type};},'white-background-images.zip'); 14 P['image-cover-maker']=async env=>processImages(env,async file=>{const source=await U.canvasFromFile(file),ratio=env.mode==='square'?1:env.mode==='landscape'?16/9:3/4,target=String(env.option||'1200x1600').split(/[xÃ]/).map(Number),width=target[0]||Math.round((target[1]||1600)*ratio),height=target[1]||Math.round(width/ratio),srcRatio=source.width/source.height;let sx=0,sy=0,sw=source.width,sh=source.height;if(srcRatio>ratio){sw=source.height*ratio;sx=(source.width-sw)/2;}else{sh=source.width/ratio;sy=(source.height-sh)/2;}const canvas=document.createElement('canvas');canvas.width=width;canvas.height=height;canvas.getContext('2d').drawImage(source,sx,sy,sw,sh,0,0,width,height);return {blob:await U.canvasBlob(canvas,'image/jpeg',Number(env.option2)||.92),type:'image/jpeg'};},'cover-images.zip'); 15 P['image-exif-clean']=async env=>processImages(env,async file=>{const canvas=await U.canvasFromFile(file),type=mimeFrom(env.mode);return {blob:await U.canvasBlob(canvas,type,Number(env.option)||.92),type};},'privacy-clean-images.zip'); 16 P['image-nine-grid']=async env=>{const source=await U.canvasFromFile(imageFiles(env)[0]),rows=Math.max(1,Math.min(10,Number(env.option)||3)),cols=Math.max(1,Math.min(10,Number(env.option2)||3)),cellW=Math.floor(source.width/cols),cellH=Math.floor(source.height/rows),entries=[];for(let y=0;y<rows;y++)for(let x=0;x<cols;x++){const canvas=document.createElement('canvas');canvas.width=cellW;canvas.height=cellH;canvas.getContext('2d').drawImage(source,x*cellW,y*cellH,cellW,cellH,0,0,cellW,cellH);entries.push({name:`tile-${y+1}-${x+1}.png`,data:await U.canvasBlob(canvas,'image/png')});}return U.zip(entries,'image-grid.zip');}; 17 P['image-watermark']=async env=>processImages(env,async file=>{const canvas=await U.canvasFromFile(file),ctx=canvas.getContext('2d'),text=env.option||'LocalFlow',size=Math.max(12,Math.round(canvas.width/25));ctx.save();ctx.globalAlpha=Math.max(.05,Math.min(1,Number(env.option2)||.35));ctx.fillStyle='#ffffff';ctx.strokeStyle='rgba(0,0,0,.45)';ctx.lineWidth=Math.max(1,size/18);ctx.font=`600 ${size}px system-ui`;if(env.mode==='tile'){for(let y=size;y<canvas.height;y+=size*4)for(let x=0;x<canvas.width;x+=size*8){ctx.save();ctx.translate(x,y);ctx.rotate(-Math.PI/8);ctx.strokeText(text,0,0);ctx.fillText(text,0,0);
17ctx.restore();}}else{const width=ctx.measureText(text).width,x=canvas.width-width-size,y=canvas.height-size;ctx.strokeText(text,x,y);ctx.fillText(text,x,y);}ctx.restore();return {blob:await U.canvasBlob(canvas,'image/png'),type:'image/png'};},'watermarked-images.zip'); 18 P['image-target-kb']=async env=>processImages(env,async file=>{let canvas=await U.canvasFromFile(file),target=Math.max(10,Number(env.option)||200)*1024,quality=.92,blob=await U.canvasBlob(canvas,'image/jpeg',quality);for(let i=0;i<12&&blob.size>target;i++){quality=Math.max(.25,quality*.82);if(quality<=.3&&blob.size>target){const scale=Math.sqrt(target/blob.size)*.96,next=document.createElement('canvas');next.width=Math.max(1,Math.round(canvas.width*scale));next.height=Math.max(1,Math.round(canvas.height*scale));next.getContext('2d').drawImage(canvas,0,0,next.width,next.height);canvas=next;}blob=await U.canvasBlob(canvas,'image/jpeg',quality);}return {blob,type:'image/jpeg'};},'target-size-images.zip'); 19 P['image-color-palette']=async env=>{const canvas=await U.canvasFromFile(imageFiles(env)[0]),ctx=canvas.getContext('2d'),sample=document.createElement('canvas');sample.width=64;sample.height=64;sample.getContext('2d').drawImage(canvas,0,0,64,64);const data=sample.getContext('2d').getImageData(0,0,64,64).data,map={};for(let i=0;i<data.length;i+=16){const key=[data[i],data[i+1],data[i+2]].map(x=>Math.round(x/32)*32>255?255:Math.round(x/32)*32).join(',');map[key]=(map[key]||0)+1;}const count=Math.max(2,Math.min(32,Number(env.option)||8)),colors=Object.entries(map).sort((a,b)=>b[1]-a[1]).slice(0,count).map(([key,n])=>{const rgb=key.split(',').map(Number),hex='#'+rgb.map(x=>x.toString(16).padStart(2,'0')).join('');return {hex,n};});return out(colors.map(x=>x.hex).join('\n'),'palette.txt',{html:`<div class="lfx-palette">${colors.map(x=>`<div style="background:${x.hex}"><span>${x.hex}</span></div>`).join('')}</div>`});}; 20 P['image-convert']=async env=>processImages(env,async file=>{const canvas=await U.canvasFromFile(file),type=mimeFrom(env.mode),blob=await U.canvasBlob(canvas,type,Number(env.option)||.9);return {blob,type};},'converted-images.zip'); 21 P['image-crop']=async env=>processImages(env,async file=>{const source=await U.canvasFromFile(file),[x=0,y=0,w=source.width,h=source.height]=String(env.option||'').split(/[,ï¼\s]+/).map(Number),canvas=document.createElement('canvas');canvas.width=Math.max(1,Math.min(source.width-x,w));canvas.height=Math.max(1,Math.min(source.height-y,h));canvas.getContext('2d').drawImage(source,x,y,canvas.width,canvas.height,0,0,canvas.width,canvas.height);return {blob:await U.canvasBlob(canvas,mimeFrom(env.mode)),type:mimeFrom(env.mode)};},'cropped-images.zip'); 22 P['image-exif']=async env=>{if(env.mode==='clean')return P['image-exif-clean'](env);await U.load('Exifr');const rows=[];for(const file of imageFiles(env)){const data=await window.exifr.parse(file,{gps:true,tiff:true,exif:true,icc:false,iptc:true})||{};rows.push({file:U.path(file),metadata:data});}return out(JSON.stringify(rows,null,2),'image-metadata.json');}; 23 P['image-heic']=async env=>{await U.load('Heic2Any');const entries=[];for(const file of files(env)){const converted=await window.heic2any({blob:file,toType:env.mode==='png'?'image/png':'image/jpeg',quality:Number(env.option)||.9}),list=Array.isArray(converted)?converted:[converted];list.forEach((blob,i)=>entries.push({name:`${file.name.replace(/\.[^.]+$/,'')}${list.length>1?'-'+(i+1):''}.${env.mode==='png'?'png':'jpg'}`,data:blob}));}return entries.length===1?U.blob(entries[0].data,entries[0].name):U.zip(entries,'converted-heic.zip');}; 24 P['image-id-photo']=async env=>processImages(env,async file=>{const source=await U.canvasFromFile(file),size=env.mode==='one-inch'?[295,413]:env.mode==='two-inch'?[413,626]:String(env.option||'295x413').split(/[xÃ]/).map(Number),width=size[0]||295,height=size[1]||413,ratio=width/height,srcRatio=source.width/source.height;let sx=0,sy=0,sw=source.width,sh=source.height;if(srcRatio>ratio){sw=sh*ratio;sx=(source.width-sw)/2;}else{sh=sw/ratio;sy=(source.height-sh)/2;}const canvas=document.createElement('canvas');canvas.width=width;canvas.height=height;const ctx=canvas.getContext('2d');ctx.fillStyle=env.option2||'#ffffff';ctx.fillRect(0,0,width,height);ctx.drawImage(source,sx,sy,sw,sh,0,0,width,height);return {blob:await U.canvasBlob(canvas,'image/jpeg',.94),type:'image/jpeg'};},'id-photos.zip'); 25 P['image-ocr']=async env=>{await U.load('Tesseract');const rows=[];for(let i=0;i<imageFiles(env).length;i++){const file=imageFiles(env)[i],result=await window.Tesseract.recognize(file,env.option||'chi_sim+eng',{logger:m=>{if(m.progress)env.progress(((i+m.progress)/imageFiles(env).length)*100,m.status||'OCR');}}),text=result.data.text.trim();rows.push(env.option2==='text'?text:`--- ${file.name} ---\n${text}`);}return out(rows.join('\n\n'),'ocr-text.txt');}; 26 P['image-scan-correct']=async env=>processImages(env,async file=>{const source=await U.canvasFromFile(file),angle=(Number(env.option)||0)*Math.PI/180,cos=Math.abs(Math.cos(angle)),sin=Math.abs(Math.sin(angle)),canvas=document.createElement('canvas');canvas.width=Math.ceil(source.width*cos+source.height*sin);canvas.height=Math.ceil(source.width*sin+source.height*cos);const ctx=canvas.getContext('2d');ctx.fillStyle='#fff';ctx.fillRect(0,0,canvas.width,canvas.height);ctx.translate(canvas.width/2,canvas.height/2);ctx.rotate(angle);
26ctx.filter=`contrast(${Math.max(50,Number(env.option2)||125)}%) grayscale(${env.mode==='gray'?100:0}%)`;ctx.drawImage(source,-source.width/2,-source.height/2);return {blob:await U.canvasBlob(canvas,'image/jpeg',.94),type:'image/jpeg'};},'corrected-scans.zip'); 27 P['image-stitch']=async env=>{const list=imageFiles(env),canvases=[];for(const file of list)canvases.push(await U.canvasFromFile(file));const vertical=env.mode!=='horizontal',width=vertical?Math.max(...canvases.map(c=>c.width)):canvases.reduce((n,c)=>n+c.width,0),height=vertical?canvases.reduce((n,c)=>n+c.height,0):Math.max(...canvases.map(c=>c.height)),canvas=document.createElement('canvas');canvas.width=width;canvas.height=height;const ctx=canvas.getContext('2d');ctx.fillStyle=env.option||'#ffffff';ctx.fillRect(0,0,width,height);let offset=0;canvases.forEach(c=>{ctx.drawImage(c,vertical?0:offset,vertical?offset:0);offset+=vertical?c.height:c.width;});return canvasResult(canvas,'stitched-image.png');}; 28 P['image-sprite']=async env=>{const list=imageFiles(env),canvases=[];for(const file of list)canvases.push(await U.canvasFromFile(file));const cols=Math.max(1,Number(env.option)||Math.ceil(Math.sqrt(canvases.length))),cellW=Math.max(...canvases.map(c=>c.width)),cellH=Math.max(...canvases.map(c=>c.height)),rows=Math.ceil(canvases.length/cols),canvas=document.createElement('canvas');canvas.width=cellW*cols;canvas.height=cellH*rows;const ctx=canvas.getContext('2d'),css=[];ctx.clearRect(0,0,canvas.width,canvas.height);canvases.forEach((c,i)=>{const x=i%cols*cellW,y=Math.floor(i/cols)*cellH;ctx.drawImage(c,x,y);css.push(`.sprite-${i+1}{width:${c.width}px;height:${c.height}px;background:url(sprite.png) -${x}px -${y}px}`);});const blob=await U.canvasBlob(canvas,'image/png');return U.zip([{name:'sprite.png',data:blob},{name:'sprite.css',data:css.join('\n')}],'image-sprite.zip');}; 29 30 P['qr-tool']=async env=>{await U.load('QRCode');const size=Math.max(128,Math.min(1024,Number(env.option)||280));let value=String(env.state.input||'').trim();if(env.mode==='wifi')value=`WIFI:T:WPA;S:${value};P:${env.option2};;`;if(env.mode==='email')value=`mailto:${value}`;if(!value)throw new Error('请è¾å ¥äºç»´ç å 容ã');const box=document.createElement('div');new window.QRCode(box,{text:value,width:size,height:size,correctLevel:window.QRCode.CorrectLevel?.M});await new Promise(resolve=>setTimeout(resolve,50));const canvas=box.querySelector('canvas'),img=box.querySelector('img'),blob=canvas?await U.canvasBlob(canvas,'image/png'):await(await fetch(img.src)).blob();return U.blob(blob,'qrcode.png',value,{previewUrl:canvas?canvas.toDataURL('image/png'):img.src});}; 31 P['barcode-generator']=async env=>{await U.load('BwipJs');const canvas=document.createElement('canvas');window.bwipjs.toCanvas(canvas,{bcid:env.mode||'code128',text:U.ensureInput(env.state),scale:Math.max(1,Number(env.option)||3),height:Math.max(5,Number(env.option2)||12),includetext:true,textxalign:'center'});return canvasResult(canvas,'barcode.png');}; 32 const scanCode=async env=>{await U.load('ZXing');const file=imageFiles(env)[0],url=URL.createObjectURL(file),img=new Image();img.src=url;await img.decode();try{const reader=new window.ZXing.BrowserMultiFormatReader(),result=await reader.decodeFromImageElement(img);return out(JSON.stringify({text:result.getText(),format:String(result.getBarcodeFormat()),file:file.name},null,2),'code-result.json');}finally{URL.revokeObjectURL(url);}}; 33 P['qr-reader']=scanCode;P['image-barcode-scan']=scanCode; 34 35 const encodeWav=(buffer,start=0,end=buffer.length,effects={})=>{const channels=buffer.numberOfChannels,rate=buffer.sampleRate,length=end-start,bytes=44+length*channels*2,array=new ArrayBuffer(bytes),view=new DataView(array),write=(offset,text)=>[...text].forEach((ch,i)=>view.setUint8(offset+i,ch.charCodeAt(0)));let peak=0;for(let i=start;i<end;i++)for(let c=0;c<channels;c++)peak=Math.max(peak,Math.abs(buffer.getChannelData(c)[i]));const gain=(effects.normalize&&peak>.000001?Math.min(4,.98/peak):1)*Math.max(0,Math.min(2,Number(effects.volume)||1)),fadeIn=Math.max(0,Math.min(length,Math.round((Number(effects.fadeIn)||0)*rate))),fadeOut=Math.max(0,Math.min(length,Math.round((Number(effects.fadeOut)||0)*rate)));write(0,'RIFF');view.setUint32(4,bytes-8,true);write(8,'WAVE');write(12,'fmt ');view.setUint32(16,16,true);view.setUint16(20,1,true);view.setUint16(22,channels,true);view.setUint32(24,rate,true);view.setUint32(28,rate*channels*2,true);view.setUint16(32,channels*2,true);view.setUint16(34,16,true);write(36,'data');view.setUint32(40,length*channels*2,true);let offset=44;for(let i=0;i<length;i++){let envelope=1;if(fadeIn)envelope=Math.min(envelope,(i+1)/fadeIn);if(fadeOut)envelope=Math.min(envelope,(length-i)/fadeOut);for(let c=0;c<channels;c++){const sample=Math.max(-1,Math.min(1,buffer.getChannelData(c)[start+i]*gain*envelope));view.setInt16(offset,sample<0?sample*0x8000:sample*0x7fff,true);offset+=2;}}return new Blob([array],{type:'audio/wav'});}; 36 P['audio-trim']=async env=>{const file=files(env)[0],context=new AudioContext(),buffer=await context.decodeAudioData(await file.arrayBuffer()),start=Math.max(0,Number(env.option)||0),end=Math.min(buffer.duration,Number(env.option2)||buffer.duration),effects=(()=>{try{return JSON.parse(env.option3||'{}')}catch{return {}}})();if(end<=start)throw new Error('ç»ææ¶é´å¿ 须大äºå¼å§æ¶é´ã');const blob=encodeWav(buffer,Math.floor(start*buffer.sampleRate),Math.floor(end*buffer.sampleRate),effects);await context.close();return U.blob(blob,`${file.name.replace(/\.[^.]+$/,'')}-enhanced.wav`,`${start.toFixed(2)}â${end.toFixed(2)} ç§ Â· ${effects.normalize?'å·²æ åå · ':''}WAV è¾åºã`);}; 37 const recorder=async(env,display)=>{const state=env.state;if(env.mode==='start'||env.mode==='record'){if(state.recorder?.state==='recording')throw new Error('æ£å¨å½å¶ã');if(!navigator.mediaDevices?.getDisplayMedia)throw new Error('å½åæµè§å¨ä¸æ¯æå±å¹æè·ã');const withAudio=env.option!=='no-audio',stream=display?await navigator.mediaDevices.getDisplayMedia({video:true,audio:withAudio}):await navigator.mediaDevices.getUserMedia({audio:true}
37),chunks=[],mime=display?(withAudio&&MediaRecorder.isTypeSupported('video/webm;codecs=vp9,opus')?'video/webm;codecs=vp9,opus':'video/webm'):'audio/webm';if(!window.MediaRecorder||!MediaRecorder.isTypeSupported(mime)){stream.getTracks().forEach(track=>track.stop());throw new Error('å½åæµè§å¨ä¸æ¯æå¯ç¨ç WebM ç¼ç ã');}const rec=new MediaRecorder(stream,{mimeType:mime});rec.ondataavailable=e=>{if(e.data.size)chunks.push(e.data);};rec.start(500);state.recorder=rec;state.recordChunks=chunks;state.recordStream=stream;state.recordStartedAt=Date.now();return out('å½å¶å·²å¼å§ã','recording-status.txt');}const rec=state.recorder;if(!rec)throw new Error('å½å没æè¿è¡ä¸çå½å¶ã');if(env.mode==='pause'){if(rec.state!=='recording')throw new Error('å½åå½é³å·²ç»æåã');rec.pause();return out('å½é³å·²æåã','recording-status.txt');}if(env.mode==='resume'){if(rec.state!=='paused')throw new Error('å½åå½é³æ²¡æå¤äºæåç¶æã');rec.resume();return out('å½é³å·²ç»§ç»ã','recording-status.txt');}if(rec.state!=='recording'&&rec.state!=='paused')throw new Error('å½å没æè¿è¡ä¸çå½å¶ã');const blob=await new Promise(resolve=>{rec.onstop=()=>resolve(new Blob(state.recordChunks,{type:rec.mimeType}));rec.stop();});state.recordStream?.getTracks().forEach(track=>track.stop());state.recorder=null;return U.blob(blob,display?'screen-recording.webm':'audio-recording.webm','å½å¶å·²åæ¢å¹¶çææ°æä»¶ã');}; 38 P['audio-record']=env=>recorder(env,false);P['screen-record']=env=>recorder(env,true); 39 40 function detectScanBounds(source){ 41 const g=source.getContext('2d'),w=source.width,h=source.height,data=g.getImageData(0,0,w,h).data,step=Math.max(1,Math.floor(Math.max(w,h)/1000)); 42 let minX=w,minY=h,maxX=-1,maxY=-1,found=0; 43 for(let y=0;y<h;y+=step)for(let x=0;x<w;x+=step){const i=(y*w+x)*4;if(data[i+3]<20)continue;const lum=.299*data[i]+.587*data[i+1]+.114*data[i+2];if(lum<242){minX=Math.min(minX,x);minY=Math.min(minY,y);maxX=Math.max(maxX,x);maxY=Math.max(maxY,y);found++;}} 44 if(!found)return {x:0,y:0,w,h}; 45 const pad=Math.max(8,Math.round(Math.min(w,h)*.03)),x=Math.max(0,minX-pad),y=Math.max(0,minY-pad),right=Math.min(w,maxX+step+pad),bottom=Math.min(h,maxY+step+pad),bw=right-x,bh=bottom-y; 46 return bw/w<.25||bh/h<.25?{x:0,y:0,w,h}:{x,y,w:bw,h:bh}; 47 } 48 P['image-scan-correct']=async env=>processImages(env,async file=>{const source=await U.canvasFromFile(file),auto=!!env.root?.querySelector('#iscAutoCrop')?.checked,crop=auto?detectScanBounds(source):{x:0,y:0,w:source.width,h:source.height},angle=(Number(env.option)||0)*Math.PI/180,cos=Math.abs(Math.cos(angle)),sin=Math.abs(Math.sin(angle)),canvas=document.createElement('canvas');canvas.width=Math.ceil(crop.w*cos+crop.h*sin);canvas.height=Math.ceil(crop.w*sin+crop.h*cos);const g=canvas.getContext('2d');g.fillStyle='#fff';g.fillRect(0,0,canvas.width,canvas.height);g.translate(canvas.width/2,canvas.height/2);g.rotate(angle);g.filter=`contrast(${Math.max(50,Number(env.option2)||125)}%) grayscale(${env.mode==='gray'?100:0}%)`;g.drawImage(source,crop.x,crop.y,crop.w,crop.h,-crop.w/2,-crop.h/2,crop.w,crop.h);return {blob:await U.canvasBlob(canvas,'image/jpeg',.94),type:'image/jpeg'};},'corrected-scans.zip'); 49})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.