domma-cms 0.7.9 → 0.8.0

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 (38) hide show
  1. package/admin/css/admin.css +1 -1
  2. package/admin/js/app.js +1 -1
  3. package/admin/js/config/sidebar-config.js +1 -1
  4. package/admin/js/templates/effects.html +594 -0
  5. package/admin/js/views/effects.js +1 -0
  6. package/admin/js/views/index.js +1 -1
  7. package/admin/js/views/plugins.js +24 -12
  8. package/config/effects.json +6 -0
  9. package/config/plugins.json +5 -31
  10. package/package.json +1 -1
  11. package/plugins/demo-viewer/config.js +4 -0
  12. package/plugins/demo-viewer/plugin.js +36 -0
  13. package/plugins/demo-viewer/plugin.json +9 -0
  14. package/plugins/theme-switcher/admin/templates/theme-switcher.html +86 -0
  15. package/plugins/theme-switcher/admin/views/theme-switcher.js +65 -0
  16. package/plugins/theme-switcher/config.js +10 -0
  17. package/plugins/theme-switcher/plugin.js +26 -0
  18. package/plugins/theme-switcher/plugin.json +74 -0
  19. package/plugins/theme-switcher/public/inject-body.html +153 -0
  20. package/plugins/theme-switcher/public/inject-head.html +260 -0
  21. package/public/js/celebrations/core/canvas.js +9 -0
  22. package/public/js/celebrations/core/particles.js +1 -0
  23. package/public/js/celebrations/core/physics.js +1 -0
  24. package/public/js/celebrations/index.js +1 -0
  25. package/public/js/celebrations/themes/christmas.js +1 -0
  26. package/public/js/celebrations/themes/guy-fawkes.js +1 -0
  27. package/public/js/celebrations/themes/halloween.js +1 -0
  28. package/public/js/celebrations/themes/st-andrews.js +1 -0
  29. package/public/js/celebrations/themes/st-davids.js +1 -0
  30. package/public/js/celebrations/themes/st-georges.js +1 -0
  31. package/public/js/celebrations/themes/st-patricks.js +1 -0
  32. package/public/js/celebrations/themes/valentines.js +1 -0
  33. package/public/js/effects.js +1 -0
  34. package/server/routes/api/effects.js +23 -0
  35. package/server/server.js +2 -0
  36. package/server/services/markdown.js +125 -2
  37. package/server/services/plugins.js +1 -1
  38. package/server/templates/page.html +3 -0
@@ -0,0 +1 @@
1
+ export default{name:"st-patricks",displayName:"St Patrick's Day",emoji:"\u2618\uFE0F",intensityConfig:{light:{count:40,speedRange:[.4,1.2],sizeRange:[2,4],pots:1,leprechaunChance:3e-4,twinklingStars:10},medium:{count:80,speedRange:[.5,1.5],sizeRange:[2,5],pots:2,leprechaunChance:5e-4,twinklingStars:18},heavy:{count:120,speedRange:[.6,1.8],sizeRange:[3,6],pots:3,leprechaunChance:8e-4,twinklingStars:25}},particles:["clover-petal","shamrock","gold-coin","sparkle"],decorations:["pot-of-gold","rainbow","leprechaun","harp","static-leprechaun","moon","twinkling-star"],colors:{primary:"#228B22",secondary:"#90EE90",white:"#FFFFFF",accent:"#FFD700",rainbow:["#ff0000","#ffa500","#ffff00","#00ff00","#0000ff","#4b0082","#8b00ff"]},createCloverPetal(e,o,n){const s=["#228B22","#32CD32","#90EE90","#3CB371","#2E8B57"];return{type:"clover-petal",x:-30,y:Math.random()*o,vx:n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]),size:(n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0]))*1.5,speed:(Math.random()-.5)*.2,opacity:.75+Math.random()*.25,windOffset:Math.random()*Math.PI*2,windSpeed:.015+Math.random()*.025,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.03,color:s[Math.floor(Math.random()*s.length)],flutter:Math.random()*Math.PI*2,flutterSpeed:.02+Math.random()*.02,active:!0}},createShamrock(e,o,n){const s=Math.random();let l,a;return s<.5?(l="#228B22",a="#006400"):s<.75?(l="#FFFFFF",a="#d0d0d0"):(l="#FFD700",a="#DAA520"),{type:"shamrock",x:-30,y:Math.random()*o,vx:n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]),size:n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0]),speed:(Math.random()-.5)*.15,opacity:.7+Math.random()*.3,windOffset:Math.random()*Math.PI*2,windSpeed:.015+Math.random()*.025,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.02,color:l,strokeColor:a,active:!0}},createGoldCoin(e,o,n){return{type:"gold-coin",x:-30,y:Math.random()*o,vx:(n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]))*.8,size:2+Math.random()*3,speed:(Math.random()-.5)*.1,opacity:.9+Math.random()*.1,rotation:0,rotationSpeed:.05+Math.random()*.1,spinPhase:Math.random()*Math.PI*2,glintPhase:Math.random()*Math.PI*2,windOffset:Math.random()*Math.PI*2,windSpeed:.01+Math.random()*.02,active:!0}},createPotOfGold(e,o,n={}){return{type:"pot-of-gold",x:n.x!==void 0?n.x:Math.random()*e*.6+e*.2,y:n.y!==void 0?n.y:o-40,size:20+Math.random()*10,opacity:1,glintPhase:Math.random()*Math.PI*2,coins:[],active:!0,static:!0}},createRainbow(e,o,n,s){const a=n,r=(20+n)/2,h=o*.2,t=s-h/2;return{type:"rainbow",x:r,y:t,startX:20,endX:a,endY:s,width:e,height:o,opacity:.7,vx:0,vy:0,active:!0,static:!0}},createSparkle(e,o,n){const s=["#FFD700","#FFFFFF","#228B22"];return{type:"sparkle",x:-20,y:Math.random()*o,vx:(n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]))*.8,size:n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0])*.6,vy:(Math.random()-.5)*.2,opacity:.6+Math.random()*.4,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.04,color:s[Math.floor(Math.random()*s.length)],twinklePhase:Math.random()*Math.PI*2,windOffset:Math.random()*Math.PI*2,windSpeed:.015+Math.random()*.02,active:!0,static:!1}},createFallingParticle(e,o,n){const s=Math.random();return s<.6?this.createCloverPetal(e,o,n):s<.8?this.createShamrock(e,o,n):s<.95?this.createGoldCoin(e,o,n):this.createSparkle(e,o,n)},createInitialDecorations(e,o,n){const s=[];s.push({type:"moon",x:e-120,y:100,size:60+Math.random()*20,opacity:.85,glowPhase:Math.random()*Math.PI*2,active:!0,static:!0});const l=20,a=220,r=o-40;s.push(this.createPotOfGold(e,o,{x:a,y:r})),s.push(this.createRainbow(e,o,a,r));const h=1+Math.floor(Math.random()*2);for(let f=0;f<h;f++){const P=60+Math.random()*(e-120);s.push({type:"static-leprechaun",x:P,y:o-35,size:12+Math.random()*4,opacity:1,time:Math.random()*1e3,wavePhase:Math.random()*Math.PI*2,active:!0,static:!0})}const t=n.twinklingStars||18;for(let f=0;f<t;f++)s.push(this.createTwinklingStar(e,o));return s},createTwinklingStar(e,o){return{type:"twinkling-star",x:Math.random()*e,y:Math.random()*(o*.5),size:1+Math.random()*2,opacity:.6+Math.random()*.3,twinklePhase:Math.random()*Math.PI*2,twinkleSpeed:.003+Math.random()*.003,active:!0,static:!0}},spawnSpecialParticle(e,o,n,s){const l=Math.random();if(l<s.leprechaunChance){if(e.some(t=>t.type==="leprechaun"))return null;const a=Math.random()<.5,r=(o+100)/180;return{type:"leprechaun",x:a?-50:o+50,y:n-35,baseY:n-35,vx:a?r:-r,vy:0,size:12+Math.random()*6,opacity:1,time:0,legPhase:Math.random()*Math.PI*2,active:!0,static:!1}}if(l<s.leprechaunChance+3e-4){if(e.some(r=>r.type==="banshee"))return null;const a=Math.random()<.5;return{type:"banshee",x:a?-100:o+100,y:Math.random()*(n*.4)+50,baseY:Math.random()*(n*.4)+50,vx:a?2+Math.random()*1:-(2+Math.random()*1),size:18+Math.random()*10,opacity:.6+Math.random()*.2,waveAmplitude:25+Math.random()*20,waveFrequency:.0015+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,wailPhase:Math.random()*Math.PI*2,active:!0,static:!1}}if(l<.001&&e.filter(r=>r.type==="pot-of-gold").length<s.pots){const r=this.createPotOfGold(o,n),h=this.createRainbow(o,n,r.x,r.y);return e.push(h),r}return null},updateSpecialParticles(e,o,n,s){return e.forEach(l=>{l.active&&((l.type==="leprechaun"||l.type==="banshee")&&(l.time+=o,l.type==="leprechaun"?(l.vx>0&&l.x>n+l.size*2||l.vx<0&&l.x<-l.size*2)&&(l.active=!1):l.type==="banshee"&&(l.vx>0&&l.x>n+l.size*4||l.vx<0&&l.x<-l.size*4)&&(l.active=!1)),l.type==="static-leprechaun"&&(l.time+=o))}),e.filter(l=>l.active)},drawCloverPetal(e,o,n){const s=o.x,l=o.y,a=o.size,r=Math.sin(n*o.flutterSpeed+o.flutter)*.3;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation+r),e.fillStyle=o.color,e.strokeStyle="#006400",e.lineWidth=a*.08,e.beginPath(),e.moveTo(0,-a*.3),e.bezierCurveTo(-a*.6,-a*.7,-a*.8,-a*.3,-a*.5,a*.2),e.lineTo(0,a*.8),e.lineTo(a*.5,a*.2),e.bezierCurveTo(a*.8,-a*.3,a*.6,-a*.7,0,-a*.3),e.closePath(),e.fill(),e.stroke(),e.strokeStyle="rgba(0, 100, 0, 0.4)",e.lineWidth=a*.06,e.beginPath(),e.moveTo(0,-a*.2),e.lineTo(0,a*.7),e.stroke(),e.restore()},drawSparkle(e,o,n){const s=o.x,l=o.y,a=o.size,r=.6+Math.sin(n*.004+o.twinklePhase)*.4;e.save(),e.globalAlpha=o.opacity*r,e.translate(s,l),e.rotate(o.rotation),e.fillStyle=o.color,e.shadowColor=o.color,e.shadowBlur=a*2,e.beginPath(),e.moveTo(0,-a),e.lineTo(a*.3,-a*.3),e.lineTo(a,0),e.lineTo(a*.3,a*.3),e.lineTo(0,a),e.lineTo(-a*.3,a*.3),e.lineTo(-a,0),e.lineTo(-a*.3,-a*.3),e.closePath(),e.fill(),e.restore()},drawShamrock(e,o){const n=o.x,s=o.y,l=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(n,s),e.rotate(o.rotation),e.fillStyle=o.color||"#228B22",e.strokeStyle=o.strokeColor||"#006400",e.lineWidth=l*.1;for(let a=0;a<3;a++){const r=a/3*Math.PI*2-Math.PI/2;e.save(),e.rotate(r),e.translate(0,-l*.7),e.beginPath(),e.moveTo(0,l*.3),e.bezierCurveTo(-l*.5,-l*.2,-l*.3,-l*.6,0,-l*.35),e.bezierCurveTo(l*.3,-l*.6,l*.5,-l*.2,0,l*.3),e.closePath(),e.fill(),e.stroke(),e.restore()}e.strokeStyle="#8B4513",e.lineWidth=l*.15,e.beginPath(),e.moveTo(0,0),e.lineTo(0,l*1.2),e.stroke(),e.restore()},drawGoldCoin(e,o,n){const s=o.x,l=o.y,a=o.size,r=Math.abs(Math.cos(o.rotation))*a;if(e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.fillStyle="#FFD700",e.strokeStyle="#DAA520",e.lineWidth=a*.15,e.beginPath(),e.ellipse(0,0,r,a,0,0,Math.PI*2),e.fill(),e.stroke(),r>a*.3){const h=(Math.sin(n*.005+o.glintPhase)+1)*.5;e.fillStyle=`rgba(255, 255, 255, ${h*.6})`,e.beginPath(),e.ellipse(-r*.3,-a*.3,r*.3,a*.3,0,0,Math.PI*2),e.fill()}e.restore()},drawPotOfGold(e,o,n){const s=o.x,l=o.y,a=o.size;e.save(),e.translate(s,l),e.fillStyle="#1a1a1a",e.strokeStyle="#0a0a0a",e.lineWidth=2,e.beginPath(),e.arc(0,0,a*.7,0,Math.PI),e.lineTo(-a*.7,a*.4),e.quadraticCurveTo(0,a*.6,a*.7,a*.4),e.closePath(),e.fill(),e.stroke(),e.strokeStyle="#2a2a2a",e.lineWidth=3,e.beginPath(),e.arc(0,-a*.2,a*.5,Math.PI,0),e.stroke();const r=[{x:-a*.4,y:-a*.2,s:.8},{x:a*.3,y:-a*.3,s:.9},{x:-a*.1,y:-a*.5,s:1},{x:a*.5,y:-a*.1,s:.7},{x:-a*.5,y:0,s:.75},{x:a*.1,y:-a*.6,s:.85},{x:0,y:-a*.3,s:.95}];e.fillStyle="#FFD700",e.strokeStyle="#DAA520",e.lineWidth=1,r.forEach((t,f)=>{const P=n*.002+f*.5,y=(Math.sin(P)+1)*.5;e.save(),e.translate(t.x,t.y),e.beginPath(),e.arc(0,0,a*.2*t.s,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle=`rgba(255, 255, 255, ${y*.6})`,e.beginPath(),e.arc(-a*.08*t.s,-a*.08*t.s,a*.08*t.s,0,Math.PI*2),e.fill(),e.restore()}),e.globalAlpha=.3;const h=e.createRadialGradient(0,-a*.2,0,0,-a*.2,a*1.5);h.addColorStop(0,"#FFD700"),h.addColorStop(1,"rgba(255, 215, 0, 0)"),e.fillStyle=h,e.fillRect(-a*1.5,-a*1.5,a*3,a*1.5),e.restore()},drawSmallGoldPot(e,o,n,s){e.save(),e.translate(o,n),e.fillStyle="#1a1a1a",e.strokeStyle="#0a0a0a",e.lineWidth=1,e.beginPath(),e.arc(0,-s*.4,s*.7,0,Math.PI,!0),e.lineTo(s*.7,s*.4),e.quadraticCurveTo(0,s*.6,-s*.7,s*.4),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#FFD700",e.beginPath(),e.arc(0,-s*.7,s*.5,0,Math.PI*2),e.fill(),e.restore()},drawRainbow(e,o){const n=o.startX,s=o.endX,l=o.endY,a=o.height;e.save(),e.globalAlpha=.5;const r=Math.abs(s-n),h=(n+s)/2,t=a*.2,f=(r*r+4*t*t)/(8*t),P=l+t-f,y=this.colors.rainbow,g=5;for(let d=y.length-1;d>=0;d--)e.strokeStyle=y[d],e.lineWidth=g,e.beginPath(),e.arc(h,P,f+(y.length-1-d)*g,Math.PI,0),e.stroke();e.restore(),this.drawSmallGoldPot(e,s,l,20)},drawLeprechaun(e,o,n){const s=o.x,l=o.y,a=o.size,r=o.vx>0?1:-1;e.save(),e.translate(s,l),r===-1&&e.scale(-1,1);const h=o.time*.05%(Math.PI*2),t=Math.sin(h+o.legPhase)*(Math.PI/6),f=Math.sin(h+Math.PI+o.legPhase)*(Math.PI/8);e.strokeStyle="#228B22",e.lineWidth=a*.15,e.beginPath(),e.moveTo(a*.2,a*.5),e.lineTo(a*.2+Math.sin(t)*a*.3,a*1.2+Math.cos(t)*a*.1),e.stroke(),e.beginPath(),e.moveTo(-a*.2,a*.5),e.lineTo(-a*.2+Math.sin(t+Math.PI)*a*.3,a*1.2+Math.cos(t+Math.PI)*a*.1),e.stroke(),e.fillStyle="#000000",e.fillRect(a*.2+Math.sin(t)*a*.3-a*.15,a*1.15+Math.cos(t)*a*.1,a*.3,a*.2),e.fillRect(-a*.2+Math.sin(t+Math.PI)*a*.3-a*.15,a*1.15+Math.cos(t+Math.PI)*a*.1,a*.3,a*.2),e.fillStyle="#FFD700",e.fillRect(a*.2+Math.sin(t)*a*.3-a*.1,a*1.2+Math.cos(t)*a*.1,a*.2,a*.1),e.fillRect(-a*.2+Math.sin(t+Math.PI)*a*.3-a*.1,a*1.2+Math.cos(t+Math.PI)*a*.1,a*.2,a*.1),e.fillStyle="#228B22",e.fillRect(-a*.5,0,a,a*.6),e.fillStyle="#000000",e.fillRect(-a*.5,a*.35,a,a*.1),e.fillStyle="#FFD700",e.fillRect(-a*.15,a*.35,a*.3,a*.1),e.fillStyle="#228B22",e.save(),e.translate(a*.4,a*.2),e.rotate(f),e.fillRect(0,0,a*.15,a*.4),e.fillStyle="#FFD7BA",e.beginPath(),e.arc(a*.075,a*.4,a*.12,0,Math.PI*2),e.fill(),e.restore(),e.save(),e.translate(-a*.4-a*.15,a*.2),e.rotate(f+Math.PI),e.fillRect(0,0,a*.15,a*.4),e.fillStyle="#FFD7BA",e.beginPath(),e.arc(a*.075,a*.4,a*.12,0,Math.PI*2),e.fill(),e.restore(),e.fillStyle="#FFD7BA",e.beginPath(),e.arc(0,-a*.3,a*.4,0,Math.PI*2),e.fill(),e.fillStyle="#ff6600",e.beginPath(),e.moveTo(-a*.3,-a*.1),e.quadraticCurveTo(-a*.4,a*.2,0,a*.3),e.quadraticCurveTo(a*.4,a*.2,a*.3,-a*.1),e.closePath(),e.fill(),e.fillStyle="#228B22",e.fillRect(-a*.5,-a*.8,a,a*.1),e.fillRect(-a*.35,-a*1.4,a*.7,a*.6),e.fillStyle="#FFD700",e.fillRect(-a*.15,-a*.85,a*.3,a*.15),e.fillStyle="#000000",e.beginPath(),e.arc(-a*.12,-a*.35,a*.06,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.12,-a*.35,a*.06,0,Math.PI*2),e.fill(),e.restore()},drawBanshee(e,o,n){const s=o.x,l=o.y+Math.sin(n*o.waveFrequency+o.waveOffset)*o.waveAmplitude,a=o.size,r=o.vx>0?1:-1;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),r===-1&&e.scale(-1,1);const h=e.createRadialGradient(0,-a*.5,0,0,-a*.5,a*3);h.addColorStop(0,"rgba(180, 255, 180, 0.4)"),h.addColorStop(.3,"rgba(150, 255, 150, 0.2)"),h.addColorStop(.6,"rgba(120, 255, 120, 0.1)"),h.addColorStop(1,"rgba(100, 255, 100, 0)"),e.fillStyle=h,e.fillRect(-a*3,-a*3,a*6,a*6);const t=Math.sin(n*.008+o.wailPhase)*a*.25;e.fillStyle="rgba(200, 240, 200, 0.3)",e.beginPath(),e.moveTo(-a*.8,-a*1.2),e.bezierCurveTo(-a*1.2+t*2,-a*.4,-a*1+t*1.5,a*.6,-a*.6+t,a*1.8),e.bezierCurveTo(-a*.3,a*1.5,a*.3,a*1.5,a*.6-t,a*1.8),e.bezierCurveTo(a*1-t*1.5,a*.6,a*1.2-t*2,-a*.4,a*.8,-a*1.2),e.closePath(),e.fill(),e.fillStyle="rgba(230, 255, 230, 0.85)",e.strokeStyle="rgba(180, 255, 180, 0.6)",e.lineWidth=a*.04,e.beginPath(),e.moveTo(-a*.5,-a*1);for(let i=0;i<=12;i++){const d=i/12,M=Math.sin(n*.012+d*Math.PI*3+o.wailPhase)*a*.3,b=(d-.5)*a*1.8+M,S=-a*1+d*a*2.2;i===0?e.moveTo(b,S):e.lineTo(b,S)}e.lineTo(-a*.5,-a*1),e.closePath(),e.fill(),e.stroke(),e.strokeStyle="rgba(180, 255, 180, 0.4)",e.lineWidth=a*.02;for(let i=0;i<8;i++){const d=-a*.7+i*(a*1.4/7),M=Math.sin(n*.015+i*.8)*a*.2;e.beginPath(),e.moveTo(d,a*.4),e.lineTo(d+M,a*1.2+i%2*a*.4),e.stroke()}const f=Math.sin(n*.006)*.15;e.save(),e.rotate(f),e.fillStyle="rgba(240, 255, 240, 0.9)",e.strokeStyle="rgba(180, 230, 180, 0.5)",e.lineWidth=a*.03,e.beginPath(),e.ellipse(0,-a*.8,a*.35,a*.55,0,0,Math.PI*2),e.fill(),e.stroke(),e.fillStyle="rgba(50, 100, 50, 0.6)",e.beginPath(),e.ellipse(-a*.15,-a*.85,a*.12,a*.08,0,0,Math.PI*2),e.ellipse(a*.15,-a*.85,a*.12,a*.08,0,0,Math.PI*2),e.fill();const P=.6+Math.sin(n*.008+o.wailPhase)*.4;e.fillStyle=`rgba(100, 255, 150, ${P})`,e.shadowColor="#64ff96",e.shadowBlur=a*.6,e.beginPath(),e.arc(-a*.15,-a*.85,a*.08,0,Math.PI*2),e.arc(a*.15,-a*.85,a*.08,0,Math.PI*2),e.fill(),e.shadowBlur=0;const y=.2+Math.sin(n*.01+o.wailPhase)*.1;e.fillStyle="rgba(80, 150, 80, 0.7)",e.strokeStyle="rgba(50, 100, 50, 0.8)",e.lineWidth=a*.02,e.beginPath(),e.ellipse(0,-a*.6,a*.15,a*(.2+y),0,0,Math.PI*2),e.fill(),e.stroke(),e.restore(),e.strokeStyle="rgba(220, 255, 220, 0.7)";for(let i=0;i<12;i++){const d=-a*.35+i/11*a*.7,M=Math.sin(n*.01+i*.6+o.wailPhase)*a*.5,b=Math.cos(n*.008+i*.4)*a*.3;e.lineWidth=a*(.04+i%3*.02),e.beginPath(),e.moveTo(d,-a*1.1),e.bezierCurveTo(d+M*.3,-a*.5,d+M*.6+b*.5,a*.2,d+M+b,a*1+i%2*a*.5),e.stroke()}const g=Math.sin(n*.01+o.wailPhase)*a*.3;e.fillStyle="rgba(230, 255, 230, 0.8)",e.strokeStyle="rgba(180, 230, 180, 0.6)",e.lineWidth=a*.02,e.save(),e.translate(-a*.6,-a*.2),e.rotate(-.3+g*.002),e.beginPath(),e.ellipse(0,0,a*.15,a*.25,0,0,Math.PI*2),e.fill(),e.stroke();for(let i=0;i<4;i++){const d=-a*.08+i*a*.055;e.beginPath(),e.moveTo(d,a*.15),e.lineTo(d,a*.35),e.stroke()}e.restore(),e.save(),e.translate(a*.6,-a*.2),e.rotate(.3-g*.002),e.beginPath(),e.ellipse(0,0,a*.15,a*.25,0,0,Math.PI*2),e.fill(),e.stroke();for(let i=0;i<4;i++){const d=-a*.08+i*a*.055;e.beginPath(),e.moveTo(d,a*.15),e.lineTo(d,a*.35),e.stroke()}e.restore(),e.strokeStyle="rgba(180, 255, 180, 0.3)",e.lineWidth=a*.02;for(let i=0;i<6;i++){const d=i/6*Math.PI*2,M=Math.sin(n*.01+i*.8)*a*.6,b=a*(1.5+i%2*.5);e.beginPath(),e.moveTo(0,a*.4),e.bezierCurveTo(Math.cos(d)*a*.8+M*.5,a*1,Math.cos(d)*a*1.5+M,a*1.8,Math.cos(d)*b+M*1.5,a*2.5),e.stroke()}e.restore()},drawMoon(e,o,n){const s=o.x,l=o.y,a=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(s,l);const r=e.createRadialGradient(-a*.2,-a*.2,0,0,0,a);r.addColorStop(0,"#90EE90"),r.addColorStop(.5,"#7FD87F"),r.addColorStop(1,"#5CB85C"),e.fillStyle=r,e.beginPath(),e.arc(0,0,a,0,Math.PI*2),e.fill(),e.fillStyle="rgba(60, 130, 60, 0.3)",e.beginPath(),e.arc(-a*.3,-a*.2,a*.15,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.25,a*.1,a*.2,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.1,-a*.4,a*.12,0,Math.PI*2),e.fill(),e.save(),e.translate(a*.2,a*.35),e.scale(.15,.15),e.fillStyle="rgba(34, 139, 34, 0.4)";for(let f=0;f<3;f++){const P=f/3*Math.PI*2-Math.PI/2;e.save(),e.rotate(P),e.translate(0,-a*.7),e.beginPath(),e.moveTo(0,a*.3),e.bezierCurveTo(-a*.5,-a*.2,-a*.3,-a*.6,0,-a*.35),e.bezierCurveTo(a*.3,-a*.6,a*.5,-a*.2,0,a*.3),e.closePath(),e.fill(),e.restore()}e.restore();const h=.3+Math.sin(n*.002+o.glowPhase)*.15;e.globalAlpha=h;const t=e.createRadialGradient(0,0,a*.8,0,0,a*1.8);t.addColorStop(0,"rgba(144, 238, 144, 0.8)"),t.addColorStop(.5,"rgba(144, 238, 144, 0.4)"),t.addColorStop(1,"rgba(144, 238, 144, 0)"),e.fillStyle=t,e.beginPath(),e.arc(0,0,a*1.8,0,Math.PI*2),e.fill(),e.restore()},drawStaticLeprechaun(e,o,n){const s=o.x,l=o.y,a=o.size;e.save(),e.translate(s,l);const r=Math.sin(o.time*.005+o.wavePhase)*a*.05,h=Math.sin(o.time*.003+o.wavePhase*.5)*.05;e.translate(0,r),e.rotate(h),e.strokeStyle="#228B22",e.lineWidth=a*.15,e.beginPath(),e.moveTo(-a*.2,a*.5),e.lineTo(-a*.25,a*1.2),e.stroke(),e.beginPath(),e.moveTo(a*.2,a*.5),e.lineTo(a*.25,a*1.2),e.stroke(),e.fillStyle="#000000",e.fillRect(-a*.35,a*1.15,a*.25,a*.15),e.fillRect(a*.1,a*1.15,a*.25,a*.15),e.fillStyle="#FFD700",e.fillRect(-a*.32,a*1.18,a*.15,a*.08),e.fillRect(a*.13,a*1.18,a*.15,a*.08),e.fillStyle="#228B22",e.fillRect(-a*.5,0,a,a*.6),e.fillStyle="#000000",e.fillRect(-a*.5,a*.35,a,a*.1),e.fillStyle="#FFD700",e.fillRect(-a*.15,a*.35,a*.3,a*.1),e.save(),e.translate(-a*.5,a*.2),e.rotate(-.3+Math.sin(o.time*.007+o.wavePhase*1.2)*.1),e.fillStyle="#228B22",e.fillRect(0,0,a*.15,a*.4),e.fillStyle="#FFD7BA",e.beginPath(),e.arc(a*.075,a*.4,a*.12,0,Math.PI*2),e.fill(),e.restore(),e.save(),e.translate(a*.5,a*.2),e.rotate(.3-Math.sin(o.time*.006+o.wavePhase*.8)*.05),e.fillStyle="#228B22",e.fillRect(-a*.15,0,a*.15,a*.4),e.fillStyle="#FFD7BA",e.beginPath(),e.arc(-a*.075,a*.4,a*.12,0,Math.PI*2),e.fill(),e.restore(),e.save(),e.translate(0,-a*.3),e.rotate(Math.sin(o.time*.004+o.wavePhase*1.5)*.02),e.beginPath(),e.arc(0,0,a*.4,0,Math.PI*2),e.fill(),e.restore(),e.fillStyle="#ff6600",e.beginPath(),e.moveTo(-a*.3,-a*.1),e.quadraticCurveTo(-a*.4,a*.2,0,a*.3),e.quadraticCurveTo(a*.4,a*.2,a*.3,-a*.1),e.closePath(),e.fill(),e.fillStyle="#228B22",e.fillRect(-a*.5,-a*.8,a,a*.1),e.fillRect(-a*.35,-a*1.4,a*.7,a*.6),e.fillStyle="#FFD700",e.fillRect(-a*.15,-a*.85,a*.3,a*.15),e.fillStyle="#000000",e.beginPath(),e.arc(-a*.12,-a*.35,a*.06,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.12,-a*.35,a*.06,0,Math.PI*2),e.fill(),e.strokeStyle="#000000",e.lineWidth=a*.04,e.beginPath(),e.arc(0,-a*.2,a*.15,0,Math.PI),e.stroke(),e.restore()},drawTwinklingStar(e,o,n){const s=o.x,l=o.y,a=o.size,r=.5+Math.sin(n*o.twinkleSpeed+o.twinklePhase)*.5,h=`rgba(220, 255, 220, ${r})`,t=`rgba(180, 255, 180, ${r*.4})`;e.save(),e.translate(s,l),e.shadowColor=t,e.shadowBlur=a*3*r,e.fillStyle=h,e.beginPath();for(let f=0;f<4;f++){const P=f*Math.PI/2,y=Math.cos(P)*a,g=Math.sin(P)*a,i=P+Math.PI/4,d=Math.cos(i)*(a*.3),M=Math.sin(i)*(a*.3);f===0?e.moveTo(y,g):e.lineTo(y,M),e.lineTo(d,M)}e.closePath(),e.fill(),e.shadowBlur=a*2*r,e.beginPath(),e.arc(0,0,a*.25,0,Math.PI*2),e.fill(),e.restore()}};
@@ -0,0 +1 @@
1
+ export default{name:"valentines",displayName:"Valentine's Day",emoji:"\u{1F495}",intensityConfig:{light:{count:40,speedRange:[.3,.8],sizeRange:[2,4],garlands:1,cupidChance:3e-4},medium:{count:80,speedRange:[.4,1.2],sizeRange:[2,5],garlands:2,cupidChance:5e-4},heavy:{count:150,speedRange:[.5,1.5],sizeRange:[3,6],garlands:3,cupidChance:8e-4}},particles:["heart","rose-petal","sparkle","lips"],decorations:["cupid","love-letter","heart-garland","envelope","butterfly","heart-moon","neon-sign"],colors:{primary:"#ff1493",secondary:"#ff69b4",accent:"#dc143c",light:"#ffb6c1",gold:"#ffd700"},createHeart(e,o,n){const s=Math.random();let l,a,h;const i=["#ff1493","#ff69b4","#dc143c","#ffb6c1","#ff0080"];return s<.33?(l=n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0])*1.5,a=n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]),h=.8+Math.random()*.2):s<.66?(l=n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0]),a=n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]),h=.6+Math.random()*.2):(l=n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0])*.8,a=n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0])*.8,h=.4+Math.random()*.2),{type:"heart",x:Math.random()*e,y:Math.random()*o,size:l,vx:(Math.random()-.5)*.3,vy:(Math.random()-.7)*.2,speed:a*.3,opacity:h,color:i[Math.floor(Math.random()*i.length)],windOffset:Math.random()*Math.PI*2,windSpeed:.005+Math.random()*.01,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,pulsePhase:Math.random()*Math.PI*2,pulseSpeed:.002+Math.random()*.003,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,active:!0,depth:s}},createRosePetal(e,o,n){const s=["#ff1493","#ff69b4","#dc143c","#ff758f","#ffb6c1"];return{type:"rose-petal",x:Math.random()*e,y:-20,size:1.5+Math.random()*2,speed:n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0])*.6,opacity:.7+Math.random()*.3,color:s[Math.floor(Math.random()*s.length)],windOffset:Math.random()*Math.PI*2,windSpeed:.02+Math.random()*.03,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.05,flutterPhase:Math.random()*Math.PI*2,active:!0}},createSparkle(e,o,n){return{type:"sparkle",x:Math.random()*e,y:Math.random()*o,size:.5+Math.random()*1.5,opacity:1,life:20+Math.random()*30,maxLife:20+Math.random()*30,color:Math.random()<.5?"#ffd700":"#ffffff",twinklePhase:Math.random()*Math.PI*2,active:!0,static:!0}},createLips(e,o,n){const s=["#dc143c","#ff1493","#c71585","#ff0066"];return{type:"lips",x:Math.random()*e,y:Math.random()*o,size:3+Math.random()*3,vx:(Math.random()-.5)*.4,vy:(Math.random()-.7)*.2,speed:.3+Math.random()*.2,opacity:.8+Math.random()*.2,color:s[Math.floor(Math.random()*s.length)],rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,pulsePhase:Math.random()*Math.PI*2,pulseSpeed:.003+Math.random()*.002,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,kissPhase:Math.random()*Math.PI*2,active:!0}},createHeartGarland(e,o,n={}){const s=8+Math.floor(Math.random()*5),l=[];for(let a=0;a<s;a++)l.push({offset:a/s,size:8+Math.random()*4,color:Math.random()<.5?"#ff1493":"#ff69b4",pulsePhase:Math.random()*Math.PI*2});return{type:"heart-garland",x:0,y:n.y!==void 0?n.y:50+Math.random()*100,width:e,hearts:l,sag:40+Math.random()*30,swayPhase:Math.random()*Math.PI*2,swayAmplitude:10+Math.random()*10,opacity:.8+Math.random()*.2,active:!0,static:!0}},createLoveLetter(e,o,n={}){const s=Math.random()<.5;return{type:"love-letter",x:s?-50:e+50,y:Math.random()*o*.6,baseY:Math.random()*o*.6,vx:s?1+Math.random()*1:-(1+Math.random()*1),size:12+Math.random()*6,opacity:.9+Math.random()*.1,rotation:(Math.random()-.5)*.3,rotationSpeed:(Math.random()-.5)*.005,waveAmplitude:15+Math.random()*15,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,active:!0,static:!1}},createFallingParticle(e,o,n){const s=Math.random();return s<.6?this.createHeart(e,o,n):s<.85?this.createRosePetal(e,o,n):this.createLips(e,o,n)},createInitialDecorations(e,o,n){const s=[],l=n.garlands||2;for(let i=0;i<l;i++)s.push(this.createHeartGarland(e,o,{y:50+i*(o/(l+1))}));const a=4;for(let i=0;i<a;i++)s.push({type:"envelope",x:e/(a+1)*(i+1)+(Math.random()-.5)*30,y:o-25-Math.random()*10,size:15+Math.random()*5,opacity:.85,rotation:(Math.random()-.5)*.3,active:!0,static:!0});const h=n.butterflies||3;for(let i=0;i<h;i++)s.push({type:"butterfly",x:Math.random()*e,y:Math.random()*o*.6+50,baseY:Math.random()*o*.6+50,size:12+Math.random()*6,vx:(Math.random()-.5)*2,vy:0,waveOffset:Math.random()*Math.PI*2,waveFrequency:.003+Math.random()*.002,waveAmplitude:20+Math.random()*15,wingPhase:Math.random()*Math.PI*2,opacity:.9,color:Math.random()<.5?"pink":"purple",active:!0,static:!1});return s.push({type:"heart-moon",x:e*.85,y:o*.15,size:60+Math.random()*20,opacity:.9,glowPhase:Math.random()*Math.PI*2,active:!0,static:!0}),s},spawnSpecialParticle(e,o,n,s){const l=Math.random();if(l<s.cupidChance){if(e.some(h=>h.type==="cupid"))return null;const a=Math.random()<.5;return{type:"cupid",x:a?-100:o+100,y:Math.random()*n*.4+50,baseY:Math.random()*n*.4+50,vx:a?2+Math.random()*1.5:-(2+Math.random()*1.5),size:15+Math.random()*8,opacity:1,waveAmplitude:20+Math.random()*20,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,wingPhase:Math.random()*Math.PI*2,active:!0,static:!1}}return l<.001?this.createLoveLetter(o,n):l<.0015?e.some(a=>a.type==="neon-sign"&&a.active)?null:{type:"neon-sign",x:o/2,y:n*.3,size:40+Math.random()*20,opacity:0,targetOpacity:1,phase:"fade-in",phaseTime:0,fadeInDuration:1500,stableDuration:5e3,fadeOutDuration:1e3,crackleIntensity:1,flickerPhase:Math.random()*Math.PI*2,glowPhase:Math.random()*Math.PI*2,tubeFlickers:[],active:!0,static:!1}:l<.02?this.createSparkle(o,n,s):null},drawHeart(e,o,n){const s=o.x,l=o.y,a=o.size,h=1+Math.sin(n*o.pulseSpeed+o.pulsePhase)*.15;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.scale(h,h),e.fillStyle=o.color,e.shadowColor=o.color,e.shadowBlur=a*.8,e.beginPath(),e.moveTo(0,a*.3),e.bezierCurveTo(-a,-a*.3,-a*.6,-a,0,-a*.4),e.bezierCurveTo(a*.6,-a,a,-a*.3,0,a*.3),e.closePath(),e.fill(),e.shadowBlur=0,e.fillStyle="rgba(255, 255, 255, 0.3)",e.beginPath(),e.arc(-a*.25,-a*.5,a*.15,0,Math.PI*2),e.fill(),e.restore()},drawRosePetal(e,o,n){const s=o.x,l=o.y,a=o.size,h=Math.sin(n*.01+o.flutterPhase)*.3;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation+h),e.fillStyle=o.color,e.strokeStyle="rgba(139, 0, 0, 0.3)",e.lineWidth=.5,e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(a*2,-a,a*2,a,0,0),e.closePath(),e.fill(),e.beginPath(),e.moveTo(0,0),e.quadraticCurveTo(a,0,a*1.5,0),e.stroke(),e.restore()},drawSparkle(e,o,n){const s=o.x,l=o.y,a=o.size,h=o.life/o.maxLife,i=(Math.sin(n*.01+o.twinklePhase)+1)*.5;e.save(),e.globalAlpha=o.opacity*h*i,e.translate(s,l),e.fillStyle=o.color,e.shadowColor=o.color,e.shadowBlur=a*2,e.beginPath();for(let r=0;r<4;r++){const d=r/4*Math.PI*2-Math.PI/2,f=Math.cos(d)*a*3,t=Math.sin(d)*a*3,M=d+Math.PI/4,u=Math.cos(M)*a*.5,P=Math.sin(M)*a*.5;r===0?e.moveTo(f,t):(e.lineTo(u,P),e.lineTo(f,t))}e.closePath(),e.fill(),e.restore()},drawLips(e,o,n){const s=o.x,l=o.y,a=o.size,h=1+Math.sin(n*o.pulseSpeed+o.pulsePhase)*.1,i=Math.sin(n*.008+o.kissPhase);e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.scale(h,h);const r=e.createRadialGradient(0,0,0,0,0,a*2);r.addColorStop(0,o.color),r.addColorStop(.6,o.color),r.addColorStop(1,"#8b0000"),e.fillStyle=r,e.strokeStyle="#8b0000",e.lineWidth=a*.1,e.beginPath(),e.moveTo(-a,0),e.bezierCurveTo(-a*.8,-a*.8,-a*.3,-a*1,0,-a*.6),e.bezierCurveTo(a*.3,-a*1,a*.8,-a*.8,a,0),e.fill(),e.stroke();const d=i*a*.15;if(e.beginPath(),e.moveTo(-a,0),e.bezierCurveTo(-a*.7,a*(.7+d),-a*.3,a*(1+d),0,a*(.8+d)),e.bezierCurveTo(a*.3,a*(1+d),a*.7,a*(.7+d),a,0),e.fill(),e.stroke(),e.globalAlpha=o.opacity*.5,e.fillStyle="rgba(255, 255, 255, 0.6)",e.beginPath(),e.ellipse(-a*.4,-a*.4,a*.2,a*.15,-.3,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(a*.4,-a*.4,a*.2,a*.15,.3,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(0,a*.5,a*.3,a*.2,0,0,Math.PI*2),e.fill(),i>.8){const f=a*1.5,t=Math.sin(o.kissPhase)*f,M=-Math.abs(Math.cos(o.kissPhase))*f;e.globalAlpha=o.opacity*(i-.8)*5,e.fillStyle="#ffd700",e.shadowColor="#ffd700",e.shadowBlur=a*.5,e.beginPath(),e.arc(t,M,a*.15,0,Math.PI*2),e.fill();for(let u=0;u<4;u++){const P=u/4*Math.PI*2+n*.01,m=t+Math.cos(P)*a*.4,y=M+Math.sin(P)*a*.4;e.beginPath(),e.moveTo(t,M),e.lineTo(m,y),e.lineWidth=a*.05,e.strokeStyle="#ffffff",e.stroke()}}e.restore()},drawCupid(e,o,n){const s=o.x,l=o.y,a=o.size,h=o.vx>0?1:-1;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),h===-1&&e.scale(-1,1);const i=Math.sin(n*.015+o.wingPhase)*(Math.PI/6);e.fillStyle="#ffffff",e.strokeStyle="#ffb6c1",e.lineWidth=1.5,e.save(),e.translate(-a*.2,-a*.3),e.rotate(-i),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*1.2,-a*.8,-a*1.5,-a*.3,-a*1.2,a*.3),e.bezierCurveTo(-a*.8,a*.2,-a*.3,0,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.fillStyle="#ffdbac",e.beginPath(),e.ellipse(0,0,a*.4,a*.5,0,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(a*.3,-a*.6,a*.35,a*.4,0,0,Math.PI*2),e.fill(),e.fillStyle="#ffd700",e.beginPath(),e.arc(a*.1,-a*.9,a*.22,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.4,-a*.95,a*.18,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.15,-a*.75,a*.15,0,Math.PI*2),e.fill(),e.fillStyle="#000000",e.beginPath(),e.arc(a*.42,-a*.62,a*.05,0,Math.PI*2),e.fill(),e.fillStyle="rgba(255, 105, 180, 0.3)",e.beginPath(),e.arc(a*.25,-a*.5,a*.12,0,Math.PI*2),e.fill(),e.strokeStyle="#ffdbac",e.lineWidth=a*.15,e.lineCap="round",e.beginPath(),e.moveTo(-a*.15,a*.4),e.lineTo(-a*.3,a*.8),e.stroke(),e.beginPath(),e.moveTo(-a*.05,a*.4),e.lineTo(-a*.15,a*.75),e.stroke(),e.strokeStyle="#ffdbac",e.lineWidth=a*.12,e.beginPath(),e.moveTo(a*.1,-a*.2),e.lineTo(a*.6,-a*.3),e.stroke(),e.beginPath(),e.moveTo(a*.05,0),e.lineTo(a*.9,-a*.35),e.stroke(),e.strokeStyle="#8B4513",e.lineWidth=2.5,e.beginPath(),e.moveTo(a*.9,-a*.6),e.quadraticCurveTo(a*1.1,-a*.35,a*.9,-a*.1),e.stroke(),e.strokeStyle="#daa520",e.lineWidth=1.5,e.beginPath(),e.moveTo(a*.9,-a*.6),e.lineTo(a*.6,-a*.35),e.lineTo(a*.9,-a*.1),e.stroke(),e.strokeStyle="#8B4513",e.lineWidth=2,e.beginPath(),e.moveTo(a*.6,-a*.35),e.lineTo(a*1.8,-a*.35),e.stroke(),e.fillStyle="#dc143c",e.beginPath(),e.moveTo(a*1.8,-a*.35),e.lineTo(a*2,-a*.35),e.lineTo(a*1.9,-a*.25),e.closePath(),e.fill(),e.beginPath(),e.moveTo(a*1.8,-a*.35),e.lineTo(a*2,-a*.35),e.lineTo(a*1.9,-a*.45),e.closePath(),e.fill(),e.fillStyle="#ff69b4",e.beginPath(),e.moveTo(a*.6,-a*.35),e.lineTo(a*.5,-a*.25),e.lineTo(a*.5,-a*.45),e.closePath(),e.fill(),e.fillStyle="#ffffff",e.strokeStyle="#ffb6c1",e.lineWidth=1.5,e.save(),e.translate(a*.1,-a*.3),e.rotate(i),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*1,-a*.6,-a*1.3,-a*.2,-a*1,a*.2),e.bezierCurveTo(-a*.6,a*.15,-a*.2,0,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.restore()},drawLoveLetter(e,o,n){const s=o.x,l=o.y,a=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.fillStyle="#fff5e6",e.strokeStyle="#daa520",e.lineWidth=1,e.fillRect(-a,-a*.7,a*2,a*1.4),e.strokeRect(-a,-a*.7,a*2,a*1.4),e.fillStyle="#ffe4b3",e.beginPath(),e.moveTo(-a,-a*.7),e.lineTo(0,0),e.lineTo(a,-a*.7),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#dc143c",e.shadowColor="#dc143c",e.shadowBlur=5,e.beginPath(),e.moveTo(0,a*.1),e.bezierCurveTo(-a*.3,-a*.1,-a*.2,-a*.3,0,-a*.15),e.bezierCurveTo(a*.2,-a*.3,a*.3,-a*.1,0,a*.1),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()},drawHeartGarland(e,o,n){const s=o.y,l=o.width,a=o.sag,h=o.hearts;e.save(),e.globalAlpha=o.opacity;const i=Math.sin(n*.001+o.swayPhase)*o.swayAmplitude;e.strokeStyle="#daa520",e.lineWidth=2,e.beginPath(),e.moveTo(0,s),e.quadraticCurveTo(l/2,s+a+i,l,s),e.stroke(),h.forEach(r=>{const d=r.offset*l,f=s+Math.sin(Math.PI*r.offset)*(a+i),t=1+Math.sin(n*.003+r.pulsePhase)*.1;e.save(),e.translate(d,f),e.scale(t,t),e.fillStyle=r.color,e.shadowColor=r.color,e.shadowBlur=r.size*.5,e.beginPath(),e.moveTo(0,r.size*.3),e.bezierCurveTo(-r.size,-r.size*.3,-r.size*.6,-r.size,0,-r.size*.4),e.bezierCurveTo(r.size*.6,-r.size,r.size,-r.size*.3,0,r.size*.3),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()}),e.restore()},drawEnvelope(e,o,n){const s=o.x,l=o.y,a=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.fillStyle="#fff5f5",e.strokeStyle="#ff69b4",e.lineWidth=a*.05,e.fillRect(-a*.7,-a*.5,a*1.4,a),e.strokeRect(-a*.7,-a*.5,a*1.4,a),e.fillStyle="#ffe4e1",e.beginPath(),e.moveTo(-a*.7,-a*.5),e.lineTo(0,a*.1),e.lineTo(a*.7,-a*.5),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#ff1493",e.shadowColor="#ff1493",e.shadowBlur=a*.3,e.beginPath(),e.moveTo(0,a*.25),e.bezierCurveTo(-a*.3,-a*.1,-a*.2,-a*.3,0,-a*.15),e.bezierCurveTo(a*.2,-a*.3,a*.3,-a*.1,0,a*.25),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()},drawButterfly(e,o,n){const s=o.x+Math.sin(n*o.waveFrequency+o.waveOffset)*o.waveAmplitude,l=o.baseY+Math.cos(n*o.waveFrequency*.7+o.waveOffset)*(o.waveAmplitude*.6),a=o.size,h=o.vx>0?1:-1;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),h===-1&&e.scale(-1,1);const i=Math.sin(n*.015+o.wingPhase)*(Math.PI/6),d={pink:{main:"#ffb3d9",accent:"#ff69b4",outline:"#ff1493"},purple:{main:"#dda0dd",accent:"#ba55d3",outline:"#9932cc"}}[o.color];e.save(),e.translate(-a*.15,0),e.rotate(-i),e.fillStyle=d.main,e.strokeStyle=d.outline,e.lineWidth=a*.03,e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*.8,-a*.6,-a*.5,-a*1.2,0,-a*.5),e.closePath(),e.fill(),e.stroke(),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*.7,a*.4,-a*.4,a*.9,0,a*.4),e.closePath(),e.fill(),e.stroke(),e.fillStyle=d.accent,e.beginPath(),e.arc(-a*.4,-a*.5,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.35,a*.35,a*.1,0,Math.PI*2),e.fill(),e.restore(),e.save(),e.translate(a*.15,0),e.rotate(i),e.fillStyle=d.main,e.strokeStyle=d.outline,e.lineWidth=a*.03,e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(a*.8,-a*.6,a*.5,-a*1.2,0,-a*.5),e.closePath(),e.fill(),e.stroke(),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(a*.7,a*.4,a*.4,a*.9,0,a*.4),e.closePath(),e.fill(),e.stroke(),e.fillStyle=d.accent,e.beginPath(),e.arc(a*.4,-a*.5,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.35,a*.35,a*.1,0,Math.PI*2),e.fill(),e.restore(),e.fillStyle="#4a4a4a",e.fillRect(-a*.05,-a*.5,a*.1,a*.9),e.fillStyle="#2a2a2a",e.beginPath(),e.arc(0,-a*.55,a*.12,0,Math.PI*2),e.fill(),e.strokeStyle="#2a2a2a",e.lineWidth=a*.02,e.beginPath(),e.moveTo(0,-a*.6),e.quadraticCurveTo(-a*.15,-a*.8,-a*.2,-a*.75),e.stroke(),e.beginPath(),e.moveTo(0,-a*.6),e.quadraticCurveTo(a*.15,-a*.8,a*.2,-a*.75),e.stroke(),e.fillStyle=d.outline,e.beginPath(),e.arc(-a*.2,-a*.75,a*.04,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.2,-a*.75,a*.04,0,Math.PI*2),e.fill(),e.restore()},drawHeartMoon(e,o,n){const s=o.x,l=o.y,a=o.size,h=.3+Math.sin(n*.002+o.glowPhase)*.2;e.save(),e.globalAlpha=o.opacity,e.translate(s,l);const i=e.createRadialGradient(0,0,a*.5,0,0,a*2.5);i.addColorStop(0,`rgba(255, 182, 193, ${h*.8})`),i.addColorStop(.4,`rgba(255, 105, 180, ${h*.5})`),i.addColorStop(.7,`rgba(255, 20, 147, ${h*.3})`),i.addColorStop(1,"rgba(255, 20, 147, 0)"),e.fillStyle=i,e.beginPath(),e.arc(0,0,a*2.5,0,Math.PI*2),e.fill();const r=e.createRadialGradient(-a*.3,-a*.3,0,0,0,a);r.addColorStop(0,"#ffcce0"),r.addColorStop(.5,"#ffb3d9"),r.addColorStop(1,"#ff69b4"),e.fillStyle=r,e.beginPath(),e.moveTo(0,a*.3),e.bezierCurveTo(-a*.5,a*.5,-a,a*.2,-a,-a*.2),e.bezierCurveTo(-a,-a*.6,-a*.5,-a*.7,0,-a*.3),e.bezierCurveTo(a*.5,-a*.7,a,-a*.6,a,-a*.2),e.bezierCurveTo(a,a*.2,a*.5,a*.5,0,a*.3),e.closePath(),e.fill(),e.strokeStyle="#ff1493",e.lineWidth=a*.02,e.stroke(),e.fillStyle="rgba(255, 240, 245, 0.4)",e.beginPath(),e.arc(-a*.4,-a*.1,a*.15,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.3,0,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.2,a*.15,a*.1,0,Math.PI*2),e.fill(),e.fillStyle=`rgba(255, 255, 255, ${h})`;const d=[{x:-a*.3,y:-a*.4},{x:a*.2,y:-a*.3},{x:0,y:0},{x:-a*.5,y:a*.1},{x:a*.4,y:a*.15}];for(const f of d){e.save(),e.translate(f.x,f.y),e.beginPath();const t=a*.08;e.moveTo(0,-t),e.lineTo(t*.2,-t*.2),e.lineTo(t,0),e.lineTo(t*.2,t*.2),e.lineTo(0,t),e.lineTo(-t*.2,t*.2),e.lineTo(-t,0),e.lineTo(-t*.2,-t*.2),e.closePath(),e.fill(),e.restore()}e.restore()},drawNeonSign(e,o,n){const s=o.x,l=o.y,a=o.size;if(o.phaseTime+=16,o.phase==="fade-in"&&o.phaseTime>=o.fadeInDuration)o.phase="stable",o.phaseTime=0,o.opacity=o.targetOpacity,o.crackleIntensity=0;else if(o.phase==="stable"&&o.phaseTime>=o.stableDuration)o.phase="fade-out",o.phaseTime=0;else if(o.phase==="fade-out"&&o.phaseTime>=o.fadeOutDuration){o.active=!1;return}o.phase==="fade-in"?(o.opacity=o.phaseTime/o.fadeInDuration*o.targetOpacity,o.crackleIntensity=1-o.phaseTime/o.fadeInDuration):o.phase==="fade-out"&&(o.opacity=o.targetOpacity*(1-o.phaseTime/o.fadeOutDuration)),e.save(),e.translate(s,l);const h="#ff1493",i="#ff0066",r="#ffffff",d="Happy Valentine's Day",f=a;e.font=`bold ${f}px 'Arial Black', sans-serif`,e.textAlign="center",e.textBaseline="middle";const t=e.measureText(d).width;let M=0;o.crackleIntensity>0&&(M=(Math.random()-.5)*o.crackleIntensity*5);const u=d.split("");let P=-t/2;for(let m=0;m<u.length;m++){const y=u[m],b=e.measureText(y).width,T=(o.crackleIntensity>0?Math.random()<o.crackleIntensity*.3:!1)?o.opacity*(.3+Math.random()*.7):o.opacity;e.save(),e.translate(P+b/2,M),e.globalAlpha=T;const v=.8+Math.sin(n*.002+o.glowPhase+m*.5)*.2;e.shadowColor=h,e.shadowBlur=a*.8*v,e.fillStyle=h,e.fillText(y,0,0),e.shadowBlur=a*.5*v,e.fillStyle=i,e.fillText(y,0,0),e.shadowBlur=a*.3*v,e.fillStyle=r,e.fillText(y,0,0),e.shadowBlur=0,e.fillStyle=r,e.globalAlpha=T*.9,e.fillText(y,0,0),e.restore(),P+=b}if(o.crackleIntensity>.3){for(let m=0;m<3;m++)if(Math.random()<o.crackleIntensity*.5){const y=(Math.random()-.5)*t,b=(Math.random()-.5)*a*.3,g=2+Math.random()*3;e.save(),e.globalAlpha=o.opacity*Math.random(),e.fillStyle=Math.random()<.5?h:r,e.shadowColor=h,e.shadowBlur=g*4,e.beginPath(),e.arc(y,b,g,0,Math.PI*2),e.fill(),e.restore()}}(o.phase==="stable"||o.phase==="fade-out")&&(e.globalAlpha=o.opacity*.1,e.fillStyle="#ff1493",e.fillRect(-t*.55,-a*.6,t*1.1,a*1.2)),e.restore()}};
@@ -0,0 +1 @@
1
+ (function(){"use strict";fetch("/api/effects/settings").then(function(d){return d.json()}).then(function(d){var w=d.respectMotion!==!1,E=d.defaultDuration||600,q=d.defaultAnimation||"fade",S=d.defaultThreshold!=null?d.defaultThreshold:.1,m=w&&window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,u=document.querySelector(".page-body");if(!u)return;function r(t,e){return t.getAttribute("data-fx-"+e)}function i(t,e,a){var n=r(t,e);return n!==null?parseFloat(n):a}function y(t,e,a){var n=r(t,e);return n===null?a:n==="true"||n==="1"}function g(t,e,a){var n=new IntersectionObserver(function(o){o[0].isIntersecting&&(n.unobserve(t),a())},{threshold:e});n.observe(t)}u.querySelectorAll(".dm-fx-reveal").forEach(function(t){var e=r(t,"animation")||q,a=i(t,"duration",E),n=i(t,"delay",0),o=i(t,"threshold",S),f=y(t,"once",!0);m||(t.style.opacity="0",t.style.transition="none",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"&&(t.style.transform="rotateY(90deg)"),requestAnimationFrame(function(){requestAnimationFrame(function(){t.style.transition="opacity "+a+"ms ease "+n+"ms, transform "+a+"ms ease "+n+"ms";var s=new IntersectionObserver(function(c){c.forEach(function(x){x.isIntersecting?(t.style.opacity="1",t.style.transform="",f&&s.unobserve(t)):f||(t.style.opacity="0",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"?t.style.transform="rotateY(90deg)":t.style.transform="")})},{threshold:o});s.observe(t)})}))}),u.querySelectorAll(".dm-fx-counter").forEach(function(t){var e=i(t,"to",0),a=i(t,"from",0),n=i(t,"duration",2e3),o=r(t,"prefix")||"",f=r(t,"suffix")||"",s=i(t,"decimals",0),c=r(t,"separator")||"";function x(v){var h=v.toFixed(s);if(c){var p=h.split(".");p[0]=p[0].replace(/\B(?=(\d{3})+(?!\d))/g,c),h=p.join(".")}return o+h+f}if(m){t.textContent=x(e);return}g(t,.5,function(){var v=null;requestAnimationFrame(function h(p){v||(v=p);var A=Math.min((p-v)/n,1),z=1-Math.pow(1-A,3);t.textContent=x(a+(e-a)*z),A<1&&requestAnimationFrame(h)})})}),m||u.querySelectorAll(".dm-fx-scramble").forEach(function(t){var e=i(t,"speed",50),a=t.textContent,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&",o=0;g(t,.3,function(){var f=setInterval(function(){for(var s="",c=0;c<a.length;c++)c<o||a[c]===" "?s+=a[c]:s+=n[Math.floor(Math.random()*n.length)];t.textContent=s,o<a.length?o++:clearInterval(f)},e)})}),m||u.querySelectorAll(".dm-fx-shake").forEach(function(t){var e=i(t,"intensity",5),a=i(t,"duration",500),n=r(t,"direction")||"horizontal",o=n==="vertical"?"translateY":"translateX";t.animate([{transform:o+"(0)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(0)"}],{duration:a,easing:"ease-in-out"})}),u.querySelectorAll(".dm-fx-ripple").forEach(function(t){t.style.position="relative",t.style.overflow="hidden",t.addEventListener("click",function(e){var a=r(t,"colour")||"rgba(255,255,255,0.3)",n=i(t,"duration",600),o=t.getBoundingClientRect(),f=Math.max(o.width,o.height)*2,s=document.createElement("span");s.style.cssText="position:absolute;border-radius:50%;pointer-events:none;width:"+f+"px;height:"+f+"px;left:"+(e.clientX-o.left-f/2)+"px;top:"+(e.clientY-o.top-f/2)+"px;background:"+a+";transform:scale(0);opacity:1;transition:transform "+n+"ms ease,opacity "+n+"ms ease;",t.appendChild(s),requestAnimationFrame(function(){s.style.transform="scale(1)",s.style.opacity="0"}),setTimeout(function(){s.remove()},n)})});var l=window.Domma&&window.Domma.effects;l&&(u.querySelectorAll(".dm-fx-breathe").forEach(function(t){if(!m){var e={};r(t,"amplitude")&&(e.amplitude=i(t,"amplitude",6)),r(t,"duration")&&(e.duration=i(t,"duration",3e3)),r(t,"easing")&&(e.easing=r(t,"easing")),r(t,"stagger")&&(e.stagger=i(t,"stagger",0)),l.breathe(t,e)}}),u.querySelectorAll(".dm-fx-pulse").forEach(function(t){if(!m){var e={};r(t,"scale")&&(e.scale=i(t,"scale",1.05)),r(t,"duration")&&(e.duration=i(t,"duration",2e3)),r(t,"easing")&&(e.easing=r(t,"easing")),l.pulse(t,e)}}),u.querySelectorAll(".dm-fx-scribe").forEach(function(t){var e={};r(t,"speed")&&(e.speed=i(t,"speed",50)),r(t,"delete-speed")&&(e.deleteSpeed=i(t,"delete-speed",30)),r(t,"cursor")&&(e.cursor=y(t,"cursor",!0)),r(t,"cursor-char")&&(e.cursorChar=r(t,"cursor-char")),r(t,"cursor-type")&&(e.cursorType=r(t,"cursor-type")),r(t,"mode")&&(e.mode=r(t,"mode")),r(t,"loop")&&(e.loop=y(t,"loop",!1)),r(t,"loop-delay")&&(e.loopDelay=i(t,"loop-delay",1e3)),r(t,"pause-on-hover")&&(e.pauseOnHover=y(t,"pause-on-hover",!1));var a=r(t,"actions");if(a)try{e.actions=JSON.parse(a)}catch{return}else{var n=t.textContent.trim();if(!n)return;t.textContent="",e.actions=[{render:n}]}l.scribe(t,e)}),u.querySelectorAll(".dm-fx-twinkle").forEach(function(t){var e={};r(t,"count")&&(e.count=i(t,"count",100)),r(t,"shape")&&(e.shape=r(t,"shape")),r(t,"colour")&&(e.colour=r(t,"colour")),r(t,"min-size")&&(e.minSize=i(t,"min-size",1)),r(t,"max-size")&&(e.maxSize=i(t,"max-size",3)),l.twinkle(t,e)}));var b=u.querySelectorAll(".dm-fx-celebrate");b.length&&!m&&import("/public/js/celebrations/index.js").then(function(t){var e=t.CelebrationsEffect;b.forEach(function(a){var n=a.getAttribute("data-fx-theme")||"auto",o=a.getAttribute("data-fx-intensity")||"medium",f=a.getAttribute("data-fx-z-index")?parseInt(a.getAttribute("data-fx-z-index"),10):999;if(!(n==="auto"&&(n=e.getCurrentTheme(),!n))){var s=new e({theme:n,intensity:o,enabled:!0,zIndex:f});s.init().then(function(){s.start()})}})}).catch(function(){})}).catch(function(){})})();
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Effects API Routes
3
+ * GET /api/effects/settings — public (read by public site IIFE)
4
+ * PUT /api/effects/settings — admin-only (save user overrides)
5
+ */
6
+ import {authenticate, requireAdmin} from '../../middleware/auth.js';
7
+ import {getConfig, saveConfig} from '../../config.js';
8
+
9
+ export async function effectsRoutes(fastify) {
10
+ fastify.get('/effects/settings', async () => {
11
+ try {
12
+ return getConfig('effects');
13
+ } catch {
14
+ return {respectMotion: true, defaultDuration: 600, defaultAnimation: 'fade', defaultThreshold: 0.1};
15
+ }
16
+ });
17
+
18
+ fastify.put('/effects/settings', {preHandler: [authenticate, requireAdmin]}, async (request) => {
19
+ const body = request.body || {};
20
+ saveConfig('effects', body);
21
+ return {ok: true};
22
+ });
23
+ }
package/server/server.js CHANGED
@@ -229,6 +229,7 @@ const { viewsRoutes } = await import('./routes/api/views.js');
229
229
  const { actionsRoutes } = await import('./routes/api/actions.js');
230
230
  const {blocksRoutes} = await import('./routes/api/blocks.js');
231
231
  const {versionsRoutes} = await import('./routes/api/versions.js');
232
+ const {effectsRoutes} = await import('./routes/api/effects.js');
232
233
 
233
234
  await app.register(pagesRoutes, { prefix: '/api' });
234
235
  await app.register(settingsRoutes, { prefix: '/api' });
@@ -243,6 +244,7 @@ await app.register(viewsRoutes, { prefix: '/api' });
243
244
  await app.register(actionsRoutes, { prefix: '/api' });
244
245
  await app.register(blocksRoutes, {prefix: '/api'});
245
246
  await app.register(versionsRoutes, {prefix: '/api'});
247
+ await app.register(effectsRoutes, {prefix: '/api'});
246
248
 
247
249
  // ---------------------------------------------------------------------------
248
250
  // CMS Plugins (server-side Fastify plugins from plugins/ directory)
@@ -532,6 +532,128 @@ function processPluginShortcodes(markdown) {
532
532
  return restore(result);
533
533
  }
534
534
 
535
+ /**
536
+ * Process built-in Effects shortcodes natively.
537
+ * Handles: [counter /], [celebrate /], [firework], [fireworks], [scribe],
538
+ * [reveal], [breathe], [pulse], [shake], [scramble], [ripple],
539
+ * [twinkle], [animate], [ambient]
540
+ *
541
+ * @param {string} markdown
542
+ * @returns {string}
543
+ */
544
+ function processEffectsBlocks(markdown) {
545
+ const {scrubbed, restore} = scrubCodeRegions(markdown);
546
+ let result = scrubbed;
547
+
548
+ function apply(name, handler) {
549
+ result = result.replace(
550
+ new RegExp(`\\[${name}([^\\]]*)\\s*\\/\\]`, 'gi'),
551
+ (_, attrStr) => handler(attrStr, null)
552
+ );
553
+ result = result.replace(
554
+ new RegExp(`\\[${name}([^\\]]*)\\]([\\s\\S]*?)\\[\\/${name}\\]`, 'gi'),
555
+ (_, attrStr, body) => handler(attrStr, body)
556
+ );
557
+ }
558
+
559
+ apply('counter', (attrStr) => {
560
+ const attrs = parseShortcodeAttrs(attrStr);
561
+ const to = attrs.to || '0';
562
+ const prefix = attrs.prefix || '';
563
+ const suffix = attrs.suffix || '';
564
+ const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
565
+ return `<span class="dm-fx-counter"${dataAttrs}>${prefix}${to}${suffix}</span>`;
566
+ });
567
+
568
+ apply('celebrate', (attrStr) => {
569
+ const attrs = parseShortcodeAttrs(attrStr);
570
+ const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
571
+ return `<div class="dm-fx-celebrate"${dataAttrs}></div>`;
572
+ });
573
+
574
+ apply('firework', (attrStr, body) => {
575
+ const attrs = parseShortcodeAttrs(attrStr);
576
+ const classes = ['firework'];
577
+ if (attrs.type) classes.push(`firework-${attrs.type}`);
578
+ if (attrs.colour) classes.push(`firework-${attrs.colour}`);
579
+ if (attrs.size) classes.push(`firework-${attrs.size}`);
580
+ if (attrs.continuous === 'true') classes.push('firework-continuous');
581
+ if (attrs.hover === 'true') classes.push('firework-on-hover');
582
+ if (body === null) return `<div class="${classes.join(' ')}"></div>`;
583
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(body.trim())));
584
+ return `<div class="${classes.join(' ')}">${innerHtml}</div>\n`;
585
+ });
586
+
587
+ apply('fireworks', (_attrStr, body) => {
588
+ if (body === null) return '';
589
+ return `<div class="fireworks-container">${body.trim()}</div>\n`;
590
+ });
591
+
592
+ apply('scribe', (attrStr, body) => {
593
+ if (body === null) return '';
594
+ const attrs = parseShortcodeAttrs(attrStr);
595
+ const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${escapeAttr(v)}"`).join('');
596
+ const hasActions = /\[\s*(render|wait|undo)\b/.test(body);
597
+ if (!hasActions) {
598
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(body.trim())));
599
+ return `<div class="dm-fx-scribe"${dataAttrs}>${innerHtml}</div>\n`;
600
+ }
601
+ const actions = [];
602
+ const actionRe = /\[render([^\]]*)\]([\s\S]*?)\[\/render\]|\[wait\]([\s\S]*?)\[\/wait\]|\[undo([^\]]*?)\/\]/gi;
603
+ for (const match of body.matchAll(actionRe)) {
604
+ if (match[0].startsWith('[render')) {
605
+ const rAttrs = parseShortcodeAttrs(match[1] || '');
606
+ const action = {render: match[2].trim()};
607
+ if (rAttrs.effect) action.effect = rAttrs.effect;
608
+ actions.push(action);
609
+ } else if (match[0].startsWith('[wait')) {
610
+ const val = match[3].trim();
611
+ actions.push({wait: isNaN(val) ? val : parseInt(val, 10)});
612
+ } else {
613
+ const uAttrs = parseShortcodeAttrs(match[4] || '');
614
+ if (uAttrs.all === 'true') actions.push({undoRender: 'all'});
615
+ else if (uAttrs.count) actions.push({undoRender: parseInt(uAttrs.count, 10)});
616
+ else actions.push({undoRender: true});
617
+ }
618
+ }
619
+ if (!actions.length) return '';
620
+ return `<span class="dm-fx-scribe"${dataAttrs} data-fx-actions="${escapeAttr(JSON.stringify(actions))}"></span>\n`;
621
+ });
622
+
623
+ for (const name of ['reveal', 'breathe', 'pulse', 'shake', 'scramble', 'ripple', 'twinkle']) {
624
+ apply(name, (attrStr, body) => {
625
+ if (body === null) return '';
626
+ const attrs = parseShortcodeAttrs(attrStr);
627
+ const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
628
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(body.trim())));
629
+ return `<div class="dm-fx-${name}"${dataAttrs}>${innerHtml}</div>\n`;
630
+ });
631
+ }
632
+
633
+ apply('animate', (attrStr, body) => {
634
+ if (body === null) return '';
635
+ const attrs = parseShortcodeAttrs(attrStr);
636
+ const classes = [];
637
+ if (attrs.type) classes.push(`animate-${attrs.type}`);
638
+ if (attrs.duration) classes.push(`animate-duration-${attrs.duration}`);
639
+ if (attrs.delay) classes.push(`animate-delay-${attrs.delay}`);
640
+ if (attrs.repeat) classes.push(`animate-${attrs.repeat}`);
641
+ return `<div class="${classes.join(' ')}">${marked.parse(processCardBlocks(processGridBlocks(body.trim())))}</div>\n`;
642
+ });
643
+
644
+ apply('ambient', (attrStr, body) => {
645
+ if (body === null) return '';
646
+ const attrs = parseShortcodeAttrs(attrStr);
647
+ const classes = [];
648
+ if (attrs.type) classes.push(`bg-ambient-${attrs.type}`);
649
+ if (attrs.speed) classes.push(`bg-ambient-${attrs.speed}`);
650
+ if (attrs.intensity) classes.push(`bg-ambient-${attrs.intensity}`);
651
+ return `<div class="${classes.join(' ')}">${marked.parse(processCardBlocks(processGridBlocks(body.trim())))}</div>\n`;
652
+ });
653
+
654
+ return restore(result);
655
+ }
656
+
535
657
  /**
536
658
  * Pre-process [row], [grid], and [col] shortcodes before running through marked.
537
659
  *
@@ -1740,7 +1862,7 @@ export async function parseMarkdown(raw) {
1740
1862
  const extensions = getSanitizeExtensions();
1741
1863
 
1742
1864
  // Pipeline:
1743
- // beforeParse → collection → view → staticBlock → dconfig → plugin shortcodes → tabs → accordion → carousel
1865
+ // beforeParse → collection → view → staticBlock → dconfig → effects → plugin shortcodes → tabs → accordion → carousel
1744
1866
  // → countdown → timeline → spacer → center → icon → form → hero → table → badge → button → link → cta
1745
1867
  // → grid → card → slideover → marked → sanitize → afterParse
1746
1868
  const preprocessed = applyTransforms('markdown:beforeParse', content);
@@ -1748,7 +1870,8 @@ export async function parseMarkdown(raw) {
1748
1870
  const withView = await processViewBlocks(withCollection);
1749
1871
  const withStaticBlock = await processStaticBlocks(withView);
1750
1872
  const withDconfig = processDConfigBlocks(withStaticBlock);
1751
- const withPluginShortcodes = processPluginShortcodes(withDconfig);
1873
+ const withEffects = processEffectsBlocks(withDconfig);
1874
+ const withPluginShortcodes = processPluginShortcodes(withEffects);
1752
1875
  const withTabs = processTabsBlocks(withPluginShortcodes);
1753
1876
  const withAccordion = processAccordionBlocks(withTabs);
1754
1877
  const withCarousel = processCarouselBlocks(withAccordion);
@@ -24,7 +24,7 @@ const REQUIRED_MANIFEST_FIELDS = ['name', 'displayName', 'version', 'description
24
24
  * Core plugins — always loaded regardless of plugins.json enabled state.
25
25
  * These are considered first-class CMS features, not optional add-ons.
26
26
  */
27
- const CORE_PLUGINS = new Set(['domma-effects']);
27
+ const CORE_PLUGINS = new Set();
28
28
 
29
29
  /**
30
30
  * Scan the plugins/ directory and return all valid manifests.
@@ -121,6 +121,9 @@
121
121
  <script src="/public/js/form-logic-engine.js"></script>
122
122
  <script src="/public/js/forms.js" type="module"></script>
123
123
 
124
+ <!-- Core effects runtime -->
125
+ <script src="/public/js/effects.js"></script>
126
+
124
127
  <!-- Plugin body-end injection -->
125
128
  {{bodyEndInject}}
126
129
  </body>