@varianta/sdk 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +6 -6
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1053 -902
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +6 -6
- package/dist/index.umd.js.map +1 -1
- package/dist/react.cjs.js +1 -1
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.esm.js +204 -185
- package/dist/react.esm.js.map +1 -1
- package/dist/types/react/Customizer.d.ts.map +1 -1
- package/dist/types/react/useCustomizer.d.ts +19 -3
- package/dist/types/react/useCustomizer.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +33 -1
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/vanilla/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class U{constructor(e){this.animationFrameId=null,this.needsRender=!0,this.areas=[],this.contents=new Map,this.artboard=null,this.bgImageElement=null,this.loadedImages=new Map,this.loadingImages=new Set,this.zoom=1,this.pan={x:0,y:0},this.selectedAreaId=null,this.hoveredHandle=null,this.eventListeners=new Map,this.safeArea=null,this.safeAreaViolations=new Set,this.showAreaBorders=!0,this.areaSelectionEnabled=!0,this.renderLoop=()=>{try{this.needsRender&&(this.render(),this.needsRender=!1,this.emit("render",void 0))}catch(t){console.error("Error in render loop:",t)}this.animationFrameId=requestAnimationFrame(this.renderLoop)},this.canvas=e;const i=e.getContext("2d");if(!i)throw new Error("Failed to get 2D context from canvas");this.ctx=i}start(){this.animationFrameId===null&&this.renderLoop()}stop(){this.animationFrameId!==null&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}setArtboard(e){this.artboard=e,this.requestRender()}getArtboard(){return this.artboard}setAreas(e){this.areas=[...e].sort((i,t)=>i.zIndex-t.zIndex),this.requestRender()}getAreas(){return this.areas}setContents(e){this.contents=e,this.loadContentImages(),this.requestRender()}getContents(){return this.contents}updateContent(e,i){this.contents.set(e,i),i.type==="image"&&this.loadContentImages(),this.requestRender(),this.emit("content:change",{areaId:e,content:i})}removeContent(e){this.contents.delete(e),this.loadedImages.delete(e),this.requestRender()}setBackgroundImage(e){if(this.backgroundImage=e,e?.url&&e.url!==this.bgImageElement?.src){const i=e.url,t=new Image;t.crossOrigin="anonymous",t.onload=()=>{this.bgImageElement=t,this.requestRender()},t.onerror=()=>{this.bgImageElement=null,console.error("Failed to load background image:",i),this.requestRender()},t.src=i}else e?.url||(this.bgImageElement=null);this.requestRender()}setZoom(e){this.zoom=Math.max(.1,Math.min(5,e)),this.requestRender()}setPan(e){this.pan=e,this.requestRender()}getZoom(){return this.zoom}getPan(){return this.pan}setShowAreaBorders(e){this.showAreaBorders=e,this.requestRender()}setAreaSelectionEnabled(e){this.areaSelectionEnabled=e}getCenteredPan(e){if(!this.artboard)return null;const{width:i,height:t}=this.artboard,s=window.devicePixelRatio||1,a=this.canvas.width/s,o=this.canvas.height/s;return{x:(a/e-i)/2,y:(o/e-t)/2}}requestRender(){this.needsRender=!0}fitToView(){if(!this.artboard)return;const e=0,{width:i,height:t}=this.artboard,s=window.devicePixelRatio||1,a=this.canvas.width/s-e*2,o=this.canvas.height/s-e*2;if(a<=0||o<=0)return;const n=a/i,r=o/t,d=Math.min(n,r),l=this.canvas.width/s,h=this.canvas.height/s,u=(l/d-i)/2,p=(h/d-t)/2;this.zoom=d,this.pan={x:u,y:p},this.requestRender()}setSelectedArea(e){this.selectedAreaId!==e&&(this.selectedAreaId=e,this.requestRender(),this.emit("area:select",{areaId:e}))}getSelectedArea(){return this.selectedAreaId}setHoveredHandle(e){this.hoveredHandle!==e&&(this.hoveredHandle=e,this.requestRender())}getHoveredHandle(){return this.hoveredHandle}screenToCanvas(e,i){const t=window.devicePixelRatio||1,s=e/t,a=i/t,o=s/this.zoom-this.pan.x,n=a/this.zoom-this.pan.y;return{x:o,y:n}}getContentBounds(e){const i=this.areas.find(d=>d.id===e);if(!i)return null;const t=this.contents.get(e);if(!t)return null;const{location:s}=i,{x:a,y:o,width:n,height:r}=s;if(t.type==="text"){const d=t.offset||{x:0,y:0},l=t.scale??1,h=t.rotation??0,u=n*l,p=r*l,m=a+n/2+d.x,g=o+r/2+d.y;return{x:m-u/2,y:g-p/2,width:u,height:p,rotation:h,centerX:m,centerY:g}}else if(t.type==="image"){const d=this.loadedImages.get(e);if(!d)return null;const l=t.offset||{x:0,y:0},h=t.scale??1,u=t.rotation??0,p=d.naturalWidth/d.naturalHeight,m=n/r;let g,f;p>m?(g=n,f=n/p):(f=r,g=r*p);const b=g*h,w=f*h,y=a+n/2+l.x,x=o+r/2+l.y;return{x:y-b/2,y:x-w/2,width:b,height:w,rotation:u,centerX:y,centerY:x}}return null}hitTestContent(e,i){if(!this.areaSelectionEnabled)return null;for(let t=this.areas.length-1;t>=0;t--){const s=this.areas[t],a=this.getContentBounds(s.id);if(a&&this.isPointInBounds(e,i,a))return s.id}return null}hitTestArea(e,i){if(!this.areaSelectionEnabled)return null;for(let t=this.areas.length-1;t>=0;t--){const s=this.areas[t],{x:a,y:o,width:n,height:r}=s.location;if(e>=a&&e<=a+n&&i>=o&&i<=o+r)return s.id}return null}isPointInBounds(e,i,t){const{centerX:s,centerY:a,width:o,height:n,rotation:r}=t,d=-r*Math.PI/180,l=Math.cos(d),h=Math.sin(d),u=e-s,p=i-a,m=u*l-p*h,g=u*h+p*l;return m>=-o/2&&m<=o/2&&g>=-n/2&&g<=n/2}getContentHandlePositions(e){const i=this.areas.find(b=>b.id===e);if(!i)return null;const t=this.contents.get(e);if(!t)return null;const s=this.getContentBounds(e);if(!s)return null;const{centerX:a,centerY:o,width:n,height:r,rotation:d}=s,l=6/this.zoom,h=30/this.zoom,u=d*Math.PI/180,p=Math.cos(u),m=Math.sin(u),g=(b,w)=>({x:a+b*p-w*m,y:o+b*m+w*p}),f=[];if(t.type==="image"&&i.imageOptions?.allowScaling||t.type==="text"&&i.textOptions?.allowScaling){const b=n/2,w=r/2,y=g(-b,-w);f.push({type:"nw",...y,radius:l});const x=g(b,-w);f.push({type:"ne",...x,radius:l});const C=g(b,w);f.push({type:"se",...C,radius:l});const M=g(-b,w);f.push({type:"sw",...M,radius:l})}if(t.type==="image"&&i.imageOptions?.allowRotation||t.type==="text"&&i.textOptions?.allowRotation){const b=g(0,-r/2-h);f.push({type:"rotate",...b,radius:l})}return f}hitTestHandle(e,i,t){const s=this.getContentHandlePositions(t);if(!s)return null;for(const a of s){const o=e-a.x,n=i-a.y;if(Math.sqrt(o*o+n*n)<=a.radius*2)return a.type}return null}isPositioningAllowed(e){const i=this.areas.find(s=>s.id===e);if(!i)return!1;const t=this.contents.get(e);return t?t.type==="text"?i.textOptions?.allowPositioning??!1:i.imageOptions?.allowPositioning??!1:!1}getAreaLocation(e){const i=this.areas.find(t=>t.id===e);return i?i.location:null}getArea(e){return this.areas.find(i=>i.id===e)}setSafeArea(e){this.safeArea=e??null,this.requestRender()}checkSafeAreaViolations(){if(!this.safeArea||!this.artboard)return[];const e=[],{top:i,right:t,bottom:s,left:a}=this.safeArea,o=a,n=i,r=this.artboard.width-t,d=this.artboard.height-s;for(const l of this.areas){if(!this.contents.get(l.id))continue;const h=this.getContentBounds(l.id);if(!h)continue;const{centerX:u,centerY:p,width:m,height:g,rotation:f}=h,b=f*Math.PI/180,w=Math.abs(Math.cos(b)),y=Math.abs(Math.sin(b)),x=m*w+g*y,C=m*y+g*w,M=u-x/2,R=p-C/2,D=u+x/2,N=p+C/2;(M<o||R<n||D>r||N>d)&&e.push(l.id)}return this.safeAreaViolations=new Set(e),this.requestRender(),e}on(e,i){return this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(i),()=>{this.eventListeners.get(e)?.delete(i)}}emit(e,i){const t=this.eventListeners.get(e);if(t)for(const s of t)s(i)}async exportToPNG(){if(!this.artboard)throw new Error("No artboard configured");const{width:e,height:i,backgroundColor:t}=this.artboard,s=document.createElement("canvas");s.width=e,s.height=i;const a=s.getContext("2d");if(!a)throw new Error("Failed to get 2D context for export");a.fillStyle=t,a.fillRect(0,0,e,i),this.backgroundImage?.position==="behind"&&this.bgImageElement&&this.drawBackgroundImageOnContext(a,e,i);for(const o of this.areas)this.drawAreaContentOnContext(a,o);return this.backgroundImage?.position==="overlay"&&this.bgImageElement&&this.drawBackgroundImageOnContext(a,e,i),new Promise((o,n)=>{try{s.toBlob(r=>{r?o(r):n(new Error("Failed to create PNG blob"))},"image/png",1)}catch(r){r instanceof DOMException&&r.name==="SecurityError"?n(new Error("Cannot export: images failed to load with CORS. Check CDN configuration.")):n(r)}})}loadContentImages(){for(const[e,i]of this.contents)if(i.type==="image"&&!this.loadedImages.has(e)&&!this.loadingImages.has(e)){this.loadingImages.add(e);const t=new Image;t.crossOrigin="anonymous",t.onload=()=>{this.loadedImages.set(e,t),this.loadingImages.delete(e),this.requestRender()},t.onerror=()=>{this.loadingImages.delete(e),console.error("Failed to load image for area:",e)},t.src=i.dataUrl}for(const e of this.loadedImages.keys()){const i=this.contents.get(e);(!i||i.type!=="image")&&this.loadedImages.delete(e)}}render(){const{ctx:e,canvas:i}=this;if(e.fillStyle="#f3f4f6",e.fillRect(0,0,i.width,i.height),!this.artboard)return;e.save();const t=window.devicePixelRatio||1;e.scale(t,t),e.scale(this.zoom,this.zoom),e.translate(this.pan.x,this.pan.y);const{width:s,height:a,backgroundColor:o}=this.artboard;e.fillStyle=o,e.fillRect(0,0,s,a),e.strokeStyle="#d1d5db",e.lineWidth=1/this.zoom,e.strokeRect(0,0,s,a),this.backgroundImage?.position==="behind"&&this.renderBackgroundImage();for(const n of this.areas)this.renderAreaWithContent(n);this.backgroundImage?.position==="overlay"&&this.renderBackgroundImage(),this.renderSafeAreaGuide();for(const n of this.safeAreaViolations){const r=this.areas.find(p=>p.id===n);if(!r)continue;const{x:d,y:l,width:h,height:u}=r.location;e.save(),e.fillStyle="rgba(239, 68, 68, 0.15)",e.fillRect(d,l,h,u),e.restore()}this.selectedAreaId&&this.renderSelectionOverlay(this.selectedAreaId),e.restore()}renderSelectionOverlay(e){const i=this.getContentBounds(e);if(!i){const l=this.areas.find(f=>f.id===e);if(!l)return;const{ctx:h}=this,{x:u,y:p,width:m,height:g}=l.location;h.save(),h.strokeStyle="#3b82f6",h.lineWidth=2/this.zoom,h.setLineDash([]),h.strokeRect(u,p,m,g),h.restore();return}const{ctx:t}=this,{centerX:s,centerY:a,width:o,height:n,rotation:r}=i;t.save(),t.translate(s,a),t.rotate(r*Math.PI/180),t.strokeStyle="#3b82f6",t.lineWidth=2/this.zoom,t.setLineDash([]),t.strokeRect(-o/2,-n/2,o,n),t.restore();const d=this.getContentHandlePositions(e);if(d)for(const l of d){t.save();const h=this.hoveredHandle===l.type,u=h?"#dbeafe":"#ffffff",p=h?"#2563eb":"#3b82f6";if(l.type==="rotate"){const m=this.getRotatedPoint(s,a-n/2,s,a,r);t.beginPath(),t.strokeStyle=p,t.lineWidth=1/this.zoom,t.setLineDash([4/this.zoom,4/this.zoom]),t.moveTo(m.x,m.y),t.lineTo(l.x,l.y),t.stroke(),t.setLineDash([]),t.beginPath(),t.fillStyle=u,t.strokeStyle=p,t.lineWidth=2/this.zoom,t.arc(l.x,l.y,l.radius,0,Math.PI*2),t.fill(),t.stroke(),t.beginPath(),t.strokeStyle=p,t.lineWidth=1.5/this.zoom,t.arc(l.x,l.y,l.radius*.5,-Math.PI*.8,Math.PI*.5),t.stroke()}else t.fillStyle=u,t.strokeStyle=p,t.lineWidth=2/this.zoom,t.fillRect(l.x-l.radius,l.y-l.radius,l.radius*2,l.radius*2),t.strokeRect(l.x-l.radius,l.y-l.radius,l.radius*2,l.radius*2);t.restore()}}getRotatedPoint(e,i,t,s,a){const o=a*Math.PI/180,n=Math.cos(o),r=Math.sin(o),d=e-t,l=i-s;return{x:t+d*n-l*r,y:s+d*r+l*n}}renderBackgroundImage(){if(!this.bgImageElement||!this.artboard||!this.backgroundImage)return;const{ctx:e}=this,{opacity:i}=this.backgroundImage,{width:t,height:s}=this.artboard;e.save(),e.globalAlpha=i;const a=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,o=t/s;let n,r,d,l;a>o?(r=s,n=s*a,d=(t-n)/2,l=0):(n=t,r=t/a,d=0,l=(s-r)/2),e.drawImage(this.bgImageElement,d,l,n,r),e.restore()}renderSafeAreaGuide(){if(!this.safeArea||!this.artboard)return;const{ctx:e}=this,{top:i,right:t,bottom:s,left:a}=this.safeArea,o=a,n=i,r=this.artboard.width-a-t,d=this.artboard.height-i-s;r<=0||d<=0||(e.save(),e.strokeStyle="#22c55e",e.lineWidth=1/this.zoom,e.setLineDash([6/this.zoom,4/this.zoom]),e.strokeRect(o,n,r,d),e.setLineDash([]),e.restore())}renderAreaWithContent(e){const{ctx:i}=this,{location:t,backgroundColor:s}=e,{x:a,y:o,width:n,height:r}=t;s&&(i.fillStyle=s,i.fillRect(a,o,n,r));const d=this.contents.get(e.id);d&&(d.type==="text"?this.renderTextContent(e,d):d.type==="image"&&this.renderImageContent(e,d)),this.showAreaBorders&&e.showBorder&&(i.save(),i.strokeStyle=e.borderColor||"#3b82f6",i.lineWidth=1/this.zoom,i.setLineDash([4/this.zoom,4/this.zoom]),i.strokeRect(a,o,n,r),i.setLineDash([]),i.restore())}renderTextContent(e,i){if(!i.text.trim())return;const{ctx:t}=this,{location:s}=e,{x:a,y:o,width:n,height:r}=s,d=i.offset||{x:0,y:0},l=i.scale??1,h=i.rotation??0,u=a+n/2+d.x,p=o+r/2+d.y;t.save(),t.beginPath(),t.rect(a,o,n,r),t.clip(),t.translate(u,p),t.rotate(h*Math.PI/180),t.scale(l,l),t.font=`${i.size}px ${i.font}, sans-serif`,t.fillStyle=i.color,t.textBaseline="middle";let m;switch(i.align){case"left":t.textAlign="left",m=-n/2+10;break;case"right":t.textAlign="right",m=n/2-10;break;case"center":default:t.textAlign="center",m=0;break}t.fillText(i.text,m,0),t.restore()}renderImageContent(e,i){const t=this.loadedImages.get(e.id);if(!t)return;const{ctx:s}=this,{location:a}=e,{x:o,y:n,width:r,height:d}=a,l=i.offset||{x:0,y:0},h=i.scale??1,u=i.rotation??0;s.save(),s.beginPath(),s.rect(o,n,r,d),s.clip();const p=t.naturalWidth/t.naturalHeight,m=r/d;let g,f;p>m?(g=r,f=r/p):(f=d,g=d*p);const b=g*h,w=f*h,y=o+r/2+l.x,x=n+d/2+l.y;s.translate(y,x),s.rotate(u*Math.PI/180),s.drawImage(t,-b/2,-w/2,b,w),s.restore()}drawBackgroundImageOnContext(e,i,t){if(!this.bgImageElement||!this.backgroundImage)return;const{opacity:s}=this.backgroundImage;e.save(),e.globalAlpha=s;const a=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,o=i/t;let n,r,d,l;a>o?(r=t,n=t*a,d=(i-n)/2,l=0):(n=i,r=i/a,d=0,l=(t-r)/2),e.drawImage(this.bgImageElement,d,l,n,r),e.restore()}drawAreaContentOnContext(e,i){const{location:t,backgroundColor:s}=i,{x:a,y:o,width:n,height:r}=t;s&&(e.fillStyle=s,e.fillRect(a,o,n,r));const d=this.contents.get(i.id);if(d){if(d.type==="text"){if(!d.text.trim())return;const l=d.offset||{x:0,y:0},h=d.scale??1,u=d.rotation??0,p=a+n/2+l.x,m=o+r/2+l.y;e.save(),e.beginPath(),e.rect(a,o,n,r),e.clip(),e.translate(p,m),e.rotate(u*Math.PI/180),e.scale(h,h),e.font=`${d.size}px ${d.font}, sans-serif`,e.fillStyle=d.color,e.textBaseline="middle";let g;switch(d.align){case"left":e.textAlign="left",g=-n/2+10;break;case"right":e.textAlign="right",g=n/2-10;break;case"center":default:e.textAlign="center",g=0;break}e.fillText(d.text,g,0),e.restore()}else if(d.type==="image"){const l=this.loadedImages.get(i.id);if(!l)return;const h=d.offset||{x:0,y:0},u=d.scale??1,p=d.rotation??0;e.save(),e.beginPath(),e.rect(a,o,n,r),e.clip();const m=l.naturalWidth/l.naturalHeight,g=n/r;let f,b;m>g?(f=n,b=n/m):(b=r,f=r*m);const w=f*u,y=b*u,x=a+n/2+h.x,C=o+r/2+h.y;e.translate(x,C),e.rotate(p*Math.PI/180),e.drawImage(l,-w/2,-y/2,w,y),e.restore()}}}destroy(){this.stop(),this.bgImageElement=null,this.loadedImages.clear(),this.loadingImages.clear(),this.eventListeners.clear()}}function S(v){const e=v.textOptions;return{type:"text",text:e?.defaultText||"",font:e?.defaultFont||"Inter",size:e?.defaultSize||24,color:e?.defaultColor||"#000000",align:e?.defaultAlign||"center"}}function z(v){return v.contentType==="text"||v.contentType==="both"}function T(v){return v.contentType==="image"||v.contentType==="both"}const V=50;class O{constructor(e){this.history=[],this.historyIndex=-1,this.listeners=new Set,this.isRestoring=!1,this.state=this.cloneState(e),this.history.push({state:this.cloneState(e),timestamp:Date.now(),description:"Initial state"}),this.historyIndex=0}getState(){return this.state}setState(e,i,t=!1){const s=this.state;this.state={...this.state,...e},!t&&!this.isRestoring&&this.saveToHistory(s,i),this.notifyListeners()}update(e){this.state={...this.state,...e},this.notifyListeners()}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}undo(){return this.canUndo()?(this.historyIndex--,this.restoreFromHistory(),!0):!1}redo(){return this.canRedo()?(this.historyIndex++,this.restoreFromHistory(),!0):!1}canUndo(){return this.historyIndex>0}canRedo(){return this.historyIndex<this.history.length-1}clearHistory(){this.history=[],this.historyIndex=-1}getHistory(){return this.history}saveToHistory(e,i){this.historyIndex<this.history.length-1&&(this.history=this.history.slice(0,this.historyIndex+1)),this.history.push({state:this.cloneState(e),timestamp:Date.now(),description:i}),this.history.length>V&&this.history.shift(),this.historyIndex=this.history.length-1}restoreFromHistory(){const e=this.history[this.historyIndex];e&&(this.isRestoring=!0,this.state=this.cloneState(e.state),this.notifyListeners(),this.isRestoring=!1)}notifyListeners(){const e=this.state;for(const i of this.listeners)try{i(e)}catch(t){console.error("Error in state change listener:",t)}}cloneState(e){return JSON.parse(JSON.stringify(e))}}class P{constructor(e){this.listeners=new Set,this.areas=e,this.contents=this.buildInitialContents()}buildInitialContents(){const e=new Map;for(const i of this.areas)z(i)&&e.set(i.id,S(i));return e}getContents(){return this.contents}getContent(e){return this.contents.get(e)}setTextContent(e,i){const t=this.contents.get(e),s=this.areas.find(o=>o.id===e),a=s?S(s):{type:"text",text:"",font:"Inter",size:24,color:"#000000",align:"center"};t?.type==="text"?this.contents.set(e,{...t,...i}):this.contents.set(e,{...a,...i}),this.notify()}setImageContent(e,i,t){const s={type:"image",dataUrl:i,filename:t};this.contents.set(e,s),this.notify()}clearContent(e){const i=this.areas.find(t=>t.id===e);i&&z(i)?this.contents.set(e,S(i)):this.contents.delete(e),this.notify()}setContentOffset(e,i){const t=this.contents.get(e);t&&(this.contents.set(e,{...t,offset:i}),this.notify())}setImageScale(e,i){const t=this.contents.get(e);if(!t||t.type!=="image")return;const s=Math.max(.1,Math.min(5,i));this.contents.set(e,{...t,scale:s}),this.notify()}setImageRotation(e,i){const t=this.contents.get(e);if(!t||t.type!=="image")return;let s=i%360;s<0&&(s+=360),this.contents.set(e,{...t,rotation:s}),this.notify()}reset(){this.contents=this.buildInitialContents(),this.notify()}toJSON(){return Array.from(this.contents.entries())}fromJSON(e){this.contents=new Map(e),this.notify()}setContentsQuiet(e){this.contents=new Map(e)}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){for(const e of this.listeners)try{e()}catch(i){console.error("Error in AreaContentManager listener:",i)}}}class E{constructor(){this.listeners=new Map}on(e,i){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(i),()=>this.off(e,i)}once(e,i){const t=s=>{i(s),this.off(e,t)};return this.on(e,t)}off(e,i){const t=this.listeners.get(e);t&&(t.delete(i),t.size===0&&this.listeners.delete(e))}emit(e,i){const t=this.listeners.get(e);if(t){const s=Array.from(t);for(const a of s)try{a(i)}catch(o){console.error(`Error in event handler for "${String(e)}":`,o)}}}clear(e){e?this.listeners.delete(e):this.listeners.clear()}listenerCount(e){return this.listeners.get(e)?.size??0}}class $ extends E{constructor(){super(...arguments),this.activeTouches=new Map,this.initialPinchDistance=null,this.lastPinchCenter=null,this.isTwoFingerGesture=!1}get isMultiTouch(){return this.isTwoFingerGesture}handleTouchStart(e){for(let i=0;i<e.changedTouches.length;i++){const t=e.changedTouches[i];this.activeTouches.set(t.identifier,{x:t.clientX,y:t.clientY})}if(this.activeTouches.size===2){this.isTwoFingerGesture=!0,e.preventDefault();const[i,t]=Array.from(this.activeTouches.values());this.initialPinchDistance=this.getDistance(i,t),this.lastPinchCenter=this.getCenter(i,t)}}handleTouchMove(e){for(let i=0;i<e.changedTouches.length;i++){const t=e.changedTouches[i];this.activeTouches.set(t.identifier,{x:t.clientX,y:t.clientY})}if(this.activeTouches.size===2&&this.initialPinchDistance!==null&&this.lastPinchCenter){e.preventDefault();const[i,t]=Array.from(this.activeTouches.values()),s=this.getDistance(i,t),a=this.getCenter(i,t),o=s/this.initialPinchDistance;this.emit("pinch",{scale:o,centerX:a.x,centerY:a.y});const n=a.x-this.lastPinchCenter.x,r=a.y-this.lastPinchCenter.y;(Math.abs(n)>.5||Math.abs(r)>.5)&&this.emit("pan",{deltaX:n,deltaY:r}),this.initialPinchDistance=s,this.lastPinchCenter=a}}handleTouchEnd(e){for(let i=0;i<e.changedTouches.length;i++){const t=e.changedTouches[i];this.activeTouches.delete(t.identifier)}this.activeTouches.size<2&&(this.initialPinchDistance=null,this.lastPinchCenter=null,this.isTwoFingerGesture=!1)}reset(){this.activeTouches.clear(),this.initialPinchDistance=null,this.lastPinchCenter=null,this.isTwoFingerGesture=!1}getDistance(e,i){const t=i.x-e.x,s=i.y-e.y;return Math.sqrt(t*t+s*s)}getCenter(e,i){return{x:(e.x+i.x)/2,y:(e.y+i.y)/2}}}class F extends E{constructor(e,i,t,s){super(),this.interactionState={mode:"idle"},this.initialPinchZoom=1,this.canvas=e,this.engine=i,this.getContents=t,this.getSelectedAreaId=s,this.gestureHandler=new $,this.boundMouseDown=this.handleMouseDown.bind(this),this.boundMouseMove=this.handleMouseMove.bind(this),this.boundMouseUp=this.handleMouseUp.bind(this),this.boundMouseLeave=this.handleMouseLeave.bind(this),this.boundContextMenu=this.handleContextMenu.bind(this),this.boundDoubleClick=this.handleDoubleClick.bind(this),this.boundTouchStart=this.handleTouchStart.bind(this),this.boundTouchMove=this.handleTouchMove.bind(this),this.boundTouchEnd=this.handleTouchEnd.bind(this),e.addEventListener("mousedown",this.boundMouseDown),e.addEventListener("mousemove",this.boundMouseMove),e.addEventListener("mouseup",this.boundMouseUp),e.addEventListener("mouseleave",this.boundMouseLeave),e.addEventListener("contextmenu",this.boundContextMenu),e.addEventListener("dblclick",this.boundDoubleClick),e.addEventListener("touchstart",this.boundTouchStart,{passive:!1}),e.addEventListener("touchmove",this.boundTouchMove,{passive:!1}),e.addEventListener("touchend",this.boundTouchEnd),this.gestureHandler.on("pinch",({scale:a})=>{const o=Math.max(.1,Math.min(5,this.initialPinchZoom*a));this.emit("zoom",{zoom:o})}),this.gestureHandler.on("pan",({deltaX:a,deltaY:o})=>{const n=this.engine.getPan(),r=this.engine.getZoom();this.emit("pan",{pan:{x:n.x+a/r,y:n.y+o/r}})})}getCanvasPosition(e,i){const t=this.canvas.getBoundingClientRect(),s=window.devicePixelRatio||1,a=(e-t.left)*s,o=(i-t.top)*s;return this.engine.screenToCanvas(a,o)}handleMouseDown(e){const i=this.getCanvasPosition(e.clientX,e.clientY);this.processPointerDown(i)}handleMouseMove(e){const i=this.getCanvasPosition(e.clientX,e.clientY);this.processPointerMove(i)}handleMouseUp(){this.processPointerUp()}handleMouseLeave(){this.processPointerUp(),this.emit("cursor",{cursor:"default"})}handleContextMenu(e){e.preventDefault()}handleDoubleClick(e){const i=this.getCanvasPosition(e.clientX,e.clientY);let t=this.engine.hitTestContent(i.x,i.y);t||(t=this.engine.hitTestArea(i.x,i.y)??null),this.emit("context-menu",{areaId:t,clientX:e.clientX,clientY:e.clientY})}handleTouchStart(e){if(this.gestureHandler.handleTouchStart(e),this.gestureHandler.isMultiTouch){this.initialPinchZoom=this.engine.getZoom(),this.interactionState.mode!=="idle"&&(this.interactionState={mode:"idle"});return}if(e.touches.length!==1)return;const i=e.touches[0],t=this.getCanvasPosition(i.clientX,i.clientY);this.processPointerDown(t)&&e.preventDefault()}handleTouchMove(e){if(this.gestureHandler.handleTouchMove(e),this.gestureHandler.isMultiTouch||e.touches.length!==1||this.interactionState.mode==="idle")return;const i=e.touches[0],t=this.getCanvasPosition(i.clientX,i.clientY);this.processPointerMove(t),e.preventDefault()}handleTouchEnd(e){this.gestureHandler.handleTouchEnd(e),this.gestureHandler.isMultiTouch||this.processPointerUp()}processPointerDown(e){const i=this.getSelectedAreaId(),t=this.getContents();if(i){const o=this.engine.hitTestHandle(e.x,e.y,i);if(o){const n=t.get(i),r=this.engine.getContentBounds(i);if(o==="rotate"&&n?.type==="image"&&r){const d=Math.atan2(e.y-r.centerY,e.x-r.centerX)*(180/Math.PI);return this.interactionState={mode:"rotating",areaId:i,startRotation:n.rotation??0,centerPoint:{x:r.centerX,y:r.centerY},startAngle:d},!0}else if(o!=="rotate"&&n?.type==="image"&&r)return this.interactionState={mode:"scaling",areaId:i,handle:o,startScale:n.scale??1,startMousePos:e,pivotPoint:{x:r.centerX,y:r.centerY}},!0}}const s=this.engine.hitTestContent(e.x,e.y);if(s){if(this.emit("area:select",{areaId:s}),this.engine.isPositioningAllowed(s)){const o=t.get(s)?.offset??{x:0,y:0};return this.interactionState={mode:"dragging",areaId:s,startOffset:o,startMousePos:e},!0}return!0}const a=this.engine.hitTestArea(e.x,e.y);return a?(this.emit("area:select",{areaId:a}),!1):(this.emit("area:select",{areaId:null}),!1)}processPointerMove(e){if(this.interactionState.mode==="dragging"){const{areaId:i,startOffset:t,startMousePos:s}=this.interactionState,a=e.x-s.x,o=e.y-s.y,n={x:t.x+a,y:t.y+o},r=this.engine.getAreaLocation(i);if(r){const d=r.width/2-10,l=r.height/2-10;n.x=Math.max(-d,Math.min(d,n.x)),n.y=Math.max(-l,Math.min(l,n.y))}this.emit("content:drag",{areaId:i,offset:n});return}if(this.interactionState.mode==="scaling"){const{areaId:i,startScale:t,startMousePos:s,pivotPoint:a}=this.interactionState,o=Math.sqrt(Math.pow(s.x-a.x,2)+Math.pow(s.y-a.y,2)),n=Math.sqrt(Math.pow(e.x-a.x,2)+Math.pow(e.y-a.y,2));if(o>0){const r=n/o,d=Math.max(.1,Math.min(5,t*r));this.emit("content:scale",{areaId:i,scale:d})}return}if(this.interactionState.mode==="rotating"){const{areaId:i,startRotation:t,centerPoint:s,startAngle:a}=this.interactionState,o=Math.atan2(e.y-s.y,e.x-s.x)*(180/Math.PI);let n=t+(o-a);n=n%360,n<0&&(n+=360);const r=[0,90,180,270,360];for(const d of r)if(Math.abs(n-d)<5){n=d===360?0:d;break}this.emit("content:rotate",{areaId:i,rotation:n});return}this.updateCursor(e)}processPointerUp(){this.interactionState.mode!=="idle"&&(this.interactionState={mode:"idle"})}updateCursor(e){const i=this.getSelectedAreaId();let t="default";if(i){const s=this.engine.hitTestHandle(e.x,e.y,i);s?t=s==="rotate"?"crosshair":s==="nw"||s==="se"?"nwse-resize":"nesw-resize":this.engine.hitTestContent(e.x,e.y)===i&&(t=this.engine.isPositioningAllowed(i)?"move":"pointer")}else this.engine.hitTestContent(e.x,e.y)&&(t="pointer");this.emit("cursor",{cursor:t})}setSelectedArea(e){this.engine.setSelectedArea(e)}destroy(){this.canvas.removeEventListener("mousedown",this.boundMouseDown),this.canvas.removeEventListener("mousemove",this.boundMouseMove),this.canvas.removeEventListener("mouseup",this.boundMouseUp),this.canvas.removeEventListener("mouseleave",this.boundMouseLeave),this.canvas.removeEventListener("contextmenu",this.boundContextMenu),this.canvas.removeEventListener("dblclick",this.boundDoubleClick),this.canvas.removeEventListener("touchstart",this.boundTouchStart),this.canvas.removeEventListener("touchmove",this.boundTouchMove),this.canvas.removeEventListener("touchend",this.boundTouchEnd),this.gestureHandler.reset(),this.clear()}}class B{constructor(e="/api"){this.baseUrl=e}async getTemplate(e){try{const i=await fetch(`${this.baseUrl}/templates/${e}`);if(!i.ok){let t=`Failed to load template (${i.status} ${i.statusText}): ${e}
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class N{constructor(e){this.animationFrameId=null,this.needsRender=!0,this.areas=[],this.contents=new Map,this.artboard=null,this.bgImageElement=null,this.loadedImages=new Map,this.loadingImages=new Set,this.zoom=1,this.pan={x:0,y:0},this.selectedAreaId=null,this.hoveredHandle=null,this.eventListeners=new Map,this.safeArea=null,this.safeAreaViolations=new Set,this.showAreaBorders=!0,this.areaSelectionEnabled=!0,this.renderLoop=()=>{try{this.needsRender&&(this.render(),this.needsRender=!1,this.emit("render",void 0))}catch(i){console.error("Error in render loop:",i)}this.animationFrameId=requestAnimationFrame(this.renderLoop)},this.canvas=e;const t=e.getContext("2d");if(!t)throw new Error("Failed to get 2D context from canvas");this.ctx=t}start(){this.animationFrameId===null&&this.renderLoop()}stop(){this.animationFrameId!==null&&(cancelAnimationFrame(this.animationFrameId),this.animationFrameId=null)}setArtboard(e){this.artboard=e,this.requestRender()}getArtboard(){return this.artboard}setAreas(e){this.areas=[...e].sort((t,i)=>t.zIndex-i.zIndex),this.requestRender()}getAreas(){return this.areas}setContents(e){this.contents=e,this.loadContentImages(),this.requestRender()}getContents(){return this.contents}updateContent(e,t){this.contents.set(e,t),t.type==="image"&&this.loadContentImages(),this.requestRender(),this.emit("content:change",{areaId:e,content:t})}removeContent(e){this.contents.delete(e),this.loadedImages.delete(e),this.requestRender()}setBackgroundImage(e){if(this.backgroundImage=e,e?.url&&e.url!==this.bgImageElement?.src){const t=e.url,i=new Image;i.crossOrigin="anonymous",i.onload=()=>{this.bgImageElement=i,this.requestRender()},i.onerror=()=>{this.bgImageElement=null,console.error("Failed to load background image:",t),this.requestRender()},i.src=t}else e?.url||(this.bgImageElement=null);this.requestRender()}setZoom(e){this.zoom=Math.max(.1,Math.min(5,e)),this.requestRender()}setPan(e){this.pan=e,this.requestRender()}getZoom(){return this.zoom}getPan(){return this.pan}setShowAreaBorders(e){this.showAreaBorders=e,this.requestRender()}setAreaSelectionEnabled(e){this.areaSelectionEnabled=e}getCenteredPan(e){if(!this.artboard)return null;const{width:t,height:i}=this.artboard,s=window.devicePixelRatio||1,n=this.canvas.width/s,o=this.canvas.height/s;return{x:(n/e-t)/2,y:(o/e-i)/2}}requestRender(){this.needsRender=!0}fitToView(){if(!this.artboard)return;const e=0,{width:t,height:i}=this.artboard,s=window.devicePixelRatio||1,n=this.canvas.width/s-e*2,o=this.canvas.height/s-e*2;if(n<=0||o<=0)return;const a=n/t,r=o/i,d=Math.min(a,r),l=this.canvas.width/s,c=this.canvas.height/s,p=(l/d-t)/2,u=(c/d-i)/2;this.zoom=d,this.pan={x:p,y:u},this.requestRender()}setSelectedArea(e){this.selectedAreaId!==e&&(this.selectedAreaId=e,this.requestRender(),this.emit("area:select",{areaId:e}))}getSelectedArea(){return this.selectedAreaId}setHoveredHandle(e){this.hoveredHandle!==e&&(this.hoveredHandle=e,this.requestRender())}getHoveredHandle(){return this.hoveredHandle}screenToCanvas(e,t){const i=window.devicePixelRatio||1,s=e/i,n=t/i,o=s/this.zoom-this.pan.x,a=n/this.zoom-this.pan.y;return{x:o,y:a}}getContentBounds(e){const t=this.areas.find(d=>d.id===e);if(!t)return null;const i=this.contents.get(e);if(!i)return null;const{location:s}=t,{x:n,y:o,width:a,height:r}=s;if(i.type==="text"){const d=i.offset||{x:0,y:0},l=i.scale??1,c=i.rotation??0,p=a*l,u=r*l,g=n+a/2+d.x,m=o+r/2+d.y;return{x:g-p/2,y:m-u/2,width:p,height:u,rotation:c,centerX:g,centerY:m}}else if(i.type==="image"){const d=this.loadedImages.get(e);if(!d)return null;const l=i.offset||{x:0,y:0},c=i.scale??1,p=i.rotation??0,u=d.naturalWidth/d.naturalHeight,g=a/r;let m,v;u>g?(m=a,v=a/u):(v=r,m=r*u);const b=m*c,w=v*c,y=n+a/2+l.x,x=o+r/2+l.y;return{x:y-b/2,y:x-w/2,width:b,height:w,rotation:p,centerX:y,centerY:x}}return null}hitTestContent(e,t){if(!this.areaSelectionEnabled)return null;for(let i=this.areas.length-1;i>=0;i--){const s=this.areas[i],n=this.getContentBounds(s.id);if(n&&this.isPointInBounds(e,t,n))return s.id}return null}hitTestArea(e,t){if(!this.areaSelectionEnabled)return null;for(let i=this.areas.length-1;i>=0;i--){const s=this.areas[i],{x:n,y:o,width:a,height:r}=s.location;if(e>=n&&e<=n+a&&t>=o&&t<=o+r)return s.id}return null}isPointInBounds(e,t,i){const{centerX:s,centerY:n,width:o,height:a,rotation:r}=i,d=-r*Math.PI/180,l=Math.cos(d),c=Math.sin(d),p=e-s,u=t-n,g=p*l-u*c,m=p*c+u*l;return g>=-o/2&&g<=o/2&&m>=-a/2&&m<=a/2}getContentHandlePositions(e){const t=this.areas.find(b=>b.id===e);if(!t)return null;const i=this.contents.get(e);if(!i)return null;const s=this.getContentBounds(e);if(!s)return null;const{centerX:n,centerY:o,width:a,height:r,rotation:d}=s,l=6/this.zoom,c=30/this.zoom,p=d*Math.PI/180,u=Math.cos(p),g=Math.sin(p),m=(b,w)=>({x:n+b*u-w*g,y:o+b*g+w*u}),v=[];if(i.type==="image"&&t.imageOptions?.allowScaling||i.type==="text"&&t.textOptions?.allowScaling){const b=a/2,w=r/2,y=m(-b,-w);v.push({type:"nw",...y,radius:l});const x=m(b,-w);v.push({type:"ne",...x,radius:l});const C=m(b,w);v.push({type:"se",...C,radius:l});const M=m(-b,w);v.push({type:"sw",...M,radius:l})}if(i.type==="image"&&t.imageOptions?.allowRotation||i.type==="text"&&t.textOptions?.allowRotation){const b=m(0,-r/2-c);v.push({type:"rotate",...b,radius:l})}return v}hitTestHandle(e,t,i){const s=this.getContentHandlePositions(i);if(!s)return null;for(const n of s){const o=e-n.x,a=t-n.y;if(Math.sqrt(o*o+a*a)<=n.radius*2)return n.type}return null}isPositioningAllowed(e){const t=this.areas.find(s=>s.id===e);if(!t)return!1;const i=this.contents.get(e);return i?i.type==="text"?t.textOptions?.allowPositioning??!1:t.imageOptions?.allowPositioning??!1:!1}getAreaLocation(e){const t=this.areas.find(i=>i.id===e);return t?t.location:null}getArea(e){return this.areas.find(t=>t.id===e)}setSafeArea(e){this.safeArea=e??null,this.requestRender()}checkSafeAreaViolations(){if(!this.safeArea||!this.artboard)return[];const e=[],{top:t,right:i,bottom:s,left:n}=this.safeArea,o=n,a=t,r=this.artboard.width-i,d=this.artboard.height-s;for(const l of this.areas){if(!this.contents.get(l.id))continue;const c=this.getContentBounds(l.id);if(!c)continue;const{centerX:p,centerY:u,width:g,height:m,rotation:v}=c,b=v*Math.PI/180,w=Math.abs(Math.cos(b)),y=Math.abs(Math.sin(b)),x=g*w+m*y,C=g*y+m*w,M=p-x/2,R=u-C/2,U=p+x/2,D=u+C/2;(M<o||R<a||U>r||D>d)&&e.push(l.id)}return this.safeAreaViolations=new Set(e),this.requestRender(),e}on(e,t){return this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t),()=>{this.eventListeners.get(e)?.delete(t)}}emit(e,t){const i=this.eventListeners.get(e);if(i)for(const s of i)s(t)}async exportToPNG(){if(!this.artboard)throw new Error("No artboard configured");const{width:e,height:t,backgroundColor:i}=this.artboard,s=document.createElement("canvas");s.width=e,s.height=t;const n=s.getContext("2d");if(!n)throw new Error("Failed to get 2D context for export");n.fillStyle=i,n.fillRect(0,0,e,t),this.backgroundImage?.position==="behind"&&this.bgImageElement&&this.drawBackgroundImageOnContext(n,e,t);for(const o of this.areas)this.drawAreaContentOnContext(n,o);return this.backgroundImage?.position==="overlay"&&this.bgImageElement&&this.drawBackgroundImageOnContext(n,e,t),new Promise((o,a)=>{try{s.toBlob(r=>{r?o(r):a(new Error("Failed to create PNG blob"))},"image/png",1)}catch(r){r instanceof DOMException&&r.name==="SecurityError"?a(new Error("Cannot export: images failed to load with CORS. Check CDN configuration.")):a(r)}})}loadContentImages(){for(const[e,t]of this.contents)if(t.type==="image"&&!this.loadedImages.has(e)&&!this.loadingImages.has(e)){this.loadingImages.add(e);const i=new Image;i.crossOrigin="anonymous",i.onload=()=>{this.loadedImages.set(e,i),this.loadingImages.delete(e),this.requestRender()},i.onerror=()=>{this.loadingImages.delete(e),console.error("Failed to load image for area:",e)},i.src=t.dataUrl}for(const e of this.loadedImages.keys()){const t=this.contents.get(e);(!t||t.type!=="image")&&this.loadedImages.delete(e)}}render(){const{ctx:e,canvas:t}=this;if(e.fillStyle="#f3f4f6",e.fillRect(0,0,t.width,t.height),!this.artboard)return;e.save();const i=window.devicePixelRatio||1;e.scale(i,i),e.scale(this.zoom,this.zoom),e.translate(this.pan.x,this.pan.y);const{width:s,height:n,backgroundColor:o}=this.artboard;e.fillStyle=o,e.fillRect(0,0,s,n),e.strokeStyle="#d1d5db",e.lineWidth=1/this.zoom,e.strokeRect(0,0,s,n),this.backgroundImage?.position==="behind"&&this.renderBackgroundImage();for(const a of this.areas)this.renderAreaWithContent(a);this.backgroundImage?.position==="overlay"&&this.renderBackgroundImage(),this.renderSafeAreaGuide();for(const a of this.safeAreaViolations){const r=this.areas.find(u=>u.id===a);if(!r)continue;const{x:d,y:l,width:c,height:p}=r.location;e.save(),e.fillStyle="rgba(239, 68, 68, 0.15)",e.fillRect(d,l,c,p),e.restore()}this.selectedAreaId&&this.renderSelectionOverlay(this.selectedAreaId),e.restore()}renderSelectionOverlay(e){const t=this.getContentBounds(e);if(!t){const l=this.areas.find(v=>v.id===e);if(!l)return;const{ctx:c}=this,{x:p,y:u,width:g,height:m}=l.location;c.save(),c.strokeStyle="#3b82f6",c.lineWidth=2/this.zoom,c.setLineDash([]),c.strokeRect(p,u,g,m),c.restore();return}const{ctx:i}=this,{centerX:s,centerY:n,width:o,height:a,rotation:r}=t;i.save(),i.translate(s,n),i.rotate(r*Math.PI/180),i.strokeStyle="#3b82f6",i.lineWidth=2/this.zoom,i.setLineDash([]),i.strokeRect(-o/2,-a/2,o,a),i.restore();const d=this.getContentHandlePositions(e);if(d)for(const l of d){i.save();const c=this.hoveredHandle===l.type,p=c?"#dbeafe":"#ffffff",u=c?"#2563eb":"#3b82f6";if(l.type==="rotate"){const g=this.getRotatedPoint(s,n-a/2,s,n,r);i.beginPath(),i.strokeStyle=u,i.lineWidth=1/this.zoom,i.setLineDash([4/this.zoom,4/this.zoom]),i.moveTo(g.x,g.y),i.lineTo(l.x,l.y),i.stroke(),i.setLineDash([]),i.beginPath(),i.fillStyle=p,i.strokeStyle=u,i.lineWidth=2/this.zoom,i.arc(l.x,l.y,l.radius,0,Math.PI*2),i.fill(),i.stroke(),i.beginPath(),i.strokeStyle=u,i.lineWidth=1.5/this.zoom,i.arc(l.x,l.y,l.radius*.5,-Math.PI*.8,Math.PI*.5),i.stroke()}else i.fillStyle=p,i.strokeStyle=u,i.lineWidth=2/this.zoom,i.fillRect(l.x-l.radius,l.y-l.radius,l.radius*2,l.radius*2),i.strokeRect(l.x-l.radius,l.y-l.radius,l.radius*2,l.radius*2);i.restore()}}getRotatedPoint(e,t,i,s,n){const o=n*Math.PI/180,a=Math.cos(o),r=Math.sin(o),d=e-i,l=t-s;return{x:i+d*a-l*r,y:s+d*r+l*a}}renderBackgroundImage(){if(!this.bgImageElement||!this.artboard||!this.backgroundImage)return;const{ctx:e}=this,{opacity:t}=this.backgroundImage,{width:i,height:s}=this.artboard;e.save(),e.globalAlpha=t;const n=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,o=i/s;let a,r,d,l;n>o?(r=s,a=s*n,d=(i-a)/2,l=0):(a=i,r=i/n,d=0,l=(s-r)/2),e.drawImage(this.bgImageElement,d,l,a,r),e.restore()}renderSafeAreaGuide(){if(!this.safeArea||!this.artboard)return;const{ctx:e}=this,{top:t,right:i,bottom:s,left:n}=this.safeArea,o=n,a=t,r=this.artboard.width-n-i,d=this.artboard.height-t-s;r<=0||d<=0||(e.save(),e.strokeStyle="#22c55e",e.lineWidth=1/this.zoom,e.setLineDash([6/this.zoom,4/this.zoom]),e.strokeRect(o,a,r,d),e.setLineDash([]),e.restore())}renderAreaWithContent(e){const{ctx:t}=this,{location:i,backgroundColor:s}=e,{x:n,y:o,width:a,height:r}=i;s&&(t.fillStyle=s,t.fillRect(n,o,a,r));const d=this.contents.get(e.id);d&&(d.type==="text"?this.renderTextContent(e,d):d.type==="image"&&this.renderImageContent(e,d)),this.showAreaBorders&&e.showBorder&&(t.save(),t.strokeStyle=e.borderColor||"#3b82f6",t.lineWidth=1/this.zoom,t.setLineDash([4/this.zoom,4/this.zoom]),t.strokeRect(n,o,a,r),t.setLineDash([]),t.restore())}renderTextContent(e,t){if(!t.text.trim())return;const{ctx:i}=this,{location:s}=e,{x:n,y:o,width:a,height:r}=s,d=t.offset||{x:0,y:0},l=t.scale??1,c=t.rotation??0,p=n+a/2+d.x,u=o+r/2+d.y;i.save(),i.beginPath(),i.rect(n,o,a,r),i.clip(),i.translate(p,u),i.rotate(c*Math.PI/180),i.scale(l,l),i.font=`${t.size}px ${t.font}, sans-serif`,i.fillStyle=t.color,i.textBaseline="middle";let g;switch(t.align){case"left":i.textAlign="left",g=-a/2+10;break;case"right":i.textAlign="right",g=a/2-10;break;case"center":default:i.textAlign="center",g=0;break}i.fillText(t.text,g,0),i.restore()}renderImageContent(e,t){const i=this.loadedImages.get(e.id);if(!i)return;const{ctx:s}=this,{location:n}=e,{x:o,y:a,width:r,height:d}=n,l=t.offset||{x:0,y:0},c=t.scale??1,p=t.rotation??0;s.save(),s.beginPath(),s.rect(o,a,r,d),s.clip();const u=i.naturalWidth/i.naturalHeight,g=r/d;let m,v;u>g?(m=r,v=r/u):(v=d,m=d*u);const b=m*c,w=v*c,y=o+r/2+l.x,x=a+d/2+l.y;s.translate(y,x),s.rotate(p*Math.PI/180),s.drawImage(i,-b/2,-w/2,b,w),s.restore()}drawBackgroundImageOnContext(e,t,i){if(!this.bgImageElement||!this.backgroundImage)return;const{opacity:s}=this.backgroundImage;e.save(),e.globalAlpha=s;const n=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,o=t/i;let a,r,d,l;n>o?(r=i,a=i*n,d=(t-a)/2,l=0):(a=t,r=t/n,d=0,l=(i-r)/2),e.drawImage(this.bgImageElement,d,l,a,r),e.restore()}drawAreaContentOnContext(e,t){const{location:i,backgroundColor:s}=t,{x:n,y:o,width:a,height:r}=i;s&&(e.fillStyle=s,e.fillRect(n,o,a,r));const d=this.contents.get(t.id);if(d){if(d.type==="text"){if(!d.text.trim())return;const l=d.offset||{x:0,y:0},c=d.scale??1,p=d.rotation??0,u=n+a/2+l.x,g=o+r/2+l.y;e.save(),e.beginPath(),e.rect(n,o,a,r),e.clip(),e.translate(u,g),e.rotate(p*Math.PI/180),e.scale(c,c),e.font=`${d.size}px ${d.font}, sans-serif`,e.fillStyle=d.color,e.textBaseline="middle";let m;switch(d.align){case"left":e.textAlign="left",m=-a/2+10;break;case"right":e.textAlign="right",m=a/2-10;break;case"center":default:e.textAlign="center",m=0;break}e.fillText(d.text,m,0),e.restore()}else if(d.type==="image"){const l=this.loadedImages.get(t.id);if(!l)return;const c=d.offset||{x:0,y:0},p=d.scale??1,u=d.rotation??0;e.save(),e.beginPath(),e.rect(n,o,a,r),e.clip();const g=l.naturalWidth/l.naturalHeight,m=a/r;let v,b;g>m?(v=a,b=a/g):(b=r,v=r*g);const w=v*p,y=b*p,x=n+a/2+c.x,C=o+r/2+c.y;e.translate(x,C),e.rotate(u*Math.PI/180),e.drawImage(l,-w/2,-y/2,w,y),e.restore()}}}destroy(){this.stop(),this.bgImageElement=null,this.loadedImages.clear(),this.loadingImages.clear(),this.eventListeners.clear()}}function z(f){const e=f.textOptions;return{type:"text",text:e?.defaultText||"",font:e?.defaultFont||"Inter",size:e?.defaultSize||24,color:e?.defaultColor||"#000000",align:e?.defaultAlign||"center"}}function E(f){return f.contentType==="text"||f.contentType==="both"}function T(f){return f.contentType==="image"||f.contentType==="both"}const V=50;class F{constructor(e){this.history=[],this.historyIndex=-1,this.listeners=new Set,this.isRestoring=!1,this.state=this.cloneState(e),this.history.push({state:this.cloneState(e),timestamp:Date.now(),description:"Initial state"}),this.historyIndex=0}getState(){return this.state}setState(e,t,i=!1){const s=this.state;this.state={...this.state,...e},!i&&!this.isRestoring&&this.saveToHistory(s,t),this.notifyListeners()}update(e){this.state={...this.state,...e},this.notifyListeners()}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}undo(){return this.canUndo()?(this.historyIndex--,this.restoreFromHistory(),!0):!1}redo(){return this.canRedo()?(this.historyIndex++,this.restoreFromHistory(),!0):!1}canUndo(){return this.historyIndex>0}canRedo(){return this.historyIndex<this.history.length-1}clearHistory(){this.history=[],this.historyIndex=-1}getHistory(){return this.history}saveToHistory(e,t){this.historyIndex<this.history.length-1&&(this.history=this.history.slice(0,this.historyIndex+1)),this.history.push({state:this.cloneState(e),timestamp:Date.now(),description:t}),this.history.length>V&&this.history.shift(),this.historyIndex=this.history.length-1}restoreFromHistory(){const e=this.history[this.historyIndex];e&&(this.isRestoring=!0,this.state=this.cloneState(e.state),this.notifyListeners(),this.isRestoring=!1)}notifyListeners(){const e=this.state;for(const t of this.listeners)try{t(e)}catch(i){console.error("Error in state change listener:",i)}}cloneState(e){return JSON.parse(JSON.stringify(e))}}class P{constructor(e){this.listeners=new Set,this.areas=e,this.contents=this.buildInitialContents()}buildInitialContents(){const e=new Map;for(const t of this.areas)E(t)&&e.set(t.id,z(t));return e}getContents(){return this.contents}getContent(e){return this.contents.get(e)}setTextContent(e,t){const i=this.contents.get(e),s=this.areas.find(o=>o.id===e),n=s?z(s):{type:"text",text:"",font:"Inter",size:24,color:"#000000",align:"center"};i?.type==="text"?this.contents.set(e,{...i,...t}):this.contents.set(e,{...n,...t}),this.notify()}setImageContent(e,t,i,s){const n=this.contents.get(e),o={type:"image",dataUrl:t,filename:i,assetId:s?.assetId,assetUrl:s?.assetUrl,...n?.type==="image"&&{offset:n.offset,scale:n.scale,rotation:n.rotation}};this.contents.set(e,o),this.notify()}clearContent(e){const t=this.areas.find(i=>i.id===e);t&&E(t)?this.contents.set(e,z(t)):this.contents.delete(e),this.notify()}setContentOffset(e,t){const i=this.contents.get(e);i&&(this.contents.set(e,{...i,offset:t}),this.notify())}setImageScale(e,t){const i=this.contents.get(e);if(!i||i.type!=="image")return;const s=Math.max(.1,Math.min(5,t));this.contents.set(e,{...i,scale:s}),this.notify()}setImageRotation(e,t){const i=this.contents.get(e);if(!i||i.type!=="image")return;let s=t%360;s<0&&(s+=360),this.contents.set(e,{...i,rotation:s}),this.notify()}reset(){this.contents=this.buildInitialContents(),this.notify()}toJSON(){return Array.from(this.contents.entries())}fromJSON(e){this.contents=new Map(e),this.notify()}setContentsQuiet(e){this.contents=new Map(e)}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){for(const e of this.listeners)try{e()}catch(t){console.error("Error in AreaContentManager listener:",t)}}}class S{constructor(){this.listeners=new Map}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}once(e,t){const i=s=>{t(s),this.off(e,i)};return this.on(e,i)}off(e,t){const i=this.listeners.get(e);i&&(i.delete(t),i.size===0&&this.listeners.delete(e))}emit(e,t){const i=this.listeners.get(e);if(i){const s=Array.from(i);for(const n of s)try{n(t)}catch(o){console.error(`Error in event handler for "${String(e)}":`,o)}}}clear(e){e?this.listeners.delete(e):this.listeners.clear()}listenerCount(e){return this.listeners.get(e)?.size??0}}class O extends S{constructor(){super(...arguments),this.activeTouches=new Map,this.initialPinchDistance=null,this.lastPinchCenter=null,this.isTwoFingerGesture=!1}get isMultiTouch(){return this.isTwoFingerGesture}handleTouchStart(e){for(let t=0;t<e.changedTouches.length;t++){const i=e.changedTouches[t];this.activeTouches.set(i.identifier,{x:i.clientX,y:i.clientY})}if(this.activeTouches.size===2){this.isTwoFingerGesture=!0,e.preventDefault();const[t,i]=Array.from(this.activeTouches.values());this.initialPinchDistance=this.getDistance(t,i),this.lastPinchCenter=this.getCenter(t,i)}}handleTouchMove(e){for(let t=0;t<e.changedTouches.length;t++){const i=e.changedTouches[t];this.activeTouches.set(i.identifier,{x:i.clientX,y:i.clientY})}if(this.activeTouches.size===2&&this.initialPinchDistance!==null&&this.lastPinchCenter){e.preventDefault();const[t,i]=Array.from(this.activeTouches.values()),s=this.getDistance(t,i),n=this.getCenter(t,i),o=s/this.initialPinchDistance;this.emit("pinch",{scale:o,centerX:n.x,centerY:n.y});const a=n.x-this.lastPinchCenter.x,r=n.y-this.lastPinchCenter.y;(Math.abs(a)>.5||Math.abs(r)>.5)&&this.emit("pan",{deltaX:a,deltaY:r}),this.initialPinchDistance=s,this.lastPinchCenter=n}}handleTouchEnd(e){for(let t=0;t<e.changedTouches.length;t++){const i=e.changedTouches[t];this.activeTouches.delete(i.identifier)}this.activeTouches.size<2&&(this.initialPinchDistance=null,this.lastPinchCenter=null,this.isTwoFingerGesture=!1)}reset(){this.activeTouches.clear(),this.initialPinchDistance=null,this.lastPinchCenter=null,this.isTwoFingerGesture=!1}getDistance(e,t){const i=t.x-e.x,s=t.y-e.y;return Math.sqrt(i*i+s*s)}getCenter(e,t){return{x:(e.x+t.x)/2,y:(e.y+t.y)/2}}}class $ extends S{constructor(e,t,i,s){super(),this.interactionState={mode:"idle"},this.initialPinchZoom=1,this.canvas=e,this.engine=t,this.getContents=i,this.getSelectedAreaId=s,this.gestureHandler=new O,this.boundMouseDown=this.handleMouseDown.bind(this),this.boundMouseMove=this.handleMouseMove.bind(this),this.boundMouseUp=this.handleMouseUp.bind(this),this.boundMouseLeave=this.handleMouseLeave.bind(this),this.boundContextMenu=this.handleContextMenu.bind(this),this.boundDoubleClick=this.handleDoubleClick.bind(this),this.boundTouchStart=this.handleTouchStart.bind(this),this.boundTouchMove=this.handleTouchMove.bind(this),this.boundTouchEnd=this.handleTouchEnd.bind(this),e.addEventListener("mousedown",this.boundMouseDown),e.addEventListener("mousemove",this.boundMouseMove),e.addEventListener("mouseup",this.boundMouseUp),e.addEventListener("mouseleave",this.boundMouseLeave),e.addEventListener("contextmenu",this.boundContextMenu),e.addEventListener("dblclick",this.boundDoubleClick),e.addEventListener("touchstart",this.boundTouchStart,{passive:!1}),e.addEventListener("touchmove",this.boundTouchMove,{passive:!1}),e.addEventListener("touchend",this.boundTouchEnd),this.gestureHandler.on("pinch",({scale:n})=>{const o=Math.max(.1,Math.min(5,this.initialPinchZoom*n));this.emit("zoom",{zoom:o})}),this.gestureHandler.on("pan",({deltaX:n,deltaY:o})=>{const a=this.engine.getPan(),r=this.engine.getZoom();this.emit("pan",{pan:{x:a.x+n/r,y:a.y+o/r}})})}getCanvasPosition(e,t){const i=this.canvas.getBoundingClientRect(),s=window.devicePixelRatio||1,n=(e-i.left)*s,o=(t-i.top)*s;return this.engine.screenToCanvas(n,o)}handleMouseDown(e){const t=this.getCanvasPosition(e.clientX,e.clientY);this.processPointerDown(t)}handleMouseMove(e){const t=this.getCanvasPosition(e.clientX,e.clientY);this.processPointerMove(t)}handleMouseUp(){this.processPointerUp()}handleMouseLeave(){this.processPointerUp(),this.emit("cursor",{cursor:"default"})}handleContextMenu(e){e.preventDefault()}handleDoubleClick(e){const t=this.getCanvasPosition(e.clientX,e.clientY);let i=this.engine.hitTestContent(t.x,t.y);i||(i=this.engine.hitTestArea(t.x,t.y)??null),this.emit("context-menu",{areaId:i,clientX:e.clientX,clientY:e.clientY})}handleTouchStart(e){if(this.gestureHandler.handleTouchStart(e),this.gestureHandler.isMultiTouch){this.initialPinchZoom=this.engine.getZoom(),this.interactionState.mode!=="idle"&&(this.interactionState={mode:"idle"});return}if(e.touches.length!==1)return;const t=e.touches[0],i=this.getCanvasPosition(t.clientX,t.clientY);this.processPointerDown(i)&&e.preventDefault()}handleTouchMove(e){if(this.gestureHandler.handleTouchMove(e),this.gestureHandler.isMultiTouch||e.touches.length!==1||this.interactionState.mode==="idle")return;const t=e.touches[0],i=this.getCanvasPosition(t.clientX,t.clientY);this.processPointerMove(i),e.preventDefault()}handleTouchEnd(e){this.gestureHandler.handleTouchEnd(e),this.gestureHandler.isMultiTouch||this.processPointerUp()}processPointerDown(e){const t=this.getSelectedAreaId(),i=this.getContents();if(t){const o=this.engine.hitTestHandle(e.x,e.y,t);if(o){const a=i.get(t),r=this.engine.getContentBounds(t);if(o==="rotate"&&a?.type==="image"&&r){const d=Math.atan2(e.y-r.centerY,e.x-r.centerX)*(180/Math.PI);return this.interactionState={mode:"rotating",areaId:t,startRotation:a.rotation??0,centerPoint:{x:r.centerX,y:r.centerY},startAngle:d},!0}else if(o!=="rotate"&&a?.type==="image"&&r)return this.interactionState={mode:"scaling",areaId:t,handle:o,startScale:a.scale??1,startMousePos:e,pivotPoint:{x:r.centerX,y:r.centerY}},!0}}const s=this.engine.hitTestContent(e.x,e.y);if(s){if(this.emit("area:select",{areaId:s}),this.engine.isPositioningAllowed(s)){const o=i.get(s)?.offset??{x:0,y:0};return this.interactionState={mode:"dragging",areaId:s,startOffset:o,startMousePos:e},!0}return!0}const n=this.engine.hitTestArea(e.x,e.y);return n?(this.emit("area:select",{areaId:n}),!1):(this.emit("area:select",{areaId:null}),!1)}processPointerMove(e){if(this.interactionState.mode==="dragging"){const{areaId:t,startOffset:i,startMousePos:s}=this.interactionState,n=e.x-s.x,o=e.y-s.y,a={x:i.x+n,y:i.y+o},r=this.engine.getAreaLocation(t);if(r){const d=r.width/2-10,l=r.height/2-10;a.x=Math.max(-d,Math.min(d,a.x)),a.y=Math.max(-l,Math.min(l,a.y))}this.emit("content:drag",{areaId:t,offset:a});return}if(this.interactionState.mode==="scaling"){const{areaId:t,startScale:i,startMousePos:s,pivotPoint:n}=this.interactionState,o=Math.sqrt(Math.pow(s.x-n.x,2)+Math.pow(s.y-n.y,2)),a=Math.sqrt(Math.pow(e.x-n.x,2)+Math.pow(e.y-n.y,2));if(o>0){const r=a/o,d=Math.max(.1,Math.min(5,i*r));this.emit("content:scale",{areaId:t,scale:d})}return}if(this.interactionState.mode==="rotating"){const{areaId:t,startRotation:i,centerPoint:s,startAngle:n}=this.interactionState,o=Math.atan2(e.y-s.y,e.x-s.x)*(180/Math.PI);let a=i+(o-n);a=a%360,a<0&&(a+=360);const r=[0,90,180,270,360];for(const d of r)if(Math.abs(a-d)<5){a=d===360?0:d;break}this.emit("content:rotate",{areaId:t,rotation:a});return}this.updateCursor(e)}processPointerUp(){this.interactionState.mode!=="idle"&&(this.interactionState={mode:"idle"})}updateCursor(e){const t=this.getSelectedAreaId();let i="default";if(t){const s=this.engine.hitTestHandle(e.x,e.y,t);s?i=s==="rotate"?"crosshair":s==="nw"||s==="se"?"nwse-resize":"nesw-resize":this.engine.hitTestContent(e.x,e.y)===t&&(i=this.engine.isPositioningAllowed(t)?"move":"pointer")}else this.engine.hitTestContent(e.x,e.y)&&(i="pointer");this.emit("cursor",{cursor:i})}setSelectedArea(e){this.engine.setSelectedArea(e)}destroy(){this.canvas.removeEventListener("mousedown",this.boundMouseDown),this.canvas.removeEventListener("mousemove",this.boundMouseMove),this.canvas.removeEventListener("mouseup",this.boundMouseUp),this.canvas.removeEventListener("mouseleave",this.boundMouseLeave),this.canvas.removeEventListener("contextmenu",this.boundContextMenu),this.canvas.removeEventListener("dblclick",this.boundDoubleClick),this.canvas.removeEventListener("touchstart",this.boundTouchStart),this.canvas.removeEventListener("touchmove",this.boundTouchMove),this.canvas.removeEventListener("touchend",this.boundTouchEnd),this.gestureHandler.reset(),this.clear()}}class B{constructor(e="/api"){this.baseUrl=e}async getTemplate(e){try{const t=await fetch(`${this.baseUrl}/templates/${e}`);if(!t.ok){let i=`Failed to load template (${t.status} ${t.statusText}): ${e}
|
|
2
2
|
|
|
3
|
-
`;throw
|
|
3
|
+
`;throw t.status===404?i+="Template not found.":t.status===403?i+="Access forbidden.":t.status>=500&&(i+="Server error."),new Error(i)}return(await t.json()).templateJson}catch(t){throw t instanceof Error&&t.message.includes("Failed to fetch")?new Error(`Network error loading template: ${e}`):t}}async getProduct(e){try{const t=await fetch(`${this.baseUrl}/products/${e}`);if(!t.ok){let i=`Failed to load product (${t.status} ${t.statusText}): ${e}
|
|
4
4
|
|
|
5
|
-
`;throw
|
|
5
|
+
`;throw t.status===404?i+="Product not found.":t.status===403?i+="Access forbidden.":t.status>=500&&(i+="Server error."),new Error(i)}return t.json()}catch(t){throw t instanceof Error&&t.message.includes("Failed to fetch")?new Error(`Network error loading product: ${e}`):t}}async finalizeDesign(e,t){try{const i=await fetch(`${this.baseUrl}/finalize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({shopId:e,templateId:t.templateId,designJson:t})});if(!i.ok){let s=`Failed to finalize design (${i.status} ${i.statusText})
|
|
6
6
|
|
|
7
|
-
`;if(
|
|
7
|
+
`;if(i.status===400){const n=await i.json().catch(()=>({}));s+=n.error||"Invalid design data."}else i.status===429?s+="Rate limit exceeded.":i.status>=500&&(s+="Server error.");throw new Error(s)}return i.json()}catch(i){throw i instanceof Error&&i.message.includes("Failed to fetch")?new Error("Network error finalizing design"):i}}async finalizeMultiView(e){try{const t=e.designs.map(s=>({templateId:s.templateId,viewName:s.viewName,areaContents:s.contents.map(([n,o])=>{const a={areaId:n,isModified:!0};return o.type==="text"?a.text={content:o.text,font:o.font,size:o.size,color:o.color,align:o.align,position:{x:o.offset?.x??0,y:o.offset?.y??0}}:o.type==="image"&&(a.image={assetId:o.assetId||"inline",assetUrl:o.assetUrl||"",rotation:o.rotation??0,scale:o.scale??1,position:{x:o.offset?.x??0,y:o.offset?.y??0},...o.assetId?{}:{dataUrl:o.dataUrl}}),a})})),i=await fetch(`${this.baseUrl}/finalize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({productId:e.productId,designs:t,cartContext:e.cartContext,customerId:e.customerId,customerEmail:e.customerEmail,sessionId:e.sessionId})});if(!i.ok){let s=`Failed to finalize multi-view design (${i.status} ${i.statusText})
|
|
8
8
|
|
|
9
|
-
`;if(
|
|
9
|
+
`;if(i.status===400){const n=await i.json().catch(()=>({}));s+=n.error||"Invalid design data."}else i.status===429?s+="Rate limit exceeded.":i.status>=500&&(s+="Server error.");throw new Error(s)}return i.json()}catch(t){throw t instanceof Error&&t.message.includes("Failed to fetch")?new Error("Network error finalizing multi-view design"):t}}async getDesignStatus(e){try{const t=await fetch(`${this.baseUrl}/designs/${e}/status`);if(!t.ok)throw new Error(`Failed to check design status (${t.status})`);return t.json()}catch(t){throw t instanceof Error&&t.message.includes("Failed to fetch")?new Error("Network error checking design status"):t}}async pollMultiViewStatus(e){try{const t=await fetch(`${this.baseUrl}/finalize-status?designIds=${e.join(",")}`);if(!t.ok)throw new Error(`Failed to check multi-view design status (${t.status})`);return t.json()}catch(t){throw t instanceof Error&&t.message.includes("Failed to fetch")?new Error("Network error checking multi-view design status"):t}}async uploadAsset(e,t){try{const i=new FormData;i.append("shopId",t),i.append("file",e);const s=await fetch(`${this.baseUrl}/assets/upload`,{method:"POST",body:i});if(!s.ok){let n=`Failed to upload asset (${s.status} ${s.statusText})
|
|
10
10
|
|
|
11
|
-
`;if(s.status===400){const o=await s.json().catch(()=>({}));a+=o.error||"Invalid file."}else s.status===413?a+="File too large (max 15MB).":s.status>=500&&(a+="Server error.");throw new Error(a)}return s.json()}catch(t){throw t instanceof Error&&t.message.includes("Failed to fetch")?new Error("Network error uploading asset"):t}}async getStorageUsage(e){try{const i=await fetch(`${this.baseUrl}/assets/usage?shopId=${encodeURIComponent(e)}`);if(!i.ok)throw new Error(`Failed to fetch storage usage (${i.status})`);return i.json()}catch(i){throw i instanceof Error&&i.message.includes("Failed to fetch")?new Error("Network error fetching storage usage"):i}}}function c(v,e,...i){const t=document.createElement(v);return e&&Object.entries(e).forEach(([s,a])=>{s==="class"?t.className=a:s in t?t[s]=a:t.setAttribute(s,String(a))}),i.forEach(s=>{typeof s=="string"?t.appendChild(document.createTextNode(s)):t.appendChild(s)}),t}function q(v,e){let i=null,t=null;const s=(...a)=>{t=a,i&&clearTimeout(i),i=setTimeout(()=>{t&&v(...t),i=null,t=null},e)};return s.flush=()=>{i&&(clearTimeout(i),i=null),t&&(v(...t),t=null)},s.cancel=()=>{i&&(clearTimeout(i),i=null),t=null},s}class H extends E{constructor(){super(),this.saveEnabled=!0,this.percentLabel=c("span",{class:"zoom-percent"},"100%");const e=c("button",{class:"zoom-btn",title:"Zoom out"},"−");e.addEventListener("click",()=>this.emit("zoom-out",void 0));const i=c("button",{class:"zoom-btn",title:"Fit to view"},"Fit");i.addEventListener("click",()=>this.emit("zoom-fit",void 0));const t=c("button",{class:"zoom-btn",title:"Zoom in"},"+");t.addEventListener("click",()=>this.emit("zoom-in",void 0)),this.viewSelect=document.createElement("select"),this.viewSelect.className="view-select",this.viewSelect.title="Switch view",this.viewSelect.style.display="none",this.viewSelect.addEventListener("change",()=>{this.emit("view-change",{viewName:this.viewSelect.value})}),this.element=c("div",{class:"zoom-toolbar"}),this.element.appendChild(e),this.element.appendChild(i),this.element.appendChild(t),this.element.appendChild(this.percentLabel),this.element.appendChild(this.viewSelect);const s=c("div",{class:"zoom-toolbar-spacer"});this.element.appendChild(s),this.saveBtn=c("button",{class:"zoom-btn zoom-save-btn",title:"Save customization"},"Save"),this.saveBtn.style.display="none",this.saveBtn.addEventListener("click",()=>this.emit("save",void 0)),this.element.appendChild(this.saveBtn),this.closeBtn=c("button",{class:"zoom-btn zoom-close-btn",title:"Close editor"},"✕ Close"),this.closeBtn.addEventListener("click",()=>this.emit("close",void 0)),this.element.appendChild(this.closeBtn)}setZoom(e){this.percentLabel.textContent=`${Math.round(e*100)}%`}showSaveButton(e){this.saveEnabled&&(this.saveBtn.style.display=e?"":"none")}setCloseButtonVisible(e){this.closeBtn.style.display=e?"":"none"}setSaveButtonEnabled(e){this.saveEnabled=e,e||(this.saveBtn.style.display="none")}setSaveDisabled(e,i){this.saveBtn.disabled=e,i&&(this.saveBtn.textContent=i)}setViews(e){this.viewSelect.innerHTML="";for(const i of e){const t=document.createElement("option");t.value=i.viewName,t.textContent=i.viewName,this.viewSelect.appendChild(t)}this.viewSelect.style.display=e.length>1?"":"none"}setActiveView(e){this.viewSelect.value=e}getElement(){return this.element}}const Z=[{label:"Inter",value:"Inter"},{label:"Arial",value:"Arial"},{label:"Georgia",value:"Georgia"},{label:"Times New Roman",value:"Times New Roman"},{label:"Courier New",value:"Courier New"}],j=[{label:"L",value:"left"},{label:"C",value:"center"},{label:"R",value:"right"}],L=["image/png","image/jpeg","image/webp","image/svg+xml"],Y=15*1024*1024;class A extends E{constructor(e){super(),this.mode="text",this.isSelected=!1,this.textInputEl=null,this.fontSelectEl=null,this.sizeInputEl=null,this.colorInputEl=null,this.alignGroupEl=null,this.area=e;const i=z(e),t=T(e);this.mode=t&&!i?"image":"text",this.element=c("div",{class:"area-card"}),this.element.addEventListener("click",r=>{r.target.tagName==="INPUT"||r.target.tagName==="SELECT"||r.target.tagName==="BUTTON"||this.emit("select",{areaId:e.id})});const s=c("div",{class:"area-card-header"}),a=c("div",{class:"area-card-name-row"}),o=c("span",{class:"area-card-name"},e.name);if(a.appendChild(o),e.required){const r=c("span",{class:"area-card-badge"},"Required");a.appendChild(r)}s.appendChild(a);const n=c("button",{class:"area-card-reset-btn",title:"Reset content"},"Reset");if(n.addEventListener("click",r=>{r.stopPropagation(),this.emit("clear",{areaId:e.id})}),s.appendChild(n),this.element.appendChild(s),i&&t){const r=c("div",{class:"area-card-mode-switcher"}),d=c("button",{class:"mode-btn mode-btn-active"},"Text");d.dataset.mode="text";const l=c("button",{class:"mode-btn"},"Image");l.dataset.mode="image",d.addEventListener("click",h=>{h.stopPropagation(),this.setMode("text"),d.classList.add("mode-btn-active"),l.classList.remove("mode-btn-active"),this.currentContent?.type==="image"&&this.emit("clear",{areaId:e.id})}),l.addEventListener("click",h=>{h.stopPropagation(),this.setMode("image"),l.classList.add("mode-btn-active"),d.classList.remove("mode-btn-active"),this.currentContent?.type==="text"&&this.emit("clear",{areaId:e.id})}),r.appendChild(d),r.appendChild(l),this.element.appendChild(r)}this.contentContainer=c("div",{class:"area-card-content"}),this.element.appendChild(this.contentContainer),this.transformContainer=c("div",{class:"area-card-transforms"}),this.element.appendChild(this.transformContainer),this.renderContent()}setMode(e){this.mode=e,this.renderContent()}setContent(e){const i=this.currentContent?.type;this.currentContent=e,e?.type==="image"&&(this.mode="image"),this.element.querySelectorAll(".mode-btn").forEach(t=>{const s=t;s.classList.toggle("mode-btn-active",s.dataset.mode===this.mode)}),e?.type!==i||e?.type==="image"||e===void 0?this.renderContent():e?.type==="text"&&this.updateTextValues(e),this.renderTransforms()}setSelected(e){this.isSelected=e,this.element.classList.toggle("area-card-selected",e),this.renderTransforms()}updateTextValues(e){this.textInputEl&&this.textInputEl.value!==e.text&&(this.textInputEl.value=e.text),this.fontSelectEl&&this.fontSelectEl.value!==e.font&&(this.fontSelectEl.value=e.font),this.sizeInputEl&&this.sizeInputEl.value!==String(e.size)&&(this.sizeInputEl.value=String(e.size)),this.colorInputEl&&this.colorInputEl.value!==e.color&&(this.colorInputEl.value=e.color),this.alignGroupEl&&this.alignGroupEl.querySelectorAll(".align-btn").forEach(i=>{const t=i,s=t.title==="Left"?"left":t.title==="Center"?"center":"right";t.classList.toggle("align-btn-active",s===e.align)})}renderContent(){this.contentContainer.innerHTML="",this.textInputEl=null,this.fontSelectEl=null,this.sizeInputEl=null,this.colorInputEl=null,this.alignGroupEl=null;const e=z(this.area),i=T(this.area),t=!i||e&&i&&this.mode==="text",s=!e||e&&i&&this.mode==="image";e&&t&&this.renderTextControls(),i&&s&&this.renderImageControls()}renderTextControls(){const e=this.currentContent,i=e?.type==="text"?e:S(this.area),t=c("input",{class:"area-input-text",type:"text",placeholder:this.area.placeholder||"Enter text..."});t.value=i.text,this.area.textOptions?.maxLength&&(t.maxLength=this.area.textOptions.maxLength),t.addEventListener("input",()=>{this.emit("text:change",{areaId:this.area.id,updates:{text:t.value}})}),this.contentContainer.appendChild(t),this.textInputEl=t;const s=c("div",{class:"area-input-row"}),a=c("select",{class:"area-input-font"});for(const l of Z){const h=c("option");h.value=l.value,h.textContent=l.label,l.value===i.font&&(h.selected=!0),a.appendChild(h)}a.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{font:a.value}})}),s.appendChild(a),this.fontSelectEl=a;const o=c("input",{class:"area-input-size",type:"number"});o.value=String(i.size),o.min=String(this.area.textOptions?.minSize||8),o.max=String(this.area.textOptions?.maxSize||200),o.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{size:parseInt(o.value,10)||24}})}),s.appendChild(o),this.sizeInputEl=o,this.contentContainer.appendChild(s);const n=c("div",{class:"area-input-row"}),r=c("div",{class:"area-input-align"});for(const l of j){const h=c("button",{class:`align-btn${i.align===l.value?" align-btn-active":""}`,title:l.label==="L"?"Left":l.label==="C"?"Center":"Right"},l.label);h.addEventListener("click",u=>{u.stopPropagation(),this.emit("text:change",{areaId:this.area.id,updates:{align:l.value}}),r.querySelectorAll(".align-btn").forEach(p=>p.classList.remove("align-btn-active")),h.classList.add("align-btn-active")}),r.appendChild(h)}n.appendChild(r),this.alignGroupEl=r;const d=c("input",{class:"area-input-color",type:"color"});d.value=i.color,d.addEventListener("input",()=>{this.emit("text:change",{areaId:this.area.id,updates:{color:d.value}})}),n.appendChild(d),this.colorInputEl=d,this.contentContainer.appendChild(n)}renderImageControls(){const e=this.currentContent;if(e?.type==="image"){const i=c("div",{class:"area-image-preview"}),t=c("img",{class:"area-image-thumb"});t.src=e.dataUrl,t.alt=e.filename||"Uploaded image",i.appendChild(t);const s=c("div",{class:"area-image-info"});s.appendChild(c("span",{class:"area-image-name"},e.filename||"Image"));const a=c("button",{class:"area-image-remove-btn"},"Remove");a.addEventListener("click",o=>{o.stopPropagation(),this.emit("clear",{areaId:this.area.id})}),s.appendChild(a),i.appendChild(s),this.contentContainer.appendChild(i)}else{const i=c("button",{class:"area-upload-btn"},"Upload Image"),t=c("input",{type:"file"});t.accept=L.join(","),t.style.display="none";const s=c("div",{class:"area-validation-error"});s.style.display="none",t.addEventListener("change",()=>{const a=t.files?.[0];if(!a)return;if(!L.includes(a.type)){const n="Only PNG, JPEG, WebP, and SVG images are accepted";s.textContent=n,s.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:n}),t.value="";return}if(a.size>Y){const n="File must be smaller than 15MB";s.textContent=n,s.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:n}),t.value="";return}s.style.display="none";const o=new FileReader;o.onload=()=>{const n=o.result;this.emit("image:change",{areaId:this.area.id,dataUrl:n,filename:a.name})},o.readAsDataURL(a),t.value=""}),i.addEventListener("click",a=>{a.stopPropagation(),t.click()}),this.contentContainer.appendChild(i),this.contentContainer.appendChild(t),this.contentContainer.appendChild(s)}}renderTransforms(){this.transformContainer.innerHTML="";const e=this.currentContent;if(!this.isSelected||!e)return;const i=e.type==="text"?this.area.textOptions?.allowPositioning:this.area.imageOptions?.allowPositioning,t=e.type==="image"&&this.area.imageOptions?.allowScaling,s=e.type==="image"&&this.area.imageOptions?.allowRotation;if(!i&&!t&&!s)return;const a=c("div",{class:"area-card-divider"});this.transformContainer.appendChild(a);const o=c("div",{class:"transform-header"});o.appendChild(c("span",{class:"transform-title"},"Transform"));const n=c("button",{class:"transform-reset-btn"},"Reset");n.addEventListener("click",d=>{d.stopPropagation(),i&&this.emit("offset:change",{areaId:this.area.id,offset:{x:0,y:0}}),t&&this.emit("scale:change",{areaId:this.area.id,scale:1}),s&&this.emit("rotation:change",{areaId:this.area.id,rotation:0})}),o.appendChild(n),this.transformContainer.appendChild(o);const r=e.offset??{x:0,y:0};if(i){const d=c("div",{class:"area-input-row"}),l=c("label",{class:"transform-label"},"X"),h=c("input",{class:"transform-input",type:"number"});h.value=String(Math.round(r.x)),h.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...r,x:parseInt(h.value,10)||0}})});const u=c("label",{class:"transform-label"},"Y"),p=c("input",{class:"transform-input",type:"number"});p.value=String(Math.round(r.y)),p.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...r,y:parseInt(p.value,10)||0}})}),d.appendChild(l),d.appendChild(h),d.appendChild(u),d.appendChild(p),this.transformContainer.appendChild(d)}if(t&&e.type==="image"){const d=e.scale??1,l=c("div",{class:"area-input-row transform-slider-row"}),h=c("label",{class:"transform-label"},`Scale: ${Math.round(d*100)}%`),u=c("input",{class:"transform-slider",type:"range"});u.min="10",u.max="500",u.step="5",u.value=String(Math.round(d*100)),u.addEventListener("input",()=>{const p=parseInt(u.value,10)/100;h.textContent=`Scale: ${Math.round(p*100)}%`,this.emit("scale:change",{areaId:this.area.id,scale:p})}),l.appendChild(h),l.appendChild(u),this.transformContainer.appendChild(l)}if(s&&e.type==="image"){const d=e.rotation??0,l=c("div",{class:"area-input-row transform-slider-row"}),h=c("label",{class:"transform-label"},`Rotation: ${Math.round(d)}°`),u=c("input",{class:"transform-slider",type:"range"});u.min="0",u.max="360",u.step="1",u.value=String(Math.round(d)),u.addEventListener("input",()=>{const p=parseInt(u.value,10);h.textContent=`Rotation: ${p}°`,this.emit("rotation:change",{areaId:this.area.id,rotation:p})}),l.appendChild(h),l.appendChild(u),this.transformContainer.appendChild(l)}}getElement(){return this.element}}class J extends E{constructor(e){super(),this.cards=new Map,this.isMobile=!1,this.boundEscapeHandler=s=>{s.key==="Escape"&&this.emit("dismiss",void 0)},this.backdrop=c("div",{class:"area-panel-backdrop"}),this.backdrop.addEventListener("click",()=>{this.emit("dismiss",void 0)}),this.element=c("div",{class:"area-panel"});const i=c("div",{class:"area-panel-header"});i.appendChild(c("span",{class:"area-panel-title"},"Customize"));const t=c("button",{class:"area-panel-close-btn",title:"Close"},"×");if(t.addEventListener("click",()=>{this.emit("dismiss",void 0)}),i.appendChild(t),this.element.appendChild(i),this.panelContent=c("div",{class:"area-panel-content"}),e.length===0){const s=c("p",{class:"area-panel-empty"},"No customization areas defined.");this.panelContent.appendChild(s)}else for(const s of e){const a=new A(s);this.cards.set(s.id,a),a.on("text:change",o=>this.emit("text:change",o)),a.on("image:change",o=>this.emit("image:change",o)),a.on("clear",o=>this.emit("clear",o)),a.on("select",o=>this.emit("select",o)),a.on("offset:change",o=>this.emit("offset:change",o)),a.on("scale:change",o=>this.emit("scale:change",o)),a.on("rotation:change",o=>this.emit("rotation:change",o)),a.on("validation:error",o=>this.emit("validation:error",o)),this.panelContent.appendChild(a.getElement())}this.element.appendChild(this.panelContent)}setAreas(e){if(this.cards.clear(),this.panelContent.innerHTML="",e.length===0){const i=c("p",{class:"area-panel-empty"},"No customization areas defined.");this.panelContent.appendChild(i)}else for(const i of e){const t=new A(i);this.cards.set(i.id,t),t.on("text:change",s=>this.emit("text:change",s)),t.on("image:change",s=>this.emit("image:change",s)),t.on("clear",s=>this.emit("clear",s)),t.on("select",s=>this.emit("select",s)),t.on("offset:change",s=>this.emit("offset:change",s)),t.on("scale:change",s=>this.emit("scale:change",s)),t.on("rotation:change",s=>this.emit("rotation:change",s)),t.on("validation:error",s=>this.emit("validation:error",s)),this.panelContent.appendChild(t.getElement())}}setContents(e){for(const[i,t]of this.cards)t.setContent(e.get(i))}setSelectedArea(e){for(const[i,t]of this.cards){const s=i===e;t.setSelected(s),t.getElement().style.display=s?"":"none"}e?(this.element.classList.add("area-panel-visible"),this.isMobile&&this.backdrop.classList.add("area-panel-backdrop-visible"),document.addEventListener("keydown",this.boundEscapeHandler)):(this.element.classList.remove("area-panel-visible"),this.isMobile&&this.backdrop.classList.remove("area-panel-backdrop-visible"),document.removeEventListener("keydown",this.boundEscapeHandler))}setMobile(e){this.isMobile=e,this.element.classList.toggle("area-panel-mobile",e),e||(this.element.classList.remove("area-panel-visible"),this.backdrop.classList.remove("area-panel-backdrop-visible"))}getElement(){return this.element}getBackdrop(){return this.backdrop}}class X{constructor(e=window){this.target=e,this.commands=[],this.isEnabled=!0,this.handleKeyDown=i=>{if(!this.isEnabled)return;const t=i.target;if(!(t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.isContentEditable)){for(const s of this.commands)if(this.matchesCommand(i,s)){i.preventDefault(),s.handler(i);break}}},this.target.addEventListener("keydown",this.handleKeyDown)}register(e){return this.commands.push(e),()=>{const i=this.commands.indexOf(e);i!==-1&&this.commands.splice(i,1)}}enable(){this.isEnabled=!0}disable(){this.isEnabled=!1}isActive(){return this.isEnabled}matchesCommand(e,i){return!(e.key.toLowerCase()!==i.key.toLowerCase()||i.ctrl!==void 0&&i.ctrl!==e.ctrlKey||i.shift!==void 0&&i.shift!==e.shiftKey||i.alt!==void 0&&i.alt!==e.altKey||i.meta!==void 0&&i.meta!==e.metaKey)}destroy(){this.target.removeEventListener("keydown",this.handleKeyDown),this.commands=[]}}function W(){return/Mac|iPod|iPhone|iPad/.test(navigator.platform)}function G(){return W()?"meta":"ctrl"}function K(v,e,i,t){const s=v/i,a=e/t;return Math.min(s,a)}function Q(v,e,i=8,t=10,s=300){const a=K(v,e,i,t),o=a>=s;return{actualDPI:Math.round(a),targetDPI:s,width:v,height:e,passed:o,warning:o?void 0:`Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${s} DPI. This may result in pixelated or blurry prints.`}}const k=':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-btn{min-width:44px;min-height:44px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}.view-select{min-height:44px;font-size:16px}}',_="2.0.0",ee=768;class I extends HTMLElement{constructor(){super(),this.resizeObserver=null,this.isReady=!1,this.currentTemplate=null,this.loadingTemplateId=null,this.isMobileLayout=!1,this.productViews=[],this.activeViewName=null,this.perViewContents=new Map,this.subscriptions=[],this.storageUsage=null,this.storageUsageLastRefresh=0,this.emitChange=q(()=>{this.dispatchEvent(new CustomEvent("change",{detail:{design:this.getDesign()},bubbles:!0,composed:!0}))},300),this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["template-id","product-id","theme","mode","store-id","api-url","show-close-button","show-save-button"]}async connectedCallback(){try{this.hasAttribute("tabindex")||this.setAttribute("tabindex","0"),this.renderLoading(),await this.initialize()}catch(e){this.showError("Failed to initialize editor",e)}}disconnectedCallback(){this.cleanup()}attributeChangedCallback(e,i,t){i!==t&&(e==="template-id"&&this.isReady&&t&&this.loadTemplate(t),e==="product-id"&&this.isReady&&t&&this.loadProduct(t),e==="show-close-button"&&this.zoomToolbar&&this.zoomToolbar.setCloseButtonVisible(t!=="false"),e==="show-save-button"&&this.zoomToolbar&&this.zoomToolbar.setSaveButtonEnabled(t!=="false"))}getDesign(){if(!this.currentTemplate)throw new Error("No design loaded");return{templateId:this.currentTemplate.metadata.id,engineVersion:_,contents:this.contentManager.toJSON(),userData:{}}}setDesign(e){if(e.templateId!==this.currentTemplate?.metadata.id)throw new Error("Design template ID does not match current template");this.contentManager.fromJSON(e.contents),this.syncEngineContents(),this.stateManager.setState({contents:this.contentManager.toJSON(),isDirty:!0})}undo(){this.stateManager.undo()&&this.syncFromState()}redo(){this.stateManager.redo()&&this.syncFromState()}clear(){this.contentManager.reset(),this.syncEngineContents(),this.stateManager.setState({contents:this.contentManager.toJSON(),selectedAreaId:null,isDirty:!1}),this.zoomToolbar?.showSaveButton(!1),this.engine.setSelectedArea(null),this.inputPanel.setSelectedArea(null),this.engine.fitToView(),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()})}selectArea(e){this.handleAreaSelect(e)}getSelectedAreaId(){return this.stateManager.getState().selectedAreaId}getActiveView(){return this.activeViewName}getViews(){return this.productViews.map(e=>({viewName:e.viewName,viewOrder:e.viewOrder,isRequired:e.isRequired,isDefault:e.isDefault,templateId:e.template.id}))}setActiveView(e){if(this.activeViewName===e)return;const i=this.productViews.find(t=>t.viewName===e);if(!i)throw new Error(`View not found: ${e}`);this.switchToView(i)}setAreaContent(e,i){i.type==="text"?this.contentManager.setTextContent(e,i):this.contentManager.setImageContent(e,i.dataUrl,i.filename),this.syncEngineContents(),this.saveContentState()}getAreaContent(e){return this.contentManager.getContent(e)}static hasEditorContent(e){return e.type==="text"?e.text.trim().length>0:e.type==="image"?!!e.dataUrl:!1}validateRequiredContent(){const e=[];if(this.productViews.length>1){this.activeViewName&&this.contentManager&&this.perViewContents.set(this.activeViewName,this.contentManager.toJSON());for(const i of this.productViews){const t=this.perViewContents.get(i.viewName),s=t?.some(([,o])=>I.hasEditorContent(o))??!1;if(i.isRequired&&!s){e.push(`View "${i.viewName}" is required`);continue}const a=i.template.templateJson.areas||[];for(const o of a){if(!o.required)continue;const n=t?.find(([r])=>r===o.id);n&&I.hasEditorContent(n[1])||e.push(`"${o.name}" in view "${i.viewName}" is required`)}}}else{const i=this.currentTemplate?.areas||[],t=this.contentManager.getContents();for(const s of i){if(!s.required)continue;const a=t.get(s.id);a&&I.hasEditorContent(a)||e.push(`"${s.name}" is required`)}}return e}showValidationErrors(e){return new Promise(i=>{const t=c("div",{class:"editor-modal-overlay"}),s=c("div",{class:"editor-modal"});s.appendChild(c("div",{class:"editor-modal-title"},"Required content missing"));const a=c("div",{class:"editor-modal-body"}),o=document.createElement("ul");o.style.margin="8px 0",o.style.paddingLeft="20px";for(const d of e){const l=document.createElement("li");l.textContent=d,o.appendChild(l)}a.appendChild(o),s.appendChild(a);const n=c("div",{class:"editor-modal-actions"}),r=c("button",{class:"editor-modal-btn editor-modal-btn-primary"},"OK");r.addEventListener("click",()=>{t.remove(),i()}),n.appendChild(r),s.appendChild(n),t.appendChild(s),t.addEventListener("click",d=>{d.target===t&&(t.remove(),i())}),this.shadow.appendChild(t)})}async finalize(){const e=this.validateRequiredContent();if(e.length>0)return await this.showValidationErrors(e),null;const i=this.engine.checkSafeAreaViolations();if(i.length>0){const o=this.currentTemplate?.areas||[],n=i.map(r=>o.find(d=>d.id===r)?.name||r).join(", ");if(!await this.showConfirmation("Content outside safe area",`Content in ${n} extends beyond the safe area. It may be trimmed during printing. Continue?`,"Cancel","Proceed Anyway"))throw new Error("Finalization cancelled by user")}const t=this.getAttribute("store-id")||"demo-shop";if(this.productViews.length>1){this.activeViewName&&this.contentManager&&this.perViewContents.set(this.activeViewName,this.contentManager.toJSON());const o=[];for(const h of this.productViews){const u=this.perViewContents.get(h.viewName);!u||u.length===0||!u.some(([,p])=>I.hasEditorContent(p))||o.push({templateId:h.template.id,viewName:h.viewName,contents:u})}if(o.length===0)throw new Error("No views have content to finalize");const n=this.getAttribute("product-id");if(!n)throw new Error("product-id attribute is required for multi-view finalize");const r=await this.apiClient.finalizeMultiView({productId:n,designs:o});if(!r.allCompleted){const h=r.views.map(g=>g.designId),u=1500,p=40;let m=0;for(;m<p;){await new Promise(f=>setTimeout(f,u)),m++;const g=await this.apiClient.pollMultiViewStatus(h);if(g.allCompleted){for(const f of g.designs){const b=r.views.find(w=>w.designId===f.designId);b&&(b.status=f.status,b.proofUrl=f.proofUrl,b.errorMessage=f.errorMessage)}r.allCompleted=!0;break}}}const d=r.views.map(h=>({viewName:h.viewName,designId:h.designId,proofUrl:h.proofUrl||"",status:h.status})),l=d[0];return this.dispatchEvent(new CustomEvent("customizer:finalize",{detail:{designId:l.designId,proofUrl:l.proofUrl,status:l.status,views:d,requestId:r.requestId},bubbles:!0,composed:!0})),{...l,views:d}}const s=this.getDesign(),a=await this.apiClient.finalizeDesign(t,s);return this.dispatchEvent(new CustomEvent("customizer:finalize",{detail:{designId:a.designId,proofUrl:a.proofUrl,templateId:s.templateId,designJson:s,status:a.status},bubbles:!0,composed:!0})),a}renderLoading(){const e=document.createElement("style");e.textContent=k,this.shadow.appendChild(e);const i=c("div",{class:"loading-container"},c("div",{class:"loading-spinner"}));this.shadow.appendChild(i)}async initialize(){const e=this.getAttribute("api-url")||"http://localhost:4000";this.apiClient=new B(`${e.replace(/\/+$/,"")}/public`);const i=this.getAttribute("product-id"),t=this.getAttribute("template-id");if(!i&&!t)throw new Error("Either template-id or product-id attribute is required");i?await this.loadProduct(i):await this.loadTemplate(t),this.isReady=!0,this.dispatchEvent(new CustomEvent("ready",{bubbles:!0,composed:!0}))}async loadTemplate(e){if(this.loadingTemplateId!==e){this.loadingTemplateId=e;try{const i=await this.apiClient.getTemplate(e);if(this.loadingTemplateId!==e)return;await this.loadTemplateData(i),this.loadingTemplateId=null}catch(i){throw this.loadingTemplateId=null,new Error(`Failed to load template: ${i.message}`)}}}async loadTemplateData(e){this.currentTemplate=e;const i=e.areas||[];this.contentManager=new P(i);const t={template:e,contents:this.contentManager.toJSON(),selectedAreaId:null,zoom:1,pan:{x:0,y:0},isDirty:!1,warnings:[]};this.stateManager=new O(t),this.buildUI(e,i),this.engine=new U(this.canvas),this.engine.setArtboard({width:e.artboard.width,height:e.artboard.height,backgroundColor:e.artboard.backgroundColor??"#ffffff"}),this.engine.setAreas(i),this.engine.setContents(this.contentManager.getContents()),e.backgroundImage&&this.engine.setBackgroundImage(e.backgroundImage);const s=e.artboard.safeArea??e.safeArea;s&&this.engine.setSafeArea(s),this.engine.start(),this.engine.fitToView(),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom()),this.interaction=new F(this.canvas,this.engine,()=>this.contentManager.getContents(),()=>this.stateManager.getState().selectedAreaId),this.wireInteractionEvents(),this.subscriptions.push(this.contentManager.subscribe(()=>{this.syncEngineContents(),this.inputPanel.setContents(this.contentManager.getContents())})),this.keyboard=new X(this),this.setupKeyboardShortcuts(),this.inputPanel.setContents(this.contentManager.getContents());const a=this.getAttribute("store-id");a&&this.apiClient.getStorageUsage(a).then(o=>{this.storageUsage=o,o.usagePercent>=100?this.showToast("Storage quota exceeded. Upgrade your plan for more storage.","error"):o.usagePercent>=80&&this.showToast(`Storage ${o.usagePercent}% full. Consider upgrading your plan.`,"warning")}).catch(()=>{})}async loadProduct(e){try{const i=await this.apiClient.getProduct(e);if(i.views.length===0)throw new Error("Product has no active templates");this.productViews=i.views,this.perViewContents.clear();const t=i.views.find(s=>s.isDefault)||i.views[0];await this.loadTemplateData(t.template.templateJson),this.activeViewName=t.viewName,this.zoomToolbar&&i.views.length>1&&(this.zoomToolbar.setViews(this.getViews()),this.zoomToolbar.setActiveView(t.viewName))}catch(i){throw new Error(`Failed to load product: ${i.message}`)}}switchToView(e){this.activeViewName&&this.contentManager&&this.perViewContents.set(this.activeViewName,this.contentManager.toJSON()),this.activeViewName=e.viewName,this.zoomToolbar?.setActiveView(e.viewName);const i=e.template.templateJson;this.currentTemplate=i;const t=i.areas||[];this.contentManager=new P(t);const s=this.perViewContents.get(e.viewName);s&&this.contentManager.fromJSON(s),this.engine.setArtboard({width:i.artboard.width,height:i.artboard.height,backgroundColor:i.artboard.backgroundColor??"#ffffff"}),this.engine.setAreas(t),this.engine.setContents(this.contentManager.getContents()),this.engine.setBackgroundImage(i.backgroundImage??void 0);const a=i.artboard.safeArea??i.safeArea;this.engine.setSafeArea(a??null),this.engine.fitToView(),this.stateManager.setState({template:i,contents:this.contentManager.toJSON(),selectedAreaId:null,isDirty:this.stateManager.getState().isDirty,zoom:this.engine.getZoom(),pan:this.engine.getPan(),warnings:[]}),this.zoomToolbar.setZoom(this.engine.getZoom()),this.inputPanel.setAreas(t),this.inputPanel.setContents(this.contentManager.getContents()),this.inputPanel.setSelectedArea(null),this.engine.setSelectedArea(null),this.dispatchEvent(new CustomEvent("view-change",{detail:{viewName:e.viewName},bubbles:!0,composed:!0}))}buildUI(e,i){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=k,this.shadow.appendChild(t),this.container=c("div",{class:"editor-container"}),this.zoomToolbar=new H,this.subscriptions.push(this.zoomToolbar.on("zoom-in",()=>this.handleZoomIn())),this.subscriptions.push(this.zoomToolbar.on("zoom-out",()=>this.handleZoomOut())),this.subscriptions.push(this.zoomToolbar.on("zoom-fit",()=>this.handleZoomFit())),this.subscriptions.push(this.zoomToolbar.on("view-change",({viewName:o})=>{const n=this.productViews.find(r=>r.viewName===o);n&&this.switchToView(n)})),this.subscriptions.push(this.zoomToolbar.on("close",()=>{this.dispatchEvent(new CustomEvent("customizer:close",{bubbles:!0,composed:!0}))})),this.subscriptions.push(this.zoomToolbar.on("save",async()=>{this.zoomToolbar.setSaveDisabled(!0,"Saving...");try{const o=await this.finalize();if(!o){this.zoomToolbar.setSaveDisabled(!1,"Save");return}this.zoomToolbar.setSaveDisabled(!0,"Saved!"),this.showToast("Design saved successfully!","info"),this.dispatchEvent(new CustomEvent("customizer:save",{detail:o,bubbles:!0,composed:!0})),setTimeout(()=>{this.zoomToolbar.setSaveDisabled(!1,"Save"),this.zoomToolbar.showSaveButton(!1),this.stateManager.update({isDirty:!1})},2e3)}catch(o){console.error("Customizer: finalize failed",o);const n=o instanceof Error?o.message:"Save failed. Please try again.";this.showToast(n,"error"),this.zoomToolbar.setSaveDisabled(!1,"Save")}})),this.getAttribute("show-close-button")==="false"&&this.zoomToolbar.setCloseButtonVisible(!1),this.getAttribute("show-save-button")==="false"&&this.zoomToolbar.setSaveButtonEnabled(!1);const s=c("div",{class:"canvas-area"});s.appendChild(this.zoomToolbar.getElement()),this.canvasWrapper=c("div",{class:"canvas-wrapper"}),this.canvas=document.createElement("canvas"),this.canvas.className="editor-canvas",this.canvasWrapper.appendChild(this.canvas),s.appendChild(this.canvasWrapper),this.inputPanel=new J(i),this.wireInputPanelEvents();const a=c("div",{class:"editor-main"});a.appendChild(s),a.appendChild(this.inputPanel.getBackdrop()),a.appendChild(this.inputPanel.getElement()),this.container.appendChild(a),this.shadow.appendChild(this.container),this.resizeObserver=new ResizeObserver(o=>{for(const n of o)n.target===this.canvasWrapper&&this.handleCanvasResize(n.contentRect),n.target===this.container&&this.handleLayoutResize(n.contentRect)}),this.resizeObserver.observe(this.canvasWrapper),this.resizeObserver.observe(this.container)}wireInteractionEvents(){this.subscriptions.push(this.interaction.on("area:select",({areaId:e})=>{this.handleAreaSelect(e)})),this.subscriptions.push(this.interaction.on("content:drag",({areaId:e,offset:i})=>{this.contentManager.setContentOffset(e,i),this.saveContentState()})),this.subscriptions.push(this.interaction.on("content:scale",({areaId:e,scale:i})=>{this.contentManager.setImageScale(e,i),this.saveContentState()})),this.subscriptions.push(this.interaction.on("content:rotate",({areaId:e,rotation:i})=>{this.contentManager.setImageRotation(e,i),this.saveContentState()})),this.subscriptions.push(this.interaction.on("zoom",({zoom:e})=>{this.engine.setZoom(e);const i=this.engine.getCenteredPan(e);i&&this.engine.setPan(i),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())})),this.subscriptions.push(this.interaction.on("pan",({pan:e})=>{this.engine.setPan(e),this.stateManager.update({pan:e})})),this.subscriptions.push(this.interaction.on("cursor",({cursor:e})=>{this.canvas.style.cursor=e})),this.subscriptions.push(this.interaction.on("context-menu",({areaId:e,clientX:i,clientY:t})=>{this.handleContextMenu(e,i,t)}))}wireInputPanelEvents(){this.subscriptions.push(this.inputPanel.on("text:change",({areaId:e,updates:i})=>{this.contentManager.setTextContent(e,i),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("image:change",async({areaId:e,dataUrl:i,filename:t})=>{const s=this.contentManager.getContents();let a=0;for(const[n,r]of s)r.type==="image"&&n!==e&&a++;if(a>=10){this.showToast("Maximum of 10 images per design reached","error");return}if(await this.refreshStorageUsage(),this.storageUsage&&this.storageUsage.usagePercent>=100){this.showToast("Storage quota exceeded. Upgrade your plan for more storage.","error");return}const o=this.currentTemplate?.areas.find(n=>n.id===e);if(o&&this.currentTemplate){const n=this.currentTemplate.print.targetDpi;try{const r=new Image;r.crossOrigin="anonymous";const d=await new Promise((p,m)=>{r.onload=()=>p({width:r.naturalWidth,height:r.naturalHeight}),r.onerror=()=>m(new Error("Failed to load image")),r.src=i}),l=o.location.width/n,h=o.location.height/n,u=Q(d.width,d.height,l,h,n);if(u.actualDPI<150){if(!await this.showConfirmation("Low resolution detected",`This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${n} DPI.`,"Cancel","Use Anyway"))return}else u.actualDPI<n&&(this.showToast(`Image resolution is ${u.actualDPI} DPI (${n} recommended)`,"warning"),this.stateManager.update({warnings:[...this.stateManager.getState().warnings,{type:"dpi",message:`${u.actualDPI} DPI`,areaId:e}]}))}catch{}}if(this.contentManager.setImageContent(e,i,t),this.saveContentState(),this.storageUsage){const n=Math.round(i.length*.75);this.storageUsage.storageUsed+=n,this.storageUsage.usagePercent=this.storageUsage.storageQuota>0?Math.round(this.storageUsage.storageUsed/this.storageUsage.storageQuota*100):0}})),this.subscriptions.push(this.inputPanel.on("validation:error",({message:e})=>{this.showToast(e,"error")})),this.subscriptions.push(this.inputPanel.on("clear",({areaId:e})=>{this.contentManager.clearContent(e),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("select",({areaId:e})=>{this.handleAreaSelect(e)})),this.subscriptions.push(this.inputPanel.on("offset:change",({areaId:e,offset:i})=>{this.contentManager.setContentOffset(e,i),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("scale:change",({areaId:e,scale:i})=>{this.contentManager.setImageScale(e,i),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("rotation:change",({areaId:e,rotation:i})=>{this.contentManager.setImageRotation(e,i),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("dismiss",()=>{this.handleAreaSelect(null)}))}handleAreaSelect(e,i=!1){this.stateManager.update({selectedAreaId:e}),this.engine.setSelectedArea(e),this.inputPanel.setSelectedArea(i?e:null),e&&document.activeElement!==this&&this.focus(),this.dispatchEvent(new CustomEvent("area:select",{detail:{areaId:e},bubbles:!0,composed:!0}))}handleContextMenu(e,i,t){this.handleAreaSelect(e,!!e)}handleZoomIn(){const e=this.engine.getZoom(),i=Math.min(e*1.25,5),t=this.engine.getCenteredPan(i);this.engine.setZoom(i),t&&this.engine.setPan(t),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())}handleZoomOut(){const e=this.engine.getZoom(),i=Math.max(e*.8,.1),t=this.engine.getCenteredPan(i);this.engine.setZoom(i),t&&this.engine.setPan(t),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())}handleZoomFit(){this.engine.fitToView(),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())}handleCanvasResize(e){const{width:i,height:t}=e;if(i===0||t===0)return;const s=window.devicePixelRatio||1;this.canvas.width=i*s,this.canvas.height=t*s,this.canvas.style.width=`${i}px`,this.canvas.style.height=`${t}px`,this.engine&&(this.engine.fitToView(),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom()))}handleLayoutResize(e){const i=e.width<ee;this.container.classList.toggle("layout-desktop",!i),this.container.classList.toggle("layout-mobile",i),i!==this.isMobileLayout&&(this.isMobileLayout=i,this.inputPanel.setMobile(i))}async refreshStorageUsage(){if(Date.now()-this.storageUsageLastRefresh<3e4)return;const e=this.getAttribute("store-id");if(e)try{this.storageUsage=await this.apiClient.getStorageUsage(e),this.storageUsageLastRefresh=Date.now()}catch{}}saveContentState(){this.stateManager.setState({contents:this.contentManager.toJSON(),isDirty:!0}),this.zoomToolbar?.showSaveButton(!0),this.engine.checkSafeAreaViolations(),this.emitChange()}syncEngineContents(){this.engine?.setContents(this.contentManager.getContents())}syncFromState(){const e=this.stateManager.getState();this.contentManager.setContentsQuiet(e.contents),this.syncEngineContents(),this.inputPanel.setContents(this.contentManager.getContents()),this.engine.setSelectedArea(e.selectedAreaId),this.inputPanel.setSelectedArea(e.selectedAreaId)}setupKeyboardShortcuts(){const e=G();this.keyboard.register({key:"z",[e]:!0,handler:()=>this.undo()}),this.keyboard.register({key:"z",[e]:!0,shift:!0,handler:()=>this.redo()}),this.keyboard.register({key:"Escape",handler:()=>this.handleAreaSelect(null)})}showToast(e,i="info"){const t=c("div",{class:`editor-toast editor-toast-${i}`},e);this.shadow.appendChild(t),setTimeout(()=>t.remove(),4e3)}showConfirmation(e,i,t="Cancel",s="Continue"){return new Promise(a=>{const o=c("div",{class:"editor-modal-overlay"}),n=c("div",{class:"editor-modal"});n.appendChild(c("div",{class:"editor-modal-title"},e)),n.appendChild(c("div",{class:"editor-modal-body"},i));const r=c("div",{class:"editor-modal-actions"}),d=c("button",{class:"editor-modal-btn"},t);d.addEventListener("click",()=>{o.remove(),a(!1)});const l=c("button",{class:"editor-modal-btn editor-modal-btn-primary"},s);l.addEventListener("click",()=>{o.remove(),a(!0)}),r.appendChild(d),r.appendChild(l),n.appendChild(r),o.appendChild(n),o.addEventListener("click",h=>{h.target===o&&(o.remove(),a(!1))}),this.shadow.appendChild(o)})}showError(e,i){this.shadow.innerHTML="";const t=document.createElement("style");t.textContent=k,this.shadow.appendChild(t);const s=c("div",{class:"error-container"});s.appendChild(c("div",{class:"error-title"},e)),s.appendChild(c("div",{class:"error-message"},i?.message??"")),this.shadow.appendChild(s),this.dispatchEvent(new CustomEvent("error",{detail:{message:e,error:i},bubbles:!0,composed:!0}))}cleanup(){for(const e of this.subscriptions)e();this.subscriptions=[],this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.engine?.destroy(),this.interaction?.destroy(),this.keyboard?.destroy(),this.isReady=!1,this.currentTemplate=null,this.productViews=[],this.activeViewName=null,this.perViewContents.clear()}}customElements.get("customizer-editor")||customElements.define("customizer-editor",I);function te(v,e){const i=typeof v=="string"?document.querySelector(v):v;if(!i)throw new Error(`Container not found: ${typeof v=="string"?v:"provided element is null"}`);if(!e.templateId&&!e.productId)throw new Error("Either templateId or productId must be provided");if(e.templateId&&e.productId)throw new Error("Only one of templateId or productId should be provided, not both");const t=document.createElement("customizer-editor");e.productId?t.setAttribute("product-id",e.productId):t.setAttribute("template-id",e.templateId),e.theme&&t.setAttribute("theme",e.theme),e.mode&&t.setAttribute("mode",e.mode),e.className&&t.classList.add(e.className),e.showCloseButton===!1&&t.setAttribute("show-close-button","false"),e.showSaveButton===!1&&t.setAttribute("show-save-button","false"),t.style.width="100%",t.style.height="100%";const s=[],a=(n,r)=>{t.addEventListener(n,r),s.push({event:n,handler:r})};i.innerHTML="",i.appendChild(t);const o={getDesign(){if(typeof t.getDesign!="function")throw new Error("Editor not ready: getDesign method not available");return t.getDesign()},setDesign(n){if(typeof t.setDesign!="function")throw new Error("Editor not ready: setDesign method not available");t.setDesign(n)},undo(){if(typeof t.undo!="function")throw new Error("Editor not ready: undo method not available");t.undo()},redo(){if(typeof t.redo!="function")throw new Error("Editor not ready: redo method not available");t.redo()},canUndo(){return typeof t.canUndo!="function"?!1:t.canUndo()},canRedo(){return typeof t.canRedo!="function"?!1:t.canRedo()},async finalize(){const n=e.apiUrl||"https://api.varianta.io";try{if(e.productId&&typeof t.finalize=="function"){const g=await t.finalize(),f={designId:g.designId,status:g.status,proofUrl:g.proofUrl??null,errorMessage:null,requestId:g.requestId,views:g.views};return e.onFinalize&&e.onFinalize(f),f}const r=this.getDesign(),d=await fetch(`${n}/public/finalize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({templateId:r.templateId||e.templateId,designJson:r})});if(!d.ok){const g=await d.json().catch(()=>({message:"Finalization failed"}));throw new Error(g.message||"Finalization failed")}const l=await d.json();let h={designId:l.designId,status:l.status,proofUrl:l.proofUrl??null,errorMessage:l.errorMessage??null};const u=1500,p=40;let m=0;for(;h.status==="processing"&&m<p;){await new Promise(b=>setTimeout(b,u)),m++;const g=await fetch(`${n}/public/designs/${h.designId}/status`);if(!g.ok)throw new Error("Failed to check design status");const f=await g.json();h={designId:f.designId,status:f.status,proofUrl:f.proofUrl??null,errorMessage:f.errorMessage??null}}return h.status==="processing"&&(h={...h,status:"failed",errorMessage:"Render timed out"}),e.onFinalize&&e.onFinalize(h),h}catch(r){const d={code:"FINALIZE_ERROR",message:r instanceof Error?r.message:"Unknown error",details:r};throw e.onError&&e.onError(d),r}},addTextLayer(n){if(typeof t.addTextLayer!="function")throw new Error("Editor not ready: addTextLayer method not available");t.addTextLayer(n)},async addImageLayer(n){if(typeof t.addImageLayer!="function")throw new Error("Editor not ready: addImageLayer method not available");return t.addImageLayer(n)},removeLayer(n){if(typeof t.removeLayer!="function")throw new Error("Editor not ready: removeLayer method not available");t.removeLayer(n)},selectLayer(n){if(typeof t.selectLayer!="function")throw new Error("Editor not ready: selectLayer method not available");t.selectLayer(n)},getSelectedLayerId(){return typeof t.getSelectedLayerId!="function"?null:t.getSelectedLayerId()},getActiveView(){return typeof t.getActiveView!="function"?null:t.getActiveView()},getViews(){return typeof t.getViews!="function"?[]:t.getViews()},setActiveView(n){if(typeof t.setActiveView!="function")throw new Error("Editor not ready: setActiveView method not available");t.setActiveView(n)},setTheme(n){t.setAttribute("theme",n)},setMode(n){t.setAttribute("mode",n)},destroy(){s.forEach(({event:n,handler:r})=>{t.removeEventListener(n,r)}),t.parentNode&&t.parentNode.removeChild(t)},getElement(){return t}};return e.onReady&&a("ready",(()=>{e.onReady?.(o)})),e.onChange&&a("change",(n=>{const r=n.detail.design;e.onChange?.(r)})),e.onLayerSelect&&a("layer:select",(n=>{e.onLayerSelect?.(n.detail.layerId)})),e.onLayerAdd&&a("layer:add",(n=>{e.onLayerAdd?.(n.detail.layerId)})),e.onLayerRemove&&a("layer:remove",(n=>{e.onLayerRemove?.(n.detail.layerId)})),e.onLayerUpdate&&a("layer:update",(n=>{e.onLayerUpdate?.(n.detail.layerId)})),e.onError&&a("error",(n=>{const r=n.detail.error;e.onError?.(r)})),e.onViewChange&&a("view-change",(n=>{e.onViewChange?.(n.detail.viewName)})),e.onClose&&a("customizer:close",(()=>{e.onClose?.()})),e.onSave&&a("customizer:save",(n=>{e.onSave?.(n.detail)})),e.initialDesign&&t.addEventListener("ready",()=>{e.initialDesign&&t.setDesign(e.initialDesign)},{once:!0}),e.debug&&(console.log("[Customizer SDK] Initialized with options:",e),console.log("[Customizer SDK] Instance:",o)),o}exports.initCustomizer=te;
|
|
11
|
+
`;if(s.status===400){const o=await s.json().catch(()=>({}));n+=o.error||"Invalid file."}else s.status===413?n+="File too large (max 15MB).":s.status>=500&&(n+="Server error.");throw new Error(n)}return s.json()}catch(i){throw i instanceof Error&&i.message.includes("Failed to fetch")?new Error("Network error uploading asset"):i}}async getStorageUsage(e){try{const t=await fetch(`${this.baseUrl}/assets/usage?shopId=${encodeURIComponent(e)}`);if(!t.ok)throw new Error(`Failed to fetch storage usage (${t.status})`);return t.json()}catch(t){throw t instanceof Error&&t.message.includes("Failed to fetch")?new Error("Network error fetching storage usage"):t}}}function h(f,e,...t){const i=document.createElement(f);return e&&Object.entries(e).forEach(([s,n])=>{s==="class"?i.className=n:s in i?i[s]=n:i.setAttribute(s,String(n))}),t.forEach(s=>{typeof s=="string"?i.appendChild(document.createTextNode(s)):i.appendChild(s)}),i}function q(f,e){let t=null,i=null;const s=(...n)=>{i=n,t&&clearTimeout(t),t=setTimeout(()=>{i&&f(...i),t=null,i=null},e)};return s.flush=()=>{t&&(clearTimeout(t),t=null),i&&(f(...i),i=null)},s.cancel=()=>{t&&(clearTimeout(t),t=null),i=null},s}class H extends S{constructor(){super(),this.saveEnabled=!0,this.percentLabel=h("span",{class:"zoom-percent"},"100%");const e=h("button",{class:"zoom-btn",title:"Zoom out"},"−");e.addEventListener("click",()=>this.emit("zoom-out",void 0));const t=h("button",{class:"zoom-btn",title:"Fit to view"},"Fit");t.addEventListener("click",()=>this.emit("zoom-fit",void 0));const i=h("button",{class:"zoom-btn",title:"Zoom in"},"+");i.addEventListener("click",()=>this.emit("zoom-in",void 0)),this.viewSelect=document.createElement("select"),this.viewSelect.className="view-select",this.viewSelect.title="Switch view",this.viewSelect.style.display="none",this.viewSelect.addEventListener("change",()=>{this.emit("view-change",{viewName:this.viewSelect.value})}),this.element=h("div",{class:"zoom-toolbar"}),this.element.appendChild(e),this.element.appendChild(t),this.element.appendChild(i),this.element.appendChild(this.percentLabel),this.element.appendChild(this.viewSelect);const s=h("div",{class:"zoom-toolbar-spacer"});this.element.appendChild(s),this.saveBtn=h("button",{class:"zoom-btn zoom-save-btn",title:"Save customization"},"✓"),this.saveBtn.style.display="none",this.saveBtn.addEventListener("click",()=>this.emit("save",void 0)),this.element.appendChild(this.saveBtn),this.closeBtn=h("button",{class:"zoom-btn zoom-close-btn",title:"Close editor"},"✕"),this.closeBtn.addEventListener("click",()=>this.emit("close",void 0)),this.element.appendChild(this.closeBtn)}setZoom(e){this.percentLabel.textContent=`${Math.round(e*100)}%`}showSaveButton(e){this.saveEnabled&&(this.saveBtn.style.display=e?"":"none")}setCloseButtonVisible(e){this.closeBtn.style.display=e?"":"none"}setSaveButtonEnabled(e){this.saveEnabled=e,e||(this.saveBtn.style.display="none")}setSaveDisabled(e,t){this.saveBtn.disabled=e,t&&(this.saveBtn.textContent=t)}setViews(e){this.viewSelect.innerHTML="";for(const t of e){const i=document.createElement("option");i.value=t.viewName,i.textContent=t.viewName,this.viewSelect.appendChild(i)}this.viewSelect.style.display=e.length>1?"":"none"}setActiveView(e){this.viewSelect.value=e}getElement(){return this.element}}const Z=[{label:"Inter",value:"Inter"},{label:"Arial",value:"Arial"},{label:"Georgia",value:"Georgia"},{label:"Times New Roman",value:"Times New Roman"},{label:"Courier New",value:"Courier New"}],j=[{label:"L",value:"left"},{label:"C",value:"center"},{label:"R",value:"right"}],A=["image/png","image/jpeg","image/webp","image/svg+xml"],Y=15*1024*1024;class L extends S{constructor(e){super(),this.mode="text",this.isSelected=!1,this.isUploading=!1,this.textInputEl=null,this.fontSelectEl=null,this.sizeInputEl=null,this.colorInputEl=null,this.alignGroupEl=null,this.area=e;const t=E(e),i=T(e);this.mode=i&&!t?"image":"text",this.element=h("div",{class:"area-card"}),this.element.addEventListener("click",r=>{r.target.tagName==="INPUT"||r.target.tagName==="SELECT"||r.target.tagName==="BUTTON"||this.emit("select",{areaId:e.id})});const s=h("div",{class:"area-card-header"}),n=h("div",{class:"area-card-name-row"}),o=h("span",{class:"area-card-name"},e.name);if(n.appendChild(o),e.required){const r=h("span",{class:"area-card-badge"},"Required");n.appendChild(r)}s.appendChild(n);const a=h("button",{class:"area-card-reset-btn",title:"Reset content"},"Reset");if(a.addEventListener("click",r=>{r.stopPropagation(),this.emit("clear",{areaId:e.id})}),s.appendChild(a),this.element.appendChild(s),t&&i){const r=h("div",{class:"area-card-mode-switcher"}),d=h("button",{class:"mode-btn mode-btn-active"},"Text");d.dataset.mode="text";const l=h("button",{class:"mode-btn"},"Image");l.dataset.mode="image",d.addEventListener("click",c=>{c.stopPropagation(),this.setMode("text"),d.classList.add("mode-btn-active"),l.classList.remove("mode-btn-active"),this.currentContent?.type==="image"&&this.emit("clear",{areaId:e.id})}),l.addEventListener("click",c=>{c.stopPropagation(),this.setMode("image"),l.classList.add("mode-btn-active"),d.classList.remove("mode-btn-active"),this.currentContent?.type==="text"&&this.emit("clear",{areaId:e.id})}),r.appendChild(d),r.appendChild(l),this.element.appendChild(r)}this.contentContainer=h("div",{class:"area-card-content"}),this.element.appendChild(this.contentContainer),this.transformContainer=h("div",{class:"area-card-transforms"}),this.element.appendChild(this.transformContainer),this.renderContent()}setMode(e){this.mode=e,this.renderContent()}setContent(e){const t=this.currentContent?.type;this.currentContent=e,e?.type==="image"?this.mode="image":t==="image"&&(this.mode=E(this.area)?"text":"image"),this.element.querySelectorAll(".mode-btn").forEach(i=>{const s=i;s.classList.toggle("mode-btn-active",s.dataset.mode===this.mode)}),e?.type!==t||e?.type==="image"||e===void 0?this.renderContent():e?.type==="text"&&this.updateTextValues(e),this.renderTransforms()}setSelected(e){this.isSelected=e,this.element.classList.toggle("area-card-selected",e),this.renderTransforms()}updateTextValues(e){this.textInputEl&&this.textInputEl.value!==e.text&&(this.textInputEl.value=e.text),this.fontSelectEl&&this.fontSelectEl.value!==e.font&&(this.fontSelectEl.value=e.font),this.sizeInputEl&&this.sizeInputEl.value!==String(e.size)&&(this.sizeInputEl.value=String(e.size)),this.colorInputEl&&this.colorInputEl.value!==e.color&&(this.colorInputEl.value=e.color),this.alignGroupEl&&this.alignGroupEl.querySelectorAll(".align-btn").forEach(t=>{const i=t,s=i.title==="Left"?"left":i.title==="Center"?"center":"right";i.classList.toggle("align-btn-active",s===e.align)})}renderContent(){this.contentContainer.innerHTML="",this.textInputEl=null,this.fontSelectEl=null,this.sizeInputEl=null,this.colorInputEl=null,this.alignGroupEl=null;const e=E(this.area),t=T(this.area),i=!t||e&&t&&this.mode==="text",s=!e||e&&t&&this.mode==="image";e&&i&&this.renderTextControls(),t&&s&&this.renderImageControls(),this.applyUploadSpinner()}renderTextControls(){const e=this.currentContent,t=e?.type==="text"?e:z(this.area),i=h("input",{class:"area-input-text",type:"text",placeholder:this.area.placeholder||"Enter text..."});i.value=t.text,this.area.textOptions?.maxLength&&(i.maxLength=this.area.textOptions.maxLength),i.addEventListener("input",()=>{this.emit("text:change",{areaId:this.area.id,updates:{text:i.value}})}),this.contentContainer.appendChild(i),this.textInputEl=i;const s=h("div",{class:"area-input-row"}),n=h("select",{class:"area-input-font"});for(const l of Z){const c=h("option");c.value=l.value,c.textContent=l.label,l.value===t.font&&(c.selected=!0),n.appendChild(c)}n.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{font:n.value}})}),s.appendChild(n),this.fontSelectEl=n;const o=h("input",{class:"area-input-size",type:"number"});o.value=String(t.size),o.min=String(this.area.textOptions?.minSize||8),o.max=String(this.area.textOptions?.maxSize||200),o.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{size:parseInt(o.value,10)||24}})}),s.appendChild(o),this.sizeInputEl=o,this.contentContainer.appendChild(s);const a=h("div",{class:"area-input-row"}),r=h("div",{class:"area-input-align"});for(const l of j){const c=h("button",{class:`align-btn${t.align===l.value?" align-btn-active":""}`,title:l.label==="L"?"Left":l.label==="C"?"Center":"Right"},l.label);c.addEventListener("click",p=>{p.stopPropagation(),this.emit("text:change",{areaId:this.area.id,updates:{align:l.value}}),r.querySelectorAll(".align-btn").forEach(u=>u.classList.remove("align-btn-active")),c.classList.add("align-btn-active")}),r.appendChild(c)}a.appendChild(r),this.alignGroupEl=r;const d=h("input",{class:"area-input-color",type:"color"});d.value=t.color,d.addEventListener("input",()=>{this.emit("text:change",{areaId:this.area.id,updates:{color:d.value}})}),a.appendChild(d),this.colorInputEl=d,this.contentContainer.appendChild(a)}renderImageControls(){const e=this.currentContent;if(e?.type==="image"){const t=h("div",{class:"area-image-preview"}),i=h("img",{class:"area-image-thumb"});i.src=e.dataUrl,i.alt=e.filename||"Uploaded image",t.appendChild(i);const s=h("div",{class:"area-image-info"});s.appendChild(h("span",{class:"area-image-name"},e.filename||"Image"));const n=h("button",{class:"area-image-remove-btn"},"Remove");n.addEventListener("click",o=>{o.stopPropagation(),this.emit("clear",{areaId:this.area.id})}),s.appendChild(n),t.appendChild(s),this.contentContainer.appendChild(t)}else{const t=h("button",{class:"area-upload-btn"},"Upload Image"),i=h("input",{type:"file"});i.accept=A.join(","),i.style.display="none";const s=h("div",{class:"area-validation-error"});s.style.display="none",i.addEventListener("change",()=>{const n=i.files?.[0];if(!n)return;if(!A.includes(n.type)){const a="Only PNG, JPEG, WebP, and SVG images are accepted";s.textContent=a,s.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:a}),i.value="";return}if(n.size>Y){const a="File must be smaller than 15MB";s.textContent=a,s.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:a}),i.value="";return}s.style.display="none";const o=new FileReader;o.onload=()=>{const a=o.result;this.emit("image:change",{areaId:this.area.id,dataUrl:a,filename:n.name})},o.readAsDataURL(n),i.value=""}),t.addEventListener("click",n=>{n.stopPropagation(),i.click()}),this.contentContainer.appendChild(t),this.contentContainer.appendChild(i),this.contentContainer.appendChild(s)}}renderTransforms(){this.transformContainer.innerHTML="";const e=this.currentContent;if(!this.isSelected||!e)return;const t=e.type==="text"?this.area.textOptions?.allowPositioning:this.area.imageOptions?.allowPositioning,i=e.type==="image"&&this.area.imageOptions?.allowScaling,s=e.type==="image"&&this.area.imageOptions?.allowRotation;if(!t&&!i&&!s)return;const n=h("div",{class:"area-card-divider"});this.transformContainer.appendChild(n);const o=h("div",{class:"transform-header"});o.appendChild(h("span",{class:"transform-title"},"Transform"));const a=h("button",{class:"transform-reset-btn"},"Reset");a.addEventListener("click",d=>{d.stopPropagation(),t&&this.emit("offset:change",{areaId:this.area.id,offset:{x:0,y:0}}),i&&this.emit("scale:change",{areaId:this.area.id,scale:1}),s&&this.emit("rotation:change",{areaId:this.area.id,rotation:0})}),o.appendChild(a),this.transformContainer.appendChild(o);const r=e.offset??{x:0,y:0};if(t){const d=h("div",{class:"area-input-row"}),l=h("label",{class:"transform-label"},"X"),c=h("input",{class:"transform-input",type:"number"});c.value=String(Math.round(r.x)),c.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...r,x:parseInt(c.value,10)||0}})});const p=h("label",{class:"transform-label"},"Y"),u=h("input",{class:"transform-input",type:"number"});u.value=String(Math.round(r.y)),u.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...r,y:parseInt(u.value,10)||0}})}),d.appendChild(l),d.appendChild(c),d.appendChild(p),d.appendChild(u),this.transformContainer.appendChild(d)}if(i&&e.type==="image"){const d=e.scale??1,l=h("div",{class:"area-input-row transform-slider-row"}),c=h("label",{class:"transform-label"},`Scale: ${Math.round(d*100)}%`),p=h("input",{class:"transform-slider",type:"range"});p.min="10",p.max="500",p.step="5",p.value=String(Math.round(d*100)),p.addEventListener("input",()=>{const u=parseInt(p.value,10)/100;c.textContent=`Scale: ${Math.round(u*100)}%`,this.emit("scale:change",{areaId:this.area.id,scale:u})}),l.appendChild(c),l.appendChild(p),this.transformContainer.appendChild(l)}if(s&&e.type==="image"){const d=e.rotation??0,l=h("div",{class:"area-input-row transform-slider-row"}),c=h("label",{class:"transform-label"},`Rotation: ${Math.round(d)}°`),p=h("input",{class:"transform-slider",type:"range"});p.min="0",p.max="360",p.step="1",p.value=String(Math.round(d)),p.addEventListener("input",()=>{const u=parseInt(p.value,10);c.textContent=`Rotation: ${u}°`,this.emit("rotation:change",{areaId:this.area.id,rotation:u})}),l.appendChild(c),l.appendChild(p),this.transformContainer.appendChild(l)}}setUploading(e){this.isUploading=e,this.applyUploadSpinner()}applyUploadSpinner(){const e=this.contentContainer.querySelector(".area-image-preview");if(!e)return;const t=e.querySelector(".area-upload-spinner-overlay");if(this.isUploading&&!t){const i=h("div",{class:"area-upload-spinner-overlay"});i.appendChild(h("div",{class:"area-upload-spinner"})),e.appendChild(i)}else!this.isUploading&&t&&t.remove()}getElement(){return this.element}}class J extends S{constructor(e){super(),this.cards=new Map,this.isMobile=!1,this.boundEscapeHandler=s=>{s.key==="Escape"&&this.emit("dismiss",void 0)},this.backdrop=h("div",{class:"area-panel-backdrop"}),this.backdrop.addEventListener("click",()=>{this.emit("dismiss",void 0)}),this.element=h("div",{class:"area-panel"});const t=h("div",{class:"area-panel-header"});t.appendChild(h("span",{class:"area-panel-title"},"Customize"));const i=h("button",{class:"area-panel-close-btn",title:"Close"},"×");if(i.addEventListener("click",()=>{this.emit("dismiss",void 0)}),t.appendChild(i),this.element.appendChild(t),this.panelContent=h("div",{class:"area-panel-content"}),e.length===0){const s=h("p",{class:"area-panel-empty"},"No customization areas defined.");this.panelContent.appendChild(s)}else for(const s of e){const n=new L(s);this.cards.set(s.id,n),n.on("text:change",o=>this.emit("text:change",o)),n.on("image:change",o=>this.emit("image:change",o)),n.on("clear",o=>this.emit("clear",o)),n.on("select",o=>this.emit("select",o)),n.on("offset:change",o=>this.emit("offset:change",o)),n.on("scale:change",o=>this.emit("scale:change",o)),n.on("rotation:change",o=>this.emit("rotation:change",o)),n.on("validation:error",o=>this.emit("validation:error",o)),this.panelContent.appendChild(n.getElement())}this.element.appendChild(this.panelContent)}setAreas(e){if(this.cards.clear(),this.panelContent.innerHTML="",e.length===0){const t=h("p",{class:"area-panel-empty"},"No customization areas defined.");this.panelContent.appendChild(t)}else for(const t of e){const i=new L(t);this.cards.set(t.id,i),i.on("text:change",s=>this.emit("text:change",s)),i.on("image:change",s=>this.emit("image:change",s)),i.on("clear",s=>this.emit("clear",s)),i.on("select",s=>this.emit("select",s)),i.on("offset:change",s=>this.emit("offset:change",s)),i.on("scale:change",s=>this.emit("scale:change",s)),i.on("rotation:change",s=>this.emit("rotation:change",s)),i.on("validation:error",s=>this.emit("validation:error",s)),this.panelContent.appendChild(i.getElement())}}setContents(e){for(const[t,i]of this.cards)i.setContent(e.get(t))}setSelectedArea(e){for(const[t,i]of this.cards){const s=t===e;i.setSelected(s),i.getElement().style.display=s?"":"none"}e?(this.element.classList.add("area-panel-visible"),this.isMobile&&this.backdrop.classList.add("area-panel-backdrop-visible"),document.addEventListener("keydown",this.boundEscapeHandler)):(this.element.classList.remove("area-panel-visible"),this.isMobile&&this.backdrop.classList.remove("area-panel-backdrop-visible"),document.removeEventListener("keydown",this.boundEscapeHandler))}setUploading(e,t){const i=this.cards.get(e);i&&i.setUploading(t)}setMobile(e){this.isMobile=e,this.element.classList.toggle("area-panel-mobile",e),e||(this.element.classList.remove("area-panel-visible"),this.backdrop.classList.remove("area-panel-backdrop-visible"))}getElement(){return this.element}getBackdrop(){return this.backdrop}}class W{constructor(e=window){this.target=e,this.commands=[],this.isEnabled=!0,this.handleKeyDown=t=>{if(!this.isEnabled)return;const i=t.target;if(!(i.tagName==="INPUT"||i.tagName==="TEXTAREA"||i.isContentEditable)){for(const s of this.commands)if(this.matchesCommand(t,s)){t.preventDefault(),s.handler(t);break}}},this.target.addEventListener("keydown",this.handleKeyDown)}register(e){return this.commands.push(e),()=>{const t=this.commands.indexOf(e);t!==-1&&this.commands.splice(t,1)}}enable(){this.isEnabled=!0}disable(){this.isEnabled=!1}isActive(){return this.isEnabled}matchesCommand(e,t){return!(e.key.toLowerCase()!==t.key.toLowerCase()||t.ctrl!==void 0&&t.ctrl!==e.ctrlKey||t.shift!==void 0&&t.shift!==e.shiftKey||t.alt!==void 0&&t.alt!==e.altKey||t.meta!==void 0&&t.meta!==e.metaKey)}destroy(){this.target.removeEventListener("keydown",this.handleKeyDown),this.commands=[]}}function X(){return/Mac|iPod|iPhone|iPad/.test(navigator.platform)}function G(){return X()?"meta":"ctrl"}function K(f,e,t,i){const s=f/t,n=e/i;return Math.min(s,n)}function Q(f,e,t=8,i=10,s=300){const n=K(f,e,t,i),o=n>=s;return{actualDPI:Math.round(n),targetDPI:s,width:f,height:e,passed:o,warning:o?void 0:`Image resolution is ${Math.round(n)} DPI at 100% scale. For best print quality, use images with at least ${s} DPI. This may result in pixelated or blurry prints.`}}const k=':host{--editor-bg: #f3f4f6;--editor-surface: #ffffff;--editor-border: #e5e7eb;--editor-text: #111827;--editor-text-muted: #6b7280;--editor-primary: #3b82f6;--editor-primary-hover: #2563eb;--editor-danger: #ef4444;--editor-radius: 8px;--editor-radius-sm: 4px;--editor-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--editor-panel-width: 280px;display:block;width:100%;height:100%;font-family:var(--editor-font);font-size:13px;color:var(--editor-text);line-height:1.4}:host([theme="dark"]){--editor-bg: #1f2937;--editor-surface: #374151;--editor-border: #4b5563;--editor-text: #f9fafb;--editor-text-muted: #9ca3af}.editor-container{display:flex;flex-direction:column;width:100%;height:100%;position:relative;overflow:hidden;background:var(--editor-bg)}.layout-desktop .editor-main{display:flex;flex:1;min-height:0}.layout-desktop .canvas-area{flex:1;min-width:0;position:relative;display:flex;flex-direction:column}.layout-desktop .area-panel{width:var(--editor-panel-width);border-left:1px solid var(--editor-border);background:var(--editor-surface);display:none;flex-direction:column;overflow:hidden}.layout-desktop .area-panel.area-panel-visible{display:flex}.layout-mobile .editor-main{display:flex;flex-direction:column;flex:1;min-height:0}.layout-mobile .canvas-area{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}.layout-mobile .area-panel{position:absolute;bottom:0;left:0;right:0;max-height:50%;background:var(--editor-surface);border-top:1px solid var(--editor-border);border-radius:var(--editor-radius) var(--editor-radius) 0 0;box-shadow:0 -4px 20px #00000026;transform:translateY(100%);transition:transform .25s ease-out;z-index:20;display:flex;flex-direction:column;overflow:hidden}.layout-mobile .area-panel.area-panel-visible{transform:translateY(0)}.area-panel-backdrop{display:none}.layout-mobile .area-panel-backdrop{display:block;position:absolute;inset:0;background:#0000004d;z-index:15;opacity:0;pointer-events:none;transition:opacity .25s ease-out}.layout-mobile .area-panel-backdrop.area-panel-backdrop-visible{opacity:1;pointer-events:auto}.zoom-toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;background:var(--editor-surface);border-bottom:1px solid var(--editor-border)}.zoom-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:14px;font-family:var(--editor-font);transition:background .15s;-webkit-tap-highlight-color:transparent;touch-action:manipulation}.zoom-btn:hover{background:var(--editor-bg)}.zoom-btn:active{background:var(--editor-border)}.zoom-percent{margin-left:8px;font-size:12px;color:var(--editor-text-muted);min-width:40px}.view-select{margin-left:8px;padding:4px 8px;height:32px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);font-family:var(--editor-font);font-size:12px;cursor:pointer;-webkit-tap-highlight-color:transparent}.view-select:hover{background:var(--editor-bg)}.view-select:focus{outline:2px solid var(--editor-primary);outline-offset:-1px}.zoom-toolbar-spacer{flex:1}.zoom-close-btn{width:auto;padding:0 12px;font-size:13px;gap:4px}.zoom-save-btn{width:auto;padding:0 16px;margin-left:8px;font-size:13px;font-weight:600;background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.zoom-save-btn:hover{background:var(--editor-primary-hover)}.zoom-save-btn:disabled{opacity:.5;cursor:not-allowed}.canvas-wrapper{flex:1;position:relative;overflow:hidden;min-height:200px}.editor-canvas{position:absolute;top:0;left:0}.area-panel-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--editor-border);flex-shrink:0}.area-panel-title{font-weight:600;font-size:14px}.area-panel-close-btn{width:28px;height:28px;border:none;background:none;color:var(--editor-text-muted);cursor:pointer;font-size:18px;line-height:1;border-radius:var(--editor-radius-sm);display:flex;align-items:center;justify-content:center}.area-panel-close-btn:hover{background:var(--editor-bg);color:var(--editor-text)}.area-panel-content{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.area-panel-empty{color:var(--editor-text-muted);font-size:12px;text-align:center;padding:20px 0}.area-card{padding:12px;border:1px solid var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-surface);cursor:pointer;transition:border-color .15s}.area-card:hover{border-color:var(--editor-primary)}.area-card-selected{border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.area-card-name-row{display:flex;align-items:center;gap:6px}.area-card-name{font-weight:600;font-size:13px}.area-card-badge{display:inline-block;padding:1px 6px;font-size:10px;font-weight:500;background:#fef3c7;color:#92400e;border-radius:10px}.area-card-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.area-card-reset-btn:hover{background:var(--editor-bg);color:var(--editor-danger);border-color:var(--editor-danger)}.area-card-mode-switcher{display:flex;gap:4px;margin-bottom:8px}.mode-btn{flex:1;padding:4px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-family:var(--editor-font);transition:all .15s}.mode-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-text{width:100%;padding:6px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:13px;color:var(--editor-text);background:var(--editor-surface);box-sizing:border-box;margin-bottom:6px}.area-input-text:focus{outline:none;border-color:var(--editor-primary);box-shadow:0 0 0 1px var(--editor-primary)}.area-input-row{display:flex;gap:6px;align-items:center;margin-bottom:6px}.area-input-font{flex:1;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface);min-width:0}.area-input-size{width:56px;padding:4px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.area-input-align{display:flex;gap:2px}.align-btn{width:28px;height:28px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:var(--editor-surface);color:var(--editor-text);cursor:pointer;font-size:12px;font-weight:600;font-family:var(--editor-font)}.align-btn-active{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.area-input-color{width:32px;height:28px;padding:0;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);cursor:pointer;background:none}.area-upload-btn{width:100%;padding:10px;border:2px dashed var(--editor-border);border-radius:var(--editor-radius);background:var(--editor-bg);color:var(--editor-text-muted);cursor:pointer;font-family:var(--editor-font);font-size:12px;text-align:center;transition:all .15s;min-height:44px}.area-upload-btn:hover{border-color:var(--editor-primary);color:var(--editor-primary)}.area-image-preview{display:flex;gap:8px;align-items:center}.area-image-thumb{width:48px;height:48px;object-fit:cover;border-radius:var(--editor-radius-sm);border:1px solid var(--editor-border)}.area-image-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.area-image-name{font-size:12px;color:var(--editor-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.area-image-remove-btn{padding:2px 8px;border:1px solid var(--editor-danger);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-danger);cursor:pointer;font-size:11px;font-family:var(--editor-font);width:fit-content}.area-image-remove-btn:hover{background:#fef2f2}.area-card-divider{height:1px;background:var(--editor-border);margin:8px 0}.transform-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}.transform-title{font-weight:600;font-size:12px}.transform-reset-btn{padding:2px 8px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);background:none;color:var(--editor-text-muted);cursor:pointer;font-size:11px;font-family:var(--editor-font)}.transform-reset-btn:hover{background:var(--editor-bg)}.transform-label{font-size:11px;color:var(--editor-text-muted);white-space:nowrap}.transform-input{width:60px;padding:3px 6px;border:1px solid var(--editor-border);border-radius:var(--editor-radius-sm);font-family:var(--editor-font);font-size:12px;color:var(--editor-text);background:var(--editor-surface)}.transform-slider-row{flex-direction:column;align-items:stretch}.transform-slider{width:100%;accent-color:var(--editor-primary)}.area-image-preview{position:relative}.area-upload-spinner-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#fff9;border-radius:var(--editor-radius-sm);z-index:1}:host([theme="dark"]) .area-upload-spinner-overlay{background:#37415199}.area-upload-spinner{width:16px;height:16px;border:2px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}.loading-container{display:flex;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px}.loading-spinner{width:32px;height:32px;border:3px solid var(--editor-border);border-top-color:var(--editor-primary);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error-container{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;min-height:200px;padding:20px;text-align:center}.error-title{font-weight:600;font-size:16px;color:var(--editor-danger);margin-bottom:8px}.error-message{font-size:13px;color:var(--editor-text-muted);max-width:400px;white-space:pre-wrap}:host(:focus){outline:2px solid var(--editor-primary);outline-offset:-2px}input:focus,select:focus,button:focus-visible{outline:2px solid var(--editor-primary);outline-offset:-1px}.context-menu{position:absolute;z-index:100;min-width:160px;background:var(--editor-surface);border:1px solid var(--editor-border);border-radius:var(--editor-radius);box-shadow:0 4px 12px #00000026;padding:4px 0;font-size:13px}.context-menu-header{padding:6px 12px;font-weight:600;font-size:12px;color:var(--editor-text-muted);user-select:none}.context-menu-divider{height:1px;background:var(--editor-border);margin:4px 0}.context-menu-item{padding:6px 12px;cursor:pointer;color:var(--editor-text);user-select:none;transition:background .1s}.context-menu-item:hover{background:var(--editor-bg)}.context-menu-item:active{background:var(--editor-border)}.context-menu-item-disabled{color:var(--editor-text-muted);cursor:default;pointer-events:none}.editor-toast{position:absolute;top:16px;left:50%;transform:translate(-50%);padding:10px 20px;border-radius:var(--editor-radius);font-size:13px;font-family:var(--editor-font);line-height:1.4;z-index:200;max-width:400px;text-align:center;animation:toast-in .25s ease-out,toast-out .25s ease-in forwards;animation-delay:0s,var(--toast-duration, 3.75s);pointer-events:none;box-shadow:0 4px 12px #00000026}.editor-toast-warning{background:#fef3c7;color:#92400e;border:1px solid #f59e0b}.editor-toast-error{background:#fef2f2;color:#991b1b;border:1px solid var(--editor-danger)}.editor-toast-info{background:#eff6ff;color:#1e40af;border:1px solid var(--editor-primary)}@keyframes toast-in{0%{opacity:0;transform:translate(-50%) translateY(-10px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@keyframes toast-out{0%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}.editor-modal-overlay{position:absolute;inset:0;background:#0006;z-index:300;display:flex;align-items:center;justify-content:center;animation:modal-overlay-in .2s ease-out}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}.editor-modal{background:var(--editor-surface);border-radius:var(--editor-radius);box-shadow:0 8px 32px #0003;max-width:380px;width:calc(100% - 32px);padding:20px;animation:modal-in .2s ease-out}@keyframes modal-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.editor-modal-title{font-weight:600;font-size:15px;margin-bottom:8px;color:var(--editor-text)}.editor-modal-body{font-size:13px;color:var(--editor-text-muted);margin-bottom:16px;line-height:1.5}.editor-modal-actions{display:flex;justify-content:flex-end;gap:8px}.editor-modal-btn{padding:8px 16px;border-radius:var(--editor-radius-sm);font-size:13px;font-family:var(--editor-font);cursor:pointer;border:1px solid var(--editor-border);background:var(--editor-surface);color:var(--editor-text);transition:background .15s}.editor-modal-btn:hover{background:var(--editor-bg)}.editor-modal-btn-primary{background:var(--editor-primary);color:#fff;border-color:var(--editor-primary)}.editor-modal-btn-primary:hover{background:var(--editor-primary-hover)}.area-validation-error{font-size:11px;color:var(--editor-danger);margin-top:4px}@media(pointer:coarse){.zoom-toolbar{padding:6px 8px;gap:2px}.zoom-btn{min-width:44px;min-height:44px}.zoom-percent{display:none}.view-select{max-width:110px;text-overflow:ellipsis;overflow:hidden;min-height:44px;font-size:16px}.zoom-close-btn,.zoom-save-btn{padding:0 8px}.area-input-text,.area-input-font,.area-input-size{min-height:44px;font-size:16px}.align-btn{min-width:44px;min-height:44px}.area-upload-btn{min-height:48px}}',_="2.0.0",ee=768;class I extends HTMLElement{constructor(){super(),this.resizeObserver=null,this.pendingUploads=new Map,this.isReady=!1,this.currentTemplate=null,this.loadingTemplateId=null,this.isMobileLayout=!1,this.productViews=[],this.activeViewName=null,this.perViewContents=new Map,this.subscriptions=[],this.renderAbortController=null,this.contentManagerUnsub=null,this.refitRafId=null,this.storageUsage=null,this.storageUsageLastRefresh=0,this.emitChange=q(()=>{this.dispatchEvent(new CustomEvent("change",{detail:{design:this.getDesign()},bubbles:!0,composed:!0}))},300),this.shadow=this.attachShadow({mode:"open"})}static get observedAttributes(){return["template-id","product-id","theme","mode","store-id","api-url","show-close-button","show-save-button"]}async connectedCallback(){try{this.hasAttribute("tabindex")||this.setAttribute("tabindex","0"),this.renderLoading(),await this.initialize()}catch(e){this.showError("Failed to initialize editor",e)}}disconnectedCallback(){this.cleanup()}attributeChangedCallback(e,t,i){t!==i&&(e==="template-id"&&this.isReady&&i&&this.loadTemplate(i),e==="product-id"&&this.isReady&&i&&this.loadProduct(i),e==="show-close-button"&&this.zoomToolbar&&this.zoomToolbar.setCloseButtonVisible(i!=="false"),e==="show-save-button"&&this.zoomToolbar&&this.zoomToolbar.setSaveButtonEnabled(i!=="false"))}getDesign(){if(!this.currentTemplate)throw new Error("No design loaded");return{templateId:this.currentTemplate.metadata.id,engineVersion:_,contents:this.contentManager.toJSON(),userData:{}}}setDesign(e){if(e.templateId!==this.currentTemplate?.metadata.id)throw new Error("Design template ID does not match current template");this.contentManager.fromJSON(e.contents),this.syncEngineContents(),this.stateManager.setState({contents:this.contentManager.toJSON(),isDirty:!0})}undo(){this.stateManager.undo()&&this.syncFromState()}redo(){this.stateManager.redo()&&this.syncFromState()}clear(){this.contentManager.reset(),this.syncEngineContents(),this.stateManager.setState({contents:this.contentManager.toJSON(),selectedAreaId:null,isDirty:!1}),this.zoomToolbar?.showSaveButton(!1),this.engine.setSelectedArea(null),this.inputPanel.setSelectedArea(null),this.engine.fitToView(),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()})}selectArea(e){this.handleAreaSelect(e)}getSelectedAreaId(){return this.stateManager.getState().selectedAreaId}getActiveView(){return this.activeViewName}getViews(){return this.productViews.map(e=>({viewName:e.viewName,viewOrder:e.viewOrder,isRequired:e.isRequired,isDefault:e.isDefault,templateId:e.template.id}))}setActiveView(e){if(this.activeViewName===e)return;const t=this.productViews.find(i=>i.viewName===e);if(!t)throw new Error(`View not found: ${e}`);this.switchToView(t)}setAreaContent(e,t){t.type==="text"?this.contentManager.setTextContent(e,t):this.contentManager.setImageContent(e,t.dataUrl,t.filename),this.syncEngineContents(),this.saveContentState()}getAreaContent(e){return this.contentManager.getContent(e)}static hasEditorContent(e){return e.type==="text"?e.text.trim().length>0:e.type==="image"?!!e.dataUrl:!1}validateRequiredContent(){const e=[];if(this.productViews.length>1){this.activeViewName&&this.contentManager&&this.perViewContents.set(this.activeViewName,this.contentManager.toJSON());for(const t of this.productViews){const i=this.perViewContents.get(t.viewName),s=i?.some(([,o])=>I.hasEditorContent(o))??!1;if(t.isRequired&&!s){e.push(`View "${t.viewName}" is required`);continue}const n=t.template.templateJson.areas||[];for(const o of n){if(!o.required)continue;const a=i?.find(([r])=>r===o.id);a&&I.hasEditorContent(a[1])||e.push(`"${o.name}" in view "${t.viewName}" is required`)}}}else{const t=this.currentTemplate?.areas||[],i=this.contentManager.getContents();for(const s of t){if(!s.required)continue;const n=i.get(s.id);n&&I.hasEditorContent(n)||e.push(`"${s.name}" is required`)}}return e}showValidationErrors(e){return new Promise(t=>{const i=h("div",{class:"editor-modal-overlay"}),s=h("div",{class:"editor-modal"});s.appendChild(h("div",{class:"editor-modal-title"},"Required content missing"));const n=h("div",{class:"editor-modal-body"}),o=document.createElement("ul");o.style.margin="8px 0",o.style.paddingLeft="20px";for(const d of e){const l=document.createElement("li");l.textContent=d,o.appendChild(l)}n.appendChild(o),s.appendChild(n);const a=h("div",{class:"editor-modal-actions"}),r=h("button",{class:"editor-modal-btn editor-modal-btn-primary"},"OK");r.addEventListener("click",()=>{i.remove(),t()}),a.appendChild(r),s.appendChild(a),i.appendChild(s),i.addEventListener("click",d=>{d.target===i&&(i.remove(),t())}),this.shadow.appendChild(i)})}async finalize(){const e=this.validateRequiredContent();if(e.length>0)return await this.showValidationErrors(e),null;const t=this.engine.checkSafeAreaViolations();if(t.length>0){const o=this.currentTemplate?.areas||[],a=t.map(r=>o.find(d=>d.id===r)?.name||r).join(", ");if(!await this.showConfirmation("Content outside safe area",`Content in ${a} extends beyond the safe area. It may be trimmed during printing. Continue?`,"Cancel","Proceed Anyway"))throw new Error("Finalization cancelled by user")}const i=this.getAttribute("store-id")||"demo-shop";if(this.pendingUploads.size>0&&await Promise.allSettled(this.pendingUploads.values()),this.productViews.length>1){this.activeViewName&&this.contentManager&&this.perViewContents.set(this.activeViewName,this.contentManager.toJSON());const o=[];for(const c of this.productViews){const p=this.perViewContents.get(c.viewName);!p||p.length===0||!p.some(([,u])=>I.hasEditorContent(u))||o.push({templateId:c.template.id,viewName:c.viewName,contents:p})}if(o.length===0)throw new Error("No views have content to finalize");const a=this.getAttribute("product-id");if(!a)throw new Error("product-id attribute is required for multi-view finalize");const r=await this.apiClient.finalizeMultiView({productId:a,designs:o}),d=r.views.map(c=>({viewName:c.viewName,designId:c.designId,proofUrl:c.proofUrl||"",status:c.status})),l=d[0];return this.dispatchEvent(new CustomEvent("customizer:finalize",{detail:{designId:l.designId,proofUrl:l.proofUrl,status:"processing",views:d,requestId:r.requestId},bubbles:!0,composed:!0})),{...l,views:d}}const s=this.getDesign(),n=await this.apiClient.finalizeDesign(i,s);return this.dispatchEvent(new CustomEvent("customizer:finalize",{detail:{designId:n.designId,proofUrl:n.proofUrl,templateId:s.templateId,designJson:s,status:n.status},bubbles:!0,composed:!0})),n}async waitForResult(e,t){const i=t?.pollInterval??1500,s=t?.maxPolls??40,n=t?.signal;let o=0;for(;o<s;){if(n?.aborted)throw new DOMException("Polling aborted","AbortError");await new Promise((d,l)=>{const c=setTimeout(d,i);n?.addEventListener("abort",()=>{clearTimeout(c),l(new DOMException("Polling aborted","AbortError"))},{once:!0})}),o++;let r;if(e.length===1){const d=await this.apiClient.getDesignStatus(e[0]);r={designs:[d],allCompleted:d.status!=="processing"}}else r=await this.apiClient.pollMultiViewStatus(e);if(r.allCompleted)return this.dispatchEvent(new CustomEvent("customizer:render-complete",{detail:r,bubbles:!0,composed:!0})),r}const a={designs:e.map(r=>({designId:r,status:"failed",proofUrl:null,errorMessage:"Render timed out"})),allCompleted:!0};return this.dispatchEvent(new CustomEvent("customizer:render-complete",{detail:a,bubbles:!0,composed:!0})),a}renderLoading(){const e=document.createElement("style");e.textContent=k,this.shadow.appendChild(e);const t=h("div",{class:"loading-container"},h("div",{class:"loading-spinner"}));this.shadow.appendChild(t)}async initialize(){const e=this.getAttribute("api-url")||"http://localhost:4000";this.apiClient=new B(`${e.replace(/\/+$/,"")}/public`);const t=this.getAttribute("product-id"),i=this.getAttribute("template-id");if(!t&&!i)throw new Error("Either template-id or product-id attribute is required");t?await this.loadProduct(t):await this.loadTemplate(i),this.isReady=!0,this.dispatchEvent(new CustomEvent("ready",{bubbles:!0,composed:!0}))}async loadTemplate(e){if(this.loadingTemplateId!==e){this.loadingTemplateId=e;try{const t=await this.apiClient.getTemplate(e);if(this.loadingTemplateId!==e)return;await this.loadTemplateData(t),this.loadingTemplateId=null}catch(t){throw this.loadingTemplateId=null,new Error(`Failed to load template: ${t.message}`)}}}async loadTemplateData(e){this.currentTemplate=e;const t=e.areas||[];this.contentManager=new P(t);const i={template:e,contents:this.contentManager.toJSON(),selectedAreaId:null,zoom:1,pan:{x:0,y:0},isDirty:!1,warnings:[]};this.stateManager=new F(i),this.buildUI(e,t),this.engine=new N(this.canvas),this.engine.setArtboard({width:e.artboard.width,height:e.artboard.height,backgroundColor:e.artboard.backgroundColor??"#ffffff"}),this.engine.setAreas(t),this.engine.setContents(this.contentManager.getContents()),e.backgroundImage&&this.engine.setBackgroundImage(e.backgroundImage);const s=e.artboard.safeArea??e.safeArea;s&&this.engine.setSafeArea(s),this.engine.start(),this.scheduleCanvasRefit(),this.interaction=new $(this.canvas,this.engine,()=>this.contentManager.getContents(),()=>this.stateManager.getState().selectedAreaId),this.wireInteractionEvents(),this.contentManagerUnsub=this.contentManager.subscribe(()=>{this.syncEngineContents(),this.inputPanel.setContents(this.contentManager.getContents())}),this.subscriptions.push(this.contentManagerUnsub),this.keyboard=new W(this),this.setupKeyboardShortcuts(),this.inputPanel.setContents(this.contentManager.getContents());const n=this.getAttribute("store-id");n&&this.apiClient.getStorageUsage(n).then(o=>{this.storageUsage=o,o.usagePercent>=100?this.showToast("Storage quota exceeded. Upgrade your plan for more storage.","error"):o.usagePercent>=80&&this.showToast(`Storage ${o.usagePercent}% full. Consider upgrading your plan.`,"warning")}).catch(()=>{})}async loadProduct(e){try{const t=await this.apiClient.getProduct(e);if(t.views.length===0)throw new Error("Product has no active templates");this.productViews=t.views,this.perViewContents.clear();const i=t.views.find(s=>s.isDefault)||t.views[0];await this.loadTemplateData(i.template.templateJson),this.activeViewName=i.viewName,this.zoomToolbar&&t.views.length>1&&(this.zoomToolbar.setViews(this.getViews()),this.zoomToolbar.setActiveView(i.viewName))}catch(t){throw new Error(`Failed to load product: ${t.message}`)}}switchToView(e){this.activeViewName&&this.contentManager&&this.perViewContents.set(this.activeViewName,this.contentManager.toJSON()),this.activeViewName=e.viewName,this.zoomToolbar?.setActiveView(e.viewName);const t=e.template.templateJson;this.currentTemplate=t;const i=t.areas||[];this.contentManager=new P(i),this.contentManagerUnsub&&this.contentManagerUnsub(),this.contentManagerUnsub=this.contentManager.subscribe(()=>{this.syncEngineContents(),this.inputPanel.setContents(this.contentManager.getContents())});const s=this.perViewContents.get(e.viewName);s&&this.contentManager.fromJSON(s),this.engine.setArtboard({width:t.artboard.width,height:t.artboard.height,backgroundColor:t.artboard.backgroundColor??"#ffffff"}),this.engine.setAreas(i),this.engine.setContents(this.contentManager.getContents()),this.engine.setBackgroundImage(t.backgroundImage??void 0);const n=t.artboard.safeArea??t.safeArea;this.engine.setSafeArea(n??null),this.engine.fitToView(),this.stateManager.setState({template:t,contents:this.contentManager.toJSON(),selectedAreaId:null,isDirty:this.stateManager.getState().isDirty,zoom:this.engine.getZoom(),pan:this.engine.getPan(),warnings:[]}),this.zoomToolbar.setZoom(this.engine.getZoom()),this.inputPanel.setAreas(i),this.inputPanel.setContents(this.contentManager.getContents()),this.inputPanel.setSelectedArea(null),this.engine.setSelectedArea(null),this.dispatchEvent(new CustomEvent("view-change",{detail:{viewName:e.viewName},bubbles:!0,composed:!0}))}buildUI(e,t){this.shadow.innerHTML="";const i=document.createElement("style");i.textContent=k,this.shadow.appendChild(i),this.container=h("div",{class:"editor-container"}),this.zoomToolbar=new H,this.subscriptions.push(this.zoomToolbar.on("zoom-in",()=>this.handleZoomIn())),this.subscriptions.push(this.zoomToolbar.on("zoom-out",()=>this.handleZoomOut())),this.subscriptions.push(this.zoomToolbar.on("zoom-fit",()=>this.handleZoomFit())),this.subscriptions.push(this.zoomToolbar.on("view-change",({viewName:o})=>{const a=this.productViews.find(r=>r.viewName===o);a&&this.switchToView(a)})),this.subscriptions.push(this.zoomToolbar.on("close",()=>{this.dispatchEvent(new CustomEvent("customizer:close",{bubbles:!0,composed:!0}))})),this.subscriptions.push(this.zoomToolbar.on("save",async()=>{this.pendingUploads.size>0&&(this.zoomToolbar.setSaveDisabled(!0,"Uploading..."),await Promise.allSettled(this.pendingUploads.values())),this.zoomToolbar.setSaveDisabled(!0,"Saving...");try{const o=await this.finalize();if(!o){this.zoomToolbar.setSaveDisabled(!1,"✓");return}this.zoomToolbar.setSaveDisabled(!0,"Saved!"),this.showToast("Design saved successfully!","info"),this.dispatchEvent(new CustomEvent("customizer:save",{detail:o,bubbles:!0,composed:!0}));const a=o.views?o.views.map(r=>r.designId):[o.designId];this.renderAbortController?.abort(),this.renderAbortController=new AbortController,this.waitForResult(a,{signal:this.renderAbortController.signal}).catch(r=>{r instanceof DOMException&&r.name==="AbortError"||console.error("Customizer: background render polling failed",r)}),setTimeout(()=>{this.zoomToolbar.setSaveDisabled(!1,"✓"),this.zoomToolbar.showSaveButton(!1),this.stateManager.update({isDirty:!1})},2e3)}catch(o){console.error("Customizer: finalize failed",o);const a=o instanceof Error?o.message:"Save failed. Please try again.";this.showToast(a,"error"),this.zoomToolbar.setSaveDisabled(!1,"✓")}})),this.getAttribute("show-close-button")==="false"&&this.zoomToolbar.setCloseButtonVisible(!1),this.getAttribute("show-save-button")==="false"&&this.zoomToolbar.setSaveButtonEnabled(!1);const s=h("div",{class:"canvas-area"});s.appendChild(this.zoomToolbar.getElement()),this.canvasWrapper=h("div",{class:"canvas-wrapper"}),this.canvas=document.createElement("canvas"),this.canvas.className="editor-canvas",this.canvasWrapper.appendChild(this.canvas),s.appendChild(this.canvasWrapper),this.inputPanel=new J(t),this.wireInputPanelEvents();const n=h("div",{class:"editor-main"});n.appendChild(s),n.appendChild(this.inputPanel.getBackdrop()),n.appendChild(this.inputPanel.getElement()),this.container.appendChild(n),this.shadow.appendChild(this.container),this.resizeObserver=new ResizeObserver(o=>{for(const a of o)a.target===this.canvasWrapper&&this.handleCanvasResize(a.contentRect),a.target===this.container&&this.handleLayoutResize(a.contentRect)}),this.resizeObserver.observe(this.canvasWrapper),this.resizeObserver.observe(this.container)}wireInteractionEvents(){this.subscriptions.push(this.interaction.on("area:select",({areaId:e})=>{this.handleAreaSelect(e)})),this.subscriptions.push(this.interaction.on("content:drag",({areaId:e,offset:t})=>{this.contentManager.setContentOffset(e,t),this.saveContentState()})),this.subscriptions.push(this.interaction.on("content:scale",({areaId:e,scale:t})=>{this.contentManager.setImageScale(e,t),this.saveContentState()})),this.subscriptions.push(this.interaction.on("content:rotate",({areaId:e,rotation:t})=>{this.contentManager.setImageRotation(e,t),this.saveContentState()})),this.subscriptions.push(this.interaction.on("zoom",({zoom:e})=>{this.engine.setZoom(e);const t=this.engine.getCenteredPan(e);t&&this.engine.setPan(t),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())})),this.subscriptions.push(this.interaction.on("pan",({pan:e})=>{this.engine.setPan(e),this.stateManager.update({pan:e})})),this.subscriptions.push(this.interaction.on("cursor",({cursor:e})=>{this.canvas.style.cursor=e})),this.subscriptions.push(this.interaction.on("context-menu",({areaId:e,clientX:t,clientY:i})=>{this.handleContextMenu(e,t,i)}))}wireInputPanelEvents(){this.subscriptions.push(this.inputPanel.on("text:change",({areaId:e,updates:t})=>{this.contentManager.setTextContent(e,t),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("image:change",async({areaId:e,dataUrl:t,filename:i})=>{const s=this.contentManager.getContents();let n=0;for(const[r,d]of s)d.type==="image"&&r!==e&&n++;if(n>=10){this.showToast("Maximum of 10 images per design reached","error");return}if(await this.refreshStorageUsage(),this.storageUsage&&this.storageUsage.usagePercent>=100){this.showToast("Storage quota exceeded. Upgrade your plan for more storage.","error");return}const o=this.currentTemplate?.areas.find(r=>r.id===e);if(o&&this.currentTemplate){const r=this.currentTemplate.print.targetDpi;try{const d=new Image;d.crossOrigin="anonymous";const l=await new Promise((g,m)=>{d.onload=()=>g({width:d.naturalWidth,height:d.naturalHeight}),d.onerror=()=>m(new Error("Failed to load image")),d.src=t}),c=o.location.width/r,p=o.location.height/r,u=Q(l.width,l.height,c,p,r);if(u.actualDPI<150){if(!await this.showConfirmation("Low resolution detected",`This image is ${u.actualDPI} DPI at print size. Print may appear blurry. Recommended: ${r} DPI.`,"Cancel","Use Anyway"))return}else u.actualDPI<r&&(this.showToast(`Image resolution is ${u.actualDPI} DPI (${r} recommended)`,"warning"),this.stateManager.update({warnings:[...this.stateManager.getState().warnings,{type:"dpi",message:`${u.actualDPI} DPI`,areaId:e}]}))}catch{}}this.contentManager.setImageContent(e,t,i),this.saveContentState();const a=this.getAttribute("store-id");if(a&&this.preUploadImage(e,t,a),this.storageUsage){const r=Math.round(t.length*.75);this.storageUsage.storageUsed+=r,this.storageUsage.usagePercent=this.storageUsage.storageQuota>0?Math.round(this.storageUsage.storageUsed/this.storageUsage.storageQuota*100):0}})),this.subscriptions.push(this.inputPanel.on("validation:error",({message:e})=>{this.showToast(e,"error")})),this.subscriptions.push(this.inputPanel.on("clear",({areaId:e})=>{this.contentManager.clearContent(e),this.syncEngineContents(),this.inputPanel.setContents(this.contentManager.getContents()),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("select",({areaId:e})=>{this.handleAreaSelect(e)})),this.subscriptions.push(this.inputPanel.on("offset:change",({areaId:e,offset:t})=>{this.contentManager.setContentOffset(e,t),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("scale:change",({areaId:e,scale:t})=>{this.contentManager.setImageScale(e,t),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("rotation:change",({areaId:e,rotation:t})=>{this.contentManager.setImageRotation(e,t),this.saveContentState()})),this.subscriptions.push(this.inputPanel.on("dismiss",()=>{this.handleAreaSelect(null)}))}handleAreaSelect(e,t=!1){this.stateManager.update({selectedAreaId:e}),this.engine.setSelectedArea(e),this.inputPanel.setSelectedArea(t?e:null),e&&document.activeElement!==this&&this.focus(),this.dispatchEvent(new CustomEvent("area:select",{detail:{areaId:e},bubbles:!0,composed:!0}))}handleContextMenu(e,t,i){this.handleAreaSelect(e,!!e)}handleZoomIn(){const e=this.engine.getZoom(),t=Math.min(e*1.25,5),i=this.engine.getCenteredPan(t);this.engine.setZoom(t),i&&this.engine.setPan(i),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())}handleZoomOut(){const e=this.engine.getZoom(),t=Math.max(e*.8,.1),i=this.engine.getCenteredPan(t);this.engine.setZoom(t),i&&this.engine.setPan(i),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())}handleZoomFit(){this.engine.fitToView(),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom())}scheduleCanvasRefit(){this.refitRafId!=null&&cancelAnimationFrame(this.refitRafId),this.refitRafId=requestAnimationFrame(()=>{this.refitRafId=requestAnimationFrame(()=>{if(this.refitRafId=null,!this.canvasWrapper||!this.engine)return;const e=this.canvasWrapper.getBoundingClientRect();e.width>0&&e.height>0&&this.handleCanvasResize(e)})})}handleCanvasResize(e){const{width:t,height:i}=e;if(t===0||i===0)return;const s=window.devicePixelRatio||1;this.canvas.width=t*s,this.canvas.height=i*s,this.canvas.style.width=`${t}px`,this.canvas.style.height=`${i}px`,this.engine&&(this.engine.fitToView(),this.stateManager.update({zoom:this.engine.getZoom(),pan:this.engine.getPan()}),this.zoomToolbar.setZoom(this.engine.getZoom()))}handleLayoutResize(e){const t=e.width<ee;this.container.classList.toggle("layout-desktop",!t),this.container.classList.toggle("layout-mobile",t),t!==this.isMobileLayout&&(this.isMobileLayout=t,this.inputPanel.setMobile(t),this.scheduleCanvasRefit())}async refreshStorageUsage(){if(Date.now()-this.storageUsageLastRefresh<3e4)return;const e=this.getAttribute("store-id");if(e)try{this.storageUsage=await this.apiClient.getStorageUsage(e),this.storageUsageLastRefresh=Date.now()}catch{}}preUploadImage(e,t,i){const s=this.activeViewName,n=this._doPreUpload(e,t,i,s);return this.pendingUploads.set(e,n),this.inputPanel?.setUploading(e,!0),this.updateSaveButtonForUploads(),n.finally(()=>{this.pendingUploads.get(e)===n&&this.pendingUploads.delete(e),this.inputPanel?.setUploading(e,!1),this.updateSaveButtonForUploads()}),n}async _doPreUpload(e,t,i,s){const[n,o]=t.split(","),a=n.match(/:(.*?);/)?.[1]||"image/png",r=atob(o),d=new Uint8Array(r.length);for(let g=0;g<r.length;g++)d[g]=r.charCodeAt(g);const l=new Blob([d],{type:a}),c=a.split("/")[1]||"png",p=new File([l],`upload.${c}`,{type:a});let u;try{u=await this.apiClient.uploadAsset(p,i)}catch(g){console.error("Customizer: image pre-upload failed",g),this.showToast("Image upload failed. Will retry on save.","warning");return}if(this.activeViewName===s){const g=this.contentManager.getContent(e);g?.type==="image"&&g.dataUrl===t&&this.contentManager.setImageContent(e,u.publicUrl,g.filename,{assetId:u.assetId,assetUrl:u.publicUrl})}else if(s){const g=this.perViewContents.get(s);if(g){const m=g.map(([v,b])=>v===e&&b.type==="image"&&b.dataUrl===t?[v,{...b,dataUrl:u.publicUrl,assetId:u.assetId,assetUrl:u.publicUrl}]:[v,b]);this.perViewContents.set(s,m)}}}saveContentState(){this.stateManager.setState({contents:this.contentManager.toJSON(),isDirty:!0}),this.zoomToolbar?.showSaveButton(!0),this.engine.checkSafeAreaViolations(),this.emitChange()}syncEngineContents(){this.engine?.setContents(this.contentManager.getContents())}syncFromState(){const e=this.stateManager.getState();this.contentManager.setContentsQuiet(e.contents),this.syncEngineContents(),this.inputPanel.setContents(this.contentManager.getContents()),this.engine.setSelectedArea(e.selectedAreaId),this.inputPanel.setSelectedArea(e.selectedAreaId)}setupKeyboardShortcuts(){const e=G();this.keyboard.register({key:"z",[e]:!0,handler:()=>this.undo()}),this.keyboard.register({key:"z",[e]:!0,shift:!0,handler:()=>this.redo()}),this.keyboard.register({key:"Escape",handler:()=>this.handleAreaSelect(null)})}updateSaveButtonForUploads(){this.zoomToolbar&&this.stateManager?.getState().isDirty&&(this.pendingUploads.size>0?this.zoomToolbar.setSaveDisabled(!0,"Uploading..."):this.zoomToolbar.setSaveDisabled(!1,"✓"))}showToast(e,t="info"){const i=h("div",{class:`editor-toast editor-toast-${t}`},e);this.shadow.appendChild(i),setTimeout(()=>i.remove(),4e3)}showConfirmation(e,t,i="Cancel",s="Continue"){return new Promise(n=>{const o=h("div",{class:"editor-modal-overlay"}),a=h("div",{class:"editor-modal"});a.appendChild(h("div",{class:"editor-modal-title"},e)),a.appendChild(h("div",{class:"editor-modal-body"},t));const r=h("div",{class:"editor-modal-actions"}),d=h("button",{class:"editor-modal-btn"},i);d.addEventListener("click",()=>{o.remove(),n(!1)});const l=h("button",{class:"editor-modal-btn editor-modal-btn-primary"},s);l.addEventListener("click",()=>{o.remove(),n(!0)}),r.appendChild(d),r.appendChild(l),a.appendChild(r),o.appendChild(a),o.addEventListener("click",c=>{c.target===o&&(o.remove(),n(!1))}),this.shadow.appendChild(o)})}showError(e,t){this.shadow.innerHTML="";const i=document.createElement("style");i.textContent=k,this.shadow.appendChild(i);const s=h("div",{class:"error-container"});s.appendChild(h("div",{class:"error-title"},e)),s.appendChild(h("div",{class:"error-message"},t?.message??"")),this.shadow.appendChild(s),this.dispatchEvent(new CustomEvent("error",{detail:{message:e,error:t},bubbles:!0,composed:!0}))}cleanup(){this.renderAbortController?.abort(),this.renderAbortController=null;for(const e of this.subscriptions)e();this.subscriptions=[],this.refitRafId!=null&&(cancelAnimationFrame(this.refitRafId),this.refitRafId=null),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.engine?.destroy(),this.interaction?.destroy(),this.keyboard?.destroy(),this.isReady=!1,this.currentTemplate=null,this.productViews=[],this.activeViewName=null,this.perViewContents.clear()}}customElements.get("customizer-editor")||customElements.define("customizer-editor",I);function te(f,e){const t=typeof f=="string"?document.querySelector(f):f;if(!t)throw new Error(`Container not found: ${typeof f=="string"?f:"provided element is null"}`);if(!e.templateId&&!e.productId)throw new Error("Either templateId or productId must be provided");if(e.templateId&&e.productId)throw new Error("Only one of templateId or productId should be provided, not both");const i=document.createElement("customizer-editor");e.productId?i.setAttribute("product-id",e.productId):i.setAttribute("template-id",e.templateId),e.theme&&i.setAttribute("theme",e.theme),e.mode&&i.setAttribute("mode",e.mode),e.className&&i.classList.add(e.className),e.showCloseButton===!1&&i.setAttribute("show-close-button","false"),e.showSaveButton===!1&&i.setAttribute("show-save-button","false"),i.style.width="100%",i.style.height="100%";const s=[],n=(a,r)=>{i.addEventListener(a,r),s.push({event:a,handler:r})};t.innerHTML="",t.appendChild(i);const o={getDesign(){if(typeof i.getDesign!="function")throw new Error("Editor not ready: getDesign method not available");return i.getDesign()},setDesign(a){if(typeof i.setDesign!="function")throw new Error("Editor not ready: setDesign method not available");i.setDesign(a)},undo(){if(typeof i.undo!="function")throw new Error("Editor not ready: undo method not available");i.undo()},redo(){if(typeof i.redo!="function")throw new Error("Editor not ready: redo method not available");i.redo()},canUndo(){return typeof i.canUndo!="function"?!1:i.canUndo()},canRedo(){return typeof i.canRedo!="function"?!1:i.canRedo()},async finalize(){const a=e.apiUrl||"https://api.varianta.io";try{if(e.productId&&typeof i.finalize=="function"){const p=await i.finalize(),u={designId:p.designId,status:"processing",proofUrl:p.proofUrl??null,errorMessage:null,requestId:p.requestId,views:p.views};return e.onFinalize&&e.onFinalize(u),u}const r=this.getDesign(),d=await fetch(`${a}/public/finalize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({templateId:r.templateId||e.templateId,designJson:r})});if(!d.ok){const p=await d.json().catch(()=>({message:"Finalization failed"}));throw new Error(p.message||"Finalization failed")}const l=await d.json(),c={designId:l.designId,status:l.status,proofUrl:l.proofUrl??null,errorMessage:l.errorMessage??null};return e.onFinalize&&e.onFinalize(c),c}catch(r){const d={code:"FINALIZE_ERROR",message:r instanceof Error?r.message:"Unknown error",details:r};throw e.onError&&e.onError(d),r}},async waitForResult(a,r){if(typeof i.waitForResult=="function"){const m=await i.waitForResult(a,r);return e.onRenderComplete&&e.onRenderComplete(m),m}const d=e.apiUrl||"https://api.varianta.io",l=r?.pollInterval??1500,c=r?.maxPolls??40,p=r?.signal;let u=0;for(;u<c;){if(p?.aborted)throw new DOMException("Polling aborted","AbortError");await new Promise((v,b)=>{const w=setTimeout(v,l);p?.addEventListener("abort",()=>{clearTimeout(w),b(new DOMException("Polling aborted","AbortError"))},{once:!0})}),u++;let m;if(a.length===1){const v=await fetch(`${d}/public/designs/${a[0]}/status`);if(!v.ok)throw new Error("Failed to check design status");const b=await v.json();m={designs:[b],allCompleted:b.status!=="processing"}}else{const v=await fetch(`${d}/public/finalize-status?designIds=${a.join(",")}`);if(!v.ok)throw new Error("Failed to check design status");m=await v.json()}if(m.allCompleted)return e.onRenderComplete&&e.onRenderComplete(m),m}const g={designs:a.map(m=>({designId:m,status:"failed",proofUrl:null,errorMessage:"Render timed out"})),allCompleted:!0};return e.onRenderComplete&&e.onRenderComplete(g),g},addTextLayer(a){if(typeof i.addTextLayer!="function")throw new Error("Editor not ready: addTextLayer method not available");i.addTextLayer(a)},async addImageLayer(a){if(typeof i.addImageLayer!="function")throw new Error("Editor not ready: addImageLayer method not available");return i.addImageLayer(a)},removeLayer(a){if(typeof i.removeLayer!="function")throw new Error("Editor not ready: removeLayer method not available");i.removeLayer(a)},selectLayer(a){if(typeof i.selectLayer!="function")throw new Error("Editor not ready: selectLayer method not available");i.selectLayer(a)},getSelectedLayerId(){return typeof i.getSelectedLayerId!="function"?null:i.getSelectedLayerId()},getActiveView(){return typeof i.getActiveView!="function"?null:i.getActiveView()},getViews(){return typeof i.getViews!="function"?[]:i.getViews()},setActiveView(a){if(typeof i.setActiveView!="function")throw new Error("Editor not ready: setActiveView method not available");i.setActiveView(a)},setTheme(a){i.setAttribute("theme",a)},setMode(a){i.setAttribute("mode",a)},destroy(){s.forEach(({event:a,handler:r})=>{i.removeEventListener(a,r)}),i.parentNode&&i.parentNode.removeChild(i)},getElement(){return i}};return e.onReady&&n("ready",(()=>{e.onReady?.(o)})),e.onChange&&n("change",(a=>{const r=a.detail.design;e.onChange?.(r)})),e.onLayerSelect&&n("layer:select",(a=>{e.onLayerSelect?.(a.detail.layerId)})),e.onLayerAdd&&n("layer:add",(a=>{e.onLayerAdd?.(a.detail.layerId)})),e.onLayerRemove&&n("layer:remove",(a=>{e.onLayerRemove?.(a.detail.layerId)})),e.onLayerUpdate&&n("layer:update",(a=>{e.onLayerUpdate?.(a.detail.layerId)})),e.onError&&n("error",(a=>{const r=a.detail.error;e.onError?.(r)})),e.onViewChange&&n("view-change",(a=>{e.onViewChange?.(a.detail.viewName)})),e.onRenderComplete&&n("customizer:render-complete",(a=>{e.onRenderComplete?.(a.detail)})),e.onClose&&n("customizer:close",(()=>{e.onClose?.()})),e.onSave&&n("customizer:save",(a=>{e.onSave?.(a.detail)})),e.initialDesign&&i.addEventListener("ready",()=>{e.initialDesign&&i.setDesign(e.initialDesign)},{once:!0}),e.debug&&(console.log("[Customizer SDK] Initialized with options:",e),console.log("[Customizer SDK] Instance:",o)),o}exports.initCustomizer=te;
|
|
12
12
|
//# sourceMappingURL=index.cjs.js.map
|