@varianta/sdk 0.1.2 → 0.1.3
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 +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +85 -62
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +3 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/types/types/index.d.ts +3 -3
- 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.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
(function(S,k){typeof exports=="object"&&typeof module<"u"?k(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],k):(S=typeof globalThis<"u"?globalThis:S||self,k(S.CustomizerSDK={},S.jsxRuntime,S.React))})(this,(function(S,k,x){"use strict";class Z{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=new Image;t.crossOrigin="anonymous",t.onload=()=>{this.bgImageElement=t,this.requestRender()},t.onerror=()=>{this.bgImageElement=null,console.error("Failed to load background image:",e.url),this.requestRender()},t.src=e.url}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,n=window.devicePixelRatio||1,a=this.canvas.width/n,s=this.canvas.height/n;return{x:(a/e-t)/2,y:(s/e-i)/2}}requestRender(){this.needsRender=!0}fitToView(){if(!this.artboard)return;const e=0,{width:t,height:i}=this.artboard,n=window.devicePixelRatio||1,a=this.canvas.width/n-e*2,s=this.canvas.height/n-e*2;if(a<=0||s<=0)return;const r=a/t,o=s/i,d=Math.min(r,o),l=this.canvas.width/n,h=this.canvas.height/n,u=(l/d-t)/2,g=(h/d-i)/2;this.zoom=d,this.pan={x:u,y:g},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,n=e/i,a=t/i,s=n/this.zoom-this.pan.x,r=a/this.zoom-this.pan.y;return{x:s,y:r}}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:n}=t,{x:a,y:s,width:r,height:o}=n;if(i.type==="text"){const d=i.offset||{x:0,y:0},l=i.scale??1,h=i.rotation??0,u=r*l,g=o*l,m=a+r/2+d.x,b=s+o/2+d.y;return{x:m-u/2,y:b-g/2,width:u,height:g,rotation:h,centerX:m,centerY:b}}else if(i.type==="image"){const d=this.loadedImages.get(e);if(!d)return null;const l=i.offset||{x:0,y:0},h=i.scale??1,u=i.rotation??0,g=d.naturalWidth/d.naturalHeight,m=r/o;let b,y;g>m?(b=r,y=r/g):(y=o,b=o*g);const v=b*h,C=y*h,p=a+r/2+l.x,I=s+o/2+l.y;return{x:p-v/2,y:I-C/2,width:v,height:C,rotation:u,centerX:p,centerY:I}}return null}hitTestContent(e,t){if(!this.areaSelectionEnabled)return null;for(let i=this.areas.length-1;i>=0;i--){const n=this.areas[i],a=this.getContentBounds(n.id);if(a&&this.isPointInBounds(e,t,a))return n.id}return null}hitTestArea(e,t){if(!this.areaSelectionEnabled)return null;for(let i=this.areas.length-1;i>=0;i--){const n=this.areas[i],{x:a,y:s,width:r,height:o}=n.location;if(e>=a&&e<=a+r&&t>=s&&t<=s+o)return n.id}return null}isPointInBounds(e,t,i){const{centerX:n,centerY:a,width:s,height:r,rotation:o}=i,d=-o*Math.PI/180,l=Math.cos(d),h=Math.sin(d),u=e-n,g=t-a,m=u*l-g*h,b=u*h+g*l;return m>=-s/2&&m<=s/2&&b>=-r/2&&b<=r/2}getContentHandlePositions(e){const t=this.areas.find(v=>v.id===e);if(!t)return null;const i=this.contents.get(e);if(!i)return null;const n=this.getContentBounds(e);if(!n)return null;const{centerX:a,centerY:s,width:r,height:o,rotation:d}=n,l=6/this.zoom,h=30/this.zoom,u=d*Math.PI/180,g=Math.cos(u),m=Math.sin(u),b=(v,C)=>({x:a+v*g-C*m,y:s+v*m+C*g}),y=[];if(i.type==="image"&&t.imageOptions?.allowScaling||i.type==="text"&&t.textOptions?.allowScaling){const v=r/2,C=o/2,p=b(-v,-C);y.push({type:"nw",...p,radius:l});const I=b(v,-C);y.push({type:"ne",...I,radius:l});const E=b(v,C);y.push({type:"se",...E,radius:l});const w=b(-v,C);y.push({type:"sw",...w,radius:l})}if(i.type==="image"&&t.imageOptions?.allowRotation||i.type==="text"&&t.textOptions?.allowRotation){const v=b(0,-o/2-h);y.push({type:"rotate",...v,radius:l})}return y}hitTestHandle(e,t,i){const n=this.getContentHandlePositions(i);if(!n)return null;for(const a of n){const s=e-a.x,r=t-a.y;if(Math.sqrt(s*s+r*r)<=a.radius*2)return a.type}return null}isPositioningAllowed(e){const t=this.areas.find(n=>n.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:n,left:a}=this.safeArea,s=a,r=t,o=this.artboard.width-i,d=this.artboard.height-n;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:g,width:m,height:b,rotation:y}=h,v=y*Math.PI/180,C=Math.abs(Math.cos(v)),p=Math.abs(Math.sin(v)),I=m*C+b*p,E=m*p+b*C,w=u-I/2,R=g-E/2,D=u+I/2,U=g+E/2;(w<s||R<r||D>o||U>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 n of i)n(t)}async exportToPNG(){if(!this.artboard)throw new Error("No artboard configured");const{width:e,height:t,backgroundColor:i}=this.artboard,n=document.createElement("canvas");n.width=e,n.height=t;const a=n.getContext("2d");if(!a)throw new Error("Failed to get 2D context for export");a.fillStyle=i,a.fillRect(0,0,e,t),this.backgroundImage?.position==="behind"&&this.bgImageElement&&this.drawBackgroundImageOnContext(a,e,t);for(const s of this.areas)this.drawAreaContentOnContext(a,s);return this.backgroundImage?.position==="overlay"&&this.bgImageElement&&this.drawBackgroundImageOnContext(a,e,t),new Promise((s,r)=>{n.toBlob(o=>{o?s(o):r(new Error("Failed to create PNG blob"))},"image/png",1)})}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.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:n,height:a,backgroundColor:s}=this.artboard;e.fillStyle=s,e.fillRect(0,0,n,a),e.strokeStyle="#d1d5db",e.lineWidth=1/this.zoom,e.strokeRect(0,0,n,a),this.backgroundImage?.position==="behind"&&this.renderBackgroundImage();for(const r of this.areas)this.renderAreaWithContent(r);this.backgroundImage?.position==="overlay"&&this.renderBackgroundImage(),this.renderSafeAreaGuide();for(const r of this.safeAreaViolations){const o=this.areas.find(g=>g.id===r);if(!o)continue;const{x:d,y:l,width:h,height:u}=o.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 t=this.getContentBounds(e);if(!t){const l=this.areas.find(y=>y.id===e);if(!l)return;const{ctx:h}=this,{x:u,y:g,width:m,height:b}=l.location;h.save(),h.strokeStyle="#3b82f6",h.lineWidth=2/this.zoom,h.setLineDash([]),h.strokeRect(u,g,m,b),h.restore();return}const{ctx:i}=this,{centerX:n,centerY:a,width:s,height:r,rotation:o}=t;i.save(),i.translate(n,a),i.rotate(o*Math.PI/180),i.strokeStyle="#3b82f6",i.lineWidth=2/this.zoom,i.setLineDash([]),i.strokeRect(-s/2,-r/2,s,r),i.restore();const d=this.getContentHandlePositions(e);if(d)for(const l of d){i.save();const h=this.hoveredHandle===l.type,u=h?"#dbeafe":"#ffffff",g=h?"#2563eb":"#3b82f6";if(l.type==="rotate"){const m=this.getRotatedPoint(n,a-r/2,n,a,o);i.beginPath(),i.strokeStyle=g,i.lineWidth=1/this.zoom,i.setLineDash([4/this.zoom,4/this.zoom]),i.moveTo(m.x,m.y),i.lineTo(l.x,l.y),i.stroke(),i.setLineDash([]),i.beginPath(),i.fillStyle=u,i.strokeStyle=g,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=g,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=u,i.strokeStyle=g,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,n,a){const s=a*Math.PI/180,r=Math.cos(s),o=Math.sin(s),d=e-i,l=t-n;return{x:i+d*r-l*o,y:n+d*o+l*r}}renderBackgroundImage(){if(!this.bgImageElement||!this.artboard||!this.backgroundImage)return;const{ctx:e}=this,{opacity:t}=this.backgroundImage,{width:i,height:n}=this.artboard;e.save(),e.globalAlpha=t;const a=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,s=i/n;let r,o,d,l;a>s?(o=n,r=n*a,d=(i-r)/2,l=0):(r=i,o=i/a,d=0,l=(n-o)/2),e.drawImage(this.bgImageElement,d,l,r,o),e.restore()}renderSafeAreaGuide(){if(!this.safeArea||!this.artboard)return;const{ctx:e}=this,{top:t,right:i,bottom:n,left:a}=this.safeArea,s=a,r=t,o=this.artboard.width-a-i,d=this.artboard.height-t-n;o<=0||d<=0||(e.save(),e.strokeStyle="#22c55e",e.lineWidth=1/this.zoom,e.setLineDash([6/this.zoom,4/this.zoom]),e.strokeRect(s,r,o,d),e.setLineDash([]),e.restore())}renderAreaWithContent(e){const{ctx:t}=this,{location:i,backgroundColor:n}=e,{x:a,y:s,width:r,height:o}=i;n&&(t.fillStyle=n,t.fillRect(a,s,r,o));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(a,s,r,o),t.setLineDash([]),t.restore())}renderTextContent(e,t){if(!t.text.trim())return;const{ctx:i}=this,{location:n}=e,{x:a,y:s,width:r,height:o}=n,d=t.offset||{x:0,y:0},l=t.scale??1,h=t.rotation??0,u=a+r/2+d.x,g=s+o/2+d.y;i.save(),i.beginPath(),i.rect(a,s,r,o),i.clip(),i.translate(u,g),i.rotate(h*Math.PI/180),i.scale(l,l),i.font=`${t.size}px ${t.font}, sans-serif`,i.fillStyle=t.color,i.textBaseline="middle";let m;switch(t.align){case"left":i.textAlign="left",m=-r/2+10;break;case"right":i.textAlign="right",m=r/2-10;break;case"center":default:i.textAlign="center",m=0;break}i.fillText(t.text,m,0),i.restore()}renderImageContent(e,t){const i=this.loadedImages.get(e.id);if(!i)return;const{ctx:n}=this,{location:a}=e,{x:s,y:r,width:o,height:d}=a,l=t.offset||{x:0,y:0},h=t.scale??1,u=t.rotation??0;n.save(),n.beginPath(),n.rect(s,r,o,d),n.clip();const g=i.naturalWidth/i.naturalHeight,m=o/d;let b,y;g>m?(b=o,y=o/g):(y=d,b=d*g);const v=b*h,C=y*h,p=s+o/2+l.x,I=r+d/2+l.y;n.translate(p,I),n.rotate(u*Math.PI/180),n.drawImage(i,-v/2,-C/2,v,C),n.restore()}drawBackgroundImageOnContext(e,t,i){if(!this.bgImageElement||!this.backgroundImage)return;const{opacity:n}=this.backgroundImage;e.save(),e.globalAlpha=n;const a=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,s=t/i;let r,o,d,l;a>s?(o=i,r=i*a,d=(t-r)/2,l=0):(r=t,o=t/a,d=0,l=(i-o)/2),e.drawImage(this.bgImageElement,d,l,r,o),e.restore()}drawAreaContentOnContext(e,t){const{location:i,backgroundColor:n}=t,{x:a,y:s,width:r,height:o}=i;n&&(e.fillStyle=n,e.fillRect(a,s,r,o));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},h=d.scale??1,u=d.rotation??0,g=a+r/2+l.x,m=s+o/2+l.y;e.save(),e.beginPath(),e.rect(a,s,r,o),e.clip(),e.translate(g,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 b;switch(d.align){case"left":e.textAlign="left",b=-r/2+10;break;case"right":e.textAlign="right",b=r/2-10;break;case"center":default:e.textAlign="center",b=0;break}e.fillText(d.text,b,0),e.restore()}else if(d.type==="image"){const l=this.loadedImages.get(t.id);if(!l)return;const h=d.offset||{x:0,y:0},u=d.scale??1,g=d.rotation??0;e.save(),e.beginPath(),e.rect(a,s,r,o),e.clip();const m=l.naturalWidth/l.naturalHeight,b=r/o;let y,v;m>b?(y=r,v=r/m):(v=o,y=o*m);const C=y*u,p=v*u,I=a+r/2+h.x,E=s+o/2+h.y;e.translate(I,E),e.rotate(g*Math.PI/180),e.drawImage(l,-C/2,-p/2,C,p),e.restore()}}}destroy(){this.stop(),this.bgImageElement=null,this.loadedImages.clear(),this.loadingImages.clear(),this.eventListeners.clear()}}function T(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 P(f){return f.contentType==="text"||f.contentType==="both"}function F(f){return f.contentType==="image"||f.contentType==="both"}const $=50;class q{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 n=this.state;this.state={...this.state,...e},!i&&!this.isRestoring&&this.saveToHistory(n,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>$&&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 B{constructor(e){this.listeners=new Set,this.areas=e,this.contents=this.buildInitialContents()}buildInitialContents(){const e=new Map;for(const t of this.areas)P(t)&&e.set(t.id,T(t));return e}getContents(){return this.contents}getContent(e){return this.contents.get(e)}setTextContent(e,t){const i=this.contents.get(e),n=this.areas.find(s=>s.id===e),a=n?T(n):{type:"text",text:"",font:"Inter",size:24,color:"#000000",align:"center"};i?.type==="text"?this.contents.set(e,{...i,...t}):this.contents.set(e,{...a,...t}),this.notify()}setImageContent(e,t,i){const n={type:"image",dataUrl:t,filename:i};this.contents.set(e,n),this.notify()}clearContent(e){const t=this.areas.find(i=>i.id===e);t&&P(t)?this.contents.set(e,T(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 n=Math.max(.1,Math.min(5,t));this.contents.set(e,{...i,scale:n}),this.notify()}setImageRotation(e,t){const i=this.contents.get(e);if(!i||i.type!=="image")return;let n=t%360;n<0&&(n+=360),this.contents.set(e,{...i,rotation:n}),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 L{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=n=>{t(n),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 n=Array.from(i);for(const a of n)try{a(t)}catch(s){console.error(`Error in event handler for "${String(e)}":`,s)}}}clear(e){e?this.listeners.delete(e):this.listeners.clear()}listenerCount(e){return this.listeners.get(e)?.size??0}}class j extends L{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()),n=this.getDistance(t,i),a=this.getCenter(t,i),s=n/this.initialPinchDistance;this.emit("pinch",{scale:s,centerX:a.x,centerY:a.y});const r=a.x-this.lastPinchCenter.x,o=a.y-this.lastPinchCenter.y;(Math.abs(r)>.5||Math.abs(o)>.5)&&this.emit("pan",{deltaX:r,deltaY:o}),this.initialPinchDistance=n,this.lastPinchCenter=a}}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,n=t.y-e.y;return Math.sqrt(i*i+n*n)}getCenter(e,t){return{x:(e.x+t.x)/2,y:(e.y+t.y)/2}}}class Y extends L{constructor(e,t,i,n){super(),this.interactionState={mode:"idle"},this.initialPinchZoom=1,this.canvas=e,this.engine=t,this.getContents=i,this.getSelectedAreaId=n,this.gestureHandler=new j,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 s=Math.max(.1,Math.min(5,this.initialPinchZoom*a));this.emit("zoom",{zoom:s})}),this.gestureHandler.on("pan",({deltaX:a,deltaY:s})=>{const r=this.engine.getPan(),o=this.engine.getZoom();this.emit("pan",{pan:{x:r.x+a/o,y:r.y+s/o}})})}getCanvasPosition(e,t){const i=this.canvas.getBoundingClientRect(),n=window.devicePixelRatio||1,a=(e-i.left)*n,s=(t-i.top)*n;return this.engine.screenToCanvas(a,s)}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 s=this.engine.hitTestHandle(e.x,e.y,t);if(s){const r=i.get(t),o=this.engine.getContentBounds(t);if(s==="rotate"&&r?.type==="image"&&o){const d=Math.atan2(e.y-o.centerY,e.x-o.centerX)*(180/Math.PI);return this.interactionState={mode:"rotating",areaId:t,startRotation:r.rotation??0,centerPoint:{x:o.centerX,y:o.centerY},startAngle:d},!0}else if(s!=="rotate"&&r?.type==="image"&&o)return this.interactionState={mode:"scaling",areaId:t,handle:s,startScale:r.scale??1,startMousePos:e,pivotPoint:{x:o.centerX,y:o.centerY}},!0}}const n=this.engine.hitTestContent(e.x,e.y);if(n){if(this.emit("area:select",{areaId:n}),this.engine.isPositioningAllowed(n)){const s=i.get(n)?.offset??{x:0,y:0};return this.interactionState={mode:"dragging",areaId:n,startOffset:s,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:t,startOffset:i,startMousePos:n}=this.interactionState,a=e.x-n.x,s=e.y-n.y;let r={x:i.x+a,y:i.y+s};const o=this.engine.getAreaLocation(t);if(o){const d=o.width/2-10,l=o.height/2-10;r.x=Math.max(-d,Math.min(d,r.x)),r.y=Math.max(-l,Math.min(l,r.y))}this.emit("content:drag",{areaId:t,offset:r});return}if(this.interactionState.mode==="scaling"){const{areaId:t,startScale:i,startMousePos:n,pivotPoint:a}=this.interactionState,s=Math.sqrt(Math.pow(n.x-a.x,2)+Math.pow(n.y-a.y,2)),r=Math.sqrt(Math.pow(e.x-a.x,2)+Math.pow(e.y-a.y,2));if(s>0){const o=r/s,d=Math.max(.1,Math.min(5,i*o));this.emit("content:scale",{areaId:t,scale:d})}return}if(this.interactionState.mode==="rotating"){const{areaId:t,startRotation:i,centerPoint:n,startAngle:a}=this.interactionState,s=Math.atan2(e.y-n.y,e.x-n.x)*(180/Math.PI);let r=i+(s-a);r=r%360,r<0&&(r+=360);const o=[0,90,180,270,360];for(const d of o)if(Math.abs(r-d)<5){r=d===360?0:d;break}this.emit("content:rotate",{areaId:t,rotation:r});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 n=this.engine.hitTestHandle(e.x,e.y,t);n?i=n==="rotate"?"crosshair":n==="nw"||n==="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 X{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}
|
|
1
|
+
(function(S,k){typeof exports=="object"&&typeof module<"u"?k(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],k):(S=typeof globalThis<"u"?globalThis:S||self,k(S.CustomizerSDK={},S.jsxRuntime,S.React))})(this,(function(S,k,x){"use strict";class ${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=new Image;t.crossOrigin="anonymous",t.onload=()=>{this.bgImageElement=t,this.requestRender()},t.onerror=()=>{this.bgImageElement=null,console.error("Failed to load background image:",e.url),this.requestRender()},t.src=e.url}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,n=window.devicePixelRatio||1,s=this.canvas.width/n,a=this.canvas.height/n;return{x:(s/e-t)/2,y:(a/e-i)/2}}requestRender(){this.needsRender=!0}fitToView(){if(!this.artboard)return;const e=0,{width:t,height:i}=this.artboard,n=window.devicePixelRatio||1,s=this.canvas.width/n-e*2,a=this.canvas.height/n-e*2;if(s<=0||a<=0)return;const r=s/t,o=a/i,d=Math.min(r,o),l=this.canvas.width/n,h=this.canvas.height/n,u=(l/d-t)/2,g=(h/d-i)/2;this.zoom=d,this.pan={x:u,y:g},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,n=e/i,s=t/i,a=n/this.zoom-this.pan.x,r=s/this.zoom-this.pan.y;return{x:a,y:r}}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:n}=t,{x:s,y:a,width:r,height:o}=n;if(i.type==="text"){const d=i.offset||{x:0,y:0},l=i.scale??1,h=i.rotation??0,u=r*l,g=o*l,m=s+r/2+d.x,f=a+o/2+d.y;return{x:m-u/2,y:f-g/2,width:u,height:g,rotation:h,centerX:m,centerY:f}}else if(i.type==="image"){const d=this.loadedImages.get(e);if(!d)return null;const l=i.offset||{x:0,y:0},h=i.scale??1,u=i.rotation??0,g=d.naturalWidth/d.naturalHeight,m=r/o;let f,y;g>m?(f=r,y=r/g):(y=o,f=o*g);const v=f*h,C=y*h,p=s+r/2+l.x,I=a+o/2+l.y;return{x:p-v/2,y:I-C/2,width:v,height:C,rotation:u,centerX:p,centerY:I}}return null}hitTestContent(e,t){if(!this.areaSelectionEnabled)return null;for(let i=this.areas.length-1;i>=0;i--){const n=this.areas[i],s=this.getContentBounds(n.id);if(s&&this.isPointInBounds(e,t,s))return n.id}return null}hitTestArea(e,t){if(!this.areaSelectionEnabled)return null;for(let i=this.areas.length-1;i>=0;i--){const n=this.areas[i],{x:s,y:a,width:r,height:o}=n.location;if(e>=s&&e<=s+r&&t>=a&&t<=a+o)return n.id}return null}isPointInBounds(e,t,i){const{centerX:n,centerY:s,width:a,height:r,rotation:o}=i,d=-o*Math.PI/180,l=Math.cos(d),h=Math.sin(d),u=e-n,g=t-s,m=u*l-g*h,f=u*h+g*l;return m>=-a/2&&m<=a/2&&f>=-r/2&&f<=r/2}getContentHandlePositions(e){const t=this.areas.find(v=>v.id===e);if(!t)return null;const i=this.contents.get(e);if(!i)return null;const n=this.getContentBounds(e);if(!n)return null;const{centerX:s,centerY:a,width:r,height:o,rotation:d}=n,l=6/this.zoom,h=30/this.zoom,u=d*Math.PI/180,g=Math.cos(u),m=Math.sin(u),f=(v,C)=>({x:s+v*g-C*m,y:a+v*m+C*g}),y=[];if(i.type==="image"&&t.imageOptions?.allowScaling||i.type==="text"&&t.textOptions?.allowScaling){const v=r/2,C=o/2,p=f(-v,-C);y.push({type:"nw",...p,radius:l});const I=f(v,-C);y.push({type:"ne",...I,radius:l});const E=f(v,C);y.push({type:"se",...E,radius:l});const w=f(-v,C);y.push({type:"sw",...w,radius:l})}if(i.type==="image"&&t.imageOptions?.allowRotation||i.type==="text"&&t.textOptions?.allowRotation){const v=f(0,-o/2-h);y.push({type:"rotate",...v,radius:l})}return y}hitTestHandle(e,t,i){const n=this.getContentHandlePositions(i);if(!n)return null;for(const s of n){const a=e-s.x,r=t-s.y;if(Math.sqrt(a*a+r*r)<=s.radius*2)return s.type}return null}isPositioningAllowed(e){const t=this.areas.find(n=>n.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:n,left:s}=this.safeArea,a=s,r=t,o=this.artboard.width-i,d=this.artboard.height-n;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:g,width:m,height:f,rotation:y}=h,v=y*Math.PI/180,C=Math.abs(Math.cos(v)),p=Math.abs(Math.sin(v)),I=m*C+f*p,E=m*p+f*C,w=u-I/2,R=g-E/2,D=u+I/2,U=g+E/2;(w<a||R<r||D>o||U>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 n of i)n(t)}async exportToPNG(){if(!this.artboard)throw new Error("No artboard configured");const{width:e,height:t,backgroundColor:i}=this.artboard,n=document.createElement("canvas");n.width=e,n.height=t;const s=n.getContext("2d");if(!s)throw new Error("Failed to get 2D context for export");s.fillStyle=i,s.fillRect(0,0,e,t),this.backgroundImage?.position==="behind"&&this.bgImageElement&&this.drawBackgroundImageOnContext(s,e,t);for(const a of this.areas)this.drawAreaContentOnContext(s,a);return this.backgroundImage?.position==="overlay"&&this.bgImageElement&&this.drawBackgroundImageOnContext(s,e,t),new Promise((a,r)=>{n.toBlob(o=>{o?a(o):r(new Error("Failed to create PNG blob"))},"image/png",1)})}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.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:n,height:s,backgroundColor:a}=this.artboard;e.fillStyle=a,e.fillRect(0,0,n,s),e.strokeStyle="#d1d5db",e.lineWidth=1/this.zoom,e.strokeRect(0,0,n,s),this.backgroundImage?.position==="behind"&&this.renderBackgroundImage();for(const r of this.areas)this.renderAreaWithContent(r);this.backgroundImage?.position==="overlay"&&this.renderBackgroundImage(),this.renderSafeAreaGuide();for(const r of this.safeAreaViolations){const o=this.areas.find(g=>g.id===r);if(!o)continue;const{x:d,y:l,width:h,height:u}=o.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 t=this.getContentBounds(e);if(!t){const l=this.areas.find(y=>y.id===e);if(!l)return;const{ctx:h}=this,{x:u,y:g,width:m,height:f}=l.location;h.save(),h.strokeStyle="#3b82f6",h.lineWidth=2/this.zoom,h.setLineDash([]),h.strokeRect(u,g,m,f),h.restore();return}const{ctx:i}=this,{centerX:n,centerY:s,width:a,height:r,rotation:o}=t;i.save(),i.translate(n,s),i.rotate(o*Math.PI/180),i.strokeStyle="#3b82f6",i.lineWidth=2/this.zoom,i.setLineDash([]),i.strokeRect(-a/2,-r/2,a,r),i.restore();const d=this.getContentHandlePositions(e);if(d)for(const l of d){i.save();const h=this.hoveredHandle===l.type,u=h?"#dbeafe":"#ffffff",g=h?"#2563eb":"#3b82f6";if(l.type==="rotate"){const m=this.getRotatedPoint(n,s-r/2,n,s,o);i.beginPath(),i.strokeStyle=g,i.lineWidth=1/this.zoom,i.setLineDash([4/this.zoom,4/this.zoom]),i.moveTo(m.x,m.y),i.lineTo(l.x,l.y),i.stroke(),i.setLineDash([]),i.beginPath(),i.fillStyle=u,i.strokeStyle=g,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=g,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=u,i.strokeStyle=g,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,n,s){const a=s*Math.PI/180,r=Math.cos(a),o=Math.sin(a),d=e-i,l=t-n;return{x:i+d*r-l*o,y:n+d*o+l*r}}renderBackgroundImage(){if(!this.bgImageElement||!this.artboard||!this.backgroundImage)return;const{ctx:e}=this,{opacity:t}=this.backgroundImage,{width:i,height:n}=this.artboard;e.save(),e.globalAlpha=t;const s=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,a=i/n;let r,o,d,l;s>a?(o=n,r=n*s,d=(i-r)/2,l=0):(r=i,o=i/s,d=0,l=(n-o)/2),e.drawImage(this.bgImageElement,d,l,r,o),e.restore()}renderSafeAreaGuide(){if(!this.safeArea||!this.artboard)return;const{ctx:e}=this,{top:t,right:i,bottom:n,left:s}=this.safeArea,a=s,r=t,o=this.artboard.width-s-i,d=this.artboard.height-t-n;o<=0||d<=0||(e.save(),e.strokeStyle="#22c55e",e.lineWidth=1/this.zoom,e.setLineDash([6/this.zoom,4/this.zoom]),e.strokeRect(a,r,o,d),e.setLineDash([]),e.restore())}renderAreaWithContent(e){const{ctx:t}=this,{location:i,backgroundColor:n}=e,{x:s,y:a,width:r,height:o}=i;n&&(t.fillStyle=n,t.fillRect(s,a,r,o));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(s,a,r,o),t.setLineDash([]),t.restore())}renderTextContent(e,t){if(!t.text.trim())return;const{ctx:i}=this,{location:n}=e,{x:s,y:a,width:r,height:o}=n,d=t.offset||{x:0,y:0},l=t.scale??1,h=t.rotation??0,u=s+r/2+d.x,g=a+o/2+d.y;i.save(),i.beginPath(),i.rect(s,a,r,o),i.clip(),i.translate(u,g),i.rotate(h*Math.PI/180),i.scale(l,l),i.font=`${t.size}px ${t.font}, sans-serif`,i.fillStyle=t.color,i.textBaseline="middle";let m;switch(t.align){case"left":i.textAlign="left",m=-r/2+10;break;case"right":i.textAlign="right",m=r/2-10;break;case"center":default:i.textAlign="center",m=0;break}i.fillText(t.text,m,0),i.restore()}renderImageContent(e,t){const i=this.loadedImages.get(e.id);if(!i)return;const{ctx:n}=this,{location:s}=e,{x:a,y:r,width:o,height:d}=s,l=t.offset||{x:0,y:0},h=t.scale??1,u=t.rotation??0;n.save(),n.beginPath(),n.rect(a,r,o,d),n.clip();const g=i.naturalWidth/i.naturalHeight,m=o/d;let f,y;g>m?(f=o,y=o/g):(y=d,f=d*g);const v=f*h,C=y*h,p=a+o/2+l.x,I=r+d/2+l.y;n.translate(p,I),n.rotate(u*Math.PI/180),n.drawImage(i,-v/2,-C/2,v,C),n.restore()}drawBackgroundImageOnContext(e,t,i){if(!this.bgImageElement||!this.backgroundImage)return;const{opacity:n}=this.backgroundImage;e.save(),e.globalAlpha=n;const s=this.bgImageElement.naturalWidth/this.bgImageElement.naturalHeight,a=t/i;let r,o,d,l;s>a?(o=i,r=i*s,d=(t-r)/2,l=0):(r=t,o=t/s,d=0,l=(i-o)/2),e.drawImage(this.bgImageElement,d,l,r,o),e.restore()}drawAreaContentOnContext(e,t){const{location:i,backgroundColor:n}=t,{x:s,y:a,width:r,height:o}=i;n&&(e.fillStyle=n,e.fillRect(s,a,r,o));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},h=d.scale??1,u=d.rotation??0,g=s+r/2+l.x,m=a+o/2+l.y;e.save(),e.beginPath(),e.rect(s,a,r,o),e.clip(),e.translate(g,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 f;switch(d.align){case"left":e.textAlign="left",f=-r/2+10;break;case"right":e.textAlign="right",f=r/2-10;break;case"center":default:e.textAlign="center",f=0;break}e.fillText(d.text,f,0),e.restore()}else if(d.type==="image"){const l=this.loadedImages.get(t.id);if(!l)return;const h=d.offset||{x:0,y:0},u=d.scale??1,g=d.rotation??0;e.save(),e.beginPath(),e.rect(s,a,r,o),e.clip();const m=l.naturalWidth/l.naturalHeight,f=r/o;let y,v;m>f?(y=r,v=r/m):(v=o,y=o*m);const C=y*u,p=v*u,I=s+r/2+h.x,E=a+o/2+h.y;e.translate(I,E),e.rotate(g*Math.PI/180),e.drawImage(l,-C/2,-p/2,C,p),e.restore()}}}destroy(){this.stop(),this.bgImageElement=null,this.loadedImages.clear(),this.loadingImages.clear(),this.eventListeners.clear()}}function T(b){const e=b.textOptions;return{type:"text",text:e?.defaultText||"",font:e?.defaultFont||"Inter",size:e?.defaultSize||24,color:e?.defaultColor||"#000000",align:e?.defaultAlign||"center"}}function P(b){return b.contentType==="text"||b.contentType==="both"}function F(b){return b.contentType==="image"||b.contentType==="both"}const Z=50;class q{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 n=this.state;this.state={...this.state,...e},!i&&!this.isRestoring&&this.saveToHistory(n,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>Z&&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 B{constructor(e){this.listeners=new Set,this.areas=e,this.contents=this.buildInitialContents()}buildInitialContents(){const e=new Map;for(const t of this.areas)P(t)&&e.set(t.id,T(t));return e}getContents(){return this.contents}getContent(e){return this.contents.get(e)}setTextContent(e,t){const i=this.contents.get(e),n=this.areas.find(a=>a.id===e),s=n?T(n):{type:"text",text:"",font:"Inter",size:24,color:"#000000",align:"center"};i?.type==="text"?this.contents.set(e,{...i,...t}):this.contents.set(e,{...s,...t}),this.notify()}setImageContent(e,t,i){const n={type:"image",dataUrl:t,filename:i};this.contents.set(e,n),this.notify()}clearContent(e){const t=this.areas.find(i=>i.id===e);t&&P(t)?this.contents.set(e,T(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 n=Math.max(.1,Math.min(5,t));this.contents.set(e,{...i,scale:n}),this.notify()}setImageRotation(e,t){const i=this.contents.get(e);if(!i||i.type!=="image")return;let n=t%360;n<0&&(n+=360),this.contents.set(e,{...i,rotation:n}),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 L{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=n=>{t(n),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 n=Array.from(i);for(const s of n)try{s(t)}catch(a){console.error(`Error in event handler for "${String(e)}":`,a)}}}clear(e){e?this.listeners.delete(e):this.listeners.clear()}listenerCount(e){return this.listeners.get(e)?.size??0}}class j extends L{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()),n=this.getDistance(t,i),s=this.getCenter(t,i),a=n/this.initialPinchDistance;this.emit("pinch",{scale:a,centerX:s.x,centerY:s.y});const r=s.x-this.lastPinchCenter.x,o=s.y-this.lastPinchCenter.y;(Math.abs(r)>.5||Math.abs(o)>.5)&&this.emit("pan",{deltaX:r,deltaY:o}),this.initialPinchDistance=n,this.lastPinchCenter=s}}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,n=t.y-e.y;return Math.sqrt(i*i+n*n)}getCenter(e,t){return{x:(e.x+t.x)/2,y:(e.y+t.y)/2}}}class Y extends L{constructor(e,t,i,n){super(),this.interactionState={mode:"idle"},this.initialPinchZoom=1,this.canvas=e,this.engine=t,this.getContents=i,this.getSelectedAreaId=n,this.gestureHandler=new j,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:s})=>{const a=Math.max(.1,Math.min(5,this.initialPinchZoom*s));this.emit("zoom",{zoom:a})}),this.gestureHandler.on("pan",({deltaX:s,deltaY:a})=>{const r=this.engine.getPan(),o=this.engine.getZoom();this.emit("pan",{pan:{x:r.x+s/o,y:r.y+a/o}})})}getCanvasPosition(e,t){const i=this.canvas.getBoundingClientRect(),n=window.devicePixelRatio||1,s=(e-i.left)*n,a=(t-i.top)*n;return this.engine.screenToCanvas(s,a)}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 a=this.engine.hitTestHandle(e.x,e.y,t);if(a){const r=i.get(t),o=this.engine.getContentBounds(t);if(a==="rotate"&&r?.type==="image"&&o){const d=Math.atan2(e.y-o.centerY,e.x-o.centerX)*(180/Math.PI);return this.interactionState={mode:"rotating",areaId:t,startRotation:r.rotation??0,centerPoint:{x:o.centerX,y:o.centerY},startAngle:d},!0}else if(a!=="rotate"&&r?.type==="image"&&o)return this.interactionState={mode:"scaling",areaId:t,handle:a,startScale:r.scale??1,startMousePos:e,pivotPoint:{x:o.centerX,y:o.centerY}},!0}}const n=this.engine.hitTestContent(e.x,e.y);if(n){if(this.emit("area:select",{areaId:n}),this.engine.isPositioningAllowed(n)){const a=i.get(n)?.offset??{x:0,y:0};return this.interactionState={mode:"dragging",areaId:n,startOffset:a,startMousePos:e},!0}return!0}const s=this.engine.hitTestArea(e.x,e.y);return s?(this.emit("area:select",{areaId:s}),!1):(this.emit("area:select",{areaId:null}),!1)}processPointerMove(e){if(this.interactionState.mode==="dragging"){const{areaId:t,startOffset:i,startMousePos:n}=this.interactionState,s=e.x-n.x,a=e.y-n.y;let r={x:i.x+s,y:i.y+a};const o=this.engine.getAreaLocation(t);if(o){const d=o.width/2-10,l=o.height/2-10;r.x=Math.max(-d,Math.min(d,r.x)),r.y=Math.max(-l,Math.min(l,r.y))}this.emit("content:drag",{areaId:t,offset:r});return}if(this.interactionState.mode==="scaling"){const{areaId:t,startScale:i,startMousePos:n,pivotPoint:s}=this.interactionState,a=Math.sqrt(Math.pow(n.x-s.x,2)+Math.pow(n.y-s.y,2)),r=Math.sqrt(Math.pow(e.x-s.x,2)+Math.pow(e.y-s.y,2));if(a>0){const o=r/a,d=Math.max(.1,Math.min(5,i*o));this.emit("content:scale",{areaId:t,scale:d})}return}if(this.interactionState.mode==="rotating"){const{areaId:t,startRotation:i,centerPoint:n,startAngle:s}=this.interactionState,a=Math.atan2(e.y-n.y,e.x-n.x)*(180/Math.PI);let r=i+(a-s);r=r%360,r<0&&(r+=360);const o=[0,90,180,270,360];for(const d of o)if(Math.abs(r-d)<5){r=d===360?0:d;break}this.emit("content:rotate",{areaId:t,rotation:r});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 n=this.engine.hitTestHandle(e.x,e.y,t);n?i=n==="rotate"?"crosshair":n==="nw"||n==="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 X{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
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 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 n=`Failed to finalize design (${i.status} ${i.statusText})
|
|
4
4
|
|
|
5
|
-
`;if(i.status===400){const
|
|
5
|
+
`;if(i.status===400){const s=await i.json().catch(()=>({}));n+=s.error||"Invalid design data."}else i.status===429?n+="Rate limit exceeded.":i.status>=500&&(n+="Server error.");throw new Error(n)}return i.json()}catch(i){throw i instanceof Error&&i.message.includes("Failed to fetch")?new Error("Network error finalizing design"):i}}async uploadAsset(e,t){try{const i=new FormData;i.append("shopId",t),i.append("file",e);const n=await fetch(`${this.baseUrl}/assets/upload`,{method:"POST",body:i});if(!n.ok){let s=`Failed to upload asset (${n.status} ${n.statusText})
|
|
6
6
|
|
|
7
|
-
`;if(n.status===400){const s=await n.json().catch(()=>({}));a+=s.error||"Invalid file."}else n.status===413?a+="File too large (max 15MB).":n.status>=500&&(a+="Server error.");throw new Error(a)}return n.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 c(f,e,...t){const i=document.createElement(f);return e&&Object.entries(e).forEach(([n,a])=>{n==="class"?i.className=a:n in i?i[n]=a:i.setAttribute(n,String(a))}),t.forEach(n=>{typeof n=="string"?i.appendChild(document.createTextNode(n)):i.appendChild(n)}),i}function W(f,e){let t=null,i=null;const n=(...a)=>{i=a,t&&clearTimeout(t),t=setTimeout(()=>{i&&f(...i),t=null,i=null},e)};return n.flush=()=>{t&&(clearTimeout(t),t=null),i&&(f(...i),i=null)},n.cancel=()=>{t&&(clearTimeout(t),t=null),i=null},n}class G extends L{constructor(){super(),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 t=c("button",{class:"zoom-btn",title:"Fit to view"},"Fit");t.addEventListener("click",()=>this.emit("zoom-fit",void 0));const i=c("button",{class:"zoom-btn",title:"Zoom in"},"+");i.addEventListener("click",()=>this.emit("zoom-in",void 0)),this.element=c("div",{class:"zoom-toolbar"}),this.element.appendChild(e),this.element.appendChild(t),this.element.appendChild(i),this.element.appendChild(this.percentLabel)}setZoom(e){this.percentLabel.textContent=`${Math.round(e*100)}%`}getElement(){return this.element}}const J=[{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"}],V=[{label:"L",value:"left"},{label:"C",value:"center"},{label:"R",value:"right"}],O=["image/png","image/jpeg","image/webp","image/svg+xml"],K=15*1024*1024;class Q extends L{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 t=P(e),i=F(e);this.mode=i&&!t?"image":"text",this.element=c("div",{class:"area-card"}),this.element.addEventListener("click",o=>{o.target.tagName==="INPUT"||o.target.tagName==="SELECT"||o.target.tagName==="BUTTON"||this.emit("select",{areaId:e.id})});const n=c("div",{class:"area-card-header"}),a=c("div",{class:"area-card-name-row"}),s=c("span",{class:"area-card-name"},e.name);if(a.appendChild(s),e.required){const o=c("span",{class:"area-card-badge"},"Required");a.appendChild(o)}n.appendChild(a);const r=c("button",{class:"area-card-reset-btn",title:"Reset content"},"Reset");if(r.addEventListener("click",o=>{o.stopPropagation(),this.emit("clear",{areaId:e.id})}),n.appendChild(r),this.element.appendChild(n),t&&i){const o=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})}),o.appendChild(d),o.appendChild(l),this.element.appendChild(o)}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 t=this.currentContent?.type;this.currentContent=e,e?.type==="image"&&(this.mode="image"),this.element.querySelectorAll(".mode-btn").forEach(i=>{const n=i;n.classList.toggle("mode-btn-active",n.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,n=i.title==="Left"?"left":i.title==="Center"?"center":"right";i.classList.toggle("align-btn-active",n===e.align)})}renderContent(){this.contentContainer.innerHTML="",this.textInputEl=null,this.fontSelectEl=null,this.sizeInputEl=null,this.colorInputEl=null,this.alignGroupEl=null;const e=P(this.area),t=F(this.area),i=!t||e&&t&&this.mode==="text",n=!e||e&&t&&this.mode==="image";e&&i&&this.renderTextControls(),t&&n&&this.renderImageControls()}renderTextControls(){const e=this.currentContent,t=e?.type==="text"?e:T(this.area),i=c("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 n=c("div",{class:"area-input-row"}),a=c("select",{class:"area-input-font"});for(const l of J){const h=c("option");h.value=l.value,h.textContent=l.label,l.value===t.font&&(h.selected=!0),a.appendChild(h)}a.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{font:a.value}})}),n.appendChild(a),this.fontSelectEl=a;const s=c("input",{class:"area-input-size",type:"number"});s.value=String(t.size),s.min=String(this.area.textOptions?.minSize||8),s.max=String(this.area.textOptions?.maxSize||200),s.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{size:parseInt(s.value,10)||24}})}),n.appendChild(s),this.sizeInputEl=s,this.contentContainer.appendChild(n);const r=c("div",{class:"area-input-row"}),o=c("div",{class:"area-input-align"});for(const l of V){const h=c("button",{class:`align-btn${t.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}}),o.querySelectorAll(".align-btn").forEach(g=>g.classList.remove("align-btn-active")),h.classList.add("align-btn-active")}),o.appendChild(h)}r.appendChild(o),this.alignGroupEl=o;const d=c("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}})}),r.appendChild(d),this.colorInputEl=d,this.contentContainer.appendChild(r)}renderImageControls(){const e=this.currentContent;if(e?.type==="image"){const t=c("div",{class:"area-image-preview"}),i=c("img",{class:"area-image-thumb"});i.src=e.dataUrl,i.alt=e.filename||"Uploaded image",t.appendChild(i);const n=c("div",{class:"area-image-info"});n.appendChild(c("span",{class:"area-image-name"},e.filename||"Image"));const a=c("button",{class:"area-image-remove-btn"},"Remove");a.addEventListener("click",s=>{s.stopPropagation(),this.emit("clear",{areaId:this.area.id})}),n.appendChild(a),t.appendChild(n),this.contentContainer.appendChild(t)}else{const t=c("button",{class:"area-upload-btn"},"Upload Image"),i=c("input",{type:"file"});i.accept=O.join(","),i.style.display="none";const n=c("div",{class:"area-validation-error"});n.style.display="none",i.addEventListener("change",()=>{const a=i.files?.[0];if(!a)return;if(!O.includes(a.type)){const r="Only PNG, JPEG, WebP, and SVG images are accepted";n.textContent=r,n.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:r}),i.value="";return}if(a.size>K){const r="File must be smaller than 15MB";n.textContent=r,n.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:r}),i.value="";return}n.style.display="none";const s=new FileReader;s.onload=()=>{const r=s.result;this.emit("image:change",{areaId:this.area.id,dataUrl:r,filename:a.name})},s.readAsDataURL(a),i.value=""}),t.addEventListener("click",a=>{a.stopPropagation(),i.click()}),this.contentContainer.appendChild(t),this.contentContainer.appendChild(i),this.contentContainer.appendChild(n)}}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,n=e.type==="image"&&this.area.imageOptions?.allowRotation;if(!t&&!i&&!n)return;const a=c("div",{class:"area-card-divider"});this.transformContainer.appendChild(a);const s=c("div",{class:"transform-header"});s.appendChild(c("span",{class:"transform-title"},"Transform"));const r=c("button",{class:"transform-reset-btn"},"Reset");r.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}),n&&this.emit("rotation:change",{areaId:this.area.id,rotation:0})}),s.appendChild(r),this.transformContainer.appendChild(s);const o=e.offset??{x:0,y:0};if(t){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(o.x)),h.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...o,x:parseInt(h.value,10)||0}})});const u=c("label",{class:"transform-label"},"Y"),g=c("input",{class:"transform-input",type:"number"});g.value=String(Math.round(o.y)),g.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...o,y:parseInt(g.value,10)||0}})}),d.appendChild(l),d.appendChild(h),d.appendChild(u),d.appendChild(g),this.transformContainer.appendChild(d)}if(i&&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 g=parseInt(u.value,10)/100;h.textContent=`Scale: ${Math.round(g*100)}%`,this.emit("scale:change",{areaId:this.area.id,scale:g})}),l.appendChild(h),l.appendChild(u),this.transformContainer.appendChild(l)}if(n&&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 g=parseInt(u.value,10);h.textContent=`Rotation: ${g}°`,this.emit("rotation:change",{areaId:this.area.id,rotation:g})}),l.appendChild(h),l.appendChild(u),this.transformContainer.appendChild(l)}}getElement(){return this.element}}class _ extends L{constructor(e){super(),this.cards=new Map,this.isMobile=!1,this.boundEscapeHandler=n=>{n.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 t=c("div",{class:"area-panel-header"});t.appendChild(c("span",{class:"area-panel-title"},"Customize"));const i=c("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=c("div",{class:"area-panel-content"}),e.length===0){const n=c("p",{class:"area-panel-empty"},"No customization areas defined.");this.panelContent.appendChild(n)}else for(const n of e){const a=new Q(n);this.cards.set(n.id,a),a.on("text:change",s=>this.emit("text:change",s)),a.on("image:change",s=>this.emit("image:change",s)),a.on("clear",s=>this.emit("clear",s)),a.on("select",s=>this.emit("select",s)),a.on("offset:change",s=>this.emit("offset:change",s)),a.on("scale:change",s=>this.emit("scale:change",s)),a.on("rotation:change",s=>this.emit("rotation:change",s)),a.on("validation:error",s=>this.emit("validation:error",s)),this.panelContent.appendChild(a.getElement())}this.element.appendChild(this.panelContent)}setContents(e){for(const[t,i]of this.cards)i.setContent(e.get(t))}setSelectedArea(e){for(const[t,i]of this.cards){const n=t===e;i.setSelected(n),i.getElement().style.display=n?"":"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 ee{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 n of this.commands)if(this.matchesCommand(t,n)){t.preventDefault(),n.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 te(){return/Mac|iPod|iPhone|iPad/.test(navigator.platform)}function ie(){return te()?"meta":"ctrl"}function ne(f,e,t,i){const n=f/t,a=e/i;return Math.min(n,a)}function ae(f,e,t=8,i=10,n=300){const a=ne(f,e,t,i),s=a>=n;return{actualDPI:Math.round(a),targetDPI:n,width:f,height:e,passed:s,warning:s?void 0:`Image resolution is ${Math.round(a)} DPI at 100% scale. For best print quality, use images with at least ${n} DPI. This may result in pixelated or blurry prints.`}}const A=':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}.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}}',re="2.0.0",se=768;class oe extends HTMLElement{constructor(){super(),this.resizeObserver=null,this.isReady=!1,this.currentTemplate=null,this.loadingTemplateId=null,this.isMobileLayout=!1,this.subscriptions=[],this.storageUsage=null,this.storageUsageLastRefresh=0,this.emitChange=W(()=>{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","theme","mode","store-id","api-url"]}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)}getDesign(){if(!this.currentTemplate)throw new Error("No design loaded");return{templateId:this.currentTemplate.metadata.id,engineVersion:re,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.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}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)}async finalize(){const e=this.engine.checkSafeAreaViolations();if(e.length>0){const a=this.currentTemplate?.areas||[],s=e.map(r=>a.find(o=>o.id===r)?.name||r).join(", ");if(!await this.showConfirmation("Content outside safe area",`Content in ${s} 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.getDesign(),i=this.getAttribute("store-id")||"demo-shop",n=await this.apiClient.finalizeDesign(i,t);return this.dispatchEvent(new CustomEvent("customizer:finalize",{detail:{designId:n.designId,proofUrl:n.proofUrl,templateId:t.templateId,designJson:t,status:n.status},bubbles:!0,composed:!0})),n}renderLoading(){const e=document.createElement("style");e.textContent=A,this.shadow.appendChild(e);const t=c("div",{class:"loading-container"},c("div",{class:"loading-spinner"}));this.shadow.appendChild(t)}async initialize(){const e=this.getAttribute("api-url")||"http://localhost:4000/public";this.apiClient=new X(e);const t=this.getAttribute("template-id");if(!t)throw new Error("template-id attribute is required");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 t=await this.apiClient.getTemplate(e);if(this.loadingTemplateId!==e)return;this.currentTemplate=t;const i=t.areas||[];this.contentManager=new B(i);const n={template:t,contents:this.contentManager.toJSON(),selectedAreaId:null,zoom:1,pan:{x:0,y:0},isDirty:!1,warnings:[]};this.stateManager=new q(n),this.buildUI(t,i),this.engine=new Z(this.canvas),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()),t.backgroundImage&&this.engine.setBackgroundImage(t.backgroundImage);const a=t.artboard.safeArea??t.safeArea;a&&this.engine.setSafeArea(a),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 Y(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 ee(this),this.setupKeyboardShortcuts(),this.inputPanel.setContents(this.contentManager.getContents());const s=this.getAttribute("store-id");s&&this.apiClient.getStorageUsage(s).then(r=>{this.storageUsage=r,r.usagePercent>=100?this.showToast("Storage quota exceeded. Upgrade your plan for more storage.","error"):r.usagePercent>=80&&this.showToast(`Storage ${r.usagePercent}% full. Consider upgrading your plan.`,"warning")}).catch(()=>{}),this.loadingTemplateId=null}catch(t){throw this.loadingTemplateId=null,new Error(`Failed to load template: ${t.message}`)}}}buildUI(e,t){this.shadow.innerHTML="";const i=document.createElement("style");i.textContent=A,this.shadow.appendChild(i),this.container=c("div",{class:"editor-container"}),this.zoomToolbar=new G,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()));const n=c("div",{class:"canvas-area"});n.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),n.appendChild(this.canvasWrapper),this.inputPanel=new _(t),this.wireInputPanelEvents();const a=c("div",{class:"editor-main"});a.appendChild(n),a.appendChild(this.inputPanel.getBackdrop()),a.appendChild(this.inputPanel.getElement()),this.container.appendChild(a),this.shadow.appendChild(this.container),this.resizeObserver=new ResizeObserver(s=>{for(const r of s)r.target===this.canvasWrapper&&this.handleCanvasResize(r.contentRect),r.target===this.container&&this.handleLayoutResize(r.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 n=this.contentManager.getContents();let a=0;for(const[r,o]of n)o.type==="image"&&r!==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 s=this.currentTemplate?.areas.find(r=>r.id===e);if(s&&this.currentTemplate){const r=this.currentTemplate.print.targetDpi;try{const o=new Image,d=await new Promise((g,m)=>{o.onload=()=>g({width:o.naturalWidth,height:o.naturalHeight}),o.onerror=()=>m(new Error("Failed to load image")),o.src=t}),l=s.location.width/r,h=s.location.height/r,u=ae(d.width,d.height,l,h,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{}}if(this.contentManager.setImageContent(e,t,i),this.saveContentState(),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.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())}handleCanvasResize(e){const{width:t,height:i}=e;if(t===0||i===0)return;const n=window.devicePixelRatio||1;this.canvas.width=t*n,this.canvas.height=i*n,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<se;t!==this.isMobileLayout&&(this.isMobileLayout=t,this.container.classList.toggle("layout-desktop",!t),this.container.classList.toggle("layout-mobile",t),this.inputPanel.setMobile(t))}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.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=ie();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,t="info"){const i=c("div",{class:`editor-toast editor-toast-${t}`},e);this.shadow.appendChild(i),setTimeout(()=>i.remove(),4e3)}showConfirmation(e,t,i="Cancel",n="Continue"){return new Promise(a=>{const s=c("div",{class:"editor-modal-overlay"}),r=c("div",{class:"editor-modal"});r.appendChild(c("div",{class:"editor-modal-title"},e)),r.appendChild(c("div",{class:"editor-modal-body"},t));const o=c("div",{class:"editor-modal-actions"}),d=c("button",{class:"editor-modal-btn"},i);d.addEventListener("click",()=>{s.remove(),a(!1)});const l=c("button",{class:"editor-modal-btn editor-modal-btn-primary"},n);l.addEventListener("click",()=>{s.remove(),a(!0)}),o.appendChild(d),o.appendChild(l),r.appendChild(o),s.appendChild(r),s.addEventListener("click",h=>{h.target===s&&(s.remove(),a(!1))}),this.shadow.appendChild(s)})}showError(e,t){this.shadow.innerHTML="";const i=document.createElement("style");i.textContent=A,this.shadow.appendChild(i);const n=c("div",{class:"error-container"});n.appendChild(c("div",{class:"error-title"},e)),n.appendChild(c("div",{class:"error-message"},t?.message??"")),this.shadow.appendChild(n),this.dispatchEvent(new CustomEvent("error",{detail:{message:e,error:t},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}}customElements.get("customizer-editor")||customElements.define("customizer-editor",oe);function H(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"}`);const i=document.createElement("customizer-editor");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),i.style.width="100%",i.style.height="100%";const n=[],a=(r,o)=>{i.addEventListener(r,o),n.push({event:r,handler:o})};t.innerHTML="",t.appendChild(i);const s={getDesign(){if(typeof i.getDesign!="function")throw new Error("Editor not ready: getDesign method not available");return i.getDesign()},setDesign(r){if(typeof i.setDesign!="function")throw new Error("Editor not ready: setDesign method not available");i.setDesign(r)},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 r=this.getDesign(),o=e.apiUrl||"https://api.varianta.io";try{const d=await fetch(`${o}/public/finalize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({templateId:e.templateId,designJson:r})});if(!d.ok){const h=await d.json().catch(()=>({message:"Finalization failed"}));throw new Error(h.message||"Finalization failed")}const l=await d.json();return e.onFinalize&&e.onFinalize(l),l}catch(d){const l={code:"FINALIZE_ERROR",message:d instanceof Error?d.message:"Unknown error",details:d};throw e.onError&&e.onError(l),d}},addTextLayer(r){if(typeof i.addTextLayer!="function")throw new Error("Editor not ready: addTextLayer method not available");i.addTextLayer(r)},async addImageLayer(r){if(typeof i.addImageLayer!="function")throw new Error("Editor not ready: addImageLayer method not available");return i.addImageLayer(r)},removeLayer(r){if(typeof i.removeLayer!="function")throw new Error("Editor not ready: removeLayer method not available");i.removeLayer(r)},selectLayer(r){if(typeof i.selectLayer!="function")throw new Error("Editor not ready: selectLayer method not available");i.selectLayer(r)},getSelectedLayerId(){return typeof i.getSelectedLayerId!="function"?null:i.getSelectedLayerId()},setTheme(r){i.setAttribute("theme",r)},setMode(r){i.setAttribute("mode",r)},destroy(){n.forEach(({event:r,handler:o})=>{i.removeEventListener(r,o)}),i.parentNode&&i.parentNode.removeChild(i)},getElement(){return i}};return e.onReady&&a("ready",(()=>{e.onReady?.(s)})),e.onChange&&a("change",(r=>{const o=r.detail.design;e.onChange?.(o)})),e.onLayerSelect&&a("layer:select",(r=>{e.onLayerSelect?.(r.detail.layerId)})),e.onLayerAdd&&a("layer:add",(r=>{e.onLayerAdd?.(r.detail.layerId)})),e.onLayerRemove&&a("layer:remove",(r=>{e.onLayerRemove?.(r.detail.layerId)})),e.onLayerUpdate&&a("layer:update",(r=>{e.onLayerUpdate?.(r.detail.layerId)})),e.onError&&a("error",(r=>{const o=r.detail.error;e.onError?.(o)})),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:",s)),s}const N=x.forwardRef((f,e)=>{const{templateId:t,apiUrl:i,theme:n="light",mode:a="edit",debug:s=!1,className:r,style:o,initialDesign:d,onReady:l,onChange:h,onLayerSelect:u,onLayerAdd:g,onLayerRemove:m,onLayerUpdate:b,onError:y,onFinalize:v}=f,C=x.useRef(null),p=x.useRef(null),I=x.useRef({onReady:l,onChange:h,onLayerSelect:u,onLayerAdd:g,onLayerRemove:m,onLayerUpdate:b,onError:y,onFinalize:v});return x.useEffect(()=>{I.current={onReady:l,onChange:h,onLayerSelect:u,onLayerAdd:g,onLayerRemove:m,onLayerUpdate:b,onError:y,onFinalize:v}},[l,h,u,g,m,b,y,v]),x.useEffect(()=>{if(!C.current)return;const E={templateId:t,apiUrl:i,theme:n,mode:a,debug:s,initialDesign:d,onReady:()=>I.current.onReady?.(),onChange:w=>I.current.onChange?.(w),onLayerSelect:w=>I.current.onLayerSelect?.(w),onLayerAdd:w=>I.current.onLayerAdd?.(w),onLayerRemove:w=>I.current.onLayerRemove?.(w),onLayerUpdate:w=>I.current.onLayerUpdate?.(w),onError:w=>I.current.onError?.(w),onFinalize:w=>I.current.onFinalize?.(w)};try{const w=H(C.current,E);p.current=w}catch(w){console.error("[Customizer React] Failed to initialize:",w),I.current.onError?.({code:"INIT_ERROR",message:w instanceof Error?w.message:"Unknown error",details:w})}return()=>{p.current&&(p.current.destroy(),p.current=null)}},[t,i,s,d]),x.useEffect(()=>{p.current&&p.current.setTheme(n)},[n]),x.useEffect(()=>{p.current&&p.current.setMode(a)},[a]),x.useImperativeHandle(e,()=>({getDesign:()=>{if(!p.current)throw new Error("Editor not initialized");return p.current.getDesign()},setDesign:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.setDesign(E)},undo:()=>{if(!p.current)throw new Error("Editor not initialized");p.current.undo()},redo:()=>{if(!p.current)throw new Error("Editor not initialized");p.current.redo()},canUndo:()=>p.current?p.current.canUndo():!1,canRedo:()=>p.current?p.current.canRedo():!1,finalize:async()=>{if(!p.current)throw new Error("Editor not initialized");return p.current.finalize()},addTextLayer:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.addTextLayer(E)},addImageLayer:async E=>{if(!p.current)throw new Error("Editor not initialized");return p.current.addImageLayer(E)},removeLayer:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.removeLayer(E)},selectLayer:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.selectLayer(E)},getSelectedLayerId:()=>p.current?p.current.getSelectedLayerId():null,setTheme:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.setTheme(E)},setMode:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.setMode(E)},destroy:()=>{p.current&&(p.current.destroy(),p.current=null)},getElement:()=>{if(!p.current)throw new Error("Editor not initialized");return p.current.getElement()}}),[]),k.jsx("div",{ref:C,className:r,style:{width:"100%",height:"100%",...o}})});N.displayName="Customizer";function de(f={}){const{autoSave:e=!1,autoSaveKey:t="customizer-design",autoSaveDebounce:i=1e3}=f,n=x.useRef(null),[a,s]=x.useState(null),[r,o]=x.useState(!1),[d,l]=x.useState(!1),[h,u]=x.useState(null),[g,m]=x.useState(!1),[b,y]=x.useState(null),v=x.useRef(void 0);x.useEffect(()=>{if(!(!e||!a))return v.current&&clearTimeout(v.current),v.current=setTimeout(()=>{try{localStorage.setItem(t,JSON.stringify(a))}catch(z){console.error("[useCustomizer] Auto-save failed:",z)}},i),()=>{v.current&&clearTimeout(v.current)}},[a,e,t,i]);const C=x.useCallback(()=>{if(!n.current)return null;try{return n.current.getDesign()}catch(z){return console.error("[useCustomizer] getDesign failed:",z),null}},[]),p=x.useCallback(z=>{if(n.current)try{n.current.setDesign(z),s(z)}catch(M){console.error("[useCustomizer] setDesign failed:",M)}},[]),I=x.useCallback(()=>{if(n.current)try{n.current.undo(),o(n.current.canUndo()),l(n.current.canRedo())}catch(z){console.error("[useCustomizer] undo failed:",z)}},[]),E=x.useCallback(()=>{if(n.current)try{n.current.redo(),o(n.current.canUndo()),l(n.current.canRedo())}catch(z){console.error("[useCustomizer] redo failed:",z)}},[]),w=x.useCallback(async()=>{if(!n.current||g)return null;m(!0);try{const z=await n.current.finalize();return y(z),z}catch(z){return console.error("[useCustomizer] finalize failed:",z),null}finally{m(!1)}},[g]),R=x.useCallback(z=>{if(n.current)try{n.current.addTextLayer(z)}catch(M){console.error("[useCustomizer] addTextLayer failed:",M)}},[]),D=x.useCallback(async z=>{if(n.current)try{await n.current.addImageLayer(z)}catch(M){console.error("[useCustomizer] addImageLayer failed:",M)}},[]),U=x.useCallback(z=>{if(n.current)try{n.current.removeLayer(z)}catch(M){console.error("[useCustomizer] removeLayer failed:",M)}},[]),le=x.useCallback(z=>{if(n.current)try{n.current.selectLayer(z),u(z)}catch(M){console.error("[useCustomizer] selectLayer failed:",M)}},[]);return{customizerRef:n,design:a,canUndo:r,canRedo:d,selectedLayerId:h,isFinalizing:g,finalizeResult:b,getDesign:C,setDesign:p,undo:I,redo:E,finalize:w,addTextLayer:R,addImageLayer:D,removeLayer:U,selectLayer:le}}S.Customizer=N,S.initCustomizer=H,S.useCustomizer=de,Object.defineProperty(S,Symbol.toStringTag,{value:"Module"})}));
|
|
7
|
+
`;if(n.status===400){const a=await n.json().catch(()=>({}));s+=a.error||"Invalid file."}else n.status===413?s+="File too large (max 15MB).":n.status>=500&&(s+="Server error.");throw new Error(s)}return n.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 c(b,e,...t){const i=document.createElement(b);return e&&Object.entries(e).forEach(([n,s])=>{n==="class"?i.className=s:n in i?i[n]=s:i.setAttribute(n,String(s))}),t.forEach(n=>{typeof n=="string"?i.appendChild(document.createTextNode(n)):i.appendChild(n)}),i}function W(b,e){let t=null,i=null;const n=(...s)=>{i=s,t&&clearTimeout(t),t=setTimeout(()=>{i&&b(...i),t=null,i=null},e)};return n.flush=()=>{t&&(clearTimeout(t),t=null),i&&(b(...i),i=null)},n.cancel=()=>{t&&(clearTimeout(t),t=null),i=null},n}class G extends L{constructor(){super(),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 t=c("button",{class:"zoom-btn",title:"Fit to view"},"Fit");t.addEventListener("click",()=>this.emit("zoom-fit",void 0));const i=c("button",{class:"zoom-btn",title:"Zoom in"},"+");i.addEventListener("click",()=>this.emit("zoom-in",void 0)),this.element=c("div",{class:"zoom-toolbar"}),this.element.appendChild(e),this.element.appendChild(t),this.element.appendChild(i),this.element.appendChild(this.percentLabel)}setZoom(e){this.percentLabel.textContent=`${Math.round(e*100)}%`}getElement(){return this.element}}const J=[{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"}],V=[{label:"L",value:"left"},{label:"C",value:"center"},{label:"R",value:"right"}],O=["image/png","image/jpeg","image/webp","image/svg+xml"],K=15*1024*1024;class Q extends L{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 t=P(e),i=F(e);this.mode=i&&!t?"image":"text",this.element=c("div",{class:"area-card"}),this.element.addEventListener("click",o=>{o.target.tagName==="INPUT"||o.target.tagName==="SELECT"||o.target.tagName==="BUTTON"||this.emit("select",{areaId:e.id})});const n=c("div",{class:"area-card-header"}),s=c("div",{class:"area-card-name-row"}),a=c("span",{class:"area-card-name"},e.name);if(s.appendChild(a),e.required){const o=c("span",{class:"area-card-badge"},"Required");s.appendChild(o)}n.appendChild(s);const r=c("button",{class:"area-card-reset-btn",title:"Reset content"},"Reset");if(r.addEventListener("click",o=>{o.stopPropagation(),this.emit("clear",{areaId:e.id})}),n.appendChild(r),this.element.appendChild(n),t&&i){const o=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})}),o.appendChild(d),o.appendChild(l),this.element.appendChild(o)}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 t=this.currentContent?.type;this.currentContent=e,e?.type==="image"&&(this.mode="image"),this.element.querySelectorAll(".mode-btn").forEach(i=>{const n=i;n.classList.toggle("mode-btn-active",n.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,n=i.title==="Left"?"left":i.title==="Center"?"center":"right";i.classList.toggle("align-btn-active",n===e.align)})}renderContent(){this.contentContainer.innerHTML="",this.textInputEl=null,this.fontSelectEl=null,this.sizeInputEl=null,this.colorInputEl=null,this.alignGroupEl=null;const e=P(this.area),t=F(this.area),i=!t||e&&t&&this.mode==="text",n=!e||e&&t&&this.mode==="image";e&&i&&this.renderTextControls(),t&&n&&this.renderImageControls()}renderTextControls(){const e=this.currentContent,t=e?.type==="text"?e:T(this.area),i=c("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 n=c("div",{class:"area-input-row"}),s=c("select",{class:"area-input-font"});for(const l of J){const h=c("option");h.value=l.value,h.textContent=l.label,l.value===t.font&&(h.selected=!0),s.appendChild(h)}s.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{font:s.value}})}),n.appendChild(s),this.fontSelectEl=s;const a=c("input",{class:"area-input-size",type:"number"});a.value=String(t.size),a.min=String(this.area.textOptions?.minSize||8),a.max=String(this.area.textOptions?.maxSize||200),a.addEventListener("change",()=>{this.emit("text:change",{areaId:this.area.id,updates:{size:parseInt(a.value,10)||24}})}),n.appendChild(a),this.sizeInputEl=a,this.contentContainer.appendChild(n);const r=c("div",{class:"area-input-row"}),o=c("div",{class:"area-input-align"});for(const l of V){const h=c("button",{class:`align-btn${t.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}}),o.querySelectorAll(".align-btn").forEach(g=>g.classList.remove("align-btn-active")),h.classList.add("align-btn-active")}),o.appendChild(h)}r.appendChild(o),this.alignGroupEl=o;const d=c("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}})}),r.appendChild(d),this.colorInputEl=d,this.contentContainer.appendChild(r)}renderImageControls(){const e=this.currentContent;if(e?.type==="image"){const t=c("div",{class:"area-image-preview"}),i=c("img",{class:"area-image-thumb"});i.src=e.dataUrl,i.alt=e.filename||"Uploaded image",t.appendChild(i);const n=c("div",{class:"area-image-info"});n.appendChild(c("span",{class:"area-image-name"},e.filename||"Image"));const s=c("button",{class:"area-image-remove-btn"},"Remove");s.addEventListener("click",a=>{a.stopPropagation(),this.emit("clear",{areaId:this.area.id})}),n.appendChild(s),t.appendChild(n),this.contentContainer.appendChild(t)}else{const t=c("button",{class:"area-upload-btn"},"Upload Image"),i=c("input",{type:"file"});i.accept=O.join(","),i.style.display="none";const n=c("div",{class:"area-validation-error"});n.style.display="none",i.addEventListener("change",()=>{const s=i.files?.[0];if(!s)return;if(!O.includes(s.type)){const r="Only PNG, JPEG, WebP, and SVG images are accepted";n.textContent=r,n.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:r}),i.value="";return}if(s.size>K){const r="File must be smaller than 15MB";n.textContent=r,n.style.display="block",this.emit("validation:error",{areaId:this.area.id,message:r}),i.value="";return}n.style.display="none";const a=new FileReader;a.onload=()=>{const r=a.result;this.emit("image:change",{areaId:this.area.id,dataUrl:r,filename:s.name})},a.readAsDataURL(s),i.value=""}),t.addEventListener("click",s=>{s.stopPropagation(),i.click()}),this.contentContainer.appendChild(t),this.contentContainer.appendChild(i),this.contentContainer.appendChild(n)}}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,n=e.type==="image"&&this.area.imageOptions?.allowRotation;if(!t&&!i&&!n)return;const s=c("div",{class:"area-card-divider"});this.transformContainer.appendChild(s);const a=c("div",{class:"transform-header"});a.appendChild(c("span",{class:"transform-title"},"Transform"));const r=c("button",{class:"transform-reset-btn"},"Reset");r.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}),n&&this.emit("rotation:change",{areaId:this.area.id,rotation:0})}),a.appendChild(r),this.transformContainer.appendChild(a);const o=e.offset??{x:0,y:0};if(t){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(o.x)),h.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...o,x:parseInt(h.value,10)||0}})});const u=c("label",{class:"transform-label"},"Y"),g=c("input",{class:"transform-input",type:"number"});g.value=String(Math.round(o.y)),g.addEventListener("change",()=>{this.emit("offset:change",{areaId:this.area.id,offset:{...o,y:parseInt(g.value,10)||0}})}),d.appendChild(l),d.appendChild(h),d.appendChild(u),d.appendChild(g),this.transformContainer.appendChild(d)}if(i&&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 g=parseInt(u.value,10)/100;h.textContent=`Scale: ${Math.round(g*100)}%`,this.emit("scale:change",{areaId:this.area.id,scale:g})}),l.appendChild(h),l.appendChild(u),this.transformContainer.appendChild(l)}if(n&&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 g=parseInt(u.value,10);h.textContent=`Rotation: ${g}°`,this.emit("rotation:change",{areaId:this.area.id,rotation:g})}),l.appendChild(h),l.appendChild(u),this.transformContainer.appendChild(l)}}getElement(){return this.element}}class _ extends L{constructor(e){super(),this.cards=new Map,this.isMobile=!1,this.boundEscapeHandler=n=>{n.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 t=c("div",{class:"area-panel-header"});t.appendChild(c("span",{class:"area-panel-title"},"Customize"));const i=c("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=c("div",{class:"area-panel-content"}),e.length===0){const n=c("p",{class:"area-panel-empty"},"No customization areas defined.");this.panelContent.appendChild(n)}else for(const n of e){const s=new Q(n);this.cards.set(n.id,s),s.on("text:change",a=>this.emit("text:change",a)),s.on("image:change",a=>this.emit("image:change",a)),s.on("clear",a=>this.emit("clear",a)),s.on("select",a=>this.emit("select",a)),s.on("offset:change",a=>this.emit("offset:change",a)),s.on("scale:change",a=>this.emit("scale:change",a)),s.on("rotation:change",a=>this.emit("rotation:change",a)),s.on("validation:error",a=>this.emit("validation:error",a)),this.panelContent.appendChild(s.getElement())}this.element.appendChild(this.panelContent)}setContents(e){for(const[t,i]of this.cards)i.setContent(e.get(t))}setSelectedArea(e){for(const[t,i]of this.cards){const n=t===e;i.setSelected(n),i.getElement().style.display=n?"":"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 ee{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 n of this.commands)if(this.matchesCommand(t,n)){t.preventDefault(),n.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 te(){return/Mac|iPod|iPhone|iPad/.test(navigator.platform)}function ie(){return te()?"meta":"ctrl"}function ne(b,e,t,i){const n=b/t,s=e/i;return Math.min(n,s)}function se(b,e,t=8,i=10,n=300){const s=ne(b,e,t,i),a=s>=n;return{actualDPI:Math.round(s),targetDPI:n,width:b,height:e,passed:a,warning:a?void 0:`Image resolution is ${Math.round(s)} DPI at 100% scale. For best print quality, use images with at least ${n} DPI. This may result in pixelated or blurry prints.`}}const A=':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}.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}}',re="2.0.0",ae=768;class oe extends HTMLElement{constructor(){super(),this.resizeObserver=null,this.isReady=!1,this.currentTemplate=null,this.loadingTemplateId=null,this.isMobileLayout=!1,this.subscriptions=[],this.storageUsage=null,this.storageUsageLastRefresh=0,this.emitChange=W(()=>{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","theme","mode","store-id","api-url"]}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)}getDesign(){if(!this.currentTemplate)throw new Error("No design loaded");return{templateId:this.currentTemplate.metadata.id,engineVersion:re,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.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}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)}async finalize(){const e=this.engine.checkSafeAreaViolations();if(e.length>0){const s=this.currentTemplate?.areas||[],a=e.map(r=>s.find(o=>o.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 t=this.getDesign(),i=this.getAttribute("store-id")||"demo-shop",n=await this.apiClient.finalizeDesign(i,t);return this.dispatchEvent(new CustomEvent("customizer:finalize",{detail:{designId:n.designId,proofUrl:n.proofUrl,templateId:t.templateId,designJson:t,status:n.status},bubbles:!0,composed:!0})),n}renderLoading(){const e=document.createElement("style");e.textContent=A,this.shadow.appendChild(e);const t=c("div",{class:"loading-container"},c("div",{class:"loading-spinner"}));this.shadow.appendChild(t)}async initialize(){const e=this.getAttribute("api-url")||"http://localhost:4000/public";this.apiClient=new X(e);const t=this.getAttribute("template-id");if(!t)throw new Error("template-id attribute is required");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 t=await this.apiClient.getTemplate(e);if(this.loadingTemplateId!==e)return;this.currentTemplate=t;const i=t.areas||[];this.contentManager=new B(i);const n={template:t,contents:this.contentManager.toJSON(),selectedAreaId:null,zoom:1,pan:{x:0,y:0},isDirty:!1,warnings:[]};this.stateManager=new q(n),this.buildUI(t,i),this.engine=new $(this.canvas),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()),t.backgroundImage&&this.engine.setBackgroundImage(t.backgroundImage);const s=t.artboard.safeArea??t.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 Y(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 ee(this),this.setupKeyboardShortcuts(),this.inputPanel.setContents(this.contentManager.getContents());const a=this.getAttribute("store-id");a&&this.apiClient.getStorageUsage(a).then(r=>{this.storageUsage=r,r.usagePercent>=100?this.showToast("Storage quota exceeded. Upgrade your plan for more storage.","error"):r.usagePercent>=80&&this.showToast(`Storage ${r.usagePercent}% full. Consider upgrading your plan.`,"warning")}).catch(()=>{}),this.loadingTemplateId=null}catch(t){throw this.loadingTemplateId=null,new Error(`Failed to load template: ${t.message}`)}}}buildUI(e,t){this.shadow.innerHTML="";const i=document.createElement("style");i.textContent=A,this.shadow.appendChild(i),this.container=c("div",{class:"editor-container"}),this.zoomToolbar=new G,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()));const n=c("div",{class:"canvas-area"});n.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),n.appendChild(this.canvasWrapper),this.inputPanel=new _(t),this.wireInputPanelEvents();const s=c("div",{class:"editor-main"});s.appendChild(n),s.appendChild(this.inputPanel.getBackdrop()),s.appendChild(this.inputPanel.getElement()),this.container.appendChild(s),this.shadow.appendChild(this.container),this.resizeObserver=new ResizeObserver(a=>{for(const r of a)r.target===this.canvasWrapper&&this.handleCanvasResize(r.contentRect),r.target===this.container&&this.handleLayoutResize(r.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 n=this.contentManager.getContents();let s=0;for(const[r,o]of n)o.type==="image"&&r!==e&&s++;if(s>=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 a=this.currentTemplate?.areas.find(r=>r.id===e);if(a&&this.currentTemplate){const r=this.currentTemplate.print.targetDpi;try{const o=new Image,d=await new Promise((g,m)=>{o.onload=()=>g({width:o.naturalWidth,height:o.naturalHeight}),o.onerror=()=>m(new Error("Failed to load image")),o.src=t}),l=a.location.width/r,h=a.location.height/r,u=se(d.width,d.height,l,h,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{}}if(this.contentManager.setImageContent(e,t,i),this.saveContentState(),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.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())}handleCanvasResize(e){const{width:t,height:i}=e;if(t===0||i===0)return;const n=window.devicePixelRatio||1;this.canvas.width=t*n,this.canvas.height=i*n,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<ae;t!==this.isMobileLayout&&(this.isMobileLayout=t,this.container.classList.toggle("layout-desktop",!t),this.container.classList.toggle("layout-mobile",t),this.inputPanel.setMobile(t))}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.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=ie();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,t="info"){const i=c("div",{class:`editor-toast editor-toast-${t}`},e);this.shadow.appendChild(i),setTimeout(()=>i.remove(),4e3)}showConfirmation(e,t,i="Cancel",n="Continue"){return new Promise(s=>{const a=c("div",{class:"editor-modal-overlay"}),r=c("div",{class:"editor-modal"});r.appendChild(c("div",{class:"editor-modal-title"},e)),r.appendChild(c("div",{class:"editor-modal-body"},t));const o=c("div",{class:"editor-modal-actions"}),d=c("button",{class:"editor-modal-btn"},i);d.addEventListener("click",()=>{a.remove(),s(!1)});const l=c("button",{class:"editor-modal-btn editor-modal-btn-primary"},n);l.addEventListener("click",()=>{a.remove(),s(!0)}),o.appendChild(d),o.appendChild(l),r.appendChild(o),a.appendChild(r),a.addEventListener("click",h=>{h.target===a&&(a.remove(),s(!1))}),this.shadow.appendChild(a)})}showError(e,t){this.shadow.innerHTML="";const i=document.createElement("style");i.textContent=A,this.shadow.appendChild(i);const n=c("div",{class:"error-container"});n.appendChild(c("div",{class:"error-title"},e)),n.appendChild(c("div",{class:"error-message"},t?.message??"")),this.shadow.appendChild(n),this.dispatchEvent(new CustomEvent("error",{detail:{message:e,error:t},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}}customElements.get("customizer-editor")||customElements.define("customizer-editor",oe);function H(b,e){const t=typeof b=="string"?document.querySelector(b):b;if(!t)throw new Error(`Container not found: ${typeof b=="string"?b:"provided element is null"}`);const i=document.createElement("customizer-editor");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),i.style.width="100%",i.style.height="100%";const n=[],s=(r,o)=>{i.addEventListener(r,o),n.push({event:r,handler:o})};t.innerHTML="",t.appendChild(i);const a={getDesign(){if(typeof i.getDesign!="function")throw new Error("Editor not ready: getDesign method not available");return i.getDesign()},setDesign(r){if(typeof i.setDesign!="function")throw new Error("Editor not ready: setDesign method not available");i.setDesign(r)},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 r=this.getDesign(),o=e.apiUrl||"https://api.varianta.io";try{const d=await fetch(`${o}/public/finalize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({templateId:e.templateId,designJson:r})});if(!d.ok){const f=await d.json().catch(()=>({message:"Finalization failed"}));throw new Error(f.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,g=40;let m=0;for(;h.status==="processing"&&m<g;){await new Promise(v=>setTimeout(v,u)),m++;const f=await fetch(`${o}/public/designs/${h.designId}/status`);if(!f.ok)throw new Error("Failed to check design status");const y=await f.json();h={designId:y.designId,status:y.status,proofUrl:y.proofUrl??null,errorMessage:y.errorMessage??null}}return h.status==="processing"&&(h={...h,status:"failed",errorMessage:"Render timed out"}),e.onFinalize&&e.onFinalize(h),h}catch(d){const l={code:"FINALIZE_ERROR",message:d instanceof Error?d.message:"Unknown error",details:d};throw e.onError&&e.onError(l),d}},addTextLayer(r){if(typeof i.addTextLayer!="function")throw new Error("Editor not ready: addTextLayer method not available");i.addTextLayer(r)},async addImageLayer(r){if(typeof i.addImageLayer!="function")throw new Error("Editor not ready: addImageLayer method not available");return i.addImageLayer(r)},removeLayer(r){if(typeof i.removeLayer!="function")throw new Error("Editor not ready: removeLayer method not available");i.removeLayer(r)},selectLayer(r){if(typeof i.selectLayer!="function")throw new Error("Editor not ready: selectLayer method not available");i.selectLayer(r)},getSelectedLayerId(){return typeof i.getSelectedLayerId!="function"?null:i.getSelectedLayerId()},setTheme(r){i.setAttribute("theme",r)},setMode(r){i.setAttribute("mode",r)},destroy(){n.forEach(({event:r,handler:o})=>{i.removeEventListener(r,o)}),i.parentNode&&i.parentNode.removeChild(i)},getElement(){return i}};return e.onReady&&s("ready",(()=>{e.onReady?.(a)})),e.onChange&&s("change",(r=>{const o=r.detail.design;e.onChange?.(o)})),e.onLayerSelect&&s("layer:select",(r=>{e.onLayerSelect?.(r.detail.layerId)})),e.onLayerAdd&&s("layer:add",(r=>{e.onLayerAdd?.(r.detail.layerId)})),e.onLayerRemove&&s("layer:remove",(r=>{e.onLayerRemove?.(r.detail.layerId)})),e.onLayerUpdate&&s("layer:update",(r=>{e.onLayerUpdate?.(r.detail.layerId)})),e.onError&&s("error",(r=>{const o=r.detail.error;e.onError?.(o)})),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:",a)),a}const N=x.forwardRef((b,e)=>{const{templateId:t,apiUrl:i,theme:n="light",mode:s="edit",debug:a=!1,className:r,style:o,initialDesign:d,onReady:l,onChange:h,onLayerSelect:u,onLayerAdd:g,onLayerRemove:m,onLayerUpdate:f,onError:y,onFinalize:v}=b,C=x.useRef(null),p=x.useRef(null),I=x.useRef({onReady:l,onChange:h,onLayerSelect:u,onLayerAdd:g,onLayerRemove:m,onLayerUpdate:f,onError:y,onFinalize:v});return x.useEffect(()=>{I.current={onReady:l,onChange:h,onLayerSelect:u,onLayerAdd:g,onLayerRemove:m,onLayerUpdate:f,onError:y,onFinalize:v}},[l,h,u,g,m,f,y,v]),x.useEffect(()=>{if(!C.current)return;const E={templateId:t,apiUrl:i,theme:n,mode:s,debug:a,initialDesign:d,onReady:()=>I.current.onReady?.(),onChange:w=>I.current.onChange?.(w),onLayerSelect:w=>I.current.onLayerSelect?.(w),onLayerAdd:w=>I.current.onLayerAdd?.(w),onLayerRemove:w=>I.current.onLayerRemove?.(w),onLayerUpdate:w=>I.current.onLayerUpdate?.(w),onError:w=>I.current.onError?.(w),onFinalize:w=>I.current.onFinalize?.(w)};try{const w=H(C.current,E);p.current=w}catch(w){console.error("[Customizer React] Failed to initialize:",w),I.current.onError?.({code:"INIT_ERROR",message:w instanceof Error?w.message:"Unknown error",details:w})}return()=>{p.current&&(p.current.destroy(),p.current=null)}},[t,i,a,d]),x.useEffect(()=>{p.current&&p.current.setTheme(n)},[n]),x.useEffect(()=>{p.current&&p.current.setMode(s)},[s]),x.useImperativeHandle(e,()=>({getDesign:()=>{if(!p.current)throw new Error("Editor not initialized");return p.current.getDesign()},setDesign:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.setDesign(E)},undo:()=>{if(!p.current)throw new Error("Editor not initialized");p.current.undo()},redo:()=>{if(!p.current)throw new Error("Editor not initialized");p.current.redo()},canUndo:()=>p.current?p.current.canUndo():!1,canRedo:()=>p.current?p.current.canRedo():!1,finalize:async()=>{if(!p.current)throw new Error("Editor not initialized");return p.current.finalize()},addTextLayer:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.addTextLayer(E)},addImageLayer:async E=>{if(!p.current)throw new Error("Editor not initialized");return p.current.addImageLayer(E)},removeLayer:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.removeLayer(E)},selectLayer:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.selectLayer(E)},getSelectedLayerId:()=>p.current?p.current.getSelectedLayerId():null,setTheme:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.setTheme(E)},setMode:E=>{if(!p.current)throw new Error("Editor not initialized");p.current.setMode(E)},destroy:()=>{p.current&&(p.current.destroy(),p.current=null)},getElement:()=>{if(!p.current)throw new Error("Editor not initialized");return p.current.getElement()}}),[]),k.jsx("div",{ref:C,className:r,style:{width:"100%",height:"100%",...o}})});N.displayName="Customizer";function de(b={}){const{autoSave:e=!1,autoSaveKey:t="customizer-design",autoSaveDebounce:i=1e3}=b,n=x.useRef(null),[s,a]=x.useState(null),[r,o]=x.useState(!1),[d,l]=x.useState(!1),[h,u]=x.useState(null),[g,m]=x.useState(!1),[f,y]=x.useState(null),v=x.useRef(void 0);x.useEffect(()=>{if(!(!e||!s))return v.current&&clearTimeout(v.current),v.current=setTimeout(()=>{try{localStorage.setItem(t,JSON.stringify(s))}catch(z){console.error("[useCustomizer] Auto-save failed:",z)}},i),()=>{v.current&&clearTimeout(v.current)}},[s,e,t,i]);const C=x.useCallback(()=>{if(!n.current)return null;try{return n.current.getDesign()}catch(z){return console.error("[useCustomizer] getDesign failed:",z),null}},[]),p=x.useCallback(z=>{if(n.current)try{n.current.setDesign(z),a(z)}catch(M){console.error("[useCustomizer] setDesign failed:",M)}},[]),I=x.useCallback(()=>{if(n.current)try{n.current.undo(),o(n.current.canUndo()),l(n.current.canRedo())}catch(z){console.error("[useCustomizer] undo failed:",z)}},[]),E=x.useCallback(()=>{if(n.current)try{n.current.redo(),o(n.current.canUndo()),l(n.current.canRedo())}catch(z){console.error("[useCustomizer] redo failed:",z)}},[]),w=x.useCallback(async()=>{if(!n.current||g)return null;m(!0);try{const z=await n.current.finalize();return y(z),z}catch(z){return console.error("[useCustomizer] finalize failed:",z),null}finally{m(!1)}},[g]),R=x.useCallback(z=>{if(n.current)try{n.current.addTextLayer(z)}catch(M){console.error("[useCustomizer] addTextLayer failed:",M)}},[]),D=x.useCallback(async z=>{if(n.current)try{await n.current.addImageLayer(z)}catch(M){console.error("[useCustomizer] addImageLayer failed:",M)}},[]),U=x.useCallback(z=>{if(n.current)try{n.current.removeLayer(z)}catch(M){console.error("[useCustomizer] removeLayer failed:",M)}},[]),le=x.useCallback(z=>{if(n.current)try{n.current.selectLayer(z),u(z)}catch(M){console.error("[useCustomizer] selectLayer failed:",M)}},[]);return{customizerRef:n,design:s,canUndo:r,canRedo:d,selectedLayerId:h,isFinalizing:g,finalizeResult:f,getDesign:C,setDesign:p,undo:I,redo:E,finalize:w,addTextLayer:R,addImageLayer:D,removeLayer:U,selectLayer:le}}S.Customizer=N,S.initCustomizer=H,S.useCustomizer=de,Object.defineProperty(S,Symbol.toStringTag,{value:"Module"})}));
|
|
8
8
|
//# sourceMappingURL=index.umd.js.map
|