>evh{TV*>
zA7tzN^0^KRpSo^c{b?#fe%%&ohgw?SQtm>~o`x*7O7J_4ZNMG^riynd`vX=%0pZO8
zYJ7SC(?4Lcot^Em3m>iLtEuPc>r@(`V$;R+qN(+OHAA=`OOcO1Z!{0ofo`6Nv92Ai
zwU;MVMxqIHPhff@^}6~1>lZ(L@4VZpwqh=wan@K!MbK{1wRXPkCHHs%l
zp5~P&Qb#>4q4hsbpwxu8@fii4i2t+u0<`E=%ERP4mKmR=vjZtP=1OpMV9%
zg^!o;K&Mc!@^UFn8tYl0eC$NN4NLjxgUzMBAGytV(v|1|ITd>xKv4tFq=`CIlR|pL
zQ78$20DDz)>58bdenF|gW%y6adf3AXu{UVb)9#tAnIrDjwu)Va?Annz%uJpcH_>TumYUzyRjulXc6+?I8r)0zQAMISUxO4tM
zW+-O{yvZ$M`P))s{E%D2QFWQ*xELJB>;2at)NF%NqsR#%%yI{KKTZ==m(aIQLyAsH
zJdE6fin`>CL7(ppp`w-Z2Rb}jt>%tOe`_thoqfJz;Gd`dmHmrjS>DS5n?|mP804X6
zTfDWFx!sDhp4NyhoLW0-ljJI91H6D=A80?*lkBd4Q#}7MD$FO0q$I>1I$wOSHtfs-
z*EI?XBqjWvrrZaKGBXGlO3voG2-WWl`XjQ``Wd9JkxQj`D|g0mnJ-lg`;YM7Ls;Me
zLrS^j$eMrPq8Iz>52cV(%RO*lz1)?0>!E!_&y?48Ii{N#CHh3obx0+jF0)>>H!A~t
zr8n<|57InI`q0;GDSQR^O5aIdw`DgN!p)s$K737EBjYl%#QY6i%*iI)&jI;&lFhTf
z$cI4>it;Q#pKN=s=J+tiRy(U$RSnB054WGGRY#vpGo8lm*Z;ES{=U`p+#z@RRgh}q
z%c2bbcsi`LzvYVsRo}A0DqI7`Qo{~rmfElUuOnwBEIX7i;y&!*Ru(M3Cx*MsF#f+U
ze_4D4XGve8x2|>Rd_L$YIey~CA%jl2MmtR-E;(J0I>;RqQU9D;>Yao%zo`fY#n3)9
zUEM~w@BXDg^KWba&@Q9W^_{WRy&gpE?Zy~WRP$lE=d$y9jyIF_R^g?A2eeVq^*oU%
zx81xxeCWTYAsSVcbHBe&YW5^03X~V2(nVf)_d4oi=vJF&O+Rina)9SQ{^)r}sHklv
zW)(eA`Tf{Ko*}H(J(9Tr$8_&H2|vezxtw)<+~D!<1&=iYJB51Bmg-j68Vre}!l|PdTkLMm-tCB$
zFu*GOxGq{v>X=+~Ngg&+(9}Qa)&rUvEXVBdCq@7xDdxkMC|o1qG@7%hz@Pa2SEU}?
zk@%2R$@$$qQ(@6pfL1upsgI4AmZmfCZ&w`cdwUGHP>U98lDC!oNaYR~M&lOJDT{=i
zH%HrWHbFuA6!4M3+1*
zC;vp|6vtVy+X_PdT8Y>IadV2IYxhm4PiVubpNTLSEWR{%@@i|E=P=He*hf5wE}!JNuQ%^|65cdiF*(Tj
z>2jXQ>uxGEkXG@iB1~TQ*;VC)ELcw!tX;F7)2Dsx(Tzzy%l>2H%U?feESRw{U6PeCDv#p|w
z!}<=o9^YfWClOZ0IhjRIoi2et(u-cq3`;(4z8M;fN*wy~iX98E|4Q1Dx3u0!Ah566
zTL39e(3-nH3V{9wS*yIGyTP4>tCL4TJI&DkrMcHwy8BV)`A55oL_~Q6urn23`iI_M
z+v*lF;OL^ZnpKh@kb(Ig`^0yUdo@2`OYx4nZG5@pw1rj4{9_=s-8=nteR>=T08U9W
z5QOaKljoN=@`O1?37Z~S^=!+Jt6|r;moONGB02~a4zw%L8cp=OW7gUd;IY8goQ*4u
zdP!f(fU=qoKTHn#4l3fhzEWc1#%(rfd4Za4YY#(t)r-`W3&;?)cKyiGJ}ALK^e}uz
zHs~JV>Uhu~b=GjJF)zsD`61%QOKgFvN&HJ)g9V@lc@kc4*3ZMhq88)sT$qE^u2ID5
zAsRddak6Sa?z!;+E`}!~GvP|%XE!M#;vEk(?$%ivpU)tY1)O~BVBP~49Jdp-A8{46
zGa98Eje{6J813s)hZ%C|D=Y-1gm}qpIYv&nXR2HId_l47nD_?B>vf0D3#LZzv^##(
zd~7>W5VoUa{f1o&nc<(%M%7h~)g
zP{NB_scG4%Q0~aBx@W(t%eP3s)Y@<(b^)k)3G`c@b=ws$<8QCQ{@ckzAGZ;=_#&(z
znh`AvgX|m!`t8%$5+9a*lh??J&(M8+aX*u6awgiTnKNa(D{r8w>r1c)ZTq*^G;som
ze31#2qcK6%!p@GFXl6(D8KSve5Qc|~uvaj~P5MgkRsBP+QUz8H0gn6_SsABn5Al>0s-VcoN0+*<?p3M#$TWogezx_
z$c5zy#BY9tE!2{yXJc?k)1qU7f4B=bR$Zd4mH*X
zyu|*D#Ek`5%mTWA&O%p~qBB=a87~Y~c*NB~9V3hlevKUu*64$f!Is8#bG@&JK_<2N
zfP!Vf)ZNZj4O|k8n^T^5+V$?`IExTik0AV1bwTcQm-+v~ng1q=z>&YdtU-K)Oi<$t
z)<3ACWmPpLpQ`3wT1CnG5G`Pip5n7n5ZUUOb<=DL>y`6c5%NcBWuIkJ({KIM`|6iZ
zY^Qhk9pDzK8iCpfLn+yD3gh_D$^~$u0y)m_TT7GqbZ65Dant&7>LDv8GRj=NKVT#u
zCl8;sa1H{ui&x;V<)t@hv69QvW|;s->2@e;y(inWjJ7u{OGUp%XXGCF0#zbJhBbV5
z4J%Z9pyKI9P;`*4(N;K;nVdjc7KiL2PHC4HDCQZJ!p)U8T7dPjVu>NoaO3F1!_iYT
z43(8p8)r>)X`VLCpq@T
zahMW(OXo`2qk2GiP;{)272z1U72agx@pk&nY9L>TDLlwuhXGFopQO
z&Qj-`?La2wyzE+{8;y0bHk4WGcOqcruex0K2t~^4$d6<1s4jXg@c0|WT^xO9*fz2y
zfZ6_y;_aV9Ulhx2^8I`5r5-diwF4({^GnC#{E)T@y%Qv-E7acckgC
z`O2FRBZ-Zt-W>5x=md@rXI--%LC_o8K|Qp6#Z1D$Qj36hbsqoA!9_KwuIkI1F25{Q
z`w@n32^iAKoY@HEWwgM|dI`
zmqY6N7)tBkM*dOKxs33#a1I-*Kyj^)X>bXvzxaJyIj6V~?;J;0Iww$tRs6Fsp$PuI
z`|mYgD?@D$A}EZ9L-bK>mV#
ziKsG%Za_I3o7Rd`cSZ#|wl>&BG)3?8O||YO{
zuZfXuAbtWzO&7?fCfgDNwY6AGcQ9^U)iQ~vm-QrZ!ksYzXd(K1x~=d4(Fz)K{lW4D
zc}DTCWpk{*k@FhdNr%^qBd>kCKBe5uM&75k_g)oHqN!{lx33~zAwS+!i*>6lfLg(w
zN+1E3k0P2l_buH^Z+IOmY$<2?>m)+MPxPbaB%~OQiYTz{5b3K!W2jVzbZHIl5c`z{
zM+bN}DR3(9Y*VOD8FEj)t5^=dH=o|#R`8^xE!{njO@J?!xb0e^qR5*&VV91oE~a64
z>s=71AxY}jL(*yKgli1y_Q3>ZsHc6?;j`ThU%7q&N|in+tjIIHo(y1%s2Jw^?xf^bYjL!uAw_ZYQuW2SF(?o4JjA|!R!nP&w?j=m+thzDP
zvU7>2b(XH6uc+W`T4*&L-!E}0dWT2TJ_iHO41md1!%2YZj=dGNj#en|cm)7d
zG5W*F;E#DyKSD;%o#u9;RTqwQR40e2_H;>NQtRFqKMF=D!w)lqv8E~^lLaj4LHZ3_
zYc9?x1<7zG6k3CPQ~4bFMqb6;E4p9ef`*cMJ?R{#r
z(=bs7pk1cf@*D1e=W0-ft0JFa9O8#;;+`d=E%y&c^8u!@*laU1IDfXntPh;~FvJ{f
zko|P)6ei}S_Yj`H=%krsoyaJX3on4T`c}GuzdrIcq!c&TrtH$UYd6AGv)rjHi|JA~
z|C5Qk`c61}bBPLD)=~-WZe0FbCjL)vQ*0a`*b4m@^jKT*wcLhwK=j*k$%i??`P!@3GiY-06%}H3hLzTL$x|7vY++%
zLlb=LKwWC=m$&G=19mT~caN20l1Z6j-kP^=ajG-gL%DG<>`(^6FBXz=;4Z;RsidqUHcPSdT
z*j7ejor=M8c@}n#1>A%;IdBn9uL?m6yd0%@%^c|ipVL6)$uM824lhOX(OHp4ktZfQ
zn?GeR@qh$Qr}byXyZLx*f(30l5$r#;_T6SFVBSpCcrD|bzajhgwDD4(nMkODQ5O6;=n0KX3KyHeYd23XO(Y
zyZnQ&D8K!F8|sn_VoYU$E)|r;o{0Ng>2aMcRHm4=Ihb?|;$f9fxfq7$1kitYH0O!zkP$ap0jZLhbG4z_=Jmkk;C7R!(qC*%cYY5(oU
z>=`^sO+
zg8#oE6HRSEZHrINrXIw>kCtqK$hNY#p&OmrsnQq%8m{`5er27r(?G4)sjK~cT8}J$
zPG$g%Bq6ots;NA}q-VSK%lcYr9<2maGquMO%;^&{(QSi%I(g=N=IHQinFdN`+#dZi
zwSLrH&1$Kg@Fa448v5PNRRl`t2}BMgR32r%iB%$ia;jY^SZDS?)@IN?`+{(kFq@@h|1l8rSu{JRC)%n^oL?U;0}m^*
zE(i~@&pr6tCFC5KAtY1XkPCG%Pvqg9);5z3@iQ|TEFm&0v-W~)Gj!v)2&A#`7APzEYV;e=wNeEuU
zlD@$F6;J6U!R@pJu)u(OTuXmj%V1H2MFy=6O#Tg!e*yDyf?L5SbM&oK)N(whM_?r2
z64b{dm)4m+t&oDc=~bV_^zn+2&91bQ2)@?l^HZbHEtCHN&%gx
z$k%w$d_dxAO!_ll_fG=c#uiWDH76T5+y-jMXmfz^x$%f|;4r)cFyrEQw)&+BqQqTKCblP=*zn>9xj$V6fUO(yB>$7tuV$NeOV8Go8Y?o((($shMXcI
zB<^i&PyEaKzRWDOIW8_p=JG(9;zl~@iuMmdRI3uN&(Qq76Od1}(?87(=yPf4Q*B%@
zK>818`Zt*VhiCe~UuZWDpJPSGno2>r9JzdSNRcr~NZk?+LdfX27zAW9b;k!@X
zclX;-sTh;N;}+;2^{2){XTjHSxrXQ1N87LpWvi%Xq?KjeE@QZ*C-DkvHq-D(>(u?V
z_Tg8FSYo~E<7=4+FyvO`Wl3rI9Nen9gKyb(_L)o{ahyU;Aujc53R}fuUPfj_EWfn=
zb@v>1ayOG6Sn22a<=!X{rJmEtO+^qPqaZD7t;6Um{F_O#H`072yKu-@5l8A)-2=5=
z%6yqoDXte3V}^lI;xg*0A`XEx#kTz^feJ;OHiTNJ;%jZ?rr4R@oDn=D%h&-RFWrRY
znL+YC6%3QS)_FIx!H@e>U2jLl!xFqPMLA_svIoLXl7})L=`_kJ4~vB;Z7aH#)88vc
z?J$2H&bcrfe!_#~$lXX+w>)5$vv;_s!2}Tifz~be6Hp8J{0wWe>F#35dzsDZU>;C^
zt(chnol(2^&MYdnA7sfZP$
zqw*#gVn=eSBPteKM0P|hy(GW%cV4T(G?`{l@7%jpYdLAjqmd&yK+tj0gP0!LDp>yg<8yCZTQ<<)%lJfzT{bf4;GTUJwSb?m?yFZ_%7v6G!Mm=B>w9hU;a;HRF(I-+
z1JgoN+*!MvZyxtV{kq*zw(s_{sgy;s``#v>qdTg2&|R41pFeH7FvMt9`CICYbW}t-Us8$wI|5mislKA3m>KYf%^d?cB
z{TPWOyT`PSLVJGzB#Wm-d|W3)0*xg~YLAMS<=dcha7k|j70VhRIxZyu;0vlonHx&V
z&iZ;T2xOru5>DxNij6?`f)jHpY^jk%rUC@9B@d<`2ejXtmrs-6)KJz@P@EU4h%VUy
zt7}Y%r_%R)ZG_$<<^T_U5(|`q`<~7^PtqOAMyYVWP!vmSz_gXWk@DrIpmp7n{9}qA
z(yQQ{WLd#j%ofQyN2nt9uP%f5FyDAKTtw2@%Qokw`;jfD0;HYgtGM@36ZHUT7Eu5`
z+gy@3&{FL}tI~8JyAJQ&Vf&>6RyvH~L`%WTwcgwB0KjOvf7SFqL4vR^?Ho~9#^J4_
zEi9ojRL^RyR!*7#{HRgUL`+7`Jx>57Lm0oSGKu^x0!<1eI
zlcaHxaNZeYs&Jpwx|6R0A;9Qf#i
zmnG%mwSchqfyE5_5sNF_V?$j6p+3p1{>Req2&5^hviiXozbUV>7z{O16dx+=@3!gi
zkp(#N98W4ydp7Z&y%!yovixa>f*?V)e7Qi(n_$3^+|ezHQHv3uIni3TjAMgfApSFt
zHhTrLZjFi{z0U!4CZW#Te2wyBwDJlVv&rBGW1eJ-w@TV}`VuMc%Knh2D{VbbF&Hau
znDqNmz@8y^`S(UZlP!*A38%FYLeTLBUFxVK>I9-46nTb$R9ZP2zNpED=D26ur!GAB
zV4qkCW14x{DSrKtT>4ccdyFdnTOxlg+667=X1&uw2CxC(CJXC`e|baej-+rbSJ(IHHy5KFt87(H*PiIz-+Hzq#pbFXwDZe(
zIgYqO?^E(~t_I>O$EKCS4Sx-$AzkGNGitWa=)>nn_kbO1Mjga=Mdr>quN)ZIuB;NPDL%u^j>N1WcU;&oz$`#v&h5aYH2
zqn?b%f=D>F!l(OM*!IjAxKkTiS7MS4H@O^!9gVqIlcKzO50ZAUSRJ$63j4U&MXu+HKd`ukW$=MDPo|DYEiD#=<7xmloPA(_A>Te4Dimu_3vP#
z=OK$LsVZ1SQ_IZDg(4f>Q|99h{l1uaIjhpc&q9TR@_YT20rh|WFDddU1{Y4tct==*
zL@LUWv68E$%m0qA{twv%8(BH2?*;g!&hYaIT^tN-EPincjUd@YUMHZnWEL=zja~9V
z!8+fWANPdAKl3BX)RC3lXz|e;moVZo>JNg2;H0UoS&AVo1q^P%pbU^n?s5-RzHH}!
zCEIsH4g8Eam4a>k)|AEwi;F3g=9NgI7++S>lQNY0Lb{>q@@yjZM`sJI1?@q8De6N1
zzZKFc2jh(c1iwG36ucEl?W6WbHxpSpSbG15BD2g{ANM=#*Z)(gd$V5d1FL(*(cu}T
zTK901B%$Jv~6Vm2Ff@bVWAub=~)8(qV@jP8Wk
zsGN}Q+K-9VYyHwbJA7WcjoCh
zrC^>%Kxe97S~(}1?#=)l+`gTz50FKvEfeWH<*lum&!Sg^g5)9aM&hSgrhM#Is@X)DHAcY!5EL!2Yr@SFbCQM
zqH;VFl#Kb-mGGkB9v0QXU?YZi(bh!PN`AJmKO|#xv~1lOSE_*gu2@xIMeFH8aEvEQ
zpXxiP8;G#z<6d%dzoqEe;)%i)bqf^;9lcbHU_ljH;by3#y9_go7BNP|hDcb&
z0O`JS@;O#tq{cab)z|V;hs$*hZN}TGV$XB$ATj8hfNUf>;)x>ejT
zh6oJLqzlPvVY=M!B7;%ix7q>vl;W;`=Obwhs($o!I>;bzyIfu0ww*2>VA!1^JO5~m
zdpEwzzGRe}09O9&&w#>Amuw>=#1=48BA${hyE|b{pWJVRToaP_z@!HemtSH=vx5mD
zrC|jPrTG~3%zx{%RLS(JE&ZpuAD@Jb2uc#l=C89iqiXM=m&e|E&qCR$
zU_a(uK>H45ENMMR#|ms@7z`qg`qS@!Z=C4Myq5c8N2{penSj5z!j43YQj!+~suDz#
znbmJU_!i>&V;)TTGo&Oz5tI-|MKE&@Ma^nyz;cBen-51*CR5IyC>dk_G~;$ktd&;A
zbDZnp?uO`GzHw)I)9|8|nM>0yL)t|yEey6FU@*^nk#r+6730VJ02Dh3Lz&<#MWeT
zSgybPdno5xLVJHTnz23e`F^fVjK@TTteC*GWY*}VG%TBi@tA3{mULykc=JYwOztd<
zhBF+t5bHW@PhcA5ZA56E`VS}kUlRjN9!^Cm)Tt5w2hzpcmrO|BpLAn}3mwWtc`Wl~
zsvwMVAAg2wS6Tns8Y3mWy?VR$(;I1V$*B&c$P!pJ1zC7Xg=-+wavpr4@_BQ?Br-`k
z+LR00>HGc5+G7g;H1bQaZfv$r(&6JW*?`gNWFY|)@l`^{mZ)WlNdB#E
zcT?-!TVBX`#Q^~l0@rVjI2vzuHw#If|KNrc-P?y9+
zDOZbZ`i={Ok})0mE{9wlBiCQD_t`h>^9s7Q+(pU?Z5w91agYsD_qi#`k>=UI;;G~$xk4d4%KwnVK_{nJa0N;6ZRG|rD5+>
zGJ`>5^QAI?JT{YiGrgj3b`Lm~{*4PeiDI+%ro&G`H_0-D7`EFPgtLXz5}y(6p|4fz
zVv>3)#83}{xBjuh(-EZunfr!mMA_=#Dl5Bqag#3`cm-MORCoQ@Srt91T9S)wBrTd_
zr9?6FK3X5-mG%|;i`S8gL_Yf7#QdD&T$xT{L`<1
zjPA)nvesPSVRI7|RTrQ_ncJQBD0rQUiFP@s>(Kb<)V{H7Fo5(K@q@WDxI=wqCO4OV
zruafAop4g{>*(shA3<8*<@k;bYW0DD4464QW4c;Asxch5$l_9yYYhoOI8uH>FpN@o
zVa_Mo8Wur+w~VPp{+&o!Qq?ZN$e>{^&Ev%6U)}aHT4b&{+;A@vUB>*|nQ6ct6KYk?
z`hSPupc$kfd4@ICXqtxRdvj=H0Ffhh5xQHv-}}RG(HE055(5XQom;3d8j_2D$1%D`
zeyL|NzRZ5n-OCz|<$mZi`1<|)({~@dSm0%4M^e@MAZhA7A;IU@Ns2A6#|4k?d~xzV
zSR!@|2OD0MkU8h5PTfGh`8uPE?g<6P5|s9?%v?VlMCf57VH~HDX*~Jj7i|q;s!xrH
z`E%m2tNLR=y?4Ath&UUK$top&<~r~cZhzPfQZs2oQ_m~b2PoLNbn-x$>oO1L=*$Ol
zI3}?4Q2>{m#1AV*gq^f!jD^eK^^p#$g_Q3WOG?3JcxkS7Ub(XSnhseYgR-a6JaxfkP58V(94jJw^ImW}Om0sn!5p{QCZTs^d#nxv@oSmi&
zY30k9<4qnH8~CHEnCGAVlZYFI}3DsRya{v}|@wqgyXR7%4P
zZ0K`}$x5>ddiZUrTfq<{(E|-OOQhNihLP81IG)4pKI48d5DvqCq!r9SK?}Dy&XGvEo+dDb8QNecz*~p`pnPC8@kWEY>{%BTT{#Ur!Jj1NlE&6
zUP^6R1P%H6Am*uz+1KLZT9IN1v!-VX`J5j-EG~;bc@46RF6wgH7>eAxTo
z-B_e=o6cNup>gZ&vns;Q@nNMWk}CBb%me=4RjEyX^ZHiTRjC32`gjj}Lhg-%jEZ!%
Iq-oIq0m1OoKL7v#
literal 0
HcmV?d00001
diff --git a/packages/twenty-sdk/src/cli/utilities/build/cover/assets/twenty-logo-mark-path.ts b/packages/twenty-sdk/src/cli/utilities/build/cover/assets/twenty-logo-mark-path.ts
new file mode 100644
index 0000000000..d4ba4a71ac
--- /dev/null
+++ b/packages/twenty-sdk/src/cli/utilities/build/cover/assets/twenty-logo-mark-path.ts
@@ -0,0 +1,2 @@
+export const TWENTY_LOGO_MARK_PATH =
+ 'M6.822 14.174c0-2.435 1.996-4.411 4.456-4.411h8.574c.125 0 .241.075.293.19a.31.31 0 0 1-.056.344l-1.88 2.023c-.326.35-.787.552-1.27.552H11.3c-.738 0-1.338.594-1.338 1.325v3.336a.78.78 0 0 1-.783.777H7.61a.78.78 0 0 1-.783-.777v-3.36zM33.5 25.553c0 2.434-1.996 4.411-4.456 4.411h-3.642c-2.46 0-4.454-1.977-4.454-4.411v-6.315c0-.43.16-.842.456-1.16l2.124-2.285a.33.33 0 0 1 .355-.081.32.32 0 0 1 .205.295v9.527c0 .73.598 1.322 1.337 1.322h3.6a1.33 1.33 0 0 0 1.337-1.322V14.197c0-.73-.599-1.325-1.337-1.325H24.84c-.481 0-.938.201-1.265.547L11.088 26.856h7.503a.78.78 0 0 1 .784.778v1.552a.78.78 0 0 1-.784.778H8.481a1.655 1.655 0 0 1-1.662-1.644v-.824c0-.412.156-.809.44-1.114l13.999-15.06a4.9 4.9 0 0 1 3.594-1.56h4.189c2.46 0 4.454 1.977 4.454 4.412v11.379z';
diff --git a/packages/twenty-sdk/src/cli/utilities/build/cover/generate-cover-image.ts b/packages/twenty-sdk/src/cli/utilities/build/cover/generate-cover-image.ts
new file mode 100644
index 0000000000..be6be36a92
--- /dev/null
+++ b/packages/twenty-sdk/src/cli/utilities/build/cover/generate-cover-image.ts
@@ -0,0 +1,67 @@
+import { readFile } from 'node:fs/promises';
+import { join } from 'node:path';
+import sharp from 'sharp';
+
+import { TWENTY_LOGO_MARK_PATH } from '@/cli/utilities/build/cover/assets/twenty-logo-mark-path';
+
+const readHalftoneBackdropDataUri = async (): Promise => {
+ const backdropBuffer = await readFile(
+ join(__dirname, 'assets', 'halftone-backdrop.png'),
+ );
+
+ return `data:image/png;base64,${backdropBuffer.toString('base64')}`;
+};
+
+const CANVAS_WIDTH = 1388;
+const CANVAS_HEIGHT = 858;
+const TILE_SIZE = 156;
+const TILE_RADIUS = 16;
+const TILE_Y = 351;
+const LEFT_TILE_X = 434;
+const RIGHT_TILE_X = 798;
+const CENTER_X = CANVAS_WIDTH / 2;
+const CENTER_Y = CANVAS_HEIGHT / 2;
+const CROSS_ARM = 19;
+const CROSS_STROKE = 8;
+const CROSS_COLOR = '#b3b3b3';
+const TWENTY_MARK_VIEWBOX = 40;
+const TWENTY_MARK_SCALE = TILE_SIZE / TWENTY_MARK_VIEWBOX;
+
+type GenerateCoverImageOptions = {
+ logoBuffer: Buffer;
+};
+
+export const generateCoverImage = async ({
+ logoBuffer,
+}: GenerateCoverImageOptions): Promise => {
+ const logoPngBuffer = await sharp(logoBuffer)
+ .resize(TILE_SIZE, TILE_SIZE, { fit: 'cover', position: 'centre' })
+ .png()
+ .toBuffer();
+ const logoDataUri = `data:image/png;base64,${logoPngBuffer.toString('base64')}`;
+
+ const backdropDataUri = await readHalftoneBackdropDataUri();
+
+ const svg = [
+ `',
+ ].join('');
+
+ return sharp(Buffer.from(svg)).png().toBuffer();
+};
diff --git a/packages/twenty-sdk/src/cli/utilities/build/cover/generated-asset.type.ts b/packages/twenty-sdk/src/cli/utilities/build/cover/generated-asset.type.ts
new file mode 100644
index 0000000000..2222092a7e
--- /dev/null
+++ b/packages/twenty-sdk/src/cli/utilities/build/cover/generated-asset.type.ts
@@ -0,0 +1,4 @@
+export type GeneratedAsset = {
+ relativePath: string;
+ content: Buffer;
+};
diff --git a/packages/twenty-sdk/src/cli/utilities/build/cover/generated-cover-path.ts b/packages/twenty-sdk/src/cli/utilities/build/cover/generated-cover-path.ts
new file mode 100644
index 0000000000..df63407d2a
--- /dev/null
+++ b/packages/twenty-sdk/src/cli/utilities/build/cover/generated-cover-path.ts
@@ -0,0 +1,3 @@
+import { ASSETS_DIR } from 'twenty-shared/application';
+
+export const GENERATED_COVER_PATH = `${ASSETS_DIR}/cover.generated.png`;
diff --git a/packages/twenty-sdk/vite.config.node.ts b/packages/twenty-sdk/vite.config.node.ts
index fca9a0783f..4ebb1f325e 100644
--- a/packages/twenty-sdk/vite.config.node.ts
+++ b/packages/twenty-sdk/vite.config.node.ts
@@ -1,3 +1,4 @@
+import fs from 'fs';
import path from 'path';
import { type PackageJson } from 'type-fest';
import { defineConfig } from 'vite';
@@ -5,6 +6,20 @@ import tsconfigPaths from 'vite-tsconfig-paths';
import packageJson from './package.json';
+const copyCoverAssetsPlugin = () => ({
+ name: 'copy-cover-assets',
+ closeBundle() {
+ const source = path.resolve(
+ __dirname,
+ 'src/cli/utilities/build/cover/assets/halftone-backdrop.png',
+ );
+ const destinationDir = path.resolve(__dirname, 'dist/assets');
+
+ fs.mkdirSync(destinationDir, { recursive: true });
+ fs.copyFileSync(source, path.join(destinationDir, 'halftone-backdrop.png'));
+ },
+});
+
export default defineConfig(() => {
return {
root: __dirname,
@@ -18,6 +33,7 @@ export default defineConfig(() => {
tsconfigPaths({
root: __dirname,
}),
+ copyCoverAssetsPlugin(),
],
build: {
emptyOutDir: false,
diff --git a/yarn.lock b/yarn.lock
index 0b294a3e9c..2083b245eb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -53329,6 +53329,7 @@ __metadata:
react-dom: "npm:^19.2.0"
rollup-plugin-dts: "npm:^6.4.1"
semver: "npm:7.6.3"
+ sharp: "npm:^0.34.5"
tinyglobby: "npm:^0.2.15"
ts-morph: "npm:^25.0.0"
tsc-alias: "npm:^1.8.16"