Files
pixel-bead-pattern/out/_next/static/chunks/page-eSu4rPbx.js
T

1 line
26 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.
import{r as e}from"./rolldown-runtime-C60lm6uB.js";import{i as t,r as n}from"./framework-BgSIrAUN.js";var r=e(n(),1),i=Object.entries({A:`#FAF4C8 #FFFFD5 #FEFF8B #FBED56 #F4D738 #FEAC4C #FE8B4C #FFDA45 #FF995B #F77C31 #FFDD99 #FE9F72 #FFC365 #FD543D #FFF365 #FFFF9F #FFE36E #FEBE7D #FD7C72 #FFD568 #FFE395 #F4F57D #E6C9B7 #F7F8A2 #FFD67D #FFC830`,B:`#E6EE31 #63F347 #9EF780 #5DE035 #35E352 #65E2A6 #3DAF80 #1C9C4F #27523A #95D3C2 #5D722A #166F41 #CAEB7B #ADE946 #2E5132 #C5ED9C #9BB13A #E6EE49 #24B88C #C2F0CC #156A6B #0B3C43 #303A21 #EEFCA5 #4E846D #8D7A35 #CCE1AF #9EE5B9 #C5E254 #E2FCB1 #B0E792 #9CAB5A`,C:`#E8FFE7 #A9F9FC #A0E2FB #41CCFF #01ACEB #50AAF0 #3677D2 #0F54C0 #324BCA #3EBCE2 #28DDDE #1C334D #CDE8FF #D5FDFF #22C4C6 #1557A8 #04D1F6 #1D3344 #1887A2 #176DAF #BEDDFF #67B4BE #C8E2FF #7CC4FF #A9E5E5 #3CAED8 #D3DFFA #BBCFED #34488E`,D:`#AEB4F2 #858EDD #2F54AF #182A84 #B843C5 #AC7BDE #8854B3 #E2D3FF #D5B9F8 #361851 #B9BAE1 #DE9AD4 #B90095 #8B279B #2F1F90 #E3E1EE #C4D4F6 #A45EC7 #D8C3D7 #9C32B2 #9A009B #333A95 #EBDAFC #7786E5 #494FC7 #DFC2F8`,E:`#FDD3CC #FEC0DF #FFB7E7 #E8649E #F551A2 #F13D74 #C63478 #FFDBE9 #E970CC #D33793 #FCDDD2 #F78FC3 #B5006D #FFD1BA #F8C7C9 #FFF3EB #FFE2EA #FFC7DB #FEBAD5 #D8C7D1 #BD9DA1 #B785A1 #937A8D #E1BCE8`,F:`#FD957B #FC3D46 #F74941 #FC283C #E7002F #943630 #971937 #BC0028 #E2677A #8A4526 #5A2121 #FD4E6A #F35744 #FFA9AD #D30022 #FEC2A6 #E69C79 #D37C46 #C1444A #CD9391 #F7B4C6 #FDC0D0 #F67E66 #E698AA #E54B4F`,G:`#FFE2CE #FFC4AA #F4C3A5 #E1B383 #EDB045 #E99C17 #9D5B3E #753832 #E6B483 #D98C39 #E0C593 #FFC890 #B7714A #8D614C #FCF9E0 #F2D9BA #78524B #FFE4CC #E07935 #A94023 #B88558`,H:`#FDFBFF #FEFFFF #B6B1BA #89858C #48464E #2F2B2F #000000 #E7D6DB #EDEDED #EEE9EA #CECDD5 #FFF5ED #F5ECD2 #CFD7D3 #98A6A8 #1D1414 #F1EDED #FFFDF0 #F6EFE2 #949FA3 #FFFBE1 #CACAD4 #9A9D94`,M:`#BCC6B8 #8AA386 #697D80 #E3D2BC #D0CCAA #B0A782 #B4A497 #B38281 #A58767 #C5B2BC #9F7594 #644749 #D19066 #C77362 #757D78`}).flatMap(([e,t])=>t.split(` `).map((t,n)=>[e+(n+1),t])),a=t();function o(e){fetch(`/api/usage`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e),keepalive:!0}).catch(()=>void 0)}var s=128,c=30,l=`bead-pattern-history`,u=`pixel-bead-pattern-history`,d=`patterns`;function f(){return new Promise((e,t)=>{let n=indexedDB.open(u,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(d)||n.result.createObjectStore(d)},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)})}async function p(){let e=await f();return new Promise((t,n)=>{let r=e.transaction(d,`readonly`),i=r.objectStore(d).get(l);i.onsuccess=()=>t(Array.isArray(i.result)?i.result:[]),i.onerror=()=>n(i.error),r.oncomplete=()=>e.close()})}async function m(e){let t=await f();return new Promise((n,r)=>{let i=t.transaction(d,`readwrite`);i.objectStore(d).put(e,l),i.oncomplete=()=>{t.close(),n()},i.onerror=()=>{t.close(),r(i.error)},i.onabort=()=>{t.close(),r(i.error)}})}var h={A:`黄橙系`,B:`绿色系`,C:`蓝青系`,D:`紫蓝系`,E:`粉红系`,F:`红色系`,G:`肤棕系`,H:`黑白灰系`,M:`莫兰迪系`};function g([e,t,n]){let r=e=>{let t=e/255;return t<=.04045?t/12.92:((t+.055)/1.055)**2.4},i=r(e),a=r(t),o=r(n),s=Math.cbrt(.4122214708*i+.5363325363*a+.0514459929*o),c=Math.cbrt(.2119034982*i+.6806995451*a+.1073969566*o),l=Math.cbrt(.0883024619*i+.2817188376*a+.6299787005*o);return[.2104542553*s+.793617785*c-.0040720468*l,1.9779984951*s-2.428592205*c+.4505937099*l,.0259040371*s+.7827717662*c-.808675766*l]}function _(e,t){return Math.hypot(e[0]-t[0],e[1]-t[1],e[2]-t[2])}var v=i.map(([e,t])=>{let n=[parseInt(t.slice(1,3),16),parseInt(t.slice(3,5),16),parseInt(t.slice(5,7),16)];return{code:e,name:`MARD ${h[e[0]]}`,hex:t,rgb:n,lab:g(n)}}),y=e=>[parseInt(e.slice(1,3),16),parseInt(e.slice(3,5),16),parseInt(e.slice(5,7),16)];function ee(e,t){let n=t[0],r=1/0;for(let i of t){let t=_(e,i.lab);t<r&&(r=t,n=i)}return n}function te(e){let t=[],n=new Map;for(let r=0;r<e.length;r+=4){if(e[r+3]<s){t.push(null);continue}let i=e[r],a=e[r+1],o=e[r+2],c=i>>3<<10|a>>3<<5|o>>3;t.push(c);let l=n.get(c);l?(l.count+=1,l.red+=i,l.green+=a,l.blue+=o):n.set(c,{count:1,red:i,green:a,blue:o})}let r=[...n.entries()].map(([e,t])=>{let n=[t.red/t.count,t.green/t.count,t.blue/t.count];return{key:e,count:t.count,rgb:n,lab:g(n)}}).sort((e,t)=>t.count-e.count),i=[];for(let e of r){let t=-1,n=1/0;for(let r=0;r<i.length;r++){let a=_(e.lab,i[r].lab);a<n&&(n=a,t=r)}if(t>=0&&n<=.025){let n=i[t],r=n.count+e.count;n.rgb=n.rgb.map((t,i)=>(t*n.count+e.rgb[i]*e.count)/r),n.lab=n.lab.map((t,i)=>(t*n.count+e.lab[i]*e.count)/r),n.count=r}else i.push({count:e.count,rgb:[...e.rgb],lab:[...e.lab]})}let a=new Map;for(let e of r){let t=0,n=1/0;i.forEach((r,i)=>{let a=_(e.lab,r.lab);a<n&&(n=a,t=i)}),a.set(e.key,t)}return{pixelKeys:t,clusters:i,binClusters:a}}function ne(e,t){let n=new Map;for(let t of e){let e=ee(t.lab,v),r=_(t.lab,e.lab),i=Math.hypot(t.lab[1],t.lab[2]),a=1+Math.min(.65,i*2.2)+(t.lab[0]<.28?.35:0),o=n.get(e.code)??{color:e,count:0,error:0,score:0};o.count+=t.count,o.error+=r*t.count,o.score+=t.count*a/(1+r*5),n.set(e.code,o)}let r=[],i=[...n.values()].sort((e,t)=>t.score-e.score||e.error/e.count-t.error/t.count);for(let e of i)r.every(t=>_(e.color.lab,t.lab)>=.035)&&r.push(e.color);return r.slice(0,Math.max(0,Math.min(t,r.length)))}function re(e,t,n,r,i,a,o){let c=document.createElement(`canvas`);c.width=t*5,c.height=n*5;let l=c.getContext(`2d`,{willReadFrequently:!0});l.clearRect(0,0,c.width,c.height),b(l,e,c.width,c.height,r,i,a,o);let u=l.getImageData(0,0,c.width,c.height).data,d=new Uint8ClampedArray(t*n*4),f=new Float32Array(t*n);for(let e=0;e<n;e++)for(let n=0;n<t;n++){let r=[],i=0;for(let t=0;t<5;t++)for(let a=0;a<5;a++){let o=((e*5+t)*c.width+n*5+a)*4;if(u[o+3]<s){i+=1;continue}let l=[u[o],u[o+1],u[o+2]],d=g(l),f=a===2&&t===2,p,m=1/0;for(let e of r){let t=_(d,e.lab);t<m&&(p=e,m=t)}if(p&&m<=.04){let e=p.count+1;p.lab=p.lab.map((t,n)=>(t*p.count+d[n])/e),p.count=e,p.samples.push({rgb:l,lab:d}),p.containsCenter||=f}else r.push({count:1,lab:[...d],samples:[{rgb:l,lab:d}],containsCenter:f})}let a=(e*t+n)*4;if(i>=25/2||r.length===0){d[a+3]=0,f[e*t+n]=i/25;continue}let o=r.sort((e,t)=>t.count-e.count||Number(t.containsCenter)-Number(e.containsCenter))[0],l=o.samples.reduce((e,t)=>_(t.lab,o.lab)<_(e.lab,o.lab)?t:e);d[a]=l.rgb[0],d[a+1]=l.rgb[1],d[a+2]=l.rgb[2],d[a+3]=255,f[e*t+n]=o.count/Math.max(1,25-i)}return{data:d,confidence:f}}function ie(e,t,n,r){let i=[...e],a=0,o=[-1,0,1];for(let s=0;s<r;s++)for(let c=0;c<n;c++){let l=s*n+c,u=e[l];if(!u||t[l]>=.625)continue;let d=Math.hypot(u.lab[1],u.lab[2]);if(u.lab[0]<.32||u.lab[0]>.96||d>.14)continue;let f=new Map,p=0,m=0;for(let i of o)for(let a of o){if(i===0&&a===0)continue;let o=s+i,l=c+a;if(o<0||l<0||o>=r||l>=n)continue;let d=o*n+l,h=e[d];if(!h)continue;h.code===u.code&&(m+=1);let g=f.get(h.code)??{color:h,count:0,confidentCount:0};g.count+=1,t[d]>=.625&&(g.confidentCount+=1),f.set(h.code,g),p+=1}if(m>0)continue;let h=[...f.values()].sort((e,t)=>t.count-e.count)[0];if(!h||h.color.code===u.code)continue;let g=p>=7?5:Math.max(2,Math.ceil(p*.67));h.count<g||h.confidentCount<Math.min(3,h.count)||_(u.lab,h.color.lab)>.075||(i[l]=h.color,a+=1)}return{colors:i,changed:a}}function b(e,t,n,r,i,a,o,s){let c=t.naturalWidth/t.naturalHeight,l=n/r,u=n,d=r;i===`cover`&&c>l||i===`contain`&&c<l?(d=r,u=r*c):(u=n,d=n/c),u*=a,d*=a;let f=Math.max(0,(u-n)/2),p=Math.max(0,(d-r)/2),m=(n-u)/2+o*f,h=(r-d)/2+s*p;e.drawImage(t,m,h,u,d)}function x(){let e=document.createElement(`canvas`);e.width=640,e.height=640;let t=e.getContext(`2d`),n=t.createLinearGradient(0,0,0,640);return n.addColorStop(0,`#bfe8f1`),n.addColorStop(.65,`#fff2ce`),n.addColorStop(1,`#f6c783`),t.fillStyle=n,t.fillRect(0,0,640,640),t.fillStyle=`#f6c742`,t.beginPath(),t.arc(488,134,70,0,Math.PI*2),t.fill(),t.fillStyle=`#58a95b`,t.beginPath(),t.moveTo(0,455),t.quadraticCurveTo(135,310,300,455),t.quadraticCurveTo(480,285,640,435),t.lineTo(640,640),t.lineTo(0,640),t.fill(),t.fillStyle=`#27865a`,t.beginPath(),t.moveTo(0,535),t.quadraticCurveTo(180,390,350,520),t.quadraticCurveTo(505,410,640,505),t.lineTo(640,640),t.lineTo(0,640),t.fill(),t.fillStyle=`#f7f5ed`,t.fillRect(245,390,152,126),t.fillStyle=`#d9383a`,t.beginPath(),t.moveTo(218,405),t.lineTo(321,323),t.lineTo(424,405),t.closePath(),t.fill(),t.fillStyle=`#754a32`,t.fillRect(300,447,43,69),t.fillStyle=`#79cbe1`,t.fillRect(258,415,39,37),t.fillRect(350,415,34,37),e.toDataURL(`image/png`)}function S(){let[e,t]=(0,r.useState)(``),[n,i]=(0,r.useState)(`示例:田野小屋`),[s,u]=(0,r.useState)(100),[d,f]=(0,r.useState)(100),[h,g]=(0,r.useState)(100),[_,S]=(0,r.useState)(100),[C,ae]=(0,r.useState)(64),[w,oe]=(0,r.useState)(`contain`),[T,E]=(0,r.useState)(`dominant`),[D,O]=(0,r.useState)(1),[k,A]=(0,r.useState)(0),[j,M]=(0,r.useState)(0),[N,se]=(0,r.useState)([]),[P,F]=(0,r.useState)(new Set),[I,ce]=(0,r.useState)(!1),[L,R]=(0,r.useState)(8),[z,le]=(0,r.useState)(!0),[B,ue]=(0,r.useState)(!0),[V,de]=(0,r.useState)(``),[H,U]=(0,r.useState)(null),[W,fe]=(0,r.useState)([]),[G,pe]=(0,r.useState)(``),[me,K]=(0,r.useState)(`示例图已准备好,可以直接转换`),q=(0,r.useRef)(null),he=(0,r.useRef)(null),ge=(0,r.useRef)(null),J=(0,r.useRef)(null),Y=(0,r.useRef)(null),_e=(0,r.useRef)(null),X=(0,r.useRef)(null),Z=(0,r.useRef)(null),Q=(0,r.useRef)(!1);(0,r.useEffect)(()=>{o({event:`page_view`});let e=!1;return(async()=>{try{let t=await p(),n=localStorage.getItem(l);if(t.length===0&&n){let e=JSON.parse(n);Array.isArray(e)&&(t=e.slice(0,c),await m(t)),localStorage.removeItem(l)}e||fe(t.slice(0,c))}catch{e||K(`历史记录读取失败,请检查浏览器是否允许本地存储`)}})(),()=>{e=!0}},[]);let ve=(e=q.current,t={zoom:D,x:k,y:j},n=!0)=>{if(!e)return;let r=Math.max(8,Math.min(256,s)),i=Math.max(8,Math.min(256,d)),a,c=null;if(T===`dominant`){let n=re(e,r,i,w,t.zoom,t.x,t.y);a=n.data,c=n.confidence}else{let n=document.createElement(`canvas`);n.width=r,n.height=i;let o=n.getContext(`2d`,{willReadFrequently:!0});o.clearRect(0,0,r,i),b(o,e,r,i,w,t.zoom,t.x,t.y),a=o.getImageData(0,0,r,i).data}let{pixelKeys:l,clusters:p,binClusters:m}=te(a),h=ne(p,Math.max(2,Math.min(C,v.length))),_=p.map(e=>ee(e.lab,h)),y=l.map(e=>e===null?null:_[m.get(e)??0]),x=0;if(c){let e=ie(y,c,r,i);y=e.colors,x=e.changed}let ae=y.map(e=>({color:e}));g(r),S(i),u(r),f(i),se(ae),F(new Set);let oe=T===`dominant`?`区域主色 · 清理 ${x} 个低可信孤立格`:`平滑取色`,E=new Set(y.filter(e=>e!==null).map(e=>e.code)).size,O=y.filter(e=>e!==null).length;n&&o({event:`conversion`,width:r,height:i,sampling_strategy:T,color_limit:C,actual_colors:E,bead_count:O,transparent_cells:r*i-O}),K(`已转换为 ${r} × ${i} · ${O} 颗拼豆 · ${oe} · ${E} 种差异色`)};(0,r.useEffect)(()=>{let e=x(),n=new Image;n.onload=()=>{q.current=n,t(e),ve(n,void 0,!1)},n.src=e},[]),(0,r.useEffect)(()=>{let e=ge.current,t=q.current;if(!e||!t)return;let n=Math.max(8,Math.min(256,s||8))/Math.max(8,Math.min(256,d||8)),r=n>=1?600:Math.max(120,Math.round(600*n)),i=n>=1?Math.max(120,Math.round(600/n)):600;e.width=r,e.height=i;let a=e.getContext(`2d`);a.fillStyle=`#f7f5ed`,a.fillRect(0,0,r,i),b(a,t,r,i,w,D,k,j)},[e,s,d,w,D,k,j]);let $=(0,r.useMemo)(()=>{let e=new Map;return N.forEach(({color:t})=>{t&&e.set(t.code,(e.get(t.code)??0)+1)}),v.filter(t=>e.has(t.code)).map(t=>({...t,count:e.get(t.code)})).sort((e,t)=>t.count-e.count)},[N]),ye=(0,r.useMemo)(()=>N.reduce((e,t)=>e+ +!!t.color,0),[N]),be=(0,r.useMemo)(()=>{let e=V.trim().toLowerCase();return $.filter(t=>!e||t.code.toLowerCase().includes(e)||t.name.includes(e)||t.hex.toLowerCase().includes(e))},[V,$]);(0,r.useEffect)(()=>{let e=J.current;if(!e||N.length===0)return;let t=L,n=Math.max(22,t),r=Math.min(window.devicePixelRatio||1,2);e.width=(h*t+n)*r,e.height=(_*t+n)*r,e.style.width=`${h*t+n}px`,e.style.height=`${_*t+n}px`;let i=e.getContext(`2d`);i.scale(r,r),i.fillStyle=`#ffffff`,i.fillRect(0,0,h*t+n,_*t+n),i.fillStyle=`#f0eee7`,i.fillRect(n,0,h*t,n),i.fillRect(0,n,n,_*t),i.strokeStyle=`rgba(32,38,36,.25)`,i.lineWidth=.6,i.font=`600 ${Math.max(7,Math.min(10,t*.35))}px Arial`,i.fillStyle=`#56605b`,i.textAlign=`center`,i.textBaseline=`middle`;let a=t<10?10:t<16?5:1;for(let e=0;e<h;e++){let r=n+e*t;i.strokeRect(r+.3,.3,t-.6,n-.6),(e===0||(e+1)%a===0)&&i.fillText(String(e+1),r+t/2,n/2)}for(let e=0;e<_;e++){let r=n+e*t;i.strokeRect(.3,r+.3,n-.6,t-.6),(e===0||(e+1)%a===0)&&i.fillText(String(e+1),n/2,r+t/2)}if(N.forEach(({color:e},r)=>{let a=n+r%h*t,o=n+Math.floor(r/h)*t;if(!e){i.clearRect(a,o,t,t),z&&(i.strokeStyle=`rgba(32,38,36,.12)`,i.lineWidth=.6,i.strokeRect(a+.3,o+.3,t-.6,t-.6));return}let s=P.size>0,c=P.has(e.code);if(I&&s&&!c?i.fillStyle=`#ffffff`:(i.fillStyle=e.hex,i.globalAlpha=s&&!c?.12:1,i.fillRect(a,o,t,t),i.globalAlpha=1),z&&(i.strokeStyle=s&&!c?`rgba(32,38,36,.07)`:`rgba(32,38,36,.22)`,i.lineWidth=.6,i.strokeRect(a+.3,o+.3,t-.6,t-.6)),B&&t>=22&&(!s||c)){let[n,r,s]=y(e.hex);i.fillStyle=n*.299+r*.587+s*.114>160?`#1f2925`:`#ffffff`,i.font=`600 ${Math.max(7,t*.28)}px Arial`,i.textAlign=`center`,i.textBaseline=`middle`,i.fillText(e.code,a+t/2,o+t/2)}}),z){i.strokeStyle=`rgba(32,38,36,.48)`,i.lineWidth=1.5;for(let e=5;e<h;e+=5){let r=n+e*t;i.beginPath(),i.moveTo(r,n),i.lineTo(r,n+_*t),i.stroke()}for(let e=5;e<_;e+=5){let r=n+e*t;i.beginPath(),i.moveTo(n,r),i.lineTo(n+h*t,r),i.stroke()}}},[N,h,_,L,P,I,z,B]);let xe=n=>{let r=n.target.files?.[0];if(!r)return;if(!r.type.startsWith(`image/`)){K(`请选择 JPG、PNG 或 WebP 图片`);return}let a=URL.createObjectURL(r),o=new Image;o.onload=()=>{e.startsWith(`blob:`)&&URL.revokeObjectURL(e),q.current=o,t(a),i(r.name),O(1),A(0),M(0),K(`图片已载入,点击“重新转换”生成图纸`),ve(o,{zoom:1,x:0,y:0})},o.src=a},Se=e=>{e.currentTarget.setPointerCapture(e.pointerId),X.current={pointerId:e.pointerId,x:e.clientX,y:e.clientY,cropX:k,cropY:j}},Ce=e=>{let t=X.current,n=_e.current;if(!t||t.pointerId!==e.pointerId||!n)return;let r=n.getBoundingClientRect(),i=t.cropX+(e.clientX-t.x)/Math.max(1,r.width)*2,a=t.cropY+(e.clientY-t.y)/Math.max(1,r.height)*2;A(Math.max(-1,Math.min(1,i))),M(Math.max(-1,Math.min(1,a)))},we=e=>{X.current?.pointerId===e.pointerId&&(X.current=null)},Te=e=>{F(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},Ee=e=>{if(e.button!==0)return;let t=Y.current;t&&(Q.current=!1,e.currentTarget.setPointerCapture(e.pointerId),Z.current={pointerId:e.pointerId,x:e.clientX,y:e.clientY,scrollLeft:t.scrollLeft,scrollTop:t.scrollTop,moved:!1})},De=e=>{let t=Y.current,n=Z.current;if(!t||!n||n.pointerId!==e.pointerId)return;let r=e.clientX-n.x,i=e.clientY-n.y;!n.moved&&Math.hypot(r,i)<5||(n.moved=!0,t.classList.add(`is-dragging`),t.scrollLeft=n.scrollLeft-r,t.scrollTop=n.scrollTop-i,U(null))},Oe=e=>{let t=Y.current,n=Z.current;!n||n.pointerId!==e.pointerId||(Q.current=n.moved,Z.current=null,t?.classList.remove(`is-dragging`),e.currentTarget.hasPointerCapture(e.pointerId)&&e.currentTarget.releasePointerCapture(e.pointerId))},ke=e=>{Z.current?.pointerId===e.pointerId&&(Z.current=null,Q.current=!1,Y.current?.classList.remove(`is-dragging`),e.currentTarget.hasPointerCapture(e.pointerId)&&e.currentTarget.releasePointerCapture(e.pointerId))},Ae=e=>{Z.current?.pointerId===e.pointerId&&(Z.current=null,Q.current=!1,Y.current?.classList.remove(`is-dragging`))},je=e=>{if(Q.current){Q.current=!1;return}let t=J.current;if(!t)return;let n=t.getBoundingClientRect(),r=Math.max(22,L),i=Math.floor((e.clientX-n.left-r)/L),a=Math.floor((e.clientY-n.top-r)/L),o=N[a*h+i];i>=0&&a>=0&&i<h&&a<_&&o?.color&&Te(o.color.code)},Me=e=>{if(Z.current?.moved){U(null);return}let t=J.current;if(!t)return;let n=t.getBoundingClientRect(),r=Math.max(22,L),i=Math.floor((e.clientX-n.left-r)/L),a=Math.floor((e.clientY-n.top-r)/L),o=N[a*h+i];if(i<0||a<0||i>=h||a>=_||!o?.color){U(null);return}U({row:a+1,column:i+1,color:o.color,left:e.clientX-n.left+14,top:e.clientY-n.top+14})},Ne=async e=>{await m(e),fe(e)},Pe=async()=>{if(!N.length)return;let e={id:crypto.randomUUID(),name:n.replace(/\.[^.]+$/,``)||`未命名图纸`,savedAt:new Date().toISOString(),width:h,height:_,colorLimit:C,codes:N.map(e=>e.color?.code??null)};try{await Ne([e,...W].slice(0,c)),K(`图纸已保存到本机历史记录`)}catch{K(`保存历史失败,请检查浏览器是否允许本地存储或剩余空间是否充足`)}},Fe=e=>{let t=new Map(v.map(e=>[e.code,e]));se(e.codes.map(e=>({color:e===null?null:t.get(e)??v[0]}))),g(e.width),S(e.height),u(e.width),f(e.height),ae(e.colorLimit),i(e.name),F(new Set),K(`已恢复历史图纸:${e.name}`),document.getElementById(`pattern-preview`)?.scrollIntoView({behavior:`smooth`})},Ie=async e=>{try{await Ne(W.filter(t=>t.id!==e))}catch{K(`删除历史失败,请稍后重试`)}},Le=(0,r.useMemo)(()=>{let e=G.trim().toLowerCase();return W.filter(t=>!e||t.name.toLowerCase().includes(e)||`${t.width}x${t.height}`.includes(e))},[W,G]);return(0,a.jsxs)(`main`,{children:[(0,a.jsxs)(`header`,{className:`topbar`,children:[(0,a.jsxs)(`a`,{className:`brand`,href:`#top`,"aria-label":`豆格工坊首页`,children:[(0,a.jsxs)(`span`,{className:`brand-mark`,"aria-hidden":`true`,children:[(0,a.jsx)(`i`,{}),(0,a.jsx)(`i`,{}),(0,a.jsx)(`i`,{}),(0,a.jsx)(`i`,{})]}),(0,a.jsx)(`span`,{children:`豆格工坊`})]}),(0,a.jsx)(`span`,{className:`top-note`,children:`图片转拼豆图纸 · 本地处理`})]}),(0,a.jsxs)(`section`,{className:`hero`,id:`top`,children:[(0,a.jsxs)(`div`,{children:[(0,a.jsx)(`p`,{className:`eyebrow`,children:`PIXEL BEAD STUDIO`}),(0,a.jsxs)(`h1`,{children:[`把喜欢的图片,`,(0,a.jsx)(`br`,{}),(0,a.jsx)(`em`,{children:`变成一格一格的快乐。`})]}),(0,a.jsx)(`p`,{className:`hero-copy`,children:`上传图片,选择图纸大小和颜色数量;点击任意像素格,就能单独查看该颜色需要摆放的区域。`})]}),(0,a.jsxs)(`div`,{className:`hero-badge`,children:[(0,a.jsxs)(`strong`,{children:[h,` × `,_]}),(0,a.jsx)(`span`,{children:`当前图纸格数`})]})]}),(0,a.jsxs)(`section`,{className:`workspace`,"aria-label":`拼豆图纸转换工具`,children:[(0,a.jsxs)(`aside`,{className:`control-panel`,children:[(0,a.jsxs)(`div`,{className:`panel-heading`,children:[(0,a.jsx)(`span`,{children:`01`}),(0,a.jsxs)(`div`,{children:[(0,a.jsx)(`h2`,{children:`图片与规格`}),(0,a.jsx)(`p`,{children:`先决定图纸需要多少格`})]})]}),(0,a.jsxs)(`div`,{className:`upload-card crop-preview`,style:{aspectRatio:`${Math.max(8,s||8)} / ${Math.max(8,d||8)}`},ref:_e,onPointerDown:Se,onPointerMove:Ce,onPointerUp:we,onPointerCancel:we,children:[(0,a.jsx)(`canvas`,{ref:ge,"aria-label":`转换区域预览,可拖动调整取景`}),(0,a.jsx)(`span`,{className:`crop-frame`,"aria-hidden":`true`}),(0,a.jsx)(`button`,{type:`button`,className:`upload-overlay`,onPointerDown:e=>e.stopPropagation(),onClick:e=>{e.stopPropagation(),he.current?.click()},children:`更换图片`}),(0,a.jsx)(`input`,{ref:he,className:`file-input`,type:`file`,accept:`image/png,image/jpeg,image/webp`,onChange:xe})]}),(0,a.jsx)(`p`,{className:`file-name`,title:n,children:n}),(0,a.jsxs)(`label`,{className:`field crop-zoom`,children:[(0,a.jsxs)(`span`,{children:[`取景缩放 `,(0,a.jsxs)(`b`,{children:[D.toFixed(1),`×`]})]}),(0,a.jsx)(`input`,{type:`range`,min:`1`,max:`5`,step:`0.1`,value:D,onChange:e=>O(Number(e.target.value))})]}),(0,a.jsx)(`button`,{className:`reset-crop`,onClick:()=>{O(1),A(0),M(0)},children:`复位取景`}),(0,a.jsxs)(`div`,{className:`field-row`,children:[(0,a.jsxs)(`label`,{children:[(0,a.jsx)(`span`,{children:`横向格数`}),(0,a.jsx)(`input`,{type:`number`,min:`8`,max:`256`,value:s,onChange:e=>u(Number(e.target.value))})]}),(0,a.jsx)(`span`,{className:`times`,children:`×`}),(0,a.jsxs)(`label`,{children:[(0,a.jsx)(`span`,{children:`纵向格数`}),(0,a.jsx)(`input`,{type:`number`,min:`8`,max:`256`,value:d,onChange:e=>f(Number(e.target.value))})]})]}),(0,a.jsxs)(`label`,{className:`field`,children:[(0,a.jsxs)(`span`,{children:[`最多使用颜色 `,(0,a.jsx)(`b`,{children:C})]}),(0,a.jsx)(`input`,{type:`range`,min:`2`,max:`64`,value:C,onChange:e=>ae(Number(e.target.value))})]}),(0,a.jsxs)(`label`,{className:`field`,children:[(0,a.jsx)(`span`,{children:`取色策略`}),(0,a.jsxs)(`select`,{value:T,onChange:e=>E(e.target.value),children:[(0,a.jsx)(`option`,{value:`dominant`,children:`区域主色(减少杂色)`}),(0,a.jsx)(`option`,{value:`smooth`,children:`平滑取色(细节丰富)`})]})]}),(0,a.jsxs)(`label`,{className:`field`,children:[(0,a.jsx)(`span`,{children:`图片适应方式`}),(0,a.jsxs)(`select`,{value:w,onChange:e=>oe(e.target.value),children:[(0,a.jsx)(`option`,{value:`contain`,children:`完整显示`}),(0,a.jsx)(`option`,{value:`cover`,children:`裁剪铺满`})]})]}),(0,a.jsx)(`button`,{className:`primary-button`,onClick:()=>ve(),children:`重新转换`}),(0,a.jsx)(`p`,{className:`privacy-note`,children:`图片只在当前浏览器中处理,不会上传到服务器。`})]}),(0,a.jsxs)(`section`,{className:`pattern-panel`,id:`pattern-preview`,children:[(0,a.jsxs)(`div`,{className:`pattern-toolbar`,children:[(0,a.jsxs)(`div`,{children:[(0,a.jsx)(`p`,{className:`section-kicker`,children:`02 / 图纸预览`}),(0,a.jsx)(`h2`,{children:`点击格子,筛选颜色`})]}),(0,a.jsxs)(`div`,{className:`zoom-control`,"aria-label":`图纸缩放`,children:[(0,a.jsx)(`button`,{onClick:()=>R(e=>Math.max(8,e-2)),"aria-label":`缩小`,children:``}),(0,a.jsx)(`input`,{"aria-label":`缩放比例`,type:`range`,min:`8`,max:`42`,value:L,onChange:e=>R(Number(e.target.value))}),(0,a.jsx)(`button`,{onClick:()=>R(e=>Math.min(42,e+2)),"aria-label":`放大`,children:``}),(0,a.jsxs)(`output`,{children:[L,`px`]})]})]}),(0,a.jsx)(`div`,{className:`canvas-viewport`,children:(0,a.jsx)(`div`,{className:`canvas-stage`,ref:Y,children:(0,a.jsxs)(`div`,{className:`canvas-wrap`,children:[(0,a.jsx)(`canvas`,{ref:J,onPointerDown:Ee,onPointerMove:De,onPointerUp:Oe,onPointerCancel:ke,onLostPointerCapture:Ae,onContextMenu:e=>e.preventDefault(),onClick:je,onMouseMove:Me,onMouseLeave:()=>U(null),"aria-label":`转换后的拼豆像素图,可拖动查看,轻点格子选择颜色`}),H&&(0,a.jsxs)(`div`,{className:`pixel-tooltip`,style:{left:H.left,top:H.top},children:[(0,a.jsx)(`span`,{className:`tooltip-swatch`,style:{backgroundColor:H.color.hex}}),(0,a.jsxs)(`strong`,{children:[`第 `,H.row,` 行 · 第 `,H.column,` 列`]}),(0,a.jsxs)(`small`,{children:[H.color.code,` · `,H.color.name,` · `,H.color.hex]})]})]})})}),(0,a.jsxs)(`div`,{className:`pattern-footer`,children:[(0,a.jsx)(`span`,{className:`status-dot`}),` `,(0,a.jsx)(`span`,{children:me}),(0,a.jsxs)(`div`,{className:`view-options`,children:[(0,a.jsxs)(`label`,{children:[(0,a.jsx)(`input`,{type:`checkbox`,checked:z,onChange:e=>le(e.target.checked)}),` 网格`]}),(0,a.jsxs)(`label`,{children:[(0,a.jsx)(`input`,{type:`checkbox`,checked:B,onChange:e=>ue(e.target.checked)}),` 格内编号`]}),(0,a.jsx)(`button`,{className:`save-button`,onClick:Pe,children:`保存历史`}),(0,a.jsx)(`button`,{onClick:()=>{let e=J.current;if(!e)return;let t=document.createElement(`a`);t.download=`拼豆图纸-${h}x${_}.png`,t.href=e.toDataURL(`image/png`),t.click()},children:`下载 PNG`})]})]}),(0,a.jsxs)(`div`,{className:`pattern-legend`,children:[(0,a.jsxs)(`div`,{className:`legend-heading`,children:[(0,a.jsx)(`strong`,{children:`本图所需颜色与用量`}),(0,a.jsxs)(`span`,{children:[`按数量从多到少,共 `,$.length,` 色`]})]}),(0,a.jsx)(`div`,{className:`legend-list`,children:$.map(e=>(0,a.jsxs)(`button`,{className:P.has(e.code)?`active`:``,onClick:()=>Te(e.code),children:[(0,a.jsx)(`i`,{style:{backgroundColor:e.hex}}),(0,a.jsx)(`strong`,{children:e.code}),(0,a.jsx)(`span`,{children:e.name}),(0,a.jsxs)(`b`,{children:[e.count,` 颗`]})]},e.code))})]})]}),(0,a.jsxs)(`aside`,{className:`color-panel`,children:[(0,a.jsxs)(`div`,{className:`panel-heading`,children:[(0,a.jsx)(`span`,{children:`03`}),(0,a.jsxs)(`div`,{children:[(0,a.jsx)(`h2`,{children:`分色查看`}),(0,a.jsxs)(`p`,{children:[$.length,` 种颜色 · `,ye,` 颗`]})]})]}),(0,a.jsxs)(`div`,{className:`search-box`,children:[(0,a.jsx)(`span`,{children:`⌕`}),(0,a.jsx)(`input`,{value:V,onChange:e=>de(e.target.value),placeholder:`输入 A6、MARD A 或 #FEAC4C`})]}),(0,a.jsxs)(`div`,{className:`selection-tools`,children:[(0,a.jsxs)(`label`,{className:`switch-row`,children:[(0,a.jsx)(`input`,{type:`checkbox`,checked:I,onChange:e=>ce(e.target.checked)}),(0,a.jsx)(`span`,{className:`switch`}),(0,a.jsx)(`span`,{children:`只显示选中颜色`})]}),P.size>0&&(0,a.jsxs)(`button`,{onClick:()=>F(new Set),children:[`清除 `,P.size,` 项`]})]}),(0,a.jsx)(`div`,{className:`color-list`,children:be.map(e=>{let t=P.has(e.code);return(0,a.jsxs)(`button`,{className:`color-item ${t?`active`:``}`,onClick:()=>Te(e.code),"aria-pressed":t,children:[(0,a.jsx)(`span`,{className:`swatch`,style:{backgroundColor:e.hex}}),(0,a.jsxs)(`span`,{className:`color-meta`,children:[(0,a.jsxs)(`strong`,{children:[e.code,` · `,e.name]}),(0,a.jsx)(`small`,{children:e.hex})]}),(0,a.jsxs)(`b`,{children:[e.count,(0,a.jsx)(`small`,{children:`颗`})]})]},e.code)})}),(0,a.jsxs)(`p`,{className:`palette-note`,children:[(0,a.jsx)(`strong`,{children:`MARD 221 基础色卡`}),`:默认使用 A/B/C/D/E/F/G/H/M 九个系列。屏幕色值仅用于近似匹配,实体豆颜色会受批次、光线和屏幕影响。`]})]})]}),(0,a.jsxs)(`section`,{className:`history-section`,"aria-labelledby":`history-title`,children:[(0,a.jsxs)(`div`,{className:`history-heading`,children:[(0,a.jsxs)(`div`,{children:[(0,a.jsx)(`p`,{className:`section-kicker`,children:`LOCAL PATTERN ARCHIVE`}),(0,a.jsx)(`h2`,{id:`history-title`,children:`历史图纸`}),(0,a.jsx)(`p`,{children:`历史保存在当前浏览器,最多保留 30 份。`})]}),(0,a.jsxs)(`div`,{className:`search-box`,children:[(0,a.jsx)(`span`,{children:`⌕`}),(0,a.jsx)(`input`,{value:G,onChange:e=>pe(e.target.value),placeholder:`按名称或 32x32 查询`})]})]}),Le.length?(0,a.jsx)(`div`,{className:`history-grid`,children:Le.map(e=>{let t=new Map;return e.codes.forEach(e=>{e&&t.set(e,(t.get(e)??0)+1)}),(0,a.jsxs)(`article`,{className:`history-card`,children:[(0,a.jsx)(`div`,{className:`history-swatches`,children:[...t.entries()].sort((e,t)=>t[1]-e[1]).slice(0,6).map(([e,t])=>(0,a.jsx)(`i`,{style:{backgroundColor:v.find(t=>t.code===e)?.hex,flexGrow:t}},e))}),(0,a.jsxs)(`div`,{children:[(0,a.jsx)(`h3`,{children:e.name}),(0,a.jsxs)(`p`,{children:[e.width,` × `,e.height,` · `,new Date(e.savedAt).toLocaleString(`zh-CN`,{hour12:!1})]})]}),(0,a.jsxs)(`div`,{className:`history-actions`,children:[(0,a.jsx)(`button`,{onClick:()=>Fe(e),children:`打开`}),(0,a.jsx)(`button`,{onClick:()=>Ie(e.id),children:`删除`})]})]},e.id)})}):(0,a.jsx)(`div`,{className:`history-empty`,children:`还没有符合条件的历史图纸。转换完成后点击“保存历史”即可在这里查询。`})]})]})}export{S as default};