framefields 2.5.3 → 2.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio/index.d.mts +4 -4
- package/dist/audio/index.mjs +10 -10
- package/dist/{audio-2ew5y0Bl.d.mts → audio-B5mdR8vv.d.mts} +3 -3
- package/dist/{audio-2ew5y0Bl.d.mts.map → audio-B5mdR8vv.d.mts.map} +1 -1
- package/dist/{audio-DrUskzwZ.mjs → audio-C9euVW4K.mjs} +3 -3
- package/dist/{audio-DrUskzwZ.mjs.map → audio-C9euVW4K.mjs.map} +1 -1
- package/dist/components/index.d.mts +10 -10
- package/dist/components/index.mjs +44 -44
- package/dist/dist-BPOi5xb_.mjs.map +1 -1
- package/dist/{dist-Bf0H4lp1.mjs → dist-BuirkW-f.mjs} +11 -3
- package/dist/dist-BuirkW-f.mjs.map +1 -0
- package/dist/{dist-DG65RDhc.mjs → dist-DBdxM6xv.mjs} +5 -6
- package/dist/dist-DBdxM6xv.mjs.map +1 -0
- package/dist/effects/index.d.mts +4 -4
- package/dist/effects/index.mjs +35 -35
- package/dist/{effects-Z-brb-n7.mjs → effects-B263lgUi.mjs} +2 -2
- package/dist/{effects-Z-brb-n7.mjs.map → effects-B263lgUi.mjs.map} +1 -1
- package/dist/fonts/index.mjs +2 -2
- package/dist/{fonts-DqYgk_1a.mjs → fonts-DNx6ps2p.mjs} +2 -2
- package/dist/{fonts-DqYgk_1a.mjs.map → fonts-DNx6ps2p.mjs.map} +1 -1
- package/dist/{gpu-CevnCkZC.mjs → gpu-BRBIoxMC.mjs} +2 -2
- package/dist/{gpu-CevnCkZC.mjs.map → gpu-BRBIoxMC.mjs.map} +1 -1
- package/dist/{index-D1C735Wp.d.mts → index-3Vdef2KE.d.mts} +3 -3
- package/dist/{index-D1C735Wp.d.mts.map → index-3Vdef2KE.d.mts.map} +1 -1
- package/dist/{index-COQTaniR.d.mts → index-B4o6F1JC.d.mts} +2 -2
- package/dist/{index-COQTaniR.d.mts.map → index-B4o6F1JC.d.mts.map} +1 -1
- package/dist/{index-KqW22NHL.d.mts → index-BhzlG_1Q.d.mts} +2 -2
- package/dist/{index-KqW22NHL.d.mts.map → index-BhzlG_1Q.d.mts.map} +1 -1
- package/dist/{index-DSGFMpwL.d.mts → index-D-Gnyffj.d.mts} +4 -4
- package/dist/{index-DSGFMpwL.d.mts.map → index-D-Gnyffj.d.mts.map} +1 -1
- package/dist/{index-tCKz9Tku.d.mts → index-N1tMc5Z9.d.mts} +54 -54
- package/dist/index-N1tMc5Z9.d.mts.map +1 -0
- package/dist/{index-CfMylY3O.d.mts → index-o8LxWdwz.d.mts} +3 -3
- package/dist/{index-CfMylY3O.d.mts.map → index-o8LxWdwz.d.mts.map} +1 -1
- package/dist/{index-CZK6CFLy.d.mts → index-ocZc12ZS.d.mts} +17 -17
- package/dist/index-ocZc12ZS.d.mts.map +1 -0
- package/dist/index.d.mts +10 -10
- package/dist/index.mjs +51 -51
- package/dist/preview/index.d.mts +3 -3
- package/dist/preview/index.mjs +10 -10
- package/dist/{preview-DVw2jLDh.mjs → preview-ChR8Z8Fm.mjs} +2 -2
- package/dist/{preview-DVw2jLDh.mjs.map → preview-ChR8Z8Fm.mjs.map} +1 -1
- package/dist/preview-engine/audio.mjs +1 -1
- package/dist/preview-engine/{chunk-MCFYUKAJ.mjs → chunk-2LQCQ3OT.mjs} +8 -8
- package/dist/preview-engine/{chunk-PR37RCNO.mjs → chunk-2TCJSHFA.mjs} +1 -1
- package/dist/preview-engine/{chunk-JMOJKT5W.mjs → chunk-2YGR5YCN.mjs} +1 -1
- package/dist/preview-engine/{chunk-LNJYHSYX.mjs → chunk-3KHD67Q3.mjs} +1 -1
- package/dist/preview-engine/chunk-3SR6BI4B.mjs +1 -0
- package/dist/preview-engine/{chunk-NXKXOSE6.mjs → chunk-4INGTMZ5.mjs} +1 -1
- package/dist/preview-engine/{chunk-T4VP45AP.mjs → chunk-4JX5LBE2.mjs} +1 -1
- package/dist/preview-engine/{chunk-7JYMNGNK.mjs → chunk-5NT7WWJR.mjs} +1 -1
- package/dist/preview-engine/{chunk-NB55FW7D.mjs → chunk-6PMATELR.mjs} +1 -1
- package/dist/preview-engine/{chunk-45XGWIOY.mjs → chunk-AKT4YQWL.mjs} +1 -1
- package/dist/preview-engine/{chunk-RNFYEZLL.mjs → chunk-ALSOO22Q.mjs} +1 -1
- package/dist/preview-engine/{chunk-3AYICEZ5.mjs → chunk-B7J72YSP.mjs} +1 -1
- package/dist/preview-engine/{chunk-I3ZTDIVM.mjs → chunk-BBHRXV75.mjs} +1 -1
- package/dist/preview-engine/{chunk-NHQR76WL.mjs → chunk-DDFWN7DI.mjs} +1 -1
- package/dist/preview-engine/{chunk-ZVUTFXDR.mjs → chunk-DOCFOSA6.mjs} +1 -1
- package/dist/preview-engine/{chunk-YJ24W73B.mjs → chunk-FZLKPBKS.mjs} +1 -1
- package/dist/preview-engine/{chunk-EU7CFSB5.mjs → chunk-GQIRV466.mjs} +1 -1
- package/dist/preview-engine/{chunk-DWHQSNSJ.mjs → chunk-IIOHF7MH.mjs} +1 -1
- package/dist/preview-engine/{chunk-WUMVCQYI.mjs → chunk-J24VHZS6.mjs} +1 -1
- package/dist/preview-engine/{chunk-2WOCQNZX.mjs → chunk-JD4TBC6Z.mjs} +8 -8
- package/dist/preview-engine/{chunk-X6BJLPW6.mjs → chunk-JTT3YX7W.mjs} +1 -1
- package/dist/preview-engine/{chunk-XJ4CQIZZ.mjs → chunk-KWTTANYN.mjs} +1 -1
- package/dist/preview-engine/{chunk-QCUGWPYP.mjs → chunk-LN3YAWUD.mjs} +1 -1
- package/dist/preview-engine/{chunk-M6ZKK6ZU.mjs → chunk-LYSU6S4O.mjs} +1 -1
- package/dist/preview-engine/{chunk-TE2QAKKF.mjs → chunk-MJCK5FLZ.mjs} +1 -1
- package/dist/preview-engine/{chunk-24H3QVYB.mjs → chunk-MMVFX42U.mjs} +1 -1
- package/dist/preview-engine/{chunk-25T3M6MI.mjs → chunk-MUOJMF7M.mjs} +1 -1
- package/dist/preview-engine/{chunk-B555EGEU.mjs → chunk-NQZRPPUU.mjs} +1 -1
- package/dist/preview-engine/{chunk-VTE3IISK.mjs → chunk-OXXRKNHW.mjs} +1 -1
- package/dist/preview-engine/{chunk-DUEY7AXY.mjs → chunk-PJMX2GFY.mjs} +1 -1
- package/dist/preview-engine/{chunk-MA4YVA2E.mjs → chunk-Q7JN4EHX.mjs} +1 -1
- package/dist/preview-engine/{chunk-YSXMXD3F.mjs → chunk-Q7XC3FI7.mjs} +1 -1
- package/dist/preview-engine/{chunk-UXCTWFKK.mjs → chunk-QPBTLJ5W.mjs} +1 -1
- package/dist/preview-engine/{chunk-LQVACZDE.mjs → chunk-QRYCB6E3.mjs} +1 -1
- package/dist/preview-engine/{chunk-2QQQTZHS.mjs → chunk-RDEQ4LQU.mjs} +1 -1
- package/dist/preview-engine/{chunk-KKBJ7X3T.mjs → chunk-RF62TV3G.mjs} +1 -1
- package/dist/preview-engine/{chunk-CE6CG5BH.mjs → chunk-RHXZXWJ4.mjs} +1 -1
- package/dist/preview-engine/{chunk-BNO5JAEZ.mjs → chunk-RKNV5N7D.mjs} +1 -1
- package/dist/preview-engine/{chunk-PXWCGXTF.mjs → chunk-SP22JWN2.mjs} +1 -1
- package/dist/preview-engine/{chunk-NYEDWUFS.mjs → chunk-SQ52PBF3.mjs} +1 -1
- package/dist/preview-engine/{chunk-QYIKOOFI.mjs → chunk-SVE3XWO7.mjs} +1 -1
- package/dist/preview-engine/{chunk-QJYAIQYU.mjs → chunk-SXNCNG4L.mjs} +1 -1
- package/dist/preview-engine/{chunk-VH5V54RA.mjs → chunk-TPFDAOW7.mjs} +1 -1
- package/dist/preview-engine/{chunk-ENS4KUJV.mjs → chunk-UEQIO6AH.mjs} +1 -1
- package/dist/preview-engine/{chunk-XYRBFLFF.mjs → chunk-ULPLHKM2.mjs} +1 -1
- package/dist/preview-engine/{chunk-MCR7XMNQ.mjs → chunk-ULVX4VYT.mjs} +1 -1
- package/dist/preview-engine/{chunk-ZAIVXGSS.mjs → chunk-UQD2UE6I.mjs} +1 -1
- package/dist/preview-engine/{chunk-QP24X77K.mjs → chunk-VY6ST4QX.mjs} +1 -1
- package/dist/preview-engine/{chunk-7BUKVFWB.mjs → chunk-W2EZFV4Q.mjs} +1 -1
- package/dist/preview-engine/{chunk-P734GD7D.mjs → chunk-WDWHOXSA.mjs} +1 -1
- package/dist/preview-engine/{chunk-2RZ6S4SW.mjs → chunk-YEPYVASB.mjs} +1 -1
- package/dist/preview-engine/{chunk-5FTCPTOX.mjs → chunk-ZJLNXN7W.mjs} +8 -8
- package/dist/preview-engine/{chunk-FAEWI3LF.mjs → chunk-ZZAZF6VC.mjs} +1 -1
- package/dist/preview-engine/components.mjs +1 -1
- package/dist/preview-engine/effects.mjs +1 -1
- package/dist/preview-engine/fonts.mjs +1 -1
- package/dist/preview-engine/framefields.mjs +1 -1
- package/dist/preview-engine/player.mjs +1 -1
- package/dist/preview-engine/renderers-4CRXZJ5G.mjs +1 -0
- package/dist/preview-engine/{renderers-WPBTAU2E.mjs → renderers-4RVSLTCX.mjs} +1 -1
- package/dist/preview-engine/{renderers-W5CTR4B5.mjs → renderers-56TGEFDK.mjs} +1 -1
- package/dist/preview-engine/renderers-5QF4OLFQ.mjs +1 -0
- package/dist/preview-engine/renderers-5SOW6OVE.mjs +1 -0
- package/dist/preview-engine/renderers-6M2HPY2J.mjs +1 -0
- package/dist/preview-engine/{renderers-DZZFIYYA.mjs → renderers-7GMW52G3.mjs} +1 -1
- package/dist/preview-engine/renderers-7HT5NW7W.mjs +1 -0
- package/dist/preview-engine/{renderers-KV3TTCO6.mjs → renderers-7SW2AHM2.mjs} +1 -1
- package/dist/preview-engine/{renderers-BNXTSIXJ.mjs → renderers-ART3EGSZ.mjs} +1 -1
- package/dist/preview-engine/{renderers-6NFRKHNO.mjs → renderers-ATTGUTYY.mjs} +1 -1
- package/dist/preview-engine/renderers-AYWCRBQC.mjs +1 -0
- package/dist/preview-engine/renderers-BA6ZCPI5.mjs +1 -0
- package/dist/preview-engine/{renderers-AKZAAGR7.mjs → renderers-BAS7RXTC.mjs} +1 -1
- package/dist/preview-engine/{renderers-CLLR5MOQ.mjs → renderers-BWRNCXR6.mjs} +1 -1
- package/dist/preview-engine/{renderers-N7T34SCI.mjs → renderers-C3YXNKNM.mjs} +1 -1
- package/dist/preview-engine/renderers-CZTCW4FC.mjs +1 -0
- package/dist/preview-engine/{renderers-C7SJ3M6S.mjs → renderers-D2XEQ2YY.mjs} +1 -1
- package/dist/preview-engine/renderers-DFIJYBW2.mjs +1 -0
- package/dist/preview-engine/{renderers-DHKB5LI6.mjs → renderers-EJGF3PYQ.mjs} +1 -1
- package/dist/preview-engine/renderers-EJN6M6DO.mjs +1 -0
- package/dist/preview-engine/{renderers-MKEXNBCW.mjs → renderers-EK3HQUUF.mjs} +1 -1
- package/dist/preview-engine/{renderers-O56IOD7J.mjs → renderers-EQDINUMT.mjs} +1 -1
- package/dist/preview-engine/{renderers-VYK2BJID.mjs → renderers-FC7G7KU7.mjs} +1 -1
- package/dist/preview-engine/{renderers-BQLYS4TM.mjs → renderers-FD3ZT73R.mjs} +1 -1
- package/dist/preview-engine/{renderers-TASYN62K.mjs → renderers-FYI6QNUH.mjs} +1 -1
- package/dist/preview-engine/renderers-G34C5J27.mjs +1 -0
- package/dist/preview-engine/{renderers-ROSSJ7Y2.mjs → renderers-GHP7SL5M.mjs} +1 -1
- package/dist/preview-engine/renderers-GKVTNDVH.mjs +1 -0
- package/dist/preview-engine/{renderers-MPCYLXXU.mjs → renderers-GY4R467B.mjs} +1 -1
- package/dist/preview-engine/{renderers-TAMDNTEQ.mjs → renderers-H2UI7YEO.mjs} +1 -1
- package/dist/preview-engine/{renderers-6PVGRTXV.mjs → renderers-HBIBRZD7.mjs} +1 -1
- package/dist/preview-engine/{renderers-GF77N2KM.mjs → renderers-HMNCIEU3.mjs} +1 -1
- package/dist/preview-engine/{renderers-SIBP54WK.mjs → renderers-HZBYQKIQ.mjs} +1 -1
- package/dist/preview-engine/renderers-IK7ZJJKJ.mjs +1 -0
- package/dist/preview-engine/renderers-ISASPH6T.mjs +1 -0
- package/dist/preview-engine/renderers-IW5MWDBI.mjs +1 -0
- package/dist/preview-engine/renderers-K2PI4WJS.mjs +1 -0
- package/dist/preview-engine/renderers-KESHVFL2.mjs +1 -0
- package/dist/preview-engine/renderers-KTZZVAB2.mjs +1 -0
- package/dist/preview-engine/renderers-KY5CJEOV.mjs +1 -0
- package/dist/preview-engine/{renderers-MS4HG3M4.mjs → renderers-LTV5RFDJ.mjs} +1 -1
- package/dist/preview-engine/{renderers-CEJP6Q7F.mjs → renderers-LV6LLULR.mjs} +1 -1
- package/dist/preview-engine/renderers-M5W6SUQD.mjs +1 -0
- package/dist/preview-engine/{renderers-H3ZM2TOA.mjs → renderers-MEM3REMI.mjs} +1 -1
- package/dist/preview-engine/{renderers-EJATUIUP.mjs → renderers-N36R42RL.mjs} +1 -1
- package/dist/preview-engine/{renderers-UVABXXQF.mjs → renderers-NTKSN32T.mjs} +1 -1
- package/dist/preview-engine/renderers-O5XMWFPY.mjs +1 -0
- package/dist/preview-engine/{renderers-QO2RHF7F.mjs → renderers-P2FTCDZ7.mjs} +1 -1
- package/dist/preview-engine/{renderers-L7EITUCD.mjs → renderers-PYDOSDEY.mjs} +1 -1
- package/dist/preview-engine/renderers-QJA35PKC.mjs +1 -0
- package/dist/preview-engine/{renderers-TWZHTKW7.mjs → renderers-RDHHVXRE.mjs} +1 -1
- package/dist/preview-engine/{renderers-RRJ2DX2W.mjs → renderers-SY435YU3.mjs} +1 -1
- package/dist/preview-engine/renderers-TJIFTQHV.mjs +1 -0
- package/dist/preview-engine/renderers-TN4XN436.mjs +1 -0
- package/dist/preview-engine/renderers-UDYLCYCB.mjs +1 -0
- package/dist/preview-engine/renderers-WZ7SFIBZ.mjs +1 -0
- package/dist/preview-engine/renderers-WZEV464O.mjs +228 -0
- package/dist/preview-engine/renderers-X6QNDC6R.mjs +1 -0
- package/dist/preview-engine/renderers-X6RNTJOV.mjs +1 -0
- package/dist/preview-engine/{renderers-QPFZBLDI.mjs → renderers-Y3N7236P.mjs} +1 -1
- package/dist/preview-engine/{renderers-XSPU2BIQ.mjs → renderers-YBTWN76S.mjs} +1 -1
- package/dist/preview-engine/{renderers-T62TXGVO.mjs → renderers-YK74777T.mjs} +1 -1
- package/dist/preview-engine/renderers-YP3ASXIJ.mjs +1 -0
- package/dist/preview-engine/renderers-YVJAG3PD.mjs +1 -0
- package/dist/preview-engine/{renderers-4RFB2LG2.mjs → renderers-ZAMSS7QN.mjs} +1 -1
- package/dist/preview-engine/{renderers-FOHW7RWH.mjs → renderers-ZDMDDWLN.mjs} +1 -1
- package/dist/preview-engine/renderers-ZPPZNMKJ.mjs +1 -0
- package/dist/preview-engine/signals.mjs +1 -1
- package/dist/{program-4mAVgg3c.mjs → program-O2bWX0cs.mjs} +2 -2
- package/dist/{program-4mAVgg3c.mjs.map → program-O2bWX0cs.mjs.map} +1 -1
- package/dist/registry/card-stack.json +3 -2
- package/dist/registry/chat-thread.json +4 -3
- package/dist/registry/index.json +3 -2
- package/dist/renderer/index.d.mts +2 -2
- package/dist/renderer/index.mjs +2 -2
- package/dist/renderer-0441BN0U.mjs +5 -0
- package/dist/{renderer-RiWi7y5C.mjs → renderer-1NoJLLDa.mjs} +4 -4
- package/dist/{renderer-RiWi7y5C.mjs.map → renderer-1NoJLLDa.mjs.map} +1 -1
- package/dist/renderer-3qDgFc-W.mjs +5 -0
- package/dist/renderer-6AIqTN5Z.mjs +5 -0
- package/dist/{renderer-zdpn1Hkk.mjs → renderer-8_d6YNnE.mjs} +3 -3
- package/dist/{renderer-zdpn1Hkk.mjs.map → renderer-8_d6YNnE.mjs.map} +1 -1
- package/dist/{renderer-DTXAa2pL.mjs → renderer-B0IJirDs.mjs} +4 -4
- package/dist/{renderer-DTXAa2pL.mjs.map → renderer-B0IJirDs.mjs.map} +1 -1
- package/dist/{renderer-_vcT__tW.mjs → renderer-B2no_9zi.mjs} +4 -4
- package/dist/{renderer-_vcT__tW.mjs.map → renderer-B2no_9zi.mjs.map} +1 -1
- package/dist/{renderer-CkJXq4bD.mjs → renderer-B5ZdkcMi.mjs} +3 -3
- package/dist/{renderer-CkJXq4bD.mjs.map → renderer-B5ZdkcMi.mjs.map} +1 -1
- package/dist/renderer-B7MZ5R7u.mjs +5 -0
- package/dist/{renderer-Bh0dwc8n.mjs → renderer-B7vXnBUo.mjs} +4 -4
- package/dist/{renderer-Bh0dwc8n.mjs.map → renderer-B7vXnBUo.mjs.map} +1 -1
- package/dist/renderer-BE2rIA4B.mjs +5 -0
- package/dist/renderer-BIQ81ln4.mjs +5 -0
- package/dist/{renderer-BIYJKdwA.mjs → renderer-BKJ8cywK.mjs} +3 -3
- package/dist/{renderer-BIYJKdwA.mjs.map → renderer-BKJ8cywK.mjs.map} +1 -1
- package/dist/renderer-BKm3_Ob3.mjs +5 -0
- package/dist/{renderer-DPzair18.mjs → renderer-BMiZIGkZ.mjs} +2 -2
- package/dist/renderer-BNdT1Wwf.mjs +5 -0
- package/dist/{renderer-C1sFg34Y.mjs → renderer-BNs31kME.mjs} +4 -4
- package/dist/{renderer-C1sFg34Y.mjs.map → renderer-BNs31kME.mjs.map} +1 -1
- package/dist/renderer-BQycs1q_.mjs +5 -0
- package/dist/{renderer-Bt_j2Zi6.mjs → renderer-BStETpqC.mjs} +3 -3
- package/dist/{renderer-Bt_j2Zi6.mjs.map → renderer-BStETpqC.mjs.map} +1 -1
- package/dist/{renderer-CT_3Fegr.mjs → renderer-BWNQQZ12.mjs} +4 -4
- package/dist/{renderer-CT_3Fegr.mjs.map → renderer-BWNQQZ12.mjs.map} +1 -1
- package/dist/{renderer-C8-NmotX.mjs → renderer-BZ5uOfoX.mjs} +3 -3
- package/dist/{renderer-C8-NmotX.mjs.map → renderer-BZ5uOfoX.mjs.map} +1 -1
- package/dist/{renderer-Ddg3dJmu.mjs → renderer-Bag1LFos.mjs} +4 -4
- package/dist/{renderer-Ddg3dJmu.mjs.map → renderer-Bag1LFos.mjs.map} +1 -1
- package/dist/{renderer-CvYxxS_X.mjs → renderer-BfQdc3RM.mjs} +73 -73
- package/dist/{renderer-CvYxxS_X.mjs.map → renderer-BfQdc3RM.mjs.map} +1 -1
- package/dist/{renderer-C9r0-KY4.mjs → renderer-BgmO1Dzw.mjs} +11 -2
- package/dist/{renderer-C9r0-KY4.mjs.map → renderer-BgmO1Dzw.mjs.map} +1 -1
- package/dist/renderer-Bj7MxXfk.mjs +5 -0
- package/dist/renderer-BjRAGYYC.mjs +5 -0
- package/dist/{renderer-BIt-At3v.mjs → renderer-BlU3Sh_0.mjs} +4 -4
- package/dist/{renderer-BIt-At3v.mjs.map → renderer-BlU3Sh_0.mjs.map} +1 -1
- package/dist/renderer-BqE2BQ_X.mjs +5 -0
- package/dist/{renderer-CxkUI9s_.mjs → renderer-BrKgtYq4.mjs} +4 -4
- package/dist/{renderer-CxkUI9s_.mjs.map → renderer-BrKgtYq4.mjs.map} +1 -1
- package/dist/{renderer-NL2IZShC.mjs → renderer-Bs6BaMLf.mjs} +4 -4
- package/dist/{renderer-NL2IZShC.mjs.map → renderer-Bs6BaMLf.mjs.map} +1 -1
- package/dist/renderer-BsewLDHZ.mjs +3 -0
- package/dist/{renderer-Bk7vVfTu.mjs → renderer-BvA4sQz5.mjs} +3 -3
- package/dist/{renderer-Bk7vVfTu.mjs.map → renderer-BvA4sQz5.mjs.map} +1 -1
- package/dist/{renderer-BwRLXhxJ.mjs → renderer-Bvizxddu.mjs} +3 -3
- package/dist/{renderer-BwRLXhxJ.mjs.map → renderer-Bvizxddu.mjs.map} +1 -1
- package/dist/{renderer-D-FwaV1R.mjs → renderer-Bwxm-kWL.mjs} +2 -2
- package/dist/renderer-Bz5BMkOE.mjs +5 -0
- package/dist/{renderer-CE9N4EEI.d.mts → renderer-C-dcd14F.d.mts} +2 -2
- package/dist/{renderer-CE9N4EEI.d.mts.map → renderer-C-dcd14F.d.mts.map} +1 -1
- package/dist/{renderer-BcSSAzc4.mjs → renderer-C2MaJKfY.mjs} +4 -4
- package/dist/{renderer-BcSSAzc4.mjs.map → renderer-C2MaJKfY.mjs.map} +1 -1
- package/dist/{renderer-C4pS7aAI.mjs → renderer-C3tdjg8B.mjs} +4 -4
- package/dist/{renderer-C4pS7aAI.mjs.map → renderer-C3tdjg8B.mjs.map} +1 -1
- package/dist/{renderer-C4f_X1Qc.mjs → renderer-C5QYI1Wu.mjs} +3 -3
- package/dist/{renderer-C4f_X1Qc.mjs.map → renderer-C5QYI1Wu.mjs.map} +1 -1
- package/dist/{renderer-DPVEs5ME.mjs → renderer-C6n8y6Pw.mjs} +4 -4
- package/dist/{renderer-DPVEs5ME.mjs.map → renderer-C6n8y6Pw.mjs.map} +1 -1
- package/dist/{renderer-BKayVZhL.mjs → renderer-C8pJFxcL.mjs} +3 -3
- package/dist/{renderer-BKayVZhL.mjs.map → renderer-C8pJFxcL.mjs.map} +1 -1
- package/dist/{renderer-CTvPNRMH.mjs → renderer-CA_CwisV.mjs} +3 -3
- package/dist/{renderer-CTvPNRMH.mjs.map → renderer-CA_CwisV.mjs.map} +1 -1
- package/dist/renderer-CDOSfB9H.mjs +5 -0
- package/dist/{renderer-C0P8ZRiC.mjs → renderer-CEAmByVW.mjs} +3 -3
- package/dist/{renderer-C0P8ZRiC.mjs.map → renderer-CEAmByVW.mjs.map} +1 -1
- package/dist/{renderer-C7HIJcJK.mjs → renderer-CFCR_7bq.mjs} +4 -4
- package/dist/{renderer-C7HIJcJK.mjs.map → renderer-CFCR_7bq.mjs.map} +1 -1
- package/dist/{renderer-Y5JmTL8J.mjs → renderer-CFRTJ7cT.mjs} +3 -3
- package/dist/{renderer-Y5JmTL8J.mjs.map → renderer-CFRTJ7cT.mjs.map} +1 -1
- package/dist/{renderer-Cm9fEQsw.mjs → renderer-CPSg6eEQ.mjs} +3 -3
- package/dist/{renderer-Cm9fEQsw.mjs.map → renderer-CPSg6eEQ.mjs.map} +1 -1
- package/dist/renderer-CT26yQT-.mjs +5 -0
- package/dist/{renderer-B8PNlKEM.mjs → renderer-CTy0bSsl.mjs} +4 -4
- package/dist/{renderer-B8PNlKEM.mjs.map → renderer-CTy0bSsl.mjs.map} +1 -1
- package/dist/{renderer-Dkt1oh9H.mjs → renderer-CVbNiH03.mjs} +4 -4
- package/dist/{renderer-Dkt1oh9H.mjs.map → renderer-CVbNiH03.mjs.map} +1 -1
- package/dist/renderer-CZeAbz48.mjs +5 -0
- package/dist/{renderer-DYdll3xW.mjs → renderer-CbxfSmYE.mjs} +4 -4
- package/dist/{renderer-DYdll3xW.mjs.map → renderer-CbxfSmYE.mjs.map} +1 -1
- package/dist/{renderer-nLPfvY3-.mjs → renderer-CdxNwGeb.mjs} +2 -2
- package/dist/{renderer-nLPfvY3-.mjs.map → renderer-CdxNwGeb.mjs.map} +1 -1
- package/dist/{renderer-Cb5uDZm0.mjs → renderer-Cg2xS8h8.mjs} +3 -3
- package/dist/{renderer-Cb5uDZm0.mjs.map → renderer-Cg2xS8h8.mjs.map} +1 -1
- package/dist/{renderer-CJ2-jEy7.mjs → renderer-CnclEQdA.mjs} +3 -3
- package/dist/{renderer-CJ2-jEy7.mjs.map → renderer-CnclEQdA.mjs.map} +1 -1
- package/dist/{renderer-CgMnZYTM.mjs → renderer-CpDaUeCN.mjs} +4 -4
- package/dist/{renderer-CgMnZYTM.mjs.map → renderer-CpDaUeCN.mjs.map} +1 -1
- package/dist/{renderer-Du8z4UrN.mjs → renderer-CqJou_2L.mjs} +3 -3
- package/dist/{renderer-Du8z4UrN.mjs.map → renderer-CqJou_2L.mjs.map} +1 -1
- package/dist/{renderer-CxOaqnho.mjs → renderer-CrKYlipx.mjs} +3 -3
- package/dist/{renderer-CxOaqnho.mjs.map → renderer-CrKYlipx.mjs.map} +1 -1
- package/dist/{renderer-CPl1wOrj.mjs → renderer-D3FJcRA3.mjs} +3 -3
- package/dist/{renderer-CPl1wOrj.mjs.map → renderer-D3FJcRA3.mjs.map} +1 -1
- package/dist/renderer-D50b7kip.mjs +5 -0
- package/dist/renderer-D5LmrO96.mjs +5 -0
- package/dist/{renderer-vc8t_z-y.mjs → renderer-D8m3fuRp.mjs} +3 -3
- package/dist/{renderer-vc8t_z-y.mjs.map → renderer-D8m3fuRp.mjs.map} +1 -1
- package/dist/{renderer-BpBhvMi1.mjs → renderer-DADTT3ym.mjs} +2 -2
- package/dist/renderer-DCvqZ5o_.mjs +5 -0
- package/dist/{renderer-BBA-iCi-.mjs → renderer-DGOY-LBR.mjs} +34 -9
- package/dist/renderer-DGOY-LBR.mjs.map +1 -0
- package/dist/{renderer-Cz5SOnJr.mjs → renderer-DHdFVpdH.mjs} +3 -3
- package/dist/{renderer-Cz5SOnJr.mjs.map → renderer-DHdFVpdH.mjs.map} +1 -1
- package/dist/renderer-DJGJeGEn.mjs +5 -0
- package/dist/{renderer-BjxceNBj.mjs → renderer-DO1TeUqV.mjs} +4 -4
- package/dist/{renderer-BjxceNBj.mjs.map → renderer-DO1TeUqV.mjs.map} +1 -1
- package/dist/renderer-DONvNNXi.mjs +5 -0
- package/dist/renderer-DPaSo--Y.mjs +6 -0
- package/dist/{renderer-D0iSbNza.mjs → renderer-DTPmvL1i.mjs} +4 -4
- package/dist/{renderer-D0iSbNza.mjs.map → renderer-DTPmvL1i.mjs.map} +1 -1
- package/dist/{renderer-CWnhRMOq.mjs → renderer-DVGVcDvB.mjs} +4 -4
- package/dist/{renderer-CWnhRMOq.mjs.map → renderer-DVGVcDvB.mjs.map} +1 -1
- package/dist/{renderer-Cjw6lyaK.mjs → renderer-DW21AyEv.mjs} +4 -4
- package/dist/{renderer-Cjw6lyaK.mjs.map → renderer-DW21AyEv.mjs.map} +1 -1
- package/dist/{renderer-B9iVE2GG.mjs → renderer-DWG6EXGj.mjs} +4 -4
- package/dist/{renderer-B9iVE2GG.mjs.map → renderer-DWG6EXGj.mjs.map} +1 -1
- package/dist/{renderer-De7gno9X.mjs → renderer-Daqvhwjp.mjs} +4 -4
- package/dist/{renderer-De7gno9X.mjs.map → renderer-Daqvhwjp.mjs.map} +1 -1
- package/dist/{renderer-5-9DPis6.mjs → renderer-DcApNxER.mjs} +3 -3
- package/dist/{renderer-5-9DPis6.mjs.map → renderer-DcApNxER.mjs.map} +1 -1
- package/dist/{renderer-CGKKjJ5Q.mjs → renderer-DdkPY4hO.mjs} +3 -3
- package/dist/{renderer-CGKKjJ5Q.mjs.map → renderer-DdkPY4hO.mjs.map} +1 -1
- package/dist/renderer-DfYYokhr.mjs +5 -0
- package/dist/{renderer-C5h2TSjH.mjs → renderer-Dk0okoS3.mjs} +3 -3
- package/dist/{renderer-C5h2TSjH.mjs.map → renderer-Dk0okoS3.mjs.map} +1 -1
- package/dist/{renderer-B9m7xIPo.mjs → renderer-DkM6Ikdf.mjs} +4 -4
- package/dist/{renderer-B9m7xIPo.mjs.map → renderer-DkM6Ikdf.mjs.map} +1 -1
- package/dist/{renderer-T2dd9a2L.mjs → renderer-DkOxEQsg.mjs} +4 -4
- package/dist/{renderer-T2dd9a2L.mjs.map → renderer-DkOxEQsg.mjs.map} +1 -1
- package/dist/renderer-DlIJhdEM.mjs +5 -0
- package/dist/{renderer-BmX2CmyO.mjs → renderer-DmTxEeMh.mjs} +4 -4
- package/dist/{renderer-BmX2CmyO.mjs.map → renderer-DmTxEeMh.mjs.map} +1 -1
- package/dist/renderer-DqD3nMtv.mjs +5 -0
- package/dist/renderer-DrIl-uGt.mjs +5 -0
- package/dist/{renderer-khVVYoVF.mjs → renderer-DsnV8tpW.mjs} +4 -4
- package/dist/{renderer-khVVYoVF.mjs.map → renderer-DsnV8tpW.mjs.map} +1 -1
- package/dist/{renderer-wEXBnlr1.mjs → renderer-DtqiWq1m.mjs} +4 -4
- package/dist/{renderer-wEXBnlr1.mjs.map → renderer-DtqiWq1m.mjs.map} +1 -1
- package/dist/{renderer-wIsJb2eO.mjs → renderer-Duorbrb8.mjs} +2 -2
- package/dist/{renderer-wIsJb2eO.mjs.map → renderer-Duorbrb8.mjs.map} +1 -1
- package/dist/{renderer-CjcrhBB2.mjs → renderer-Dw3K8KT9.mjs} +3 -3
- package/dist/{renderer-CjcrhBB2.mjs.map → renderer-Dw3K8KT9.mjs.map} +1 -1
- package/dist/{renderer-4P-DCjk2.mjs → renderer-Dwh0jaor.mjs} +4 -4
- package/dist/{renderer-4P-DCjk2.mjs.map → renderer-Dwh0jaor.mjs.map} +1 -1
- package/dist/renderer-DyCB3Q1d.mjs +5 -0
- package/dist/renderer-DyJ-iSv6.mjs +5 -0
- package/dist/{renderer-DnT62IDK.mjs → renderer-Dz4muMyE.mjs} +3 -3
- package/dist/{renderer-DnT62IDK.mjs.map → renderer-Dz4muMyE.mjs.map} +1 -1
- package/dist/renderer-Dzs0jV0G.mjs +5 -0
- package/dist/{renderer-B9yv3R1y.mjs → renderer-H4-8sBjl.mjs} +3 -3
- package/dist/{renderer-B9yv3R1y.mjs.map → renderer-H4-8sBjl.mjs.map} +1 -1
- package/dist/renderer-HIKXw5-Y.mjs +5 -0
- package/dist/renderer-Jgt2g8Wp.mjs +5 -0
- package/dist/renderer-KnZWmT-M.mjs +5 -0
- package/dist/renderer-OMSXddG9.mjs +5 -0
- package/dist/renderer-P78xh6OV.mjs +5 -0
- package/dist/{renderer-BVtQzjgT.mjs → renderer-PhjHSS97.mjs} +3 -3
- package/dist/{renderer-BVtQzjgT.mjs.map → renderer-PhjHSS97.mjs.map} +1 -1
- package/dist/{renderer-9gjn790a.mjs → renderer-QQDU1b0Z.mjs} +4 -4
- package/dist/{renderer-9gjn790a.mjs.map → renderer-QQDU1b0Z.mjs.map} +1 -1
- package/dist/{renderer-BWqQxZNk.mjs → renderer-R5Lg65F8.mjs} +2 -2
- package/dist/{renderer-D22g6ION.mjs → renderer-RbMM6Njv.mjs} +4 -4
- package/dist/{renderer-D22g6ION.mjs.map → renderer-RbMM6Njv.mjs.map} +1 -1
- package/dist/{renderer-92IAZzBU.d.mts → renderer-WTvo3wo9.d.mts} +18 -4
- package/dist/renderer-WTvo3wo9.d.mts.map +1 -0
- package/dist/renderer-WyaqnVMz.mjs +3 -0
- package/dist/{renderer-B9AZ6R1U.mjs → renderer-YBwN9uyj.mjs} +172 -8
- package/dist/renderer-YBwN9uyj.mjs.map +1 -0
- package/dist/{renderer-CXa6SWXy.mjs → renderer-YFHESw3v.mjs} +4 -4
- package/dist/{renderer-CXa6SWXy.mjs.map → renderer-YFHESw3v.mjs.map} +1 -1
- package/dist/renderer-gaFP1Kak.mjs +5 -0
- package/dist/{renderer-PNIgmWft.mjs → renderer-kFtcuY9J.mjs} +2 -2
- package/dist/{renderer-PNIgmWft.mjs.map → renderer-kFtcuY9J.mjs.map} +1 -1
- package/dist/renderer-lQ8gH7Zd.mjs +5 -0
- package/dist/{renderer-MQPiZQXN.mjs → renderer-oR1LvHeO.mjs} +3 -3
- package/dist/{renderer-MQPiZQXN.mjs.map → renderer-oR1LvHeO.mjs.map} +1 -1
- package/dist/{renderer-CuwVuVKG.mjs → renderer-tcS585Iy.mjs} +3 -3
- package/dist/{renderer-CuwVuVKG.mjs.map → renderer-tcS585Iy.mjs.map} +1 -1
- package/dist/{renderer-Datqywo_.mjs → renderer-x1xlZaH_.mjs} +4 -4
- package/dist/{renderer-Datqywo_.mjs.map → renderer-x1xlZaH_.mjs.map} +1 -1
- package/dist/{renderers-CP8CFWDs-BbLkcJTN.mjs → renderers-CP8CFWDs-EZZPEbhW.mjs} +3 -3
- package/dist/{renderers-CP8CFWDs-BbLkcJTN.mjs.map → renderers-CP8CFWDs-EZZPEbhW.mjs.map} +1 -1
- package/dist/signals/index.d.mts +5 -5
- package/dist/signals/index.mjs +5 -5
- package/dist/{src-WAnxhDYD.mjs → src-r7Suenhp.mjs} +8 -8
- package/dist/{src-WAnxhDYD.mjs.map → src-r7Suenhp.mjs.map} +1 -1
- package/package.json +58 -58
- package/dist/dist-Bf0H4lp1.mjs.map +0 -1
- package/dist/dist-DG65RDhc.mjs.map +0 -1
- package/dist/index-CZK6CFLy.d.mts.map +0 -1
- package/dist/index-tCKz9Tku.d.mts.map +0 -1
- package/dist/preview-engine/chunk-I6E3AKLS.mjs +0 -1
- package/dist/preview-engine/renderers-2S5ZWL2J.mjs +0 -1
- package/dist/preview-engine/renderers-37CN7O4S.mjs +0 -1
- package/dist/preview-engine/renderers-3UGG2IMP.mjs +0 -1
- package/dist/preview-engine/renderers-7MPBAHYA.mjs +0 -1
- package/dist/preview-engine/renderers-AFTYMS3H.mjs +0 -1
- package/dist/preview-engine/renderers-AYEWAW7F.mjs +0 -1
- package/dist/preview-engine/renderers-BILNWTUC.mjs +0 -228
- package/dist/preview-engine/renderers-DUF2CTX6.mjs +0 -1
- package/dist/preview-engine/renderers-EIJFIY6L.mjs +0 -1
- package/dist/preview-engine/renderers-EOMMSENQ.mjs +0 -1
- package/dist/preview-engine/renderers-JXBZPQKP.mjs +0 -1
- package/dist/preview-engine/renderers-LD3COS4X.mjs +0 -1
- package/dist/preview-engine/renderers-MCTBJEL7.mjs +0 -1
- package/dist/preview-engine/renderers-OZGEXW2X.mjs +0 -1
- package/dist/preview-engine/renderers-PSEDMUBV.mjs +0 -1
- package/dist/preview-engine/renderers-PVUROEGR.mjs +0 -1
- package/dist/preview-engine/renderers-QBVVTQV6.mjs +0 -1
- package/dist/preview-engine/renderers-R4U2TK6P.mjs +0 -1
- package/dist/preview-engine/renderers-RBLNDAO4.mjs +0 -1
- package/dist/preview-engine/renderers-RM25AXWZ.mjs +0 -1
- package/dist/preview-engine/renderers-RYRFCEYT.mjs +0 -1
- package/dist/preview-engine/renderers-SKT7AGGE.mjs +0 -1
- package/dist/preview-engine/renderers-SRU6QXLN.mjs +0 -1
- package/dist/preview-engine/renderers-TBP2EKJ4.mjs +0 -1
- package/dist/preview-engine/renderers-UQHKFGDX.mjs +0 -1
- package/dist/preview-engine/renderers-WDV7ENJA.mjs +0 -1
- package/dist/preview-engine/renderers-WNBDSSJ3.mjs +0 -1
- package/dist/preview-engine/renderers-WTC2RNGU.mjs +0 -1
- package/dist/preview-engine/renderers-XVUOO7B6.mjs +0 -1
- package/dist/preview-engine/renderers-YHOQUEHD.mjs +0 -1
- package/dist/preview-engine/renderers-YPFM5UJO.mjs +0 -1
- package/dist/preview-engine/renderers-YW3LBALW.mjs +0 -1
- package/dist/renderer-1ymN1FIT.mjs +0 -5
- package/dist/renderer-92IAZzBU.d.mts.map +0 -1
- package/dist/renderer-B-43EuKb.mjs +0 -5
- package/dist/renderer-B-v5ZboU.mjs +0 -5
- package/dist/renderer-B9AZ6R1U.mjs.map +0 -1
- package/dist/renderer-BBA-iCi-.mjs.map +0 -1
- package/dist/renderer-BKF8Sfbg.mjs +0 -3
- package/dist/renderer-BTB8ifgH.mjs +0 -5
- package/dist/renderer-BYxzR6Hp.mjs +0 -5
- package/dist/renderer-BcGhO_Mw.mjs +0 -5
- package/dist/renderer-Bk3RgiFg.mjs +0 -5
- package/dist/renderer-BvItCxkw.mjs +0 -5
- package/dist/renderer-BvcqncI2.mjs +0 -3
- package/dist/renderer-CCnfPHqk.mjs +0 -5
- package/dist/renderer-CIoWQNaA.mjs +0 -5
- package/dist/renderer-CMwQtIIq.mjs +0 -5
- package/dist/renderer-CN8kRB90.mjs +0 -6
- package/dist/renderer-CPKBDif1.mjs +0 -5
- package/dist/renderer-CamsHxEP.mjs +0 -5
- package/dist/renderer-CeQormOH.mjs +0 -5
- package/dist/renderer-Cex4eMtv.mjs +0 -5
- package/dist/renderer-CjyTrNlr.mjs +0 -5
- package/dist/renderer-Clv9TO6C.mjs +0 -5
- package/dist/renderer-D4LnJk-M.mjs +0 -5
- package/dist/renderer-D5OfQ8Ut.mjs +0 -5
- package/dist/renderer-DBYssktC.mjs +0 -5
- package/dist/renderer-DC-Aj3me.mjs +0 -5
- package/dist/renderer-DG97_Aoc.mjs +0 -5
- package/dist/renderer-DS4UGjcS.mjs +0 -5
- package/dist/renderer-DdG6ibtM.mjs +0 -5
- package/dist/renderer-DhZi4f3t.mjs +0 -5
- package/dist/renderer-MgLekf6d.mjs +0 -5
- package/dist/renderer-Qvs-1lWh.mjs +0 -5
- package/dist/renderer-bN7dOErt.mjs +0 -5
- package/dist/renderer-jWKVmReU.mjs +0 -5
- package/dist/renderer-lk-At57G.mjs +0 -5
- package/dist/renderer-qi8Slwfo.mjs +0 -5
- package/dist/renderer-qxOH4JH1.mjs +0 -5
- package/dist/renderer-t30NpmIl.mjs +0 -5
- package/dist/renderer-wKW4HWRx.mjs +0 -5
- package/dist/renderer-ydjPm8bp.mjs +0 -5
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "card-stack",
|
|
3
3
|
"title": "CardStack",
|
|
4
|
-
"summary": "Cards in a depth stack that shuffle: the front card flies
|
|
4
|
+
"summary": "Cards in a depth stack that shuffle: the front card flies, drops, zooms or flips away and returns to the back; cards are dealt onto the pile; or the stack fans out into a hand.",
|
|
5
5
|
"tags": [
|
|
6
6
|
"cards",
|
|
7
7
|
"stack",
|
|
8
8
|
"deck",
|
|
9
9
|
"carousel",
|
|
10
|
+
"fan",
|
|
10
11
|
"testimonials",
|
|
11
12
|
"features",
|
|
12
13
|
"3d"
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
"files": [
|
|
25
26
|
{
|
|
26
27
|
"path": "card-stack/index.ts",
|
|
27
|
-
"content": "/**\n * CardStack: cards in a depth stack that shuffle. In `cycle` the front card\n * flies off and returns to the back while the rest step forward; in `deal`\n * cards land one at a time on top of the pile.\n *\n * Draw order is fixed by the tree (the renderer has no animated z-order), so\n * in `cycle` every card sits in a clip that shows only the strip peeking out\n * above the card in front of it: whichever card is drawn later, a card\n * behind can never paint over one in front. The leaving card flies sideways,\n * clear of the stack, before it slots in at the back.\n */\nimport {\n\ttype KeyframeTuple,\n\tLayerAnimation,\n\ttype LayoutNode,\n} from \"framefields\";\nimport {\n\ttype Animatable,\n\tbindComponent,\n\tdefineComponent,\n\tthemeFrames,\n\tz,\n} from \"framefields/components\";\n\nexport interface CardStackCues {\n\t[name: string]: ReadonlyArray<{ frame: number }>;\n\t/** A move starts; `index` is the card coming to the front. */\n\tchanges: ReadonlyArray<{ frame: number; index: number }>;\n}\n\nconst SIMPLE = z\n\t.object({\n\t\ttitle: z.string(),\n\t\tbody: z.string().optional(),\n\t\timage: z.string().optional(),\n\t\taccent: z.string().optional(),\n\t})\n\t.strict();\ntype SimpleCard = z.infer<typeof SIMPLE>;\n\n/** Slot geometry: card k behind the front is this much smaller and higher. */\nexport const SHRINK = 0.06;\n\nexport const cardStack = /* @__PURE__ */ defineComponent({\n\tname: \"CardStack\",\n\tprops: () => ({\n\t\tcards: z\n\t\t\t.array(\n\t\t\t\tz.union([\n\t\t\t\t\tSIMPLE,\n\t\t\t\t\tz.custom<LayoutNode>(\n\t\t\t\t\t\t(v) => typeof v === \"object\" && v !== null && \"kind\" in v,\n\t\t\t\t\t\t{ message: \"Expected a layer or { title, body?, image? }\" },\n\t\t\t\t\t),\n\t\t\t\t]),\n\t\t\t)\n\t\t\t.min(2),\n\t\tmode: z.enum([\"cycle\", \"deal\"]).default(\"cycle\"),\n\t\t/** Frames between moves. Default 1.5 seconds. */\n\t\tevery: z.number().int().positive().optional(),\n\t\tcycles: z.number().int().positive().default(1),\n\t\t/** Cards shown behind the front one. */\n\t\tvisible: z.number().int().min(0).max(8).default(3),\n\t\tflyTo: z.enum([\"up\", \"left\", \"right\"]).default(\"left\"),\n\t\tcardWidth: z.number().positive().optional(),\n\t\tcardHeight: z.number().positive().optional(),\n\t\ttilt: z\n\t\t\t.union([\n\t\t\t\tz.literal(false),\n\t\t\t\tz\n\t\t\t\t\t.object({\n\t\t\t\t\t\trotateX: z.number().default(12),\n\t\t\t\t\t\trotateY: z.number().default(0),\n\t\t\t\t\t})\n\t\t\t\t\t.strict(),\n\t\t\t])\n\t\t\t.default({ rotateX: 12, rotateY: 0 }),\n\t}),\n\tdefaults: { enter: false, exit: false, hold: 30 },\n\tbuild(ctx, props) {\n\t\tconst { host, theme, fps } = ctx;\n\t\tconst s = theme.scale;\n\t\tconst cw = props.cardWidth ?? 640 * s;\n\t\tconst ch = props.cardHeight ?? 400 * s;\n\t\tconst n = props.cards.length;\n\t\tconst every = props.every ?? Math.round(fps * 1.5);\n\t\tconst base = themeFrames(theme, \"base\", fps);\n\t\tconst tr = Math.min(every - 1, Math.round(base * 1.8));\n\t\tconst ease = theme.motion.ease.move;\n\t\tconst step = 26 * s;\n\t\tconst depth = Math.min(props.visible, n - 1);\n\t\tconst W = ctx.requested.width ?? cw * 1.5;\n\t\tconst H = ctx.requested.height ?? ch + step * depth + ch * 0.4;\n\t\tconst front = { x: (W - cw) / 2, y: H - ch - ch * 0.2 };\n\t\tconst pose = (k: number) => {\n\t\t\tconst shown = Math.min(k, depth);\n\t\t\treturn {\n\t\t\t\ty: front.y - shown * step,\n\t\t\t\tscale: 1 - SHRINK * shown,\n\t\t\t\topacity: k <= depth ? 1 : 0,\n\t\t\t\tdim: Math.min(0.6, 0.14 * shown),\n\t\t\t\t// Local height of the strip that peeks above the card in front.\n\t\t\t\tclip: k === 0 ? ch : step / (1 - SHRINK * shown),\n\t\t\t};\n\t\t};\n\n\t\t// ── Timeline: each card's slot at each move ──────────────────────\n\t\tconst cycle = props.mode === \"cycle\";\n\t\tconst moves = cycle ? n * props.cycles : n - 1;\n\t\tconst changes = Array.from({ length: moves }, (_, m) => ({\n\t\t\tframe: (m + 1) * every,\n\t\t\tindex: cycle ? (m + 1) % n : m + 1,\n\t\t}));\n\t\tconst fly =\n\t\t\tprops.flyTo === \"up\"\n\t\t\t\t? { x: 0, y: -ch * 1.3, rotation: -4 }\n\t\t\t\t: {\n\t\t\t\t\t\tx: (props.flyTo === \"left\" ? -1 : 1) * cw * 1.1,\n\t\t\t\t\t\ty: -ch * 0.15,\n\t\t\t\t\t\trotation: props.flyTo === \"left\" ? -10 : 10,\n\t\t\t\t\t};\n\n\t\tconst parts: Record<string, Animatable<LayoutNode>> = {};\n\t\tconst nodes: LayoutNode[] = [];\n\t\tprops.cards.forEach((card, c) => {\n\t\t\tconst tracks = {\n\t\t\t\tx: [] as KeyframeTuple[],\n\t\t\t\ty: [] as KeyframeTuple[],\n\t\t\t\tscale: [] as KeyframeTuple[],\n\t\t\t\trotation: [] as KeyframeTuple[],\n\t\t\t\topacity: [] as KeyframeTuple[],\n\t\t\t\tdim: [] as KeyframeTuple[],\n\t\t\t\tclip: [] as KeyframeTuple[],\n\t\t\t};\n\t\t\tconst put = (\n\t\t\t\tf: number,\n\t\t\t\tk: number,\n\t\t\t\te?: string,\n\t\t\t\talso?: Partial<Record<keyof typeof tracks, number>>,\n\t\t\t) => {\n\t\t\t\tconst p = pose(k);\n\t\t\t\tconst v = {\n\t\t\t\t\tx: front.x,\n\t\t\t\t\ty: p.y,\n\t\t\t\t\tscale: p.scale,\n\t\t\t\t\trotation: 0,\n\t\t\t\t\topacity: p.opacity,\n\t\t\t\t\tdim: p.dim,\n\t\t\t\t\tclip: p.clip,\n\t\t\t\t\t...also,\n\t\t\t\t};\n\t\t\t\tfor (const key of Object.keys(tracks) as Array<keyof typeof tracks>)\n\t\t\t\t\ttracks[key].push(e ? [f, round(v[key]), e] : [f, round(v[key])]);\n\t\t\t};\n\t\t\tif (cycle) {\n\t\t\t\tput(0, c);\n\t\t\t\tchanges.forEach(({ frame: t }, m) => {\n\t\t\t\t\tconst k = (((c - m) % n) + n) % n;\n\t\t\t\t\tif (k === 0) {\n\t\t\t\t\t\t// Leaves: flies clear, then slots in at the back unseen and fades up.\n\t\t\t\t\t\tconst away = t + Math.round(tr * 0.55);\n\t\t\t\t\t\tput(t, 0);\n\t\t\t\t\t\tput(away, 0, \"power2.in\", {\n\t\t\t\t\t\t\tx: front.x + fly.x,\n\t\t\t\t\t\t\ty: front.y + fly.y,\n\t\t\t\t\t\t\trotation: fly.rotation,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tput(away + 1, n - 1, \"hold\", { opacity: 0 });\n\t\t\t\t\t\tput(t + tr, n - 1, \"power2.out\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Steps forward once the leaving card is half way out.\n\t\t\t\t\t\tput(t + Math.round(tr * 0.3), k);\n\t\t\t\t\t\tput(t + tr, k - 1, ease);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Deal: card c lands on top at move c (card 0 is already down).\n\t\t\t\tconst landAt = c === 0 ? 0 : (changes[c - 1]?.frame as number);\n\t\t\t\tif (c > 0) {\n\t\t\t\t\tput(0, 0, undefined, {\n\t\t\t\t\t\ty: front.y - ch * 1.4,\n\t\t\t\t\t\trotation: 9,\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t});\n\t\t\t\t\tput(landAt, 0, \"hold\", {\n\t\t\t\t\t\ty: front.y - ch * 1.4,\n\t\t\t\t\t\trotation: 9,\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t});\n\t\t\t\t\tput(landAt + tr, 0, theme.motion.ease.enter);\n\t\t\t\t} else put(0, 0);\n\t\t\t\t// Pushed back one slot by every later card.\n\t\t\t\tchanges.slice(c).forEach(({ frame: t }, j) => {\n\t\t\t\t\tput(t, j);\n\t\t\t\t\tput(t + tr, j + 1, ease);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst content = isSimple(card) ? simpleCard(ctx, card, c, cw, ch) : card;\n\t\t\tconst dim = host.box({\n\t\t\t\tid: ctx.partId(`card-${c}-dim`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\twidth: cw,\n\t\t\t\theight: ch,\n\t\t\t\tbackground: \"#000000\",\n\t\t\t\tborderRadius: theme.radius.lg,\n\t\t\t\topacity: 0,\n\t\t\t});\n\t\t\tdim.animate(LayerAnimation.keys(\"opacity\", ascending(tracks.dim)));\n\t\t\tconst clip = host.box({\n\t\t\t\tid: ctx.partId(`card-${c}-clip`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\twidth: cw,\n\t\t\t\theight: ch,\n\t\t\t\toverflow: \"hidden\",\n\t\t\t\tborderRadius: theme.radius.lg,\n\t\t\t\tchildren: [content, dim],\n\t\t\t});\n\t\t\tif (cycle)\n\t\t\t\tclip.animate(LayerAnimation.keys(\"height\", ascending(tracks.clip)));\n\t\t\tconst node = host.box({\n\t\t\t\tid: ctx.partId(`card-${c}`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: front.x,\n\t\t\t\ty: pose(c).y,\n\t\t\t\twidth: cw,\n\t\t\t\theight: ch,\n\t\t\t\tanchorX: 0.5,\n\t\t\t\tanchorY: 0,\n\t\t\t\tchildren: [clip],\n\t\t\t});\n\t\t\tconst anim = LayerAnimation.create()\n\t\t\t\t.keys(\"y\", ascending(tracks.y))\n\t\t\t\t.keys(\"scale\", ascending(tracks.scale))\n\t\t\t\t.keys(\"opacity\", ascending(tracks.opacity));\n\t\t\tif (tracks.x.some((k) => k[1] !== front.x))\n\t\t\t\tanim.keys(\"x\", ascending(tracks.x));\n\t\t\tif (tracks.rotation.some((k) => k[1] !== 0))\n\t\t\t\tanim.keys(\"rotation\", ascending(tracks.rotation));\n\t\t\tnode.animate(anim);\n\t\t\thost.style(node, {\n\t\t\t\tdropShadow: {\n\t\t\t\t\tcolor: \"#000000\",\n\t\t\t\t\topacity: 0.35,\n\t\t\t\t\tangle: 90,\n\t\t\t\t\tdistance: 12 * s,\n\t\t\t\t\tsize: 34 * s,\n\t\t\t\t},\n\t\t\t});\n\t\t\tparts[`card-${c}`] = node;\n\t\t\tparts[`card-${c}-clip`] = clip;\n\t\t\tnodes.push(node);\n\t\t});\n\t\t// Front-most last: cycle starts with card 0 in front, deal ends with the last on top.\n\t\tconst ordered = cycle ? [...nodes].reverse() : nodes;\n\t\tconst children = props.tilt\n\t\t\t? [\n\t\t\t\t\thost.box({\n\t\t\t\t\t\tid: ctx.partId(\"tilt\"),\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\twidth: W,\n\t\t\t\t\t\theight: H,\n\t\t\t\t\t\tperspective: 1600 * s,\n\t\t\t\t\t\trotateX: props.tilt.rotateX,\n\t\t\t\t\t\trotateY: props.tilt.rotateY,\n\t\t\t\t\t\tchildren: ordered,\n\t\t\t\t\t}),\n\t\t\t\t]\n\t\t\t: ordered;\n\t\tif (props.tilt) parts.tilt = children[0] as Animatable<LayoutNode>;\n\n\t\treturn {\n\t\t\tchildren,\n\t\t\twidth: W,\n\t\t\theight: H,\n\t\t\tduration: (changes.at(-1)?.frame ?? 0) + tr,\n\t\t\topenEnded: true,\n\t\t\tcues: { changes } as CardStackCues,\n\t\t\tparts,\n\t\t};\n\t},\n});\n\nconst round = (v: number) => Math.round(v * 1000) / 1000;\n\nconst isSimple = (card: unknown): card is SimpleCard =>\n\ttypeof card === \"object\" &&\n\tcard !== null &&\n\t\"title\" in card &&\n\t!(\"kind\" in card);\n\n/** Later keys on a shared frame win; frames ascend. */\nfunction ascending(keys: KeyframeTuple[]): KeyframeTuple[] {\n\tconst byFrame = new Map<number, KeyframeTuple>();\n\tfor (const k of keys) byFrame.set(k[0] as number, k);\n\treturn [...byFrame.values()].sort(\n\t\t(a, b) => (a[0] as number) - (b[0] as number),\n\t);\n}\n\n/** A card drawn from the theme: optional image on top, title, body, accent bar. */\nfunction simpleCard(\n\tctx: Parameters<(typeof cardStack)[\"build\"]>[0],\n\tcard: SimpleCard,\n\tc: number,\n\tw: number,\n\th: number,\n): LayoutNode {\n\tconst { host, theme } = ctx;\n\tconst s = theme.scale;\n\tconst pad = 36 * s;\n\tconst imageH = card.image ? h * 0.48 : 0;\n\tconst kids: LayoutNode[] = [];\n\tif (card.image)\n\t\tkids.push(\n\t\t\thost.media(card.image, {\n\t\t\t\tid: ctx.partId(`card-${c}-image`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\twidth: w,\n\t\t\t\theight: imageH,\n\t\t\t\tfit: \"cover\",\n\t\t\t}),\n\t\t);\n\tkids.push(\n\t\thost.box({\n\t\t\tid: ctx.partId(`card-${c}-accent`),\n\t\t\tposition: \"absolute\",\n\t\t\tx: pad,\n\t\t\ty: imageH + pad,\n\t\t\twidth: 44 * s,\n\t\t\theight: 5 * s,\n\t\t\tbackground: card.accent ?? theme.color.accent,\n\t\t\tborderRadius: 3 * s,\n\t\t}),\n\t\thost.text(card.title, {\n\t\t\tid: ctx.partId(`card-${c}-title`),\n\t\t\tposition: \"absolute\",\n\t\t\tx: pad,\n\t\t\ty: imageH + pad + 22 * s,\n\t\t\twidth: w - pad * 2,\n\t\t\tfontFamily: theme.font.display,\n\t\t\tfontSize: 40 * s,\n\t\t\tfontWeight: 700,\n\t\t\tfill: theme.color.ink,\n\t\t}),\n\t);\n\tif (card.body)\n\t\tkids.push(\n\t\t\thost.text(card.body, {\n\t\t\t\tid: ctx.partId(`card-${c}-body`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: pad,\n\t\t\t\ty: imageH + pad + 80 * s,\n\t\t\t\twidth: w - pad * 2,\n\t\t\t\tfontFamily: theme.font.body,\n\t\t\t\tfontSize: 24 * s,\n\t\t\t\tfill: theme.color.muted,\n\t\t\t}),\n\t\t);\n\treturn host.box({\n\t\tid: ctx.partId(`card-${c}-face`),\n\t\tposition: \"absolute\",\n\t\tx: 0,\n\t\ty: 0,\n\t\twidth: w,\n\t\theight: h,\n\t\tbackground: theme.color.raised,\n\t\tborderRadius: theme.radius.lg,\n\t\tborderColor: theme.color.line,\n\t\tborderWidth: Math.max(1, s),\n\t\tstrokeAlign: \"inside\",\n\t\tchildren: kids,\n\t});\n}\n\n/**\n * Cards in a depth stack that shuffle: the front card flies off and returns to\n * the back, or cards are dealt onto the pile.\n */\nexport const CardStack = /* @__PURE__ */ bindComponent(cardStack);\n"
|
|
28
|
+
"content": "/**\n * CardStack: cards in a depth stack that shuffle. In `cycle` the front card\n * leaves (flies, drops, zooms or flips away) and returns to the back while\n * the rest step forward; in `deal` cards land one at a time on top of the\n * pile; in `fan` the stack spreads into a hand and gathers again.\n *\n * Draw order is fixed by the tree (the renderer has no animated z-order), so\n * in `cycle` every card sits in a clip that shows only the strip peeking out\n * above the card in front of it: whichever card is drawn later, a card\n * behind can never paint over one in front. The leaving card flies sideways,\n * clear of the stack, before it slots in at the back. The card coming to the\n * front shows whole at once: everything drawn after it is either the card\n * leaving (in front of it anyway) or a card already clipped at the back.\n */\nimport {\n\ttype KeyframeTuple,\n\tLayerAnimation,\n\ttype LayoutNode,\n} from \"framefields\";\nimport {\n\ttype Animatable,\n\tbindComponent,\n\tdefineComponent,\n\tthemeFrames,\n\tz,\n} from \"framefields/components\";\n\nexport interface CardStackCues {\n\t[name: string]: ReadonlyArray<{ frame: number }>;\n\t/** A move starts; `index` is the card coming to the front. */\n\tchanges: ReadonlyArray<{ frame: number; index: number }>;\n}\n\nconst SIMPLE = z\n\t.object({\n\t\ttitle: z.string(),\n\t\tbody: z.string().optional(),\n\t\timage: z.string().optional(),\n\t\taccent: z.string().optional(),\n\t})\n\t.strict();\ntype SimpleCard = z.infer<typeof SIMPLE>;\n\n/** Slot geometry: card k behind the front is this much smaller and higher. */\nexport const SHRINK = 0.06;\n\nexport const cardStack = /* @__PURE__ */ defineComponent({\n\tname: \"CardStack\",\n\tprops: () => ({\n\t\tcards: z\n\t\t\t.array(\n\t\t\t\tz.union([\n\t\t\t\t\tSIMPLE,\n\t\t\t\t\tz.custom<LayoutNode>(\n\t\t\t\t\t\t(v) => typeof v === \"object\" && v !== null && \"kind\" in v,\n\t\t\t\t\t\t{ message: \"Expected a layer or { title, body?, image? }\" },\n\t\t\t\t\t),\n\t\t\t\t]),\n\t\t\t)\n\t\t\t.min(2),\n\t\tmode: z.enum([\"cycle\", \"deal\", \"fan\"]).default(\"cycle\"),\n\t\t/** Frames between moves. Default 1.5 seconds. */\n\t\tevery: z.number().int().positive().optional(),\n\t\tcycles: z.number().int().positive().default(1),\n\t\t/** Cards shown behind the front one. */\n\t\tvisible: z.number().int().min(0).max(8).default(3),\n\t\t/** How the front card leaves in `cycle`. */\n\t\tleave: z.enum([\"fly\", \"drop\", \"zoom\", \"flip\"]).default(\"fly\"),\n\t\t/** Where `fly` sends the card, and which way `drop` tips it. */\n\t\tflyTo: z.enum([\"up\", \"left\", \"right\"]).default(\"left\"),\n\t\t/** Degrees between neighbouring cards in `fan`. */\n\t\tfanAngle: z.number().min(0).max(30).default(8),\n\t\tcardWidth: z.number().positive().optional(),\n\t\tcardHeight: z.number().positive().optional(),\n\t\ttilt: z\n\t\t\t.union([\n\t\t\t\tz.literal(false),\n\t\t\t\tz\n\t\t\t\t\t.object({\n\t\t\t\t\t\trotateX: z.number().default(12),\n\t\t\t\t\t\trotateY: z.number().default(0),\n\t\t\t\t\t})\n\t\t\t\t\t.strict(),\n\t\t\t])\n\t\t\t.default({ rotateX: 12, rotateY: 0 }),\n\t}),\n\tdefaults: { enter: false, exit: false, hold: 30 },\n\tbuild(ctx, props) {\n\t\tconst { host, theme, fps } = ctx;\n\t\tconst s = theme.scale;\n\t\tconst cw = props.cardWidth ?? 640 * s;\n\t\tconst ch = props.cardHeight ?? 400 * s;\n\t\tconst n = props.cards.length;\n\t\tconst every = props.every ?? Math.round(fps * 1.5);\n\t\tconst base = themeFrames(theme, \"base\", fps);\n\t\tconst tr = Math.min(every - 1, Math.round(base * 1.8));\n\t\tconst ease = theme.motion.ease.move;\n\t\tconst step = 26 * s;\n\t\tconst depth = Math.min(props.visible, n - 1);\n\t\tconst fan = props.mode === \"fan\";\n\t\t// In a fan, cards shrink a little and overlap by this much of their width.\n\t\tconst fanScale = 0.82;\n\t\tconst fanStep = cw * fanScale * 0.42;\n\t\tconst W =\n\t\t\tctx.requested.width ??\n\t\t\t(fan\n\t\t\t\t? Math.max(cw * 1.5, cw * fanScale + fanStep * (n - 1) + cw * 0.3)\n\t\t\t\t: cw * 1.5);\n\t\tconst H = ctx.requested.height ?? ch + step * depth + ch * 0.4;\n\t\tconst front = { x: (W - cw) / 2, y: H - ch - ch * 0.2 };\n\t\tconst pose = (k: number) => {\n\t\t\tconst shown = Math.min(k, depth);\n\t\t\treturn {\n\t\t\t\ty: front.y - shown * step,\n\t\t\t\tscale: 1 - SHRINK * shown,\n\t\t\t\topacity: k <= depth ? 1 : 0,\n\t\t\t\tdim: Math.min(0.6, 0.14 * shown),\n\t\t\t\t// Local height of the strip that peeks above the card in front.\n\t\t\t\tclip: k === 0 ? ch : step / (1 - SHRINK * shown),\n\t\t\t};\n\t\t};\n\n\t\t// ── Timeline: each card's slot at each move ──────────────────────\n\t\tconst cycle = props.mode === \"cycle\";\n\t\tconst deal = props.mode === \"deal\";\n\t\t// A fan spreads and gathers once per cycle.\n\t\tconst moves = cycle ? n * props.cycles : fan ? 2 * props.cycles : n - 1;\n\t\tconst changes = Array.from({ length: moves }, (_, m) => ({\n\t\t\tframe: (m + 1) * every,\n\t\t\tindex: cycle ? (m + 1) % n : fan ? 0 : m + 1,\n\t\t}));\n\t\tconst side = props.flyTo === \"right\" ? 1 : -1;\n\t\t// Where the leaving card ends (relative to the front slot), how, and\n\t\t// how much of the move it takes.\n\t\tconst leave: {\n\t\t\tto: Partial<Record<\"x\" | \"y\" | \"rotation\" | \"rotateX\" | \"scale\", number>>;\n\t\t\tease: string;\n\t\t\tshare: number;\n\t\t\t/** Share of its exit it stays opaque, so the next card doesn't ghost through. */\n\t\t\tsolid: number;\n\t\t} = {\n\t\t\tfly: {\n\t\t\t\tto:\n\t\t\t\t\tprops.flyTo === \"up\"\n\t\t\t\t\t\t? { y: -ch * 1.3, rotation: -4 }\n\t\t\t\t\t\t: { x: side * cw * 1.1, y: -ch * 0.15, rotation: side * 10 },\n\t\t\t\tease: \"power2.in\",\n\t\t\t\tshare: 0.55,\n\t\t\t\tsolid: 0.5,\n\t\t\t},\n\t\t\t// Falls out of the bottom, tipping toward `flyTo`.\n\t\t\tdrop: {\n\t\t\t\tto: { x: side * cw * 0.12, y: ch * 1.1, rotation: side * 14 },\n\t\t\t\tease: \"power2.in\",\n\t\t\t\tshare: 0.6,\n\t\t\t\tsolid: 0.55,\n\t\t\t},\n\t\t\t// Toward the viewer, fading as it passes.\n\t\t\tzoom: {\n\t\t\t\tto: { y: ch * 0.06, scale: 1.22 },\n\t\t\t\tease: \"power2.out\",\n\t\t\t\tshare: 0.45,\n\t\t\t\tsolid: 0,\n\t\t\t},\n\t\t\t// Folds back over its top edge.\n\t\t\tflip: {\n\t\t\t\tto: { y: -ch * 0.04, rotateX: 90, scale: 0.96 },\n\t\t\t\tease: \"sine.in\",\n\t\t\t\tshare: 0.55,\n\t\t\t\tsolid: 0.9,\n\t\t\t},\n\t\t}[props.leave];\n\t\t// The fan: an arc, centred, outer cards turned and lowered.\n\t\tconst fanPose = (c: number) => {\n\t\t\tconst off = c - (n - 1) / 2;\n\t\t\treturn {\n\t\t\t\tx: front.x + off * fanStep,\n\t\t\t\ty: front.y + ch * 0.06 + Math.abs(off) ** 2 * ch * 0.02,\n\t\t\t\trotation: off * props.fanAngle,\n\t\t\t\tscale: fanScale,\n\t\t\t\tdim: 0,\n\t\t\t\topacity: 1,\n\t\t\t};\n\t\t};\n\n\t\tconst parts: Record<string, Animatable<LayoutNode>> = {};\n\t\tconst nodes: LayoutNode[] = [];\n\t\tprops.cards.forEach((card, c) => {\n\t\t\tconst tracks = {\n\t\t\t\tx: [] as KeyframeTuple[],\n\t\t\t\ty: [] as KeyframeTuple[],\n\t\t\t\tscale: [] as KeyframeTuple[],\n\t\t\t\trotation: [] as KeyframeTuple[],\n\t\t\t\trotateX: [] as KeyframeTuple[],\n\t\t\t\topacity: [] as KeyframeTuple[],\n\t\t\t\tdim: [] as KeyframeTuple[],\n\t\t\t\tclip: [] as KeyframeTuple[],\n\t\t\t};\n\t\t\tconst put = (\n\t\t\t\tf: number,\n\t\t\t\tk: number,\n\t\t\t\te?: string,\n\t\t\t\talso?: Partial<Record<keyof typeof tracks, number>>,\n\t\t\t) => {\n\t\t\t\tconst p = pose(k);\n\t\t\t\tconst v = {\n\t\t\t\t\tx: front.x,\n\t\t\t\t\ty: p.y,\n\t\t\t\t\tscale: p.scale,\n\t\t\t\t\trotation: 0,\n\t\t\t\t\trotateX: 0,\n\t\t\t\t\topacity: p.opacity,\n\t\t\t\t\tdim: p.dim,\n\t\t\t\t\tclip: p.clip,\n\t\t\t\t\t...also,\n\t\t\t\t};\n\t\t\t\tfor (const key of Object.keys(tracks) as Array<keyof typeof tracks>)\n\t\t\t\t\ttracks[key].push(e ? [f, round(v[key]), e] : [f, round(v[key])]);\n\t\t\t};\n\t\t\tif (cycle) {\n\t\t\t\tput(0, c);\n\t\t\t\tchanges.forEach(({ frame: t }, m) => {\n\t\t\t\t\tconst k = (((c - m) % n) + n) % n;\n\t\t\t\t\tif (k === 0) {\n\t\t\t\t\t\t// Leaves, then slots in at the back unseen and fades up.\n\t\t\t\t\t\tconst away = t + Math.round(tr * leave.share);\n\t\t\t\t\t\tconst to = leave.to;\n\t\t\t\t\t\tput(t, 0);\n\t\t\t\t\t\tconst solid = t + Math.round((away - t) * leave.solid);\n\t\t\t\t\t\tif (solid > t) tracks.opacity.push([solid, 1]);\n\t\t\t\t\t\tput(away, 0, leave.ease, {\n\t\t\t\t\t\t\tx: front.x + (to.x ?? 0),\n\t\t\t\t\t\t\ty: front.y + (to.y ?? 0),\n\t\t\t\t\t\t\trotation: to.rotation ?? 0,\n\t\t\t\t\t\t\trotateX: to.rotateX ?? 0,\n\t\t\t\t\t\t\tscale: to.scale ?? 1,\n\t\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tput(away + 1, n - 1, \"hold\", { opacity: 0 });\n\t\t\t\t\t\tput(t + tr, n - 1, \"power2.out\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Steps forward once the leaving card is under way. Coming to\n\t\t\t\t\t\t// the front, it shows whole from the start.\n\t\t\t\t\t\tconst whole = k === 1 ? { clip: ch } : undefined;\n\t\t\t\t\t\tput(t, k);\n\t\t\t\t\t\tif (whole) tracks.clip.push([t + 1, ch, \"hold\"]);\n\t\t\t\t\t\tput(t + Math.round(tr * 0.25), k, undefined, whole);\n\t\t\t\t\t\tput(t + tr, k - 1, ease);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else if (fan) {\n\t\t\t\tput(0, c);\n\t\t\t\tchanges.forEach(({ frame: t }, m) => {\n\t\t\t\t\t// Spreads on even moves, gathers on odd ones, the front card first.\n\t\t\t\t\tconst at = t + c * Math.round(fps * 0.04);\n\t\t\t\t\tconst spread = m % 2 === 0;\n\t\t\t\t\tput(at, c, undefined, spread ? undefined : fanPose(c));\n\t\t\t\t\tif (spread) put(at + tr, c, theme.motion.ease.snap, fanPose(c));\n\t\t\t\t\telse put(at + tr, c, ease);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Deal: card c lands on top at move c (card 0 is already down).\n\t\t\t\tconst landAt = c === 0 ? 0 : (changes[c - 1]?.frame as number);\n\t\t\t\tif (c > 0) {\n\t\t\t\t\tput(0, 0, undefined, {\n\t\t\t\t\t\ty: front.y - ch * 1.4,\n\t\t\t\t\t\trotation: 9,\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t});\n\t\t\t\t\tput(landAt, 0, \"hold\", {\n\t\t\t\t\t\ty: front.y - ch * 1.4,\n\t\t\t\t\t\trotation: 9,\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t});\n\t\t\t\t\tput(landAt + tr, 0, theme.motion.ease.enter);\n\t\t\t\t} else put(0, 0);\n\t\t\t\t// Pushed back one slot by every later card.\n\t\t\t\tchanges.slice(c).forEach(({ frame: t }, j) => {\n\t\t\t\t\tput(t, j);\n\t\t\t\t\tput(t + tr, j + 1, ease);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst content = isSimple(card) ? simpleCard(ctx, card, c, cw, ch) : card;\n\t\t\tconst dim = host.box({\n\t\t\t\tid: ctx.partId(`card-${c}-dim`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\twidth: cw,\n\t\t\t\theight: ch,\n\t\t\t\tbackground: \"#000000\",\n\t\t\t\tborderRadius: theme.radius.lg,\n\t\t\t\topacity: 0,\n\t\t\t});\n\t\t\tdim.animate(LayerAnimation.keys(\"opacity\", ascending(tracks.dim)));\n\t\t\tconst clip = host.box({\n\t\t\t\tid: ctx.partId(`card-${c}-clip`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\twidth: cw,\n\t\t\t\theight: ch,\n\t\t\t\toverflow: \"hidden\",\n\t\t\t\tborderRadius: theme.radius.lg,\n\t\t\t\tchildren: [content, dim],\n\t\t\t});\n\t\t\tif (cycle)\n\t\t\t\tclip.animate(LayerAnimation.keys(\"height\", ascending(tracks.clip)));\n\t\t\tconst node = host.box({\n\t\t\t\tid: ctx.partId(`card-${c}`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: front.x,\n\t\t\t\ty: pose(c).y,\n\t\t\t\twidth: cw,\n\t\t\t\theight: ch,\n\t\t\t\tanchorX: 0.5,\n\t\t\t\tanchorY: 0,\n\t\t\t\tchildren: [clip],\n\t\t\t});\n\t\t\tconst anim = LayerAnimation.create()\n\t\t\t\t.keys(\"y\", ascending(tracks.y))\n\t\t\t\t.keys(\"scale\", ascending(tracks.scale))\n\t\t\t\t.keys(\"opacity\", ascending(tracks.opacity));\n\t\t\tif (tracks.x.some((k) => k[1] !== front.x))\n\t\t\t\tanim.keys(\"x\", ascending(tracks.x));\n\t\t\tif (tracks.rotation.some((k) => k[1] !== 0))\n\t\t\t\tanim.keys(\"rotation\", ascending(tracks.rotation));\n\t\t\tif (tracks.rotateX.some((k) => k[1] !== 0))\n\t\t\t\tanim.keys(\"rotateX\", ascending(tracks.rotateX));\n\t\t\tnode.animate(anim);\n\t\t\thost.style(node, {\n\t\t\t\tdropShadow: {\n\t\t\t\t\tcolor: \"#000000\",\n\t\t\t\t\topacity: 0.35,\n\t\t\t\t\tangle: 90,\n\t\t\t\t\tdistance: 12 * s,\n\t\t\t\t\tsize: 34 * s,\n\t\t\t\t},\n\t\t\t});\n\t\t\tparts[`card-${c}`] = node;\n\t\t\tparts[`card-${c}-clip`] = clip;\n\t\t\tnodes.push(node);\n\t\t});\n\t\t// Front-most last: cycle and fan start with card 0 in front, deal ends\n\t\t// with the last on top.\n\t\tconst ordered = deal ? nodes : [...nodes].reverse();\n\t\tconst children = props.tilt\n\t\t\t? [\n\t\t\t\t\thost.box({\n\t\t\t\t\t\tid: ctx.partId(\"tilt\"),\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\twidth: W,\n\t\t\t\t\t\theight: H,\n\t\t\t\t\t\tperspective: 1600 * s,\n\t\t\t\t\t\trotateX: props.tilt.rotateX,\n\t\t\t\t\t\trotateY: props.tilt.rotateY,\n\t\t\t\t\t\tchildren: ordered,\n\t\t\t\t\t}),\n\t\t\t\t]\n\t\t\t: ordered;\n\t\tif (props.tilt) parts.tilt = children[0] as Animatable<LayoutNode>;\n\n\t\treturn {\n\t\t\tchildren,\n\t\t\twidth: W,\n\t\t\theight: H,\n\t\t\tduration: (changes.at(-1)?.frame ?? 0) + tr,\n\t\t\topenEnded: true,\n\t\t\tcues: { changes } as CardStackCues,\n\t\t\tparts,\n\t\t};\n\t},\n});\n\nconst round = (v: number) => Math.round(v * 1000) / 1000;\n\nconst isSimple = (card: unknown): card is SimpleCard =>\n\ttypeof card === \"object\" &&\n\tcard !== null &&\n\t\"title\" in card &&\n\t!(\"kind\" in card);\n\n/** Later keys on a shared frame win; frames ascend. */\nfunction ascending(keys: KeyframeTuple[]): KeyframeTuple[] {\n\tconst byFrame = new Map<number, KeyframeTuple>();\n\tfor (const k of keys) byFrame.set(k[0] as number, k);\n\treturn [...byFrame.values()].sort(\n\t\t(a, b) => (a[0] as number) - (b[0] as number),\n\t);\n}\n\n/** A card drawn from the theme: optional image on top, title, body, accent bar. */\nfunction simpleCard(\n\tctx: Parameters<(typeof cardStack)[\"build\"]>[0],\n\tcard: SimpleCard,\n\tc: number,\n\tw: number,\n\th: number,\n): LayoutNode {\n\tconst { host, theme } = ctx;\n\tconst s = theme.scale;\n\tconst pad = 36 * s;\n\tconst imageH = card.image ? h * 0.48 : 0;\n\tconst kids: LayoutNode[] = [];\n\tif (card.image)\n\t\tkids.push(\n\t\t\thost.media(card.image, {\n\t\t\t\tid: ctx.partId(`card-${c}-image`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\twidth: w,\n\t\t\t\theight: imageH,\n\t\t\t\tfit: \"cover\",\n\t\t\t}),\n\t\t);\n\tkids.push(\n\t\thost.box({\n\t\t\tid: ctx.partId(`card-${c}-accent`),\n\t\t\tposition: \"absolute\",\n\t\t\tx: pad,\n\t\t\ty: imageH + pad,\n\t\t\twidth: 44 * s,\n\t\t\theight: 5 * s,\n\t\t\tbackground: card.accent ?? theme.color.accent,\n\t\t\tborderRadius: 3 * s,\n\t\t}),\n\t\thost.text(card.title, {\n\t\t\tid: ctx.partId(`card-${c}-title`),\n\t\t\tposition: \"absolute\",\n\t\t\tx: pad,\n\t\t\ty: imageH + pad + 22 * s,\n\t\t\twidth: w - pad * 2,\n\t\t\tfontFamily: theme.font.display,\n\t\t\tfontSize: 40 * s,\n\t\t\tfontWeight: 700,\n\t\t\tfill: theme.color.ink,\n\t\t}),\n\t);\n\tif (card.body)\n\t\tkids.push(\n\t\t\thost.text(card.body, {\n\t\t\t\tid: ctx.partId(`card-${c}-body`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: pad,\n\t\t\t\ty: imageH + pad + 80 * s,\n\t\t\t\twidth: w - pad * 2,\n\t\t\t\tfontFamily: theme.font.body,\n\t\t\t\tfontSize: 24 * s,\n\t\t\t\tfill: theme.color.muted,\n\t\t\t}),\n\t\t);\n\treturn host.box({\n\t\tid: ctx.partId(`card-${c}-face`),\n\t\tposition: \"absolute\",\n\t\tx: 0,\n\t\ty: 0,\n\t\twidth: w,\n\t\theight: h,\n\t\tbackground: theme.color.raised,\n\t\tborderRadius: theme.radius.lg,\n\t\tborderColor: theme.color.line,\n\t\tborderWidth: Math.max(1, s),\n\t\tstrokeAlign: \"inside\",\n\t\tchildren: kids,\n\t});\n}\n\n/**\n * Cards in a depth stack that shuffle: the front card flies, drops, zooms or\n * flips away and returns to the back; cards are dealt onto the pile; or the\n * stack fans out into a hand.\n */\nexport const CardStack = /* @__PURE__ */ bindComponent(cardStack);\n"
|
|
28
29
|
}
|
|
29
30
|
]
|
|
30
31
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat-thread",
|
|
3
3
|
"title": "ChatThread",
|
|
4
|
-
"summary": "A messaging conversation: bubbles that spring in from either side, typing dots before replies, names, timestamps, reactions, images and auto-scroll.",
|
|
4
|
+
"summary": "A messaging conversation: bubbles that spring in from either side, typing dots before replies, names, avatars, timestamps, reactions, images and auto-scroll.",
|
|
5
5
|
"tags": [
|
|
6
6
|
"chat",
|
|
7
7
|
"messages",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"column",
|
|
17
17
|
"bubble-<n>",
|
|
18
18
|
"typing-<n>",
|
|
19
|
-
"reaction-<n>"
|
|
19
|
+
"reaction-<n>",
|
|
20
|
+
"avatar-<n>"
|
|
20
21
|
],
|
|
21
22
|
"cues": [
|
|
22
23
|
"messages",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"files": [
|
|
27
28
|
{
|
|
28
29
|
"path": "chat-thread/index.ts",
|
|
29
|
-
"content": "/**\n * ChatThread: a messaging conversation. Bubbles arrive from either side\n * with a spring, the other side types first (three bouncing dots), and the\n * thread scrolls to keep the newest message in view. Names, timestamps,\n * reactions and images included.\n */\nimport {\n\ttype KeyframeTuple,\n\tLayerAnimation,\n\ttype LayoutNode,\n} from \"framefields\";\nimport {\n\ttype Animatable,\n\tbindComponent,\n\tdefineComponent,\n\trangeRects,\n\tspringKeys,\n\tz,\n} from \"framefields/components\";\n\nexport interface ChatThreadCues {\n\t[name: string]: ReadonlyArray<{ frame: number }>;\n\t/** Each message as it lands. */\n\tmessages: ReadonlyArray<{ frame: number; index: number; from: string }>;\n\t/** Each typing indicator, shown to removed. */\n\ttyping: ReadonlyArray<{ frame: number; end: number; from: string }>;\n}\n\nfunction messageSchema() {\n\treturn z\n\t\t.object({\n\t\t\tfrom: z.string(),\n\t\t\ttext: z.string().optional(),\n\t\t\t/** An image (path or URL) instead of, or above, text. */\n\t\t\timage: z.string().optional(),\n\t\t\t/** Image width / height. */\n\t\t\taspect: z\n\t\t\t\t.number()\n\t\t\t\t.positive()\n\t\t\t\t.default(4 / 3),\n\t\t\t/** Frame it lands. Default: after the previous one has been read. */\n\t\t\tat: z.number().int().min(0).optional(),\n\t\t\t/** A reaction pinned to the bubble (an emoji or short text). */\n\t\t\treaction: z.string().optional(),\n\t\t\t/** A small timestamp under the bubble. */\n\t\t\ttime: z.string().optional(),\n\t\t})\n\t\t.strict()\n\t\t.refine((m) => m.text !== undefined || m.image !== undefined, {\n\t\t\tmessage: \"A message needs `text` or `image`.\",\n\t\t});\n}\n\nconst SPRING = { stiffness: 260, damping: 20 };\n\nexport const chatThread = /* @__PURE__ */ defineComponent({\n\tname: \"ChatThread\",\n\tprops: () => ({\n\t\tmessages: z.array(messageSchema()).min(1),\n\t\t/** Who is \"me\" (right side, accent bubbles). Default the first sender. */\n\t\tme: z.string().optional(),\n\t\t/** Display names and bubble colours per sender. */\n\t\tparticipants: z\n\t\t\t.record(\n\t\t\t\tz.string(),\n\t\t\t\tz\n\t\t\t\t\t.object({ name: z.string().optional(), color: z.string().optional() })\n\t\t\t\t\t.strict(),\n\t\t\t)\n\t\t\t.default({}),\n\t\t/** Names above the first bubble of each run (default: when more than two people talk). */\n\t\tnames: z.boolean().optional(),\n\t\t/** Others type before they send. */\n\t\ttyping: z.boolean().default(true),\n\t\t/** Reading speed between messages, characters a second. */\n\t\treadCps: z.number().positive().default(28),\n\t\tfontFamily: z.string().optional(),\n\t\tfontSize: z.number().positive().optional(),\n\t}),\n\tdefaults: { enter: false, exit: false },\n\tbuild(ctx, props) {\n\t\tconst { host, theme, fps } = ctx;\n\t\tconst s = theme.scale;\n\t\tconst W = ctx.requested.width ?? 720 * s;\n\t\tconst H = ctx.requested.height ?? 960 * s;\n\t\tconst fs = props.fontSize ?? 30 * s;\n\t\tconst fontFamily = props.fontFamily ?? theme.font.body;\n\t\tconst me = props.me ?? props.messages[0]?.from ?? \"me\";\n\t\tconst senders = [...new Set(props.messages.map((m) => m.from))];\n\t\tconst showNames = props.names ?? senders.length > 2;\n\t\tconst pad = fs * 0.8;\n\t\tconst padX = fs * 0.62;\n\t\tconst padY = fs * 0.42;\n\t\tconst maxW = W * 0.74;\n\t\tconst radius = fs * 0.9;\n\t\tconst smallFs = fs * 0.62;\n\n\t\t// ── Timing ────────────────────────────────────────────────────────\n\t\tconst landed: number[] = [];\n\t\tconst typingWindows: Array<{\n\t\t\tframe: number;\n\t\t\tend: number;\n\t\t\tfrom: string;\n\t\t\tindex: number;\n\t\t}> = [];\n\t\tlet t = Math.round(fps * 0.2);\n\t\tprops.messages.forEach((m, i) => {\n\t\t\tconst prev = props.messages[i - 1];\n\t\t\tif (prev && m.at === undefined) {\n\t\t\t\tconst read = ((prev.text?.length ?? 12) / props.readCps) * fps;\n\t\t\t\tt += Math.round(Math.min(fps * 2.5, Math.max(fps * 0.5, read)));\n\t\t\t}\n\t\t\tlet at = m.at ?? t;\n\t\t\tif (props.typing && m.from !== me && m.at === undefined) {\n\t\t\t\tconst typingFor = Math.round(\n\t\t\t\t\tMath.min(2.2, Math.max(0.7, (m.text?.length ?? 10) / 14)) * fps,\n\t\t\t\t);\n\t\t\t\ttypingWindows.push({\n\t\t\t\t\tframe: at,\n\t\t\t\t\tend: at + typingFor,\n\t\t\t\t\tfrom: m.from,\n\t\t\t\t\tindex: i,\n\t\t\t\t});\n\t\t\t\tat += typingFor;\n\t\t\t}\n\t\t\tlanded.push(at);\n\t\t\tt = at;\n\t\t});\n\n\t\t// ── Layout (top to bottom) ────────────────────────────────────────\n\t\tconst measure = (text: string, size: number, width?: number) =>\n\t\t\thost.measure(text, {\n\t\t\t\tfontFamily,\n\t\t\t\tfontSize: size,\n\t\t\t\t...(width !== undefined && { width }),\n\t\t\t});\n\t\tinterface Slot {\n\t\t\tindex: number;\n\t\t\ty: number;\n\t\t\twidth: number;\n\t\t\theight: number;\n\t\t\tright: boolean;\n\t\t\tnameY?: number;\n\t\t\ttimeY?: number;\n\t\t\ttextH: number;\n\t\t\timageH: number;\n\t\t}\n\t\tconst slots: Slot[] = [];\n\t\tlet y = pad;\n\t\tprops.messages.forEach((m, i) => {\n\t\t\tconst prev = props.messages[i - 1];\n\t\t\tconst right = m.from === me;\n\t\t\tconst newRun = !prev || prev.from !== m.from;\n\t\t\tif (prev) y += newRun ? fs * 0.75 : fs * 0.22;\n\t\t\tlet nameY: number | undefined;\n\t\t\tif (showNames && newRun && !right) {\n\t\t\t\tnameY = y;\n\t\t\t\ty += measure(\"Ag\", smallFs).height + fs * 0.12;\n\t\t\t}\n\t\t\tconst inner = maxW - padX * 2;\n\t\t\tlet textW = 0;\n\t\t\tlet textH = 0;\n\t\t\tif (m.text) {\n\t\t\t\tconst one = measure(m.text, fs);\n\t\t\t\tif (one.width <= inner) {\n\t\t\t\t\ttextW = one.width;\n\t\t\t\t\ttextH = one.height;\n\t\t\t\t} else {\n\t\t\t\t\tconst wrapped = measure(m.text, fs, inner);\n\t\t\t\t\ttextW = Math.max(\n\t\t\t\t\t\t...rangeRects(wrapped, 0, m.text.length, {\n\t\t\t\t\t\t\tfontFamily,\n\t\t\t\t\t\t\tfontSize: fs,\n\t\t\t\t\t\t\twidth: inner,\n\t\t\t\t\t\t}).map((r) => r.width),\n\t\t\t\t\t);\n\t\t\t\t\ttextH = wrapped.height;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst imageW = m.image ? maxW : 0;\n\t\t\tconst imageH = m.image ? imageW / m.aspect : 0;\n\t\t\tconst width = m.image ? maxW : textW + padX * 2;\n\t\t\tconst height = imageH + (m.text ? textH + padY * 2 : 0);\n\t\t\tslots.push({ index: i, y, width, height, right, nameY, textH, imageH });\n\t\t\ty += height;\n\t\t\tif (m.time) {\n\t\t\t\t(slots[slots.length - 1] as Slot).timeY = y + fs * 0.12;\n\t\t\t\ty += measure(m.time, smallFs * 0.9).height + fs * 0.12;\n\t\t\t}\n\t\t});\n\n\t\t// ── Nodes ─────────────────────────────────────────────────────────\n\t\tconst parts: Record<string, Animatable<LayoutNode>> = {};\n\t\tconst content: LayoutNode[] = [];\n\t\tconst colorOf = (from: string) =>\n\t\t\tprops.participants[from]?.color ??\n\t\t\t(from === me ? theme.color.accent : theme.color.raised);\n\t\tconst ink = (from: string) =>\n\t\t\tfrom === me && !props.participants[from]?.color\n\t\t\t\t? \"#ffffff\"\n\t\t\t\t: theme.color.ink;\n\t\tconst entry = (node: Animatable<LayoutNode>, at: number, baseY: number) =>\n\t\t\tnode.animate(\n\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t.keys(\"scale\", springKeys(at, 0.6, 1, fps, SPRING))\n\t\t\t\t\t.keys(\"y\", springKeys(at, baseY + fs * 0.5, baseY, fps, SPRING))\n\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t[at, 0],\n\t\t\t\t\t\t[at + Math.max(2, Math.round(fps * 0.12)), 1, \"power2.out\"],\n\t\t\t\t\t]),\n\t\t\t);\n\n\t\tslots.forEach((slot) => {\n\t\t\tconst m = props.messages[slot.index] as (typeof props.messages)[number];\n\t\t\tconst at = landed[slot.index] as number;\n\t\t\tconst x = slot.right ? W - pad - slot.width : pad;\n\t\t\tconst children: LayoutNode[] = [];\n\t\t\tif (m.image) {\n\t\t\t\tchildren.push(\n\t\t\t\t\thost.box({\n\t\t\t\t\t\tid: ctx.partId(`image-${slot.index}`),\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\twidth: slot.width,\n\t\t\t\t\t\theight: slot.imageH,\n\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\tborderRadius: radius,\n\t\t\t\t\t\tchildren: [\n\t\t\t\t\t\t\thost.media(m.image, {\n\t\t\t\t\t\t\t\tid: ctx.partId(`media-${slot.index}`),\n\t\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\t\t\twidth: slot.width,\n\t\t\t\t\t\t\t\theight: slot.imageH,\n\t\t\t\t\t\t\t\tfit: \"cover\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (m.text)\n\t\t\t\tchildren.push(\n\t\t\t\t\thost.text(m.text, {\n\t\t\t\t\t\tid: ctx.partId(`text-${slot.index}`),\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tx: padX,\n\t\t\t\t\t\ty: slot.imageH + padY,\n\t\t\t\t\t\twidth: slot.width - padX * 2 + 1,\n\t\t\t\t\t\tfontFamily,\n\t\t\t\t\t\tfontSize: fs,\n\t\t\t\t\t\tfill: ink(m.from),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\tconst bubble = host.box({\n\t\t\t\tid: ctx.partId(`bubble-${slot.index}`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx,\n\t\t\t\ty: slot.y,\n\t\t\t\twidth: slot.width,\n\t\t\t\theight: slot.height,\n\t\t\t\tbackground: m.image && !m.text ? \"rgba(0,0,0,0)\" : colorOf(m.from),\n\t\t\t\tborderRadius: radius,\n\t\t\t\t// Springs grow from the corner the bubble comes from.\n\t\t\t\tanchorX: slot.right ? 1 : 0,\n\t\t\t\tanchorY: 1,\n\t\t\t\tchildren,\n\t\t\t});\n\t\t\tentry(bubble, at, slot.y);\n\t\t\tcontent.push(bubble);\n\t\t\tparts[`bubble-${slot.index}`] = bubble;\n\n\t\t\tconst small = (\n\t\t\t\tid: string,\n\t\t\t\ttext: string,\n\t\t\t\tsize: number,\n\t\t\t\ttx: number,\n\t\t\t\tty: number,\n\t\t\t) => {\n\t\t\t\tconst node = host.text(text, {\n\t\t\t\t\tid: ctx.partId(id),\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tx: tx,\n\t\t\t\t\ty: ty,\n\t\t\t\t\tfontFamily,\n\t\t\t\t\tfontSize: size,\n\t\t\t\t\tfill: theme.color.muted,\n\t\t\t\t});\n\t\t\t\tnode.animate(\n\t\t\t\t\tLayerAnimation.keys(\"opacity\", [\n\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t[at, 0],\n\t\t\t\t\t\t[at + Math.round(fps * 0.25), 1, \"power2.out\"],\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t\tcontent.push(node);\n\t\t\t};\n\t\t\tif (slot.nameY !== undefined)\n\t\t\t\tsmall(\n\t\t\t\t\t`name-${slot.index}`,\n\t\t\t\t\tprops.participants[m.from]?.name ?? m.from,\n\t\t\t\t\tsmallFs,\n\t\t\t\t\tx + padX * 0.5,\n\t\t\t\t\tslot.nameY,\n\t\t\t\t);\n\t\t\tif (slot.timeY !== undefined && m.time) {\n\t\t\t\tconst tw = measure(m.time, smallFs * 0.9).width;\n\t\t\t\tsmall(\n\t\t\t\t\t`time-${slot.index}`,\n\t\t\t\t\tm.time,\n\t\t\t\t\tsmallFs * 0.9,\n\t\t\t\t\tslot.right ? x + slot.width - tw - padX * 0.3 : x + padX * 0.3,\n\t\t\t\t\tslot.timeY,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (m.reaction) {\n\t\t\t\t// Hearts are drawn (most fonts have no emoji); anything else is text.\n\t\t\t\tconst heart = /^(❤️?|♥️?|heart)$/u.test(m.reaction.trim());\n\t\t\t\tconst rm = measure(heart ? \"M\" : m.reaction, fs * 0.7);\n\t\t\t\tconst rh = rm.height + fs * 0.16;\n\t\t\t\tconst rw = heart ? rh * 1.3 : rm.width + fs * 0.5;\n\t\t\t\tconst rAt = at + Math.round(fps * 0.6);\n\t\t\t\tconst pill = host.box({\n\t\t\t\t\tid: ctx.partId(`reaction-${slot.index}`),\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tx: slot.right ? x - rw * 0.35 : x + slot.width - rw * 0.65,\n\t\t\t\t\ty: slot.y + slot.height - rh * 0.45,\n\t\t\t\t\twidth: rw,\n\t\t\t\t\theight: rh,\n\t\t\t\t\tbackground: theme.color.surface,\n\t\t\t\t\tborderRadius: rh / 2,\n\t\t\t\t\tborderColor: theme.color.bg,\n\t\t\t\t\tborderWidth: Math.max(2, fs * 0.08),\n\t\t\t\t\tchildren: [\n\t\t\t\t\t\theart\n\t\t\t\t\t\t\t? host.shape(\"path\", {\n\t\t\t\t\t\t\t\t\tid: ctx.partId(`reaction-${slot.index}-heart`),\n\t\t\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\t\t\tx: (rw - rh * 0.56) / 2,\n\t\t\t\t\t\t\t\t\ty: rh * 0.24,\n\t\t\t\t\t\t\t\t\twidth: rh * 0.56,\n\t\t\t\t\t\t\t\t\theight: rh * 0.52,\n\t\t\t\t\t\t\t\t\td: heartPath(rh * 0.56),\n\t\t\t\t\t\t\t\t\tfillType: \"solid\",\n\t\t\t\t\t\t\t\t\tfillColor: theme.color.negative,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t: host.text(m.reaction, {\n\t\t\t\t\t\t\t\t\tid: ctx.partId(`reaction-${slot.index}-text`),\n\t\t\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\t\t\tx: fs * 0.25,\n\t\t\t\t\t\t\t\t\ty: fs * 0.08,\n\t\t\t\t\t\t\t\t\tfontFamily,\n\t\t\t\t\t\t\t\t\tfontSize: fs * 0.7,\n\t\t\t\t\t\t\t\t\tfill: theme.color.ink,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tpill.animate(\n\t\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t\t.keys(\n\t\t\t\t\t\t\t\"scale\",\n\t\t\t\t\t\t\tspringKeys(rAt, 0, 1, fps, { stiffness: 320, damping: 14 }),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t\t[rAt, 1, \"hold\"],\n\t\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t\tcontent.push(pill);\n\t\t\t}\n\t\t});\n\n\t\t// Typing indicators sit where the message will land.\n\t\tconst dot = fs * 0.24;\n\t\ttypingWindows.forEach((w, k) => {\n\t\t\tconst slot = slots[w.index] as Slot;\n\t\t\tconst bw = dot * 3 + dot * 1.1 * 2 + padX * 2;\n\t\t\tconst bh = Math.max(fs + padY * 2 - fs * 0.3, dot + padY * 2);\n\t\t\tconst dots: LayoutNode[] = [0, 1, 2].map((d) => {\n\t\t\t\tconst node = host.box({\n\t\t\t\t\tid: ctx.partId(`typing-${k}-dot-${d}`),\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tx: padX + d * dot * 2.1,\n\t\t\t\t\ty: (bh - dot) / 2,\n\t\t\t\t\twidth: dot,\n\t\t\t\t\theight: dot,\n\t\t\t\t\tbackground: theme.color.muted,\n\t\t\t\t\tborderRadius: dot / 2,\n\t\t\t\t});\n\t\t\t\t// A wave through the dots, one bounce every 0.6 s.\n\t\t\t\tconst period = Math.round(fps * 0.6);\n\t\t\t\tconst keys: KeyframeTuple[] = [[0, (bh - dot) / 2]];\n\t\t\t\tfor (\n\t\t\t\t\tlet f = w.frame + d * Math.round(period / 5);\n\t\t\t\t\tf < w.end;\n\t\t\t\t\tf += period\n\t\t\t\t)\n\t\t\t\t\tkeys.push(\n\t\t\t\t\t\t[f, (bh - dot) / 2],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\tf + Math.round(period / 4),\n\t\t\t\t\t\t\t(bh - dot) / 2 - dot * 0.7,\n\t\t\t\t\t\t\t\"sine.out\",\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[f + Math.round(period / 2), (bh - dot) / 2, \"sine.in\"],\n\t\t\t\t\t);\n\t\t\t\tnode.animate(LayerAnimation.keys(\"y\", ascending(keys)));\n\t\t\t\treturn node;\n\t\t\t});\n\t\t\tconst bubble = host.box({\n\t\t\t\tid: ctx.partId(`typing-${k}`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: pad,\n\t\t\t\ty: slot.y,\n\t\t\t\twidth: bw,\n\t\t\t\theight: bh,\n\t\t\t\tbackground: colorOf(w.from),\n\t\t\t\tborderRadius: bh / 2,\n\t\t\t\tanchorX: 0,\n\t\t\t\tanchorY: 1,\n\t\t\t\tchildren: dots,\n\t\t\t});\n\t\t\tbubble.animate(\n\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t.keys(\"scale\", springKeys(w.frame, 0.5, 1, fps, SPRING))\n\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t[w.frame, 1, \"hold\"],\n\t\t\t\t\t\t[w.end, 0, \"hold\"],\n\t\t\t\t\t]),\n\t\t\t);\n\t\t\tcontent.push(bubble);\n\t\t});\n\n\t\t// ── Scroll: keep the newest bubble (or typing dots) in view ───────\n\t\tconst events = [\n\t\t\t...slots.map((sl) => ({\n\t\t\t\tframe: landed[sl.index] as number,\n\t\t\t\tbottom: sl.y + sl.height + (sl.timeY !== undefined ? fs : 0),\n\t\t\t})),\n\t\t\t...typingWindows.map((w) => ({\n\t\t\t\tframe: w.frame,\n\t\t\t\tbottom: (slots[w.index] as Slot).y + fs * 1.6,\n\t\t\t})),\n\t\t].sort((a, b) => a.frame - b.frame);\n\t\tconst scroll: KeyframeTuple[] = [[0, 0]];\n\t\tlet top = 0;\n\t\tconst glide = Math.round(fps * 0.35);\n\t\tfor (const e of events) {\n\t\t\tconst want = Math.min(0, H - pad - e.bottom);\n\t\t\tif (want >= top) continue;\n\t\t\tconst last = scroll[scroll.length - 1] as KeyframeTuple;\n\t\t\tif (e.frame > last[0]) scroll.push([e.frame, top]);\n\t\t\tscroll.push([Math.max(e.frame, last[0]) + glide, want, \"power3.out\"]);\n\t\t\ttop = want;\n\t\t}\n\t\tconst column = host.box({\n\t\t\tid: ctx.partId(\"column\"),\n\t\t\tposition: \"absolute\",\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\twidth: W,\n\t\t\theight: Math.max(H, y + pad),\n\t\t\tchildren: content,\n\t\t});\n\t\tcolumn.animate(LayerAnimation.keys(\"y\", ascending(scroll)));\n\t\tconst viewport = host.box({\n\t\t\tid: ctx.partId(\"viewport\"),\n\t\t\tposition: \"absolute\",\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\twidth: W,\n\t\t\theight: H,\n\t\t\toverflow: \"hidden\",\n\t\t\tchildren: [column],\n\t\t});\n\t\tparts.column = column;\n\t\tparts.viewport = viewport;\n\n\t\tconst lastAt = Math.max(...landed);\n\t\treturn {\n\t\t\tchildren: [viewport],\n\t\t\twidth: W,\n\t\t\theight: H,\n\t\t\tduration: lastAt + Math.round(fps * 0.8),\n\t\t\topenEnded: true,\n\t\t\tcues: {\n\t\t\t\tmessages: landed.map((frame, index) => ({\n\t\t\t\t\tframe,\n\t\t\t\t\tindex,\n\t\t\t\t\tfrom: props.messages[index]?.from ?? \"\",\n\t\t\t\t})),\n\t\t\t\ttyping: typingWindows.map(({ frame, end, from }) => ({\n\t\t\t\t\tframe,\n\t\t\t\t\tend,\n\t\t\t\t\tfrom,\n\t\t\t\t})),\n\t\t\t} as ChatThreadCues,\n\t\t\tparts,\n\t\t};\n\t},\n});\n\n/** A heart `size` wide, top-left at 0, 0 (two lobes and a point). */\nfunction heartPath(size: number): string {\n\tconst w = size;\n\tconst h = size * 0.92;\n\treturn [\n\t\t`M${w / 2} ${h}`,\n\t\t`C${w * 0.18} ${h * 0.72} 0 ${h * 0.5} 0 ${h * 0.28}`,\n\t\t`C0 ${h * 0.1} ${w * 0.14} 0 ${w * 0.28} 0`,\n\t\t`C${w * 0.39} 0 ${w * 0.47} ${h * 0.07} ${w / 2} ${h * 0.16}`,\n\t\t`C${w * 0.53} ${h * 0.07} ${w * 0.61} 0 ${w * 0.72} 0`,\n\t\t`C${w * 0.86} 0 ${w} ${h * 0.1} ${w} ${h * 0.28}`,\n\t\t`C${w} ${h * 0.5} ${w * 0.82} ${h * 0.72} ${w / 2} ${h} Z`,\n\t].join(\" \");\n}\n\nfunction ascending(keys: KeyframeTuple[]): KeyframeTuple[] {\n\tconst out: KeyframeTuple[] = [];\n\tfor (const k of [...keys].sort((a, b) => a[0] - b[0])) {\n\t\twhile (out.length > 0 && (out[out.length - 1] as KeyframeTuple)[0] >= k[0])\n\t\t\tout.pop();\n\t\tout.push(k);\n\t}\n\treturn out;\n}\n\n/**\n * A messaging conversation: bubbles that spring in from either side, typing\n * dots before replies, names, timestamps, reactions, images and auto-scroll.\n */\nexport const ChatThread = /* @__PURE__ */ bindComponent(chatThread);\n"
|
|
30
|
+
"content": "/**\n * ChatThread: a messaging conversation. Bubbles arrive from either side\n * with a spring, the other side types first (three bouncing dots), and the\n * thread scrolls to keep the newest message in view. Names, avatars,\n * timestamps, reactions and images included.\n */\nimport {\n\ttype KeyframeTuple,\n\tLayerAnimation,\n\ttype LayoutNode,\n} from \"framefields\";\nimport {\n\ttype Animatable,\n\tbindComponent,\n\tdefineComponent,\n\trangeRects,\n\tspringKeys,\n\tz,\n} from \"framefields/components\";\n\nexport interface ChatThreadCues {\n\t[name: string]: ReadonlyArray<{ frame: number }>;\n\t/** Each message as it lands. */\n\tmessages: ReadonlyArray<{ frame: number; index: number; from: string }>;\n\t/** Each typing indicator, shown to removed. */\n\ttyping: ReadonlyArray<{ frame: number; end: number; from: string }>;\n}\n\nfunction messageSchema() {\n\treturn z\n\t\t.object({\n\t\t\tfrom: z.string(),\n\t\t\ttext: z.string().optional(),\n\t\t\t/** An image (path or URL) instead of, or above, text. */\n\t\t\timage: z.string().optional(),\n\t\t\t/** Image width / height. */\n\t\t\taspect: z\n\t\t\t\t.number()\n\t\t\t\t.positive()\n\t\t\t\t.default(4 / 3),\n\t\t\t/** Frame it lands. Default: after the previous one has been read. */\n\t\t\tat: z.number().int().min(0).optional(),\n\t\t\t/** A reaction pinned to the bubble (an emoji or short text). */\n\t\t\treaction: z.string().optional(),\n\t\t\t/** A small timestamp under the bubble. */\n\t\t\ttime: z.string().optional(),\n\t\t})\n\t\t.strict()\n\t\t.refine((m) => m.text !== undefined || m.image !== undefined, {\n\t\t\tmessage: \"A message needs `text` or `image`.\",\n\t\t});\n}\n\nconst SPRING = { stiffness: 260, damping: 20 };\n/** Livelier, for reactions. */\nconst POP = { stiffness: 320, damping: 14 };\n\nexport const chatThread = /* @__PURE__ */ defineComponent({\n\tname: \"ChatThread\",\n\tprops: () => ({\n\t\tmessages: z.array(messageSchema()).min(1),\n\t\t/** Who is \"me\" (right side, accent bubbles). Default the first sender. */\n\t\tme: z.string().optional(),\n\t\t/** Display names and bubble colours per sender. */\n\t\tparticipants: z\n\t\t\t.record(\n\t\t\t\tz.string(),\n\t\t\t\tz\n\t\t\t\t\t.object({\n\t\t\t\t\t\tname: z.string().optional(),\n\t\t\t\t\t\tcolor: z.string().optional(),\n\t\t\t\t\t\t/** A picture (path or URL); without one the avatar shows initials. */\n\t\t\t\t\t\tavatar: z.string().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.strict(),\n\t\t\t)\n\t\t\t.default({}),\n\t\t/**\n\t\t * Avatars beside each run of bubbles, at its newest one. Default:\n\t\t * `\"others\"` when any participant has an `avatar`, else `\"none\"`.\n\t\t */\n\t\tavatars: z.enum([\"others\", \"everyone\", \"none\"]).optional(),\n\t\t/** Names above the first bubble of each run (default: when more than two people talk). */\n\t\tnames: z.boolean().optional(),\n\t\t/** Others type before they send. */\n\t\ttyping: z.boolean().default(true),\n\t\t/** Reading speed between messages, characters a second. */\n\t\treadCps: z.number().positive().default(28),\n\t\tfontFamily: z.string().optional(),\n\t\tfontSize: z.number().positive().optional(),\n\t}),\n\tdefaults: { enter: false, exit: false },\n\tbuild(ctx, props) {\n\t\tconst { host, theme, fps } = ctx;\n\t\tconst s = theme.scale;\n\t\tconst W = ctx.requested.width ?? 720 * s;\n\t\tconst H = ctx.requested.height ?? 960 * s;\n\t\tconst fs = props.fontSize ?? 30 * s;\n\t\tconst fontFamily = props.fontFamily ?? theme.font.body;\n\t\tconst me = props.me ?? props.messages[0]?.from ?? \"me\";\n\t\tconst senders = [...new Set(props.messages.map((m) => m.from))];\n\t\tconst showNames = props.names ?? senders.length > 2;\n\t\tconst avatars =\n\t\t\tprops.avatars ??\n\t\t\t(Object.values(props.participants).some((p) => p.avatar)\n\t\t\t\t? \"others\"\n\t\t\t\t: \"none\");\n\t\tconst hasAvatar = (from: string) =>\n\t\t\tavatars === \"everyone\" || (avatars === \"others\" && from !== me);\n\t\tconst pad = fs * 0.8;\n\t\tconst padX = fs * 0.62;\n\t\tconst padY = fs * 0.42;\n\t\tconst avatarSize = fs * 1.5;\n\t\t// Bubbles on a side with avatars move in by this much.\n\t\tconst inset = (from: string) =>\n\t\t\thasAvatar(from) ? avatarSize + fs * 0.3 : 0;\n\t\tconst maxW = Math.min(\n\t\t\tW * 0.74,\n\t\t\tW - pad * 2 - Math.max(...senders.map(inset)),\n\t\t);\n\t\tconst radius = fs * 0.9;\n\t\tconst smallFs = fs * 0.62;\n\n\t\t// ── Timing ────────────────────────────────────────────────────────\n\t\tconst landed: number[] = [];\n\t\tconst typingWindows: Array<{\n\t\t\tframe: number;\n\t\t\tend: number;\n\t\t\tfrom: string;\n\t\t\tindex: number;\n\t\t}> = [];\n\t\tlet t = Math.round(fps * 0.2);\n\t\tprops.messages.forEach((m, i) => {\n\t\t\tconst prev = props.messages[i - 1];\n\t\t\tif (prev && m.at === undefined) {\n\t\t\t\tconst read = ((prev.text?.length ?? 12) / props.readCps) * fps;\n\t\t\t\tt += Math.round(Math.min(fps * 2.5, Math.max(fps * 0.5, read)));\n\t\t\t}\n\t\t\tlet at = m.at ?? t;\n\t\t\tif (props.typing && m.from !== me && m.at === undefined) {\n\t\t\t\tconst typingFor = Math.round(\n\t\t\t\t\tMath.min(2.2, Math.max(0.7, (m.text?.length ?? 10) / 14)) * fps,\n\t\t\t\t);\n\t\t\t\ttypingWindows.push({\n\t\t\t\t\tframe: at,\n\t\t\t\t\tend: at + typingFor,\n\t\t\t\t\tfrom: m.from,\n\t\t\t\t\tindex: i,\n\t\t\t\t});\n\t\t\t\tat += typingFor;\n\t\t\t}\n\t\t\tlanded.push(at);\n\t\t\tt = at;\n\t\t});\n\n\t\t// ── Layout (top to bottom) ────────────────────────────────────────\n\t\tconst measure = (text: string, size: number, width?: number) =>\n\t\t\thost.measure(text, {\n\t\t\t\tfontFamily,\n\t\t\t\tfontSize: size,\n\t\t\t\t...(width !== undefined && { width }),\n\t\t\t});\n\t\tinterface Slot {\n\t\t\tindex: number;\n\t\t\ty: number;\n\t\t\twidth: number;\n\t\t\theight: number;\n\t\t\tright: boolean;\n\t\t\tnameY?: number;\n\t\t\ttimeY?: number;\n\t\t\ttextH: number;\n\t\t\timageH: number;\n\t\t}\n\t\tconst slots: Slot[] = [];\n\t\tlet y = pad;\n\t\tprops.messages.forEach((m, i) => {\n\t\t\tconst prev = props.messages[i - 1];\n\t\t\tconst right = m.from === me;\n\t\t\tconst newRun = !prev || prev.from !== m.from;\n\t\t\tif (prev) y += newRun ? fs * 0.75 : fs * 0.22;\n\t\t\t// A reaction sits on the bubble's top corner; leave it room.\n\t\t\tif (prev && m.reaction) y += fs * 0.5;\n\t\t\tlet nameY: number | undefined;\n\t\t\tif (showNames && newRun && !right) {\n\t\t\t\tnameY = y;\n\t\t\t\ty += measure(\"Ag\", smallFs).height + fs * 0.12;\n\t\t\t}\n\t\t\tconst inner = maxW - padX * 2;\n\t\t\tlet textW = 0;\n\t\t\tlet textH = 0;\n\t\t\tif (m.text) {\n\t\t\t\tconst one = measure(m.text, fs);\n\t\t\t\tif (one.width <= inner) {\n\t\t\t\t\ttextW = one.width;\n\t\t\t\t\ttextH = one.height;\n\t\t\t\t} else {\n\t\t\t\t\tconst wrapped = measure(m.text, fs, inner);\n\t\t\t\t\ttextW = Math.max(\n\t\t\t\t\t\t...rangeRects(wrapped, 0, m.text.length, {\n\t\t\t\t\t\t\tfontFamily,\n\t\t\t\t\t\t\tfontSize: fs,\n\t\t\t\t\t\t\twidth: inner,\n\t\t\t\t\t\t}).map((r) => r.width),\n\t\t\t\t\t);\n\t\t\t\t\ttextH = wrapped.height;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst imageW = m.image ? maxW : 0;\n\t\t\tconst imageH = m.image ? imageW / m.aspect : 0;\n\t\t\tconst width = m.image ? maxW : textW + padX * 2;\n\t\t\tconst height = imageH + (m.text ? textH + padY * 2 : 0);\n\t\t\tslots.push({ index: i, y, width, height, right, nameY, textH, imageH });\n\t\t\ty += height;\n\t\t\tif (m.time) {\n\t\t\t\t(slots[slots.length - 1] as Slot).timeY = y + fs * 0.12;\n\t\t\t\ty += measure(m.time, smallFs * 0.9).height + fs * 0.12;\n\t\t\t}\n\t\t});\n\n\t\t// ── Nodes ─────────────────────────────────────────────────────────\n\t\tconst parts: Record<string, Animatable<LayoutNode>> = {};\n\t\tconst content: LayoutNode[] = [];\n\t\tconst colorOf = (from: string) =>\n\t\t\tprops.participants[from]?.color ??\n\t\t\t(from === me ? theme.color.accent : theme.color.raised);\n\t\tconst ink = (from: string) =>\n\t\t\tfrom === me && !props.participants[from]?.color\n\t\t\t\t? \"#ffffff\"\n\t\t\t\t: theme.color.ink;\n\t\tconst fade = Math.max(2, Math.round(fps * 0.12));\n\t\t// The typing bubble: three dots in a pill, where the message will land.\n\t\tconst dot = fs * 0.24;\n\t\tconst typingW = dot * 3 + dot * 1.1 * 2 + padX * 2;\n\t\tconst typingH = Math.max(fs + padY * 2 - fs * 0.3, dot + padY * 2);\n\t\tconst typingOf = (index: number) =>\n\t\t\ttypingWindows.find((w) => w.index === index);\n\n\t\tslots.forEach((slot) => {\n\t\t\tconst m = props.messages[slot.index] as (typeof props.messages)[number];\n\t\t\tconst at = landed[slot.index] as number;\n\t\t\tconst x = slot.right\n\t\t\t\t? W - pad - inset(m.from) - slot.width\n\t\t\t\t: pad + inset(m.from);\n\t\t\tconst children: LayoutNode[] = [];\n\t\t\tif (m.image) {\n\t\t\t\tchildren.push(\n\t\t\t\t\thost.box({\n\t\t\t\t\t\tid: ctx.partId(`image-${slot.index}`),\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\twidth: slot.width,\n\t\t\t\t\t\theight: slot.imageH,\n\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\tborderRadius: radius,\n\t\t\t\t\t\tchildren: [\n\t\t\t\t\t\t\thost.media(m.image, {\n\t\t\t\t\t\t\t\tid: ctx.partId(`media-${slot.index}`),\n\t\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\t\t\twidth: slot.width,\n\t\t\t\t\t\t\t\theight: slot.imageH,\n\t\t\t\t\t\t\t\tfit: \"cover\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst typed = typingOf(slot.index);\n\t\t\t// A typed text reply grows out of its typing bubble: same place,\n\t\t\t// same colour, so the dots become the message.\n\t\t\tconst morph = typed !== undefined && !m.image;\n\t\t\tif (m.text) {\n\t\t\t\tconst ty = slot.imageH + padY;\n\t\t\t\tconst text = host.text(m.text, {\n\t\t\t\t\tid: ctx.partId(`text-${slot.index}`),\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tx: padX,\n\t\t\t\t\ty: ty,\n\t\t\t\t\twidth: slot.width - padX * 2 + 1,\n\t\t\t\t\tfontFamily,\n\t\t\t\t\tfontSize: fs,\n\t\t\t\t\tfill: ink(m.from),\n\t\t\t\t});\n\t\t\t\t// The words settle in just after their bubble opens.\n\t\t\t\tconst t0 = at + (morph ? Math.round(fps * 0.12) : 1);\n\t\t\t\ttext.animate(\n\t\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t\t[t0, 0],\n\t\t\t\t\t\t\t[t0 + fade, 1, \"power2.out\"],\n\t\t\t\t\t\t])\n\t\t\t\t\t\t.keys(\"y\", [\n\t\t\t\t\t\t\t[0, ty + fs * 0.2],\n\t\t\t\t\t\t\t[t0, ty + fs * 0.2],\n\t\t\t\t\t\t\t[t0 + Math.round(fps * 0.3), ty, \"power3.out\"],\n\t\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t\tchildren.push(text);\n\t\t\t}\n\t\t\tconst bubble = host.box({\n\t\t\t\tid: ctx.partId(`bubble-${slot.index}`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx,\n\t\t\t\ty: slot.y,\n\t\t\t\twidth: slot.width,\n\t\t\t\theight: slot.height,\n\t\t\t\tbackground: m.image && !m.text ? \"rgba(0,0,0,0)\" : colorOf(m.from),\n\t\t\t\tborderRadius: radius,\n\t\t\t\t// Springs grow from the corner the bubble comes from.\n\t\t\t\tanchorX: slot.right ? 1 : 0,\n\t\t\t\tanchorY: 1,\n\t\t\t\tchildren,\n\t\t\t});\n\t\t\tif (morph)\n\t\t\t\tbubble.animate(\n\t\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t\t.keys(\"width\", springKeys(at, typingW, slot.width, fps, SPRING))\n\t\t\t\t\t\t.keys(\"height\", springKeys(at, typingH, slot.height, fps, SPRING))\n\t\t\t\t\t\t.keys(\n\t\t\t\t\t\t\t\"borderRadius\",\n\t\t\t\t\t\t\tspringKeys(at, typingH / 2, radius, fps, SPRING),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t\t[at, 1, \"hold\"],\n\t\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\telse {\n\t\t\t\t// Sent bubbles rise from below, as if from the compose field;\n\t\t\t\t// received ones pop from their corner.\n\t\t\t\tconst [scale, lift] = slot.right ? [0.86, fs * 1.1] : [0.6, fs * 0.5];\n\t\t\t\tbubble.animate(\n\t\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t\t.keys(\"scale\", springKeys(at, scale, 1, fps, SPRING))\n\t\t\t\t\t\t.keys(\"y\", springKeys(at, slot.y + lift, slot.y, fps, SPRING))\n\t\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t\t[at, 0],\n\t\t\t\t\t\t\t[at + fade, 1, \"power2.out\"],\n\t\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t}\n\t\t\tcontent.push(bubble);\n\t\t\tparts[`bubble-${slot.index}`] = bubble;\n\n\t\t\tconst small = (\n\t\t\t\tid: string,\n\t\t\t\ttext: string,\n\t\t\t\tsize: number,\n\t\t\t\ttx: number,\n\t\t\t\tty: number,\n\t\t\t\tfrom = at,\n\t\t\t) => {\n\t\t\t\tconst node = host.text(text, {\n\t\t\t\t\tid: ctx.partId(id),\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tx: tx,\n\t\t\t\t\ty: ty,\n\t\t\t\t\tfontFamily,\n\t\t\t\t\tfontSize: size,\n\t\t\t\t\tfill: theme.color.muted,\n\t\t\t\t});\n\t\t\t\tnode.animate(\n\t\t\t\t\tLayerAnimation.keys(\"opacity\", [\n\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t[from, 0],\n\t\t\t\t\t\t[from + Math.round(fps * 0.25), 1, \"power2.out\"],\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t\tcontent.push(node);\n\t\t\t};\n\t\t\tif (slot.nameY !== undefined)\n\t\t\t\tsmall(\n\t\t\t\t\t`name-${slot.index}`,\n\t\t\t\t\tprops.participants[m.from]?.name ?? m.from,\n\t\t\t\t\tsmallFs,\n\t\t\t\t\tx + padX * 0.5,\n\t\t\t\t\tslot.nameY,\n\t\t\t\t\t// With the run's typing dots, when it starts with them.\n\t\t\t\t\ttyped?.frame ?? at,\n\t\t\t\t);\n\t\t\tif (slot.timeY !== undefined && m.time) {\n\t\t\t\tconst tw = measure(m.time, smallFs * 0.9).width;\n\t\t\t\tsmall(\n\t\t\t\t\t`time-${slot.index}`,\n\t\t\t\t\tm.time,\n\t\t\t\t\tsmallFs * 0.9,\n\t\t\t\t\tslot.right ? x + slot.width - tw - padX * 0.3 : x + padX * 0.3,\n\t\t\t\t\tslot.timeY,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (m.reaction) {\n\t\t\t\t// Hearts are drawn, so they show without an emoji font; anything\n\t\t\t\t// else is text (emoji need one registered).\n\t\t\t\tconst heart = /^(❤️?|♥️?|heart)$/u.test(m.reaction.trim());\n\t\t\t\tconst rfs = fs * 0.9;\n\t\t\t\tconst rm = measure(m.reaction, rfs);\n\t\t\t\tconst rh = fs * 1.5;\n\t\t\t\tconst rw = heart ? rh : Math.max(rh, rm.width + fs * 0.6);\n\t\t\t\tconst rAt = at + Math.round(fps * 0.6);\n\t\t\t\tconst border = Math.max(2, fs * 0.1);\n\t\t\t\tconst hs = rh * 0.46;\n\t\t\t\t// On the outer top corner, like a tapback.\n\t\t\t\tconst pill = host.box({\n\t\t\t\t\tid: ctx.partId(`reaction-${slot.index}`),\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tx: slot.right ? x - rw * 0.45 : x + slot.width - rw * 0.55,\n\t\t\t\t\ty: slot.y - rh * 0.6,\n\t\t\t\t\twidth: rw,\n\t\t\t\t\theight: rh,\n\t\t\t\t\tbackground: theme.color.raised,\n\t\t\t\t\tborderRadius: rh / 2,\n\t\t\t\t\tborderColor: theme.color.bg,\n\t\t\t\t\tborderWidth: border,\n\t\t\t\t\tanchorX: 0.5,\n\t\t\t\t\tanchorY: 1,\n\t\t\t\t\tchildren: [\n\t\t\t\t\t\theart\n\t\t\t\t\t\t\t? host.shape(\"path\", {\n\t\t\t\t\t\t\t\t\tid: ctx.partId(`reaction-${slot.index}-heart`),\n\t\t\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\t\t\tx: (rw - hs) / 2,\n\t\t\t\t\t\t\t\t\ty: (rh - hs * 0.92) / 2 + hs * 0.04,\n\t\t\t\t\t\t\t\t\twidth: hs,\n\t\t\t\t\t\t\t\t\theight: hs * 0.92,\n\t\t\t\t\t\t\t\t\td: heartPath(hs),\n\t\t\t\t\t\t\t\t\tfillType: \"solid\",\n\t\t\t\t\t\t\t\t\tfillColor: theme.color.negative,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t: host.text(m.reaction, {\n\t\t\t\t\t\t\t\t\tid: ctx.partId(`reaction-${slot.index}-text`),\n\t\t\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\t\t\tx: (rw - rm.width) / 2,\n\t\t\t\t\t\t\t\t\ty: (rh - rm.height) / 2,\n\t\t\t\t\t\t\t\t\tfontFamily,\n\t\t\t\t\t\t\t\t\tfontSize: rfs,\n\t\t\t\t\t\t\t\t\tfill: theme.color.ink,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\t\tpill.animate(\n\t\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t\t.keys(\"scale\", springKeys(rAt, 0, 1, fps, POP))\n\t\t\t\t\t\t.keys(\"rotation\", springKeys(rAt, -24, 0, fps, POP))\n\t\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t\t[rAt, 1, \"hold\"],\n\t\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t\tcontent.push(pill);\n\t\t\t}\n\t\t});\n\n\t\t// Typing indicators sit where the message will land.\n\t\ttypingWindows.forEach((w, k) => {\n\t\t\tconst slot = slots[w.index] as Slot;\n\t\t\tconst bw = typingW;\n\t\t\tconst bh = typingH;\n\t\t\tconst dots: LayoutNode[] = [0, 1, 2].map((d) => {\n\t\t\t\tconst node = host.box({\n\t\t\t\t\tid: ctx.partId(`typing-${k}-dot-${d}`),\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\tx: padX + d * dot * 2.1,\n\t\t\t\t\ty: (bh - dot) / 2,\n\t\t\t\t\twidth: dot,\n\t\t\t\t\theight: dot,\n\t\t\t\t\tbackground: theme.color.muted,\n\t\t\t\t\tborderRadius: dot / 2,\n\t\t\t\t});\n\t\t\t\t// A wave through the dots, one bounce every 0.6 s.\n\t\t\t\tconst period = Math.round(fps * 0.6);\n\t\t\t\tconst keys: KeyframeTuple[] = [[0, (bh - dot) / 2]];\n\t\t\t\tconst glow: KeyframeTuple[] = [[0, 0.45]];\n\t\t\t\tfor (\n\t\t\t\t\tlet f = w.frame + d * Math.round(period / 5);\n\t\t\t\t\tf < w.end;\n\t\t\t\t\tf += period\n\t\t\t\t) {\n\t\t\t\t\tkeys.push(\n\t\t\t\t\t\t[f, (bh - dot) / 2],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\tf + Math.round(period / 4),\n\t\t\t\t\t\t\t(bh - dot) / 2 - dot * 0.7,\n\t\t\t\t\t\t\t\"sine.out\",\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[f + Math.round(period / 2), (bh - dot) / 2, \"sine.in\"],\n\t\t\t\t\t);\n\t\t\t\t\t// Each dot brightens at the top of its bounce.\n\t\t\t\t\tglow.push(\n\t\t\t\t\t\t[f, 0.45],\n\t\t\t\t\t\t[f + Math.round(period / 4), 1, \"sine.out\"],\n\t\t\t\t\t\t[f + Math.round(period / 2), 0.45, \"sine.in\"],\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tnode.animate(\n\t\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t\t.keys(\"y\", ascending(keys))\n\t\t\t\t\t\t.keys(\"opacity\", ascending(glow)),\n\t\t\t\t);\n\t\t\t\treturn node;\n\t\t\t});\n\t\t\tconst bubble = host.box({\n\t\t\t\tid: ctx.partId(`typing-${k}`),\n\t\t\t\tposition: \"absolute\",\n\t\t\t\tx: pad + inset(w.from),\n\t\t\t\ty: slot.y,\n\t\t\t\twidth: bw,\n\t\t\t\theight: bh,\n\t\t\t\tbackground: colorOf(w.from),\n\t\t\t\tborderRadius: bh / 2,\n\t\t\t\tanchorX: 0,\n\t\t\t\tanchorY: 1,\n\t\t\t\tchildren: dots,\n\t\t\t});\n\t\t\tbubble.animate(\n\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t.keys(\"scale\", springKeys(w.frame, 0.5, 1, fps, SPRING))\n\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t[w.frame, 1, \"hold\"],\n\t\t\t\t\t\t[w.end, 0, \"hold\"],\n\t\t\t\t\t]),\n\t\t\t);\n\t\t\tcontent.push(bubble);\n\t\t});\n\n\t\tconst avatar = (\n\t\t\tfrom: string,\n\t\t\tid: string,\n\t\t\tx: number,\n\t\t\ty: number,\n\t\t): Animatable<LayoutNode> => {\n\t\t\tconst p = props.participants[from];\n\t\t\tconst size = avatarSize;\n\t\t\tconst base = {\n\t\t\t\tid: ctx.partId(id),\n\t\t\t\tposition: \"absolute\" as const,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\twidth: size,\n\t\t\t\theight: size,\n\t\t\t\tborderRadius: size / 2,\n\t\t\t\tanchorX: 0.5,\n\t\t\t\tanchorY: 1,\n\t\t\t};\n\t\t\tif (p?.avatar)\n\t\t\t\treturn host.box({\n\t\t\t\t\t...base,\n\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\tbackground: theme.color.raised,\n\t\t\t\t\tchildren: [\n\t\t\t\t\t\thost.media(p.avatar, {\n\t\t\t\t\t\t\tid: ctx.partId(`${id}-image`),\n\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\tx: 0,\n\t\t\t\t\t\t\ty: 0,\n\t\t\t\t\t\t\twidth: size,\n\t\t\t\t\t\t\theight: size,\n\t\t\t\t\t\t\tfit: \"cover\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t],\n\t\t\t\t});\n\t\t\tconst letters = initials(p?.name ?? from);\n\t\t\tconst ifs = size * (letters.length > 1 ? 0.38 : 0.44);\n\t\t\tconst m = host.measure(letters, {\n\t\t\t\tfontFamily,\n\t\t\t\tfontSize: ifs,\n\t\t\t\tfontWeight: 600,\n\t\t\t});\n\t\t\treturn host.box({\n\t\t\t\t...base,\n\t\t\t\tbackground: colorOf(from),\n\t\t\t\tchildren: [\n\t\t\t\t\thost.text(letters, {\n\t\t\t\t\t\tid: ctx.partId(`${id}-initials`),\n\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\tx: (size - m.width) / 2,\n\t\t\t\t\t\ty: (size - m.height) / 2,\n\t\t\t\t\t\tfontFamily,\n\t\t\t\t\t\tfontSize: ifs,\n\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\tfill: ink(from),\n\t\t\t\t\t}),\n\t\t\t\t],\n\t\t\t});\n\t\t};\n\n\t\t// One avatar per run of bubbles: it arrives with the run's typing dots\n\t\t// or first bubble and moves down to each newer bubble, iMessage style.\n\t\tconst runs: number[][] = [];\n\t\tprops.messages.forEach((m, i) => {\n\t\t\tconst run = runs[runs.length - 1];\n\t\t\tif (run && props.messages[run[0] as number]?.from === m.from) run.push(i);\n\t\t\telse runs.push([i]);\n\t\t});\n\t\truns.forEach((run) => {\n\t\t\tconst from = props.messages[run[0] as number]?.from ?? \"\";\n\t\t\tif (!hasAvatar(from)) return;\n\t\t\tconst r = run[0] as number;\n\t\t\tconst stops: Array<[number, number]> = [];\n\t\t\tfor (const i of run) {\n\t\t\t\tconst slot = slots[i] as Slot;\n\t\t\t\tconst w = typingWindows.find((tw) => tw.index === i);\n\t\t\t\tif (w) stops.push([w.frame, slot.y + typingH - avatarSize]);\n\t\t\t\tstops.push([landed[i] as number, slot.y + slot.height - avatarSize]);\n\t\t\t}\n\t\t\tconst [start, startY] = stops[0] as [number, number];\n\t\t\t// Same spring as the bubbles, so it stays level with their bottoms.\n\t\t\tconst y: KeyframeTuple[] = [[0, startY]];\n\t\t\tlet current = startY;\n\t\t\tfor (const [f, to] of stops.slice(1)) {\n\t\t\t\tif (to === current) continue;\n\t\t\t\ty.push(...springKeys(f, current, to, fps, SPRING));\n\t\t\t\tcurrent = to;\n\t\t\t}\n\t\t\tconst node = avatar(\n\t\t\t\tfrom,\n\t\t\t\t`avatar-${r}`,\n\t\t\t\tfrom === me ? W - pad - avatarSize : pad,\n\t\t\t\tstartY,\n\t\t\t);\n\t\t\tnode.animate(\n\t\t\t\tLayerAnimation.create()\n\t\t\t\t\t.keys(\"y\", ascending(y))\n\t\t\t\t\t.keys(\"scale\", springKeys(start, 0.4, 1, fps, SPRING))\n\t\t\t\t\t.keys(\"opacity\", [\n\t\t\t\t\t\t[0, 0],\n\t\t\t\t\t\t[start, 0],\n\t\t\t\t\t\t[start + Math.max(2, Math.round(fps * 0.12)), 1, \"power2.out\"],\n\t\t\t\t\t]),\n\t\t\t);\n\t\t\tcontent.push(node);\n\t\t\tparts[`avatar-${r}`] = node;\n\t\t});\n\n\t\t// ── Scroll: keep the newest bubble (or typing dots) in view ───────\n\t\tconst events = [\n\t\t\t...slots.map((sl) => ({\n\t\t\t\tframe: landed[sl.index] as number,\n\t\t\t\tbottom: sl.y + sl.height + (sl.timeY !== undefined ? fs : 0),\n\t\t\t})),\n\t\t\t...typingWindows.map((w) => ({\n\t\t\t\tframe: w.frame,\n\t\t\t\tbottom: (slots[w.index] as Slot).y + fs * 1.6,\n\t\t\t})),\n\t\t].sort((a, b) => a.frame - b.frame);\n\t\tconst scroll: KeyframeTuple[] = [[0, 0]];\n\t\tlet top = 0;\n\t\tconst glide = Math.round(fps * 0.35);\n\t\tfor (const e of events) {\n\t\t\tconst want = Math.min(0, H - pad - e.bottom);\n\t\t\tif (want >= top) continue;\n\t\t\tconst last = scroll[scroll.length - 1] as KeyframeTuple;\n\t\t\tif (e.frame > last[0]) scroll.push([e.frame, top]);\n\t\t\tscroll.push([Math.max(e.frame, last[0]) + glide, want, \"power3.out\"]);\n\t\t\ttop = want;\n\t\t}\n\t\tconst column = host.box({\n\t\t\tid: ctx.partId(\"column\"),\n\t\t\tposition: \"absolute\",\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\twidth: W,\n\t\t\theight: Math.max(H, y + pad),\n\t\t\tchildren: content,\n\t\t});\n\t\tcolumn.animate(LayerAnimation.keys(\"y\", ascending(scroll)));\n\t\tconst viewport = host.box({\n\t\t\tid: ctx.partId(\"viewport\"),\n\t\t\tposition: \"absolute\",\n\t\t\tx: 0,\n\t\t\ty: 0,\n\t\t\twidth: W,\n\t\t\theight: H,\n\t\t\toverflow: \"hidden\",\n\t\t\tchildren: [column],\n\t\t});\n\t\tparts.column = column;\n\t\tparts.viewport = viewport;\n\n\t\tconst lastAt = Math.max(...landed);\n\t\treturn {\n\t\t\tchildren: [viewport],\n\t\t\twidth: W,\n\t\t\theight: H,\n\t\t\tduration: lastAt + Math.round(fps * 0.8),\n\t\t\topenEnded: true,\n\t\t\tcues: {\n\t\t\t\tmessages: landed.map((frame, index) => ({\n\t\t\t\t\tframe,\n\t\t\t\t\tindex,\n\t\t\t\t\tfrom: props.messages[index]?.from ?? \"\",\n\t\t\t\t})),\n\t\t\t\ttyping: typingWindows.map(({ frame, end, from }) => ({\n\t\t\t\t\tframe,\n\t\t\t\t\tend,\n\t\t\t\t\tfrom,\n\t\t\t\t})),\n\t\t\t} as ChatThreadCues,\n\t\t\tparts,\n\t\t};\n\t},\n});\n\n/** \"Ana Lima\" → \"AL\", \"kai\" → \"K\". */\nfunction initials(name: string): string {\n\tconst words = name.trim().split(/\\s+/u).filter(Boolean);\n\tconst first = (w: string | undefined) => [...(w ?? \"\")][0] ?? \"\";\n\treturn (\n\t\tfirst(words[0]) + (words.length > 1 ? first(words[words.length - 1]) : \"\")\n\t).toUpperCase();\n}\n\n/** A heart `size` wide, top-left at 0, 0 (two lobes and a point). */\nfunction heartPath(size: number): string {\n\tconst w = size;\n\tconst h = size * 0.92;\n\treturn [\n\t\t`M${w / 2} ${h}`,\n\t\t`C${w * 0.18} ${h * 0.72} 0 ${h * 0.5} 0 ${h * 0.28}`,\n\t\t`C0 ${h * 0.1} ${w * 0.14} 0 ${w * 0.28} 0`,\n\t\t`C${w * 0.39} 0 ${w * 0.47} ${h * 0.07} ${w / 2} ${h * 0.16}`,\n\t\t`C${w * 0.53} ${h * 0.07} ${w * 0.61} 0 ${w * 0.72} 0`,\n\t\t`C${w * 0.86} 0 ${w} ${h * 0.1} ${w} ${h * 0.28}`,\n\t\t`C${w} ${h * 0.5} ${w * 0.82} ${h * 0.72} ${w / 2} ${h} Z`,\n\t].join(\" \");\n}\n\nfunction ascending(keys: KeyframeTuple[]): KeyframeTuple[] {\n\tconst out: KeyframeTuple[] = [];\n\tfor (const k of [...keys].sort((a, b) => a[0] - b[0])) {\n\t\twhile (out.length > 0 && (out[out.length - 1] as KeyframeTuple)[0] >= k[0])\n\t\t\tout.pop();\n\t\tout.push(k);\n\t}\n\treturn out;\n}\n\n/**\n * A messaging conversation: bubbles that spring in from either side, typing\n * dots before replies, names, avatars, timestamps, reactions, images and\n * auto-scroll.\n */\nexport const ChatThread = /* @__PURE__ */ bindComponent(chatThread);\n"
|
|
30
31
|
}
|
|
31
32
|
]
|
|
32
33
|
}
|
package/dist/registry/index.json
CHANGED
|
@@ -93,12 +93,13 @@
|
|
|
93
93
|
{
|
|
94
94
|
"name": "card-stack",
|
|
95
95
|
"title": "CardStack",
|
|
96
|
-
"summary": "Cards in a depth stack that shuffle: the front card flies
|
|
96
|
+
"summary": "Cards in a depth stack that shuffle: the front card flies, drops, zooms or flips away and returns to the back; cards are dealt onto the pile; or the stack fans out into a hand.",
|
|
97
97
|
"tags": [
|
|
98
98
|
"cards",
|
|
99
99
|
"stack",
|
|
100
100
|
"deck",
|
|
101
101
|
"carousel",
|
|
102
|
+
"fan",
|
|
102
103
|
"testimonials",
|
|
103
104
|
"features",
|
|
104
105
|
"3d"
|
|
@@ -109,7 +110,7 @@
|
|
|
109
110
|
{
|
|
110
111
|
"name": "chat-thread",
|
|
111
112
|
"title": "ChatThread",
|
|
112
|
-
"summary": "A messaging conversation: bubbles that spring in from either side, typing dots before replies, names, timestamps, reactions, images and auto-scroll.",
|
|
113
|
+
"summary": "A messaging conversation: bubbles that spring in from either side, typing dots before replies, names, avatars, timestamps, reactions, images and auto-scroll.",
|
|
113
114
|
"tags": [
|
|
114
115
|
"chat",
|
|
115
116
|
"messages",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { F as describeGpu, L as initHeadlessWebGPU, p as GpuAdapterDescription } from "../index-
|
|
2
|
-
import { _ as renderSemaphore, a as renderFrameGrid, c as HeadlessMediaRenderer, d as QaSegment, f as RenderProgress, g as formatQaReport, h as VideoQaStats, i as computeGridLayout, l as HeadlessWebGPURenderer, m as VideoQaReport, n as FrameGridRenderable, o as resolveSamplePoints, p as VideoQaOptions, r as FrameSamplePoint, s as AudioQaStats, t as FrameGridOptions, u as QaIssue, v as resetRenderCaches } from "../index-
|
|
1
|
+
import { F as describeGpu, L as initHeadlessWebGPU, p as GpuAdapterDescription } from "../index-ocZc12ZS.mjs";
|
|
2
|
+
import { _ as renderSemaphore, a as renderFrameGrid, c as HeadlessMediaRenderer, d as QaSegment, f as RenderProgress, g as formatQaReport, h as VideoQaStats, i as computeGridLayout, l as HeadlessWebGPURenderer, m as VideoQaReport, n as FrameGridRenderable, o as resolveSamplePoints, p as VideoQaOptions, r as FrameSamplePoint, s as AudioQaStats, t as FrameGridOptions, u as QaIssue, v as resetRenderCaches } from "../index-BhzlG_1Q.mjs";
|
|
3
3
|
export { AudioQaStats, FrameGridOptions, FrameGridRenderable, FrameSamplePoint, GpuAdapterDescription, HeadlessMediaRenderer, HeadlessWebGPURenderer, QaIssue, QaSegment, RenderProgress, VideoQaOptions, VideoQaReport, VideoQaStats, computeGridLayout, describeGpu, formatQaReport, initHeadlessWebGPU, renderFrameGrid, renderSemaphore, resetRenderCaches, resolveSamplePoints };
|
package/dist/renderer/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../chunk-D7gtBK74.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as HeadlessMediaRenderer, c as renderSemaphore, i as resetRenderCaches, n as renderFrameGrid, o as HeadlessWebGPURenderer, r as resolveSamplePoints, s as formatQaReport, t as computeGridLayout } from "../renderer-
|
|
2
|
+
import { R as initHeadlessWebGPU, x as describeGpu } from "../dist-BuirkW-f.mjs";
|
|
3
|
+
import { a as HeadlessMediaRenderer, c as renderSemaphore, i as resetRenderCaches, n as renderFrameGrid, o as HeadlessWebGPURenderer, r as resolveSamplePoints, s as formatQaReport, t as computeGridLayout } from "../renderer-BfQdc3RM.mjs";
|
|
4
4
|
|
|
5
5
|
export { HeadlessMediaRenderer, HeadlessWebGPURenderer, computeGridLayout, describeGpu, formatQaReport, initHeadlessWebGPU, renderFrameGrid, renderSemaphore, resetRenderCaches, resolveSamplePoints };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as defineRenderer, u as unwrapFrameParam } from "./renderer-BgmO1Dzw.mjs";
|
|
2
2
|
import { a as configBuilder, i as VideoResultSchema, n as ImageResultSchema } from "./dist-BPOi5xb_.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { a as createFullscreenPipeline, c as drawTextureToTarget, d as signalTextureView, f as signalTiming, l as fragmentBindGroupLayout, o as drawChildToTexture, p as texturesLayout, s as drawFullscreenPass, t as FULLSCREEN_QUAD_WGSL, u as perDevice } from "./gpu-
|
|
3
|
+
import { In as signalRegistry } from "./dist-BuirkW-f.mjs";
|
|
4
|
+
import { a as createFullscreenPipeline, c as drawTextureToTarget, d as signalTextureView, f as signalTiming, l as fragmentBindGroupLayout, o as drawChildToTexture, p as texturesLayout, s as drawFullscreenPass, t as FULLSCREEN_QUAD_WGSL, u as perDevice } from "./gpu-BRBIoxMC.mjs";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
7
|
//#region ../../nodes/node-blur/dist/renderers-rcrwEq-B.mjs
|
|
@@ -845,4 +845,4 @@ var renderers_default = defineRenderer({ WebGPURenderer: BlurWebGPURenderer });
|
|
|
845
845
|
|
|
846
846
|
//#endregion
|
|
847
847
|
export { renderers_default as t };
|
|
848
|
-
//# sourceMappingURL=renderer-
|
|
848
|
+
//# sourceMappingURL=renderer-1NoJLLDa.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer-RiWi7y5C.mjs","names":[],"sources":["../../../nodes/node-blur/dist/renderers-rcrwEq-B.mjs"],"sourcesContent":["import { defineRenderer } from \"@framefields/node-sdk/renderer\";\nimport { ImageResultSchema, VideoResultSchema, configBuilder, unwrapFrameParam } from \"@framefields/node-sdk\";\nimport { FULLSCREEN_QUAD_WGSL, createFullscreenPipeline, drawChildToTexture, drawFullscreenPass, drawTextureToTarget, fragmentBindGroupLayout, perDevice, signalTextureView, signalTiming, texturesLayout } from \"@framefields/node-sdk/gpu\";\nimport { signalRegistry } from \"@framefields/webgpu-renderers\";\nimport { z } from \"zod\";\n\n//#region src/shared/config.ts\nconst MAX_BLUR = 100;\nconst blurConfig = configBuilder().field(\"blurType\", z.enum([\n\t\"Gaussian\",\n\t\"Box\",\n\t\"Median\",\n\t\"Motion\",\n\t\"Bilateral\",\n\t\"Edge-preserving\",\n\t\"Radial\",\n\t\"Zoom\"\n]).default(\"Gaussian\"), {\n\tbindable: false,\n\tlabel: \"Blur Type\"\n}).field(\"strength\", z.number().min(0).max(MAX_BLUR).default(5), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Strength Signal\",\n\tdescription: \"The intensity of the blur. 0 = no blur. Can be modulated by a static number or a dynamic signal.\"\n}).field(\"angle\", z.number().int().min(0).max(360).default(0), {\n\tbindable: false,\n\tsignal: true,\n\tlabel: \"Angle\",\n\tdescription: \"Direction of a Motion blur, in degrees.\"\n}).field(\"sigmaColor\", z.number().min(.01).max(1).default(.1), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Color Sigma Signal\",\n\tdescription: \"Sigma value for color space in Bilateral / Edge-preserving blurs. Can be modulated by a static number or a dynamic signal.\"\n}).field(\"centerX\", z.number().min(0).max(1).default(.5), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Center X Signal\",\n\tdescription: \"The X coordinate of the blur center (0.0 to 1.0). Can be modulated by a static number or a dynamic signal.\"\n}).field(\"centerY\", z.number().min(0).max(1).default(.5), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Center Y Signal\",\n\tdescription: \"The Y coordinate of the blur center (0.0 to 1.0). Can be modulated by a static number or a dynamic signal.\"\n}).field(\"partialBlur\", z.boolean().default(false), {\n\tbindable: false,\n\tsignal: true,\n\tlabel: \"Partial Blur\",\n\tdescription: \"Blurs only a specific circular area around center point.\"\n}).field(\"radius\", z.number().min(.01).max(1).default(.3), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Partial Radius Signal\",\n\tdescription: \"Radius of the partial blur region (0.01 to 1.0). Can be modulated by a static number or a dynamic signal.\"\n}).field(\"radiusY\", z.number().min(0).max(1).default(0), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Partial Radius Y Signal\",\n\tdescription: \"Vertical radius of the partial blur region (0 to 1.0). 0 reuses the horizontal radius.\"\n}).field(\"shape\", z.enum([\n\t\"ellipse\",\n\t\"circle\",\n\t\"rect\"\n]).default(\"circle\"), {\n\tbindable: false,\n\tlabel: \"Partial Shape\",\n\tdescription: \"Shape of the partial blur region.\"\n}).build();\nconst BlurNodeConfigSchema = blurConfig.schema;\nconst BlurResultSchema = z.union([ImageResultSchema, VideoResultSchema]);\nconst BLUR_OUTPUT_TYPE_MAP = {\n\tVideo: \"Video\",\n\tLottie: \"Video\",\n\tGIF: \"GIF\",\n\tImage: \"Image\",\n\tSVG: \"Image\"\n};\n\n//#endregion\n//#region src/renderers/webgpu-renderer.ts\nconst WGSL_BLUR_UNIFORMS = `\nstruct BlurUniforms {\n\tdirX : f32,\n\tdirY : f32,\n\tstrength : f32,\n\tangle : f32,\n\n\tsigmaColor : f32,\n\tcenterX : f32,\n\tcenterY : f32,\n\tradius : f32,\n\n\tpartialBlur : f32,\n\thasStrengthSig : f32,\n\thasSigmaColorSig: f32,\n\thasCenterXSig : f32,\n\n\thasCenterYSig : f32,\n\thasRadiusSig : f32,\n\tradiusY : f32,\n\tshape : f32,\n};\n\n@group(0) @binding(0) var<uniform> u : BlurUniforms;\n@group(1) @binding(0) var tex : texture_2d<f32>;\n@group(1) @binding(1) var samp : sampler;\n@group(1) @binding(2) var origTex : texture_2d<f32>;\n\n@group(2) @binding(0) var strengthSigTex : texture_2d<f32>;\n@group(2) @binding(1) var sigmaColorSigTex : texture_2d<f32>;\n@group(2) @binding(2) var centerXSigTex : texture_2d<f32>;\n@group(2) @binding(3) var centerYSigTex : texture_2d<f32>;\n@group(2) @binding(4) var radiusSigTex : texture_2d<f32>;\n@group(2) @binding(5) var signalSamp : sampler;\n\nfn applyPartialBlur(blurred: vec4<f32>, uv: vec2<f32>, dimensions: vec2<f32>) -> vec4<f32> {\n\tif (u.partialBlur <= 0.5) {\n\t\treturn blurred;\n\t}\n\tlet orig = textureSampleLevel(origTex, samp, uv, 0.0);\n\tvar centerX = u.centerX;\n\tif (u.hasCenterXSig > 0.5) {\n\t\tcenterX = textureSampleLevel(centerXSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerY = u.centerY;\n\tif (u.hasCenterYSig > 0.5) {\n\t\tcenterY = textureSampleLevel(centerYSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar radiusX = u.radius;\n\tif (u.hasRadiusSig > 0.5) {\n\t\tradiusX = textureSampleLevel(radiusSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar radiusY = u.radiusY;\n\n\tvar mask = 0.0;\n\tif (radiusY <= 0.0) {\n\t\tlet aspect = dimensions.x / dimensions.y;\n\t\tlet diff = (uv - vec2<f32>(centerX, centerY)) * vec2<f32>(aspect, 1.0);\n\t\tlet dist = length(diff);\n\t\tlet feather = 0.05;\n\t\tmask = 1.0 - smoothstep(radiusX - feather, radiusX + feather, dist);\n\t} else {\n\t\tlet diff = abs(uv - vec2<f32>(centerX, centerY));\n\t\tif (u.shape > 0.5) {\n\t\t\tlet feather = 0.02;\n\t\t\tlet edgeX = 1.0 - smoothstep(radiusX - feather, radiusX + feather, diff.x);\n\t\t\tlet edgeY = 1.0 - smoothstep(radiusY - feather, radiusY + feather, diff.y);\n\t\t\tmask = edgeX * edgeY;\n\t\t} else {\n\t\t\tlet normDist = length(vec2<f32>(diff.x / max(1e-4, radiusX), diff.y / max(1e-4, radiusY)));\n\t\t\tlet feather = 0.08;\n\t\t\tmask = 1.0 - smoothstep(1.0 - feather, 1.0 + feather, normDist);\n\t\t}\n\t}\n\treturn mix(orig, blurred, mask);\n}\n`;\nconst GAUSSIAN_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet c = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar spatial_sigma = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tspatial_sigma = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (spatial_sigma <= 0.0) {\n\t\treturn applyPartialBlur(c, in.uv, dimensions);\n\t}\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet stepScale = max(1.0, spatial_sigma / 20.0);\n\tlet effectiveSigma = spatial_sigma / stepScale;\n\tlet radius = min(i32(ceil(3.0 * effectiveSigma)), 64);\n\t\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\t\n\tvar color = vec4<f32>(0.0);\n\tvar totalWeight = 0.0;\n\n\tfor (var i = -radius; i <= radius; i++) {\n\t\tlet offset = vec2<f32>(f32(i) * stepScale) * direction * texelSize;\n\t\tlet x = f32(i) * stepScale;\n\t\tlet weight = exp(-0.5 * (x / spatial_sigma) * (x / spatial_sigma));\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0) * weight;\n\t\ttotalWeight += weight;\n\t}\n\n\tlet blurred = color / totalWeight;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst BOX_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet c = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tlet radius = i32(round(strength));\n\tif (radius <= 0) {\n\t\treturn applyPartialBlur(c, in.uv, dimensions);\n\t}\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\t\n\tvar color = vec4<f32>(0.0);\n\tvar count = 0.0;\n\n\tfor (var i = -radius; i <= radius; i++) {\n\t\tlet offset = vec2<f32>(f32(i)) * direction * texelSize;\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t\tcount += 1.0;\n\t}\n\n\tlet blurred = color / count;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst MEDIAN_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\nstruct Val {\n\tluma : f32,\n\tcolor : vec4<f32>,\n};\n\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tlet radius = min(i32(round(strength)), 16);\n\tif (radius <= 0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet count = radius * 2 + 1;\n\tlet texelSize = 1.0 / dimensions;\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\n\tvar values : array<Val, 33>;\n\n\tfor (var i = 0; i < 33; i++) {\n\t\tif (i >= count) { break; }\n\t\tlet offset = vec2<f32>(f32(i - radius)) * direction * texelSize;\n\t\tlet col = textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t\tvalues[i] = Val(dot(col.rgb, vec3<f32>(0.299, 0.587, 0.114)), col);\n\t}\n\n\t// Selection sort\n\tfor (var i = 0; i < 33; i++) {\n\t\tif (i >= count - 1) { break; }\n\t\tvar minIdx = i;\n\t\tfor (var j = 0; j < 33; j++) {\n\t\t\tif (j <= i) { continue; }\n\t\t\tif (j >= count) { break; }\n\t\t\tif (values[j].luma < values[minIdx].luma) {\n\t\t\t\tminIdx = j;\n\t\t\t}\n\t\t}\n\t\tif (minIdx != i) {\n\t\t\tlet temp = values[i];\n\t\t\tvalues[i] = values[minIdx];\n\t\t\tvalues[minIdx] = temp;\n\t\t}\n\t}\n\n\tlet medianIdx = count / 2;\n\treturn applyPartialBlur(values[medianIdx].color, in.uv, dimensions);\n}\n`;\nconst MOTION_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet c = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (strength <= 0.0) {\n\t\treturn applyPartialBlur(c, in.uv, dimensions);\n\t}\n\n\tlet PI = 3.14159265359;\n\tlet angleRad = u.angle * PI / 180.0;\n\tlet dir = vec2<f32>(cos(angleRad), sin(angleRad));\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet step = dir * texelSize;\n\n\tlet numSamples = i32(clamp(strength, 1.0, 64.0));\n\tvar color = vec4<f32>(0.0);\n\t\n\tfor (var i = 0; i < 64; i++) {\n\t\tif (i >= numSamples) { break; }\n\t\tlet offset = f32(i - numSamples / 2) * step;\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t}\n\n\tlet blurred = color / f32(numSamples);\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst BILATERAL_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar spatial_sigma = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tspatial_sigma = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar sigmaColor = u.sigmaColor;\n\tif (u.hasSigmaColorSig > 0.5) {\n\t\tsigmaColor = textureSampleLevel(sigmaColorSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (spatial_sigma <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet radius = min(i32(ceil(3.0 * spatial_sigma)), 32);\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\t\n\tvar color = vec4<f32>(0.0);\n\tvar totalWeight = 0.0;\n\n\tfor (var i = -32; i <= 32; i++) {\n\t\tif (i < -radius || i > radius) { continue; }\n\t\tlet offset = vec2<f32>(f32(i)) * direction * texelSize;\n\t\tlet sampleCol = textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t\t\n\t\tlet spatialDistSq = f32(i * i);\n\t\tlet spatialWeight = exp(-0.5 * spatialDistSq / (spatial_sigma * spatial_sigma));\n\t\t\n\t\tlet diff = sampleCol - centerCol;\n\t\tlet colorDistSq = dot(diff, diff);\n\t\tlet rangeWeight = exp(-0.5 * colorDistSq / (sigmaColor * sigmaColor));\n\t\t\n\t\tlet weight = spatialWeight * rangeWeight;\n\t\tcolor += sampleCol * weight;\n\t\ttotalWeight += weight;\n\t}\n\n\tvar blurred = centerCol;\n\tif (totalWeight > 0.0) {\n\t\tblurred = color / totalWeight;\n\t}\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst EDGE_PRESERVING_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\t// Map strength (1-10) to radius (1-6)\n\tlet radius = i32(clamp(round(strength * 5.0 / 10.0) + 1.0, 1.0, 6.0));\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tlet texelSize = 1.0 / dimensions;\n\n\tvar m0 = vec3<f32>(0.0); var m1 = vec3<f32>(0.0); var m2 = vec3<f32>(0.0); var m3 = vec3<f32>(0.0);\n\tvar s0 = vec3<f32>(0.0); var s1 = vec3<f32>(0.0); var s2 = vec3<f32>(0.0); var s3 = vec3<f32>(0.0);\n\n\tlet n = f32((radius + 1) * (radius + 1));\n\n\tfor (var j = -6; j <= 6; j++) {\n\t\tif (j < -radius || j > radius) { continue; }\n\t\tfor (var i = -6; i <= 6; i++) {\n\t\t\tif (i < -radius || i > radius) { continue; }\n\t\t\t\n\t\t\tlet c = textureSampleLevel(tex, samp, in.uv + vec2<f32>(f32(i), f32(j)) * texelSize, 0.0).rgb;\n\t\t\t\n\t\t\tif (i <= 0 && j <= 0) {\n\t\t\t\tm0 += c;\n\t\t\t\ts0 += c * c;\n\t\t\t}\n\t\t\tif (i >= 0 && j <= 0) {\n\t\t\t\tm1 += c;\n\t\t\t\ts1 += c * c;\n\t\t\t}\n\t\t\tif (i <= 0 && j >= 0) {\n\t\t\t\tm2 += c;\n\t\t\t\ts2 += c * c;\n\t\t\t}\n\t\t\tif (i >= 0 && j >= 0) {\n\t\t\t\tm3 += c;\n\t\t\t\ts3 += c * c;\n\t\t\t}\n\t\t}\n\t}\n\n\tm0 = m0 / n; s0 = abs(s0 / n - m0 * m0);\n\tm1 = m1 / n; s1 = abs(s1 / n - m1 * m1);\n\tm2 = m2 / n; s2 = abs(s2 / n - m2 * m2);\n\tm3 = m3 / n; s3 = abs(s3 / n - m3 * m3);\n\n\tlet v0 = s0.r + s0.g + s0.b;\n\tlet v1 = s1.r + s1.g + s1.b;\n\tlet v2 = s2.r + s2.g + s2.b;\n\tlet v3 = s3.r + s3.g + s3.b;\n\n\tvar minVar = v0;\n\tvar finalCol = m0;\n\n\tif (v1 < minVar) {\n\t\tminVar = v1;\n\t\tfinalCol = m1;\n\t}\n\tif (v2 < minVar) {\n\t\tminVar = v2;\n\t\tfinalCol = m2;\n\t}\n\tif (v3 < minVar) {\n\t\tminVar = v3;\n\t\tfinalCol = m3;\n\t}\n\n\tlet blurred = vec4<f32>(finalCol, centerCol.a);\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst RADIAL_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerX = u.centerX;\n\tif (u.hasCenterXSig > 0.5) {\n\t\tcenterX = textureSampleLevel(centerXSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerY = u.centerY;\n\tif (u.hasCenterYSig > 0.5) {\n\t\tcenterY = textureSampleLevel(centerYSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (strength <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet center = vec2<f32>(centerX, centerY);\n\tlet toPixel = in.uv - center;\n\tlet dist = length(toPixel);\n\tif (dist <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet dir = vec2<f32>(-toPixel.y, toPixel.x);\n\tlet blurAmount = (strength / 100.0) * 0.1 * dist;\n\n\tlet numSamples = 24;\n\tvar color = vec4<f32>(0.0);\n\tlet step = dir * (blurAmount / f32(numSamples));\n\n\tfor (var i = 0; i < 24; i++) {\n\t\tlet offset = step * f32(i - 12);\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t}\n\n\tlet blurred = color / 24.0;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst ZOOM_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerX = u.centerX;\n\tif (u.hasCenterXSig > 0.5) {\n\t\tcenterX = textureSampleLevel(centerXSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerY = u.centerY;\n\tif (u.hasCenterYSig > 0.5) {\n\t\tcenterY = textureSampleLevel(centerYSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (strength <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet center = vec2<f32>(centerX, centerY);\n\tlet toPixel = in.uv - center;\n\tlet blurAmount = (strength / 100.0) * 0.15;\n\n\tlet numSamples = 24;\n\tvar color = vec4<f32>(0.0);\n\tlet step = toPixel * (blurAmount / f32(numSamples));\n\n\tfor (var i = 0; i < 24; i++) {\n\t\tlet offset = step * f32(i - 12);\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t}\n\n\tlet blurred = color / 24.0;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst getDeviceLayouts = perDevice((device) => ({\n\tblurUniformLayout: fragmentBindGroupLayout(device, [\"uniform\"]),\n\tsingleTextureLayout: fragmentBindGroupLayout(device, [\n\t\t\"texture\",\n\t\t\"sampler\",\n\t\t\"texture\"\n\t]),\n\tsignalTextureLayout: texturesLayout(device, 5),\n\tpipelineCache: /* @__PURE__ */ new Map()\n}));\nfunction getBlurResources(device, format, blurType) {\n\tconst layouts = getDeviceLayouts(device);\n\tconst cacheKey = `blur_${blurType}_${format}`;\n\tlet pipeline = layouts.pipelineCache.get(cacheKey);\n\tif (!pipeline) {\n\t\tlet code = GAUSSIAN_BLUR_SHADER;\n\t\tif (blurType === \"Box\") code = BOX_BLUR_SHADER;\n\t\telse if (blurType === \"Median\") code = MEDIAN_BLUR_SHADER;\n\t\telse if (blurType === \"Motion\") code = MOTION_BLUR_SHADER;\n\t\telse if (blurType === \"Bilateral\") code = BILATERAL_BLUR_SHADER;\n\t\telse if (blurType === \"Edge-preserving\") code = EDGE_PRESERVING_SHADER;\n\t\telse if (blurType === \"Radial\") code = RADIAL_BLUR_SHADER;\n\t\telse if (blurType === \"Zoom\") code = ZOOM_BLUR_SHADER;\n\t\tconst blurModule = device.createShaderModule({\n\t\t\tlabel: `blur_${blurType}_${format}.wgsl`,\n\t\t\tcode\n\t\t});\n\t\tpipeline = createFullscreenPipeline(device, {\n\t\t\tlabel: `BlurPipeline_${blurType}_${format}`,\n\t\t\tmodule: blurModule,\n\t\t\tlayouts: [\n\t\t\t\tlayouts.blurUniformLayout,\n\t\t\t\tlayouts.singleTextureLayout,\n\t\t\t\tlayouts.signalTextureLayout\n\t\t\t],\n\t\t\tformat\n\t\t});\n\t\tlayouts.pipelineCache.set(cacheKey, pipeline);\n\t}\n\treturn {\n\t\tblurPipeline: pipeline,\n\t\tblurUniformLayout: layouts.blurUniformLayout,\n\t\tsingleTextureLayout: layouts.singleTextureLayout,\n\t\tsignalTextureLayout: layouts.signalTextureLayout\n\t};\n}\nconst BlurWebGPURenderer = async (args) => {\n\tconst { ctx, encoder, pass, targetTexture, targetWidth, targetHeight, props } = args;\n\tconst frame = props.frame ?? 0;\n\tconst compositionFrame = props.compositionFrame ?? frame;\n\tconst fps = props.fps || 30;\n\tconst op = props.virtualMedia?.operation;\n\tif (op?.op !== \"Blur\" || !op) return;\n\tconst childMedia = props.virtualMedia.children?.[0];\n\tif (!childMedia) return;\n\tpass.end();\n\tconst blurData = new Float32Array(16);\n\tconst width = targetWidth;\n\tconst height = targetHeight;\n\tconst dpiScale = width / (props.containerWidth ?? ctx.surface.width / (typeof window !== \"undefined\" ? window.devicePixelRatio : 1));\n\tconst nodeId = op.inputs ? Object.keys(op.inputs)[0] : \"blur_node\";\n\tconst resolveBindable = (configKey, defaultValue, minVal, maxVal, scaleByDpi = false) => {\n\t\tconst handleId = op[`${configKey}HandleId`];\n\t\tconst input = handleId ? op.inputs?.[handleId] : null;\n\t\tconst hasSignal = !!(input?.connectionValid && (input.outputItem?.type === \"Signal\" || input.outputItem?.type === \"Numeric\"));\n\t\tconst sd = hasSignal && input?.outputItem?.data ? input.outputItem.data : null;\n\t\tlet val = defaultValue;\n\t\tlet hasStaticSig = false;\n\t\tlet gpuTextureView;\n\t\tif (!hasSignal) if (input?.connectionValid && input.outputItem?.type === \"Number\") {\n\t\t\tconst rawVal = Number(input.outputItem.data ?? defaultValue);\n\t\t\tval = Math.max(minVal, Math.min(maxVal, rawVal));\n\t\t\tif (scaleByDpi) val *= dpiScale;\n\t\t} else {\n\t\t\tconst unwrapped = unwrapFrameParam(op[configKey], compositionFrame, fps);\n\t\t\tif (unwrapped.gpuTextureView) {\n\t\t\t\tgpuTextureView = unwrapped.gpuTextureView;\n\t\t\t\thasStaticSig = true;\n\t\t\t}\n\t\t\tconst rawVal = unwrapped.value;\n\t\t\tconst numVal = Number(rawVal ?? defaultValue);\n\t\t\tval = Math.max(minVal, Math.min(maxVal, isNaN(numVal) ? defaultValue : numVal));\n\t\t\tif (scaleByDpi) val *= dpiScale;\n\t\t}\n\t\telse if (sd) {\n\t\t\tconst rawVal = Number(sd.offset ?? 0);\n\t\t\tval = Math.max(minVal, Math.min(maxVal, rawVal));\n\t\t\tif (scaleByDpi) val *= dpiScale;\n\t\t\thasStaticSig = true;\n\t\t}\n\t\treturn {\n\t\t\tval,\n\t\t\thasStaticSig,\n\t\t\tsd,\n\t\t\tgpuTextureView\n\t\t};\n\t};\n\tconst strengthRes = resolveBindable(\"strength\", 5, 0, MAX_BLUR, true);\n\tconst sigmaColorRes = resolveBindable(\"sigmaColor\", .1, .01, 1, false);\n\tconst centerXRes = resolveBindable(\"centerX\", .5, 0, 1, false);\n\tconst centerYRes = resolveBindable(\"centerY\", .5, 0, 1, false);\n\tconst radiusRes = resolveBindable(\"radius\", .3, .001, 1, false);\n\tconst radiusYRes = resolveBindable(\"radiusY\", 0, 0, 1, false);\n\tconst { texture: tmpTex } = await drawChildToTexture(args, childMedia);\n\tconst blurType = op.blurType ?? \"Gaussian\";\n\tconst isSeparable = blurType === \"Gaussian\" || blurType === \"Box\" || blurType === \"Median\" || blurType === \"Bilateral\";\n\tconst { blurPipeline: pipeline, blurUniformLayout: uLayout, singleTextureLayout: tLayout, signalTextureLayout: sigLayout } = getBlurResources(ctx.device, ctx.renderer.format, blurType);\n\tconst sampler = ctx.renderer.samplerCache.getSampler(ctx.device);\n\tconst { elapsedSeconds, durationSeconds } = signalTiming(props, frame, fps);\n\tfor (const bindableRes of [\n\t\tstrengthRes,\n\t\tsigmaColorRes,\n\t\tcenterXRes,\n\t\tcenterYRes,\n\t\tradiusRes\n\t]) if (bindableRes.hasStaticSig && bindableRes.sd) await signalRegistry.ensureAudioSourcesExtracted(ctx.device, bindableRes.sd, fps, props.renderId);\n\tconst signalContext = {\n\t\targs,\n\t\twidth,\n\t\theight,\n\t\tframe,\n\t\tfps,\n\t\telapsedSeconds,\n\t\tdurationSeconds\n\t};\n\tconst getSignalView = (res, suffix) => signalTextureView(signalContext, res, `${nodeId}_${suffix}`);\n\tconst strengthView = getSignalView(strengthRes, \"strength_sig\");\n\tconst sigmaColorView = getSignalView(sigmaColorRes, \"sigmacolor_sig\");\n\tconst centerXView = getSignalView(centerXRes, \"centerx_sig\");\n\tconst centerYView = getSignalView(centerYRes, \"centery_sig\");\n\tconst radiusView = getSignalView(radiusRes, \"radius_sig\");\n\tconst createBlurBindGroup1 = (inputTex, origTex) => ctx.device.createBindGroup({\n\t\tlayout: tLayout,\n\t\tentries: [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tresource: inputTex.createView()\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 1,\n\t\t\t\tresource: sampler\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 2,\n\t\t\t\tresource: origTex.createView()\n\t\t\t}\n\t\t]\n\t});\n\tconst isPartial = Boolean(op.partialBlur && op.partialBlur !== \"false\" && op.partialBlur !== 0);\n\tblurData[2] = strengthRes.val;\n\tblurData[3] = Number(op.angle ?? 0);\n\tblurData[4] = sigmaColorRes.val;\n\tblurData[5] = centerXRes.val;\n\tblurData[6] = centerYRes.val;\n\tblurData[7] = radiusRes.val;\n\tblurData[8] = isPartial ? 1 : 0;\n\tblurData[9] = strengthRes.hasStaticSig ? 1 : 0;\n\tblurData[10] = sigmaColorRes.hasStaticSig ? 1 : 0;\n\tblurData[11] = centerXRes.hasStaticSig ? 1 : 0;\n\tblurData[12] = centerYRes.hasStaticSig ? 1 : 0;\n\tblurData[13] = radiusRes.hasStaticSig ? 1 : 0;\n\tblurData[14] = radiusYRes.val;\n\tblurData[15] = op.shape === \"rect\" ? 1 : 0;\n\tlet outTex2;\n\tif (isSeparable) {\n\t\tconst outTex1 = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t\ttmpTex,\n\t\t\ttargetTexture,\n\t\t\t...props.excludeTextures || []\n\t\t]);\n\t\toutTex2 = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t\ttmpTex,\n\t\t\toutTex1,\n\t\t\ttargetTexture,\n\t\t\t...props.excludeTextures || []\n\t\t]);\n\t\tblurData[0] = 1;\n\t\tblurData[1] = 0;\n\t\tblurData[8] = 0;\n\t\tconst hBuffer = ctx.renderer.getTemporaryBuffer(blurData);\n\t\tdrawFullscreenPass(encoder, outTex1.createView(), pipeline, [\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: uLayout,\n\t\t\t\tentries: [{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tresource: { buffer: hBuffer }\n\t\t\t\t}]\n\t\t\t}),\n\t\t\tcreateBlurBindGroup1(tmpTex, tmpTex),\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: sigLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: strengthView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 1,\n\t\t\t\t\t\tresource: sigmaColorView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 2,\n\t\t\t\t\t\tresource: centerXView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 3,\n\t\t\t\t\t\tresource: centerYView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 4,\n\t\t\t\t\t\tresource: radiusView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 5,\n\t\t\t\t\t\tresource: sampler\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t})\n\t\t]);\n\t\tblurData[0] = 0;\n\t\tblurData[1] = 1;\n\t\tblurData[8] = isPartial ? 1 : 0;\n\t\tconst vBuffer = ctx.renderer.getTemporaryBuffer(blurData);\n\t\tdrawFullscreenPass(encoder, outTex2.createView(), pipeline, [\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: uLayout,\n\t\t\t\tentries: [{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tresource: { buffer: vBuffer }\n\t\t\t\t}]\n\t\t\t}),\n\t\t\tcreateBlurBindGroup1(outTex1, tmpTex),\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: sigLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: strengthView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 1,\n\t\t\t\t\t\tresource: sigmaColorView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 2,\n\t\t\t\t\t\tresource: centerXView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 3,\n\t\t\t\t\t\tresource: centerYView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 4,\n\t\t\t\t\t\tresource: radiusView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 5,\n\t\t\t\t\t\tresource: sampler\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t})\n\t\t]);\n\t} else {\n\t\toutTex2 = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t\ttmpTex,\n\t\t\ttargetTexture,\n\t\t\t...props.excludeTextures || []\n\t\t]);\n\t\tblurData[0] = 0;\n\t\tblurData[1] = 0;\n\t\tblurData[8] = isPartial ? 1 : 0;\n\t\tconst singleBuffer = ctx.renderer.getTemporaryBuffer(blurData);\n\t\tdrawFullscreenPass(encoder, outTex2.createView(), pipeline, [\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: uLayout,\n\t\t\t\tentries: [{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tresource: { buffer: singleBuffer }\n\t\t\t\t}]\n\t\t\t}),\n\t\t\tcreateBlurBindGroup1(tmpTex, tmpTex),\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: sigLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: strengthView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 1,\n\t\t\t\t\t\tresource: sigmaColorView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 2,\n\t\t\t\t\t\tresource: centerXView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 3,\n\t\t\t\t\t\tresource: centerYView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 4,\n\t\t\t\t\t\tresource: radiusView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 5,\n\t\t\t\t\t\tresource: sampler\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t})\n\t\t]);\n\t}\n\tdrawTextureToTarget(args, outTex2, op.opacity ?? 1);\n};\n\n//#endregion\n//#region src/renderers/index.ts\nvar renderers_default = defineRenderer({ WebGPURenderer: BlurWebGPURenderer });\n\n//#endregion\nexport { MAX_BLUR as a, BlurResultSchema as i, BLUR_OUTPUT_TYPE_MAP as n, blurConfig as o, BlurNodeConfigSchema as r, renderers_default as t };\n//# sourceMappingURL=renderers-rcrwEq-B.mjs.map"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderer-1NoJLLDa.mjs","names":[],"sources":["../../../nodes/node-blur/dist/renderers-rcrwEq-B.mjs"],"sourcesContent":["import { defineRenderer } from \"@framefields/node-sdk/renderer\";\nimport { ImageResultSchema, VideoResultSchema, configBuilder, unwrapFrameParam } from \"@framefields/node-sdk\";\nimport { FULLSCREEN_QUAD_WGSL, createFullscreenPipeline, drawChildToTexture, drawFullscreenPass, drawTextureToTarget, fragmentBindGroupLayout, perDevice, signalTextureView, signalTiming, texturesLayout } from \"@framefields/node-sdk/gpu\";\nimport { signalRegistry } from \"@framefields/webgpu-renderers\";\nimport { z } from \"zod\";\n\n//#region src/shared/config.ts\nconst MAX_BLUR = 100;\nconst blurConfig = configBuilder().field(\"blurType\", z.enum([\n\t\"Gaussian\",\n\t\"Box\",\n\t\"Median\",\n\t\"Motion\",\n\t\"Bilateral\",\n\t\"Edge-preserving\",\n\t\"Radial\",\n\t\"Zoom\"\n]).default(\"Gaussian\"), {\n\tbindable: false,\n\tlabel: \"Blur Type\"\n}).field(\"strength\", z.number().min(0).max(MAX_BLUR).default(5), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Strength Signal\",\n\tdescription: \"The intensity of the blur. 0 = no blur. Can be modulated by a static number or a dynamic signal.\"\n}).field(\"angle\", z.number().int().min(0).max(360).default(0), {\n\tbindable: false,\n\tsignal: true,\n\tlabel: \"Angle\",\n\tdescription: \"Direction of a Motion blur, in degrees.\"\n}).field(\"sigmaColor\", z.number().min(.01).max(1).default(.1), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Color Sigma Signal\",\n\tdescription: \"Sigma value for color space in Bilateral / Edge-preserving blurs. Can be modulated by a static number or a dynamic signal.\"\n}).field(\"centerX\", z.number().min(0).max(1).default(.5), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Center X Signal\",\n\tdescription: \"The X coordinate of the blur center (0.0 to 1.0). Can be modulated by a static number or a dynamic signal.\"\n}).field(\"centerY\", z.number().min(0).max(1).default(.5), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Center Y Signal\",\n\tdescription: \"The Y coordinate of the blur center (0.0 to 1.0). Can be modulated by a static number or a dynamic signal.\"\n}).field(\"partialBlur\", z.boolean().default(false), {\n\tbindable: false,\n\tsignal: true,\n\tlabel: \"Partial Blur\",\n\tdescription: \"Blurs only a specific circular area around center point.\"\n}).field(\"radius\", z.number().min(.01).max(1).default(.3), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Partial Radius Signal\",\n\tdescription: \"Radius of the partial blur region (0.01 to 1.0). Can be modulated by a static number or a dynamic signal.\"\n}).field(\"radiusY\", z.number().min(0).max(1).default(0), {\n\tbindable: true,\n\tdataTypes: [\"Number\", \"Signal\"],\n\tlabel: \"Partial Radius Y Signal\",\n\tdescription: \"Vertical radius of the partial blur region (0 to 1.0). 0 reuses the horizontal radius.\"\n}).field(\"shape\", z.enum([\n\t\"ellipse\",\n\t\"circle\",\n\t\"rect\"\n]).default(\"circle\"), {\n\tbindable: false,\n\tlabel: \"Partial Shape\",\n\tdescription: \"Shape of the partial blur region.\"\n}).build();\nconst BlurNodeConfigSchema = blurConfig.schema;\nconst BlurResultSchema = z.union([ImageResultSchema, VideoResultSchema]);\nconst BLUR_OUTPUT_TYPE_MAP = {\n\tVideo: \"Video\",\n\tLottie: \"Video\",\n\tGIF: \"GIF\",\n\tImage: \"Image\",\n\tSVG: \"Image\"\n};\n\n//#endregion\n//#region src/renderers/webgpu-renderer.ts\nconst WGSL_BLUR_UNIFORMS = `\nstruct BlurUniforms {\n\tdirX : f32,\n\tdirY : f32,\n\tstrength : f32,\n\tangle : f32,\n\n\tsigmaColor : f32,\n\tcenterX : f32,\n\tcenterY : f32,\n\tradius : f32,\n\n\tpartialBlur : f32,\n\thasStrengthSig : f32,\n\thasSigmaColorSig: f32,\n\thasCenterXSig : f32,\n\n\thasCenterYSig : f32,\n\thasRadiusSig : f32,\n\tradiusY : f32,\n\tshape : f32,\n};\n\n@group(0) @binding(0) var<uniform> u : BlurUniforms;\n@group(1) @binding(0) var tex : texture_2d<f32>;\n@group(1) @binding(1) var samp : sampler;\n@group(1) @binding(2) var origTex : texture_2d<f32>;\n\n@group(2) @binding(0) var strengthSigTex : texture_2d<f32>;\n@group(2) @binding(1) var sigmaColorSigTex : texture_2d<f32>;\n@group(2) @binding(2) var centerXSigTex : texture_2d<f32>;\n@group(2) @binding(3) var centerYSigTex : texture_2d<f32>;\n@group(2) @binding(4) var radiusSigTex : texture_2d<f32>;\n@group(2) @binding(5) var signalSamp : sampler;\n\nfn applyPartialBlur(blurred: vec4<f32>, uv: vec2<f32>, dimensions: vec2<f32>) -> vec4<f32> {\n\tif (u.partialBlur <= 0.5) {\n\t\treturn blurred;\n\t}\n\tlet orig = textureSampleLevel(origTex, samp, uv, 0.0);\n\tvar centerX = u.centerX;\n\tif (u.hasCenterXSig > 0.5) {\n\t\tcenterX = textureSampleLevel(centerXSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerY = u.centerY;\n\tif (u.hasCenterYSig > 0.5) {\n\t\tcenterY = textureSampleLevel(centerYSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar radiusX = u.radius;\n\tif (u.hasRadiusSig > 0.5) {\n\t\tradiusX = textureSampleLevel(radiusSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar radiusY = u.radiusY;\n\n\tvar mask = 0.0;\n\tif (radiusY <= 0.0) {\n\t\tlet aspect = dimensions.x / dimensions.y;\n\t\tlet diff = (uv - vec2<f32>(centerX, centerY)) * vec2<f32>(aspect, 1.0);\n\t\tlet dist = length(diff);\n\t\tlet feather = 0.05;\n\t\tmask = 1.0 - smoothstep(radiusX - feather, radiusX + feather, dist);\n\t} else {\n\t\tlet diff = abs(uv - vec2<f32>(centerX, centerY));\n\t\tif (u.shape > 0.5) {\n\t\t\tlet feather = 0.02;\n\t\t\tlet edgeX = 1.0 - smoothstep(radiusX - feather, radiusX + feather, diff.x);\n\t\t\tlet edgeY = 1.0 - smoothstep(radiusY - feather, radiusY + feather, diff.y);\n\t\t\tmask = edgeX * edgeY;\n\t\t} else {\n\t\t\tlet normDist = length(vec2<f32>(diff.x / max(1e-4, radiusX), diff.y / max(1e-4, radiusY)));\n\t\t\tlet feather = 0.08;\n\t\t\tmask = 1.0 - smoothstep(1.0 - feather, 1.0 + feather, normDist);\n\t\t}\n\t}\n\treturn mix(orig, blurred, mask);\n}\n`;\nconst GAUSSIAN_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet c = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar spatial_sigma = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tspatial_sigma = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (spatial_sigma <= 0.0) {\n\t\treturn applyPartialBlur(c, in.uv, dimensions);\n\t}\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet stepScale = max(1.0, spatial_sigma / 20.0);\n\tlet effectiveSigma = spatial_sigma / stepScale;\n\tlet radius = min(i32(ceil(3.0 * effectiveSigma)), 64);\n\t\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\t\n\tvar color = vec4<f32>(0.0);\n\tvar totalWeight = 0.0;\n\n\tfor (var i = -radius; i <= radius; i++) {\n\t\tlet offset = vec2<f32>(f32(i) * stepScale) * direction * texelSize;\n\t\tlet x = f32(i) * stepScale;\n\t\tlet weight = exp(-0.5 * (x / spatial_sigma) * (x / spatial_sigma));\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0) * weight;\n\t\ttotalWeight += weight;\n\t}\n\n\tlet blurred = color / totalWeight;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst BOX_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet c = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tlet radius = i32(round(strength));\n\tif (radius <= 0) {\n\t\treturn applyPartialBlur(c, in.uv, dimensions);\n\t}\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\t\n\tvar color = vec4<f32>(0.0);\n\tvar count = 0.0;\n\n\tfor (var i = -radius; i <= radius; i++) {\n\t\tlet offset = vec2<f32>(f32(i)) * direction * texelSize;\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t\tcount += 1.0;\n\t}\n\n\tlet blurred = color / count;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst MEDIAN_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\nstruct Val {\n\tluma : f32,\n\tcolor : vec4<f32>,\n};\n\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tlet radius = min(i32(round(strength)), 16);\n\tif (radius <= 0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet count = radius * 2 + 1;\n\tlet texelSize = 1.0 / dimensions;\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\n\tvar values : array<Val, 33>;\n\n\tfor (var i = 0; i < 33; i++) {\n\t\tif (i >= count) { break; }\n\t\tlet offset = vec2<f32>(f32(i - radius)) * direction * texelSize;\n\t\tlet col = textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t\tvalues[i] = Val(dot(col.rgb, vec3<f32>(0.299, 0.587, 0.114)), col);\n\t}\n\n\t// Selection sort\n\tfor (var i = 0; i < 33; i++) {\n\t\tif (i >= count - 1) { break; }\n\t\tvar minIdx = i;\n\t\tfor (var j = 0; j < 33; j++) {\n\t\t\tif (j <= i) { continue; }\n\t\t\tif (j >= count) { break; }\n\t\t\tif (values[j].luma < values[minIdx].luma) {\n\t\t\t\tminIdx = j;\n\t\t\t}\n\t\t}\n\t\tif (minIdx != i) {\n\t\t\tlet temp = values[i];\n\t\t\tvalues[i] = values[minIdx];\n\t\t\tvalues[minIdx] = temp;\n\t\t}\n\t}\n\n\tlet medianIdx = count / 2;\n\treturn applyPartialBlur(values[medianIdx].color, in.uv, dimensions);\n}\n`;\nconst MOTION_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet c = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (strength <= 0.0) {\n\t\treturn applyPartialBlur(c, in.uv, dimensions);\n\t}\n\n\tlet PI = 3.14159265359;\n\tlet angleRad = u.angle * PI / 180.0;\n\tlet dir = vec2<f32>(cos(angleRad), sin(angleRad));\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet step = dir * texelSize;\n\n\tlet numSamples = i32(clamp(strength, 1.0, 64.0));\n\tvar color = vec4<f32>(0.0);\n\t\n\tfor (var i = 0; i < 64; i++) {\n\t\tif (i >= numSamples) { break; }\n\t\tlet offset = f32(i - numSamples / 2) * step;\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t}\n\n\tlet blurred = color / f32(numSamples);\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst BILATERAL_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar spatial_sigma = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tspatial_sigma = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar sigmaColor = u.sigmaColor;\n\tif (u.hasSigmaColorSig > 0.5) {\n\t\tsigmaColor = textureSampleLevel(sigmaColorSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (spatial_sigma <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet texelSize = 1.0 / dimensions;\n\tlet radius = min(i32(ceil(3.0 * spatial_sigma)), 32);\n\tlet direction = vec2<f32>(u.dirX, u.dirY);\n\t\n\tvar color = vec4<f32>(0.0);\n\tvar totalWeight = 0.0;\n\n\tfor (var i = -32; i <= 32; i++) {\n\t\tif (i < -radius || i > radius) { continue; }\n\t\tlet offset = vec2<f32>(f32(i)) * direction * texelSize;\n\t\tlet sampleCol = textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t\t\n\t\tlet spatialDistSq = f32(i * i);\n\t\tlet spatialWeight = exp(-0.5 * spatialDistSq / (spatial_sigma * spatial_sigma));\n\t\t\n\t\tlet diff = sampleCol - centerCol;\n\t\tlet colorDistSq = dot(diff, diff);\n\t\tlet rangeWeight = exp(-0.5 * colorDistSq / (sigmaColor * sigmaColor));\n\t\t\n\t\tlet weight = spatialWeight * rangeWeight;\n\t\tcolor += sampleCol * weight;\n\t\ttotalWeight += weight;\n\t}\n\n\tvar blurred = centerCol;\n\tif (totalWeight > 0.0) {\n\t\tblurred = color / totalWeight;\n\t}\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst EDGE_PRESERVING_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\t// Map strength (1-10) to radius (1-6)\n\tlet radius = i32(clamp(round(strength * 5.0 / 10.0) + 1.0, 1.0, 6.0));\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tlet texelSize = 1.0 / dimensions;\n\n\tvar m0 = vec3<f32>(0.0); var m1 = vec3<f32>(0.0); var m2 = vec3<f32>(0.0); var m3 = vec3<f32>(0.0);\n\tvar s0 = vec3<f32>(0.0); var s1 = vec3<f32>(0.0); var s2 = vec3<f32>(0.0); var s3 = vec3<f32>(0.0);\n\n\tlet n = f32((radius + 1) * (radius + 1));\n\n\tfor (var j = -6; j <= 6; j++) {\n\t\tif (j < -radius || j > radius) { continue; }\n\t\tfor (var i = -6; i <= 6; i++) {\n\t\t\tif (i < -radius || i > radius) { continue; }\n\t\t\t\n\t\t\tlet c = textureSampleLevel(tex, samp, in.uv + vec2<f32>(f32(i), f32(j)) * texelSize, 0.0).rgb;\n\t\t\t\n\t\t\tif (i <= 0 && j <= 0) {\n\t\t\t\tm0 += c;\n\t\t\t\ts0 += c * c;\n\t\t\t}\n\t\t\tif (i >= 0 && j <= 0) {\n\t\t\t\tm1 += c;\n\t\t\t\ts1 += c * c;\n\t\t\t}\n\t\t\tif (i <= 0 && j >= 0) {\n\t\t\t\tm2 += c;\n\t\t\t\ts2 += c * c;\n\t\t\t}\n\t\t\tif (i >= 0 && j >= 0) {\n\t\t\t\tm3 += c;\n\t\t\t\ts3 += c * c;\n\t\t\t}\n\t\t}\n\t}\n\n\tm0 = m0 / n; s0 = abs(s0 / n - m0 * m0);\n\tm1 = m1 / n; s1 = abs(s1 / n - m1 * m1);\n\tm2 = m2 / n; s2 = abs(s2 / n - m2 * m2);\n\tm3 = m3 / n; s3 = abs(s3 / n - m3 * m3);\n\n\tlet v0 = s0.r + s0.g + s0.b;\n\tlet v1 = s1.r + s1.g + s1.b;\n\tlet v2 = s2.r + s2.g + s2.b;\n\tlet v3 = s3.r + s3.g + s3.b;\n\n\tvar minVar = v0;\n\tvar finalCol = m0;\n\n\tif (v1 < minVar) {\n\t\tminVar = v1;\n\t\tfinalCol = m1;\n\t}\n\tif (v2 < minVar) {\n\t\tminVar = v2;\n\t\tfinalCol = m2;\n\t}\n\tif (v3 < minVar) {\n\t\tminVar = v3;\n\t\tfinalCol = m3;\n\t}\n\n\tlet blurred = vec4<f32>(finalCol, centerCol.a);\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst RADIAL_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerX = u.centerX;\n\tif (u.hasCenterXSig > 0.5) {\n\t\tcenterX = textureSampleLevel(centerXSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerY = u.centerY;\n\tif (u.hasCenterYSig > 0.5) {\n\t\tcenterY = textureSampleLevel(centerYSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (strength <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet center = vec2<f32>(centerX, centerY);\n\tlet toPixel = in.uv - center;\n\tlet dist = length(toPixel);\n\tif (dist <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet dir = vec2<f32>(-toPixel.y, toPixel.x);\n\tlet blurAmount = (strength / 100.0) * 0.1 * dist;\n\n\tlet numSamples = 24;\n\tvar color = vec4<f32>(0.0);\n\tlet step = dir * (blurAmount / f32(numSamples));\n\n\tfor (var i = 0; i < 24; i++) {\n\t\tlet offset = step * f32(i - 12);\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t}\n\n\tlet blurred = color / 24.0;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst ZOOM_BLUR_SHADER = `\n${WGSL_BLUR_UNIFORMS}\n${FULLSCREEN_QUAD_WGSL}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet centerCol = textureSampleLevel(tex, samp, in.uv, 0.0);\n\tvar strength = u.strength;\n\tif (u.hasStrengthSig > 0.5) {\n\t\tstrength = textureSampleLevel(strengthSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerX = u.centerX;\n\tif (u.hasCenterXSig > 0.5) {\n\t\tcenterX = textureSampleLevel(centerXSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\tvar centerY = u.centerY;\n\tif (u.hasCenterYSig > 0.5) {\n\t\tcenterY = textureSampleLevel(centerYSigTex, signalSamp, vec2<f32>(0.5, 0.5), 0.0).r;\n\t}\n\n\tlet dimensions = vec2<f32>(textureDimensions(tex));\n\tif (strength <= 0.0) {\n\t\treturn applyPartialBlur(centerCol, in.uv, dimensions);\n\t}\n\n\tlet center = vec2<f32>(centerX, centerY);\n\tlet toPixel = in.uv - center;\n\tlet blurAmount = (strength / 100.0) * 0.15;\n\n\tlet numSamples = 24;\n\tvar color = vec4<f32>(0.0);\n\tlet step = toPixel * (blurAmount / f32(numSamples));\n\n\tfor (var i = 0; i < 24; i++) {\n\t\tlet offset = step * f32(i - 12);\n\t\tcolor += textureSampleLevel(tex, samp, in.uv + offset, 0.0);\n\t}\n\n\tlet blurred = color / 24.0;\n\treturn applyPartialBlur(blurred, in.uv, dimensions);\n}\n`;\nconst getDeviceLayouts = perDevice((device) => ({\n\tblurUniformLayout: fragmentBindGroupLayout(device, [\"uniform\"]),\n\tsingleTextureLayout: fragmentBindGroupLayout(device, [\n\t\t\"texture\",\n\t\t\"sampler\",\n\t\t\"texture\"\n\t]),\n\tsignalTextureLayout: texturesLayout(device, 5),\n\tpipelineCache: /* @__PURE__ */ new Map()\n}));\nfunction getBlurResources(device, format, blurType) {\n\tconst layouts = getDeviceLayouts(device);\n\tconst cacheKey = `blur_${blurType}_${format}`;\n\tlet pipeline = layouts.pipelineCache.get(cacheKey);\n\tif (!pipeline) {\n\t\tlet code = GAUSSIAN_BLUR_SHADER;\n\t\tif (blurType === \"Box\") code = BOX_BLUR_SHADER;\n\t\telse if (blurType === \"Median\") code = MEDIAN_BLUR_SHADER;\n\t\telse if (blurType === \"Motion\") code = MOTION_BLUR_SHADER;\n\t\telse if (blurType === \"Bilateral\") code = BILATERAL_BLUR_SHADER;\n\t\telse if (blurType === \"Edge-preserving\") code = EDGE_PRESERVING_SHADER;\n\t\telse if (blurType === \"Radial\") code = RADIAL_BLUR_SHADER;\n\t\telse if (blurType === \"Zoom\") code = ZOOM_BLUR_SHADER;\n\t\tconst blurModule = device.createShaderModule({\n\t\t\tlabel: `blur_${blurType}_${format}.wgsl`,\n\t\t\tcode\n\t\t});\n\t\tpipeline = createFullscreenPipeline(device, {\n\t\t\tlabel: `BlurPipeline_${blurType}_${format}`,\n\t\t\tmodule: blurModule,\n\t\t\tlayouts: [\n\t\t\t\tlayouts.blurUniformLayout,\n\t\t\t\tlayouts.singleTextureLayout,\n\t\t\t\tlayouts.signalTextureLayout\n\t\t\t],\n\t\t\tformat\n\t\t});\n\t\tlayouts.pipelineCache.set(cacheKey, pipeline);\n\t}\n\treturn {\n\t\tblurPipeline: pipeline,\n\t\tblurUniformLayout: layouts.blurUniformLayout,\n\t\tsingleTextureLayout: layouts.singleTextureLayout,\n\t\tsignalTextureLayout: layouts.signalTextureLayout\n\t};\n}\nconst BlurWebGPURenderer = async (args) => {\n\tconst { ctx, encoder, pass, targetTexture, targetWidth, targetHeight, props } = args;\n\tconst frame = props.frame ?? 0;\n\tconst compositionFrame = props.compositionFrame ?? frame;\n\tconst fps = props.fps || 30;\n\tconst op = props.virtualMedia?.operation;\n\tif (op?.op !== \"Blur\" || !op) return;\n\tconst childMedia = props.virtualMedia.children?.[0];\n\tif (!childMedia) return;\n\tpass.end();\n\tconst blurData = new Float32Array(16);\n\tconst width = targetWidth;\n\tconst height = targetHeight;\n\tconst dpiScale = width / (props.containerWidth ?? ctx.surface.width / (typeof window !== \"undefined\" ? window.devicePixelRatio : 1));\n\tconst nodeId = op.inputs ? Object.keys(op.inputs)[0] : \"blur_node\";\n\tconst resolveBindable = (configKey, defaultValue, minVal, maxVal, scaleByDpi = false) => {\n\t\tconst handleId = op[`${configKey}HandleId`];\n\t\tconst input = handleId ? op.inputs?.[handleId] : null;\n\t\tconst hasSignal = !!(input?.connectionValid && (input.outputItem?.type === \"Signal\" || input.outputItem?.type === \"Numeric\"));\n\t\tconst sd = hasSignal && input?.outputItem?.data ? input.outputItem.data : null;\n\t\tlet val = defaultValue;\n\t\tlet hasStaticSig = false;\n\t\tlet gpuTextureView;\n\t\tif (!hasSignal) if (input?.connectionValid && input.outputItem?.type === \"Number\") {\n\t\t\tconst rawVal = Number(input.outputItem.data ?? defaultValue);\n\t\t\tval = Math.max(minVal, Math.min(maxVal, rawVal));\n\t\t\tif (scaleByDpi) val *= dpiScale;\n\t\t} else {\n\t\t\tconst unwrapped = unwrapFrameParam(op[configKey], compositionFrame, fps);\n\t\t\tif (unwrapped.gpuTextureView) {\n\t\t\t\tgpuTextureView = unwrapped.gpuTextureView;\n\t\t\t\thasStaticSig = true;\n\t\t\t}\n\t\t\tconst rawVal = unwrapped.value;\n\t\t\tconst numVal = Number(rawVal ?? defaultValue);\n\t\t\tval = Math.max(minVal, Math.min(maxVal, isNaN(numVal) ? defaultValue : numVal));\n\t\t\tif (scaleByDpi) val *= dpiScale;\n\t\t}\n\t\telse if (sd) {\n\t\t\tconst rawVal = Number(sd.offset ?? 0);\n\t\t\tval = Math.max(minVal, Math.min(maxVal, rawVal));\n\t\t\tif (scaleByDpi) val *= dpiScale;\n\t\t\thasStaticSig = true;\n\t\t}\n\t\treturn {\n\t\t\tval,\n\t\t\thasStaticSig,\n\t\t\tsd,\n\t\t\tgpuTextureView\n\t\t};\n\t};\n\tconst strengthRes = resolveBindable(\"strength\", 5, 0, MAX_BLUR, true);\n\tconst sigmaColorRes = resolveBindable(\"sigmaColor\", .1, .01, 1, false);\n\tconst centerXRes = resolveBindable(\"centerX\", .5, 0, 1, false);\n\tconst centerYRes = resolveBindable(\"centerY\", .5, 0, 1, false);\n\tconst radiusRes = resolveBindable(\"radius\", .3, .001, 1, false);\n\tconst radiusYRes = resolveBindable(\"radiusY\", 0, 0, 1, false);\n\tconst { texture: tmpTex } = await drawChildToTexture(args, childMedia);\n\tconst blurType = op.blurType ?? \"Gaussian\";\n\tconst isSeparable = blurType === \"Gaussian\" || blurType === \"Box\" || blurType === \"Median\" || blurType === \"Bilateral\";\n\tconst { blurPipeline: pipeline, blurUniformLayout: uLayout, singleTextureLayout: tLayout, signalTextureLayout: sigLayout } = getBlurResources(ctx.device, ctx.renderer.format, blurType);\n\tconst sampler = ctx.renderer.samplerCache.getSampler(ctx.device);\n\tconst { elapsedSeconds, durationSeconds } = signalTiming(props, frame, fps);\n\tfor (const bindableRes of [\n\t\tstrengthRes,\n\t\tsigmaColorRes,\n\t\tcenterXRes,\n\t\tcenterYRes,\n\t\tradiusRes\n\t]) if (bindableRes.hasStaticSig && bindableRes.sd) await signalRegistry.ensureAudioSourcesExtracted(ctx.device, bindableRes.sd, fps, props.renderId);\n\tconst signalContext = {\n\t\targs,\n\t\twidth,\n\t\theight,\n\t\tframe,\n\t\tfps,\n\t\telapsedSeconds,\n\t\tdurationSeconds\n\t};\n\tconst getSignalView = (res, suffix) => signalTextureView(signalContext, res, `${nodeId}_${suffix}`);\n\tconst strengthView = getSignalView(strengthRes, \"strength_sig\");\n\tconst sigmaColorView = getSignalView(sigmaColorRes, \"sigmacolor_sig\");\n\tconst centerXView = getSignalView(centerXRes, \"centerx_sig\");\n\tconst centerYView = getSignalView(centerYRes, \"centery_sig\");\n\tconst radiusView = getSignalView(radiusRes, \"radius_sig\");\n\tconst createBlurBindGroup1 = (inputTex, origTex) => ctx.device.createBindGroup({\n\t\tlayout: tLayout,\n\t\tentries: [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tresource: inputTex.createView()\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 1,\n\t\t\t\tresource: sampler\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 2,\n\t\t\t\tresource: origTex.createView()\n\t\t\t}\n\t\t]\n\t});\n\tconst isPartial = Boolean(op.partialBlur && op.partialBlur !== \"false\" && op.partialBlur !== 0);\n\tblurData[2] = strengthRes.val;\n\tblurData[3] = Number(op.angle ?? 0);\n\tblurData[4] = sigmaColorRes.val;\n\tblurData[5] = centerXRes.val;\n\tblurData[6] = centerYRes.val;\n\tblurData[7] = radiusRes.val;\n\tblurData[8] = isPartial ? 1 : 0;\n\tblurData[9] = strengthRes.hasStaticSig ? 1 : 0;\n\tblurData[10] = sigmaColorRes.hasStaticSig ? 1 : 0;\n\tblurData[11] = centerXRes.hasStaticSig ? 1 : 0;\n\tblurData[12] = centerYRes.hasStaticSig ? 1 : 0;\n\tblurData[13] = radiusRes.hasStaticSig ? 1 : 0;\n\tblurData[14] = radiusYRes.val;\n\tblurData[15] = op.shape === \"rect\" ? 1 : 0;\n\tlet outTex2;\n\tif (isSeparable) {\n\t\tconst outTex1 = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t\ttmpTex,\n\t\t\ttargetTexture,\n\t\t\t...props.excludeTextures || []\n\t\t]);\n\t\toutTex2 = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t\ttmpTex,\n\t\t\toutTex1,\n\t\t\ttargetTexture,\n\t\t\t...props.excludeTextures || []\n\t\t]);\n\t\tblurData[0] = 1;\n\t\tblurData[1] = 0;\n\t\tblurData[8] = 0;\n\t\tconst hBuffer = ctx.renderer.getTemporaryBuffer(blurData);\n\t\tdrawFullscreenPass(encoder, outTex1.createView(), pipeline, [\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: uLayout,\n\t\t\t\tentries: [{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tresource: { buffer: hBuffer }\n\t\t\t\t}]\n\t\t\t}),\n\t\t\tcreateBlurBindGroup1(tmpTex, tmpTex),\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: sigLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: strengthView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 1,\n\t\t\t\t\t\tresource: sigmaColorView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 2,\n\t\t\t\t\t\tresource: centerXView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 3,\n\t\t\t\t\t\tresource: centerYView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 4,\n\t\t\t\t\t\tresource: radiusView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 5,\n\t\t\t\t\t\tresource: sampler\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t})\n\t\t]);\n\t\tblurData[0] = 0;\n\t\tblurData[1] = 1;\n\t\tblurData[8] = isPartial ? 1 : 0;\n\t\tconst vBuffer = ctx.renderer.getTemporaryBuffer(blurData);\n\t\tdrawFullscreenPass(encoder, outTex2.createView(), pipeline, [\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: uLayout,\n\t\t\t\tentries: [{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tresource: { buffer: vBuffer }\n\t\t\t\t}]\n\t\t\t}),\n\t\t\tcreateBlurBindGroup1(outTex1, tmpTex),\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: sigLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: strengthView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 1,\n\t\t\t\t\t\tresource: sigmaColorView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 2,\n\t\t\t\t\t\tresource: centerXView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 3,\n\t\t\t\t\t\tresource: centerYView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 4,\n\t\t\t\t\t\tresource: radiusView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 5,\n\t\t\t\t\t\tresource: sampler\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t})\n\t\t]);\n\t} else {\n\t\toutTex2 = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t\ttmpTex,\n\t\t\ttargetTexture,\n\t\t\t...props.excludeTextures || []\n\t\t]);\n\t\tblurData[0] = 0;\n\t\tblurData[1] = 0;\n\t\tblurData[8] = isPartial ? 1 : 0;\n\t\tconst singleBuffer = ctx.renderer.getTemporaryBuffer(blurData);\n\t\tdrawFullscreenPass(encoder, outTex2.createView(), pipeline, [\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: uLayout,\n\t\t\t\tentries: [{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tresource: { buffer: singleBuffer }\n\t\t\t\t}]\n\t\t\t}),\n\t\t\tcreateBlurBindGroup1(tmpTex, tmpTex),\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: sigLayout,\n\t\t\t\tentries: [\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 0,\n\t\t\t\t\t\tresource: strengthView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 1,\n\t\t\t\t\t\tresource: sigmaColorView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 2,\n\t\t\t\t\t\tresource: centerXView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 3,\n\t\t\t\t\t\tresource: centerYView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 4,\n\t\t\t\t\t\tresource: radiusView\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tbinding: 5,\n\t\t\t\t\t\tresource: sampler\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t})\n\t\t]);\n\t}\n\tdrawTextureToTarget(args, outTex2, op.opacity ?? 1);\n};\n\n//#endregion\n//#region src/renderers/index.ts\nvar renderers_default = defineRenderer({ WebGPURenderer: BlurWebGPURenderer });\n\n//#endregion\nexport { MAX_BLUR as a, BlurResultSchema as i, BLUR_OUTPUT_TYPE_MAP as n, blurConfig as o, BlurNodeConfigSchema as r, renderers_default as t };\n//# sourceMappingURL=renderers-rcrwEq-B.mjs.map"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn as resolveNumber } from "./dist-D021IFfp.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { g as WebGPUAudioProcessor } from "./dist-
|
|
2
|
+
import { i as isHandleConnected, n as defineRenderer } from "./renderer-BgmO1Dzw.mjs";
|
|
3
|
+
import { g as WebGPUAudioProcessor } from "./dist-BuirkW-f.mjs";
|
|
4
4
|
|
|
5
5
|
//#region ../../nodes/node-audio-reverb/dist/renderers-DCFjIwTB.mjs
|
|
6
6
|
const PARAM_ORDER = [
|
|
@@ -370,4 +370,4 @@ var renderers_default = defineRenderer({ audioProcessor: reverbAudioProcessor })
|
|
|
370
370
|
|
|
371
371
|
//#endregion
|
|
372
372
|
export { renderers_default as t };
|
|
373
|
-
//# sourceMappingURL=renderer-
|
|
373
|
+
//# sourceMappingURL=renderer-8_d6YNnE.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer-zdpn1Hkk.mjs","names":[],"sources":["../../../nodes/node-audio-reverb/dist/renderers-DCFjIwTB.mjs"],"sourcesContent":["import { defineRenderer } from \"@framefields/node-sdk/renderer\";\nimport { isHandleConnected, resolveNumber } from \"@framefields/node-sdk\";\nimport { WebGPUAudioProcessor } from \"@framefields/webgpu-renderers\";\n\n//#region src/renderers/audio-processor.ts\nconst PARAM_ORDER = [\n\t\"roomSize\",\n\t\"damping\",\n\t\"wet\",\n\t\"dry\",\n\t\"width\"\n];\nconst REVERB_SHADER_TEMPLATE = () => `\nstruct Uniforms {\n sampleRate : f32,\n roomSize : f32,\n damping : f32,\n wet : f32,\n dry : f32,\n width : f32,\n hasRoomSizeSig : f32,\n hasDampingSig : f32,\n hasWetSig : f32,\n hasDrySig : f32,\n hasWidthSig : f32,\n baseTime : f32,\n frame : f32,\n numSamples : f32,\n numChannels : f32,\n pad1 : f32,\n};\n\n@group(0) @binding(0) var<uniform> u : Uniforms;\n@group(0) @binding(1) var<storage, read> inputChannels : array<f32>;\n@group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;\n@group(0) @binding(3) var<storage, read_write> state : array<f32>;\n@group(0) @binding(4) var<storage, read> roomSizeSignal : array<f32>;\n@group(0) @binding(5) var<storage, read> dampingSignal : array<f32>;\n@group(0) @binding(6) var<storage, read> wetSignal : array<f32>;\n@group(0) @binding(7) var<storage, read> drySignal : array<f32>;\n@group(0) @binding(8) var<storage, read> widthSignal : array<f32>;\n\nfn tanh(x: f32) -> f32 {\n let exp2x = exp(2.0f * x);\n return (exp2x - 1.0f) / (exp2x + 1.0f);\n}\n\nfn is_nan_or_inf(v: f32) -> bool {\n return (v != v) || (abs(v) > 3.402823466e+38f);\n}\n\n@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n if (gid.x != 0u) { return; }\n\n let numSamples = u32(u.numSamples);\n let numChannels = u32(u.numChannels);\n let fixedgain: f32 = 0.015f;\n\n var bufIndices: array<u32, 24>;\n var filterStores: array<f32, 24>;\n var sizes: array<u32, 24>;\n var startOffsets: array<u32, 24>;\n\n for (var f = 0u; f < 24u; f = f + 1u) {\n let metaOffset = f * 4u;\n sizes[f] = u32(state[metaOffset]);\n startOffsets[f] = u32(state[metaOffset + 1u]);\n bufIndices[f] = u32(state[metaOffset + 2u]);\n var fs = state[metaOffset + 3u];\n if (is_nan_or_inf(fs)) { fs = 0.0f; }\n filterStores[f] = fs;\n }\n\n if (numChannels == 1u) {\n for (var index = 0u; index < numSamples; index = index + 1u) {\n // Dynamic parameter signal evaluation\n var effRoomSize = u.roomSize;\n if (u.hasRoomSizeSig > 0.5f) { effRoomSize = roomSizeSignal[index]; }\n effRoomSize = clamp(effRoomSize, 0.0f, 0.98f);\n\n var effDamping = u.damping;\n if (u.hasDampingSig > 0.5f) { effDamping = dampingSignal[index]; }\n effDamping = clamp(effDamping, 0.0f, 1.0f) * 0.4f;\n\n var effWet = u.wet;\n if (u.hasWetSig > 0.5f) { effWet = wetSignal[index]; }\n effWet = clamp(effWet, 0.0f, 1.0f) * 3.0f;\n\n var effDry = u.dry;\n if (u.hasDrySig > 0.5f) { effDry = drySignal[index]; }\n effDry = clamp(effDry, 0.0f, 2.0f);\n\n var x = inputChannels[index];\n if (is_nan_or_inf(x)) { x = 0.0f; }\n let inputVal = x * fixedgain;\n\n var combSum = 0.0f;\n for (var j = 0u; j < 8u; j = j + 1u) {\n let size = sizes[j];\n let startOffset = startOffsets[j];\n let bufferIndex = bufIndices[j];\n var fs = filterStores[j];\n\n var output = state[startOffset + bufferIndex];\n if (is_nan_or_inf(output)) { output = 0.0f; }\n\n fs = output * (1.0f - effDamping) + fs * effDamping;\n var nextVal = inputVal + fs * effRoomSize;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n bufIndices[j] = (bufferIndex + 1u) % size;\n filterStores[j] = fs;\n combSum = combSum + output;\n }\n\n var revOut = combSum;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let f = 8u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n\n var bufOut = state[startOffset + bufferIndex];\n if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }\n\n var nextVal = revOut + bufOut * 0.5f;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n let output = bufOut - revOut;\n bufIndices[f] = (bufferIndex + 1u) % size;\n revOut = output;\n }\n\n var outVal = x * effDry + revOut * effWet;\n if (is_nan_or_inf(outVal)) { outVal = 0.0f; }\n\n if (outVal > 1.0f) { outVal = 1.0f; }\n else if (outVal < -1.0f) { outVal = -1.0f; }\n else if (outVal > 0.9f) { outVal = 0.9f + 0.1f * tanh((outVal - 0.9f) / 0.1f); }\n else if (outVal < -0.9f) { outVal = -0.9f + 0.1f * tanh((outVal + 0.9f) / 0.1f); }\n\n outputChannels[index] = outVal;\n }\n } else {\n for (var index = 0u; index < numSamples; index = index + 1u) {\n // Dynamic parameter signal evaluation\n var effRoomSize = u.roomSize;\n if (u.hasRoomSizeSig > 0.5f) { effRoomSize = roomSizeSignal[index]; }\n effRoomSize = clamp(effRoomSize, 0.0f, 0.98f);\n\n var effDamping = u.damping;\n if (u.hasDampingSig > 0.5f) { effDamping = dampingSignal[index]; }\n effDamping = clamp(effDamping, 0.0f, 1.0f) * 0.4f;\n\n var effWet = u.wet;\n if (u.hasWetSig > 0.5f) { effWet = wetSignal[index]; }\n effWet = clamp(effWet, 0.0f, 1.0f) * 3.0f;\n\n var effDry = u.dry;\n if (u.hasDrySig > 0.5f) { effDry = drySignal[index]; }\n effDry = clamp(effDry, 0.0f, 2.0f);\n\n var effWidth = u.width;\n if (u.hasWidthSig > 0.5f) { effWidth = widthSignal[index]; }\n effWidth = clamp(effWidth, 0.0f, 1.0f);\n\n let wet1 = effWet * (effWidth / 2.0f + 0.5f);\n let wet2 = effWet * ((1.0f - effWidth) / 2.0f);\n\n var inputL = inputChannels[index];\n if (is_nan_or_inf(inputL)) { inputL = 0.0f; }\n var inputR = inputChannels[numSamples + index];\n if (is_nan_or_inf(inputR)) { inputR = 0.0f; }\n\n let inputVal = (inputL + inputR) * fixedgain;\n\n // Left channel (Filters 0-11)\n var combSumL = 0.0f;\n for (var j = 0u; j < 8u; j = j + 1u) {\n let size = sizes[j];\n let startOffset = startOffsets[j];\n let bufferIndex = bufIndices[j];\n var fs = filterStores[j];\n\n var output = state[startOffset + bufferIndex];\n if (is_nan_or_inf(output)) { output = 0.0f; }\n\n fs = output * (1.0f - effDamping) + fs * effDamping;\n var nextVal = inputVal + fs * effRoomSize;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n bufIndices[j] = (bufferIndex + 1u) % size;\n filterStores[j] = fs;\n combSumL = combSumL + output;\n }\n var revOutL = combSumL;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let f = 8u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n\n var bufOut = state[startOffset + bufferIndex];\n if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }\n\n var nextVal = revOutL + bufOut * 0.5f;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n let output = bufOut - revOutL;\n bufIndices[f] = (bufferIndex + 1u) % size;\n revOutL = output;\n }\n\n // Right channel (Filters 12-23)\n var combSumR = 0.0f;\n for (var j = 0u; j < 8u; j = j + 1u) {\n let f = 12u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n var fs = filterStores[f];\n\n var output = state[startOffset + bufferIndex];\n if (is_nan_or_inf(output)) { output = 0.0f; }\n\n fs = output * (1.0f - effDamping) + fs * effDamping;\n var nextVal = inputVal + fs * effRoomSize;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n bufIndices[f] = (bufferIndex + 1u) % size;\n filterStores[f] = fs;\n combSumR = combSumR + output;\n }\n var revOutR = combSumR;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let f = 20u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n\n var bufOut = state[startOffset + bufferIndex];\n if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }\n\n var nextVal = revOutR + bufOut * 0.5f;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n let output = bufOut - revOutR;\n bufIndices[f] = (bufferIndex + 1u) % size;\n revOutR = output;\n }\n\n var outL = inputL * effDry + revOutL * wet1 + revOutR * wet2;\n var outR = inputR * effDry + revOutR * wet1 + revOutL * wet2;\n if (is_nan_or_inf(outL)) { outL = 0.0f; }\n if (is_nan_or_inf(outR)) { outR = 0.0f; }\n\n if (outL > 1.0f) { outL = 1.0f; } else if (outL < -1.0f) { outL = -1.0f; }\n else if (outL > 0.9f) { outL = 0.9f + 0.1f * tanh((outL - 0.9f) / 0.1f); }\n else if (outL < -0.9f) { outL = -0.9f + 0.1f * tanh((outL + 0.9f) / 0.1f); }\n\n if (outR > 1.0f) { outR = 1.0f; } else if (outR < -1.0f) { outR = -1.0f; }\n else if (outR > 0.9f) { outR = 0.9f + 0.1f * tanh((outR - 0.9f) / 0.1f); }\n else if (outR < -0.9f) { outR = -0.9f + 0.1f * tanh((outR + 0.9f) / 0.1f); }\n\n outputChannels[index] = outL;\n outputChannels[numSamples + index] = outR;\n }\n }\n\n for (var f = 0u; f < 24u; f = f + 1u) {\n let metaOffset = f * 4u;\n state[metaOffset + 2u] = f32(bufIndices[f]);\n state[metaOffset + 3u] = filterStores[f];\n }\n}\n`;\nconst leftCombLengths = [\n\t1116,\n\t1188,\n\t1277,\n\t1356,\n\t1422,\n\t1491,\n\t1557,\n\t1617\n];\nconst leftAllpassLengths = [\n\t556,\n\t441,\n\t341,\n\t225\n];\nconst reverbAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {\n\tif (!ctx?.device) throw new Error(\"GPUDevice is required for WebGPU Reverb.\");\n\tconst op = virtualMedia.operation || {};\n\tconst numChannels = channels.length;\n\tif (numChannels === 0) return;\n\tconst numSamples = channels[0].length;\n\tif (numSamples === 0) return;\n\tconst roomSizeInternal = Math.max(0, Math.min(resolveNumber(op.roomSize, .5), .98));\n\tconst dampingInternal = Math.max(0, Math.min(resolveNumber(op.damping, .5), 1));\n\tconst wetInternal = Math.max(0, Math.min(resolveNumber(op.wet, .3), 1));\n\tconst dryInternal = Math.max(0, Math.min(resolveNumber(op.dry, 1), 1));\n\tconst widthInternal = Math.max(0, Math.min(resolveNumber(op.width, 1), 1));\n\tconst hasRoomSizeSig = isHandleConnected(op, \"roomSizeHandleId\") ? 1 : 0;\n\tconst hasDampingSig = isHandleConnected(op, \"dampingHandleId\") ? 1 : 0;\n\tconst hasWetSig = isHandleConnected(op, \"wetHandleId\") ? 1 : 0;\n\tconst hasDrySig = isHandleConnected(op, \"dryHandleId\") ? 1 : 0;\n\tconst hasWidthSig = isHandleConnected(op, \"widthHandleId\") ? 1 : 0;\n\tconst scale = sampleRate / 44100;\n\tconst nodeId = op.id || \"audio-reverb\";\n\tconst frame = ctx.frame ?? 0;\n\tconst fps = ctx.fps ?? 24;\n\tconst sizesL = [];\n\tconst sizesR = [];\n\tleftCombLengths.forEach((len) => {\n\t\tsizesL.push(Math.round(len * scale));\n\t});\n\tleftCombLengths.forEach((len) => {\n\t\tsizesR.push(Math.round((len + 23) * scale));\n\t});\n\tleftAllpassLengths.forEach((len) => {\n\t\tsizesL.push(Math.round(len * scale));\n\t});\n\tleftAllpassLengths.forEach((len) => {\n\t\tsizesR.push(Math.round((len + 23) * scale));\n\t});\n\tconst totalMetadataFloats = 96;\n\tlet totalDelaySamples = 0;\n\tconst filterSizes = [...sizesL, ...sizesR];\n\tfor (const size of filterSizes) totalDelaySamples += size;\n\tconst stateFloatCount = totalMetadataFloats + totalDelaySamples;\n\tconst metaData = new Float32Array(totalMetadataFloats);\n\tlet currentOffset = totalMetadataFloats;\n\tfor (let f = 0; f < 24; f++) {\n\t\tconst size = filterSizes[f];\n\t\tmetaData[f * 4] = size;\n\t\tmetaData[f * 4 + 1] = currentOffset;\n\t\tmetaData[f * 4 + 2] = 0;\n\t\tmetaData[f * 4 + 3] = 0;\n\t\tcurrentOffset += size;\n\t}\n\tawait WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, REVERB_SHADER_TEMPLATE, () => [\n\t\tsampleRate,\n\t\troomSizeInternal,\n\t\tdampingInternal,\n\t\twetInternal,\n\t\tdryInternal,\n\t\twidthInternal,\n\t\thasRoomSizeSig,\n\t\thasDampingSig,\n\t\thasWetSig,\n\t\thasDrySig,\n\t\thasWidthSig,\n\t\t0,\n\t\tframe,\n\t\tnumSamples,\n\t\tnumChannels,\n\t\t0\n\t], 16, stateFloatCount, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, metaData, void 0, PARAM_ORDER);\n};\n\n//#endregion\n//#region src/renderers/index.ts\nvar renderers_default = defineRenderer({ audioProcessor: reverbAudioProcessor });\n\n//#endregion\nexport { renderers_default as t };\n//# sourceMappingURL=renderers-DCFjIwTB.mjs.map"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderer-8_d6YNnE.mjs","names":[],"sources":["../../../nodes/node-audio-reverb/dist/renderers-DCFjIwTB.mjs"],"sourcesContent":["import { defineRenderer } from \"@framefields/node-sdk/renderer\";\nimport { isHandleConnected, resolveNumber } from \"@framefields/node-sdk\";\nimport { WebGPUAudioProcessor } from \"@framefields/webgpu-renderers\";\n\n//#region src/renderers/audio-processor.ts\nconst PARAM_ORDER = [\n\t\"roomSize\",\n\t\"damping\",\n\t\"wet\",\n\t\"dry\",\n\t\"width\"\n];\nconst REVERB_SHADER_TEMPLATE = () => `\nstruct Uniforms {\n sampleRate : f32,\n roomSize : f32,\n damping : f32,\n wet : f32,\n dry : f32,\n width : f32,\n hasRoomSizeSig : f32,\n hasDampingSig : f32,\n hasWetSig : f32,\n hasDrySig : f32,\n hasWidthSig : f32,\n baseTime : f32,\n frame : f32,\n numSamples : f32,\n numChannels : f32,\n pad1 : f32,\n};\n\n@group(0) @binding(0) var<uniform> u : Uniforms;\n@group(0) @binding(1) var<storage, read> inputChannels : array<f32>;\n@group(0) @binding(2) var<storage, read_write> outputChannels : array<f32>;\n@group(0) @binding(3) var<storage, read_write> state : array<f32>;\n@group(0) @binding(4) var<storage, read> roomSizeSignal : array<f32>;\n@group(0) @binding(5) var<storage, read> dampingSignal : array<f32>;\n@group(0) @binding(6) var<storage, read> wetSignal : array<f32>;\n@group(0) @binding(7) var<storage, read> drySignal : array<f32>;\n@group(0) @binding(8) var<storage, read> widthSignal : array<f32>;\n\nfn tanh(x: f32) -> f32 {\n let exp2x = exp(2.0f * x);\n return (exp2x - 1.0f) / (exp2x + 1.0f);\n}\n\nfn is_nan_or_inf(v: f32) -> bool {\n return (v != v) || (abs(v) > 3.402823466e+38f);\n}\n\n@compute @workgroup_size(1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n if (gid.x != 0u) { return; }\n\n let numSamples = u32(u.numSamples);\n let numChannels = u32(u.numChannels);\n let fixedgain: f32 = 0.015f;\n\n var bufIndices: array<u32, 24>;\n var filterStores: array<f32, 24>;\n var sizes: array<u32, 24>;\n var startOffsets: array<u32, 24>;\n\n for (var f = 0u; f < 24u; f = f + 1u) {\n let metaOffset = f * 4u;\n sizes[f] = u32(state[metaOffset]);\n startOffsets[f] = u32(state[metaOffset + 1u]);\n bufIndices[f] = u32(state[metaOffset + 2u]);\n var fs = state[metaOffset + 3u];\n if (is_nan_or_inf(fs)) { fs = 0.0f; }\n filterStores[f] = fs;\n }\n\n if (numChannels == 1u) {\n for (var index = 0u; index < numSamples; index = index + 1u) {\n // Dynamic parameter signal evaluation\n var effRoomSize = u.roomSize;\n if (u.hasRoomSizeSig > 0.5f) { effRoomSize = roomSizeSignal[index]; }\n effRoomSize = clamp(effRoomSize, 0.0f, 0.98f);\n\n var effDamping = u.damping;\n if (u.hasDampingSig > 0.5f) { effDamping = dampingSignal[index]; }\n effDamping = clamp(effDamping, 0.0f, 1.0f) * 0.4f;\n\n var effWet = u.wet;\n if (u.hasWetSig > 0.5f) { effWet = wetSignal[index]; }\n effWet = clamp(effWet, 0.0f, 1.0f) * 3.0f;\n\n var effDry = u.dry;\n if (u.hasDrySig > 0.5f) { effDry = drySignal[index]; }\n effDry = clamp(effDry, 0.0f, 2.0f);\n\n var x = inputChannels[index];\n if (is_nan_or_inf(x)) { x = 0.0f; }\n let inputVal = x * fixedgain;\n\n var combSum = 0.0f;\n for (var j = 0u; j < 8u; j = j + 1u) {\n let size = sizes[j];\n let startOffset = startOffsets[j];\n let bufferIndex = bufIndices[j];\n var fs = filterStores[j];\n\n var output = state[startOffset + bufferIndex];\n if (is_nan_or_inf(output)) { output = 0.0f; }\n\n fs = output * (1.0f - effDamping) + fs * effDamping;\n var nextVal = inputVal + fs * effRoomSize;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n bufIndices[j] = (bufferIndex + 1u) % size;\n filterStores[j] = fs;\n combSum = combSum + output;\n }\n\n var revOut = combSum;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let f = 8u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n\n var bufOut = state[startOffset + bufferIndex];\n if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }\n\n var nextVal = revOut + bufOut * 0.5f;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n let output = bufOut - revOut;\n bufIndices[f] = (bufferIndex + 1u) % size;\n revOut = output;\n }\n\n var outVal = x * effDry + revOut * effWet;\n if (is_nan_or_inf(outVal)) { outVal = 0.0f; }\n\n if (outVal > 1.0f) { outVal = 1.0f; }\n else if (outVal < -1.0f) { outVal = -1.0f; }\n else if (outVal > 0.9f) { outVal = 0.9f + 0.1f * tanh((outVal - 0.9f) / 0.1f); }\n else if (outVal < -0.9f) { outVal = -0.9f + 0.1f * tanh((outVal + 0.9f) / 0.1f); }\n\n outputChannels[index] = outVal;\n }\n } else {\n for (var index = 0u; index < numSamples; index = index + 1u) {\n // Dynamic parameter signal evaluation\n var effRoomSize = u.roomSize;\n if (u.hasRoomSizeSig > 0.5f) { effRoomSize = roomSizeSignal[index]; }\n effRoomSize = clamp(effRoomSize, 0.0f, 0.98f);\n\n var effDamping = u.damping;\n if (u.hasDampingSig > 0.5f) { effDamping = dampingSignal[index]; }\n effDamping = clamp(effDamping, 0.0f, 1.0f) * 0.4f;\n\n var effWet = u.wet;\n if (u.hasWetSig > 0.5f) { effWet = wetSignal[index]; }\n effWet = clamp(effWet, 0.0f, 1.0f) * 3.0f;\n\n var effDry = u.dry;\n if (u.hasDrySig > 0.5f) { effDry = drySignal[index]; }\n effDry = clamp(effDry, 0.0f, 2.0f);\n\n var effWidth = u.width;\n if (u.hasWidthSig > 0.5f) { effWidth = widthSignal[index]; }\n effWidth = clamp(effWidth, 0.0f, 1.0f);\n\n let wet1 = effWet * (effWidth / 2.0f + 0.5f);\n let wet2 = effWet * ((1.0f - effWidth) / 2.0f);\n\n var inputL = inputChannels[index];\n if (is_nan_or_inf(inputL)) { inputL = 0.0f; }\n var inputR = inputChannels[numSamples + index];\n if (is_nan_or_inf(inputR)) { inputR = 0.0f; }\n\n let inputVal = (inputL + inputR) * fixedgain;\n\n // Left channel (Filters 0-11)\n var combSumL = 0.0f;\n for (var j = 0u; j < 8u; j = j + 1u) {\n let size = sizes[j];\n let startOffset = startOffsets[j];\n let bufferIndex = bufIndices[j];\n var fs = filterStores[j];\n\n var output = state[startOffset + bufferIndex];\n if (is_nan_or_inf(output)) { output = 0.0f; }\n\n fs = output * (1.0f - effDamping) + fs * effDamping;\n var nextVal = inputVal + fs * effRoomSize;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n bufIndices[j] = (bufferIndex + 1u) % size;\n filterStores[j] = fs;\n combSumL = combSumL + output;\n }\n var revOutL = combSumL;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let f = 8u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n\n var bufOut = state[startOffset + bufferIndex];\n if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }\n\n var nextVal = revOutL + bufOut * 0.5f;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n let output = bufOut - revOutL;\n bufIndices[f] = (bufferIndex + 1u) % size;\n revOutL = output;\n }\n\n // Right channel (Filters 12-23)\n var combSumR = 0.0f;\n for (var j = 0u; j < 8u; j = j + 1u) {\n let f = 12u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n var fs = filterStores[f];\n\n var output = state[startOffset + bufferIndex];\n if (is_nan_or_inf(output)) { output = 0.0f; }\n\n fs = output * (1.0f - effDamping) + fs * effDamping;\n var nextVal = inputVal + fs * effRoomSize;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n bufIndices[f] = (bufferIndex + 1u) % size;\n filterStores[f] = fs;\n combSumR = combSumR + output;\n }\n var revOutR = combSumR;\n for (var j = 0u; j < 4u; j = j + 1u) {\n let f = 20u + j;\n let size = sizes[f];\n let startOffset = startOffsets[f];\n let bufferIndex = bufIndices[f];\n\n var bufOut = state[startOffset + bufferIndex];\n if (is_nan_or_inf(bufOut)) { bufOut = 0.0f; }\n\n var nextVal = revOutR + bufOut * 0.5f;\n if (is_nan_or_inf(nextVal)) { nextVal = 0.0f; }\n state[startOffset + bufferIndex] = clamp(nextVal, -4.0f, 4.0f);\n\n let output = bufOut - revOutR;\n bufIndices[f] = (bufferIndex + 1u) % size;\n revOutR = output;\n }\n\n var outL = inputL * effDry + revOutL * wet1 + revOutR * wet2;\n var outR = inputR * effDry + revOutR * wet1 + revOutL * wet2;\n if (is_nan_or_inf(outL)) { outL = 0.0f; }\n if (is_nan_or_inf(outR)) { outR = 0.0f; }\n\n if (outL > 1.0f) { outL = 1.0f; } else if (outL < -1.0f) { outL = -1.0f; }\n else if (outL > 0.9f) { outL = 0.9f + 0.1f * tanh((outL - 0.9f) / 0.1f); }\n else if (outL < -0.9f) { outL = -0.9f + 0.1f * tanh((outL + 0.9f) / 0.1f); }\n\n if (outR > 1.0f) { outR = 1.0f; } else if (outR < -1.0f) { outR = -1.0f; }\n else if (outR > 0.9f) { outR = 0.9f + 0.1f * tanh((outR - 0.9f) / 0.1f); }\n else if (outR < -0.9f) { outR = -0.9f + 0.1f * tanh((outR + 0.9f) / 0.1f); }\n\n outputChannels[index] = outL;\n outputChannels[numSamples + index] = outR;\n }\n }\n\n for (var f = 0u; f < 24u; f = f + 1u) {\n let metaOffset = f * 4u;\n state[metaOffset + 2u] = f32(bufIndices[f]);\n state[metaOffset + 3u] = filterStores[f];\n }\n}\n`;\nconst leftCombLengths = [\n\t1116,\n\t1188,\n\t1277,\n\t1356,\n\t1422,\n\t1491,\n\t1557,\n\t1617\n];\nconst leftAllpassLengths = [\n\t556,\n\t441,\n\t341,\n\t225\n];\nconst reverbAudioProcessor = async (channels, sampleRate, virtualMedia, ctx) => {\n\tif (!ctx?.device) throw new Error(\"GPUDevice is required for WebGPU Reverb.\");\n\tconst op = virtualMedia.operation || {};\n\tconst numChannels = channels.length;\n\tif (numChannels === 0) return;\n\tconst numSamples = channels[0].length;\n\tif (numSamples === 0) return;\n\tconst roomSizeInternal = Math.max(0, Math.min(resolveNumber(op.roomSize, .5), .98));\n\tconst dampingInternal = Math.max(0, Math.min(resolveNumber(op.damping, .5), 1));\n\tconst wetInternal = Math.max(0, Math.min(resolveNumber(op.wet, .3), 1));\n\tconst dryInternal = Math.max(0, Math.min(resolveNumber(op.dry, 1), 1));\n\tconst widthInternal = Math.max(0, Math.min(resolveNumber(op.width, 1), 1));\n\tconst hasRoomSizeSig = isHandleConnected(op, \"roomSizeHandleId\") ? 1 : 0;\n\tconst hasDampingSig = isHandleConnected(op, \"dampingHandleId\") ? 1 : 0;\n\tconst hasWetSig = isHandleConnected(op, \"wetHandleId\") ? 1 : 0;\n\tconst hasDrySig = isHandleConnected(op, \"dryHandleId\") ? 1 : 0;\n\tconst hasWidthSig = isHandleConnected(op, \"widthHandleId\") ? 1 : 0;\n\tconst scale = sampleRate / 44100;\n\tconst nodeId = op.id || \"audio-reverb\";\n\tconst frame = ctx.frame ?? 0;\n\tconst fps = ctx.fps ?? 24;\n\tconst sizesL = [];\n\tconst sizesR = [];\n\tleftCombLengths.forEach((len) => {\n\t\tsizesL.push(Math.round(len * scale));\n\t});\n\tleftCombLengths.forEach((len) => {\n\t\tsizesR.push(Math.round((len + 23) * scale));\n\t});\n\tleftAllpassLengths.forEach((len) => {\n\t\tsizesL.push(Math.round(len * scale));\n\t});\n\tleftAllpassLengths.forEach((len) => {\n\t\tsizesR.push(Math.round((len + 23) * scale));\n\t});\n\tconst totalMetadataFloats = 96;\n\tlet totalDelaySamples = 0;\n\tconst filterSizes = [...sizesL, ...sizesR];\n\tfor (const size of filterSizes) totalDelaySamples += size;\n\tconst stateFloatCount = totalMetadataFloats + totalDelaySamples;\n\tconst metaData = new Float32Array(totalMetadataFloats);\n\tlet currentOffset = totalMetadataFloats;\n\tfor (let f = 0; f < 24; f++) {\n\t\tconst size = filterSizes[f];\n\t\tmetaData[f * 4] = size;\n\t\tmetaData[f * 4 + 1] = currentOffset;\n\t\tmetaData[f * 4 + 2] = 0;\n\t\tmetaData[f * 4 + 3] = 0;\n\t\tcurrentOffset += size;\n\t}\n\tawait WebGPUAudioProcessor.process(ctx.device, nodeId, channels, sampleRate, virtualMedia, frame, fps, REVERB_SHADER_TEMPLATE, () => [\n\t\tsampleRate,\n\t\troomSizeInternal,\n\t\tdampingInternal,\n\t\twetInternal,\n\t\tdryInternal,\n\t\twidthInternal,\n\t\thasRoomSizeSig,\n\t\thasDampingSig,\n\t\thasWetSig,\n\t\thasDrySig,\n\t\thasWidthSig,\n\t\t0,\n\t\tframe,\n\t\tnumSamples,\n\t\tnumChannels,\n\t\t0\n\t], 16, stateFloatCount, ctx?.renderId, true, ctx?.elapsedMs, ctx?.durationMs, metaData, void 0, PARAM_ORDER);\n};\n\n//#endregion\n//#region src/renderers/index.ts\nvar renderers_default = defineRenderer({ audioProcessor: reverbAudioProcessor });\n\n//#endregion\nexport { renderers_default as t };\n//# sourceMappingURL=renderers-DCFjIwTB.mjs.map"],"mappings":"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"}
|