@snowcone-app/canvas 0.1.10 → 0.1.12

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.
Files changed (48) hide show
  1. package/dist/{CanvasStateV1-BmE5V6me.cjs → CanvasStateV1-C4hC1MCe.cjs} +5 -5
  2. package/dist/{CanvasStateV1-BmE5V6me.cjs.map → CanvasStateV1-C4hC1MCe.cjs.map} +1 -1
  3. package/dist/{CanvasStateV1-CD3Q94F4.js → CanvasStateV1-CJU_xYW5.js} +3 -3
  4. package/dist/{CanvasStateV1-CD3Q94F4.js.map → CanvasStateV1-CJU_xYW5.js.map} +1 -1
  5. package/dist/{HybridHistoryManager-BV6XV0nD.js → HybridHistoryManager-jBBnVim8.js} +54 -54
  6. package/dist/{HybridHistoryManager-BV6XV0nD.js.map → HybridHistoryManager-jBBnVim8.js.map} +1 -1
  7. package/dist/{ElementFactory-Ckv6sSev.js → ImportManager-Oqu2yB54.js} +595 -378
  8. package/dist/ImportManager-Oqu2yB54.js.map +1 -0
  9. package/dist/{ElementFactory-DEjwp-Wg.cjs → ImportManager-W1eWhfyM.cjs} +5 -5
  10. package/dist/ImportManager-W1eWhfyM.cjs.map +1 -0
  11. package/dist/ThemeContext-BMNQKl1c.cjs +2 -0
  12. package/dist/{ThemeContext-4mJ_y0Me.cjs.map → ThemeContext-BMNQKl1c.cjs.map} +1 -1
  13. package/dist/ThemeContext-wj-wSO7J.js +1158 -0
  14. package/dist/{ThemeContext-H0Z-MqqR.js.map → ThemeContext-wj-wSO7J.js.map} +1 -1
  15. package/dist/advanced.js +5 -32
  16. package/dist/advanced.js.map +1 -1
  17. package/dist/advanced.mjs +588 -15069
  18. package/dist/advanced.mjs.map +1 -1
  19. package/dist/components/embed/KitLayout.d.ts +22 -0
  20. package/dist/components/embed/UndoRedoControls.d.ts +3 -0
  21. package/dist/compose-Dqh2f8tS.js +22222 -0
  22. package/dist/compose-Dqh2f8tS.js.map +1 -0
  23. package/dist/compose-HDJp4Z_d.cjs +60 -0
  24. package/dist/compose-HDJp4Z_d.cjs.map +1 -0
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +595 -513
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/internals.js +1 -1
  30. package/dist/internals.js.map +1 -1
  31. package/dist/internals.mjs +101 -102
  32. package/dist/internals.mjs.map +1 -1
  33. package/dist/style.css.d.ts +4 -0
  34. package/dist/testing.js +1 -1
  35. package/dist/testing.mjs +11 -11
  36. package/package.json +8 -4
  37. package/dist/ElementFactory-Ckv6sSev.js.map +0 -1
  38. package/dist/ElementFactory-DEjwp-Wg.cjs.map +0 -1
  39. package/dist/ImportManager-64OYjELO.js +0 -222
  40. package/dist/ImportManager-64OYjELO.js.map +0 -1
  41. package/dist/ImportManager-wSzrR-5a.cjs +0 -2
  42. package/dist/ImportManager-wSzrR-5a.cjs.map +0 -1
  43. package/dist/ThemeContext-4mJ_y0Me.cjs +0 -2
  44. package/dist/ThemeContext-H0Z-MqqR.js +0 -1077
  45. package/dist/compose-DHBRwi_A.cjs +0 -33
  46. package/dist/compose-DHBRwi_A.cjs.map +0 -1
  47. package/dist/compose-DIPiisIw.js +0 -7690
  48. package/dist/compose-DIPiisIw.js.map +0 -1
package/dist/internals.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("./ElementFactory-DEjwp-Wg.cjs"),o=require("./HybridHistoryManager-BXD93pp8.cjs"),M=require("./ImportManager-wSzrR-5a.cjs"),p=require("./CanvasStateV1-BmE5V6me.cjs");function k(e,r,g){var t,s;e.save(),e.translate(r.x,r.y),r.rotation&&e.rotate(-r.rotation*Math.PI/180);let a=1,d=1;if(r.flipHorizontal&&(a=-1),r.flipVertical&&(d=-1),(a!==1||d!==1)&&e.scale(a,d),r.opacity!==void 0&&r.opacity!==1&&(e.globalAlpha=r.opacity),r.borderRadius&&r.borderRadius>0){const n=Math.min(r.borderRadius/100*Math.min(r.width,r.height),r.width/2,r.height/2);e.beginPath();const i=-r.width/2,h=-r.height/2,c=r.width,f=r.height;e.moveTo(i+n,h),e.lineTo(i+c-n,h),e.arcTo(i+c,h,i+c,h+n,n),e.lineTo(i+c,h+f-n),e.arcTo(i+c,h+f,i+c-n,h+f,n),e.lineTo(i+n,h+f),e.arcTo(i,h+f,i,h+f-n,n),e.lineTo(i,h+n),e.arcTo(i,h,i+n,h,n),e.closePath(),e.clip()}if(r.cropX!==void 0&&r.cropWidth!==void 0?e.drawImage(g,r.cropX,r.cropY||0,r.cropWidth,r.cropHeight||g.height,-r.width/2,-r.height/2,r.width,r.height):e.drawImage(g,-r.width/2,-r.height/2,r.width,r.height),e.restore(),(t=r.stroke)!=null&&t.enabled){const n=((s=r.knockoutParts)==null?void 0:s.stroke)===!0,i={x:r.x,y:r.y,rotation:r.rotation,stroke:r.stroke,knockoutParts:r.knockoutParts,transformData:{type:"image",width:r.width,height:r.height,borderRadius:r.borderRadius||0}};if(n){const h=e.canvas;let c;if(typeof OffscreenCanvas<"u")c=new OffscreenCanvas(h.width,h.height);else if(typeof document<"u")c=document.createElement("canvas"),c.width=h.width,c.height=h.height;else{o.renderImageStroke(e,i);return}const f=c.getContext("2d");if(!f){o.renderImageStroke(e,i);return}o.renderImageStroke(f,i,{isKnockout:!0}),e.save(),e.globalCompositeOperation="destination-out",e.drawImage(c,0,0),e.restore()}else o.renderImageStroke(e,i)}}function y(e,r){if(typeof OffscreenCanvas<"u")return new OffscreenCanvas(e,r);const g=document.createElement("canvas");return g.width=e,g.height=r,g}function m(e,r){var t;const g=r.opacity??1,a=!!((t=r.stroke)!=null&&t.enabled);g<1&&a?C(e,r,g):u(e,r,g)}function C(e,r,g){var f;const a=r.transformData,t=(((f=r.stroke)==null?void 0:f.width)||2)+2,s=Math.ceil(a.width+t*2),n=Math.ceil(a.height+t*2),i=y(s,n),h=i.getContext("2d");if(!h){u(e,r,g);return}const c={...r,x:s/2,y:n/2};u(h,c,1),e.save(),e.translate(r.x,r.y),r.rotation&&e.rotate(-r.rotation*Math.PI/180),e.globalAlpha=g,e.drawImage(i,-s/2,-n/2),e.restore()}function u(e,r,g){var t;const a=r.transformData,d=a.fillOpacity??1;if(e.save(),e.translate(r.x,r.y),r.rotation&&e.rotate(-r.rotation*Math.PI/180),e.globalAlpha=g*d,e.fillStyle=a.fillColor||"#3b82f6",e.beginPath(),S(e,a),e.fill(),e.restore(),(t=r.stroke)!=null&&t.enabled){e.save(),e.translate(r.x,r.y),r.rotation&&e.rotate(-r.rotation*Math.PI/180);const s=r.stroke;e.globalAlpha=g*(s.opacity??1),e.strokeStyle=s.color||"#000000",e.lineWidth=s.width||2,e.lineCap=s.lineCap||"round",e.lineJoin=s.lineJoin||"round",e.beginPath(),S(e,a),e.stroke(),e.restore()}}function S(e,r){const{shapeType:g,width:a,height:d}=r;switch(g){case"rectangle":{const t=r.borderRadius||0,s=-a/2,n=-d/2;if(t>0){const i=Math.min(t/100*Math.min(a,d),a/2,d/2);e.roundRect(s,n,a,d,i)}else e.rect(s,n,a,d);break}case"circle":{const t=Math.min(a,d)/2;e.arc(0,0,t,0,Math.PI*2);break}case"ellipse":{e.ellipse(0,0,a/2,d/2,0,0,Math.PI*2);break}case"triangle":{const t=a/2,s=d/2;e.moveTo(0,-s),e.lineTo(t,s),e.lineTo(-t,s),e.closePath();break}case"polygon":{const t=r.sides||5,s=Math.min(a,d)/2;for(let n=0;n<t;n++){const i=n*2*Math.PI/t-Math.PI/2,h=s*Math.cos(i),c=s*Math.sin(i);n===0?e.moveTo(h,c):e.lineTo(h,c)}e.closePath();break}case"star":{const t=r.points||5,s=Math.min(a,d)/2,n=s*(r.innerRadius||.4);for(let i=0;i<t*2;i++){const h=i*Math.PI/t-Math.PI/2,c=i%2===0?s:n,f=c*Math.cos(h),T=c*Math.sin(h);i===0?e.moveTo(f,T):e.lineTo(f,T)}e.closePath();break}case"line":{const t=a/2,s=d/2;e.rect(-t,-s,a,d);break}default:e.rect(-a/2,-d/2,a,d)}}exports.AlignmentSnapSystem=l.AlignmentSnapSystem;exports.ArtboardRenderer=l.ArtboardRenderer;exports.CanvasRenderer=l.CanvasRenderer;exports.DEFAULT_MAX_BITMAP_DIMENSION=l.DEFAULT_MAX_BITMAP_DIMENSION;exports.ExportManager=l.ExportManager;exports.InteractionStateMachine=l.InteractionStateMachine;exports.ResizePipeline=l.ResizePipeline;exports.SpacingSystem=l.SpacingSystem;exports.TransformHandles=l.TransformHandles;exports.WorkerExportManager=l.WorkerExportManager;exports.clearImageBitmapCache=l.clearImageBitmapCache;exports.clearMaskCache=l.clearMaskCache;exports.clearRegisteredBitmapKeys=l.clearRegisteredBitmapKeys;exports.getSharedWorkerExportManager=l.getSharedWorkerExportManager;exports.invalidateMaskCache=l.invalidateMaskCache;exports.removeFromImageBitmapCache=l.removeFromImageBitmapCache;exports.renderWithKnockout=l.renderWithKnockout;exports.renderWithMasks=l.renderWithMasks;exports.terminateSharedWorkerExportManager=l.terminateSharedWorkerExportManager;exports.unregisterElementBitmaps=l.unregisterElementBitmaps;exports.ArchTransform=o.ArchTransform;exports.ArtboardManager=o.ArtboardManager;exports.AscendTransform=o.AscendTransform;exports.CircleTransform=o.CircleTransform;exports.CustomTransform=o.CustomTransform;exports.ElementStore=o.ElementStore;exports.FlagTransform=o.FlagTransform;exports.HybridHistoryManager=o.HybridHistoryManager;exports.LeanTransform=o.LeanTransform;exports.LogLevel=o.LogLevel;exports.WaveTransform=o.WaveTransform;exports.applySpaceLayoutRules=o.applySpaceLayoutRules;exports.applyStrokeStyle=o.applyStrokeStyle;exports.buildFontString=o.buildFontString;exports.calculateFixedCornerPosition=o.calculateFixedCornerPosition;exports.calculateResizeHandles=o.calculateResizeHandles;exports.calculateRotationHandlePosition=o.calculateRotationHandlePosition;exports.createCirclePath=o.createCirclePath;exports.createImagePath=o.createImagePath;exports.createLogger=o.createLogger;exports.createRectPath=o.createRectPath;exports.createTextPath=o.createTextPath;exports.getFontMetrics=o.getFontMetrics;exports.hitTestCircle=o.hitTestCircle;exports.hitTestRect=o.hitTestRect;exports.logger=o.logger;exports.measureTextWidth=o.measureTextWidth$1;exports.renderCustomTransform=o.renderCustomTransform;exports.renderImageStroke=o.renderImageStroke;exports.renderMultilineText=o.renderMultilineText;exports.renderPathStroke=o.renderPathStroke;exports.renderRichTextFillOnly=o.renderRichTextFillOnly;exports.renderTextElement=o.renderTextElement;exports.renderTextFillOnly=o.renderTextFillOnly;exports.renderTextStroke=o.renderTextStroke;exports.splitRichTextIntoLines=o.splitRichTextIntoLines;exports.wrapRichTextSpans=o.wrapRichTextSpans;exports.wrapText=o.wrapText$1;exports.ImportManager=M.ImportManager;exports.AnyElementConfigSchema=p.AnyElementConfigSchema;exports.AnyTransformDataSchema=p.AnyTransformDataSchema;exports.CanvasStateV1Schema=p.CanvasStateV1Schema;exports.SerializedArtboardSchema=p.SerializedArtboardSchema;exports.renderImageElement=k;exports.renderShapeElement=m;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("./ImportManager-W1eWhfyM.cjs"),o=require("./HybridHistoryManager-BXD93pp8.cjs"),p=require("./CanvasStateV1-C4hC1MCe.cjs");function M(r,e,g){var t,s;r.save(),r.translate(e.x,e.y),e.rotation&&r.rotate(-e.rotation*Math.PI/180);let a=1,d=1;if(e.flipHorizontal&&(a=-1),e.flipVertical&&(d=-1),(a!==1||d!==1)&&r.scale(a,d),e.opacity!==void 0&&e.opacity!==1&&(r.globalAlpha=e.opacity),e.borderRadius&&e.borderRadius>0){const n=Math.min(e.borderRadius/100*Math.min(e.width,e.height),e.width/2,e.height/2);r.beginPath();const i=-e.width/2,h=-e.height/2,c=e.width,f=e.height;r.moveTo(i+n,h),r.lineTo(i+c-n,h),r.arcTo(i+c,h,i+c,h+n,n),r.lineTo(i+c,h+f-n),r.arcTo(i+c,h+f,i+c-n,h+f,n),r.lineTo(i+n,h+f),r.arcTo(i,h+f,i,h+f-n,n),r.lineTo(i,h+n),r.arcTo(i,h,i+n,h,n),r.closePath(),r.clip()}if(e.cropX!==void 0&&e.cropWidth!==void 0?r.drawImage(g,e.cropX,e.cropY||0,e.cropWidth,e.cropHeight||g.height,-e.width/2,-e.height/2,e.width,e.height):r.drawImage(g,-e.width/2,-e.height/2,e.width,e.height),r.restore(),(t=e.stroke)!=null&&t.enabled){const n=((s=e.knockoutParts)==null?void 0:s.stroke)===!0,i={x:e.x,y:e.y,rotation:e.rotation,stroke:e.stroke,knockoutParts:e.knockoutParts,transformData:{type:"image",width:e.width,height:e.height,borderRadius:e.borderRadius||0}};if(n){const h=r.canvas;let c;if(typeof OffscreenCanvas<"u")c=new OffscreenCanvas(h.width,h.height);else if(typeof document<"u")c=document.createElement("canvas"),c.width=h.width,c.height=h.height;else{o.renderImageStroke(r,i);return}const f=c.getContext("2d");if(!f){o.renderImageStroke(r,i);return}o.renderImageStroke(f,i,{isKnockout:!0}),r.save(),r.globalCompositeOperation="destination-out",r.drawImage(c,0,0),r.restore()}else o.renderImageStroke(r,i)}}function k(r,e){if(typeof OffscreenCanvas<"u")return new OffscreenCanvas(r,e);const g=document.createElement("canvas");return g.width=r,g.height=e,g}function y(r,e){var t;const g=e.opacity??1,a=!!((t=e.stroke)!=null&&t.enabled);g<1&&a?C(r,e,g):u(r,e,g)}function C(r,e,g){var f;const a=e.transformData,t=(((f=e.stroke)==null?void 0:f.width)||2)+2,s=Math.ceil(a.width+t*2),n=Math.ceil(a.height+t*2),i=k(s,n),h=i.getContext("2d");if(!h){u(r,e,g);return}const c={...e,x:s/2,y:n/2};u(h,c,1),r.save(),r.translate(e.x,e.y),e.rotation&&r.rotate(-e.rotation*Math.PI/180),r.globalAlpha=g,r.drawImage(i,-s/2,-n/2),r.restore()}function u(r,e,g){var t;const a=e.transformData,d=a.fillOpacity??1;if(r.save(),r.translate(e.x,e.y),e.rotation&&r.rotate(-e.rotation*Math.PI/180),r.globalAlpha=g*d,r.fillStyle=a.fillColor||"#3b82f6",r.beginPath(),S(r,a),r.fill(),r.restore(),(t=e.stroke)!=null&&t.enabled){r.save(),r.translate(e.x,e.y),e.rotation&&r.rotate(-e.rotation*Math.PI/180);const s=e.stroke;r.globalAlpha=g*(s.opacity??1),r.strokeStyle=s.color||"#000000",r.lineWidth=s.width||2,r.lineCap=s.lineCap||"round",r.lineJoin=s.lineJoin||"round",r.beginPath(),S(r,a),r.stroke(),r.restore()}}function S(r,e){const{shapeType:g,width:a,height:d}=e;switch(g){case"rectangle":{const t=e.borderRadius||0,s=-a/2,n=-d/2;if(t>0){const i=Math.min(t/100*Math.min(a,d),a/2,d/2);r.roundRect(s,n,a,d,i)}else r.rect(s,n,a,d);break}case"circle":{const t=Math.min(a,d)/2;r.arc(0,0,t,0,Math.PI*2);break}case"ellipse":{r.ellipse(0,0,a/2,d/2,0,0,Math.PI*2);break}case"triangle":{const t=a/2,s=d/2;r.moveTo(0,-s),r.lineTo(t,s),r.lineTo(-t,s),r.closePath();break}case"polygon":{const t=e.sides||5,s=Math.min(a,d)/2;for(let n=0;n<t;n++){const i=n*2*Math.PI/t-Math.PI/2,h=s*Math.cos(i),c=s*Math.sin(i);n===0?r.moveTo(h,c):r.lineTo(h,c)}r.closePath();break}case"star":{const t=e.points||5,s=Math.min(a,d)/2,n=s*(e.innerRadius||.4);for(let i=0;i<t*2;i++){const h=i*Math.PI/t-Math.PI/2,c=i%2===0?s:n,f=c*Math.cos(h),T=c*Math.sin(h);i===0?r.moveTo(f,T):r.lineTo(f,T)}r.closePath();break}case"line":{const t=a/2,s=d/2;r.rect(-t,-s,a,d);break}default:r.rect(-a/2,-d/2,a,d)}}exports.AlignmentSnapSystem=l.AlignmentSnapSystem;exports.ArtboardRenderer=l.ArtboardRenderer;exports.CanvasRenderer=l.CanvasRenderer;exports.DEFAULT_MAX_BITMAP_DIMENSION=l.DEFAULT_MAX_BITMAP_DIMENSION;exports.ExportManager=l.ExportManager;exports.ImportManager=l.ImportManager;exports.InteractionStateMachine=l.InteractionStateMachine;exports.ResizePipeline=l.ResizePipeline;exports.SpacingSystem=l.SpacingSystem;exports.TransformHandles=l.TransformHandles;exports.WorkerExportManager=l.WorkerExportManager;exports.clearImageBitmapCache=l.clearImageBitmapCache;exports.clearMaskCache=l.clearMaskCache;exports.clearRegisteredBitmapKeys=l.clearRegisteredBitmapKeys;exports.getSharedWorkerExportManager=l.getSharedWorkerExportManager;exports.invalidateMaskCache=l.invalidateMaskCache;exports.removeFromImageBitmapCache=l.removeFromImageBitmapCache;exports.renderWithKnockout=l.renderWithKnockout;exports.renderWithMasks=l.renderWithMasks;exports.terminateSharedWorkerExportManager=l.terminateSharedWorkerExportManager;exports.unregisterElementBitmaps=l.unregisterElementBitmaps;exports.ArchTransform=o.ArchTransform;exports.ArtboardManager=o.ArtboardManager;exports.AscendTransform=o.AscendTransform;exports.CircleTransform=o.CircleTransform;exports.CustomTransform=o.CustomTransform;exports.ElementStore=o.ElementStore;exports.FlagTransform=o.FlagTransform;exports.HybridHistoryManager=o.HybridHistoryManager;exports.LeanTransform=o.LeanTransform;exports.LogLevel=o.LogLevel;exports.WaveTransform=o.WaveTransform;exports.applySpaceLayoutRules=o.applySpaceLayoutRules;exports.applyStrokeStyle=o.applyStrokeStyle;exports.buildFontString=o.buildFontString;exports.calculateFixedCornerPosition=o.calculateFixedCornerPosition;exports.calculateResizeHandles=o.calculateResizeHandles;exports.calculateRotationHandlePosition=o.calculateRotationHandlePosition;exports.createCirclePath=o.createCirclePath;exports.createImagePath=o.createImagePath;exports.createLogger=o.createLogger;exports.createRectPath=o.createRectPath;exports.createTextPath=o.createTextPath;exports.getFontMetrics=o.getFontMetrics;exports.hitTestCircle=o.hitTestCircle;exports.hitTestRect=o.hitTestRect;exports.logger=o.logger;exports.measureTextWidth=o.measureTextWidth$1;exports.renderCustomTransform=o.renderCustomTransform;exports.renderImageStroke=o.renderImageStroke;exports.renderMultilineText=o.renderMultilineText;exports.renderPathStroke=o.renderPathStroke;exports.renderRichTextFillOnly=o.renderRichTextFillOnly;exports.renderTextElement=o.renderTextElement;exports.renderTextFillOnly=o.renderTextFillOnly;exports.renderTextStroke=o.renderTextStroke;exports.splitRichTextIntoLines=o.splitRichTextIntoLines;exports.wrapRichTextSpans=o.wrapRichTextSpans;exports.wrapText=o.wrapText$1;exports.AnyElementConfigSchema=p.AnyElementConfigSchema;exports.AnyTransformDataSchema=p.AnyTransformDataSchema;exports.CanvasStateV1Schema=p.CanvasStateV1Schema;exports.SerializedArtboardSchema=p.SerializedArtboardSchema;exports.renderImageElement=M;exports.renderShapeElement=y;
2
2
  //# sourceMappingURL=internals.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"internals.js","sources":["../src/rendering/image-renderer.ts","../src/rendering/shape-renderer.ts"],"sourcesContent":["/**\n * Image Renderer - Image element rendering with crop, flip, border radius, and stroke.\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n */\n\nimport type { ImageElementConfig } from '../types/index.js';\nimport { renderImageStroke } from './StrokeRenderer.js';\nimport type { RenderContext, SerializedImageElement } from './renderer-types.js';\n\n/**\n * Render an image element using ImageBitmap\n * Works in both main thread and worker\n */\nexport function renderImageElement(\n ctx: RenderContext,\n elementData: SerializedImageElement,\n imageBitmap: ImageBitmap\n): void {\n ctx.save();\n\n // Translate to element position\n ctx.translate(elementData.x, elementData.y);\n\n // Apply rotation (negative for clockwise)\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Apply flip transformations\n let scaleX = 1;\n let scaleY = 1;\n if (elementData.flipHorizontal) scaleX = -1;\n if (elementData.flipVertical) scaleY = -1;\n if (scaleX !== 1 || scaleY !== 1) {\n ctx.scale(scaleX, scaleY);\n }\n\n // Apply opacity if specified\n if (elementData.opacity !== undefined && elementData.opacity !== 1) {\n ctx.globalAlpha = elementData.opacity;\n }\n\n // Apply border radius if specified (borderRadius is stored as percentage 0-100)\n if (elementData.borderRadius && elementData.borderRadius > 0) {\n const radius = Math.min(\n (elementData.borderRadius / 100) * Math.min(elementData.width, elementData.height),\n elementData.width / 2,\n elementData.height / 2\n );\n\n // Create clipping path for rounded corners\n ctx.beginPath();\n const x = -elementData.width / 2;\n const y = -elementData.height / 2;\n const w = elementData.width;\n const h = elementData.height;\n\n ctx.moveTo(x + radius, y);\n ctx.lineTo(x + w - radius, y);\n ctx.arcTo(x + w, y, x + w, y + radius, radius);\n ctx.lineTo(x + w, y + h - radius);\n ctx.arcTo(x + w, y + h, x + w - radius, y + h, radius);\n ctx.lineTo(x + radius, y + h);\n ctx.arcTo(x, y + h, x, y + h - radius, radius);\n ctx.lineTo(x, y + radius);\n ctx.arcTo(x, y, x + radius, y, radius);\n ctx.closePath();\n ctx.clip();\n }\n\n // Draw the image\n if (elementData.cropX !== undefined && elementData.cropWidth !== undefined) {\n // Draw with crop\n ctx.drawImage(\n imageBitmap,\n elementData.cropX,\n elementData.cropY || 0,\n elementData.cropWidth,\n elementData.cropHeight || imageBitmap.height,\n -elementData.width / 2,\n -elementData.height / 2,\n elementData.width,\n elementData.height\n );\n } else {\n // Draw without crop (centered)\n ctx.drawImage(imageBitmap, -elementData.width / 2, -elementData.height / 2, elementData.width, elementData.height);\n }\n\n ctx.restore();\n\n // Render stroke if enabled (after restore to apply fresh transforms)\n if (elementData.stroke?.enabled) {\n const hasKnockoutStroke = elementData.knockoutParts?.stroke === true;\n\n const imageConfig: ImageElementConfig = {\n transformType: 'image',\n x: elementData.x,\n y: elementData.y,\n rotation: elementData.rotation,\n stroke: elementData.stroke,\n knockoutParts: elementData.knockoutParts,\n transformData: {\n type: 'image',\n width: elementData.width,\n height: elementData.height,\n borderRadius: elementData.borderRadius || 0,\n },\n };\n\n if (hasKnockoutStroke) {\n // For knockout stroke, use offscreen canvas approach\n const canvas = ctx.canvas;\n let offscreen: HTMLCanvasElement | OffscreenCanvas;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n offscreen = new OffscreenCanvas(canvas.width, canvas.height);\n } else if (typeof document !== 'undefined') {\n offscreen = document.createElement('canvas');\n offscreen.width = canvas.width;\n offscreen.height = canvas.height;\n } else {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n // Render the stroke to the offscreen canvas with knockout flag\n renderImageStroke(offCtx, imageConfig, { isKnockout: true });\n\n // Composite with destination-out\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.drawImage(offscreen as CanvasImageSource, 0, 0);\n ctx.restore();\n } else {\n // Normal stroke\n renderImageStroke(ctx, imageConfig);\n }\n }\n}\n","/**\n * Shape Renderer - Shape element rendering (rectangle, circle, ellipse, triangle, etc.)\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n *\n * Opacity strategy: When a shape has both element-level opacity AND a stroke,\n * we render fill+stroke at full opacity on a temporary canvas, then composite\n * the result at the element's opacity. This prevents fill/stroke overlap from\n * compounding (e.g., 50% fill + 50% stroke overlap ≠ 75% opaque).\n */\n\nimport type { RenderContext, SerializedShapeElement } from './renderer-types.js';\n\ntype CanvasLike = HTMLCanvasElement | OffscreenCanvas;\n\nfunction createTempCanvas(width: number, height: number): CanvasLike {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(width, height);\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return canvas;\n}\n\n/**\n * Render a shape element\n * Works in both main thread and worker\n */\nexport function renderShapeElement(ctx: RenderContext, elementData: SerializedShapeElement): void {\n const elementOpacity = elementData.opacity ?? 1;\n const hasStroke = !!elementData.stroke?.enabled;\n const needsOffscreen = elementOpacity < 1 && hasStroke;\n\n if (needsOffscreen) {\n // Render fill+stroke at full opacity on a temp canvas, then composite\n renderShapeWithOffscreen(ctx, elementData, elementOpacity);\n } else {\n // No compounding risk — render directly\n renderShapeDirect(ctx, elementData, elementOpacity);\n }\n}\n\n/**\n * Render shape via temporary canvas to avoid fill/stroke opacity compounding.\n */\nfunction renderShapeWithOffscreen(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const td = elementData.transformData;\n const strokeWidth = elementData.stroke?.width || 2;\n // Pad temp canvas to fit the stroke (strokes extend beyond the shape bounds)\n const padding = strokeWidth + 2;\n const offW = Math.ceil(td.width + padding * 2);\n const offH = Math.ceil(td.height + padding * 2);\n\n const offCanvas = createTempCanvas(offW, offH);\n const offCtx = offCanvas.getContext('2d') as RenderContext;\n if (!offCtx) {\n // Fallback to direct render\n renderShapeDirect(ctx, elementData, elementOpacity);\n return;\n }\n\n // Render at center of temp canvas with full opacity (elementOpacity = 1)\n const tempData: SerializedShapeElement = {\n ...elementData,\n x: offW / 2,\n y: offH / 2,\n opacity: 1,\n };\n renderShapeDirect(offCtx, tempData, 1);\n\n // Composite temp canvas onto main canvas at element opacity\n ctx.save();\n\n // Position: elementData.(x,y) is the shape center, temp canvas center is (offW/2, offH/2)\n // We need to account for rotation around the element center\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n ctx.globalAlpha = elementOpacity;\n ctx.drawImage(offCanvas, -offW / 2, -offH / 2);\n ctx.restore();\n}\n\n/**\n * Render shape directly onto ctx (no offscreen). Used when there's no\n * compounding risk, or as the inner render for the offscreen path.\n */\nfunction renderShapeDirect(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const transformData = elementData.transformData;\n const fillOpacity = transformData.fillOpacity ?? 1;\n\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Fill\n ctx.globalAlpha = elementOpacity * fillOpacity;\n ctx.fillStyle = transformData.fillColor || '#3b82f6';\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.fill();\n ctx.restore();\n\n // Stroke\n if (elementData.stroke?.enabled) {\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n const stroke = elementData.stroke;\n ctx.globalAlpha = elementOpacity * (stroke.opacity ?? 1);\n ctx.strokeStyle = stroke.color || '#000000';\n ctx.lineWidth = stroke.width || 2;\n ctx.lineCap = stroke.lineCap || 'round';\n ctx.lineJoin = stroke.lineJoin || 'round';\n\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.stroke();\n ctx.restore();\n }\n}\n\n/**\n * Trace the shape path (without fill/stroke) — reusable for both fill and stroke passes.\n */\nfunction traceShapePath(\n ctx: RenderContext,\n transformData: SerializedShapeElement['transformData']\n): void {\n const { shapeType, width, height } = transformData;\n\n switch (shapeType) {\n case 'rectangle': {\n const borderRadius = transformData.borderRadius || 0;\n const x = -width / 2;\n const y = -height / 2;\n\n if (borderRadius > 0) {\n const radius = Math.min((borderRadius / 100) * Math.min(width, height), width / 2, height / 2);\n ctx.roundRect(x, y, width, height, radius);\n } else {\n ctx.rect(x, y, width, height);\n }\n break;\n }\n\n case 'circle': {\n const radius = Math.min(width, height) / 2;\n ctx.arc(0, 0, radius, 0, Math.PI * 2);\n break;\n }\n\n case 'ellipse': {\n ctx.ellipse(0, 0, width / 2, height / 2, 0, 0, Math.PI * 2);\n break;\n }\n\n case 'triangle': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.moveTo(0, -halfHeight);\n ctx.lineTo(halfWidth, halfHeight);\n ctx.lineTo(-halfWidth, halfHeight);\n ctx.closePath();\n break;\n }\n\n case 'polygon': {\n const sides = transformData.sides || 5;\n const radius = Math.min(width, height) / 2;\n for (let i = 0; i < sides; i++) {\n const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;\n const px = radius * Math.cos(angle);\n const py = radius * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'star': {\n const points = transformData.points || 5;\n const outerRadius = Math.min(width, height) / 2;\n const innerRadius = outerRadius * (transformData.innerRadius || 0.4);\n for (let i = 0; i < points * 2; i++) {\n const angle = (i * Math.PI) / points - Math.PI / 2;\n const r = i % 2 === 0 ? outerRadius : innerRadius;\n const px = r * Math.cos(angle);\n const py = r * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'line': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.rect(-halfWidth, -halfHeight, width, height);\n break;\n }\n\n default:\n ctx.rect(-width / 2, -height / 2, width, height);\n }\n}\n"],"names":["renderImageElement","ctx","elementData","imageBitmap","scaleX","scaleY","radius","x","y","w","h","_a","hasKnockoutStroke","_b","imageConfig","canvas","offscreen","renderImageStroke","offCtx","createTempCanvas","width","height","renderShapeElement","elementOpacity","hasStroke","renderShapeWithOffscreen","renderShapeDirect","td","padding","offW","offH","offCanvas","tempData","transformData","fillOpacity","traceShapePath","stroke","shapeType","borderRadius","halfWidth","halfHeight","sides","i","angle","px","py","points","outerRadius","innerRadius","r"],"mappings":"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"}
1
+ {"version":3,"file":"internals.js","sources":["../src/rendering/image-renderer.ts","../src/rendering/shape-renderer.ts"],"sourcesContent":["/**\n * Image Renderer - Image element rendering with crop, flip, border radius, and stroke.\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n */\n\nimport type { ImageElementConfig } from '../types/index.js';\nimport { renderImageStroke } from './StrokeRenderer.js';\nimport type { RenderContext, SerializedImageElement } from './renderer-types.js';\n\n/**\n * Render an image element using ImageBitmap\n * Works in both main thread and worker\n */\nexport function renderImageElement(\n ctx: RenderContext,\n elementData: SerializedImageElement,\n imageBitmap: ImageBitmap\n): void {\n ctx.save();\n\n // Translate to element position\n ctx.translate(elementData.x, elementData.y);\n\n // Apply rotation (negative for clockwise)\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Apply flip transformations\n let scaleX = 1;\n let scaleY = 1;\n if (elementData.flipHorizontal) scaleX = -1;\n if (elementData.flipVertical) scaleY = -1;\n if (scaleX !== 1 || scaleY !== 1) {\n ctx.scale(scaleX, scaleY);\n }\n\n // Apply opacity if specified\n if (elementData.opacity !== undefined && elementData.opacity !== 1) {\n ctx.globalAlpha = elementData.opacity;\n }\n\n // Apply border radius if specified (borderRadius is stored as percentage 0-100)\n if (elementData.borderRadius && elementData.borderRadius > 0) {\n const radius = Math.min(\n (elementData.borderRadius / 100) * Math.min(elementData.width, elementData.height),\n elementData.width / 2,\n elementData.height / 2\n );\n\n // Create clipping path for rounded corners\n ctx.beginPath();\n const x = -elementData.width / 2;\n const y = -elementData.height / 2;\n const w = elementData.width;\n const h = elementData.height;\n\n ctx.moveTo(x + radius, y);\n ctx.lineTo(x + w - radius, y);\n ctx.arcTo(x + w, y, x + w, y + radius, radius);\n ctx.lineTo(x + w, y + h - radius);\n ctx.arcTo(x + w, y + h, x + w - radius, y + h, radius);\n ctx.lineTo(x + radius, y + h);\n ctx.arcTo(x, y + h, x, y + h - radius, radius);\n ctx.lineTo(x, y + radius);\n ctx.arcTo(x, y, x + radius, y, radius);\n ctx.closePath();\n ctx.clip();\n }\n\n // Draw the image\n if (elementData.cropX !== undefined && elementData.cropWidth !== undefined) {\n // Draw with crop\n ctx.drawImage(\n imageBitmap,\n elementData.cropX,\n elementData.cropY || 0,\n elementData.cropWidth,\n elementData.cropHeight || imageBitmap.height,\n -elementData.width / 2,\n -elementData.height / 2,\n elementData.width,\n elementData.height\n );\n } else {\n // Draw without crop (centered)\n ctx.drawImage(imageBitmap, -elementData.width / 2, -elementData.height / 2, elementData.width, elementData.height);\n }\n\n ctx.restore();\n\n // Render stroke if enabled (after restore to apply fresh transforms)\n if (elementData.stroke?.enabled) {\n const hasKnockoutStroke = elementData.knockoutParts?.stroke === true;\n\n const imageConfig: ImageElementConfig = {\n transformType: 'image',\n x: elementData.x,\n y: elementData.y,\n rotation: elementData.rotation,\n stroke: elementData.stroke,\n knockoutParts: elementData.knockoutParts,\n transformData: {\n type: 'image',\n width: elementData.width,\n height: elementData.height,\n borderRadius: elementData.borderRadius || 0,\n },\n };\n\n if (hasKnockoutStroke) {\n // For knockout stroke, use offscreen canvas approach\n const canvas = ctx.canvas;\n let offscreen: HTMLCanvasElement | OffscreenCanvas;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n offscreen = new OffscreenCanvas(canvas.width, canvas.height);\n } else if (typeof document !== 'undefined') {\n offscreen = document.createElement('canvas');\n offscreen.width = canvas.width;\n offscreen.height = canvas.height;\n } else {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n // Render the stroke to the offscreen canvas with knockout flag\n renderImageStroke(offCtx, imageConfig, { isKnockout: true });\n\n // Composite with destination-out\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.drawImage(offscreen as CanvasImageSource, 0, 0);\n ctx.restore();\n } else {\n // Normal stroke\n renderImageStroke(ctx, imageConfig);\n }\n }\n}\n","/**\n * Shape Renderer - Shape element rendering (rectangle, circle, ellipse, triangle, etc.)\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n *\n * Opacity strategy: When a shape has both element-level opacity AND a stroke,\n * we render fill+stroke at full opacity on a temporary canvas, then composite\n * the result at the element's opacity. This prevents fill/stroke overlap from\n * compounding (e.g., 50% fill + 50% stroke overlap ≠ 75% opaque).\n */\n\nimport type { RenderContext, SerializedShapeElement } from './renderer-types.js';\n\ntype CanvasLike = HTMLCanvasElement | OffscreenCanvas;\n\nfunction createTempCanvas(width: number, height: number): CanvasLike {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(width, height);\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return canvas;\n}\n\n/**\n * Render a shape element\n * Works in both main thread and worker\n */\nexport function renderShapeElement(ctx: RenderContext, elementData: SerializedShapeElement): void {\n const elementOpacity = elementData.opacity ?? 1;\n const hasStroke = !!elementData.stroke?.enabled;\n const needsOffscreen = elementOpacity < 1 && hasStroke;\n\n if (needsOffscreen) {\n // Render fill+stroke at full opacity on a temp canvas, then composite\n renderShapeWithOffscreen(ctx, elementData, elementOpacity);\n } else {\n // No compounding risk — render directly\n renderShapeDirect(ctx, elementData, elementOpacity);\n }\n}\n\n/**\n * Render shape via temporary canvas to avoid fill/stroke opacity compounding.\n */\nfunction renderShapeWithOffscreen(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const td = elementData.transformData;\n const strokeWidth = elementData.stroke?.width || 2;\n // Pad temp canvas to fit the stroke (strokes extend beyond the shape bounds)\n const padding = strokeWidth + 2;\n const offW = Math.ceil(td.width + padding * 2);\n const offH = Math.ceil(td.height + padding * 2);\n\n const offCanvas = createTempCanvas(offW, offH);\n const offCtx = offCanvas.getContext('2d') as RenderContext;\n if (!offCtx) {\n // Fallback to direct render\n renderShapeDirect(ctx, elementData, elementOpacity);\n return;\n }\n\n // Render at center of temp canvas with full opacity (elementOpacity = 1)\n const tempData: SerializedShapeElement = {\n ...elementData,\n x: offW / 2,\n y: offH / 2,\n opacity: 1,\n };\n renderShapeDirect(offCtx, tempData, 1);\n\n // Composite temp canvas onto main canvas at element opacity\n ctx.save();\n\n // Position: elementData.(x,y) is the shape center, temp canvas center is (offW/2, offH/2)\n // We need to account for rotation around the element center\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n ctx.globalAlpha = elementOpacity;\n ctx.drawImage(offCanvas, -offW / 2, -offH / 2);\n ctx.restore();\n}\n\n/**\n * Render shape directly onto ctx (no offscreen). Used when there's no\n * compounding risk, or as the inner render for the offscreen path.\n */\nfunction renderShapeDirect(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const transformData = elementData.transformData;\n const fillOpacity = transformData.fillOpacity ?? 1;\n\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Fill\n ctx.globalAlpha = elementOpacity * fillOpacity;\n ctx.fillStyle = transformData.fillColor || '#3b82f6';\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.fill();\n ctx.restore();\n\n // Stroke\n if (elementData.stroke?.enabled) {\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n const stroke = elementData.stroke;\n ctx.globalAlpha = elementOpacity * (stroke.opacity ?? 1);\n ctx.strokeStyle = stroke.color || '#000000';\n ctx.lineWidth = stroke.width || 2;\n ctx.lineCap = stroke.lineCap || 'round';\n ctx.lineJoin = stroke.lineJoin || 'round';\n\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.stroke();\n ctx.restore();\n }\n}\n\n/**\n * Trace the shape path (without fill/stroke) — reusable for both fill and stroke passes.\n */\nfunction traceShapePath(\n ctx: RenderContext,\n transformData: SerializedShapeElement['transformData']\n): void {\n const { shapeType, width, height } = transformData;\n\n switch (shapeType) {\n case 'rectangle': {\n const borderRadius = transformData.borderRadius || 0;\n const x = -width / 2;\n const y = -height / 2;\n\n if (borderRadius > 0) {\n const radius = Math.min((borderRadius / 100) * Math.min(width, height), width / 2, height / 2);\n ctx.roundRect(x, y, width, height, radius);\n } else {\n ctx.rect(x, y, width, height);\n }\n break;\n }\n\n case 'circle': {\n const radius = Math.min(width, height) / 2;\n ctx.arc(0, 0, radius, 0, Math.PI * 2);\n break;\n }\n\n case 'ellipse': {\n ctx.ellipse(0, 0, width / 2, height / 2, 0, 0, Math.PI * 2);\n break;\n }\n\n case 'triangle': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.moveTo(0, -halfHeight);\n ctx.lineTo(halfWidth, halfHeight);\n ctx.lineTo(-halfWidth, halfHeight);\n ctx.closePath();\n break;\n }\n\n case 'polygon': {\n const sides = transformData.sides || 5;\n const radius = Math.min(width, height) / 2;\n for (let i = 0; i < sides; i++) {\n const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;\n const px = radius * Math.cos(angle);\n const py = radius * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'star': {\n const points = transformData.points || 5;\n const outerRadius = Math.min(width, height) / 2;\n const innerRadius = outerRadius * (transformData.innerRadius || 0.4);\n for (let i = 0; i < points * 2; i++) {\n const angle = (i * Math.PI) / points - Math.PI / 2;\n const r = i % 2 === 0 ? outerRadius : innerRadius;\n const px = r * Math.cos(angle);\n const py = r * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'line': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.rect(-halfWidth, -halfHeight, width, height);\n break;\n }\n\n default:\n ctx.rect(-width / 2, -height / 2, width, height);\n }\n}\n"],"names":["renderImageElement","ctx","elementData","imageBitmap","scaleX","scaleY","radius","x","y","w","h","_a","hasKnockoutStroke","_b","imageConfig","canvas","offscreen","renderImageStroke","offCtx","createTempCanvas","width","height","renderShapeElement","elementOpacity","hasStroke","renderShapeWithOffscreen","renderShapeDirect","td","padding","offW","offH","offCanvas","tempData","transformData","fillOpacity","traceShapePath","stroke","shapeType","borderRadius","halfWidth","halfHeight","sides","i","angle","px","py","points","outerRadius","innerRadius","r"],"mappings":"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"}
@@ -1,23 +1,22 @@
1
- import { n as C, A as P, C as w, o as I, b as R, I as A, R as W, S as E, m as O, W as H, q as F, s as L, t as z, u as B, v as K, w as m, x as X, y as J, z as _, B as q } from "./ElementFactory-Ckv6sSev.js";
2
- import { ab as l } from "./HybridHistoryManager-BV6XV0nD.js";
3
- import { ac as V, ad as Y, ae as j, af as G, aa as U, ag as Q, ah as Z, ai as $, aj as x, ak as D, al as oo, am as ro, an as so, ao, ap as io, aq as eo, ar as no, as as to, at as ho, c as co, au as fo, av as lo, aw as po, ax as go, ay as uo, az as ko, aA as To, aB as yo, aC as Mo, aD as bo, aE as So, aF as vo, aG as Co, aH as Po, aI as wo, aJ as Io, aK as Ro } from "./HybridHistoryManager-BV6XV0nD.js";
4
- import { I as Wo } from "./ImportManager-64OYjELO.js";
5
- import { A as Oo, a as Ho, C as Fo, S as Lo } from "./CanvasStateV1-CD3Q94F4.js";
6
- function M(o, r, c) {
1
+ import { j as C, A as P, C as w, q as I, m as R, I as A, k as W, R as E, S as O, T as H, W as F, s as L, t as z, u as B, v as K, w as X, x as m, y as J, z as _, B as j, F as q } from "./ImportManager-Oqu2yB54.js";
2
+ import { ab as l } from "./HybridHistoryManager-jBBnVim8.js";
3
+ import { ac as V, ad as Y, ae as G, af as U, a0 as Q, ag as Z, ah as $, ai as x, aj as D, ak as ss, al as rs, am as os, an as as, ao as is, ap as es, aq as ns, ar as ts, as as hs, at as ds, c as cs, au as fs, av as ls, aw as ps, ax as gs, ay as us, az as ks, aA as Ts, aB as ys, aC as Ms, aD as Ss, aE as bs, aF as vs, aG as Cs, aH as Ps, aI as ws, aJ as Is, aK as Rs } from "./HybridHistoryManager-jBBnVim8.js";
4
+ import { A as Ws, a as Es, C as Os, S as Hs } from "./CanvasStateV1-CJU_xYW5.js";
5
+ function M(s, r, c) {
7
6
  var e, n;
8
- o.save(), o.translate(r.x, r.y), r.rotation && o.rotate(-r.rotation * Math.PI / 180);
7
+ s.save(), s.translate(r.x, r.y), r.rotation && s.rotate(-r.rotation * Math.PI / 180);
9
8
  let i = 1, h = 1;
10
- if (r.flipHorizontal && (i = -1), r.flipVertical && (h = -1), (i !== 1 || h !== 1) && o.scale(i, h), r.opacity !== void 0 && r.opacity !== 1 && (o.globalAlpha = r.opacity), r.borderRadius && r.borderRadius > 0) {
9
+ if (r.flipHorizontal && (i = -1), r.flipVertical && (h = -1), (i !== 1 || h !== 1) && s.scale(i, h), r.opacity !== void 0 && r.opacity !== 1 && (s.globalAlpha = r.opacity), r.borderRadius && r.borderRadius > 0) {
11
10
  const a = Math.min(
12
11
  r.borderRadius / 100 * Math.min(r.width, r.height),
13
12
  r.width / 2,
14
13
  r.height / 2
15
14
  );
16
- o.beginPath();
17
- const s = -r.width / 2, t = -r.height / 2, d = r.width, f = r.height;
18
- o.moveTo(s + a, t), o.lineTo(s + d - a, t), o.arcTo(s + d, t, s + d, t + a, a), o.lineTo(s + d, t + f - a), o.arcTo(s + d, t + f, s + d - a, t + f, a), o.lineTo(s + a, t + f), o.arcTo(s, t + f, s, t + f - a, a), o.lineTo(s, t + a), o.arcTo(s, t, s + a, t, a), o.closePath(), o.clip();
15
+ s.beginPath();
16
+ const o = -r.width / 2, t = -r.height / 2, d = r.width, f = r.height;
17
+ s.moveTo(o + a, t), s.lineTo(o + d - a, t), s.arcTo(o + d, t, o + d, t + a, a), s.lineTo(o + d, t + f - a), s.arcTo(o + d, t + f, o + d - a, t + f, a), s.lineTo(o + a, t + f), s.arcTo(o, t + f, o, t + f - a, a), s.lineTo(o, t + a), s.arcTo(o, t, o + a, t, a), s.closePath(), s.clip();
19
18
  }
20
- if (r.cropX !== void 0 && r.cropWidth !== void 0 ? o.drawImage(
19
+ if (r.cropX !== void 0 && r.cropWidth !== void 0 ? s.drawImage(
21
20
  c,
22
21
  r.cropX,
23
22
  r.cropY || 0,
@@ -27,8 +26,8 @@ function M(o, r, c) {
27
26
  -r.height / 2,
28
27
  r.width,
29
28
  r.height
30
- ) : o.drawImage(c, -r.width / 2, -r.height / 2, r.width, r.height), o.restore(), (e = r.stroke) != null && e.enabled) {
31
- const a = ((n = r.knockoutParts) == null ? void 0 : n.stroke) === !0, s = {
29
+ ) : s.drawImage(c, -r.width / 2, -r.height / 2, r.width, r.height), s.restore(), (e = r.stroke) != null && e.enabled) {
30
+ const a = ((n = r.knockoutParts) == null ? void 0 : n.stroke) === !0, o = {
32
31
  x: r.x,
33
32
  y: r.y,
34
33
  rotation: r.rotation,
@@ -42,42 +41,42 @@ function M(o, r, c) {
42
41
  }
43
42
  };
44
43
  if (a) {
45
- const t = o.canvas;
44
+ const t = s.canvas;
46
45
  let d;
47
46
  if (typeof OffscreenCanvas < "u")
48
47
  d = new OffscreenCanvas(t.width, t.height);
49
48
  else if (typeof document < "u")
50
49
  d = document.createElement("canvas"), d.width = t.width, d.height = t.height;
51
50
  else {
52
- l(o, s);
51
+ l(s, o);
53
52
  return;
54
53
  }
55
54
  const f = d.getContext("2d");
56
55
  if (!f) {
57
- l(o, s);
56
+ l(s, o);
58
57
  return;
59
58
  }
60
- l(f, s, { isKnockout: !0 }), o.save(), o.globalCompositeOperation = "destination-out", o.drawImage(d, 0, 0), o.restore();
59
+ l(f, o, { isKnockout: !0 }), s.save(), s.globalCompositeOperation = "destination-out", s.drawImage(d, 0, 0), s.restore();
61
60
  } else
62
- l(o, s);
61
+ l(s, o);
63
62
  }
64
63
  }
65
- function k(o, r) {
64
+ function k(s, r) {
66
65
  if (typeof OffscreenCanvas < "u")
67
- return new OffscreenCanvas(o, r);
66
+ return new OffscreenCanvas(s, r);
68
67
  const c = document.createElement("canvas");
69
- return c.width = o, c.height = r, c;
68
+ return c.width = s, c.height = r, c;
70
69
  }
71
- function b(o, r) {
70
+ function S(s, r) {
72
71
  var e;
73
72
  const c = r.opacity ?? 1, i = !!((e = r.stroke) != null && e.enabled);
74
- c < 1 && i ? T(o, r, c) : p(o, r, c);
73
+ c < 1 && i ? T(s, r, c) : p(s, r, c);
75
74
  }
76
- function T(o, r, c) {
75
+ function T(s, r, c) {
77
76
  var f;
78
- const i = r.transformData, e = (((f = r.stroke) == null ? void 0 : f.width) || 2) + 2, n = Math.ceil(i.width + e * 2), a = Math.ceil(i.height + e * 2), s = k(n, a), t = s.getContext("2d");
77
+ const i = r.transformData, e = (((f = r.stroke) == null ? void 0 : f.width) || 2) + 2, n = Math.ceil(i.width + e * 2), a = Math.ceil(i.height + e * 2), o = k(n, a), t = o.getContext("2d");
79
78
  if (!t) {
80
- p(o, r, c);
79
+ p(s, r, c);
81
80
  return;
82
81
  }
83
82
  const d = {
@@ -85,135 +84,135 @@ function T(o, r, c) {
85
84
  x: n / 2,
86
85
  y: a / 2
87
86
  };
88
- p(t, d, 1), o.save(), o.translate(r.x, r.y), r.rotation && o.rotate(-r.rotation * Math.PI / 180), o.globalAlpha = c, o.drawImage(s, -n / 2, -a / 2), o.restore();
87
+ p(t, d, 1), s.save(), s.translate(r.x, r.y), r.rotation && s.rotate(-r.rotation * Math.PI / 180), s.globalAlpha = c, s.drawImage(o, -n / 2, -a / 2), s.restore();
89
88
  }
90
- function p(o, r, c) {
89
+ function p(s, r, c) {
91
90
  var e;
92
91
  const i = r.transformData, h = i.fillOpacity ?? 1;
93
- if (o.save(), o.translate(r.x, r.y), r.rotation && o.rotate(-r.rotation * Math.PI / 180), o.globalAlpha = c * h, o.fillStyle = i.fillColor || "#3b82f6", o.beginPath(), u(o, i), o.fill(), o.restore(), (e = r.stroke) != null && e.enabled) {
94
- o.save(), o.translate(r.x, r.y), r.rotation && o.rotate(-r.rotation * Math.PI / 180);
92
+ if (s.save(), s.translate(r.x, r.y), r.rotation && s.rotate(-r.rotation * Math.PI / 180), s.globalAlpha = c * h, s.fillStyle = i.fillColor || "#3b82f6", s.beginPath(), u(s, i), s.fill(), s.restore(), (e = r.stroke) != null && e.enabled) {
93
+ s.save(), s.translate(r.x, r.y), r.rotation && s.rotate(-r.rotation * Math.PI / 180);
95
94
  const n = r.stroke;
96
- o.globalAlpha = c * (n.opacity ?? 1), o.strokeStyle = n.color || "#000000", o.lineWidth = n.width || 2, o.lineCap = n.lineCap || "round", o.lineJoin = n.lineJoin || "round", o.beginPath(), u(o, i), o.stroke(), o.restore();
95
+ s.globalAlpha = c * (n.opacity ?? 1), s.strokeStyle = n.color || "#000000", s.lineWidth = n.width || 2, s.lineCap = n.lineCap || "round", s.lineJoin = n.lineJoin || "round", s.beginPath(), u(s, i), s.stroke(), s.restore();
97
96
  }
98
97
  }
99
- function u(o, r) {
98
+ function u(s, r) {
100
99
  const { shapeType: c, width: i, height: h } = r;
101
100
  switch (c) {
102
101
  case "rectangle": {
103
102
  const e = r.borderRadius || 0, n = -i / 2, a = -h / 2;
104
103
  if (e > 0) {
105
- const s = Math.min(e / 100 * Math.min(i, h), i / 2, h / 2);
106
- o.roundRect(n, a, i, h, s);
104
+ const o = Math.min(e / 100 * Math.min(i, h), i / 2, h / 2);
105
+ s.roundRect(n, a, i, h, o);
107
106
  } else
108
- o.rect(n, a, i, h);
107
+ s.rect(n, a, i, h);
109
108
  break;
110
109
  }
111
110
  case "circle": {
112
111
  const e = Math.min(i, h) / 2;
113
- o.arc(0, 0, e, 0, Math.PI * 2);
112
+ s.arc(0, 0, e, 0, Math.PI * 2);
114
113
  break;
115
114
  }
116
115
  case "ellipse": {
117
- o.ellipse(0, 0, i / 2, h / 2, 0, 0, Math.PI * 2);
116
+ s.ellipse(0, 0, i / 2, h / 2, 0, 0, Math.PI * 2);
118
117
  break;
119
118
  }
120
119
  case "triangle": {
121
120
  const e = i / 2, n = h / 2;
122
- o.moveTo(0, -n), o.lineTo(e, n), o.lineTo(-e, n), o.closePath();
121
+ s.moveTo(0, -n), s.lineTo(e, n), s.lineTo(-e, n), s.closePath();
123
122
  break;
124
123
  }
125
124
  case "polygon": {
126
125
  const e = r.sides || 5, n = Math.min(i, h) / 2;
127
126
  for (let a = 0; a < e; a++) {
128
- const s = a * 2 * Math.PI / e - Math.PI / 2, t = n * Math.cos(s), d = n * Math.sin(s);
129
- a === 0 ? o.moveTo(t, d) : o.lineTo(t, d);
127
+ const o = a * 2 * Math.PI / e - Math.PI / 2, t = n * Math.cos(o), d = n * Math.sin(o);
128
+ a === 0 ? s.moveTo(t, d) : s.lineTo(t, d);
130
129
  }
131
- o.closePath();
130
+ s.closePath();
132
131
  break;
133
132
  }
134
133
  case "star": {
135
134
  const e = r.points || 5, n = Math.min(i, h) / 2, a = n * (r.innerRadius || 0.4);
136
- for (let s = 0; s < e * 2; s++) {
137
- const t = s * Math.PI / e - Math.PI / 2, d = s % 2 === 0 ? n : a, f = d * Math.cos(t), g = d * Math.sin(t);
138
- s === 0 ? o.moveTo(f, g) : o.lineTo(f, g);
135
+ for (let o = 0; o < e * 2; o++) {
136
+ const t = o * Math.PI / e - Math.PI / 2, d = o % 2 === 0 ? n : a, f = d * Math.cos(t), g = d * Math.sin(t);
137
+ o === 0 ? s.moveTo(f, g) : s.lineTo(f, g);
139
138
  }
140
- o.closePath();
139
+ s.closePath();
141
140
  break;
142
141
  }
143
142
  case "line": {
144
143
  const e = i / 2, n = h / 2;
145
- o.rect(-e, -n, i, h);
144
+ s.rect(-e, -n, i, h);
146
145
  break;
147
146
  }
148
147
  default:
149
- o.rect(-i / 2, -h / 2, i, h);
148
+ s.rect(-i / 2, -h / 2, i, h);
150
149
  }
151
150
  }
152
151
  export {
153
152
  C as AlignmentSnapSystem,
154
- Oo as AnyElementConfigSchema,
155
- Ho as AnyTransformDataSchema,
153
+ Ws as AnyElementConfigSchema,
154
+ Es as AnyTransformDataSchema,
156
155
  V as ArchTransform,
157
156
  Y as ArtboardManager,
158
157
  P as ArtboardRenderer,
159
- j as AscendTransform,
158
+ G as AscendTransform,
160
159
  w as CanvasRenderer,
161
- Fo as CanvasStateV1Schema,
162
- G as CircleTransform,
163
- U as CustomTransform,
160
+ Os as CanvasStateV1Schema,
161
+ U as CircleTransform,
162
+ Q as CustomTransform,
164
163
  I as DEFAULT_MAX_BITMAP_DIMENSION,
165
- Q as ElementStore,
164
+ Z as ElementStore,
166
165
  R as ExportManager,
167
- Z as FlagTransform,
168
- $ as HybridHistoryManager,
169
- Wo as ImportManager,
170
- A as InteractionStateMachine,
171
- x as LeanTransform,
172
- D as LogLevel,
173
- W as ResizePipeline,
174
- Lo as SerializedArtboardSchema,
175
- E as SpacingSystem,
176
- O as TransformHandles,
177
- oo as WaveTransform,
178
- H as WorkerExportManager,
179
- ro as applySpaceLayoutRules,
180
- so as applyStrokeStyle,
181
- ao as buildFontString,
182
- io as calculateFixedCornerPosition,
183
- eo as calculateResizeHandles,
184
- no as calculateRotationHandlePosition,
185
- F as clearImageBitmapCache,
186
- L as clearMaskCache,
187
- z as clearRegisteredBitmapKeys,
188
- to as createCirclePath,
189
- ho as createImagePath,
190
- co as createLogger,
191
- fo as createRectPath,
192
- lo as createTextPath,
193
- po as getFontMetrics,
194
- B as getSharedWorkerExportManager,
195
- go as hitTestCircle,
196
- uo as hitTestRect,
197
- K as invalidateMaskCache,
198
- ko as logger,
199
- To as measureTextWidth,
166
+ $ as FlagTransform,
167
+ x as HybridHistoryManager,
168
+ A as ImportManager,
169
+ W as InteractionStateMachine,
170
+ D as LeanTransform,
171
+ ss as LogLevel,
172
+ E as ResizePipeline,
173
+ Hs as SerializedArtboardSchema,
174
+ O as SpacingSystem,
175
+ H as TransformHandles,
176
+ rs as WaveTransform,
177
+ F as WorkerExportManager,
178
+ os as applySpaceLayoutRules,
179
+ as as applyStrokeStyle,
180
+ is as buildFontString,
181
+ es as calculateFixedCornerPosition,
182
+ ns as calculateResizeHandles,
183
+ ts as calculateRotationHandlePosition,
184
+ L as clearImageBitmapCache,
185
+ z as clearMaskCache,
186
+ B as clearRegisteredBitmapKeys,
187
+ hs as createCirclePath,
188
+ ds as createImagePath,
189
+ cs as createLogger,
190
+ fs as createRectPath,
191
+ ls as createTextPath,
192
+ ps as getFontMetrics,
193
+ K as getSharedWorkerExportManager,
194
+ gs as hitTestCircle,
195
+ us as hitTestRect,
196
+ X as invalidateMaskCache,
197
+ ks as logger,
198
+ Ts as measureTextWidth,
200
199
  m as removeFromImageBitmapCache,
201
- yo as renderCustomTransform,
200
+ ys as renderCustomTransform,
202
201
  M as renderImageElement,
203
202
  l as renderImageStroke,
204
- Mo as renderMultilineText,
205
- bo as renderPathStroke,
206
- So as renderRichTextFillOnly,
207
- b as renderShapeElement,
208
- vo as renderTextElement,
209
- Co as renderTextFillOnly,
210
- Po as renderTextStroke,
211
- X as renderWithKnockout,
212
- J as renderWithMasks,
213
- wo as splitRichTextIntoLines,
214
- _ as terminateSharedWorkerExportManager,
203
+ Ms as renderMultilineText,
204
+ Ss as renderPathStroke,
205
+ bs as renderRichTextFillOnly,
206
+ S as renderShapeElement,
207
+ vs as renderTextElement,
208
+ Cs as renderTextFillOnly,
209
+ Ps as renderTextStroke,
210
+ J as renderWithKnockout,
211
+ _ as renderWithMasks,
212
+ ws as splitRichTextIntoLines,
213
+ j as terminateSharedWorkerExportManager,
215
214
  q as unregisterElementBitmaps,
216
- Io as wrapRichTextSpans,
217
- Ro as wrapText
215
+ Is as wrapRichTextSpans,
216
+ Rs as wrapText
218
217
  };
219
218
  //# sourceMappingURL=internals.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"internals.mjs","sources":["../src/rendering/image-renderer.ts","../src/rendering/shape-renderer.ts"],"sourcesContent":["/**\n * Image Renderer - Image element rendering with crop, flip, border radius, and stroke.\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n */\n\nimport type { ImageElementConfig } from '../types/index.js';\nimport { renderImageStroke } from './StrokeRenderer.js';\nimport type { RenderContext, SerializedImageElement } from './renderer-types.js';\n\n/**\n * Render an image element using ImageBitmap\n * Works in both main thread and worker\n */\nexport function renderImageElement(\n ctx: RenderContext,\n elementData: SerializedImageElement,\n imageBitmap: ImageBitmap\n): void {\n ctx.save();\n\n // Translate to element position\n ctx.translate(elementData.x, elementData.y);\n\n // Apply rotation (negative for clockwise)\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Apply flip transformations\n let scaleX = 1;\n let scaleY = 1;\n if (elementData.flipHorizontal) scaleX = -1;\n if (elementData.flipVertical) scaleY = -1;\n if (scaleX !== 1 || scaleY !== 1) {\n ctx.scale(scaleX, scaleY);\n }\n\n // Apply opacity if specified\n if (elementData.opacity !== undefined && elementData.opacity !== 1) {\n ctx.globalAlpha = elementData.opacity;\n }\n\n // Apply border radius if specified (borderRadius is stored as percentage 0-100)\n if (elementData.borderRadius && elementData.borderRadius > 0) {\n const radius = Math.min(\n (elementData.borderRadius / 100) * Math.min(elementData.width, elementData.height),\n elementData.width / 2,\n elementData.height / 2\n );\n\n // Create clipping path for rounded corners\n ctx.beginPath();\n const x = -elementData.width / 2;\n const y = -elementData.height / 2;\n const w = elementData.width;\n const h = elementData.height;\n\n ctx.moveTo(x + radius, y);\n ctx.lineTo(x + w - radius, y);\n ctx.arcTo(x + w, y, x + w, y + radius, radius);\n ctx.lineTo(x + w, y + h - radius);\n ctx.arcTo(x + w, y + h, x + w - radius, y + h, radius);\n ctx.lineTo(x + radius, y + h);\n ctx.arcTo(x, y + h, x, y + h - radius, radius);\n ctx.lineTo(x, y + radius);\n ctx.arcTo(x, y, x + radius, y, radius);\n ctx.closePath();\n ctx.clip();\n }\n\n // Draw the image\n if (elementData.cropX !== undefined && elementData.cropWidth !== undefined) {\n // Draw with crop\n ctx.drawImage(\n imageBitmap,\n elementData.cropX,\n elementData.cropY || 0,\n elementData.cropWidth,\n elementData.cropHeight || imageBitmap.height,\n -elementData.width / 2,\n -elementData.height / 2,\n elementData.width,\n elementData.height\n );\n } else {\n // Draw without crop (centered)\n ctx.drawImage(imageBitmap, -elementData.width / 2, -elementData.height / 2, elementData.width, elementData.height);\n }\n\n ctx.restore();\n\n // Render stroke if enabled (after restore to apply fresh transforms)\n if (elementData.stroke?.enabled) {\n const hasKnockoutStroke = elementData.knockoutParts?.stroke === true;\n\n const imageConfig: ImageElementConfig = {\n transformType: 'image',\n x: elementData.x,\n y: elementData.y,\n rotation: elementData.rotation,\n stroke: elementData.stroke,\n knockoutParts: elementData.knockoutParts,\n transformData: {\n type: 'image',\n width: elementData.width,\n height: elementData.height,\n borderRadius: elementData.borderRadius || 0,\n },\n };\n\n if (hasKnockoutStroke) {\n // For knockout stroke, use offscreen canvas approach\n const canvas = ctx.canvas;\n let offscreen: HTMLCanvasElement | OffscreenCanvas;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n offscreen = new OffscreenCanvas(canvas.width, canvas.height);\n } else if (typeof document !== 'undefined') {\n offscreen = document.createElement('canvas');\n offscreen.width = canvas.width;\n offscreen.height = canvas.height;\n } else {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n // Render the stroke to the offscreen canvas with knockout flag\n renderImageStroke(offCtx, imageConfig, { isKnockout: true });\n\n // Composite with destination-out\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.drawImage(offscreen as CanvasImageSource, 0, 0);\n ctx.restore();\n } else {\n // Normal stroke\n renderImageStroke(ctx, imageConfig);\n }\n }\n}\n","/**\n * Shape Renderer - Shape element rendering (rectangle, circle, ellipse, triangle, etc.)\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n *\n * Opacity strategy: When a shape has both element-level opacity AND a stroke,\n * we render fill+stroke at full opacity on a temporary canvas, then composite\n * the result at the element's opacity. This prevents fill/stroke overlap from\n * compounding (e.g., 50% fill + 50% stroke overlap ≠ 75% opaque).\n */\n\nimport type { RenderContext, SerializedShapeElement } from './renderer-types.js';\n\ntype CanvasLike = HTMLCanvasElement | OffscreenCanvas;\n\nfunction createTempCanvas(width: number, height: number): CanvasLike {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(width, height);\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return canvas;\n}\n\n/**\n * Render a shape element\n * Works in both main thread and worker\n */\nexport function renderShapeElement(ctx: RenderContext, elementData: SerializedShapeElement): void {\n const elementOpacity = elementData.opacity ?? 1;\n const hasStroke = !!elementData.stroke?.enabled;\n const needsOffscreen = elementOpacity < 1 && hasStroke;\n\n if (needsOffscreen) {\n // Render fill+stroke at full opacity on a temp canvas, then composite\n renderShapeWithOffscreen(ctx, elementData, elementOpacity);\n } else {\n // No compounding risk — render directly\n renderShapeDirect(ctx, elementData, elementOpacity);\n }\n}\n\n/**\n * Render shape via temporary canvas to avoid fill/stroke opacity compounding.\n */\nfunction renderShapeWithOffscreen(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const td = elementData.transformData;\n const strokeWidth = elementData.stroke?.width || 2;\n // Pad temp canvas to fit the stroke (strokes extend beyond the shape bounds)\n const padding = strokeWidth + 2;\n const offW = Math.ceil(td.width + padding * 2);\n const offH = Math.ceil(td.height + padding * 2);\n\n const offCanvas = createTempCanvas(offW, offH);\n const offCtx = offCanvas.getContext('2d') as RenderContext;\n if (!offCtx) {\n // Fallback to direct render\n renderShapeDirect(ctx, elementData, elementOpacity);\n return;\n }\n\n // Render at center of temp canvas with full opacity (elementOpacity = 1)\n const tempData: SerializedShapeElement = {\n ...elementData,\n x: offW / 2,\n y: offH / 2,\n opacity: 1,\n };\n renderShapeDirect(offCtx, tempData, 1);\n\n // Composite temp canvas onto main canvas at element opacity\n ctx.save();\n\n // Position: elementData.(x,y) is the shape center, temp canvas center is (offW/2, offH/2)\n // We need to account for rotation around the element center\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n ctx.globalAlpha = elementOpacity;\n ctx.drawImage(offCanvas, -offW / 2, -offH / 2);\n ctx.restore();\n}\n\n/**\n * Render shape directly onto ctx (no offscreen). Used when there's no\n * compounding risk, or as the inner render for the offscreen path.\n */\nfunction renderShapeDirect(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const transformData = elementData.transformData;\n const fillOpacity = transformData.fillOpacity ?? 1;\n\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Fill\n ctx.globalAlpha = elementOpacity * fillOpacity;\n ctx.fillStyle = transformData.fillColor || '#3b82f6';\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.fill();\n ctx.restore();\n\n // Stroke\n if (elementData.stroke?.enabled) {\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n const stroke = elementData.stroke;\n ctx.globalAlpha = elementOpacity * (stroke.opacity ?? 1);\n ctx.strokeStyle = stroke.color || '#000000';\n ctx.lineWidth = stroke.width || 2;\n ctx.lineCap = stroke.lineCap || 'round';\n ctx.lineJoin = stroke.lineJoin || 'round';\n\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.stroke();\n ctx.restore();\n }\n}\n\n/**\n * Trace the shape path (without fill/stroke) — reusable for both fill and stroke passes.\n */\nfunction traceShapePath(\n ctx: RenderContext,\n transformData: SerializedShapeElement['transformData']\n): void {\n const { shapeType, width, height } = transformData;\n\n switch (shapeType) {\n case 'rectangle': {\n const borderRadius = transformData.borderRadius || 0;\n const x = -width / 2;\n const y = -height / 2;\n\n if (borderRadius > 0) {\n const radius = Math.min((borderRadius / 100) * Math.min(width, height), width / 2, height / 2);\n ctx.roundRect(x, y, width, height, radius);\n } else {\n ctx.rect(x, y, width, height);\n }\n break;\n }\n\n case 'circle': {\n const radius = Math.min(width, height) / 2;\n ctx.arc(0, 0, radius, 0, Math.PI * 2);\n break;\n }\n\n case 'ellipse': {\n ctx.ellipse(0, 0, width / 2, height / 2, 0, 0, Math.PI * 2);\n break;\n }\n\n case 'triangle': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.moveTo(0, -halfHeight);\n ctx.lineTo(halfWidth, halfHeight);\n ctx.lineTo(-halfWidth, halfHeight);\n ctx.closePath();\n break;\n }\n\n case 'polygon': {\n const sides = transformData.sides || 5;\n const radius = Math.min(width, height) / 2;\n for (let i = 0; i < sides; i++) {\n const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;\n const px = radius * Math.cos(angle);\n const py = radius * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'star': {\n const points = transformData.points || 5;\n const outerRadius = Math.min(width, height) / 2;\n const innerRadius = outerRadius * (transformData.innerRadius || 0.4);\n for (let i = 0; i < points * 2; i++) {\n const angle = (i * Math.PI) / points - Math.PI / 2;\n const r = i % 2 === 0 ? outerRadius : innerRadius;\n const px = r * Math.cos(angle);\n const py = r * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'line': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.rect(-halfWidth, -halfHeight, width, height);\n break;\n }\n\n default:\n ctx.rect(-width / 2, -height / 2, width, height);\n }\n}\n"],"names":["renderImageElement","ctx","elementData","imageBitmap","scaleX","scaleY","radius","x","y","w","h","_a","hasKnockoutStroke","_b","imageConfig","canvas","offscreen","renderImageStroke","offCtx","createTempCanvas","width","height","renderShapeElement","elementOpacity","hasStroke","renderShapeWithOffscreen","renderShapeDirect","td","padding","offW","offH","offCanvas","tempData","transformData","fillOpacity","traceShapePath","stroke","shapeType","borderRadius","halfWidth","halfHeight","sides","i","angle","px","py","points","outerRadius","innerRadius","r"],"mappings":"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"}
1
+ {"version":3,"file":"internals.mjs","sources":["../src/rendering/image-renderer.ts","../src/rendering/shape-renderer.ts"],"sourcesContent":["/**\n * Image Renderer - Image element rendering with crop, flip, border radius, and stroke.\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n */\n\nimport type { ImageElementConfig } from '../types/index.js';\nimport { renderImageStroke } from './StrokeRenderer.js';\nimport type { RenderContext, SerializedImageElement } from './renderer-types.js';\n\n/**\n * Render an image element using ImageBitmap\n * Works in both main thread and worker\n */\nexport function renderImageElement(\n ctx: RenderContext,\n elementData: SerializedImageElement,\n imageBitmap: ImageBitmap\n): void {\n ctx.save();\n\n // Translate to element position\n ctx.translate(elementData.x, elementData.y);\n\n // Apply rotation (negative for clockwise)\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Apply flip transformations\n let scaleX = 1;\n let scaleY = 1;\n if (elementData.flipHorizontal) scaleX = -1;\n if (elementData.flipVertical) scaleY = -1;\n if (scaleX !== 1 || scaleY !== 1) {\n ctx.scale(scaleX, scaleY);\n }\n\n // Apply opacity if specified\n if (elementData.opacity !== undefined && elementData.opacity !== 1) {\n ctx.globalAlpha = elementData.opacity;\n }\n\n // Apply border radius if specified (borderRadius is stored as percentage 0-100)\n if (elementData.borderRadius && elementData.borderRadius > 0) {\n const radius = Math.min(\n (elementData.borderRadius / 100) * Math.min(elementData.width, elementData.height),\n elementData.width / 2,\n elementData.height / 2\n );\n\n // Create clipping path for rounded corners\n ctx.beginPath();\n const x = -elementData.width / 2;\n const y = -elementData.height / 2;\n const w = elementData.width;\n const h = elementData.height;\n\n ctx.moveTo(x + radius, y);\n ctx.lineTo(x + w - radius, y);\n ctx.arcTo(x + w, y, x + w, y + radius, radius);\n ctx.lineTo(x + w, y + h - radius);\n ctx.arcTo(x + w, y + h, x + w - radius, y + h, radius);\n ctx.lineTo(x + radius, y + h);\n ctx.arcTo(x, y + h, x, y + h - radius, radius);\n ctx.lineTo(x, y + radius);\n ctx.arcTo(x, y, x + radius, y, radius);\n ctx.closePath();\n ctx.clip();\n }\n\n // Draw the image\n if (elementData.cropX !== undefined && elementData.cropWidth !== undefined) {\n // Draw with crop\n ctx.drawImage(\n imageBitmap,\n elementData.cropX,\n elementData.cropY || 0,\n elementData.cropWidth,\n elementData.cropHeight || imageBitmap.height,\n -elementData.width / 2,\n -elementData.height / 2,\n elementData.width,\n elementData.height\n );\n } else {\n // Draw without crop (centered)\n ctx.drawImage(imageBitmap, -elementData.width / 2, -elementData.height / 2, elementData.width, elementData.height);\n }\n\n ctx.restore();\n\n // Render stroke if enabled (after restore to apply fresh transforms)\n if (elementData.stroke?.enabled) {\n const hasKnockoutStroke = elementData.knockoutParts?.stroke === true;\n\n const imageConfig: ImageElementConfig = {\n transformType: 'image',\n x: elementData.x,\n y: elementData.y,\n rotation: elementData.rotation,\n stroke: elementData.stroke,\n knockoutParts: elementData.knockoutParts,\n transformData: {\n type: 'image',\n width: elementData.width,\n height: elementData.height,\n borderRadius: elementData.borderRadius || 0,\n },\n };\n\n if (hasKnockoutStroke) {\n // For knockout stroke, use offscreen canvas approach\n const canvas = ctx.canvas;\n let offscreen: HTMLCanvasElement | OffscreenCanvas;\n\n if (typeof OffscreenCanvas !== 'undefined') {\n offscreen = new OffscreenCanvas(canvas.width, canvas.height);\n } else if (typeof document !== 'undefined') {\n offscreen = document.createElement('canvas');\n offscreen.width = canvas.width;\n offscreen.height = canvas.height;\n } else {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n const offCtx = offscreen.getContext('2d');\n if (!offCtx) {\n renderImageStroke(ctx, imageConfig);\n return;\n }\n\n // Render the stroke to the offscreen canvas with knockout flag\n renderImageStroke(offCtx, imageConfig, { isKnockout: true });\n\n // Composite with destination-out\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.drawImage(offscreen as CanvasImageSource, 0, 0);\n ctx.restore();\n } else {\n // Normal stroke\n renderImageStroke(ctx, imageConfig);\n }\n }\n}\n","/**\n * Shape Renderer - Shape element rendering (rectangle, circle, ellipse, triangle, etc.)\n *\n * Works in both main thread and Web Worker (via OffscreenCanvas).\n *\n * Opacity strategy: When a shape has both element-level opacity AND a stroke,\n * we render fill+stroke at full opacity on a temporary canvas, then composite\n * the result at the element's opacity. This prevents fill/stroke overlap from\n * compounding (e.g., 50% fill + 50% stroke overlap ≠ 75% opaque).\n */\n\nimport type { RenderContext, SerializedShapeElement } from './renderer-types.js';\n\ntype CanvasLike = HTMLCanvasElement | OffscreenCanvas;\n\nfunction createTempCanvas(width: number, height: number): CanvasLike {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(width, height);\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return canvas;\n}\n\n/**\n * Render a shape element\n * Works in both main thread and worker\n */\nexport function renderShapeElement(ctx: RenderContext, elementData: SerializedShapeElement): void {\n const elementOpacity = elementData.opacity ?? 1;\n const hasStroke = !!elementData.stroke?.enabled;\n const needsOffscreen = elementOpacity < 1 && hasStroke;\n\n if (needsOffscreen) {\n // Render fill+stroke at full opacity on a temp canvas, then composite\n renderShapeWithOffscreen(ctx, elementData, elementOpacity);\n } else {\n // No compounding risk — render directly\n renderShapeDirect(ctx, elementData, elementOpacity);\n }\n}\n\n/**\n * Render shape via temporary canvas to avoid fill/stroke opacity compounding.\n */\nfunction renderShapeWithOffscreen(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const td = elementData.transformData;\n const strokeWidth = elementData.stroke?.width || 2;\n // Pad temp canvas to fit the stroke (strokes extend beyond the shape bounds)\n const padding = strokeWidth + 2;\n const offW = Math.ceil(td.width + padding * 2);\n const offH = Math.ceil(td.height + padding * 2);\n\n const offCanvas = createTempCanvas(offW, offH);\n const offCtx = offCanvas.getContext('2d') as RenderContext;\n if (!offCtx) {\n // Fallback to direct render\n renderShapeDirect(ctx, elementData, elementOpacity);\n return;\n }\n\n // Render at center of temp canvas with full opacity (elementOpacity = 1)\n const tempData: SerializedShapeElement = {\n ...elementData,\n x: offW / 2,\n y: offH / 2,\n opacity: 1,\n };\n renderShapeDirect(offCtx, tempData, 1);\n\n // Composite temp canvas onto main canvas at element opacity\n ctx.save();\n\n // Position: elementData.(x,y) is the shape center, temp canvas center is (offW/2, offH/2)\n // We need to account for rotation around the element center\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n ctx.globalAlpha = elementOpacity;\n ctx.drawImage(offCanvas, -offW / 2, -offH / 2);\n ctx.restore();\n}\n\n/**\n * Render shape directly onto ctx (no offscreen). Used when there's no\n * compounding risk, or as the inner render for the offscreen path.\n */\nfunction renderShapeDirect(\n ctx: RenderContext,\n elementData: SerializedShapeElement,\n elementOpacity: number\n): void {\n const transformData = elementData.transformData;\n const fillOpacity = transformData.fillOpacity ?? 1;\n\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n // Fill\n ctx.globalAlpha = elementOpacity * fillOpacity;\n ctx.fillStyle = transformData.fillColor || '#3b82f6';\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.fill();\n ctx.restore();\n\n // Stroke\n if (elementData.stroke?.enabled) {\n ctx.save();\n ctx.translate(elementData.x, elementData.y);\n if (elementData.rotation) {\n ctx.rotate((-elementData.rotation * Math.PI) / 180);\n }\n\n const stroke = elementData.stroke;\n ctx.globalAlpha = elementOpacity * (stroke.opacity ?? 1);\n ctx.strokeStyle = stroke.color || '#000000';\n ctx.lineWidth = stroke.width || 2;\n ctx.lineCap = stroke.lineCap || 'round';\n ctx.lineJoin = stroke.lineJoin || 'round';\n\n ctx.beginPath();\n traceShapePath(ctx, transformData);\n ctx.stroke();\n ctx.restore();\n }\n}\n\n/**\n * Trace the shape path (without fill/stroke) — reusable for both fill and stroke passes.\n */\nfunction traceShapePath(\n ctx: RenderContext,\n transformData: SerializedShapeElement['transformData']\n): void {\n const { shapeType, width, height } = transformData;\n\n switch (shapeType) {\n case 'rectangle': {\n const borderRadius = transformData.borderRadius || 0;\n const x = -width / 2;\n const y = -height / 2;\n\n if (borderRadius > 0) {\n const radius = Math.min((borderRadius / 100) * Math.min(width, height), width / 2, height / 2);\n ctx.roundRect(x, y, width, height, radius);\n } else {\n ctx.rect(x, y, width, height);\n }\n break;\n }\n\n case 'circle': {\n const radius = Math.min(width, height) / 2;\n ctx.arc(0, 0, radius, 0, Math.PI * 2);\n break;\n }\n\n case 'ellipse': {\n ctx.ellipse(0, 0, width / 2, height / 2, 0, 0, Math.PI * 2);\n break;\n }\n\n case 'triangle': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.moveTo(0, -halfHeight);\n ctx.lineTo(halfWidth, halfHeight);\n ctx.lineTo(-halfWidth, halfHeight);\n ctx.closePath();\n break;\n }\n\n case 'polygon': {\n const sides = transformData.sides || 5;\n const radius = Math.min(width, height) / 2;\n for (let i = 0; i < sides; i++) {\n const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;\n const px = radius * Math.cos(angle);\n const py = radius * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'star': {\n const points = transformData.points || 5;\n const outerRadius = Math.min(width, height) / 2;\n const innerRadius = outerRadius * (transformData.innerRadius || 0.4);\n for (let i = 0; i < points * 2; i++) {\n const angle = (i * Math.PI) / points - Math.PI / 2;\n const r = i % 2 === 0 ? outerRadius : innerRadius;\n const px = r * Math.cos(angle);\n const py = r * Math.sin(angle);\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.closePath();\n break;\n }\n\n case 'line': {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n ctx.rect(-halfWidth, -halfHeight, width, height);\n break;\n }\n\n default:\n ctx.rect(-width / 2, -height / 2, width, height);\n }\n}\n"],"names":["renderImageElement","ctx","elementData","imageBitmap","scaleX","scaleY","radius","x","y","w","h","_a","hasKnockoutStroke","_b","imageConfig","canvas","offscreen","renderImageStroke","offCtx","createTempCanvas","width","height","renderShapeElement","elementOpacity","hasStroke","renderShapeWithOffscreen","renderShapeDirect","td","padding","offW","offH","offCanvas","tempData","transformData","fillOpacity","traceShapePath","stroke","shapeType","borderRadius","halfWidth","halfHeight","sides","i","angle","px","py","points","outerRadius","innerRadius","r"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ // Ambient declaration for the side-effect CSS import.
2
+ // Lets `import '@snowcone-app/canvas/style.css'` type-check in consumer projects.
3
+ declare const styles: void;
4
+ export default styles;
package/dist/testing.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react/jsx-runtime"),u=require("react"),c=require("./ThemeContext-4mJ_y0Me.cjs"),l=require("./HybridHistoryManager-BXD93pp8.cjs"),h=({children:t,initialElements:e=[],initialArtboards:n,initialSelection:r=null,initialMultiSelection:o=[],initialArtboardConfig:d={}})=>{const[F]=u.useState(()=>{const s=new l.ArtboardManager;return n&&n.length>0?n.forEach(i=>{s.createArtboard({name:i.name,width:i.width,height:i.height,backgroundColor:i.backgroundColor})}):s.createArtboard({name:"Test Artboard",width:1200,height:1200,backgroundColor:"#FFFFFF",...d}),s});return a.jsx(c.ThemeProvider,{children:a.jsx(c.EditorProvider,{initialArtboardConfig:d,children:t})})},g=({children:t,initialElements:e=[],initialSelection:n=null,initialMultiSelection:r=[],initialArtboardConfig:o={}})=>a.jsx(c.ThemeProvider,{children:a.jsx(c.EditorProvider,{initialArtboardConfig:o,children:t})});function E(t="custom",e={}){const n=l.getTransformById(t);if(!n||!n.Component)throw new Error(`Transform type "${t}" not found in registry`);return new n.Component({transformType:t,text:"Test Text",fontSize:32,color:"#000000",fontFamily:"Arial",x:100,y:100,rotation:0,...e})}function A(t={}){return new l.ImageElement({imageUrl:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",imageAspectRatio:1,x:100,y:100,rotation:0,...t})}function w(t={}){return new l.ArtboardElement({name:"Test Artboard",width:1200,height:1200,x:0,y:0,backgroundColor:"#FFFFFF",...t})}async function m(t,e=5e3,n=100){const r=Date.now();for(;!t();){if(Date.now()-r>e)throw new Error(`Timeout waiting for condition after ${e}ms`);await new Promise(o=>setTimeout(o,n))}}async function f(t,e=5e3){let n=null;return await m(()=>!1,e),n}function b(t,e={}){const n=new KeyboardEvent("keydown",{key:t,ctrlKey:e.ctrl,shiftKey:e.shift,altKey:e.alt,metaKey:e.meta,bubbles:!0,cancelable:!0});document.dispatchEvent(n)}function M(t=800,e=600){const n=document.createElement("canvas");return n.width=t,n.height=e,n}function y(t,e){Object.entries(e).forEach(([n,r])=>{if(t[n]!==r)throw new Error(`Expected element.${n} to be ${r}, got ${t[n]}`)})}function x(t){const e=t.getBoundingBox();return{x:e.x,y:e.y,width:e.width,height:e.height}}exports.MockEditorProvider=h;exports.MockEditorProviderWithState=g;exports.assertElementProperties=y;exports.createMockArtboard=w;exports.createMockCanvas=M;exports.createMockElement=E;exports.createMockImageElement=A;exports.getElementBounds=x;exports.simulateKeyPress=b;exports.waitFor=m;exports.waitForExport=f;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react/jsx-runtime"),u=require("react"),c=require("./ThemeContext-BMNQKl1c.cjs"),l=require("./HybridHistoryManager-BXD93pp8.cjs"),h=({children:t,initialElements:e=[],initialArtboards:n,initialSelection:r=null,initialMultiSelection:o=[],initialArtboardConfig:d={}})=>{const[F]=u.useState(()=>{const s=new l.ArtboardManager;return n&&n.length>0?n.forEach(i=>{s.createArtboard({name:i.name,width:i.width,height:i.height,backgroundColor:i.backgroundColor})}):s.createArtboard({name:"Test Artboard",width:1200,height:1200,backgroundColor:"#FFFFFF",...d}),s});return a.jsx(c.ThemeProvider,{children:a.jsx(c.EditorProvider,{initialArtboardConfig:d,children:t})})},g=({children:t,initialElements:e=[],initialSelection:n=null,initialMultiSelection:r=[],initialArtboardConfig:o={}})=>a.jsx(c.ThemeProvider,{children:a.jsx(c.EditorProvider,{initialArtboardConfig:o,children:t})});function E(t="custom",e={}){const n=l.getTransformById(t);if(!n||!n.Component)throw new Error(`Transform type "${t}" not found in registry`);return new n.Component({transformType:t,text:"Test Text",fontSize:32,color:"#000000",fontFamily:"Arial",x:100,y:100,rotation:0,...e})}function A(t={}){return new l.ImageElement({imageUrl:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",imageAspectRatio:1,x:100,y:100,rotation:0,...t})}function w(t={}){return new l.ArtboardElement({name:"Test Artboard",width:1200,height:1200,x:0,y:0,backgroundColor:"#FFFFFF",...t})}async function m(t,e=5e3,n=100){const r=Date.now();for(;!t();){if(Date.now()-r>e)throw new Error(`Timeout waiting for condition after ${e}ms`);await new Promise(o=>setTimeout(o,n))}}async function f(t,e=5e3){let n=null;return await m(()=>!1,e),n}function b(t,e={}){const n=new KeyboardEvent("keydown",{key:t,ctrlKey:e.ctrl,shiftKey:e.shift,altKey:e.alt,metaKey:e.meta,bubbles:!0,cancelable:!0});document.dispatchEvent(n)}function M(t=800,e=600){const n=document.createElement("canvas");return n.width=t,n.height=e,n}function y(t,e){Object.entries(e).forEach(([n,r])=>{if(t[n]!==r)throw new Error(`Expected element.${n} to be ${r}, got ${t[n]}`)})}function x(t){const e=t.getBoundingBox();return{x:e.x,y:e.y,width:e.width,height:e.height}}exports.MockEditorProvider=h;exports.MockEditorProviderWithState=g;exports.assertElementProperties=y;exports.createMockArtboard=w;exports.createMockCanvas=M;exports.createMockElement=E;exports.createMockImageElement=A;exports.getElementBounds=x;exports.simulateKeyPress=b;exports.waitFor=m;exports.waitForExport=f;
2
2
  //# sourceMappingURL=testing.js.map