From 95fc2f45971f2956a87063ff5505e49d10dba41a Mon Sep 17 00:00:00 2001 From: Orkhan Ahmadov Date: Mon, 11 May 2026 20:50:39 +0200 Subject: [PATCH 01/26] WIP --- .../tests/visual-regression-baseline.spec.ts | 119 ++++++++++++++++++ .../blank-editor-chromium-darwin.png | Bin 0 -> 41132 bytes .../editor-dark-mode-chromium-darwin.png | Bin 0 -> 92285 bytes ...editor-mobile-viewport-chromium-darwin.png | Bin 0 -> 90611 bytes .../editor-with-template-chromium-darwin.png | Bin 0 -> 91867 bytes .../json-modal-open-chromium-darwin.png | Bin 0 -> 133178 bytes ...sidebar-block-selected-chromium-darwin.png | Bin 0 -> 112037 bytes .../e2e-consumer/aggressive-host.spec.ts | 96 ++++++++++++++ .../editor/tests/global-refs-audit.test.ts | 93 ++++++++++++++ 9 files changed, 308 insertions(+) create mode 100644 apps/playground/e2e/tests/visual-regression-baseline.spec.ts create mode 100644 apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/blank-editor-chromium-darwin.png create mode 100644 apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-dark-mode-chromium-darwin.png create mode 100644 apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-mobile-viewport-chromium-darwin.png create mode 100644 apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-with-template-chromium-darwin.png create mode 100644 apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/json-modal-open-chromium-darwin.png create mode 100644 apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/right-sidebar-block-selected-chromium-darwin.png create mode 100644 packages/editor/tests/e2e-consumer/aggressive-host.spec.ts create mode 100644 packages/editor/tests/global-refs-audit.test.ts diff --git a/apps/playground/e2e/tests/visual-regression-baseline.spec.ts b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts new file mode 100644 index 0000000..28c15cd --- /dev/null +++ b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts @@ -0,0 +1,119 @@ +import { test, expect } from "../fixtures/editor.fixture"; +import { SELECTORS } from "../helpers/selectors"; + +/** + * Visual regression baseline for the Shadow DOM migration (Phase 0.2). + * + * Captures pixel-level snapshots of canonical editor chrome states so each + * subsequent migration phase can diff against the baseline and surface + * unintended visual drift (popover stacking changes, padding shifts when + * teleport targets move, etc.). + * + * Scope is intentionally strategic, not exhaustive. The full matrix of + * "14 block types × 5 sidebar tabs × every modal × mobile/desktop × dark/light" + * is too brittle for screenshot diffing across machines; behavioral tests + * cover most of that ground separately. This file focuses on the chrome + * surfaces where Shadow DOM has the highest pixel-shift risk: + * + * 1. Editor chrome layout (sidebar + toolbar + canvas frame) + * 2. Modal teleport surfaces (JSON modal — same Teleport pattern the 4 + * shadow-DOM-affected popups use) + * 3. Right sidebar tab content + * 4. Mobile viewport rendering + * 5. Dark theme + * + * First run materializes baselines; CI thereafter compares. + */ + +// Allow ~1% pixel difference per snapshot — covers font subpixel rendering +// drift across machines without admitting real regressions. +const DIFF_OPTIONS = { + maxDiffPixelRatio: 0.01, + animations: "disabled" as const, + // Mask elements that legitimately vary between runs (none today; placeholder + // for future use). + mask: [] as never[], +}; + +test.describe("visual regression baseline (Phase 0.2)", () => { + test("blank editor — empty canvas + sidebar rail", async ({ + blankEditorReady: { editorPage }, + page, + }) => { + await editorPage.waitForReady(); + await editorPage.dismissOverlays(); + // Wait for the canvas empty-state to be present so the screenshot is stable. + await page.locator(SELECTORS.canvasEmpty).waitFor(); + await expect(page).toHaveScreenshot("blank-editor.png", DIFF_OPTIONS); + }); + + test("editor with sample template loaded", async ({ + editorReady: { editorPage }, + page, + }) => { + await editorPage.waitForReady(); + await editorPage.dismissOverlays(); + // At least one block rendered. + await page.locator(SELECTORS.block).first().waitFor(); + await expect(page).toHaveScreenshot("editor-with-template.png", DIFF_OPTIONS); + }); + + test("editor in mobile viewport", async ({ + editorReady: { editorPage }, + page, + }) => { + await editorPage.waitForReady(); + await editorPage.dismissOverlays(); + await page.locator(SELECTORS.viewportMobile).click(); + // Wait for layout to settle after viewport swap. + await page.waitForFunction( + () => + document + .querySelector('[role="radio"][aria-label="Mobile"]') + ?.getAttribute("aria-checked") === "true", + ); + await expect(page).toHaveScreenshot("editor-mobile-viewport.png", DIFF_OPTIONS); + }); + + test("editor with dark mode toggled", async ({ + editorReady: { editorPage }, + page, + }) => { + await editorPage.waitForReady(); + await editorPage.dismissOverlays(); + const root = page.locator("html"); + const classBefore = await root.getAttribute("class"); + await editorPage.clickThemeToggle(); + await expect(root).not.toHaveAttribute("class", classBefore ?? ""); + await expect(page).toHaveScreenshot("editor-dark-mode.png", DIFF_OPTIONS); + }); + + test("JSON modal open — Teleport-rendered overlay", async ({ + editorReady: { editorPage }, + page, + }) => { + // The JSON modal uses the same Teleport-to-body pattern that the four + // shadow-DOM-affected popups use. Pixel-stability here is the canary for + // teleport-target rewrites in Phase 2. + await editorPage.waitForReady(); + await editorPage.dismissOverlays(); + await editorPage.openJson(); + await page.locator(SELECTORS.jsonModal).waitFor(); + await expect(page).toHaveScreenshot("json-modal-open.png", DIFF_OPTIONS); + }); + + test("right sidebar — block selected", async ({ + editorReady: { editorPage }, + page, + }) => { + await editorPage.waitForReady(); + await editorPage.dismissOverlays(); + // Select first block on canvas. The right sidebar shows whichever tab is + // active by default — capturing it as baseline is the goal, not asserting + // which tab. + await editorPage.selectBlock(0); + await page.locator(SELECTORS.rightSidebar).waitFor(); + await page.locator(SELECTORS.rightTabContent).waitFor(); + await expect(page).toHaveScreenshot("right-sidebar-block-selected.png", DIFF_OPTIONS); + }); +}); diff --git a/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/blank-editor-chromium-darwin.png b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/blank-editor-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..b08bdad4a7f6bf8b15a2a1f90a235d61d861ba32 GIT binary patch literal 41132 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�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 literal 0 HcmV?d00001 diff --git a/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-dark-mode-chromium-darwin.png b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-dark-mode-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..c04cf2e0674fece724b98cf6879d0ae9f10cac49 GIT binary patch literal 92285 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

(|&;VOJj{+82WMI{Nh1ZeZTdn>(2 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 literal 0 HcmV?d00001 diff --git a/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-mobile-viewport-chromium-darwin.png b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-mobile-viewport-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..9fe5e39dd660b70d5a16b45b7d83bdeaf2c8bf92 GIT binary patch literal 90611 zcmbrFWmsEH*Y5)qXem;`DbSYU#XV?ocPm;Pg1bvgp~Xva32s3G6bM$N#XUgK0Do@<2P*;^Bz@x+i000CE^3s|B05K=W1KvKT)}d>+l{aeS4zfv)b@Y}{d-uYrNTPRrlP*lYa8?j{vPQ-F|2>j;P@{A zIRBi6SYu#t{(F)4@xu_b5$Gf5y#xUN--|<-m>>Qf&=}k*# z)nmE{Xl+5Lz9GhYAWx_FmH&B?0UHNvaYdKvYpC1ZntC-~<@3Ujkpy##@nukDdAZRz zqP`^Ei4pK24??3}TI1gSG5__1&{y7SwqhEz83kxF)N=Y&v{;59G{SmzQ2ms0Aygaz;$vk^!Abd!e@4< z)QRDKEo8mKJf=tAj!k%GR&@db6W>b5Z3o35RDVkhDHM8v>WcCpLpA@cxFcsg07Lr7 z@OZ%AbWiy@X3I`T3r5jt8b_?{LmOdW3|3Bv{&s~`29gc2Cuh8D_=S(MLjJ3m9Vm1w z22}a~kl0Yua0p4NcKPW)^$j}Dhq~hE7mw$PN6hB>l@%as*WOo`BsAHI)#+UdV575Z zvu@Y=w=S>J&31Kyc+rJkVXK*6k%rs+jO}N$NXq|Z8u3%yNB?cVE@|H!S()Bi`$R`2 z&|#e?CieS^WDT11OAO_np`S^ARH5sC>KCA?=m+v+Ah``}3B|fKkokOc+57FCRybgq zftk3!F2l?CXF$HT$?%4Usw$zQ#cRe`ia}nBaG(YR0R*eE)JcK0X+cDQbRIl_?UY>c z9lpe|1l3K%I9dIU;J1VfXL8@O8=b4|%{`#GEh@H}ON0gs{)Vp`a53F?462*8wDc%* z5zwUm5qwIAR1%F%@3C3aZ1#cxTh+kl^`*7;s_j2x`u?t4EjFPy*@u2?h@?)l?fU*f=L z=f{KuggNF0ev^f)0>_UdKZV}RO3U?H-qV1T6jy6U8cDKOws$EeO0mHp_hqed?)^7K z@Z16!+m`|iqCD4L0h&Dx`~(N&E?2dDcp+>r1SK(V12RTGhDs%!jXMwch%D5oDKG1A z82o5(gr7 zYA9}+&80oX`_`+K7}KD8o3ZIvC)KQeLSVH3p>v|JqrKxFUc17I8r$w_$~K^q^75Oy z^VR|kVc|#E?c4nOMIFL%2 zy(-G4_P;(Psy_Cxp`M1b`tau98pL74bu+b;S$P+^Zw&A$)zn!2u zr{gPTJPLC?=-uR5=Ro=a1|2)PEpSfv#jJgTO_rm0&UVCQM>)WHTK?mZnw03~CwWB3 z+(2bX&8@z1vQVsm>(jm8`e|?czSgdfuiEG)XB+7DIe6`M%Udv}M?r}wqA+D* zL_8HKwr7>S0Mun-U^S(_w`qjbufEg*;DGWC7pyfrgxzxyLDz?o-rqM0JSI=LBtNb) zsa0rj7&WnTwok!l_3af4w^r-Ld1%IqJUhqZR4xx{vVZ(IUK0_bW8sdL9a}qD^$u>m zx}ig;_wo`FkOA!LX2}*VR(dKI%G`$47J}dBPmq$O1RR|@-}-w~Alh(aYU}GQw!-GIa-q)k1l z%i18Pp0e=w-1E9bhHfy%2LH4>**H)%_|wRQYk z7fiB)4)6%nX?#|jc5B-n6CM@kNk?yxwI>gnHk4XiZvcX6q4MzuLl>R9&4fc)d>d45 z6Am2f?KjmNg_))1PWE($iyW5BAuGickg|#fYvN`6TGwI4&4zK%>TpQRlDj6M#ct6& zFTB)hBbqs%A(mS5k*~J^86Lvj{kRcUwQ3Ke5exwB78%bsbsC&L!;cKwHJ5!zEt)jt zr7Ol*sG~%5r-%g|Uwb5CxDp`#E!>lmMC^9sCmjiZ?Qg;mJ%tPIh{c#;_w`G}l#^ews<#by7Lz;My^+8ZOuSbqM! zz{|@+B!kz9iF$C-VVWZQ+n?1lG&S~Y?Y=HgM*j)@x3+6v zjcb^Og&bkdaYkD*w9!|1^c_P!NZe!Rgr}xBd}BEer!0ZmpKzSRvGbeQ1mroPqsW}K z_23`Mwt&;gv-vOkZy2txd^Di>HuZuaDPx!$k4m0Id@KdJ?Q_?;UHbSBW&+b?on~A^ zNRQgiP5y=~jitoflEF6XsIQcA0{HLHj$Cm*e zeYW&ZfNY;E^_L>;)N zd*e0FCz9H-^;{!Fcv#nO1+uLGIcBb-&JNAC%kd8KQUpa&x>M{p!^3^~9ZcS<^6P|- z8wVOjnvxx49H^WGjM-~fwqo9nV>R)UyZa!)psP=2*~|OuE%yg~L4i%=>@iNYJ^@*M zAD2%W2UsVaGPs@ejjlH|eP>eK?+;wO3q^fp)?X3xI?Xl3slM76AlyJ!=$#Ytj`>uQ z(~wFtYf&l`zh;xfq|%{DjSWn^4%`jvIolDI#o#0eI3IWo8qP@!qOUBqMJ~S8|B-yu zZeH#4d*hw9MRcJByL&JRdUQ9?++b2ZhMSexOf_h9TyYT*QwxB>FN};c4SwF8AZY_? z%(iLR0kDQ90y$+M83Ex*aYb0s+A0}zz9H(Uj~9ztmNtzG8nizp?7zeUc7_A^=EhBN z(^{4^9i^o7-wO#V6RS~WUWe*TLi(pilD*Hr z+l@>S+8=3MXfn2j76TbgHYT{Yqu8Sj6cR*Cru*vM&7;ocg_U#LE|sjyzK_5q4r52? z*{(GO*$8(*Hzs-Qf+-QGBX9tDg+Y=VyYo^9?;`M6O+&(AvUq1Uv0KH9@-FDtkx*pD z^#-dTp>GDG=VTIz?Eqd{X|JDnSwoW)cwU|g2Kwk_A1NeG>{I6*ry6?YSqy$iLJ>%(s}K=@%6+cYO_c9I!biV1=WjVS zoeLdSMSRW#XQ;Kgq?`5Zb!f!1F>NU+tKQzFbcH_-5u&Q!5%oC1-v!ALZYo`y<6YXm zX?ZN**|{jj)*%Jk1vYrDe>@<4_q-c_cz7s3pOP9`;u~ksYx_m%nt{xwxGlLNf0DR6 zePib%r6)@^BWL@UPXtj3dvB_y>^-Bey{S%*Jnq-V>$3vQfq~$W^}G0QiQGAA_3&@# z=s{IOSae8TU)zn5i=I!^e(UqnnPxNnfIxC`vXHQ_%k>`^S3H-7LxP%9&^t2?E`osL z4$R0PJHFHHi8PiN7E_2e&!lz_a0$L%P|omvea!E36RLOfn0$(^ zZjPG88>A9XQ>SRa^%TR>LOPRK5wyXEURyi#v;0lou`OP7cgN1_<4+T>A#U~jE;{c} zDWg*pOBOaHcrsJ5u)tNS5VjxWw{A>0MaCFi?aD)Sj@0jj$3|Lj!Lwf>OG%UprapcQ zG3!u@SROxNMc^nq*DJ0GPV3pur4#SQLL_Us*(hpTr5jE$@I9Dcd$}-L5n`hrApBnz z(B#Vc>mZIW)^*pwzoC&`zo{rE=WBWd0lgppF>{I<@_In9Dwh^s) zzWBISKrZWYsTY&;tpgp{16j`+zSy}vw+vi!xo3*47zLjp~gnMiCw#st_7+J@&J>r4J z#TzNHtlGu3p&Pl*H%h8QIe~$Vt)7u0+<^`mlTQ<2d7^78haJFfSkb}GxlGx2t+B?H zAdfQAgr4dtErr5oip}(M!6?GIsha@88}p$pYKv3i zcdjl~w4y)7JBm*0?D_Yad|5S^h&gxg&hotD94HiHEHQ7%S#t$TwM~Lf9yDKVQmeSN zMYldz+Iu2@2-4Msrlf4DXnEM?wq?4ds^#j!uU>Tkd?=r?vOUe`~${f;IsCa~E zLl97%d0!>*--wgJxw(MzNG&|tfTMh~i&HY%{La=(%3^hwtAcoqr)HWEYQ7t-<@5OG z>)rVKsCqp{P|)6d4P6R7^g*48T87{!oQ2zcqvO@}6%uu>Jeh|TropC_ohu%S$!>ad zhM`@vJo|Wcz&aa=_17m_g~!$)67v&1cR2sI#P7O&O19(NnE(ul(6sC0KVqUl{pcVe zBbtkAQ?jWOGQ}RkS!eanQjVP`9Gvav=zwjELsXP5BQ}E}Ma$2x!MOFxnW1xN!fK;E zi@19(Qq8hFPHfA)GRRU;X4JGocmB*T64ZKaRh@f*$WPdOySh1}wQ#-B9M^i2LqLZcKc4JaFLK`8=6O1E^^a|F#F)?SG+qt1^$zFmzfe3*rnwr*CG-Cx+4sx0+xoal{XQ4g4+T%CFpbTk~~#=ntbU-b6kRC)!k5 z$DK3qcJ(TNj}CN1QhRP}1n=04x@PzN#&7c1vV)INGtBYnq2rovQ4z3ca1toAZ1A9} znTp8~647U8B+|Wp+z^_OkL6#HqG)U;E5MNrM#o4TFXwIuMlM=yhvz|%MHrRSmi>r0 zh$(Ic~ReTzz{!H2dO3L^v40V0QlU2*?kxjod zP5i9+`NmuY8f_Z}bWQ|*k)$MQ(s6@vMjB_>_T156Ns%`mTsuB^R3>bB zYf{h>4O~~9Ic#=Ra8fI0nY}TK$urGMAe*DsOe)hH){;ye>;2w)2P`!5J#KaC^N7?` zTv<4Ok2$MkaK_wRyNTlZ=wLqgB)yae`nAJ9>gFwKh3O>1+@|&C>E7UF-|K78LhCQB zIk8UON1<2V0!Au}wjLG_vE|SZAUFFG9iMRV@dmj3&Hup{gOe?RD=SI{z*;Q3rvm1+ zPYsp*{vI`BjKw*oj10XM9=1$Iqc8S^uaNf;en&UC14HClMeSpLW$^u!B9KP>c3OLO zK>hU|d~}?Fqj;W)e!JjV-?ek~In&r-mL|BCEWZmD;Ut7c2el{V2{!Cb3$59NUv4WV zjkC&X5(XDq{Zr1M?8*KeMLA)2g+*3b%|1g&&FnNIl>IefRPzO}fRZ!K?X!Vc_w!Eyhlgz$tYXB&mT&%>~ z_N#cTIA#vph@7Lni8hj9i6rK~y;j#^EjFBl%TdTLz+r zjY~)vc!X0hLU0`=vY~ApMl!_>)U$@FnAsxkn}3q1;zW5{^XSWGs*d~Xkf?y8BI(z$ zZ5<{CG8JDyF<0~V!vq9`0an8u!>X~N*XOcL$_ z_U?#YKEtz7rn&6NoA|QV_5ezgX0-JiT6 zeQo_!kXRO)!PPn!bTZeq728-|USD#0N^l*d9qW}0|XyIWYloWSoebKT_~Jb1eU_PbF)`<3ah3Hz~~GN7OCRSRXvQbrht<6?`~!o>4u0C_1B}l;kQS z*JwTqD`0Q>fthV$By@aAP%;RNBOh)2=2ZC>DX;?P#!RlI0|)npuco z=Of`Tu2ujsUM_-#CIW=0pGlrwKfbtOL(a&({T%kdsM+<)XmKKQ_W;M+JZi4p?@UfB zd8n1dlSz#wk8axRZ<3G3LIAYv<4Q}emILq#VZt}XMQ!(MHEC&$!EA5&UPmrzS!hAt_i23+RwG7GCGIJLgPzW5BWHymgRBye+c%L#AS*@mU0 zu-c{svWjuBye=KiQ^ zDP{((xcO3eM^RG$WDZz`isdAS!7Au%{l7-U=0vXUsW$~OLaqkHJZImuobQpPR9!e=mwk`UOT^%n3^0YA!S@IXAr0PC3BW~5CV%>c}dfOrfGxl(G z?Mrh{+1#K@P(ap49ihBE#$ka7Y4REF&p&d|JvcY?h^a>G?9Z&s{zE~@9`kPCAcv># zb`qm9J9X(%U%N-cT-|<2uDpBr7wr419=F+nmgB92#BWB8PX5vZPoM+%$Hz|$F3?LS z!FRXLzz5kPf$?4u;AQhcR9DAP(vc^cPaU2gLx0HiHAHgQR?69om>wRV@;+!!9OlbP zPfnJxN0|eR`e0^IW#3h>K{&{ zrlK%EQ0l|X$D>9DdH35hVY^}HPI2{P?)DxsX;zHME*(y4(D?NQ*R;kQkNY?y^7mx@ z$S+Jotb|qq1K$J1)0Np~PlET<$_oyr+5Nr(IexMGG0C*0&k3jRAb~nUUG-=4HYs3J z#_iMS@}B1+kQ#VCHfkoN1!R!V()Poc7(VQ|rPeRpvtvGuoMG}Sp5thdo($yo7q6<} zV5{g?vz_wOpE(RGtsRK=l$QNGc6n1T&c+?h%p5k;!1x5bMfFuRAY>S_)^3Ku2`C>l$US&5e9Yv>P`>*t%lHEkLu`jMg z2E`V(T4lwKam1z42&uODck|{6c5SjVxw3RMJ+uX75Qrb-^QBz{)l#-`ea0YeYoGfe z;j{f|_s_q{`@7goT0cH3kitF76ttFib=|y^VmzY}RE;sKbuRpx@|%V|Yh}+A^pp}& z=kDK~-W@}E(d}0{JggS&i7xwrY+(V~D0u4&GGvyS2v5CyIc)O5Q%% zFSMa8a%ex|+tGR9K^*)iULm@0+obWS$gST}h6>GC+n4%cB)C&M5@jo{$_O;C_N?bR z2Ab8($kLYg1FOl`4ULvXo4Tn*gJNN%vZz(#`r%#3CQy_|__fGq{sQzgUVTo<(5KA= z44Lqn=~wYskXqh9mHu7kc4;AtpX1_zgoYZ?(ggZ>9M!vVz}M;oEG5)#yUx6I-un-> z#B^41{k)m$5Wj;tG~c}^LqwkJERSy#e7PI_?wv`DoBdxFuqP_w{0pzNzH_Bk5&9D| z1c%3;lnM)t1s>quQf+D|M&_a9SYv`7?LJG;Y&Hiz|V^9?NKvc zgkt3xzc{b;ohABt^uj+!$nVdNS)kX{NY8dIB)>sDQur&C*FsHE%pEG_m|O|h+n%${ zqr}Z)F{i;}x7#?JOSIWDr$%D7adgVt)is^V3X!y3AAIFFW6x1_K9!pNrBk{dU4xup zGIk?aEU40SpZ%82O32SEN;vp%7R{@%umVtim##&Ju4R2)=IBs|RE__it|`k%Mf`in zv|)=QkZ+93+dwDI{0EJb;VPx?=1=k0B0fhAW^-0XBo$7}bPR@6YO2LyBDd3t*=rnz z<@?bw=}IR=`p^+m3~Umz%DERpwIf5wi0qMDQK<1YVfCmHJGj()J2q6I&u*sia&E9& z@5m4fH52E&zXjjNzB~-;zo47&jZoE-agqpfgOsl)%zk&^7xfqSyjr~}RBKYck$omA zooAX1yj9$zb-3Rul)Ec9j167*Mthm>a#}WQAzru78`^FEc-%eM;<|%FxNfj@oL9qz zQA1tMF9|M?{>-!#@wgB-DEZWHwsqjGY}urCKJ8npF*`?BQVYJUJ*e|f-s_vf?eI7K ztE}}NBH{e$!0i&IIYc-L3H26+IeU_U0%;{a6lJ ziWk@ZyOJYj^}CA2UxuO9zNWs0$Jwh;_jLp~)&HxLipK5F(_mD|#MWklT&eaJdQmDl zF*)e`QaH)x`*w?J^hAm3;CrLxIp+qbXqQ%rLwt$NTB6~!sMZ%sG5}}jE$(hQxy7nB_YCg5 zV8q2e#v-7GF$u|j)bfnY(BYmD(q3=YT1)<3eaq5fWw-z9Ic1&ohc+kWd2L@BO?7=` zjk(Y!P(5kg`T8He#N-{#cT=~Brrc3m+Kl(_w-xA-;f){~_syLyRhC?YeA46cn}+v2 zQlO-_P$!`IST3r7zrVlV?x&K@d;oWyK?JdJHqd=D%*J}yuqtz5wPh-M4Qg3{I=a5U z{>5pCUalv&&DA}gS86Mt;i29Es?P}qNO30mMZ8Y19k6cFcV z`|8GqC_q%|kqPKA#(_Fp>ZbE($_+AK^LsGrdqxDMZHd}0knf_2jzW*nETitT7&-NirXk;qlQ9Tf(!MOGo>bl%>sPime7Y(#) z13-I$A%@z46Ior^r3GTDUKlwGX`h=X;({mwO{RLrOg?$Vk$6U{^{DLa_23pdZh!5% zcPNN^g^ol8L$z6i-1qPbS<`@JMEvuY;nrHU+uO<RyZ>Nnlf+j91f>gw1)0o0tr|3rW|HHH;)MXNN%@`jKP~OVnHV_i(D6-F3PRP zgIcB@`Ag=H3ju+=rNHX%Yg+!j04#0O8ldJB`}=rsg7@7Y&)fMVQu)q5XO4!x2o(LN znB<{CzpcGr)*%(SUaawM!-ifjrW?;WS0{pRw#36TPtd4Jx`H1ax{!-C)T`?+NBgPp zw5tc!^#Y~{x!d9L5z@bzm&3=}zCUY>?Gfy!)axj|c=zTC-&0e!+soo*#hK?(d(Igk zC++aWrR;ij2mp)vWT?32y$84MNEQu`2U3soGq~-1#USzAdJN}%34l4m+=kSoKo>Ua z{PZQF06>WK?;hkbdfA^UG7&eZzvLlY`~B^m^1s@fknWsr49>Woaoh51hq0o_t%~nX zG#NHsd8%7^mk5*9e{^Em;Nr_+4BXf)`*5_>XSCou@*$PukjQkB?b7_N>u#;i|A>UV zBlVX7`Y#vD5Fi{-?EjnKX!tzRvK>#EWzx@nX3gty-L=Nl1-4|a1UrXK&RWR=i*Kvb zart!Vb^$(>VOaVIsm{NuqxBH;e^p0sr%VzS+(3c6cC~!RAA&L0_Sr$=5&t7Pf=vF^ zLjm;PKC|;|`kkkV1Inu^kuDyOkS2lVTI*L&ls>P3R*_R>U=P_ul^$hahD2DqNmMud zl{BTZl(!T$uOe1$#{~aTioI=Z&an!9C-053mGtr_hfet+IqP516=d=X{gHKmJ24)8 z_DG5%J+b@pSf`>buYux_*7Ac2nh0s5)tjN*zk)8;`m_F%I!34dI=%7T2WUaw1lY#& zA7NSn5G_nYYo2^~?cV&87ab?b**RgW$BbN*Bv_}CZ<2;hM!Zk>vo%fsDJwov3Vyu- zKx)YbI3nDj(*s{Sq-XgNcEGH*bl#R2WqqIC+pPljbhX`yKYz2u(0E+FhOSY* zVt=%N3-MP#HWA=`x*D@(=wD|?=0QJ$k9aZiD`9_1mwZ8O+E1IlW@5kq);KRYAB?CY z`i~$H;P4Ro_^(d#Z#(<{5iEuP$p3AL>DUk2|2>1h{oiRI|944#`UYVHf@A;HTLMa) zMQGw6MCkl_`r%8O{%c^B7(4#jCRd$i1|aouARlFnVqpvjz$Tjt!cKr;v5|4Wt zC5>hQUS=*aSRX2~G$DC8QyoJQ&PU0o2`T?-JOMcmb^_`9+tyY{4t+q?`khl2pig>6 zuPg87!Z-BRUM5A#h#dQn{Z|KyAOl}MG)~jhG0@bFB5%N{`JpFlUxybqW@2r_L3;dT z=iiEu=*Uw~#MD&m*K|>Q1&Z10t#}7ej?yigz>i~0kpE9{hu})HkZPbGDAmFI09|G+ zR9xQM(q2-QfAk+Ispuaa-gLQN5XC}>#^68>%c4fzCav2No7%7342FVbPf z8tfYP&IhgdW3-2FYAY71mc3F|fhJ=B(1Np%679>6rOmlip8^2wTW_gB_)9EtdZ+Ov zIOk}^>ohN(f6}TO^&gWEzk+c)1Snj?UR$vmENc4l13G<5E+b3=zz`-lpS54{=`E)I zquYiJM%f`{w18HEE*zf|z>5V0D{6ED5M^s~S(>AK*8Ogc;1;ZojygC10GEFSzzHde z8*A&ml?+^>!DAwh!p3}M`f`!4?Sopem2-aaeVGnkLS^N+=~K2R5$JLak$r@&D&9a) zOgcshkwJy-OJaqBvmwqszQw~!NwMsw5uabj=NUPTWxu$c`>WZN$Pe=m!N}PhUuu;u zAxbPP{xZ1&OHd7CxjUv6`89{_x_a=_IgFwORk?@?)Jb<|r+hchuCl zZOkW8W)9C${5>rHYM%W=XT$O9QH-8KH+V$NB-aw>ZR||$p=3X4vo3fh-PBq#KbbGnvf?dVc z>RyVs>aU=NPnW`?FO55aj(t#PTbyFoY#vy&BSRJBt~yDP-s1WK^H=m28;;9u?~fAj zLOC^bXJiRO7e{-dY`&1!%0X0R>)xi0Cm<7?bU8m|8cWWVt}2zb#zrSPj}ouS+em@G zNq1tb$;rXZ*gUHDs*P#;vewLU;U;41WMFT^k)+{EYMZoSX40`EZGG|7jq$DoTTxUqm;?w+|ZI9Wxot@~x6KAn|!Jk&0QgLESJAZxy0KCqBTVwe)WZmS+ z!v`tsU~&H~kAR!q6qCUG*pvuT;uQ|lCP8j)%{L&;a~femV9U*H2CvJ`#u8G%c{ug- zA%XA&>hdZ*X`n|n(;**&Q_j^l;}u|Xghr-S>`6*ZN8#6}*S|mJSKJ`Him2>0gScV%ai&{tRHEX{Fkhtq#T^qE;1- z*)Ib37XOz8v|jBb37~RmDX{?XXtW0bRI!_ii>E1*Qj$)V>7ho%?heompxTw%?q=B0 zP&Hdb;g?GadVzz6&rDvMmlN`;pUQdGJa||H5(PYNVz`F2 z*I<(jR5~opG;gh3&S`s|ReU!#E!7;33NRGL_mSB^rc+fa3!2nk>3~a(W_^KHfp*G$ zSOKN_YXr~(};CCJA0?`>G@bfPh73jcx4jT0C4-C-*Hv3fKX6dF!mqzMro)3hcGfGE4OEA&e7Ki_fX!aNAylN9|Qf-2%c8N zp|kKY8O(X!;pY3wN~cv6nQT{W7O$0oo96E;zB1dDoZOBLXQLj~{GQn74;_9S>Md?C z<87%2`LgP18_j~q_)@7RnH3-32Zd`S3B_D+bdV9>9GAc1Os2h6nmqSaKz#cSHf46E z$>B=BA$Ii5J^a4PNIIa#UbBB29XP_ciW|(_RvTeA1~qB&_<-8G4>~(7pHUkVr<|_0 znToNWBG2&Lx)@C?=-jNKS>3+=wKbVPwY54BODkr{jofg3PO`cxM?`~m;ptp|$0C9+ zuaSAbUsDSDUuhS!&oj|%!>iquo6pFG!5U=~?vBdmGuVw#ZJ|72^vLT*IWxXGkd;Jf?b zoUh_40l8p(CmoZ**hx0{tUZ1eV0NBp%vGpIZ>*_}p^AEE}Xt7JJ*)EGnEd_f2!ryrf+=WTv6radu`U|7g8+r!y zg$SzswLNxXJh@nkddGnx^y=UErcYc}OT+j~;!42)8HXTHxY1X?!IzJRuA^@z4AX)# zS+US$VS$%{HyTZ)2L_@t7&$pppZ!LcqNjUYo2UQn>_plo#p`%@&-tF!(!zq+oP>n> z0lLA)sBQ4@jG>+7Lcl3~N9w1W0L3;Q*huWNMx>aAjfs}gJU!mt8TkA>hd=%jWD1QKbl}xQ~VoLIu z1mun*e}8k{2Zsej0XD-)pMlYATG}uclJZK$i+Q`)>T7TFci&377kG^^O#0FQBvk(T zTicgHLNgHNzLx7#o0AkNtzj4{8NIKguUHLmwXQBn4;8fNd`Q^>T{KIq43ZkmrdQH+e*Z*}%6N=T^iO7UEQ ze?%j3l~fVFw{HWEy9w+deRCIiznjSp*DS4oLc&x%EKnz>B{n*1@zZFt=p2YhZsh*r zE5gn4hMyHTv#NIM6yb-MjN<)QTm4bYS5~|%DnWRtuDFZ>w&67#Uf)N=Zw!sf+h^3! zbaQf9p(rA6$YOek9YcL%$A{@%OlOeCg3M~6#TxCWo=9cYp73N@LDSMmKJ?%Q6~t_j7;;;uWrLsU}~ zx1I=(yY%$$d73Ozl2S6S523q;k&#he`_rm+WpL7^x$C`|y3n!rDeReQ4GHi(E2VD! z!ongy-%Dv}SrRfbQ^8=>LQVI+1ue$7@q&|D-qLQkI)5KCab*8Mf5`&VRZLspHrhE* z*7*(flLX)K#|h7SySXWU%%e$H#^^*jeEBRoD&e#Mn^}++I397y*i)r>3TEKHq8a7&Xl@S>X72G&?9qYuNH9 z8|(T^NT^xa-!}Qn7seFo(}SbC{osY%)%v)o?YTNCi;Sl8ojS{Z ziTSMw+n}?sTlR92X1O>bj7THt! zXjlakjADog1+Jlm|B~j-lXL~H3HyfHbDd+N?$}NkgjlBI#D(ia*}cn;yOu<)=4mX_ zp@}P&YHA}XD4+Go7^=R$2uvF1-mjx(OG~S(RzfpEFyzydPCFj4yAfn|@JVooT%7$l zG;oSWw0WWVD2;nT>Fx^rY(BqREmLe;0|$`fyNurUSe6-)j;u3a11ps90Zl85T&PF@ zysTFAGRi|FWF*$s))a97HSq$i%x%(_NfnjG;Hi4m1QZ^#o{!_3a z7!_c^^2aMWIRZ-lo7o9gW`zj7N+Xtp0ps~OVUNADlgE{rW8$=_jcsag(KzcNI+&Lr zVK){WvEp`jpT7I7d3wvKVM{g+qaJ6{;Jg{LcjhD*?wLA2*-S{Bgl=G3gh{@uo65DY zDY|^>SEpT^nIL^;<73_8tRBRF`C_O&B&p@5Te!O`{|nI=DnhH+y$dSpH!9xUt#M7( zb+Be8LU#Td2M6$Xi?2HNeMrtbsc9PXsMB+R`;5GIpmW)Ww?&TEWqOr2sAgw~HcJ$b zx@P*WLy4n*(Pi~Mca2XESs`s5Oi+gyG3t7NWm{vrCjEQOlT_NZ(xB{p(?Q(uynhaz zEx;D$Li86?)HUOhhjh$i89~*3d@f%)RLK`=CZ{Cx-l3TUAZHV@DcbIX{|pUFCN^?P zM@uNGr1NEm&~d5bKYNhXDWF1Wcco9)bR^0uDGGYfuN+GK-JGnB-J12YAl0AnIeGp` zrD9>hl^Fm%?-qRlMgNpaG^*eoco;Y;Jl$h#BdU8yOh$5W@((*9YLP%g4#p=Bc_|67 z)D4N}M|BG(Km!Aco(3`bHIIoNsfFhvoL$W@uv;;6;ZFcsGF<2cV@S~0!1n4Q;1%YF z61slC<5RUE4M<|+%zMo=TK0GJE)IrbWhZ7fwt&h#ANXoDWqff&ngsfTQ$CDxsar$ z$ZNHx0c6BIMa~?%Vxe&%uM;}N&1uZhau-euRfHhy_;%QSef^y41mEXU3RV$}<9HI$ zrTqbIDy0B=Rt+ydkT(Ap&<#CvLSUe;qwC{;$z8)J#c>5$MU(zb4dAo*;EXT5r5mGa za1_0910I3~hnpU}?YDt1kKEnC#`6^ad~}v&aD?QstMQuBsOpS>x zTa)sS$uh+gd?~C3j=|CQlru&Xz>`&kAb`Ihm>|WJhscSey+iiz3joiKW^YBuXyLZi z?u^XsG5OB+4?0)mayIvM_M>j zizh%a#oT~N%V|s@BLU^;OO>i2S^9UjDicRI2Fn81=l$Ps_<1px{>uVP3H|~Mbc2ai zR%LFS?s#%F7QAc89WVcyIO-zcBng{9v23Cw3B8?M!^8kIEg!?t<><6^S79IgsJ1T^ z7xQ24i-PFuxby*r5T-;+n66^)t zQks0zAfi~r{p-Ha2O@qcaqP_%iNcQ2oG3+ztOOHrqn{7SiZvY$luqP`>hCj)VsjI* ziPu&D%ICNIeI3Rdo*)0xLG%qLxcvode80;o4=;bMiekYT1Cj-c@4t6Vi8VI%??`w6 zF+`4OwSlkqD)@NqG1`r%6ELS=%w1utw?%7#%&R|R*akGPjb8+L;(*YZk{JKeOPT{I zU8|%jmILT;-=ur^C7L~d=$eHe^Ym{zlyYqkL+u}kJA@SBEU^PpIZ5S+; zpn~zq1W@vH8I1GE4B#$UC2661zFSjLrCy=?mtPIVO<*mK_r&ea)Y0+Lr!`(jn^UXx z&^Qc5-C=x0&v!TBjgPSa6QLbE00h`f?UOut$hZhmW%`ZfGdjRo_Djn?zNy>q`CoKX`L63!qS$r7pdy){5*K}T}#F<>%8o;&pLLsd~F~yIYdFrHd;3S zDSxlZe#E0kn*RPA92}HlK|33e1V#jCVllTP6~}UZKP@{&?^`x1_}~cPBRs-Ovps>~VfJUj&CKIPJi1-t_Fj9fx#k>mjIqQc&5%MOE80F7Z%pvW-HOhp zBjW(UsToUAbc#+3Vsq95GE(9vGV)f7DSfwC(9i$1fU>4m z6=QY%*`)v0yoSvt-K)9-TdgXVT&*jAR3 zK(!pUrbA&qG-AG7jjC*QJx*^uU1d!?B9^77P^QMf2*gapJkG&3#IGZ}0M^}P*>UdY zAIPNYa5pjF&E`^{p>DR_c z{7uxbe-jU?ySAE+R{dJj@>cwZlr2_I|@H!R@V z-lnYN-djl7Q=7z`CA|(yP_BVR4`YSw@yf=cxn?}RF;?q&n)TErw z>7kQPTue`F=>!|U4_TP0FA}tetx;kg+xU4Ka z{daQ1H4_>l82b#RB=O3|bvWPbLRc)n8tbc2WhThaXRwlqL0VhVn0ILF?NPh8vToPk zuTOX%^?S!BKC;QAJV0OQN*Hp1DRKLiH*r;j^qgMZe_MWSl2-r_Kcq*GzI-l|!Qgj4 zzd5?DwTO1_KHU-!5I{{xkl_XJp;h3w(6Qc>i=Nlv{kTEeW;G76MTC`N)R<1&vu-`v zVQ4p823FFLFsWz-%QeWATH)BuiBAy~a5I{vq2;>zf$o;~+X}Xqe zXLD;C6H^zSpY&}}_jSCD6UfU1<)|5&N)#}s8`1+Fzo@UEn3_&U)rhCAD7ZNl1RTzH zZ|x{%1k#MkfORY zn%vHzuCAe>SG;tArG%9l+8T-mo=N3m1xx0WE8=h8`V|xqjrfB89vfLDy3Gzg2Z9im z{Ln!Cu~2n7K|ZSga|8rmzm>y#CPI~^cr~YB%w%Y*Nibr-K$z)AG6AzHP>jcP%F5`bqjO-3a*+=+j=0uRHoYDrz?D6(fYn7Un13lmD zcdNvCbxyvF<%uCmItx_C!M_gMJkwTw%Ds5+l}RIwRddPu#WeNlyMC-DL*+G|q!_BI zjrLl>>ajzfYEuq-1iqkN<8}B}*AFS&W%C1NOBG32ob7h2fnV~_v2BUI1U6_&XrZ-J z$|eib=!)G7EpCn%R`O^|+8>`VmNn?JH}f}Vw1;M0goRpBV1`IaO1fWc^)%R4@AGFW z()=^{Pb=J4{%Cf+t8-YsS@ckCOTcf1D*86EM`Wh5RR*XARpiF|ItIxji- z?qcUT`{OxDG(SV|7h4L)V*{nL)NB zR$5>egA>nm@9PV8z7b!`RvnKwm?6-M%`QH(aWvoB*XgP`h5fy=R$O0&9h71>V~L8q zziX|LLrR{g+y&D~uExtP0}F_VFA}(h8h@H05E(Iqz{ap(Cj>n;PEI_v3By2)Z!eV2 z{G-EPOdc$+OET(1nunj*M5h(Y7W0JU&+$P9AFFuhm0$6-N0iw8v1BQ4Q)YtKHaj*$ z>41A~_4vH2vYCz{LX8`1?{RK`dYU`{MdZ{7l(4XGi*y_Ee@PeVqPG@hK<}C9CHM9^ zRBWZfVTUeg3 zNsy|3y<~h(u4jw982^Laeg5vP=gtED6L}?#FiKW-iBwawZmWKeMV6#@)Qb}2m;x!f zN$w>u{$_1}{SW3z3I8T8>f|@?W*U>vlp`d0-UVT!0<=3sjp)gtXqtX`Q=JV;39{=E zU7$jp4gJvx$Ck3(KJSyWT@g%lJqaI96}0&tD%aL`3nSCT9Eu&v$Kk{%5p5J2F2l%B zps^ty#S8cm)8Isybv*v6iu#$vZ;!3fr)ztyyHWuLU{!`{qz&Q`_^D&sC}Rgx(7SId zuK{W+EdAYZdZ4UIcBo)?qpA@V`c9EjceKm5tq?_kf+dwHyKQi3V$Xz#K)10ex<6yV z(xqQai{?_y#d-7Wx8tHWKV-fx=)n5HYU>>3aFV>HRE$6hwzMT(tXeChWWA@ zJIh^g%9R{cXf~$Odu3h>>S+GRkZedznF5q{SJPw z))j4WfXHq&G$gf(mqF7lpQEc!H$0@dIiaBxRV3Tgjhp28xp8PZ-7&hc)8+WNLMfAW zw7!j@=@&cA+XQ+A!btd58tkkU?Err3Wn_GYO{H|&xpG7R@pb=QWlfPd%7tuB(cgPJ zA+E-X5sJl9qCiOih))d}kr@sr4RV--h0DJ^-Rz$0Vi4$MuM_Ac@+0u!SAO%}V$@Bh zIJWfP#Di?($x%O7+OH#TEvYVbjT99%#Gf^SlR_zb!50?)(u@Ci~>?d7(N_OlGWN-lU{L44V%iw>A~t!tRSG(7W9 z#fuMFe)sV|EC5*qQ~l+$+^FE*(acZvvn;h*{&eYSEkmmBXGpC5{<(6d1PsrjdmG?j zzCqJ-T&A7^-A+g-5Iz3(aFilXjuQgMO>{!5$h7+tsJ86!0cCJ5T5d&dY9>>P*g^xZ z?^~&7>cru-GfL<%2rB|)l=sB+1kB(0m7+p? zoy5tmdmJ24^O!_7EnjS^5}cCJfoaC$23M9FT| z1Ct07gz$R4UzBcFvaaaGF3viGWlJEcQuI@$* z_)seW*JknUu*!|E%*s(aC&J2CP@kLFKXG&_@)WB7!9<3t{KEZ{kM@#(D?E5<5$?>^+FiOu5$&Bz46~YFOYYQth z%ctE!MS$oL2k&_N+};bpUj?{j^Ee<#nvF^iH@yHA9|Wy^S?4kMTQSi7bY-GFxXb^+ z!1)pQS&plA&1WCq;yB9#I$J0Bzh;lw>*gjh&Aci94vIT@#4TDY&R)RJ>HTql_K>^* zm(wE(l@r>j#`?9qJX|e!*)5O#G(h%S*|~Mbd$a>a%yD}>g(7*ts>osFfTlM-YYs9I4$YicGC zN*BYShaBgnY!vbe!NaX416ng(pm3oE?xN}eBFa#@LhuWs-3q0q{+6fKigafF9QPf9 zMI{e2xAjav!rIfeQ;Ce9NY$1L5d{;roL!KgHMMopDW27;YsY1KP>fsH#)56baYbxS zLRTX3$}|@*w`y4~_+?d898H~~&OW-y+Ks_z%j&z6bq8l>OeBl1dyjg!gY)KBd;nx; zg#zXfa_X^;N>~W)y+#Js8y~>~AhVnODDC)80jTTBV{>F)1eDLhC?dY1ok?qaMr^uX zngB*nhsV1XET#iC#I(A~(X4EAlsHnYQLh$ioCY%OTJdwa3srkI$~h$nel+7^{l-l} z4Lt-v+h6*go*(eYBFGw!>aIBL4bwb#3=Nwqagvi(?ui>|&o(tFo~dCKc=dq{_x<~T za3XGT+RBXB&YtcGn@PK?Z5d!RmFmrp8lq8RxOtdl;xqgklHUE$m4DZY5?hFd zfyU#$Y-K00)ympV%I(}f(_<*_Q$^WFC;4)b@ye8+vlS@x${mh@Sr7&Z_XiCP+&Nm$ zV_+l$i~f-qUuxZPM^uev7JyDN=+JZS#VBy`6}URGIgO@b%`m_wyBxG2TC|KAeV5B? zJCf1CRqbGc$dB1M44;dY8?UV@^hzfftBb!3o2|EA7>YDqJGQrFyh!tRS4sl>ILIJ* zD%Km{_N-%c!y3TteiX7i0HoA4(AsH7u?v`wXn4HsV85qhCkYP9mb%<0#nRilQ#@igP2qO$F4 zC-#GDOj@3H_i$fbUB|=4VZ~9#=Dlwq$aTjc<@H*d?t5bRMK+1;csQB}dy@qQg>Kv) zRf`pPxt~{5S!>X1H@>2O9m3)v?eAKGBL%!1%|+-3IT_xl*Tbg6W?p|eP2#uwA(nA< z3|*+T{t-`<+pj5F;d+-Br^pKx#4S>$RiTp8opo5;j5=yvaNT+m9K6A(4h-fx*&YWd zir)W3?yJH26`X2G2Z59*0G$Jy7>@RRZ{C7z2^aK&l;2^W1x-FR8n_KCo{%Q62PI^V zn*nPZOq{7tU#j={K(6;8YV0mdt=1^C?~S$KU#}1_v`QfW>8AaH5hLGjB_Qjfq$;P&!(mF$IXp!x8DdP zKwo(Py9G#3T|zBeWzJ5H(%qTdg4%*v;sUF_QIW*`?h}g8F|a2@lQ$^ORKA<+4N^{_ z7xoVh4zBI2)o1(I7N60b*#z`t+-```$<{F!J zHtVo?FLPD#GFzl}_4T-X?=s_fk35yDk09)iKw-c<@=|e_h2YBs*`E~iN4>?KV%6$k z(mevPw}gLu^N|!ew*3;;TA#s;XtgEQ+B)!6*xuObL(4QZ3V3woFvMs+MfUkq0a(n- zm~lYnve`xRhOMOAMOs!S=UIS<(r}Rs9jt?`=DUz?tEm};PaxcbmvD2Qy?=cC3gN}?82V46Co0~}CcUjtAE8wi&<0*04xZ6nf z{0ilqoHjalmR^y+H7aiNE}@M_2F(n=a(76*-EI8l&niPInLPXcaw<71ohDp3Cg0k2 z2WV1k&NYy?QkN|s2R{j-F<=^ZK0+1W5`PAv=0$Igl!NNlqD%5P@O+$Z*-)?mTUkl4V` z>?9*hHBWi|o7^ggtVMRM-A;eAH-BQ%3sJJ?ZC&)jQ^>wU?N=u^SwPq-aa!HzvNM}} zr*L?4geM4O)@l0ii|tRAz@lZN0Hy!Q$??w}mGHcV2ETo;Kv&yn*6Z0$#gmx}K6)PuG=MF?PF zY9!fQG`{{4>+0c=2>qC4TilP1UPeE2p#Z#pF+TFTSiP^U1fqF#g2{wsG(wvR%3uS& zQt>EV^>_MD=s&y^dhdm!svOO@J6C=(@899ATVpS@VB>H$I$`st96{39 z#-^p^H5H8s5pyPESWvLl(%xFBafdf=9x-CWj7#a+V3iYjn`1+rOQwaLopWgDV_wfw zw#w&x91g#MvNe`L_h5DP8R3iXanVW|v=`_3uxH~@B}sZ&@f@)~H$F!k0}F3WqdCY= zUYCuNNu|l*;U5`+=#&(*PtehJ=F6)`=jV+~;8GgUWeO})NhIM23u#Xpz@jdQrzW~u z%}~16sXW>YDp$@NokAJl>z&)a8Z{ib)2cZ3(Y+IH^@p8_i%YKQusG_0+c*suxR-eO z%yDb9$Xc3?q&1m@J9hKA6IlU*UQ<~jy6EY)N=uoo zWw0e=4#uu;jZ=JEHbM%0*$4(`rtKfl#$zB&wLqSFlrSmB63u3(yQlB=z{9rT{>ap3 zf1kJ7n!CnDZf0`Q!BiFlpRZz}xh1++3*e_l&>Hq&1`!CYKIUM2j$7Q*Ixmh?(w7&p zM2CY$V;)rM>?-QYZz{`K9ZQVl!8ZS_$J0~6?g^sp`bw>6m(lrbUn>%J%*Sv%B!mzg zm7rg{TG5hw2QJlyg-WIj4BCxL2?O)iRS_uG%7kI#EiDySDp{%{F+# zgXW?I$c+U`6E;{m{hO1uJB36O_>kNuDZ<&ha}W~wHPm0Zc`C?Vihx{CEU)ifPgjOn^y@#iR$d=s%xIFCNoF1IZYkX&-zMD2$Vu5bT(Iq{;X)wW3{}>S_6w&zs@DBI zjL$;ef#4kNCXoZ#MG@siv<`y?&aNC8G=UAQtCr_^A?%)}Si4ULA zCL}ap8rbdK4&!IC=WE{dNOd(lPvgtCH&p%>TNpD>RL@=#J&Z#~9iY~5-k3RQz%-SW zFIPBqpFH}#q2n$}TX{$>X2UW2`ca>jlPvWRxC=h@njAcE6H!jA&4AhoB5emcY3Aky zlu~6qrs?|JH3*U=RS{8kH4jSNtCqFy`S$!ecNB8!K?O zp8JjK+6ygi&>Biq^te94%3*@&wUg6_f}%CG<>eF#_-+vBXak~<)8^>fc63juz@I`h z4xWlH@87Wib^`x_=b;l=Rh(dfEf3?;%#U{T zLN7vnwu&FN3zy5e`3QxJ*l@;iZ#Ef8Qfe)>cn;<;Bzc` z!Gr+d_k{(>miVsh?;AXJ;r-J-`%SLSaSaJx4_w$c_-{s(pUL`Aqc3`6mWq?J%oSKx z)&WBpVjlPWlHAFT9*4{Cjm$&zwGv%<1T)UNO?JF3BXib=zR8+Z2OdYY?~TqCk>WY4 zu-<%-dZ5_Q0E9(Ps%E|fyt9wMhtp=ZUIf8-Eg1L?N7G^R*EuuAnQkPD@Ls&w2T#0{ z8v_W^aTgm4as$cpJhb&SHIDP7KFGlRg5P5rI1Wg~+EUXoPFPf;`jWsJ;UT`14@xcH zLLR4evRhVcsM9wmXJ@wLw;4k=bEQqD7vElgInOc>FY(b3!!>rNfe1JsYf0qYl*VDT z87PDdrlzMa1FB-{Ct@xlW5cI<+#H0$G(2x0^qTqtgu4#EFYfPm5)SM&V&&S z1BHYybs|jD1*~N>R%#Usm?c#Ne*0HZVL099mC|s@GEPv^4*?Y$_^>$vw8bj?Ve09L zrO9tQ@-!|k5I>|Tw+)_q+4{X}1=a93US`b;6TCFsVbFp^a`J2@i2J{j6q~^hq_z(e zqqY_i1UXhKwSno--b8xd^R{>IS84(ii=gV*4v-^_+s;#9E~h>`s0=}I z%{!enX&}@bMtK?_TsY)b$B};h=h(hXdzKI1SFHDAP~H`OB-}~UEUS5i`|QAXqC-1} zX6t78*R+*&neXD)E z69nYMsE~;#F~2B)baD_I;5L6>%J#P(4G*p*K5cDT=;-G)d^jK+g@|Qz4fKqdDNElz zfBX&vCU`$zzibpRxrNHVPyvadZ(pt3%66XNdm1E@Z6eVoVyLqv!SgmDtOwQg(lW8ro=vQtijS4~0$4qRxi1k=^*kP8CzxQG0f+&!LXOg! z_sv4tcbSw_y0wmeNWo802AN%z0O76q9Mt3h3wyw&!V<*_*O7oUsou*o&OLL*4z5Hp z%Oo}EVQK$+J%|wf;)7=7;q&(xlgYo|9RGJUv{DR0s*scPKo|mn9Uc&}s@_LCetEcK z5Gg?Dp?pg4&*FRd2M`N5@Tb2YGh9My1T7ua?2)a^^YhA|;Lwc!3I38SK$@j4u}ePN zJl3DcHlr5}-7q)H3B#^?P-HmGb0XxJa?YN6Lxh|1UtsscC=>0G6OosUFJ5Ou$z zU-FR&0J&qcX}HEXJwAH=_>pu&IwH`>UF>uI1TPFc<0MIAWHIgD4<@E>jO}3v5P4fi zi!7G2^iHZDG}Pdv7(!+_#)FntF=hxm0A`!d4~ZK#b9z>e%+&211T)44eKU=1zc4D# z&lZp1wb6T=Tne}Kd4;MLq)Z1cp6Od0+G@HFq!zAr? zQX8o=2Y!uEM^~eliu#FT&P(N}QrnW6=+a_`Xr7MdVRyS0Q9rNVOjC-cTL~`_jd=Is z+&1F5B>CtjS6n9+ltBpUG}N;Nu3wdDml~&l!=3Q#>;~Y zJffv6OXQkSTwg_acDh5NgJ9`N{#-TXRlmfe9fNN>oxcHJ+37N2m0|NLY9~xYr_9he zMcvgY?jZj>_0& zb(c>XG;cK>&#_?g%%VwdB7$T8-;P(+CL#5yMP8Q_V0D;;*t``U_~>MlP8mr5bneq5 zE9SS6SOd6o{)i;>quee)R&(0+%f>x~MqrpN9?1mv(!T z=^xp&Z0~Bu*OQAVM0!bCmu})Pd_+sfkS5jCwUS7QY$W!2U$sg6cHy@?w%hyvr`;6} zj2*(0rgEYe8x63>l4);)tEm0yVPa1bJ~po%(eBlgW1Xih_-Xf_MC3+BeiuW3x&5U>#aD3yCIu6NZ0kTq(MRjWaU* zs9%6DjhN`wM^P$Um?Czq!guKtl*D>h0~#XCkD~A8g0-i<3uTPr@l!jG#h9>Kcn!7q zQ$@@rb;tBD<)m%p{LwNMu|fP1@Hu$2>Y{F%(JT+qu-+wFNn;y7hnk+Rf9 zFucY}1qV%uX^1B+(mZ_(-pQqBRj1jRQb-=o7vT@iGnw1zd3@8ZpSBngl`Ghs5k6w1 zxlzv=gz(Ud=2twbQdbr4$?W?vSF!3gnk_`9unCOyI0_%r9-HNxqQIC&#hL{!qzcCI z0+fx4?avAwE;ms7`)tDN;y2P3Bcv|2<4QtG}XUZ0 zUw6-{q6Yy2ghO1XDE-7BP2P-4L*?6p$Amp4&&Igo@Aql)mX66ql~cTLG;4hCWPL7& zG^7jP>Bv@vBp@RUezkj5(8f%Ou{LqNh*le|72p1uj0|Is}3rpZux*1t$iO*IUUud*{hFM~airG8c4T5p80cxtU9N zd(T(N5WSI=k&&=}D`yEA%kGlHy{>$FiUK(clGzM#v_SMQZ)i?{gP8rXFDoBN=1GoB z${I4GO;LLJ%+=Ms&3Siu$xWB2qmnMxR;Nir{kv{bTpEpe?goSoTU0cF(^jXNE1Gg@ zN`e5AOVa4iJJ25l_u%M}_F17T&V64E2a=k+w@}_7t)rU6O(WdjE1q*jCGFP9#KO;i zHhT1WERi7oC(={jx!Kt*-L+UCPhfvCD-8NIJBvZY_`vP~Fj8VeyuEI3_#3=vQ@D9w z`~TLCZ(9N*85}ofRly@$$(FH(-K)gK~`W7E&y7BlY}Cs{Se^1;UWGY z%ZLOVB9QIewei;iKaEt0-w)Z+`$dx9e^VeKGR}XIr-v}hM7~r=4z51nSMX<&5 zc@{xIxeNt0fgGWi$A_nEqzlmdRKRKc{Tu$vkP0^|8^vFa{-Q3Zfy!b)ezzi)r-32I zn2o-hsR-*3V)g$osRa+}s{sZ?&*S4QD@6V8FZoiU{>|JT#^k@G6^LN;JlJZVpiaWq zYN+HZ=2d94$86z$X+*po*>seCZ9=Uu~ZF6bU`IT%xCCb%#Zz)+;SwZpr_eI>< zpJSZGI^BKi?ONS^(-F^n$X>HUrRUpQ3j%!>Gh>Eu1yTD-PPf}$Z*}ESwAnZhGnq~V zrcXN_pOtS8sFhf_kk$7q?oCV3UCjJSjkNt_=Kn3gdL&V| zbkSoU-PKP(vUP5~Zik7GopES!zA$f;v2Gox<%IVK+gxieq`{Mf6Ui|zP2_|R3*9$A zJAqurp5(BuQeYbq!Y|6eetvI{${QwO05T*ma%N^C4Q6Q2#q7$STp>& z=jlx8wfk7@Le++3r21Dxfy%7w(@?e98WwsMllkW7l|@)Tim6k|%*RSYm5W?C5~hB9 z-05(j64;tbd8n^)vHyGLE#$X7c^Bk|%LRDJGv(1p?pMhC!*$xJ84huhqUM^x3)HjP zj%&Ytn+NTd3V-rUMz~&s2dv*U4)6F=<>-|2-FvK$uKMXQjpWVJZqP;DDNCl!t*$ztuK zk6w;SZ)94P$TY%jp1*qj_#%t^#`A6zi9G@U2a6TS z8?u!py-uGH%1*v<-6y}@BYIZve2Oii8YwKP8!_Hrokvlm; zQsI=&PMZdm<4xSBfok3hPpA?`k$4Jgd+SauzowZ&!ojR(z6|n8xI(_-)RWIM9fG6t zBMC$!bBu!nEk`{{@8|jJsm6P95^RO&w~e+crL<136)wCP+nv|N(9zL}@Uc0(SDqqm zBLJZDq1FWuLK=we4-nOMQrjtG;HP%jHhR3UysgKFl8loA;<_jMO?8t>77J|?Dd4=Y zo>2nP-7lN5nwRfx(VC}>j}uwAIggD@EE>}jkpy<<14F#g1onr*?-VyZSXld9J>~9S zik$Yu4bh5GVh%N*)oY!EGxKIQ0LfDQcp6bwHaib-)~8x#_QFV+BhF3R+{m89^Q2JI zeNuduMjJF_J>P%#7oZ3AmATg$7^vjRBZ+MG`zA}&E?u~({n--^@SF&#8Ikq#qNibw?FBpL^u)bK1BzOkx| zF8n-sasBOLJXwc>{?U3_hV>H0#A(`gat)J7S6=6_z5>)yg8w@-o)!E6gY~1i*l|;% zY3{Ak`iX@Oy67C3*qaAiy=+BPv`OFcUJD|>KUp07NKr24Qf>O{gxiWyDayUF=Gn<+ zY=xs!IaAK}q>;g;7{BiW81{}VDT4C%-_(xF#x&^r4Egf9d1&OR6@k?LW#@1Qx>I{n z0a%Fg4@PKJ$efUgV#(pAM0aU;OGlokIX-+YWQ`@wuJ!)n()ac~JwJIIq9^0RSDCY4 z9Z`7EMyDHRPxFQCBNr+9`oCUygKm1tZ7Qal+Vx>PFY-=uo$jgeGsr-uBpA#e zC4&`W{2#ydJ~F`rvq`$QA{>am!OIc)nx1%pUWr=?nMou{j-| zJ;$)^&jRJ;>WaC_RdDYM@8q>&%?*lE-2cxuXcL}C$m9Xo(HN*O^?n-<1nRJZmV3*ZYjdFYfnrMQ(t1<;FYguv?LDzK`FSDy;jWeAt zP}m6EsM&+=+UIxV3dEd*_u%##JVB%|YSwIB z*Yh|{M79g~Gi1X|$<7(~yx!mQ5`yS68u`6`X=U79-E=*~=VWj&U~;-HAwCXn%V^6+ z&bMEr{B5qiMVY)gr~0%Pp0BglorcFaec>1BpHx%7C$=1=V;}2^&H2XkkZMF*q;5cL z7wV6C^CH9f&vtWkeK;>-uTB}Mxr|0@p0Ek|_zMf0L53KG*q2}%?g|-ym(^BQr4Qvt z-dhnnn&Io^(TFmS>dxWjfepC^FEdUxw#Jev5sLw1dA0faY}5cJVACB?ltVtiQDyF=BMmvE8)hO0#FYWv_EcFr))h7`h>iM9`fA5&+PAz zYF;w>Y*f+P-fbW-+L9tcwn4Ht7DAZi!|fyuFE~$W)3SCV(H3yRnEb*1OxX5*QCRe9 z>zCux``_AH$cUAfYrkoN?MEa-ZjD@czq(Tt?F5A?OZq7eUbp*c&$E0eyE~tFLZ3nH zn^W9eAoAkINBdfZmV|w|ykq&PN9=T9w`1e$F{F8oT)4}|-M2GzHgk@ zZd(T16d#Y%`3^DEuDr0`ZtU&*;84pY5W1^1wVvDz~M55ljA~=@adf+cM#!QF)m-M(=$f;|KGi47O z^*J!&_St_C=5{Z5!0dFg$EiDFk4_4X`KSR3IVCDNN^(jjCoy)B1>vkFt^$1lF>h~@ zh7~2bUXxTCLNbJ3%+PWa$YRU#i7Qo(V88l@b*19?&?gSgSEheH@3L@<+So_acK5Xo z7R0=>&^?_^SV!4f-E#p)?$jS2qW}p!`nTC~hpwBqueVZ;5>b%CvwcD-Ubkv!XmKHC z**fmZ-YZH47q4y9%uJ0O828dTxH>nD2<4PkvMHF2Xv6QT^?tJtoNjSJvv-^EfRzJh%$tsG zT^xvC5WAo4ms{_|e8A|Vrlrk13JQ8cC7bNJQmMAgbW^p8|u8$E_ZpALz*KUXTs+_U)+R?i+gjpBDX>% zohejmTb$ffMwzFW9LHZFK40bbr!yUud9-y;4Sc#+`aeNoEZf3z_G@zIJS}PKKz=3&H)(q1V+m zb3eL(!x6*QWZp>K8PQZ+MmqK5g)+C3QS*^J@nV?M{Eb4!`wPJ;P;nQsysp)sw$W(JmpW9dze&EIsmHiG@Iu;t$Ohtim$vY9 z>b&~+Oq}SC4p$;4U zgA3lbK;gv83*F~;SF|~~xjF%pXUU|qr$#%xI|s+NJ030`B&0`@yX0Dqb3Bw^;1_pye@6hMUaaDPUXd# zuF({&%=Xbb>dp#rR-^&7j*L_Vbc5BJci2~5e!@F{$?st|*L(Bw2#I3 zB|Z%!4NlOzFIF-BuV=hO%=2cMM9j@;IOU7#J?;-XhcYP0g1Ua2b7Q|A?T z+n!mjzG}L^>NtS99Ke>r0kBsK?~|a8ca}@bvBY|cec{)MKFPNU@ngO8wKBn9BhGjLN}#eoT0 z;Io%J2X*!Ime-Rv5EhP3$xphY0H%C`_cwGIP2Vq<+d>L8L{*P58G2O<*eF-Ph7-e} z4S0s7?tMJ9T6ZZI^S2uZ#xvE{1^t@Jr!waCQ0Vm(tEm|(MHdYszfouN&v9#9Ms1U0 zW?J{y8@_Y*#Mkf9nNhEWK8I{Df(IOQp!D5qIU4}DQtR{GvVyV0>!x9P6YyRp4Ep2N%i?c2M^@BtmD$(h3d-`OZud2Lbj?aidv zFb-YJLZg@Ea1;$TS|hBR4*R1wtgC~SG``e)Ma=sQc5X}Xd??MZ15Eq+`;-gSVvVcA zUxDj<*A-R69mQX&L%(x#?ao0x?rcwFlezTq4;D8suMe6UVBI~ZPO^zy{Q(QfIy%wy z0mTA8@9&_s6m4xdUL#v0rqt2hPgXh@bSgG$O;ftMd$(t^LasV;H?`9>tI?mJbKBkg zQIZpAG+X)|E&%iOg8{)w^LehrGB8uw(E~IqNX>!>Mjt8$1}FA42L9#V>;XAhmyh7h z;$n+`Yam7n=e%-Q2)sxo>iS=ejKLosB4BBOv0UI0g=VvpvO_oDs z^*>_8$$wPOyU(<=9QogS__V^Dz8qnHX4ENrLrWXq*dJ=HL{omd>)_yiwyoWz$#=fF z`?a&vw(*X(_+?YNEo^<}fbVEkNg^o7w&4nPg|d;7z`9Y(J&@o7HqU_L_NLoAkDeI% zJcY%T@cZlIo?;exQr9+$V_MVUEyvNvY5&j<3qtcS7QBxntO8}dkWr_hmL0XMIlA5} z*4gX-o7!iXqxN1~QetdQh_broq=%2tx>8fXGkfyB{tHF|LCrS1Sz^QIhUoX5no z`1?vOJv{-W)FZ%*wL3X9N?k82E7Q`}7R3=1N$%GyV_|JL@LB)fwpIg$qF9W%Ju?0f z5)!htCY{u^c3fZ2CkU3#8%L-4^mJRvm64HQFHf)hUMsaz2B*Bl^_PFig$m{HF1`8o zcgVPqoSYo)&KEC|5`#m6B_*Xiy>2Z^pFL_GynFz7XVL*i9VqLp{}#%iP{sY*F?hhq|5+cPPzTuTKg5au0iPdG+@V!UP=<}CMQ=Qa zB4i$5JP3dsX|LY=#rl{2a#J2^U+}R2JH%hK_23!nI?GI^`oS>ZoF-r&H^+}R6wMT8u;_wJUx5IhS-J>DG7kdAE4Lm?q zpy`IV4|`kOcIZy1D*fp}t&FH;m+iAHW4|SRnkA5zxeT|AI36X>_WZ%}uK`z&G1u0T}wS!1}8n?Y{!e zg8jFDgu~_iJaQ>7z$s|x83zgRCaDs$^xg0mdTH-b++Q^$0>S9eZ}ZZML!Xcf6CRwaes8iA)l=g!SbB4t+@?$$o6|ab z)#fhUY0xlsb^&V6o6G3_^=8H*xO~jqN&&dz{UsPEWa$jbIXy5BVpj7N2g*g`5~x!f zs@UYB?e`bb`Q)O1F-}8+;SDQ~J@?&MJ@Z1Ssb$qvQ>A%Yi*)nKfqNmva+E~QS;36N zi&+IVolI%Iwn8tvk5t-jr7TDPu_*qEiNB<)D*Tev%Q>b%C#zCVuj5srMBhqRilIGt zVH#rKVP~fHmz)twADp>dV#A{n$8wk3X!cj5{Fl&?kR(rLKIE@Jhh2@a6Jj6{l6;Sg zXdvM#m!y$4isvR=S|ci#B$ik2^p*bUyX8Y2o-j$b2hs?3;KjK4Sa$;QB$@6@o2kGd z3vAAd@{r{daFjO-PvLUA!0mc}U*q-a^>us!@SuCXc&h7e{Xb{XmE?MZtwp0`&SzcH-h1SpO zf6?{UVNrfj`|ltEQlfMSC`e0}G?LQY-QC@Y0#YK~AYIZO14wswN_P)6z|48@`@Yxj zoO7MS30IcK<7r zp(a&WXBG^{j|29?NJlnb`6gz?g%H}n=qf*lfg6rPJji3!tAY1&-<3B?B6o(BtvK0= zV{prvuUG(aTx=i0qo0v^9+(@)oZ8Q4Y$v~`HmzR4$) z6{@2&b34Jm#NO-H*uxi_D3;Gu2Ubl%AFed`1@Y9fvxOtn18aHeDMCDwN!iRh;1tqH7SQX&^wvvY$ z1|M)5k#CG6pP5C$M{S98Q@gg?ub2ARKFE!*X#qm3A#CRjZHs3@P z7qRDa)TtaVnsoA56uR(`JR-C?%if|XT6Gzkh-8E;zVP%Fv_pmCd-2y7<$hOUa6H>O zC*(>sM*K`#_S(IMkzqVE$u>q()FUENct`%SI)>10hM%nLZKk|SMStN)T4M23pOvby zC|enz5#HOJAkL2VuArSJNYL$m5P&BuYV1Ldgcu6@o86^Lc5Y zU0j>8gw0RQbG&2@_}AApJ>6-KT5I&wZvstUHqT}D-j7JpjnVs5OEr#MjMZ8kCTSqM z8-CP`zc9fZGa8(cq$CN|p|uhs$iKjf(9q1I7sd5hCU!YCZag2Hc%4 z4S}=WjD_IA|KfTb^nJqr=`@(d=~ zKp_Q%Z<+f>y5bN(B=H$Q1KQbpY}BWC>RqyR!tX03wV*rm-zsH;)t#&7){z0eOY?HTOXK--KtEox0%=dZ% z1)xkCADnhgLxX2jH5C<=bxmp2A$N8$K=bDCaM8_9tY3=X_yCO--I#F5u||W#ilHqE+1KZWKJ)&r zBvIAX&Smlpf6B$+D*+CZ*PmW0Yx2<#t@Nw@)uusaQ`J~U}} z{B^EQgOv~D?8~+c{#4$W5R~QZN6X&dlBf5C9vfIkYd%+#;*vDGF($orR&%Qlx%dQl zFxXJ^d4ognw{MuFJiN5;yG@Y?W$m>ab|&v!YF%tr>Q{=Ka2j)EKk37IF>UO1!6F7F zE^{Y{p)c9JjJ&l6W83jDNO)39a(K}nu0osqkTI59Tu^3YEj4dzwXpbjcu3kE?&N?O?Cja1gAqwsn1}9ktU^Aw2HK+0LM-Pu^YkQjA(e%{@3k!F1bJat_XrIRpv%LHa9ybc!g0dd# zE!`GpDQgFE+&357G~^Qvu;ac{zsF|u6O44}%-k4$(38iWa=XAb2!4&4b?^G6P#XfE=4VJYfW5 z7E-n}ebCNVKEJOJN=QhQPhl-pQ$5@sN?dO26@&<~vw!VH|Mn0{ma#&?ACxn@)%`6o zM(KEycYJyRhZ=G;Gv~>J7Q7MdB_z`ws}(Kq8gIpWzf)-cmn}?5qvE!2sm}x26|KB; z4Lk#%AVr_3N8XmuGd zizQB0>dN$VLuoSo+J5V{9h~NSv`-5K7_>X51P!+#B4_gYyWa0aQAgwux{ZVf1c{26 z^Is4eyuF2$j?t-*2`keI$K-hk_)>_?yC z{I`?>j{u~6`I*CVqr_`-$IrB7xFrYudwW_nHVvHx6=NR z#eX??2;V(9nQeCfI@8E#f!23wKcZti z3YAS}%LvKUZny)4aZ0b|3*o|DHgv$drd@8>I7q+89$YZrH6>qe2#pTL1Yw8namlw{tIVQ4#qVDDryY?%Rkz|q+xFas-4-08?6_b3a!x9dk zcr@KpSnbw)9`T?oVCXfsEF&W$kN4hR6&L-#`g@0`H&YD;(Qo)pMl<-V=mxTU1?EQO z2P-w&6oXi2&gi)kVOKQpz>f2{!qiqhadcN__iSTJO?0RP2a98WB~<{-=t8@sLA}fb zsQN~>+X7~=jc?3P+Yo1eKZN>4#9~WFwx1o47_BoBJ;trhR_Kcl*lmg*FSpfsv?DAN zIJ{nUUX5N;T0Y#~+&o!d2sl%;Ah_si=y1 zXDox-;v-5KM$`xRqcERZS-KiL%u*8_+Tq6sS7`z*9!9AgSRM_H*GEd}(GhwGpuc2; znM#KE0hc@baeYaQy24+m%#7&>W#ps^9G$k;WEvO5D4eXN!_U#Dl;g~a@#eeqpc2C@lwOwF?EeL9k1VywOYCp^}?JcxZk?Mqc(O_RHz< zMGNzkfb7?fyIe<34llmU zj(|kNtG#eK=x0g{m;L05!smBRmYN^^m^VR1cFzCNTg0hn1@G)O;Zv6Wl;dB z2p6|6u#AW+Pw3GEKpF!rY)^78;_f>ai$}y~{QUenVq)X%nSN0Jy8+1tpBp=(*Tu9N zA9;BP-n?n#S@PI3Y?O%;Wqw1=fDJH^N1Gy;Rq|Vo&h=S+?c}CHnZI&=_gPw$rd&NuXZy z#`I+<))6_2XVJiPIKF3vX9Zo;;#kkCRUgz-{UTY1Kbz;6c%Ko!hDNjaqXsA8(&#)q zFeEYwxXz{_0up?jr+_RC-y87;A@(m$)k2-67Rlh`W*#p%zcqvT8$^s&y zM0vuN>;F#*6jk!nsR=5o>$<=3zQ2`EW{YBgB0cBj;Un1DovI8F{*}{eds|es445O} z7OR>+Xs50Nb{C?Okvp+j@y)9A3;lgUEZ?1*9+5G3%$I8YqZ*PF3hh@IpcsIugK`P( zX}|-~T+j4`C<^1KMA-HZy=LdG_{*c&(wj$e>Y~=)A9z?}TChJdQl((mz`lH;58jxM z5!H?gK}R1;=g*8y=lACKAt+UR?`u68!Na3<>3=K8OWixi{EHXe^Z-lkEz(cGD8TP= zp`oB*Xt>|e$&Gbbn9cye%$CTO-?OHAod*daza*1x+QaGl(FQ-KS-zStO@iy)ktn@c zD)$S$w2)WHsnfGXVe(!Bk_*?g`b}y%BM5;Bvkrvd2LJx%=w!SKJ>QW|sMYVXcLx6O zxGIT<+(Di7Q{NfjttxHyruoW6EQM8M!J}+6?y-nFM-u%5q}x%S=;c=qCs^%xN%8WT zEWB(vJttrkEOG&_3kVNF)>^sUq0!fSkK@TdEW5C;l2~te-7nSoF=^gY1wi&2IL`Kd z7X!cm8u^TsHW$FYVaxPtE7(P`1n}zCCNumMIoBLma=3oFQkaX@9%SPcNj(Z`^hwNG_10@&a`%Z+$|$;8-1a}3Z4gGd_`|NErSnS^D(t~N z?wl#L?p?DoNfmdVK7!}-qSSHa`t40eS)54~x?`gQS2tHycncTjedgl?tiz5c5BX6# z^A@PB*OTnZ#iW>9J3H`)B?FE#=+M~YC#S9g0LhoZ69B7DN|J2hDl3#F#(9Hl2s__0 zGlPg19VjbLiHZjCx>*X@U3}F3tKkxmT^udM+cj4R^U0=V*7&_rxedKqQk|nKw(@Ti zWb6zwS!zgOasPHcqh+<967bODF2QEg`~DJ3=%(+kr-w?9w+{f&gp$;lKjN3L+GAiI zEra3R#V$NNO?JjP&2GmZotFE<1&5(gOZCUgY4Pb4*x@1~&rqHK(ua zLIG2G=bsB6CxmmQ7Jxh0NlVDaa8pED!^s82F#sB-RZGTgzxXN5;L|4~P0jxJuv~`4 zoJsa-F1rP%%UROJKyn?Pg0Sb%zbhQnJ_Q*9WutUEk2! zNC9I9z2+;;ddpA3if>5>&XEe`-{d~G^%Q$AXT4UnY0y5x=LPw^G~c+?;J|cyJhyA$ zwbC<_aP5;%l@Ab$a@271ts87L=L+E+v6Hbv0e8icS#5%C{#B%WOvyKgZ7X~&uBT3$ zi*+kY4FdO*47I`v%vsCn>42>ciX@7tc|y~@7)71Dj8e(Y-!`yT%D&mf3$%P$_mWVo zljz8Bw3nz$?H(RRMn;ERwyNg0EI=s8X6yvKVFb)O#jed4IZJ~?L}<+z3%YYY*IM7L zeGQGGWujkhw3pkp7idIMj@&)ik$^W}?^SJ8AOWhkA5H~}>GC@xBOq5HWwj3iPy3Htm}zGuw$RK53T~}G-t+T=X=woavg^_$Cb_J;?=aR@ z?GldE^_?gi3;E1nS0Z&91G{0z(@y&aN8mwrvNj)a-VCdvcG^W~b8f%SO}g@j z!Ly#qof)h+1^uSi00r3ny5P=&z=A+JM5XuRhY#toTR=nnx}ec#%Xz>!#wtWz>1O4J z&V>79z28wRQoy+TWS!3#;C7_jRgr+ui)2z=1c6xejlax1i(Cxy7#mE^$huij)@HX_V+zjJ!2UBq1!hG{+BfaY#z4y ztE}ZKt!o6}$1DZj?)vlrsftgIJo9{)zqNxvue7oAb7u6 zQkQ0|-N0LElVOx6o$r16b!->j)NN{Br3smUgRNd95HRapZIqr)2tUkhGBF)Z5zx9H zOh)(-cL%HZWO=QRbV)iGioiAsD-NQ&+GQA>;L#8a4`ENFKxb1t>6mrzyeXUI)Khr3rpq`Nv z)@=AIZ5|Tl_(&;P4>zRt!$H{ViQ&Hvk)Kuwd#~%UjnzGOahgDt3t^=dC#IW~K z*sTtC60=ThLX16gT#x(SN25X4lLq(O4j8EpH$Fb@;(}CYG}%&1-^b9K|E&d(`yjr| z5c!~g#uv;BLqK3|g%+$MDtcZnjR^q?+(SaW!(=WcR@PFO{xz^0L4J-zvU7K~!cC{v z=(21Tff=-J@PbwviE*5DfRGDEr$|16;rPyWsm{QC`;* zUN5LQrO$v`hMmPT{A2hV2uDre7}c?5h@LY{q*4~k_pc{aOD;Ir`z;~^UiZ=^Sv&no3=}5 zUCs}h69SwD)TG1p8(ps&gz#1Hxm(3)38FDcCbK#TnLyW7($xD)#v6R4ou>LEjnSpjSCIy5v?zun2^YHHGJN>vqm)o_;ae zOPeh5J=yPTc0Umgdz`W1wp(k8zQCHcul8EOE_t)FD$!rIwx|ADG!)`6N1fU>6y46$ zKW5=Tp;>=sfmYenfL0pMt0|gBm>iWyt%03K=d<&&WA)-ZJo{l z-dIeS;~wc(<9Qt*p+!U5uaB3lNVQcHw?YAFeD2dL_X~T77-X!$)*&G@a)g}Lw}!l* zPlY_c`C#BR+yP0uXC;{}Xn0^`WS!Ml^hTzamScmduI#gvQw(_cPb&ef^tJLwjS+*a z*4p^Qjul+%Oc>{{UqkQ3I6fH>f|o~++O*I(^RJaRhg~?;YCF8U1-L&0Mr{)q@o$)! zn7HmXtn0$*M=Gu9i)*D7{RV-pQp8wPlA=0w;e+G$;mQ_HVqzjMFOM2qbkFfO{m)iL zaVT_4iK)mPjn9cYuIHs`G;t?JQdz^4jh4&ZJLg}UNW3KCOY!r6+e4xYl3izgT&lL? zM(Pk;)#qp*5TR`@P_($2RjWE)7;>f!mTBQav(@)IM3l(t%Hu@rs6Sl z720P2^W5$!Y&wTOZrOzU@9$L`T~Tru#Wci-2Xsp3Hr$;7C3V}`V#Wx|}OQ93O z*)p3x8uB!vIX6QmDLS*JCQ#!=GxREynOdRselK`mrB_l?-2e7m19zoN7yM;1dHL1@ ze#*`SU8!|vMOs?!pP&qN-N(I$F2T3MckGzlI8JYRaPyR0o6R1Tp+ix5%pdJ88jW%b zvp%{digYu>ZnEfG)_yofJWf&{>KXNSZPVo)_ zrB$f)(x`m3*^06#`ZGT{A%m=nUD!nymXO0*eck%OqruLjc)&g6K!V3Yv><8q`>bIh znJ67&tLXy0DCprPybcg}tOw`^z!q9G+AxX7eT3Wf;|hfSa0mju0)M|adQ`9!tC-=6 zd6`6P>gn1zf|J2VYacYIl*G0tm{ptzwY+TABamiu>DPjoUnK`Pq3uLz$Q`HNMH1Mw zKBue0|79zt@0o9nhvsN*uM*TUWZ&U<>q2i6+1;>q+J+)cvpc zK`ixCsEIW-I2q?8ed4(CwNogJ8s+*GFlSneA@TM|Scww!{<0!qd%G&(tcM29+BBUa zvghwNpBB-UdXJ6-UhZeq@nD5+r9DYRU>7yLH+!?AKlJVi{YtJk;XV-&{yZlq<;()` z_oTxBnkN4Gx9_|7&m3ay>4oNFTgXLLfh50HHe=g0CORnD3kh=!UIs@{n;f7APXTq{ z_+JGLEMo0%pw?ZXRbK?CgwWB#lbi33ay>97n9m`OSsxRR!`b<0xM{efN$8jv`10DN zAHw>~fni1?Trz*56{_CdMI=S_si+66lM4aA#$KhUH6X92NhaZT0NPn3E#x={vf3{? zRUkdZ7j!?idlfZLwzps7?!hn66#y;GyKq4+P6?Ky8Y-AyYgB zl7q&Ht|!%|rSR+s$U(E_U>pYilbzjaZHnxweGJ$OLm}k-SLi69`XA2TxQJX4KK`6*IokVi z#}29-3By;pq*B~+2ll)LZA$Tc@T3Yx`N@5l92bL{biqqNgEP4R$qD)8<7Y`!pE#sZ z?&82RbtH{|<~NoxFE{dvdb6ubG-~spMdqbxk}a!cz=eLx*DtEp-0qOdHJIjbnWaR) z%;O>+I-v|L-w0z*D7j?kdjZd9~P$1ayea@&?hsFZNIF^VK$(p^Um`pUTcoJ@I_S($3E z*`US$axqFrwF+_yS%_F3sl_0{V0T+TJhUTwzo4>g0_ zt%mQY7OPQjTG?sozFzJxowF~Rv5w=EUib^(_LSJ}+!Id}+a18s}2f)a<^} zG?&r_1WnVru2JbpJG*gb#>oUd;=V$;NWG@TxrcN zm`@`jLS|)Ic2m}^rz*5(HBZ&L&S~OAzk%rAA&--iu~$>j>d+^1V#M8@ZT8tJpmtKY z(>jW&aywVxRysQo1F4WHY`jXVZ-0Q^Kk1WbY%^WUUZcT?Y}DPvHFgMaD|K*tdi`%w zW%T_nzlzIfutD>jFw{Qd>b$cv)^RFbwyVA#|wXEAzzLFw=4Cs4- zqvDXKe!ZtiIg3L~?0PqN_1=$cUe{Tgn7czylw^r%XZ2PUDEU%xj`*VThl~g~V>NOc zT`X(RNTPUnXbj%w=bN!5zTtJ|XX(gNj+g-xHX9q=FdfUwjeL)^G-BhVqKc{tYHbYy z=woRAilQYO;9lZ&V? z(HxzM%^VWr$292GcJ|H6?I}CEj;^rQ=bQbo2p$3W?rsvfAoqR<<0k`}5HwUwRA*=B zJgh`pE2R8wF72r>$hhhzDqJwLN$ju zvwmOkcN>_*6TW$xLhU{Aq;oViPUD>`;l6{)D5~5JD?|e8>+?*A+=ngM<&>hQ-6ULg z+*!>|moIzKUXlxB&(eT}9>xx-0aV^;QUnQ$Znw?e`B3Y%{YqOG;7$4C7K7UsfLyNn z9>4|DmK^%Ot(%y$xH&>2gQ;Bf6kO797CipG$W?d&e*jxSWbBuX9Td5BwK_J>7Y{Yk zdq#k}6&*O(2XVZ8DmVA3^ZqFjWU{8GXDZ)$n4*q0gN!}TY&Q%Wct3~Tf_+Xu#ewd}+F=FUoGD%#w=s|# zpvr{ul9WBAR<_*{>JCU0j1NA%|L7P58xR(PUu$SX{ZMz~3dhQ{+Jmc}NryWl?r0p4 z@i~Q{BZWa%6V~-($#?k$sw zwMIEq;c(&fH>9+D7Rm5X2bg3^^HKV;Qydv5f9#FBZ)cuWw2R+$DxrY$i`@3at7G^> zbc82>Acfu9EF7Lb%(Yq+0MT)q>9haCz6J&>i(g*Pkow~pI!Uw3B z0AXitwXz)jEF~8J2_uLImr2jaFdr$Ja(TYz0|pif>Ft$%_pvt7XrW7b%vubr3|6;i z+|^}m^0aA>GY##8)`7_W8SQ&3@cVcMVuPc{i%SK!aqd5|X;;HxDi%#Kz~U6mf1b1c zZVZ@p<1D{@uqxwfT%4Sgu6CK3=w01Qw!u1}5y`2;=cOur*YRQbuZ%AuX^L)l9bEc; z5NqGbv^^X>PKRcJeYc?~moxuc3ouDe z0S8<~pZ3Z5N3!MH@j@c3R>E_VfOw-k{?X~7NX>>T^}_>D6e0NVKtsdrFfo|`iB_%% zlcM8b&yVl>L3X{Lv_^vB9B%}3Iw=GyC8F6^^HR~zZ|_4wo=?z7$qoG~)@yOS6(+Bl zi8#a0KGcA5+FV~k7HFSwL;`#06i$L&b4-|7iYP(yZ!55 zf$FN1*7$d>mEygvE#Y$uC#mANxJ`g23OToMA$sgtJw9Erd7MnAQOM+#yKz(k<%9uh#N_LuUNz6K`pL?vM6#{yqAz45#isl zX@r@xPAUfVvW4jaIozI^WtyAwYpBbrZWs7Z4S5P|E*5WKpm>_P+V(D0f29Q%A1{l? zBZjL4-{>9U{Y~c0Jw^Bfoy^!3ro9CX;ElTbrHgvvS?G^c2^~sKob-0j6hZcUNU`3p zs;jR6`4DJ;MXj+%Zj@`ew$b=KUu!=CkV{G)Bx}la>Z;{zRa)wCeywtXk`mVPL>Z7<t!Fazo)j{vMUJ6BK~+;4eWwdeeUZk_RGgw z*ls5Gul5R01%0lICHHg`ae(t;3-=SjU&^KXLu5jY+X)0&JLT*KcGIWlzYxF;zYV0- z@rP&W_nkTOPda#9T;~V1W!}v zs1{pBot?cV( zZmy7ys}4?ufR`FAD$-$n_=}$DdGs!P{xBKuDK#Ws|IdP1-n+<8`+s`Lh;{o5)l20` zh1`L%QdIpx=$pv};lfcp6FxU=v{Ijt_@ubwjh#Or4&xny0^;6ejAZ!sg~;+(<;JXc z+<*1GYou25Bn_gl%Wy@#eY|FA>3^v%)g5vl=TBBv8Zs+H53X&@9zXX^8viL=PE$6h zv#sKZk58<6lbKZ-9h<}41|`FMzshfS{^^gKSQ&=L z$$NA2?eUu&+=iar?bQA;=0?Z;lJ+dDo1WQ{?r3r4pbC8Uq1Dh zLzeiZ-fu9>*&oe=;lm3NOI$EJeyjUYFz(}IM_`-4KMUC+(9!!MKX z0$XzTqdZCE>(iAwnb5R`4d9He-tC?&_2B3Tav(Ft0&@EwGT=YRN~%7MrlQV7?J>{yw~#`9(YIN4V#~xfaLU@;Rm!ptJo~)hD2NefH`0@7MwGIF|MRJRD znEW-;n*D9s*a(hZogs<+KTl2?+@b`5m(|wC&44)t z^meMkF)d4s0&tpGzNN7kdw}G60CZEt5m8|7l-ajs8U^;c1T-=Jd8p_?K(rMaPsZEY zZuIhG1Nuj+FJ*$CWr|U(|Ikt!jh6WZ0Df`|SQ3-tFP0-5lprL3Pg_j?C=4`=<@ILq zc^Kae@8eh<#N^Vf`N5|SK7=~AkMYBi#oO(c#7>v0LUj5!qep*9wLezf<|?!qJzZ|w zH&1^NU{ZC=!v*2xMUCI8+eqd#E}vL9G$prA4Jh7UG{Jb z0f>5X2WIV;Jss1L(&%>^ad7_oX-bZ(8>FFd8EGQRf5tAL|2QL0q#%>fm*j=*Dl}1; zFVR?1ARFm4jbGSznbg~U#!o|li#XAdhno9_^rS@}S~ize_^Uau*0rbP>S^8tGX7I- zM@5KaKL81#8?Rmi6Di8|shMC`)n=i`2@jC}>ieDVUtC-O@fz5S&0pIM??P@a*O9TY z;}`;_Olag?5LKvC-BY*U#<`u&I_;hy!hdUW-^c9#8-L^~`;5MKU^L zk*sdc81w*q)nh)#_vLuQ zjYO8ZOtyOX}(Aq^G0m z%ALY0q5p;Sf2+tis(PH{#OeG*m!;uVvmAAFtbSE2W*Bf_zjbt^I)FWz-g4W&$;<{_ z3KW8!*%i=>_w>m25E=4?eIucbOL`LBfVln1R!{V9;AzjL_w9ZO5A8lSYm6{UO>xLo zW%-x0}sT zn9ZC65f5}9=;fuSgoF2z$}^VO8UJn`i$}Xjn|)Qm+$1GU9h)@V|A*iL{lpCdup z%htQp0_hAj)YWIQy!Vxf1*oaJKj&lrn|{Xc0N~^@xL5F+MZkWktuQCYxvST1c;+%k zt`)X3YzD8kkQ8D&02+>WI9EQo0ww|bOxR&X-mMmE@8YC@Yjo;gPgyQc>Ua()enFaC z&kaFCz{+z%+7*J~=;Wk6J=+FfV=gJNIXXTf^EE6Q;0Hk2cbjAFR)*7GyZke+g2kLE z#TPq3aFkH2>@QkEqlMiz{8kgOcs*447#>1K$R`N@)MLMslMz5N|AtmBHiZ8%h@8<>+`=k8?jRsqf^3O z;`!(Nc1!PD*|rx>Eyi>jjBGr0licla*x9Nt(8Dd2JvIA}P6f^Wu6DQwnUN1XT|mlo zK)U8NjyfwqMaTMs84P&5xF0S~m>#6m5Txkre)T9EyWb}Pmg(>*l$T`r5QbMTdIhe} z76S;Ho3h~mQ5RI{8B}5|Ucyc8gBY&YQDRO>%c(jNzz`)-P4pTkYZ!H?|UU0ITOWw) z$d_|24tUiT{oT#OWQk`*E6UaKN&lvDO;(y-<4a@U>=vKsF$+8tdUCrbL2(HYvqrA?6j9n`T9PuMqjI}nQi8i z^hh(k8a1Dq;pSV+li#It*FS+o@js0~sj3CpcIuDGzw*tCXl}Et*G={EvA=$(M(sMt zQq{U8K6e|Z8=PLCnJ>tQcwY&Yf2U!QwUt^P+u>d?Lfpg~YO~lP{j5lVA zy@&2q*5(Uc&N*D(dnsSz+HIcHPF(wv_IW;Soy0P_=59{J?p3A5>JM@gX7&5TwAbO& zgEbsIx>JmwkBG7pEUd#-Fy-W|Lh9c~J=U(?IyjdiuN;+?~I?e)z{da^d3=xF%l zopCNTKtPt0Dyip)v-1v#SPt4(+pB9df6lmEG$22NZ!mi%SMego=2aO>8P{B7-c=sA zj6;f@x8y_%i?zg*&or+7bmFd3+$x_P!lHfc)qJsFRc(`OYVC;CP>tN0=4;Z1L7@up zWF!vH-T1C}7I6!0yC+mPG8Gk4TzomYR}#KZ5J2HqP)jooGqkjJRu)kAS=3Qopn-(5)f0^;UQ;c;bMs}dEE^wfT=;P<>UQmv5hRfN?b>d!iwcG+qC-%? z4}efXl@d7(YyO2)m%x%$rd~S5eqFD;Q?691I6uG03R0IIGt&_0^X!-yNZDY{7eu3f zP%sU-H&cjSj8zq!(zkNH`(oD|s;oIP$X?E|BGPESGgJIWBhB01`c<%oo)$hqCK5%1 zaCkK@^wV#P+`4|wz*sI4!p4Gm&cN4UbVyEe;Q5)X*s^kpBxP2C3AvWQ+1tRitsh>TWlJ+i_8lWN;NCKQNRZVD5H zIo~G~3}@PvST_m<3!Bq8L(Heqz_FyFOHmv6+%!wL(sse}ZATH4QwcL7#h7+pA+uRF zlU#+-``RiWa;FP-)7;cV!esY0#@alzam?`i8gXAQjA&|z2Ry6#_UwJG%HbjXe)LN( z#nwDb|1znTMENc*8;0i;ug#R8zpRq&(1LMYk-Qm64SoqNBKDBIdr)08UY=H5v}jBj zLVEUPo-+PXemYSl5$DA`p=>nnR-&g&`Gg-#ZUX9yeHckHiFkV_hHC^k}IH{^$Rh;a8TOv}gr7_TJs6(S? zmR(=+oeRBXmqk|1EdLi-h``p&w8>XQ_cjH{YMj-sH(A3)fPf3Wbs;f?kk7~+B1c(1N z-|ge<040W&R4BKLk|ryV z9)duP$f5HJukMRF^^1m1zpZ5+?he}T` zp}aYHg7%7+(PaI($ZN_(R8sz6t{)hhxYt=p>9)YTbr5Gi!+07mQ*Cla-FiF4JE0S8 zk~!Ps(`oSPYcMnN2ecm3*H<&8fg5&wJt(mahh)~81R zryY=H&XNWKQ&Lj#j5RXI9>Afr4Oww$gY_NwB?_mk>W;!jI5G>k9yq{1s-$P3ZNCX# zRy}1LEu;v^{%4E^(7DjlHl9l%_IkV3emL6mK#~v!$ccHdI2C1I+t1WYGyaP{0|LLZ z0S^wxv;MF`8t}xVnNs+yl`Q9Oyl8wVtCJe3|}wcKk= zjC5L;r$Q*#NB(;geH%D&grLTEi<1}+B6W%}>)9$<0Xf0!HsMTa<=#V{k^K`(cjx4# z4FL?-;nh;a6OgH^gT>|=8@r__F0$f5L4EMKN#Ffy!t?reHtI*=<8!moXx}c7qiZ>o3$>#)b;Tu}{7(OEjRojH$s)_}i1Mi( zXXDEEtWv%LXMBtccGVsGr|aZ&65IzA8ryI8y2!xZSR|~Iv-Nid&LG1;;6nP-$Y%{P z)3R}3cGcC@0cMM`HZR`SQ=8OYU}S$qd7L5Bp+~jMm0f)oam471S;E&Muam;;d=b-w zg){i(VANr}6t^xEf8O0|@^+ts#=*OMqr>;UjXC9mmWE0Rf4h{98f~pxLJtOR6Y?JJ zcFDAW@tEUw{)&i>MOt3WLD1zvWS8-&rIshN#D{DPtBX-Ol^f-Ol-}=;Pe>I|V9QL# zKGNbskJ-Z%&OL;r(sq9XI21a*4~gEJXsJ;A?!$&q`(iYp?f$XOHcI#{3&)JLoU$*6 zi)gJGPl!=WE&*j?32lJ4c$nYD!DzXf8ClFN)&3PUl~m}3Z3d75&RE@-&cf%srjsa= zKy_!0{Wbn{3YK#|(%Tnv?~PXSKapaXT$L<_Z&xgJV?GK{^{%cRj=Tk#G50$Isc;sX zpY~Yl2lA`PbSxiD7b6>@NqHN+w=cka9{dmU^F_&;fWR@APD&KHZOHF@t%0I52y}}6AABr2r&HZ{QN?ORI@JO41X_Vo+)h%K9R{LI1CHVRzQdkje zlQ@M4r}?>HgXgR^ii*~u8eDJ1JxC4nPgQwaS0PaK24Q|uDrk|NX8~A;0y&%Y5H!d4 z;$P}DQ`g%x*j*MFn8R5N&&Kb`JbxktQfC7Ni|>kCZ2tZ@Q{tok4}pF+7I;1Z#RBzW z2XhkmLh-L(r0Q479Vxmkeo1|7m-ihFBljoh)LYd<(aCh`u7T+r^iHPwj4(ZNozd~um#cIl0ZLsiIVDP`{ zo?(yU0^`5BilKC2neKTA{o}8G&JJp)Z^E+MT6Mw{Q#SE(%bS49x!v5hl-b}9hM^*Q zTwdsB(dHMnZxRM{7L*tp?OK2=|6&=CRgEJbE}`;hyzNNM_?8Iwon zFf70<+fywGkmH)oWgF zRI?u!chQGUGF~L_BS=Z900`mg9DH&56?6Np()r4{Gd|7r(6*Q* zE(d{WKx`b^buP*5&oshm3;pd_*tlr6WqhkPP7A4TN6d9y3nk7-8zT zbNHCGJB)7u-PPT^w>m!}0uB9?1ZFrU*ocWOECXW2pKFOM^Q&ujT`n0A|PH0KGR(_Hu|qHPz96>Og6EbZw?+u zcC+Re7O>uU8=U$hic!2q1aV8N#D-w}Uu?Z~Sk!Cx#*2kpRM-kg30Sm%wB#t=9nu2Q z-CasZi8`_dyQ9n0`TKBBGd0cd(e%@H zKe6A_4RDLjC7OdBRgy3qoQQjHzL4qnHid||NNb{%X03&BF7&s?!R)$A8qLY&X?ao|G!I@OR?$y^#${_7LazORdiC9YgxfL{4QLt#I~h%f|u+llXz@Ht3t^2 zp>;KCDyt=Wtf=MGQ>IG9h`%j@IWbiYJFt9vIL&jDL+o-+^tSy{D^YMvA$9qW7Qok{ zRcZ9BlgYL1pW23ExEp>-2QgaK+|_7;K4_omm0}K>(_Tz$w}vX znwjsDQSluk=}0Yk*2cw*sOn%LWWCjtK#1hf^KR*@bT-{YA>Ghteb|GxJs*TKlu8%( zcuT5{Ju&vmQy@Q`ME|}%e!sm%o7b6M{+w%{+?WoO@xl@ExIp*`%_t-bxvo!iDfx(y za^uid+Y%0Zr<2)AD1KA#ye1TwQRO`qAVt{0_i|!fe?D-zN8jsIM#)~v(5u%{g?0iC z{~ji2Z*l-B1Y6D58Fcw8lg@suXDkjYG+_=hUG)LnkwIobRB7wIk4!3Q`g?xQbACTW z%(sN`IV1k94~q=Y8tE%ABxY?xllOq_9&6)%MN%P0#J6H45ErW7DW4iGWqj@2 z#rGomo^ZNfhv~{P`fMws$2?Jw%Em|nGLdO{lN@;p+oHK|ucXwqi%yBJ`K#K*5lky|mdMti#{%oouGLm6LCWrAJ29*RM{nLp{@s?sir#94nRivt-PxTR`Th{DQ$ejK-IK=rkb+WP z`wRYKum`sq?cthvytam1$c3042jOq`@t9Y;pSP9FdK-mCRnu|JUCf8sq`)H{3Voch zR<|usr%(0TPRDgMGV*9lJt?*3KP7n(=jKr}G!l%zEwY5&3)7t8uhvHlFeZ1S~hn?+d6GV~L z*^Hd%dJ?G2<}6oX_w6y7V5hy}uQ4T5cEb7g6tvFS=5%3dL`OH7zF$pa{Fg5=DqUe{ z5mF*okoPSo@aQQ7k3H$1fF<$sBi$kpQCkZ@f>}&QH=f zrzZZKrq^JUoEBW?bzuqAd>#(1XG4HIxG={Ux-?WdlWYweglWQ_Z%?Q9o!d5UufK&X zp$Arsi8*~*J`;1AFUAGgg2lo$!oXVT@++Shd4d9vEt!Iz5}Nr&+gDo$hn<( z(vLS%xgL^-^AH)+B_tr(-n80AKu6Y#=* zQ#+|@ZvkrgoZ<1whJBQsyZ05V_~_!KeNfIrMg)-W23srz zG)>&jPeu`D{;D!MO&&&uUYvbPyQ{+4lK!tg{Y@bj*Af2hA-R!TNR^bm@e1)p*MjK) zorFYCO1&Ec!{gQE4M)l;Iwi6Q2RKM=74kCk{W{q*f?grR3fiN*-q=0jVn{eVUcRQ& zntSyu>WHL~$H+o9c4I9=wSd0%fa^^tU;aiSSY4hWY*u2ELa)rO!|8!eQIJg?ohXzD zxS?`Xs?=saHQfB^O5?`Ipf1P!XEH~09b!YciQLA*2KEeXdU#E7o6ri{?Xizx-K_K& z)0H|F%BjI;8X9X-gq_1~?(Xe81^tlC1O|HN27td6p-!yxwGE^TjGvU|Nppr^Cpb=!gKxE^1 zbQ?G>^Cl~ev{CqF6cC!C%THTqGq~OVG>3nRw{Z0v^PsSp!=CYv;zXssoC!MT+S^;( z*CIM)od*uj^c?O?Qr5xe1Oy zLBZ)V$3uk2NyTVoQq|EC!=|G2^SGY;MN^}vNqwWdx5Ta%SRA(bV|-$_%>23y!POhv z60YCcGRwlpeu3bvqM*}yNj!L#F?}$ymg>1#dhuSkz*kvv)fE3Oe%5=sB3x>}yA=Kg zN3g{aTOTJ!=a4X)`7$X_er=)Sds0&*<=5hh7RnbI?06V%=LJmTPoL(#{Ti~kAvp&lmW*l;ZKbqA`t+;)cY|xT`a(82L za#@Y9LN2Y9|E}Vz>~3Uhwpc~L;(d3~Hy_Jmj0o#XU(coL<}KH|pQrXrlSHUT9YP=s z^!wc`ujfi|VYI1KyRu=I>uP{12qe+5D4#Ty=p*57V@6dWPOle`GCASim5T82Z#tL@ zQ&eK__c(Ea(7=m3stHG@$N4Z$k*L=}oMNzkzX8Sn zAzg}C)x)XDqutiBzI{<$M8SM|=m5U5TVX%3^jew*>!9ksqVW|fP2HzW?WL!~nEPv{ zRQJAh;t>h!K@6%aSK#5!Y_{NNK)$Nwp>RdiD80qyosp{GbFr`xVn4LTqSxQYG+*Lw zW5~lj(RAKS06|kdxE~w)_tElFrVz>@-r)1XNcuowOUbPf@_%~P-(s}68r>TK#;eeJ zI|k;bQ;JNP-#zw!-XeFz&injYR>;aaT=i?(PHc>2bJ^yVluDzytzYV*VrJJ&x2Lf7 zp>LSj@>s)mT3T#gL_$HiYNIEmOvbB0a0gVvzROpd+|MQ$K;?w$$x`eiSY9Df-hp@a z#9X~K=X)Pk!1$F zoji0mcjkG7Wj&66` z5T!X_*SMjqqom)cYq3X_GLT&FcG&CQMh0BkCok>Jesqnj9eW67heg4@j=W6}t3Rb? zU|`OK9>&spc{iLDD$mU5JiwOMWt4^L+4MIAdY(|{C=S6CPFpiRk8V{f)2QpI9~@|u zmzN!ViBJC}T4+DNZ_kKT_Q;hjb3dnIOk|%vZUcSJ7)bg^4{Pn1hQx=`7-H@$;|}ph zM1>?D_gqDfzWfz%e)AOk?ro7XH-|YKn@|SD3!>^D#+vF%E5g6ou3f0>DRtR7i{Z8P z_eWMtIyxLxTg?>?eb;3x(t=ewo^vy6Y7z1WsXkd4NH(=mE&u22lyz{n-yN$Jv_5ep zQKnbuM;nD0+^5v$_1V<9J@%e#qJ)m}PR1Qs3e%A)K{|PTvpMcm{P#{4b0+Bc6p(!V zKI3x{|LGBjO3@b=+hX=!8;gQ&a`mK0@9Q^F%otNO(D{;krAO}EAF~E4@hHUFLJrt~ z=$|8N24&F@k>nrLM$|4#*%I_C1e-40eo zj2D>Rrnk7TD-FxtpsQby?O_;*z}ocNs_0>xXZz!&9ZTe!9Kte9W*`;V_Sjzs19F+4 z#2j`mk{vgj$`5wbtkk2Gbiv762E@MPQoYTY>v*@1` zcBDO`M`S;nC=^)mO`-8DC_4?90YeX(JTvxtnS@P;V!#15BmDqa4IN-JkA@zn|j!bFPU&pXYW6&$1r z-p+>{j;mLdzUSHwPDoH+7#$qK;e0+xiucY= z#d^;An>Q`zxb6EJ#BSMJ;o#%#1JWovB?4lbK~sNFBO#&On)^!Tiradc5NOs_IahE0 z!evja_&-JaKU~wn>8v7S9C^{~$gz5=l$w@unaRqb49O(n(Gw#>DpKV4`e2D3D9fd~ zYT15)Q>~BoNHh2?uVS<$D1$HM)m^THcSC5hqqO&_(nUG?C$dax>gpp=wrzoFa+&)3 zs_E>zF||TP>e_lk%>oEfip1XKK(q3nJ>9yxIyOjV1>#VxBKfs-J0|P~zw^meV=YXE z4&z9K`>Jtwb%$$TEm9*_H~U+|@Vl*{T@me&t?hzQ?fe!>k!wbYP|C)lu*YG33a97urS*+Wg6y)koRk5IqFg+y$Z*k`P(;pd(lyN}iUh-Bx z;ZT{Z-9Yr%>GJTrj7pz+W`x*FsZU}u!gYyolJnwl+2c?CntI$CP|B^mQ8r2eE{n`3 z!e6ven+U8QN;?Vzdpk`G7B18n0qk&x(b)7vD$DnG_vqB5(a0S~>ZNQmPQz*dUgXpeITEjF26oi!yEkJ!< zpo7Bl9_++h1-qKc>QZkWaAJd3!3^MU}R z$$?9a$=Q^#nSq4$xqewqRdJOq*1z7>#@7_Z>7w@Ib4Uy=2hQL_YjfOC0H4wFiGj_? zfIRD^@{>ck10{Z-*g7eORc9Mww(s}&wCi12DjJp0wZb{t*Vm|~`wDz>GxKkDxH&XY z5QpOQsKFRr7tCT$`u;BFfmTuE6~Cm* zHHrWI7TmWK+Ebste^1e_B3fQsSzEE1tnt717e?!>9;8TI@uSFQ?t5!PL>WJ9Q5l{% zB(v^>LM?H9P4YI^+O~e4xw`@Tg zZ^VAcy|;g9;l;rzG&z}i6ya?lY_GInibc;KVb2wdl$*{D3(4a)V-oj@w2w;wYg&V? zSK-cqH0j*IF{!kiTQJr4NMb8bF!ic2pR$RG9v$Vn9KOdS4NM0Z7jnyb?0Bzo!+qD! zfNcHNCSPrXxwJDkk76+|IqpuVS$ zx+$Ma>0@#ltG!P#v?edzQ5PHKBuQTu;`c^X=DyT|4&y<;*X3xxR`7=#*H_&&+IXW2 zC&z7{KEun68vwd6*}LA;({tV!CDb?2cP~_QR;cN!|07!NRM_2NP{8mxZF?d1(+diu zzm9Zuxo?jQ461?=>$~)nx04e;O&U<|lbBdotpXb^S_hht2XS2kvc4OWh;PXyK3_oc zCkjy`5x$1>K48a_u~mRtnOdHoXRJWI&qqLi-TEe)7sf(GnuCTO)oga1I z!-vb@%?2701@XOtk*$0TuX$^Y`|bEuVj`LjG1%eah`c;L;+u*o>qVoXw}rTRXVoqL zD^03XUKi0fwGQzL^NQnJJeYi2dd? zA_1CtNPf4oQ@rR+W2v~3%-&YwLDXM?+hlBHn!L9YE<7=dD--J<@15Q?OCsuI^#vNW zs@IqqClG8sJ+|98SND9!IXt{wY%tM;re-RG9vz*c+*P!kO?fp=m{#BQyTiTdE&jAy zCF5JzqlE+@kizr5??YBWKfVnwwHAHzGh73@H(bgb5fXu?@G(q!pqI@jLq1m<=DxP2 zLKhTxUFoPcw^QJFks@)@y~W?BS5AfAVhvM@nCRl5+O+{yFe!HpYsB-o1R%X{baAW! z#q#`km(K?ASG4305S)24_wUsgR|cr@iKq zRJGgR-DNpWn|i{8ab)vg^?-|1?M_RSOf^}zX5BI}AGiP|oLD=}KN`7gPyRo{h6-u+ ze5+qmx20mEv%Sr(QHAJ@=@s@XI$2NLg6{YKH*k zaT>uOG`k%^F#BI&JV@i)tZI?sv-ZyY9yT8jNkyEI!CljEmsBu^u%4=geb4(b(iRcV z>-3g-mG)2NY67`YH$?IPZ_0T+&&Y{D0P(Rz9|wK6ZqLU(Uq?x$4J<*ok)nGu$&2KG zpqnJd@%1lGnY(qjxUJ_ZPhUi^GQ^EQ4XVCvPuJD)1u=_(MPeg+(F>3F8req2I#vs@ zeM_cqnmm@^a#MR0(+R@c>6vsIC3D?T`gMks*`3w;NzeYqYdTvX2@11F)T|}1{7U`h zQaO(KuX3D4mRE(`7>5AQU;~GDnQ}JDaf7j2i`k1Gdyb6yh4`jHD~O&APpBD9>ak0B zSYDDhw11>{wTtz6&K11Ia{}sI9FB#Mp{34nol2jcrVpKF0yN14kaMUaeMVqFFs?Ud zW+3*5nU}l!%8Bl|z6YjD>1hy)_V51hPQpG3x*QJXUxiVarJW6VOZjrHDq1jK4BPW% zDf#B*2oH-5)zF=h#@+T8rrz7BC)k$*GLW=ATeK6Ap8_naSYFCJB9ep@Qu?jobH}-x zZf#;RC|25s4ny%)bCkZJ4ufkEdN~58KR|7Zt()Jw-Ic(Q>4R9=zG%%bSD_8}c>5xI za_#|heVG&i7073ukYN=drY1Q?N5Q;#OflZq&J{~h(73G)mA5szAD>>ysD5Sa4-}c zG_*kK5qfa{D%!hxccPO(Lt}hUqLgJ+or}4VxMKTeCQDz(V2%Z(%ZGp;p5wRzK$t{gTCv4051wc$gk-X<&?+tPwb_lpW zUTKbIG9}-GyM6>OPQ1|}0@oE{L{Yt)Q$j(rGa!^aM9bzf#(JCK76;FjKQnzw;`xpV z_~b3p*Sb4#`V_{8%BWkCfUGk6T*bMx8rB^9hW=a$k-aeZsaf|NU*&P(My{gPYLBK- z;yu;ktk7ar#`ML}+XDi#^%AD!*j5fBg;b%=0f96HS7|xUG}U|v#=-^7q>-;Kd(1;l z%AR&w8e?ky7POZ5CoZh8GorhlViE!;y*;vFH_pzLtu?WE3UqMF#Q$Ke9ES~s`q zm{HAH^w-qMLeE_9n^E2GhO70VqSyyYb{PRi6SQDV<8NU}w80RXWoqKO*GhXGga-ba z2;PX6o<1jNtn(i5S0cD$ZYRf*lcluc(lVewH8oi!7tUOgmvtZ7#i%rM*f!uhU%ff| zK_Y)vrt|6@6$U0B!7U9_y3TuTxa5#9*kS@>b`Trleq7vWuUt}6)i=oDk-XwIow<#?>eRbf$v`L|IL**wJ=v7NnD_y8nss2wZI$Wq zQeQJWDvrQ8hBh*V&)Y;xJbppoYG^_%ZzqKBGwVdqb)+B%vM;u0L$x(dI@?D`5BRAo zzy@&z4Cd%`WB+=e2*qZuJ_)ddR61Tai_$vUr9+(LcgP%kKD1NxGpA{&l9v*5)=~&6 zBz3h)XxMSgNJwvaYq`YM0C1ZB}r~6 z*nXnQeA=eWk~`FtuJ~(+YP3{$5#$POLIjzgCYirG<@jD%nRvMEhVmGG(WuYI@+nE* ztl_BQ!5;M%0SC%d;K0!Z(f3k)c=e4Y>MD}2;ET+HAoccjpc z`If8$(YN(b!i1C#-8;Vaf;qw@&FG5ir)kgmJo|%B_|#r#m^*^a8?$gHA5nLnH#IqF z$C}O^o^IfIz7_ebssWPQg7zc}+SpM%5wLyqpie+;0F%za)z zzT1(##PofWAe*PniRp7;-UM#>%0r)HKV<)rB zQd6#~b27xvWT12tfzL)KDe2Fna9LuR>Bus_T#C@A2jvJIEc5AT{D zkI}j9pkb2s(x~QY4Cz%1nO>A}3mC87H_W7WReF$}1BBZ?((!-@y%e@*vjv+LBI3s6 zUaUdRD`#Y*r}W2a4%(S5T=|76WH65p+M@JCh>fAZg28(Ruk$xFsT-WLapZ#unl5~H z17mxj{mCu*LKflg35y_<`twOdaSVhWx_vSYA0*`2nT;O$ZR|*HdY4t$Nn~1ZKyTfc zrYKvDDP{8Op$HAc?YquYSkQ|x&R=Sx&Id?+gN{_46f zb;pq+d6u;#*DwefRn)D(@J*1Vn2O%8*1Fqv^(JEH^@^p>7f5%TisRbOQQO^JdFI%^ zNz^j)`oNLyDTF)-4hBOuXuTURPl;bQOirM%bY{Tsq?+0XCHVXqJ(&A-bxL?6Rxa0_ z;gH)0U;8dgOL6+^IxddZQoPgqO2eI;!FfN0S!jPJESqU;v-UQ7eyh6kG5gQM(C!== zsnk>}ho7v^7uy(#(=eK~J{?R096Zkk=Xf}_v`xMRJVBAot^{QZ5kQ zlQnGU?w%cXo1(BEWck_odwaM56oIu(_``Z$K>D+@o#eUCthwIYjzL|>3W0P*$;2H8 zEnIzu>?l7W#R3cRfg?;3D_ap56DnzGiSzr3w%Uj@zKD;FSEA%+q8r#3&l8qmdS%=t zkSRBG9h0G%p`357P3DT*0WG6=@bG1tt!q#in*Zb>;;^B3eRFB2#Z{|gK!=evSGgc2 zv++Qn;x2Z9B{m!GP@a2b#H5|j4VhU1*R`$3|Uni?vsk5Nf=PdWb_jh*06KMEJ{ zKsY$qDKe@dM3RH5K`{(D9n}#FV{c1z>_RN1`iQe)Ou39y4+|7%<4)G$219d&cz9u3 zo3Xyw`u@Il&1d)7c$S-9dq+Klh$tlw|FZREssA#(Kb0jJpPFcNmYHb%jy?2&>MN5P zobT_DS~lF3A3jj3Il1l%W`T{5>}4){kYNP6Bi~KZ-vSUWsuGQ#yxd2sW;)j9##|Q> zwSclP=Y%u!pXkX-?M#=-8Cl+^ytlcraB|vxJgwQ3aF+UM^ESaH<=}?*s6N?H5HSbJ${-ts-^P)7>P|6-x<; zb5MU}_DbE_2Wf-rZlY@^QovsvmQa*mT%0Zk0i=y}%;H(V^nI}HL9!_}NAVY?Zr|3` zm1hY@HgFE`;zg4+5m&jh=Ril!a%LIGd@7!G|CV=m#ReEHt7NCR^n`XZ!adh|wQEeV z2Bl|p8U*`Cx9}0#7Xyrpxjgkkm!Jc)*CE0k&SqAPJuBo5QlT=lO>b7%OIhTk^hkS3 zUIb6Mvt`BJWK@40{@1NiCC+40YLWWC)jlJcIfMmdE;?vb1_E&DSOt zZ2K)ESnBcCQAvZyPY;P5(V#gSV3(mm%DApFIL|#GU-}R zqMf#;S@`KnT8vM2M&lx%I@Nn9q*PK=t8}9->Y)*OoM-g|vzbrv9aot{qe3zga(>9r zEavJZ%h&R-KVJq16kGLsRYv51WlR%hPC(04TRUkMB)VFtxF>eUsm9J$IYLv1Moc-; z*=t%&vZ#-jVzh^ZkI<}Y0ynRmN4Mh=Ea_+}b{vxC9U|RVBA@!Iq{ptY1W@pog7;0^ z#>0LD!(4AF_E(tgAcl;ROdX5ca7W`h#(StNb%u|-Ug(rySE7ETcc3X(gf}u0pRfaB z_%kC`H5bVK!wfsTwsM})kZ&W0GhJ?=6sopOlBCuVen{c>;i^|YX1Y|@5Z%vuM}c~x znrGbR()JMk!^H2%@CM^kGp zf69dFPTMqO%J@yC`9%DD@!Oy4JDDEY_Q#)FwvOp7Qqx3RjXS<<<&eiNOHByEE!^|6W|ic&q*A>-ZR&3LC51V zBr57IK51HvOij}Npvrze5B4unWuM<1>t&BDfM;|!=dW%$W(C-tgnAox^yl8>1~O_d zpRp(jR~cUb<5U2xO4)vT-uG-E+2A|&8c!3xd?F4Qi5^{h*8+n{)%QC z{Z*Tlm5trEQRng1!jg7r_T$~FcD#aHQSCi@)YB#2Np83{ME~3kK>)Mu?Rl8{%5yTW zi0j(5=9x#VbQHO-+5~T0VyY~cuLc^}L+{}Dx&M(Eeg_fQMevjZ7`OWPmc?cA*Bgfa4?YaOQ1kl2I`NBkQAQc? zioeMn;aJU5EdX)}u$db@Hd6s;tok^o?-F03==lfrLTiVhBsc}LjqeMl|C3-aO_TDr z4{fxO0O_?h#0|`FnJkEqwY=c;J*Y9wm2*d5tLz9@dUuL>8YP4t{eV)$S`mK~R@$c#uHW^2^bgfyJdFr#tbE_vm=WB()a-w! zMO^;WyW3MnAOK#j*KYZ2<2cFn1+zGD1NgJwZ3VgF+aE2J9X@%9pZxTjWx`yPdMBO5 zLkb%*J|EUfd*2KV0E8dd!hk)G!U`hK=y8iJcJRl(P)t$VEZ|LB|9QHH!0O?V&q%=6 zAUIM|ORKq-N`}V@)ab3Oc1UrU#OC-hL+_f^)fg)rtrvs~EQv;DG4wB+u`wlUy;#S| z_Sg!S5PNpAb1|TdU80L1LZk%Txpgo3Z`?Segt&uPJD=QlGAc)Sp|f9+Vv=dFgj{|u zbNOgVqV+w?H-|t@Q5oTwqG<<-gFc`*U@Rxg`&T#?eG%O7&2X=~n zp^%K+1nY&+rA|PW1je%p+4N6>Ce?fMr2zIj^By`(Zg~I5S6@QnCb>Jf`a!{mtcGBD zT@H0r-TrD!>EQl(6YAyqm^bc$+liM0;ARdFZ*QorVoKj33nJEHSZRKUgVCch5q6~- z)|7~5ifH)s`Rm^#)|QqWJ3CPuf>iwOV^ckCbK~_b8d;}b9C(2$qyAjCG*W$HvTNu} z#m>GSZf)6msuBif@^hcL@Y+F2{A=yEGpi{TLKUqQ zn(H6VEhoP^lCXMncsA7+_AD6Xswj_aTtn@4&Y3y3iw1!dr`?zm-{~@=k`ImEc4?4C zQETfNKt~R3VQQUL0CIi}jRqb4*9mR*GKckR=#BCAHCDZx)0L&2$%$^BoXV0nsyZkb zl%zMce*ahEZEki=`+P4utX@#|&RtHS^8DWqe>BMzO!3q# zaaBP$&mz}7s}1{dG$E{CVEa(YdV-Jb86Us@9YcHu?pWaZ00b(huZuip3X8Uo#Ofh- z^*q;%Jo)QgG3U;j?qcWMx9+k)X@^`*PgrmBSWW8cJh6@WyKQf?LD_whV$XYw_VHNA z)F>BuAHo%qO>F1rJX_~DM>2;#>|9)_APfq|t*d)Ea);XILyTk#X3C}B;#ADcn*oqfU=^pOmKk)qH5%tr19F|t!RXuz7|*Dv*qml^c49x7 zGiI0AuJuNR;9=*B5(*269es2~pPKhEsDd(Rx2-z2w(eyknD`)e$)_GE2%LOMNbnr6 zp)^-BU%FUpV+AXj%%94BeHnRm_&p7FSO5rcLT(5m>*CWnuUe44(%%3vU*=e%%8@8& z5tj(yIth(c!)R4~EvN)=b#AiNA;SfbEH(7JHj%m1bB^=$*<~F<;PZWldbmU+Dd7A3 zEBI%kehA9J#m8U>4Ex0eU$~8o%>bIKgGE9Kfc*!{u@!(EIXXRxPKd+SzRP~~DEZ*< z=t=ti2L)JW1a?}*h_6a6>#uHoeYpi@F2OB;EyH-0XtXJkN-#XtA&r5Hr@>IP0zJ`C z??U=(q7$M}c!zouTakwt-!iKG6q2B!-o(4p-i!kSC-c|meeZ4>O?-IN8OJy}RicnH zv$UlNJESY*saoQyfpDHgn)#+Zy|+Z=y8sKvmCA!NjjbLm>g>H0d4{n@->*s@pzWnpYwY9Ch2_Y{zTpa=s45;A7ArOxf_Ga zZ8akXE6`8ki^l)b-Yy!ep{iu;u7kw$uXvhi3KQ^ zTXt5(3Yz)ty{G4V);!)B#^}!pGWK|A?Z%thRQIwM0Elt2GwBS_YEFTb$paV$Ne~$7 zt6sb}!Qy{-DTnaK>BN@O2T>SK9cekyvX?2&~du&d;@KAcGy|`;?P#_+YK;Zru#(dR35F{pe$+X5W??-o#E0H2F@Dab4N`*;7tskYRRO-o}X}%OLaa1 zZmX_=M?`pV+8-t>%b?1|8GLqG-a1}Oc`KFmx$uJqyR5Zt%7@0r@zI~6*9=QP;({~q z0+B?0ba=axPj_5?=Nbq(`Y;eCvtoVU zkYQnjJavt6R`pIYpk-AhF7L5_VcF_^VhS&Q0Y;_6YedpyEEuj62F|L7S_TrqmKym7 z3Pg6${FE8zBZ`ct_d)`K+Q}6c;Z5hSgsoJSy<~-TIJxe|Z0<3ujgEHQZm46ADE9iG zbsXyK%qZf02A`lMb4~8p3y|A$JLUpsai#Cs#_9J@X;}eZ=%}fIlvPgdRfpNslfE_S z)?hHgRprGuOZ0GAfos;f9{;&^Wprp=P^N$Y9}6Khg_yHj_b@_legBhuZf=`wWTZI% zhg^P|<(&VJI+yVeJ;2OekBm(7ZV=!obNCIuN@eq6>RzL)i8-nosS@&l{>X-3A(N@GBd)y ze}PJ5EBm7QlGjo_dON2^`uoTB^}syZE z_^o&DhTbXct1#)b+@?eWmX*d76Yfeb!k?;V>oH&e7u+mPJyX2|^r(Eh05a6H9m(}oXajC?_)VO?2K{?H0oYrS1 zhmy!UUn+_yXo{{qOf+MxDFDpy=1#K@+4;4#8EH#^tky~q_;-OlGf>rvBEE&J@$tS? z^p`0#Ho~Q`WBjYI*5fTqPZv~}&bx~=TKFpgi=Wv;qyG!eGRP$JCR_k(7pGR$ew+iZ zH;@_S8t{k*%}OnH`^8n)bNpJMaOt+Weq>R@`yH{60vDp7loZPB``kESYN~=U-zH9~ zdfNqSr*Q~vxgGy)eX{YX@s|#vzz!!HsOUY9gED|paq+s}I3mG}a5Kn1F+Wb1rAQ1n z{G3r0zi^*&?rB2Z0l$C}iqBCfkvm7d@Q=i)bT2)k$Sc3W3%yFo6$(#sZ(skY$moe_ zbP#mKKcZ>k;3f>B4Bs0o6yhr$DxfSWbrqe`X(5iuPeV;Xd%hIX*;16?1X}Z@3iDeV z8y~|gc{s$4Cvp-Lg56HGA+k*31EU)i2p@{@b-{XPS-AlLH^e(=Y6_C=+WE17+wyhe zw_1gAQzywUz0wg8Z@73kUN5?+ShXE)-G-@YI(gJ58CX_U|zXdEgloloy>Uh*se zcI~vwt|BXGcqNkqbCI&&^F5piILMT^LoqSW#NF4`f@#;7Gp@08%@sJmKC+xU_=u4o z#^KE#cm#}w8)Sk-1BirFBdvk1E(0*d1GB37qKLcRO$q|smyst*_UzA}eQ!aUtH2Pg zF{)>1;6mfkFlAJ&tFIl@|G)%I(s*Gyn*oeB9j0>CC&U{mO*}@MK!M0sQ2a-~te?G6 z+`<3GXs<$zU(blT69B0Ie!kVNs&$n90rjI(eCX@x=4_YSjXWV)f0tek)}E}-xgA2X zH}yC5Zx2o!LOr~GZM*vl9kg!64l^`09dCvmk4Lx+ao;M9g>ID3cM5QN*-7PJfa!Jm z!`**CT7=vJNFU)cC;vz2`6oVrkWwHA%fu8X9ihIrVNslvmHkYAqRBl*Sm_9!#9YV4 z7X5FKUn#1R1T6LQM@1nTm#A-Hxt4D>SpDMh#j9Az21Z2?9Z7UVdZb8iu~UruxWFd= zmj#A%K0fV!ye#d?9-d`5o*B@+ri~LM7P3HFtekl86EGSr%ZD>4a&0C!+;}C9_V%|T z73K969*qwr6Z;hT1yuY8qtK@FpP<+llC0k<$!+vkNdMN*I@>SL825rFy@h9D`NG3P zHwHQx!tssEp%xBH+z9qecj7ZeZnQ;G>h+r zVU8m_GuBqfzT)Lx1(-r;6dBiIU3un4{VO#y5fUeH%c+a6oZ^WMjlYCG8L z2$|d;(;Pckbkv3===pAF#z-M_^PoQ3HM9|bq`2L8I62DArES4gHnVTOKuIVP_J0~J z#WQPinyq`^!QtGv6ct(5fj7$0_3sc-aB7t$F->UU=~I{Fb_(@27nNJ?r4!~3$oX#Y zE(taF{fQF*@eFT@)WI@u*UqsBb=XzhMsVi_;J1O#X-T7tiJmY!12|^XhdnyaVtE{IM2d82fq(dAkBTtwq@OS3uqNUGIs3!{beZkP2uPUFO72;rq1=$iNIB7RP zx(7}wiHq#NTEM@t1}3vO-vIlj*RT(Ei$==Jw^5N!Ho8(Le)DHx0gDUQN&Y4!BpJYY zb^B5-eL?-_A1A*1_k;suT)`s<)X>h^`FN6p&edinIr?k&9Q;>WU4aF5cILxwJ@krS z{7}5fW%w1qeE~chnpk~R_IOq!2d1fAC@Cf1lGi0$X%zRCiYvSQ>eDBQ9S^*IP%J zg2I8W$!t?-lXNya)6?RHo8t&e*4WPovm#mEG>i#<&a@2s+L zoJM8qk9qFhjkGEPEQSE!QdM#X1Uw%H`q%$Sjc}>oyC%E6L+N#K!Ae7SeiIigQ#bzk z{bqox=FGKu7QhnE%Yz3eSh~o>ra!2UKlsi%dNH6>_;EkFmg3(r*dv^nk3WC*Bz!jJ zwu%~@@i;NWN#pwc@vDx|$^_^4BFF;Z=jRu~>|bm2bjpbacL^}!Uz%0b)STb?&6O)e zhJ=NrZRVvE_0>pFFGFjrMmvC*1#lxh-OMv&H4J#npT>0Kp6p=Go<1eNE%=cnQ7;yx@h>SGEZOa%g>gcQQ0W3<|+EYG_^^p{U!q4slwb*5C zR;1$a5C8Z>Q@EP=pNqz&UcrA=Otd{qx2eIqC&%a7O$u!9Rh*ishcCT!@|7FWsO2wt z`z5AmEj>M{r`XdYYCuPn$aW0`a#i!g$Z{gfONB}YZ zN4avTXX_$u0Do+Jhy02vdBR#a0leykF{$Y_XFYqqlzbka#ew7hVzL_I{2#?ksi_D| zb`L57fE*u5#k*aZKd`z=>~%5vFsPo7hl9!Wq#;n`=84bJ${K5S4i*S_fcLafZ-^0=w6*UJHLQdNAyHLNWcF#Qki;Z=O zjP0qpHUKOB%M9-46xI7`SA?&ow-1{r$V=7x+Ch9F)VMaR|6%xBP2V|HNM{rMdLig} zYK)T>2XcOQmbaFUFL$h6fB)$b7SuY%YpeA&F9Gi+glPT*yvac_sUR~XLOgS0EPv)3 zltf3e_8~Q)J1=ug*%RaOFa~qeemY+)@*HS!oaJDQx-l*mC3x`){bc{w8ZJu7@kP(gzk>M4DC1C_u1k^^gDRxr1Yu`-c|q8u@Du(&rM-1jRo%Nbya53v zL5}g5k`fS*lH7F1reo8HbeD8@ch|c%=yRUmIq!GIc*nQ?kuhCs z?G|`!$NvV!)V1S>7COV`0s8ImqqKY3(j-$;&7UHtPz9CNEVO zUjv%rL-d*HgSh1?0?=adtO^*F0tjf#59=kG0LX##^;ZL17GT}b;sDN>aw4j8`OQEt z&^H!3^qQ0Os8PG(fpFjPKWP<+kE*<^ic%K0!&@>Y@@Nw2lsFv@5}+7-brvT7HuGnB zhf6>J3M$&(blt6pvC130mEoiAH4)Y$S(U`&t}k`SAwbH)E3h)93GV6&AgGxwy%lMg znVyH7o?|el>W6XAh&7ECUF@s==RSbF{}Sn^^;cCLKtUI}QHi1kU%bo$bKKT|WVy z2LWyCg@{nbsga+IIlsCnx_n_h!kM=xoSb6>JGAMtCTBJ1l*XG=YGCv}BA~Unp^%OC zyI&nQ58>AK*}lB_@oAlPV$srsx-WiJH#-ycX-}*sJI9z@-D!Q3o81Mqfr;E(4)%7B z)kN<}d!kQ3yQL4gP^h!k!{+Ivb>IuTnC(MFI^GV8e4<;zob$o7M9;9CGJ%VWE_6r?b%{l z4ipxOFExLzNq3AAE4OQ_2NDV)+u?@aLMw%XQ!6}n&1W=-hXD|K5=YY3lA-fiVRE)l zY8v_|%iOvR&bc)mk1YE6zA*9JDu?SV+OpcVgN zWEzH{S{;F5sEq24FL;ncT7`V2}SC zc^U#(n>?3G!a9EG6a#^+UtX@s4;sy`qAdPWHsohPf-zy(=LzfoG`yS&p+K2KLS%f0^ODAxbn%p#)>cbo%MT___`JHSR}irpl;z2Y0V3ICb@YgKoNO` z{>nex<~pX%YLiYy0m&Taeo~|HFaTs7fY)ol!NKv3h-U$)fmW6m4;YsH3I2-p`aV>A z8?RIvMOU@Q+nIV5Avzn&yBt4$2t>f>1hLGVjHW>KXIN9;GfDa&vZb-xtoSt0*M!m}BT=vJl5&l3DY6dCb3 z&3Sa{E>HxEpwAF}9p0*+d2zxuMDD@0T6q`iy49ptt<&=;c~p>is9$Z#>7J?b!z%Yv zX!qEW>@)s`z<(%W>87Io283^~N4jV>L*L?M-?nQ$%w62m6mV~(fAQUmE0{N^e~p}?RuR0C>%I5itBKT4sK*35UU zmo=Nr|&4GST1$)o&rnCl|5TGd#ETalL`9GYw zFsdc)s`nTGk=#AY?%h2}&fp0Z0;^{?zpDB#;J|xy$E)uXn+hXnpvHpC!h-%E`mn^f zA_^Qp`u2570IwJw0c0t@&O)WIgIDYV7+IjH5U@S~Dey$i+pVa^Wq?=xo75|7)MqY2 zhRd;W3-b8%_NdQfN@>L9Y@zOQOLrvaKUNJ<hQpdPDVJRSRJI7Y9=T?CE4}gG_oYdJ0!6?NiPRIqByB`0-QsSbtk&H^9xN3jV;M1u#J+^c_Oq6SGECy zTv?S*+?av07bQrTDD@_f|*}xgsZ5KVENKDNZacvrxwK9ws%%?Cr$(y;*%Fj@QX!WM0A|p8nAFRj7xiIlo|`F zy&r5J(ETQCK3U~OjEq*-=eQK`%73)rqEZ14cW&lG3t5gP=^#C5%2_2UlRfyU6HII5 zcY7^dl>{9kAvz9=ki%PO(Ap)Cizp%zx3o+h`4= z2;Hq2WQQm4JxQ&USY>MpAKstbR&v$>J=|{srrzU%K!;B*$zWu>iYNfXX%Tl^I5Ui; z)W@D31CXrC|KTLl!m+s%66Fr;@>y>Lv6{F4H96sr5RGF1d@rLy!dI*l$724IY!&Ce$q z$%ydpL_WpD*VOE{BXKo_NB>AwQuX}t1nLi?m30jXOUsNPT2J+iM?fzU zaNNCPV<^sqYbau6hmFt=Kc*0u-22{%5SX8Uqyeyt+FzH>To^dS(&R2z3470;f4o&d ziQr#sP=NdTzYf68_}`}$Fa|h*RDXDSJfB>0|+_xE&TN9&vR z5lNa~4XLm*KS!wJ{}U_K4cFYJC%RoHfQ>$7jao=U&t|F>a8M3o<5$crk}ht@nfLl@ zIk@P^hWNk6Uv{v~W_cYAhTCgik?>7Aq_uKSXIxf9qGH@$4DI9X`axZIuU9Ro`w~8@ z7)e}82{SSciUk>+9jTHya&hw9w5d6B(YjucV{kJ=4Q9jRHXdub%F8Hqg(mPgZ&ih; z*U!|S-?A3yHZm52wL0?dmT#X4#QO-Kp8&z{YW5mK_M)dSpI!4iJ=Q}cp*FfC_KuO> z5U5cG3rh}}DdF}L-0skIY~l_KpK6ygc8>k#ZFujsef|r{Z6wu9iA$$$M{-qydP>Zo z^-v=o3>W`Or0F(KY}h;cLoE!GWb%X1o!wM@SW#q; zjFk%AhJNkBMbsLkeVs5LZd@Chq}@yKo07ukI`l@&=IV>4iF|JO236*9;^qwXL{~hV z$)5mZ9?KBqsc1P*Rri!wLnq~9+7MAkr=xqvdwkxg_~yPRTN@|m-l4XS9v5WiO~<+3 zT-)Vg8!MDXpgq=058mz{{|L8(*NQaPgVTo*60k{(f3v%eDe4Y)B~H@Q$-vzuM0)PL zv^M`p6eVioRN%5V+rJ!$;sq*ZR(?A(-QmCWjQZ2_Z`M7AhJRq$> zVDaktP{l>x26n980ddMa35Lg-tkr;x`15}5SI;JDe_MPMl{|iR=#i>F`!zfEG`LDl z_Gn8J6}Ji#(9_$xQ$jWI`5WN%XQHm+LROsS!MDBmJob*RgK?!<&q=skAumE(T{ziy zoR@=BiuZNG+DALi$Ia^$dH8%ibTA{n?c1q$<^l9!r+evbgK=@1C%c(p)9=So7?Oge zS67B}NBj)jSQqBoxa^-le2}t3O!AmuUrUDopAzfgO0Zj?M*C3HyR1<=^hcKmO~OLu zM$!=zNDmIA(|rh6dWFDJO(~1FFc_o~<*A#z@bS?O^L|i+=XX z!~UqLdy@{7nU23d&n0tvO4lRN<1X_SjlkRug_^qZjpo<0T`p~sdPxN45)%;$9@z+w zg?4CZ(&Z)wzT-yUdDg)g>B;voN3mXUX~J#3%}vOx>&12mt#~NOzLmAPsdqw8-}T}i z9DZ^tq(kAC{6xmEy0X&fd{?Lrue(Lh_)r;Q=Fx_*?nzcwpwG|5`KQdPp+yhf-ERnY z-NOcss^+iokanK@Nq_tPu~|eP)@^~WF6q1 z1J!dB2X%cHYvLzFLr1SMg-fcsJQ2P)3+}<4$p(wrk&*{l8%2NrSP$Et;!KzhJ_`a2Gr+@>V9^?Ek5e7QXE3EOE--6cWj>f!c$_Yr6LL{G2#p5-jmPtKK;uxWfr_Bf z;DWBdANgzaZy_09n6B#v4H}_eYTG=L;uYRlY*Ff=w6w*KdYsR_Dm*5z{k7J)^g_wO zIbBdDY>FYAFW$NNyp#lkWE`0A6iQOJPP04f2uKd7=zL&+DNT}6~nyk59v>{?O*n-8_vfJ6KxcKO&tHe;b; z%BwFTdZrr;NF1mb3u87_ps;(kY3U=@ekxBvSCBKm%TP?> z^Jfg~A+(;Kq@>$xa-T7bIBH&=?%J*PO6-h859S*cVa3!}i{)jcS^=N#{JtncsxGG6 z^`hHV;_krZbU8KfUY)genbe&>YV8m_)x%X*P!bNUk)!Y7D;i~x?X%+sE2_9ct0)GC zf_4>LE;E97nn^DNg+h&+6J2ZeuauE?2>&G4A1Agm$}UqzheAZ2hR+0ZoX^w6Ogn#u-iL@mI!dQ z44nOp7muVgW6H<8-lf%wV*@VZnRxpSh66`?{Z*|B)V$|NT7i+$4(sk7t_z#j$Zd}2 zdrFjClM@GWU05g%E|-NQ&PO8p7e`TDXnmb@^l5|p-^Y9&V$eG`gj!=y5K(sY)K4W2 zi|6$wEN~^S(6YPEmAiYxtErvVU#vj7Iu<$Y?TX8%?T1R2F{#(iBwB(qRE=gTJ67(73u@eA%DQE0CaoRuxn#u^QPgT`Wutp#L?8$mXpm* z8;Bp^8_efgOOrIYwHV?Q^LrNdb+Aw+FN1Rxf$!FWEqb;hOU+eh{_3i;v+1oB;G$-z z>>P?eUy6)o;mN6QR;uw_pa<16nr0(a(trv%Tmp5VA}?Gm+OHqEyE7>kW2*%AlR`GQ zO!^)>n=az?^S&56Q^xv`Rj6W5$LF9^nP~BtP;5U+_JTZqw0sGBR{hcfyrLqh3dzP} zzKoB6j_>o<%Q+uY=Ro41+Q)u*S!^WjB8!(bE3>b9T32U|*KW_H{%Em0P#6i+HyGSJ z<^)US$QzeAODnmo2+=B^2`9lJxL(6QzuhNbY2!VX(2zPw1aq~0^Y{XNc@VAxQ_G&C zQ*7va`QNZcQzmpBsh3P2L%ZS&87hOKz0)EJ%!G^r{4Qf89LKl}ZB=;Z%3Z@b#=ert zAG1L9V}y@Cx#ZBC8Y8Q`*~*ZWMivfS1MoA7DS{m1V=GEC>Xj$7w^uGm_^G{|8Z<{( z4-06;fbc(w5P%J&1{VkVF;<7*)@zx#A zi!H$&4z!b;x{;geS6#FaH7q~d_}V5p+*CcPx=9r4XMFoSkk$;Yjf7&43FgS7eR}w( z)NmKO-nsa|!i_ZLrI8Fb2k7>|P$K`ijQxjXhL+Qx_B&Yo3d|^2r~&)~(f&ip^`AlG z{|F|=%+MQeZ|c%t(2qJwxNGz_+|mHWVHQ|W^cT@$uZm!4B{Ohaf1R71{oUd@_VezG zlg+^eQlOC^z%ax86E{?{fcW^1mpwg`m1kXZmVmvejd{7w$zYzy&g1*wWo)}1$#IZ+ z2p-;BI*HrrWcU$&@*4zDfe$PM1~&6XBEYPjQ_?#*y9|+_-k;dgM_7Zs3{E25N?Y~lK&qcywUmf#W~Y`RQ8{N zRB&v6Ir3dh2D?y6SQv+l17g#6bd|l$#zjgNZC-e0q@9l*2WI$_x}u?uhY%0}c-8E|%iS+CuZW94%7c!&RABh6PQ!Fqs!7kKg00!Z5zsoc@shegn* zjRMnC|HZ!e-7Vg!UJvALMjBn(m1BQx0$HN2Z|1Hg;lSNRnVY^t2_TwKew|nQfy02X zD3?=^8vL-6ft`XNnrdYL4yUupL7x#dD&C32I(t||DCcehB+yLEbTjJIv?QaR_j+>DrLI-6_^m5dGQMc9GvOg_Eqg_7!k)7*a!2K?a9!c zK&h2nnjqcH8%et+L3PB*NJnAR@tlr{12uDFth!f2`Z9qPH2!AJf`MuXEWVTz)@k|! zWM;~M-sux4QY^C(wb7FDFqV^F(DWCUmgsUdxh*`pRQ&-xGrU<~54@hOr*!sgU}e?t zT*{uMOuv9>0cNrs6$s_YLrkbY(Bi_Zw)z=R6e||a23Qz(^S=2ReM=Fw8N8Ypw3QTf zUhF}uPXl;goYzexPqm7Ls}vJ+D5aJ&kp_(8nT^nmc*aNVP@fgcm9D8Xvoh5=Ou(BC zv3zIMx9KsRs4yKO^W=>-ksJjTD`B~j9ziYTuGe2dM!PfmBF*mVArKbzE5p^dW{OKj zD0J3LmxCd~HR#*iqjA{hWs5AohZR=x4#(eYZD7N5sGg+%?rt2IKUx-dFpc=+yStk&l<*HbpquiIS{cU@cevkvH{seaMi{E&; zFU@FVoEOOAWGc5b_fkLWN4AZ0soeH78Jrn}g=mWG#>|!_V%_GCS!mvDRsS>$uwG6LqTNZ0T{uZXc`z?N}o6}bf+{3c1M#fxfRQgR(mZCIk zI{wBNmcq%8OwLsq*phIr)5sd4{DXAun_^iaYoRkIMr%};&@2=&!TryqRb3KB8bt4P z#YLbiPc-3IhFp3sn@%bBu%FrPnUX3$Gt^B5l5qrCXFea1#>Hx^ADEj{y$amw>JQR1 zuFVY}<AK^}Y573!$t5Kn3in!;vPrUu0`!5G$Kl_*|2zDcYAK|mb2K+Irx*d+3z3}Gwv*E8);7!hyMWHx|Z zX`bS-2|kkniZm)Y(yInPP@1a=c%sc1H#pTV-c?YBp$HI|ZM4Wf?a9v_(IZ^Q?DR>Q zUb7j_V4J|oHUXw7eOQ2Du2@PJd$&~7#6y8<4HGd_pOoSJ0X@R`%n1~MYUb%->b&Dv z;qlCFDcq@88O6=B%wZ{9oe5z0D7Gz|?E3`jd(O9=R79hn(@)-47{$bz{8m^msmft= z(Y9QaG}m`C%ixXQw*2(M=E(zx;ZcQ+^BaG?$3N#nB-Ar{vvYtlARwM-P+^7jz>a|# zFE))~EZb=DHJWi@A?>KmdT*K0CJmZt+whM~eA5YbRPTv(Y&~Z>n>bc8bssOYO_t@0 zgdue1Z8aY?{*hJI{^7~$uke$MEY|AYl3YqM`~+@xIf-2zZzNl1B-fJC3+V=I!c?_( z>Gq^e^E<|BP{;L)6UBEkC6O^wG{2-!TB2h)`VBdJe;R0V02G*Ef@X6Ku5xl@1bR*byNrL4dP%%lyiFR}x&l^^6}=FU=_VXjkJRPOb>Qm z28yA>Gfc_B>a0p82n;|tQ^b8yVwz&X4a=EZ!|zh?IIQpic|V0a%?5a6ht}2rI8fEd z16C<`0%X`Scb_=+Qe}{O^8m#3)KT!Rbh=T2G}nMbOG8i1L@f6vnqIxk6VCH@zaCf% zW{lg8MIQsXs3>zyP~!Er5_t%b9%L6oq|iW$n2YstvJjwEgH{76gGPy^SOCEZh=obQ zIsRtTTweOz!06@|6rZ>6$mYO?9~%)N$aJ6628_847d(USB;c+pX(Y`ru4;Nv1*eNL zBQP@XqdtNMB+@|o3(vcOZ|)OiDe_$A5IqJSZnDAydkBmz;33a@SSpTMZw7RAm`?&g z+ce@>>jI9t((p2+;-&B$cM?c|JoTL#+h3}HZVkAK@^Z8DZu<&*XCCt%`2M@_=ugJm zIhGC7z;LXmq;z`gcB$}A!+gkQ5UsTSx^txG8t^D!j|sL_#*I@q%4x>$?2?>ak7_c9 zc^N(H;V&KN1`y6Lf88~YDfmvtIB=BF7y|J!)101mm^{w;40+X3Wxd^b`5ecyj_PXF~FU<3UB+5>jT-Pt@{ z;Qu-`T0-~U70y2c|695n@%_K@-@qaF7dM4@hhKns5KOQgKw5SG4E&u#|JQ*hutWar z0XyXGbaRQG|ESKvdYFK`1DSreM2`H*XT9|!PCv;>_=ZN1s%p)1DMU}Czm9r-E&QD? z3QWdAb*Ro}Ci?cEa;PV8X+1-swh?cd<4ii(mmGph|| z-3Q)U`Gpxp(;H-FMMX!WKERfgINpEv_c5z>d;@E(+ipeGec1$x!W+kTC$zdd;orAN z53q*ZP5<*E*n9m0(@5*$Y=Wr(jsgJz$kPt?uJ7L@a2>E~r(^=W)`*FIT}jDfbP2?} z2M4Mu@BRd5Z{q;)Nl%vAwACXc%*`k-mDPQMOlYfbszN;9!*0|9o&O zJc!N;-4|hA z##X);MX6VvWI-8Z8L~Ouzk7Qb6w+iS$0rWumdP@J$9N?VGQ6W-{yQ1E9nl#$Y&Mp9 zw^e23WZ}Pj!QBa*E$gDx;GMY|2AUJpK7Hgzm=6oP8XoSVOq`hkE*J)$_dIWB!t=+y zf6s40)KQ(99&wTg+E1Sf?09%0*;~vCy^5Hq$eRE1cRP`2;kjhZVhV@$r=>;6WkIHii4UuSgK7Cj+S;O92{|0+CWeJcDvQg3 zS;tYGkNGp%9#$c>1Z&NEv;n68cjJ1hPfyJ_-wFr@|7;r_?VYK=L0e3W>rXQvp(jZ~ z%7W*=qa6AZLT>8KXe7J2UdJWAe9fKk=e8O64rh>YLESLEPft83;@{x)uU-3h%xkMm z4&3kI+}&4LBn23wKh_C&55RMv2J*Z2GY|NPV4x&ejqclhVUPZw0oOm3zW>_*DlGo| zO9l=*=d%XD90PK*vvo-R-{bRNN|JwB(PUn+% z$)lTyz~naK=lEAZo0a`vcE-WSlV>xwm><4C#l@&Fxmtw2Vjw7;)WE^3E^Fp@Y_W|l zkgG8Na`*Th5aBX3gQ{1QvM5IN2^BS`Afpt8-=016edgjEiTDfk!!s{wv_%{LkT)O@ zzra6sn8_MdU%s0tg^lx4IYV((uaxa`NoD2f-hp-@I;;0auGj_JNYg-=n_XRfdUoT` zWTkH(_FCD92XBTFwM1*sR;{4<({Ex;9}%k{ou7?`jC0jAbPR=r+Bi5l7z+T=6-DJY z7>`jcO#KEI77nbP96#G)q7E3cbQtutRvLAc2?+{1S-q?8Ztd-AQ+-paZ7#2;to-gB zB)9`cVfCec2UsVj~>T5?CuQpGpfcYu}Q{{7q?+mzRsNH5(BXCWW8^_A{#cGPQ-- z7W$i{!EMN|G;2(X)X*kJp@*>bBeg<2TY-VfD{|_^sr85xK!x%Q!Np;~;Mdgmjoe(! zHVm$ss@##>SIDOT^r7-8;4}OXj<$P`I~)R6+5yS!&iPDVW*aR9UwU-kwhRru zBI9V`d$Bh!Ou|O9{d0WC#bGxUdvm;|umC*0qdsZ$S^M3)kA{Z&`py!r3-i}57{p`3 z)Qpa6MKNbSMDc7IwT?|QP3~*guLy}$uXjh5#XIUQ9xtF)Fy?&A9e?|13(y6qo~Txt zFg$X?#(AE|=MXq!XKR~Z=rLE^jO;@yBWYJ&S?Taug1aF>MX=mEBCWEpfvtILd}1gC zxm?WX%o20bHd#7CtVh0yj*ccf2V!k$+u7Uuad_BvcZ7c1+S>lPi!r}!{0U@-7!W&7 zr`0#n9ZeB;kPnATCWC!b-xVCbu`YoQ(EB6JtpC1jsUX5bEnd5dC6Ojf@oGwW+VGfYV9Yct z2=&(DmM$VJvBLf-WG96V0(QIJF&xnYx1nH-V!+{nBMmcb`w@H%?@dje zmmQh=aaiQ_U~6yF?;op*5ckR($GQ+&B)K4CwzZ`1m31(!iSq?(=o8pswjWsBAh>DgOnyhsD?mZmK zs2m8An`=5+98_A`)oT`y1~=B+m6e`t*j6DqlQ{EoKJ0x;<5@FpcOM`=q*5NI-E~k# zxAhVYtT>tG;K6Af77;OBGpj|Fo~0?4(u`~ynJ%zuo)axQNT2oL!=kkb$dDNDn5Wbk zsXEWO$8p6PLR;R{4i@M67ZBOt^lK6n{fw`aA0A=3w&KQpdTq7q)ys%yLvGq5l6EAH zaL%GYElc+Tq>2};BGAGUAU{zcY!MqwD@f@eO@hC>yey|IBL(*@&X_&T5mHxbOAOp?fjGR1Peaz0mC7vORZ)s(f@r~%o z6Ol&=xzS$lUIrJW_x~2$B4{gGo_#MVx>5dUienN!R$%Cs4r>LcU3_`9H$gE^J_>uQ zNz%eXGth%>$2@U{_wnP$&jLPl>8*b+dWaIM{WPql#n;R0-PKSIRDT(zTvpU+@a%!L zhRi!J53TQY-mk8C_PcGEz4L-5f6lJtZbV{zj{ig*mHS)Bkp78=(nsu|51e(q{qj^% zjCB40Z;d$FGxszF#dhp+7I}4p)o78(PEM7F8JhM20X}8$S5^;lafM~cxOsD`7m7!6+wzRdhy_ggiXKJEL{RCypY&&fclfnuc2b-Ov zCQ_}ge)KbUkD83)#>S3}vhvQxg1O_P&Pn-$3=~T}Jzuc5u4nLeK$<@VlW=P*TTR7# za*MFc5R{)v6P)O;o@|iy*kUektR#MqiR{lb>uf>YAtug1KCx!=Hs+Vn_KFUb*3gh* zNNPQHE?22r*ll>zzzANL9Zk9>3C0ZYm491TVd)V1vzEI+N=aP-s@=0;KJAjgW3$m= zkkkP17tDLBt=ELvnwyMTHmFsw+LHDdMld>Kw!PKWML*zK z0tv~jx3_EwPYR2YSBcjjbV<>Oy6=z)9IA=#kB6uBtVgpM6tO&a zQr})Q`9Vv1uDY}PG_B?wd5}LWb;UZ?bsj2bA}5Et^K2^i(!O_V zAR4-T@xZTkxV|gw%aj&*v_ZrKlO;6}a4lmqI3$hvqyx*joUbBPSSxv12K`hMf=iA4 z?()`#SVpf;dc_oXfN&^bS!pq*cncAy9h38p-J-4zW+>sr=Gs!~kWnx~Q9;49mm3$0 zG3|(Z&YY;nd$0b1jsUTc;DRU!04T3$01TrPb&=T(ycb~kXq3z5fTkdMNw=*4gSeQ( zy_D-3d-Du2$4M!5ihlE%s+U|$&3$~9=d(dZlHOkg`oO%{MA(()+hVz|FVD^gjUp%yy=sV3Z<>uCmSk_4btO)Fo8o z;MSgL+7}c)sq|*4fN*!e5GooHE^gyq)81V(J%HDoKZ}DKH$! z5bh6ZajP7M69@?hHh9#xV{80s)5ltVUYf(@DcvS8|F_DbDa*Oy)T7@uE)};{C$Ht` zK?M1tlCiZ;w0v8;bGCZ{2C=H!m%7NR?&y23@@{y4fnQ(QS87B|*z3^zB1b+gTf(2? z8IRQ+rCaq!hZ~#AIleXTlgNT|30PVvrGKl1ZA-RZPNhm!^9aD)#{J?uz=qy&j!xxX z;u@8ySVd&*vc3)NaI7CQVZ=BMEl6w_EHBBYIbS&6%f@AMIK2=bQh2UNOGsY?z?sR` zjKx@dJFXm4x<$8QDXc9cT8;)TE7yF8%2mk1MB-z#drvj_Zk8SZf|~)uwyaTNlu?~$ zEEblpaZ~aAz{rKVilWh`Ce0P!neSKK^4Ksc$ZrL z=p+HT%7yf=E+tisGNeKFhih{~!)EWP6*|+-ohbD(0*E!X-L6bq&~KUY`m_76almwx zktl-2JBelRlHE?CbXkbcsdMYLi6Xe`NYx83X%lJaqeiJ*i3raRy=-!+D)lo(eSUhq z)Sj__>^Dvq`6B^vw(|1O?QM6SI+{okHSloZHk0Pz#S{oGM&}xWgT59`T#A z@-=2z2{1LKaL{H#7HjwWdnXF6;P1;Xi^gS0tcPTOS$&1gLwrjID znoZW+dn~KGa7;!cFgorY5+ZM-pFg5L^hnWL<(VCReB0;R?38iCHp~FXm3!((9AfNg ztqZ-Row=9a$JRHgB&iy83UQTxGI)VYXQ;!CVu2(qp6%@Q$<%Sr@~#dedtL)9D9MWoN;U$lqLx!;*>3IL z6eY)Kb5E`~4mw=SL5KgPy;+9{OX9dFRlLWnxB9nKtd-|1vW$>yO+pj7 zoW{no+s9&gE~_WhYOc{T#5&Ji!N14i)tx2GW?TBNF4k_)bG8JnkX!l02TtpFCeQZ| zw}}a{ej?3X#pSNDK{%9cx6>6hUD_U2s#><>M+_96sF-73J1(pZ1_6>Ei)-ZJU1IuM zWBE(f@E6s${laPEp#h}(1YB%~#p5pqN45(fbx8g`Lq$SaF1E*fS7(CFBs)a7rrfIs zk*I~{lZ}>2w>x-TDN=cFyK7hGi_Pj>(QMq^mKtm2+H!+=*}00B##xCpHu__C1+Stb zghw-qC%04DV@Qx5Gj-d)pkEzO@HlKHPjnxsDl88mP$aXNzshQAF5mz)E6_>0OCG%s zyM8rmLbuDxJ0z^NNfSiy^Q*jAvfc<-erkHHi)XB%JFeO=oQ z4rj*edf2bS&kdyVb8s{}ZbOJzr5eyiHE{%Wc)$8%|B>YOGI~Gr{VAJkFb@_l?dxii>&}=W06{Nt_Y}6hhB!CcCHkGbm}Xt zLeuHSEKR>Un`iOJ*>ODPJ=Ovj8J#MgX-w#9R1tC&SLeer_$S2+`8_ZcN9;v;I*0}c9MJBm%pycawo0qw2+)1EWT*aUDHF5b} zo-8FycXmXL8E}E*%c$5*8{_rkDb%-}lh0^qnX);F#%Kv(Zw3Li=M;D};Ms)%M(g>4 zb-|6IY+f>>x(kVBr&OeDqNFh&4Gm*!^P%IrnIocjBQ&xxix9%I%-bh%PHq!qoj%M@ zb`~nmy!@{T2`MvW9WwzM8uB4tdBgAq?;#U#4f;bA$wCSbo6#RwbntF&j<*O(ixKuhc?|*oxzca1f;<}I*Iz==6x zJXL;|o}S`{@v~~+vtNeR}%oJ5&UIu9EMHqzeI7TRo37|vlSOo(p#fg$c?LLz_ z>kLDEK@PtDmcFEho#=y+x$l-YSE`uWbdWp};>~V###M8ZQ{`Q+G{utR=3#4&j5iVD zL2bW&d5l;*; zV^w(Lsy7l<>_joR{@%#SdwKay0(G6WFdk1cVQ1g@;ua;!_En4#QO!uwY`PM|#ADQ6 z@Phv>+a7Q8aR-sBEBWh++x?}dRK2_HosJBo&it0RL=MS|Sn@xo6B0Q|N#l}imNSB` z8n3m(c7kUSFM^sc&%Bhl>^E(WCEz+ol8%VkE!RnyYcB=`Q5r*@QR->c6G}qkFd8Wj zo^hfzS^64s+Ll~ytO}l}5!KU|W#HJWP^dpL!yIOS12uBOQl7~!HJCJsVreXBlV1xX z7b#d`yPt16lab;}d7U&&U=J-E8W56|h;DPTKFt8uVQ|K1xcxU7k`#Gf7AKuw$8K^m zN2#fXzOtgt27#!QAd4P}MRJ*{4ZG`%0{LDybsEU>#phJ(qnMZnel_~#J;yksGDw+I zpR%*{m6Cs#VR%sG;b&>Ku3cFw5yYoxvk^@4PF0pR?aNyidYrxJZ5~RO-XcRH%LEnDB%|3bOV)lg}`MYabxeOLbFug!!sRtsPDbvM$9ic&@=h2ZKDapECfmC_i=a2fs%&us@5vfh4g`g?aC-?UdiBR z*(en|NY&RFsesf>sk{~8Tw9fh@oxb7Pd&Wr+>7FH>TL+)ZMr@9Y^FfswR6l#P=rG1 zFsl9$fbMYs>bN>%;nPvS24^lOBm;V*pVI?~P8UUIeLW{#>GYC~(Q9N6JI96;O$sX9 z!P!3Y&;r-B;uquwE5EI3Z$iK$xzy0EmQ1hoZF3VB=vMVO1uH$bLm=sRPU`y=zhSlQ z`mS}w&lLj$R<213#P;^{y^wi`xqPR-N?-GDx}W9qy>>1S)I=-f*NA~NMe|WD9j%_L z+F)QL_44fgK6+`&P0v-LTb-rXN(cfwj6j#+kb}CEBB@(3sxi7@2 z>C+scOFxzC3+HqMdyc^Ix5RJbpmv>`kPphzTqmE6+VUtPf9KwG>nQ~#^{Ty$i+iO+ zBU-4%^cImd@beYk=wTlr8x}*np2sn(;2R?4*_eTW{i^~8i*NDUQC(sYwY9gnCm%}> zLxUuj5cUF>4zWC=HMgHuyPqnxs+mFrual54EEcZpXZrj1yr-HXd7#REQU&J`3CO-a zGN4{au1Hw)R-JRBo`@m4?)H6Y1qCGwOD5ylyDkfhnLv~xfFlYerZZQ?K$rjtw3Fac zi%Ek5iKxt9^v{tptm@h^;bJ{Rn8m2IkpE1>CjnRD4qEYrR=M}5?UqSR*K=6UjSiVw zv|^=fe!C#77mdtDr{w7ku5)DH*&zFcw~UUJZBv*yOw1R)O0O_s8a z4#F7nxe!%-4ZD$yO?uQ*8_OjZeDNRAdrI&abAbAg(=83ZezLTIP6ch16H03nHJ!lY!(hOFh4($%r0?NsCm z7#>Qk(!g1plr-Gb77cx*Ny70_Ba83}q1dgKxmZK@Y*aM&Y$ifd9?$kxl9XXrsE6BS zE0ZAN^fkPK^YqPBe#>f~8W{S@qYg4(QC&uHqoSyC=#&+*JSzW|R_L5|qK7ATlgqgC zhaFS(QF3i1ADfGrF^b^3m6KEfY`IB?^M>mh!?QL_VQVUCA;A`zIcYlT=?E~C<5d(@ zWu6cZvCgmnuJ0{9RN8-rt9iN_8emg|ehQa31sMvAJS@kTRB;;ry6jQcUm@@{0(HbE zC%pFT$_W2frtqfqo%5$puUmFY@>Vkl;2D||_Xyw7n~f=+{z7a7%*k{9WLXpOu1+$Y zw}SF0x%wQ4anHqC^kO9xm6Vh|%IoaTt*w~09is)~qFJg=bAh$+b_{gCh}meK5)4jL zpvu_e<36ik@viH8;&G}@>?_*r=Iy9SG_I62Lc{41ck?Y;6z{l)t}Z72$@0q0X6)jT z0k`!>k+UwMYW%oe+g-Qf{K$s2$nc_}bN zS!MDVbeU_Osta1*YnrU7#A^)=3AtRopgJ<+Z-b|c#G+rUE>($$d+*UIN0h<1ew@YN zG(L{;C}=FwKGuTu_cgY(!?B0ZJE>m4ujO+(RLM|`SD{NPwh8J;2r1dDevlTppM_jV zND*Ll$guO!%u?hRo0`Ee+Gq!>hll>=or?4vgzQQG?cOqWVB7{(jy!49parr_*ARV; zXRfhe*DBC^2Gr>S8>yniIui5VB#&aDsGf!c4;Z!;yf>21(jB5m8QGLg`5+T6mvIQ>s}1mAjbwjBK)T=mFeL%VNCP-;&J|CA|4tVD@J{}2M^Ja)6K{(% z(kX(5dH;?)QUJCK+dMg@a?P{Mxk2&#d;p>awpp@a!n&Pd+bQTjYSnjRcXj5wzd(1` zpSwTra65N@-C=I-|N9OvbNA;R*5!Wiu7rN~XBGJsfJ6h`-VXMNz{>1MuxmvCiINmG ToRP%b!Kp+Ar9MF3Ykm1Yf8_)U literal 0 HcmV?d00001 diff --git a/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-with-template-chromium-darwin.png b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/editor-with-template-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..ecfcc8a2f27a9ea78747de696bd97e45a36f7ef9 GIT binary patch literal 91867 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 literal 0 HcmV?d00001 diff --git a/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/json-modal-open-chromium-darwin.png b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/json-modal-open-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..894897b65c262f85b52456a1a89e4d1aef065b2e GIT binary patch literal 133178 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 literal 0 HcmV?d00001 diff --git a/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/right-sidebar-block-selected-chromium-darwin.png b/apps/playground/e2e/tests/visual-regression-baseline.spec.ts-snapshots/right-sidebar-block-selected-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..624d3af3e8a54e4694930fe1ca8af1992fd21661 GIT binary patch literal 112037 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�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�#F+MYdwD2hJM=Gv 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
DqtH*cG?opwZ*r-nc{%wa`^M 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 CdURU= literal 0 HcmV?d00001 diff --git a/packages/editor/tests/e2e-consumer/aggressive-host.spec.ts b/packages/editor/tests/e2e-consumer/aggressive-host.spec.ts new file mode 100644 index 0000000..bdf88e6 --- /dev/null +++ b/packages/editor/tests/e2e-consumer/aggressive-host.spec.ts @@ -0,0 +1,96 @@ +import { test, expect } from "@playwright/test"; + +/** + * Proof-of-fix gate for issue #70 (host CSS bleeding into editor). + * + * Reuses the existing vanilla-consumer e2e setup (built + packed editor served + * by vite — see `playwright.consumer.config.ts`) and injects a hostile host + * stylesheet via `page.addInitScript()` so the CSS is present in `` + * before the editor mounts. + * + * Current behavior (light DOM): host CSS cascades into the editor's chrome, + * the assertions below fail, and `test.fail()` marks the run as + * expected-failing. + * + * Target behavior (after Phase 7 default flip to `shadowDom: true`): host + * CSS is blocked at the shadow boundary, assertions pass, and the + * `test.fail()` annotation must be removed as part of the Phase 7 PR + * (Playwright fails the run if a `test.fail()`-marked test actually passes). + * + * Removing `test.fail()` is therefore the explicit, mechanical signal that + * #70 is fixed. + */ + +const HOSTILE_CSS = ` + /* Element-level rules with !important — beat anything without !important + regardless of specificity. This is the bug class consumers report. */ + body * { font-family: "Comic Sans MS" !important; } + p { color: red !important; } + h1, h2, h3 { color: hotpink !important; font-weight: 100 !important; } + a { text-decoration: line-through !important; color: red !important; } + button { border: 5px solid lime !important; } + input, textarea, select { border: 5px solid magenta !important; } + * { box-sizing: content-box !important; } +`; + +test.describe("aggressive host CSS — issue #70 proof-of-fix gate", () => { + test.beforeEach(async ({ page }) => { + // Run before any script on the page — guarantees the hostile style is in + // the document before editor mount logic runs. + await page.addInitScript((css) => { + const inject = () => { + if (document.getElementById("hostile-host-css")) return; + const style = document.createElement("style"); + style.id = "hostile-host-css"; + style.textContent = css; + document.head.appendChild(style); + }; + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", inject); + } else { + inject(); + } + }, HOSTILE_CSS); + }); + + test("editor chrome resists host CSS bleed (font-family)", async ({ page }) => { + // EXPECTED TO FAIL until Phase 7. Today the editor mounts in light DOM, + // body's `* { font-family: Comic Sans MS !important }` hits every editor + // descendant. Remove this annotation in the Phase 7 PR. + test.fail( + true, + "Light DOM lets host CSS through. Phase 7 default flip to shadowDom: true should make this pass.", + ); + + await page.goto("/"); + await page.waitForFunction( + () => typeof (window as unknown as { editor?: unknown }).editor === "object", + null, + { timeout: 30_000 }, + ); + + // Sanity: hostile CSS actually landed. + const hostileStylePresent = await page.evaluate( + () => !!document.getElementById("hostile-host-css"), + ); + expect(hostileStylePresent).toBe(true); + + // Any visible editor-rendered button — sidebar palette items always exist + // post-mount. We pick the first one as a representative chrome element. + const editor = page.locator("#editor"); + const paletteButton = editor.locator("button[data-palette-type]").first(); + await expect(paletteButton).toBeVisible(); + + const computed = await paletteButton.evaluate((el) => { + const cs = window.getComputedStyle(el); + return { fontFamily: cs.fontFamily, borderColor: cs.borderColor }; + }); + + // Comic Sans MS bleed: the canonical reporter symptom. + expect(computed.fontFamily.toLowerCase()).not.toContain("comic sans"); + + // Lime green border on every button: the most visible chrome corruption. + // rgb(0, 255, 0) in any browser's color space. + expect(computed.borderColor).not.toMatch(/rgb\(\s*0,\s*255,\s*0\s*\)/i); + }); +}); diff --git a/packages/editor/tests/global-refs-audit.test.ts b/packages/editor/tests/global-refs-audit.test.ts new file mode 100644 index 0000000..a781fcf --- /dev/null +++ b/packages/editor/tests/global-refs-audit.test.ts @@ -0,0 +1,93 @@ +import { describe, it, expect } from "vitest"; +import { readdirSync, readFileSync } from "node:fs"; +import { join, relative, sep } from "node:path"; + +/** + * Snapshot the editor's use of global browser APIs that the Shadow DOM + * migration touches: `document.body`, `document.head`, `document.activeElement`, + * `window.getSelection`, and ``. + * + * Each phase of the migration updates one of these counts deliberately. The + * snapshot fails CI when a new file introduces one of these patterns without + * intent — preventing accidental regressions to the host-CSS-bleeds bug class + * (issue #70). + * + * Updating: when a phase removes (or adds, with `// shadow-ok` justification) + * a reference, edit the expected arrays below in the same PR. + * + * File paths are relative to `packages/editor/src/`, POSIX-style. + */ + +const SRC = join(import.meta.dirname, "..", "src"); + +function listSourceFiles(): string[] { + const entries = readdirSync(SRC, { + recursive: true, + withFileTypes: true, + }); + return entries + .filter( + (entry) => + entry.isFile() && + (entry.name.endsWith(".ts") || entry.name.endsWith(".vue")) && + !entry.name.endsWith(".d.ts"), + ) + .map((entry) => + relative(SRC, join(entry.parentPath ?? SRC, entry.name)).split(sep).join("/"), + ) + .sort(); +} + +function filesMatching(files: string[], pattern: RegExp): string[] { + return files + .filter((relPath) => pattern.test(readFileSync(join(SRC, relPath), "utf8"))) + .sort(); +} + +const FILES = listSourceFiles(); + +describe("editor global DOM-reference audit", () => { + it("source tree was discovered (sanity check)", () => { + // Guard against the walker silently returning [] (e.g. if SRC path is wrong). + expect(FILES.length).toBeGreaterThan(50); + }); + + it("only the expected source files reference `document.body`", () => { + // Shadow DOM migration will rework MergeTagSuggestion's `appendChild` + // (Phase 3.1) to mount inside the editor's popover root. + const actual = filesMatching(FILES, /document\.body/); + expect(actual).toEqual(["extensions/MergeTagSuggestion.ts"]); + }); + + it("only the expected source files reference `document.head`", () => { + // `useFonts` deliberately appends to document.head — fonts must + // live at document level (shadow root @font-face is unreliable across + // browsers). This is the one intentional global escape in the migration. + const actual = filesMatching(FILES, /document\.head/); + expect(actual).toEqual(["composables/useFonts.ts"]); + }); + + it("only the expected source files reference `document.activeElement`", () => { + // `useFocusTrap` currently reads `document.activeElement`. Phase 4.1 will + // refactor to use `useEditorRoot().root.activeElement` (works for both + // Document and ShadowRoot — same API surface). + const actual = filesMatching(FILES, /document\.activeElement/); + expect(actual).toEqual(["composables/useFocusTrap.ts"]); + }); + + it("no source file references `window.getSelection` (use TipTap selection APIs instead)", () => { + const actual = filesMatching(FILES, /window\.getSelection/); + expect(actual).toEqual([]); + }); + + it("only the expected `.vue` files declare ``", () => { + // Phase 2 rewrites all four to teleport to the injected popover root. + const actual = filesMatching(FILES, / Date: Mon, 11 May 2026 21:20:00 +0200 Subject: [PATCH 02/26] WIP --- apps/playground/vite.config.ts | 6 + .../editor/scripts/inline-style-css-plugin.ts | 111 +++++++++++ packages/editor/src/Editor.vue | 8 + packages/editor/src/cloud/CloudEditor.vue | 7 + packages/editor/src/cloud/cloudConfig.ts | 15 ++ .../composables/useCloudInitialization.ts | 6 + .../editor/src/composables/useEditorCore.ts | 11 ++ .../editor/src/composables/useEditorRoot.ts | 23 +++ packages/editor/src/index.ts | 99 +++++++++- packages/editor/src/keys.ts | 9 + packages/editor/src/virtual-modules.d.ts | 17 ++ .../editor/tests/global-refs-audit.test.ts | 10 +- packages/editor/tests/shadow-mount.test.ts | 173 ++++++++++++++++++ packages/editor/vite.cdn.config.ts | 4 + packages/editor/vite.config.ts | 4 + packages/editor/vitest.config.ts | 11 +- 16 files changed, 509 insertions(+), 5 deletions(-) create mode 100644 packages/editor/scripts/inline-style-css-plugin.ts create mode 100644 packages/editor/src/composables/useEditorRoot.ts create mode 100644 packages/editor/src/virtual-modules.d.ts create mode 100644 packages/editor/tests/shadow-mount.test.ts diff --git a/apps/playground/vite.config.ts b/apps/playground/vite.config.ts index 6c61ec3..ac196f4 100644 --- a/apps/playground/vite.config.ts +++ b/apps/playground/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import tailwindcss from '@tailwindcss/vite'; import { resolve } from 'node:path'; +import { inlineStyleCssPlugin } from '../../packages/editor/scripts/inline-style-css-plugin'; const packagesDir = resolve(import.meta.dirname, '../../packages'); @@ -16,6 +17,11 @@ export default defineConfig({ }, }), tailwindcss(), + // Editor consumes `virtual:editor-css`; playground resolves workspace + // editor source directly so it needs the plugin too. + inlineStyleCssPlugin({ + fallbackSourcePath: resolve(packagesDir, 'editor/src/styles/index.css'), + }), ], resolve: { // Deduplicate vue and @vue/reactivity so they share one reactive system. diff --git a/packages/editor/scripts/inline-style-css-plugin.ts b/packages/editor/scripts/inline-style-css-plugin.ts new file mode 100644 index 0000000..7ed4775 --- /dev/null +++ b/packages/editor/scripts/inline-style-css-plugin.ts @@ -0,0 +1,111 @@ +import { readFileSync } from 'node:fs' +import type { Plugin } from 'vite' + +/** + * Inlines the editor's *fully-bundled* library CSS as a JS string export so + * shadow-DOM mounts can adopt it via `adoptedStyleSheets`. + * + * Source modules import a virtual ID: + * + * import editorStyles from 'virtual:editor-css' + * + * The plugin's `load()` initially emits a placeholder string. After + * Vite/Rolldown produces the combined CSS asset (Tailwind utilities + every + * `.vue` SFC ` diff --git a/apps/playground/src/main.ts b/apps/playground/src/main.ts index d1b930c..0fbf1c5 100644 --- a/apps/playground/src/main.ts +++ b/apps/playground/src/main.ts @@ -30,6 +30,8 @@ import "./style.css"; // Lazy-load Cloud page — only fetched when user navigates to #cloud const Cloud = defineAsyncComponent(() => import("./Cloud.vue")); +// Lazy-load multi-instance shadow-DOM playground — only used by Phase 6.3 e2e. +const MultiInstance = defineAsyncComponent(() => import("./MultiInstance.vue")); const pages: Record< string, @@ -37,8 +39,15 @@ const pages: Record< > = { "": App, "#cloud": Cloud, + "#multi": MultiInstance, }; +function pageKeyFor(component: unknown): string { + if (component === Cloud) return "cloud"; + if (component === MultiInstance) return "multi"; + return "oss"; +} + const currentPage = shallowRef(pages[window.location.hash] ?? App); useEventListener(window, "hashchange", () => { @@ -52,7 +61,7 @@ const app = createApp({ return () => h(Transition, { name: "pg-screen", mode: "out-in" }, () => h(currentPage.value, { - key: currentPage.value === Cloud ? "cloud" : "oss", + key: pageKeyFor(currentPage.value), }), ); }, diff --git a/packages/editor/scripts/inline-style-css-plugin.ts b/packages/editor/scripts/inline-style-css-plugin.ts index 155149c..4f344c8 100644 --- a/packages/editor/scripts/inline-style-css-plugin.ts +++ b/packages/editor/scripts/inline-style-css-plugin.ts @@ -103,21 +103,73 @@ export function inlineStyleCssPlugin(opts: { } const cssContent = cssParts.join('\n\n') - // The placeholder ships as a JSON-encoded string literal in the chunk. - const placeholderQuoted = JSON.stringify(PLACEHOLDER) - const cssQuoted = JSON.stringify(cssContent) + const cssDoubleQuoted = JSON.stringify(cssContent) + // Backticks let us avoid escaping every embedded `${...}` and `\` + // for template-literal output. Replicate the minifier's expected + // form so the chunk lands a valid string literal either way. + const cssBacktickQuoted = + '`' + + cssContent + .replace(/\\/g, '\\\\') + .replace(/`/g, '\\`') + .replace(/\$\{/g, '\\${') + + '`' - let replaced = 0 + // The plugin emits the placeholder via `JSON.stringify(PLACEHOLDER)` + // (double-quoted), but library/app bundlers downstream may re-emit + // it as a single-quoted string OR a template literal. Rolldown app + // builds in particular promote long single-line strings to + // template literals during minification, which the original + // double-quote-only replacement missed — shipping the literal + // `__TPL_INLINE_EDITOR_CSS__` token into the runtime and giving + // shadow-root mounts an adopted stylesheet of garbage. + // + // Match all three string-literal forms and substitute with the + // quote variant the chunk already uses, so we preserve whatever + // escaping convention the downstream bundler picked. + const variants: Array<{ from: string; to: string }> = [ + { from: `"${PLACEHOLDER}"`, to: cssDoubleQuoted }, + { from: `'${PLACEHOLDER}'`, to: cssDoubleQuoted }, + { from: '`' + PLACEHOLDER + '`', to: cssBacktickQuoted }, + ] + + let chunksTouched = 0 for (const file of Object.values(bundle)) { if (file.type !== 'chunk') continue - if (!file.code.includes(placeholderQuoted)) continue - file.code = file.code.split(placeholderQuoted).join(cssQuoted) - replaced++ + let touched = false + for (const { from, to } of variants) { + if (!file.code.includes(from)) continue + file.code = file.code.split(from).join(to) + touched = true + } + if (touched) chunksTouched++ } this.info?.( - `inline-style-css: injected ${(cssContent.length / 1024).toFixed(1)} kB raw CSS into ${replaced} chunk(s)`, + `inline-style-css: injected ${(cssContent.length / 1024).toFixed(1)} kB raw CSS into ${chunksTouched} chunk(s)`, ) + + // Self-check: if the placeholder still appears in any chunk after the + // variant-aware replacement above, a downstream bundler re-emitted it + // in a form we don't recognize. Fail the build so the regression + // surfaces immediately instead of shipping a shadow root whose + // adopted stylesheet content is the literal placeholder token. + // Real-world precedent: Rolldown app-mode minification promoted long + // single-line strings to backtick template literals, slipping past + // the original double-quote-only replacement and shipping broken + // styling to consumers using `shadowDom: true`. + for (const file of Object.values(bundle)) { + if (file.type !== 'chunk') continue + if (!file.code.includes(PLACEHOLDER)) continue + const surrounding = file.code.match( + new RegExp(`.{0,40}${PLACEHOLDER}.{0,40}`), + ) + this.error?.( + `inline-style-css: placeholder \`${PLACEHOLDER}\` still present in chunk ${file.fileName} after replacement. ` + + `A downstream bundler likely re-emitted the placeholder string in a quote form not handled by the plugin. ` + + `Surrounding context: ${surrounding?.[0] ?? ''}`, + ) + } }, } } diff --git a/packages/editor/src/composables/useEditorCore.ts b/packages/editor/src/composables/useEditorCore.ts index 6d826c0..9ca4517 100644 --- a/packages/editor/src/composables/useEditorCore.ts +++ b/packages/editor/src/composables/useEditorCore.ts @@ -331,13 +331,16 @@ export function useEditorCore( }); } - // Attach to the editor's effective DOM root rather than the global - // `document`. In shadow mode this scopes shortcuts to keydowns originating - // inside the editor's shadow tree — without this, Cmd+S typed while the - // host page has focus would fire the editor's onSave. In light-DOM mode - // the root IS the document, so behavior is unchanged. - const keydownTarget = options.editorRoot ?? document; - useEventListener(keydownTarget, "keydown", handleKeyboard); + // Attach the global keydown listener at the `document` level even in + // shadow mode. Non-focusable elements (most blocks are bare `

`) + // don't capture focus on click, so after a user clicks a block their + // active element stays on `document.body` — a subsequent keystroke + // never reaches the shadow root, only `document`. Listening at + // `document` keeps Escape / Cmd+Z / Delete shortcuts working in both + // modes; multi-instance scoping (a future concern) will need to + // disambiguate per-editor at the handler level rather than via + // listener target. + useEventListener(document, "keydown", handleKeyboard); // --- Popover mount --- // Ref bound by the editor template to `
`. diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 952fd78..2466a0c 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -162,6 +162,86 @@ function getEditorStyleSheet(): CSSStyleSheet { interface MountTarget { target: Element; shadowRoot: ShadowRoot | null; + /** + * Disposer for any dev-mode side effects attached to the shadow root + * (currently: the document.head ` - - -
- -
-
+ + + + Templatical Editor + + + +
+ +
+
- - + await fetch("/api/templates", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ content, mjml }), + }); + }; + + ``` @@ -61,7 +78,7 @@ Ihr Backend erhält sowohl das JSON (speichern Sie es, damit Nutzer das Template ::: info Shadow DOM als Standard Der Editor mountet standardmäßig innerhalb eines Shadow DOM, sodass Host-Seiten-CSS nicht in Editor-Elemente durchschlagen kann. Verwenden Sie ein `
` — oder ein beliebiges [Shadow-Host-fähiges Element](/de/api/editor#anforderungen-an-das-container-element) — als Container; Elemente wie ``, `` oder `` können keinen Shadow Root aufnehmen. -Übergeben Sie `shadowDom: false`, um zu deaktivieren, falls Sie einen ungewöhnlichen Container benötigen, Editor-Interna über `document.querySelector` ansprechen oder Firefox <101 / Safari <16.4 unterstützen müssen. +Übergeben Sie `shadowDom: false`, um zu deaktivieren, falls Sie einen ungewöhnlichen Container benötigen, Editor-Interna über `document.querySelector` ansprechen oder Firefox <101 / Safari <16.4 unterstützen müssen. Siehe den [Shadow-DOM-Leitfaden](/de/guide/shadow-dom) für die vollständige Kompromissliste und Theming via `:host`. ::: ## Nächste Schritte diff --git a/apps/docs/de/guide/blocks.md b/apps/docs/de/guide/blocks.md index c7bd598..7b80cc7 100644 --- a/apps/docs/de/guide/blocks.md +++ b/apps/docs/de/guide/blocks.md @@ -13,110 +13,110 @@ Um Blöcke programmatisch zu erstellen, siehe [Programmatische Templates](/de/gu ## Den richtigen Block auswählen -| Bedarf | Block | Hinweise | -|------|-------|-------| -| Überschriften, Titel | [Title](#title) | Überschriften mit fester Größe (H1-H4) mit Formatierung auf Blockebene | -| Fließtext, Absätze | [Paragraph](#paragraph) | Rich Text mit Inline-Formatierung über TipTap | -| Fotos, Banner, Logos | [Image](#image) | Optionales Link-Wrapping, responsive Breite | -| Call-to-Action | [Button](#button) | Bulletproof-Schaltflächen, die in allen E-Mail-Clients funktionieren | -| Mehrspaltiges Layout | [Section](#section) | Der einzige Block, der andere Blöcke enthält | -| Visuelle Trennung | [Divider](#divider) | Horizontale Linie mit Stiloptionen | -| Vertikaler Abstand | [Spacer](#spacer) | Leerraum zwischen Blöcken | -| Social Links | [Social Icons](#social-icons) | 16 Plattformen, 5 Icon-Stile | -| Navigationslinks | [Menu](#menu) | Horizontale Linkliste mit Trennzeichen | -| Tabellarische Daten | [Table](#table) | Datentabelle mit optionaler Kopfzeilenformatierung | -| Video-Vorschau | [Video](#video) | Klickbares Thumbnail (E-Mail-Clients unterstützen keine eingebetteten Videos) | -| Rohes Markup | [HTML](#html) | Notausgang für benutzerdefinierten Code | -| Domänenspezifische Inhalte | [Custom](#custom) | Ihre eigenen Blocktypen mit Feldern und Liquid-Templates | +| Bedarf | Block | Hinweise | +| -------------------------- | ----------------------------- | ----------------------------------------------------------------------------- | +| Überschriften, Titel | [Title](#title) | Überschriften mit fester Größe (H1-H4) mit Formatierung auf Blockebene | +| Fließtext, Absätze | [Paragraph](#paragraph) | Rich Text mit Inline-Formatierung über TipTap | +| Fotos, Banner, Logos | [Image](#image) | Optionales Link-Wrapping, responsive Breite | +| Call-to-Action | [Button](#button) | Bulletproof-Schaltflächen, die in allen E-Mail-Clients funktionieren | +| Mehrspaltiges Layout | [Section](#section) | Der einzige Block, der andere Blöcke enthält | +| Visuelle Trennung | [Divider](#divider) | Horizontale Linie mit Stiloptionen | +| Vertikaler Abstand | [Spacer](#spacer) | Leerraum zwischen Blöcken | +| Social Links | [Social Icons](#social-icons) | 16 Plattformen, 5 Icon-Stile | +| Navigationslinks | [Menu](#menu) | Horizontale Linkliste mit Trennzeichen | +| Tabellarische Daten | [Table](#table) | Datentabelle mit optionaler Kopfzeilenformatierung | +| Video-Vorschau | [Video](#video) | Klickbares Thumbnail (E-Mail-Clients unterstützen keine eingebetteten Videos) | +| Rohes Markup | [HTML](#html) | Notausgang für benutzerdefinierten Code | +| Domänenspezifische Inhalte | [Custom](#custom) | Ihre eigenen Blocktypen mit Feldern und Liquid-Templates | ## Title Ein Überschriften-Block mit festen Größenstufen. Verwenden Sie Titel für Überschriften, Sektionsüberschriften und andere prominente Texte. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `content` | `string` | HTML-Inhalt | -| `level` | `1 \| 2 \| 3 \| 4` | Überschriftsebene (H1=36px, H2=28px, H3=22px, H4=18px) | -| `color` | `string` | Textfarbe | -| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| Eigenschaft | Typ | Beschreibung | +| ------------ | ------------------------------- | ------------------------------------------------------ | +| `content` | `string` | HTML-Inhalt | +| `level` | `1 \| 2 \| 3 \| 4` | Überschriftsebene (H1=36px, H2=28px, H3=22px, H4=18px) | +| `color` | `string` | Textfarbe | +| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | ## Paragraph Fließtext, der als HTML gerendert wird. Der Editor verwendet [Tiptap](https://tiptap.dev) für die Inline-Bearbeitung mit Formatierungssteuerungen (fett, kursiv, Links, Ausrichtung, Schriftgröße, Farbe usw.). Jede Formatierung wird inline angewendet -- es gibt keine Formatierungseigenschaften auf Blockebene. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `content` | `string` | HTML-Inhalt | +| Eigenschaft | Typ | Beschreibung | +| ----------- | -------- | ------------ | +| `content` | `string` | HTML-Inhalt | ## Image Zeigt ein Bild mit optionalem Link-Wrapping an. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `src` | `string` | Bild-URL | -| `alt` | `string` | Alternativtext | -| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `linkUrl` | `string` | Umschließt das Bild mit einem Link | -| `linkOpenInNewTab` | `boolean` | Verhalten des Linkziels | -| `placeholderUrl` | `string` | Platzhalter, der im Editor angezeigt wird, wenn `src` ein Merge-Tag verwendet | +| Eigenschaft | Typ | Beschreibung | +| ------------------ | ------------------------------- | ----------------------------------------------------------------------------- | +| `src` | `string` | Bild-URL | +| `alt` | `string` | Alternativtext | +| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `linkUrl` | `string` | Umschließt das Bild mit einem Link | +| `linkOpenInNewTab` | `boolean` | Verhalten des Linkziels | +| `placeholderUrl` | `string` | Platzhalter, der im Editor angezeigt wird, wenn `src` ein Merge-Tag verwendet | ## Button Eine Call-to-Action-Schaltfläche mit anpassbarem Erscheinungsbild. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `text` | `string` | Schaltflächentext | -| `url` | `string` | Link-URL | -| `backgroundColor` | `string` | Hintergrundfarbe der Schaltfläche | -| `textColor` | `string` | Textfarbe der Schaltfläche | -| `borderRadius` | `number` | Eckenradius in px | -| `fontSize` | `number` | Schriftgröße in px | -| `buttonPadding` | `SpacingValue` | Innerer Abstand | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | -| `openInNewTab` | `boolean` | Verhalten des Linkziels | +| Eigenschaft | Typ | Beschreibung | +| ----------------- | -------------- | --------------------------------- | +| `text` | `string` | Schaltflächentext | +| `url` | `string` | Link-URL | +| `backgroundColor` | `string` | Hintergrundfarbe der Schaltfläche | +| `textColor` | `string` | Textfarbe der Schaltfläche | +| `borderRadius` | `number` | Eckenradius in px | +| `fontSize` | `number` | Schriftgröße in px | +| `buttonPadding` | `SpacingValue` | Innerer Abstand | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| `openInNewTab` | `boolean` | Verhalten des Linkziels | ## Divider Ein horizontaler Linientrenner. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Linienstil | -| `color` | `string` | Linienfarbe | -| `thickness` | `number` | Liniendicke in px | -| `width` | `number \| 'full'` | Linienbreite in px oder `'full'` für 100% | +| Eigenschaft | Typ | Beschreibung | +| ----------- | --------------------------------- | ----------------------------------------- | +| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Linienstil | +| `color` | `string` | Linienfarbe | +| `thickness` | `number` | Liniendicke in px | +| `width` | `number \| 'full'` | Linienbreite in px oder `'full'` für 100% | ## Spacer Leerer vertikaler Raum. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `height` | `number` | Höhe in px | +| Eigenschaft | Typ | Beschreibung | +| ----------- | -------- | ------------ | +| `height` | `number` | Höhe in px | ## HTML Fügt rohes HTML in das Template ein. Verwenden Sie dies für Inhalte, die mit anderen Blocktypen nicht ausgedrückt werden können. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `content` | `string` | Rohes HTML-Markup | +| Eigenschaft | Typ | Beschreibung | +| ----------- | -------- | ----------------- | +| `content` | `string` | Rohes HTML-Markup | ## Social Icons Eine Reihe von Social-Media-Icons, die zu Plattformprofilen verlinken. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `icons` | `SocialIcon[]` | Liste der Social Icons | -| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visueller Stil | -| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon-Größe | -| `spacing` | `number` | Abstand zwischen Icons in px | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| Eigenschaft | Typ | Beschreibung | +| ----------- | ------------------------------------------------------------ | ---------------------------- | +| `icons` | `SocialIcon[]` | Liste der Social Icons | +| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visueller Stil | +| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon-Größe | +| `spacing` | `number` | Abstand zwischen Icons in px | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | 16 Plattformen werden unterstützt: Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, Pinterest, E-Mail, WhatsApp, Telegram, Discord, Snapchat, Reddit, GitHub, Dribbble und Behance. @@ -134,17 +134,17 @@ interface SocialIcon { Ein horizontales Navigationsmenü mit Textlinks. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `items` | `MenuItemData[]` | Menüeinträge | -| `fontSize` | `number` | Schriftgröße in px | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | -| `color` | `string` | Textfarbe | -| `linkColor` | `string` (optional) | Linkfarbe | -| `textAlign` | `'left' \| 'center' \| 'right'` | Ausrichtung | -| `separator` | `string` | Zeichen zwischen Einträgen | -| `separatorColor` | `string` | Farbe des Trennzeichens | -| `spacing` | `number` | Abstand um das Trennzeichen | +| Eigenschaft | Typ | Beschreibung | +| ---------------- | ------------------------------- | --------------------------------- | +| `items` | `MenuItemData[]` | Menüeinträge | +| `fontSize` | `number` | Schriftgröße in px | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| `color` | `string` | Textfarbe | +| `linkColor` | `string` (optional) | Linkfarbe | +| `textAlign` | `'left' \| 'center' \| 'right'` | Ausrichtung | +| `separator` | `string` | Zeichen zwischen Einträgen | +| `separatorColor` | `string` | Farbe des Trennzeichens | +| `spacing` | `number` | Abstand um das Trennzeichen | Jedes `MenuItemData` hat: @@ -164,18 +164,18 @@ interface MenuItemData { Eine Datentabelle mit optionaler Formatierung der Kopfzeile. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `rows` | `TableRowData[]` | Tabellenzeilen | -| `hasHeaderRow` | `boolean` | Erste Zeile als Kopfzeile formatieren | -| `headerBackgroundColor` | `string` (optional) | Hintergrund der Kopfzeile | -| `borderColor` | `string` | Rahmenfarbe | -| `borderWidth` | `number` | Rahmenbreite in px | -| `cellPadding` | `number` | Zellenabstand in px | -| `fontSize` | `number` | Schriftgröße in px | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | -| `color` | `string` | Textfarbe | -| `textAlign` | `'left' \| 'center' \| 'right'` | Textausrichtung der Zelle | +| Eigenschaft | Typ | Beschreibung | +| ----------------------- | ------------------------------- | ------------------------------------- | +| `rows` | `TableRowData[]` | Tabellenzeilen | +| `hasHeaderRow` | `boolean` | Erste Zeile als Kopfzeile formatieren | +| `headerBackgroundColor` | `string` (optional) | Hintergrund der Kopfzeile | +| `borderColor` | `string` | Rahmenfarbe | +| `borderWidth` | `number` | Rahmenbreite in px | +| `cellPadding` | `number` | Zellenabstand in px | +| `fontSize` | `number` | Schriftgröße in px | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| `color` | `string` | Textfarbe | +| `textAlign` | `'left' \| 'center' \| 'right'` | Textausrichtung der Zelle | ## Video @@ -185,32 +185,32 @@ Zeigt ein Video-Thumbnail an, das zur Video-URL verlinkt. E-Mail-Clients unterstützen keine eingebettete Videowiedergabe. Der Renderer gibt ein klickbares Thumbnail-Bild aus, das zur Video-URL verlinkt. Stellen Sie immer eine gute `thumbnailUrl` bereit -- das ist das Einzige, was Empfänger in ihrem Posteingang sehen. ::: -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `url` | `string` | Video-URL (YouTube, Vimeo usw.) | -| `thumbnailUrl` | `string` | URL des Thumbnail-Bildes | -| `alt` | `string` | Alternativtext für das Thumbnail | -| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `openInNewTab` | `boolean` | Verhalten des Linkziels | -| `placeholderUrl` | `string` | Nur im Editor sichtbarer Platzhalter | +| Eigenschaft | Typ | Beschreibung | +| ---------------- | ------------------------------- | ------------------------------------------ | +| `url` | `string` | Video-URL (YouTube, Vimeo usw.) | +| `thumbnailUrl` | `string` | URL des Thumbnail-Bildes | +| `alt` | `string` | Alternativtext für das Thumbnail | +| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `openInNewTab` | `boolean` | Verhalten des Linkziels | +| `placeholderUrl` | `string` | Nur im Editor sichtbarer Platzhalter | ## Section Ein Layout-Container, der eine oder mehrere Spalten enthält. Siehe [Sektionen und Spalten](/de/guide/sections-and-columns) für alle Details. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `columns` | `ColumnLayout` | Preset für das Spaltenlayout | -| `children` | `Block[][]` | Array von Block-Arrays, eines pro Spalte | +| Eigenschaft | Typ | Beschreibung | +| ----------- | -------------- | ---------------------------------------- | +| `columns` | `ColumnLayout` | Preset für das Spaltenlayout | +| `children` | `Block[][]` | Array von Block-Arrays, eines pro Spalte | ## Custom Ein benutzerdefinierter Blocktyp, der durch Felddefinitionen und ein Liquid-Template angetrieben wird. Siehe [Benutzerdefinierte Blöcke](/de/guide/custom-blocks) für alle Details. -| Eigenschaft | Typ | Beschreibung | -|----------|------|-------------| -| `customType` | `string` | Eindeutige Kennung für den benutzerdefinierten Blocktyp | -| `fieldValues` | `Record` | Aktuelle Werte für definierte Felder | -| `renderedHtml` | `string` | Zwischengespeichertes gerendertes Ergebnis | -| `dataSourceFetched` | `boolean` | Ob die Datenquelle abgerufen wurde | +| Eigenschaft | Typ | Beschreibung | +| ------------------- | ------------------------- | ------------------------------------------------------- | +| `customType` | `string` | Eindeutige Kennung für den benutzerdefinierten Blocktyp | +| `fieldValues` | `Record` | Aktuelle Werte für definierte Felder | +| `renderedHtml` | `string` | Zwischengespeichertes gerendertes Ergebnis | +| `dataSourceFetched` | `boolean` | Ob die Datenquelle abgerufen wurde | diff --git a/apps/docs/de/guide/custom-blocks.md b/apps/docs/de/guide/custom-blocks.md index 90732ac..3ef3126 100644 --- a/apps/docs/de/guide/custom-blocks.md +++ b/apps/docs/de/guide/custom-blocks.md @@ -8,7 +8,7 @@ description: Definieren Sie Ihre eigenen Blocktypen mit benutzerdefinierten Feld Benutzerdefinierte Blöcke ermöglichen es Ihnen, Templatical um Ihre eigenen Blocktypen zu erweitern. Definieren Sie eine Reihe von Feldern, schreiben Sie ein Liquid-Template für das Rendering und verbinden Sie optional eine Datenquelle. Benutzer interagieren mit benutzerdefinierten Blöcken über die gleiche Drag-and-Drop-Oberfläche wie mit integrierten Blöcken. ::: warning Shadow DOM und Host-seitige Queries -Benutzerdefinierte Blöcke rendern standardmäßig innerhalb des Shadow DOM des Editors. Wenn Ihr benutzerdefinierter Block aus Host-Seiten-Code heraus erreichbar sein muss (z. B. um ein Drittanbieter-Widget per ID anzubinden), finden `document.querySelector`-Aufrufe in den Editor das Element nicht — durchlaufen Sie stattdessen den Shadow Root über `container.shadowRoot.querySelector(...)` oder deaktivieren Sie mit `shadowDom: false`. +Benutzerdefinierte Blöcke rendern standardmäßig innerhalb des Shadow DOM des Editors. Wenn Ihr benutzerdefinierter Block aus Host-Seiten-Code heraus erreichbar sein muss (z. B. um ein Drittanbieter-Widget per ID anzubinden), finden `document.querySelector`-Aufrufe in den Editor das Element nicht — durchlaufen Sie stattdessen den Shadow Root über `container.shadowRoot.querySelector(...)` oder deaktivieren Sie mit `shadowDom: false`. Siehe den [Shadow-DOM-Leitfaden](./shadow-dom) für die vollständige Host-Integrations-Geschichte. ::: ## Einen benutzerdefinierten Block definieren @@ -16,23 +16,36 @@ Benutzerdefinierte Blöcke rendern standardmäßig innerhalb des Shadow DOM des Übergeben Sie benutzerdefinierte Blockdefinitionen über die Editor-Konfiguration. Das folgende Beispiel erstellt einen "Testimonial"-Block mit einem Zitat, Autorendetails, Avatar und einer Sternebewertung. Nach der Registrierung können Benutzer ihn aus der Block-Palette in ihr Template ziehen und jedes Feld im Einstellungsbereich bearbeiten. ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", customBlocks: [ { - type: 'testimonial', - name: 'Testimonial', + type: "testimonial", + name: "Testimonial", icon: '', - description: 'Customer quote with photo and rating', + description: "Customer quote with photo and rating", fields: [ - { key: 'quote', label: 'Quote', type: 'textarea' }, - { key: 'authorName', label: 'Author Name', type: 'text' }, - { key: 'authorTitle', label: 'Author Title', type: 'text' }, - { key: 'avatar', label: 'Avatar', type: 'image' }, - { key: 'rating', label: 'Rating (1-5)', type: 'number', min: 1, max: 5, step: 1, default: 5 }, - { key: 'showRating', label: 'Show Rating', type: 'boolean', default: true }, + { key: "quote", label: "Quote", type: "textarea" }, + { key: "authorName", label: "Author Name", type: "text" }, + { key: "authorTitle", label: "Author Title", type: "text" }, + { key: "avatar", label: "Avatar", type: "image" }, + { + key: "rating", + label: "Rating (1-5)", + type: "number", + min: 1, + max: 5, + step: 1, + default: 5, + }, + { + key: "showRating", + label: "Show Rating", + type: "boolean", + default: true, + }, ], template: `
@@ -99,15 +112,15 @@ interface CustomBlockDefinition { } ``` -| Eigenschaft | Erforderlich | Beschreibung | -|----------|----------|-------------| -| `type` | Ja | Eindeutige Kennung (wird als `customType` in Block-Instanzen verwendet) | -| `name` | Ja | Anzeigename in der Block-Palette | -| `icon` | Nein | Inline-SVG-String, Bild-URL oder base64-Daten-URI für das Palettensymbol | -| `description` | Nein | Tooltip oder Untertitel in der Palette | -| `fields` | Ja | Array von Felddefinitionen | -| `template` | Ja | Liquid-Template-String für das Rendering | -| `dataSource` | Nein | Konfiguration für das Abrufen externer Daten | +| Eigenschaft | Erforderlich | Beschreibung | +| ------------- | ------------ | ------------------------------------------------------------------------ | +| `type` | Ja | Eindeutige Kennung (wird als `customType` in Block-Instanzen verwendet) | +| `name` | Ja | Anzeigename in der Block-Palette | +| `icon` | Nein | Inline-SVG-String, Bild-URL oder base64-Daten-URI für das Palettensymbol | +| `description` | Nein | Tooltip oder Untertitel in der Palette | +| `fields` | Ja | Array von Felddefinitionen | +| `template` | Ja | Liquid-Template-String für das Rendering | +| `dataSource` | Nein | Konfiguration für das Abrufen externer Daten | ## Feldtypen @@ -125,16 +138,16 @@ interface CustomBlockFieldBase { Alle Feldtypen erweitern diese Basis. Der `key` wird als Variablenname in Ihrem Liquid-Template verwendet. Zusätzliche Eigenschaften hängen vom Feld-`type` ab: -| Eigenschaft | Gilt für | Beschreibung | -|----------|------------|-------------| -| `required` | Alle | Feld als erforderlich markieren | -| `placeholder` | Alle | Platzhaltertext für die Eingabe | -| `readOnly` | Alle | Benutzerbearbeitung verhindern (nützlich bei Datenquellen) | -| `default` | Alle | Standardwert bei Erstellung des Blocks | -| `min`, `max`, `step` | `number` | Numerische Einschränkungen | -| `options` | `select` | Array von `{ label, value }`-Auswahlmöglichkeiten | -| `fields` | `repeatable` | Unterfelddefinitionen | -| `minItems`, `maxItems` | `repeatable` | Grenzen für die Anzahl der Einträge | +| Eigenschaft | Gilt für | Beschreibung | +| ---------------------- | ------------ | ---------------------------------------------------------- | +| `required` | Alle | Feld als erforderlich markieren | +| `placeholder` | Alle | Platzhaltertext für die Eingabe | +| `readOnly` | Alle | Benutzerbearbeitung verhindern (nützlich bei Datenquellen) | +| `default` | Alle | Standardwert bei Erstellung des Blocks | +| `min`, `max`, `step` | `number` | Numerische Einschränkungen | +| `options` | `select` | Array von `{ label, value }`-Auswahlmöglichkeiten | +| `fields` | `repeatable` | Unterfelddefinitionen | +| `minItems`, `maxItems` | `repeatable` | Grenzen für die Anzahl der Einträge | ### text @@ -308,7 +321,9 @@ Benutzerdefinierte Blöcke werden noch leistungsfähiger, wenn sie durch eine AP ```ts interface DataSourceConfig { label: string; - onFetch: (context: DataSourceFetchContext) => Promise | null>; + onFetch: ( + context: DataSourceFetchContext, + ) => Promise | null>; } interface DataSourceFetchContext { @@ -370,26 +385,36 @@ Ein Einladungsblock für eine Veranstaltung mit einem Zeitplan, der mit wiederho ```ts const eventCard: CustomBlockDefinition = { - type: 'event-card', - name: 'Event Card', + type: "event-card", + name: "Event Card", icon: '', - description: 'Event details with schedule and RSVP', + description: "Event details with schedule and RSVP", fields: [ - { key: 'eventName', label: 'Event Name', type: 'text', default: 'Untitled Event' }, - { key: 'date', label: 'Date', type: 'text', default: 'January 1, 2026' }, - { key: 'venue', label: 'Venue', type: 'text' }, - { key: 'venueAddress', label: 'Venue Address', type: 'text' }, - { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, - { key: 'rsvpUrl', label: 'RSVP URL', type: 'text' }, { - key: 'schedule', - label: 'Schedule', - type: 'repeatable', + key: "eventName", + label: "Event Name", + type: "text", + default: "Untitled Event", + }, + { key: "date", label: "Date", type: "text", default: "January 1, 2026" }, + { key: "venue", label: "Venue", type: "text" }, + { key: "venueAddress", label: "Venue Address", type: "text" }, + { + key: "accentColor", + label: "Accent Color", + type: "color", + default: "#4f46e5", + }, + { key: "rsvpUrl", label: "RSVP URL", type: "text" }, + { + key: "schedule", + label: "Schedule", + type: "repeatable", minItems: 1, maxItems: 10, fields: [ - { key: 'time', label: 'Time', type: 'text' }, - { key: 'session', label: 'Session', type: 'text' }, + { key: "time", label: "Time", type: "text" }, + { key: "session", label: "Session", type: "text" }, ], }, ], @@ -422,26 +447,39 @@ Ein Preisblock mit einer Funktionsliste und CTA-Schaltfläche: ```ts const pricingTier: CustomBlockDefinition = { - type: 'pricing-tier', - name: 'Pricing Tier', + type: "pricing-tier", + name: "Pricing Tier", icon: '', - description: 'Pricing card with features list', + description: "Pricing card with features list", fields: [ - { key: 'planName', label: 'Plan Name', type: 'text', default: 'Pro' }, - { key: 'price', label: 'Price', type: 'text', default: '$29/mo' }, - { key: 'highlighted', label: 'Highlighted', type: 'boolean', default: false }, - { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, - { key: 'ctaLabel', label: 'Button Label', type: 'text', default: 'Get Started' }, - { key: 'ctaUrl', label: 'Button URL', type: 'text' }, + { key: "planName", label: "Plan Name", type: "text", default: "Pro" }, + { key: "price", label: "Price", type: "text", default: "$29/mo" }, + { + key: "highlighted", + label: "Highlighted", + type: "boolean", + default: false, + }, { - key: 'features', - label: 'Features', - type: 'repeatable', + key: "accentColor", + label: "Accent Color", + type: "color", + default: "#4f46e5", + }, + { + key: "ctaLabel", + label: "Button Label", + type: "text", + default: "Get Started", + }, + { key: "ctaUrl", label: "Button URL", type: "text" }, + { + key: "features", + label: "Features", + type: "repeatable", minItems: 1, maxItems: 8, - fields: [ - { key: 'text', label: 'Feature', type: 'text' }, - ], + fields: [{ key: "text", label: "Feature", type: "text" }], }, ], template: ` diff --git a/apps/docs/de/guide/defaults.md b/apps/docs/de/guide/defaults.md index 80f103d..a57f4e8 100644 --- a/apps/docs/de/guide/defaults.md +++ b/apps/docs/de/guide/defaults.md @@ -12,29 +12,31 @@ Blockeigenschaften (Farben, Schriftgrößen, Padding, Platzhaltertexte usw.) sin Übergeben Sie ein `blockDefaults`-Objekt an `init()`. Jeder Schlüssel wird einem Blocktyp zugeordnet und akzeptiert eine partielle Überschreibung der Eigenschaften dieses Blocks: ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", blockDefaults: { - title: { color: '#000000' }, + title: { color: "#000000" }, paragraph: {}, button: { - backgroundColor: '#ff6600', - textColor: '#ffffff', + backgroundColor: "#ff6600", + textColor: "#ffffff", styles: { padding: { top: 20, bottom: 20 } }, }, - divider: { color: '#eeeeee', thickness: 2 }, - image: { alt: 'Brand image' }, + divider: { color: "#eeeeee", thickness: 2 }, + image: { alt: "Brand image" }, }, }); ``` Standardwerte werden angewendet bei: + - Ziehen eines Blocks aus der Seitenleiste - Programmatischem Aufruf von `createAndAddBlock()` Standardwerte werden **nicht** angewendet bei: + - Duplizieren eines vorhandenen Blocks (die Werte des Quellblocks bleiben erhalten) - Laden gespeicherter Inhalte aus der API @@ -57,29 +59,29 @@ Arrays werden **ersetzt**, nicht zusammengeführt. Beispielsweise ersetzt das Se ### Unterstützte Blocktypen -| Schlüssel | Blocktyp | -|-----|-----------| -| `title` | Title | -| `paragraph` | Paragraph | -| `image` | Image | -| `button` | Button | -| `divider` | Divider | -| `section` | Section | -| `video` | Video | -| `social` | Social Icons | -| `spacer` | Spacer | -| `html` | HTML | -| `menu` | Menu | -| `table` | Table | +| Schlüssel | Blocktyp | +| ----------- | ------------ | +| `title` | Title | +| `paragraph` | Paragraph | +| `image` | Image | +| `button` | Button | +| `divider` | Divider | +| `section` | Section | +| `video` | Video | +| `social` | Social Icons | +| `spacer` | Spacer | +| `html` | HTML | +| `menu` | Menu | +| `table` | Table | Benutzerdefinierte Blöcke sind von `blockDefaults` nicht betroffen. Sie verwenden ihre eigenen `default`-Werte, die in der `CustomBlockDefinition`-Feldkonfiguration definiert sind. ### TypeScript-Typ ```ts -import type { BlockDefaults } from '@templatical/editor'; +import type { BlockDefaults } from "@templatical/editor"; // oder -import type { BlockDefaults } from '@templatical/types'; +import type { BlockDefaults } from "@templatical/types"; ``` Jeder Blocktyp-Schlüssel akzeptiert `Partial>` — Sie können jede Eigenschaft außer `id` und `type` überschreiben, die immer automatisch generiert werden. Siehe [Blocktypen](/de/guide/blocks) für die vollständige Liste der verfügbaren Eigenschaften pro Block. @@ -90,12 +92,12 @@ Jeder Blocktyp-Schlüssel akzeptiert `Partial>` ```ts const editor = await init({ - container: '#editor', + container: "#editor", templateDefaults: { width: 640, - backgroundColor: '#f5f5f5', - fontFamily: 'Helvetica, sans-serif', - preheaderText: 'Check out our latest news', + backgroundColor: "#f5f5f5", + fontFamily: "Helvetica, sans-serif", + preheaderText: "Check out our latest news", }, }); ``` @@ -109,19 +111,19 @@ Mit anderen Worten, `templateDefaults` sind Fallbacks für fehlenden Inhalt, kei ### Verfügbare Einstellungen -| Eigenschaft | Standard | Beschreibung | -|----------|---------|-------------| -| `width` | `600` | Template-Breite in Pixeln | +| Eigenschaft | Standard | Beschreibung | +| ----------------- | --------- | ------------------------------ | +| `width` | `600` | Template-Breite in Pixeln | | `backgroundColor` | `#ffffff` | Hintergrundfarbe des Templates | -| `fontFamily` | `Arial` | Standard-Schriftfamilie | -| `preheaderText` | — | E-Mail-Preheader-Text | +| `fontFamily` | `Arial` | Standard-Schriftfamilie | +| `preheaderText` | — | E-Mail-Preheader-Text | ### TypeScript-Typ ```ts -import type { TemplateDefaults } from '@templatical/editor'; +import type { TemplateDefaults } from "@templatical/editor"; // oder -import type { TemplateDefaults } from '@templatical/types'; +import type { TemplateDefaults } from "@templatical/types"; ``` ## Integrierte Standardkonstanten @@ -135,7 +137,7 @@ import { TITLE_BLOCK_DEFAULTS, PARAGRAPH_BLOCK_DEFAULTS, BUTTON_BLOCK_DEFAULTS, -} from '@templatical/types'; +} from "@templatical/types"; // Die Standardwerte für einen einzelnen Blocktyp inspizieren console.log(TITLE_BLOCK_DEFAULTS); @@ -148,7 +150,7 @@ console.log(DEFAULT_TEMPLATE_DEFAULTS); // Ein benutzerdefiniertes Preset erstellen, indem die Standardwerte eines einzelnen Blocks erweitert werden const myButtonDefaults = { ...BUTTON_BLOCK_DEFAULTS, - backgroundColor: '#ff6600', + backgroundColor: "#ff6600", borderRadius: 8, }; ``` @@ -157,27 +159,27 @@ const myButtonDefaults = { **Pro-Block-Konstanten** — jede enthält die Standardwerte der Eigenschaften für diesen Blocktyp (ausgenommen `id`, `type` und `styles`): -| Konstante | Blocktyp | -|----------|-----------| -| `TITLE_BLOCK_DEFAULTS` | Title | -| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | -| `IMAGE_BLOCK_DEFAULTS` | Image | -| `BUTTON_BLOCK_DEFAULTS` | Button | -| `DIVIDER_BLOCK_DEFAULTS` | Divider | -| `SECTION_BLOCK_DEFAULTS` | Section | -| `VIDEO_BLOCK_DEFAULTS` | Video | +| Konstante | Blocktyp | +| ----------------------------- | ------------ | +| `TITLE_BLOCK_DEFAULTS` | Title | +| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | +| `IMAGE_BLOCK_DEFAULTS` | Image | +| `BUTTON_BLOCK_DEFAULTS` | Button | +| `DIVIDER_BLOCK_DEFAULTS` | Divider | +| `SECTION_BLOCK_DEFAULTS` | Section | +| `VIDEO_BLOCK_DEFAULTS` | Video | | `SOCIAL_ICONS_BLOCK_DEFAULTS` | Social Icons | -| `SPACER_BLOCK_DEFAULTS` | Spacer | -| `HTML_BLOCK_DEFAULTS` | HTML | -| `MENU_BLOCK_DEFAULTS` | Menu | -| `TABLE_BLOCK_DEFAULTS` | Table | +| `SPACER_BLOCK_DEFAULTS` | Spacer | +| `HTML_BLOCK_DEFAULTS` | HTML | +| `MENU_BLOCK_DEFAULTS` | Menu | +| `TABLE_BLOCK_DEFAULTS` | Table | **Kombinierte Konstanten:** -| Konstante | Beschreibung | -|----------|-------------| -| `DEFAULT_BLOCK_DEFAULTS` | Alle Blocktyp-Standardwerte in einem einzigen Objekt (Schlüssel entsprechen der `BlockDefaults`-Schnittstelle) | -| `DEFAULT_TEMPLATE_DEFAULTS` | Standardwerte der Template-Einstellungen (`width`, `backgroundColor`, `fontFamily`) | +| Konstante | Beschreibung | +| --------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `DEFAULT_BLOCK_DEFAULTS` | Alle Blocktyp-Standardwerte in einem einzigen Objekt (Schlüssel entsprechen der `BlockDefaults`-Schnittstelle) | +| `DEFAULT_TEMPLATE_DEFAULTS` | Standardwerte der Template-Einstellungen (`width`, `backgroundColor`, `fontFamily`) | Diese Konstanten sind die einzige Quelle der Wahrheit, die intern von den Factory-Funktionen verwendet wird. Wenn Sie nur einige Eigenschaften überschreiben müssen, brauchen Sie sie nicht zu referenzieren — übergeben Sie einfach Ihre Überschreibungen an `blockDefaults` und sie werden mit diesen Werten deep-merged. @@ -186,23 +188,28 @@ Diese Konstanten sind die einzige Quelle der Wahrheit, die intern von den Factor Die zugrunde liegenden Factory-Funktionen akzeptieren Standardwerte auch direkt: ```ts -import { createBlock, createTitleBlock, createParagraphBlock, createDefaultTemplateContent } from '@templatical/types'; -import type { BlockDefaults } from '@templatical/types'; +import { + createBlock, + createTitleBlock, + createParagraphBlock, + createDefaultTemplateContent, +} from "@templatical/types"; +import type { BlockDefaults } from "@templatical/types"; // Einzelner Block mit partiellen Überschreibungen -const title = createTitleBlock({ level: 2, color: '#000' }); -const paragraph = createParagraphBlock({ content: '

Hello

' }); +const title = createTitleBlock({ level: 2, color: "#000" }); +const paragraph = createParagraphBlock({ content: "

Hello

" }); // Über createBlock mit vollständiger Standardwert-Map const defaults: BlockDefaults = { - title: { color: '#000000' }, - button: { backgroundColor: '#ff6600' }, + title: { color: "#000000" }, + button: { backgroundColor: "#ff6600" }, }; -const block = createBlock('title', defaults); +const block = createBlock("title", defaults); // Template mit benutzerdefinierten Einstellungen -const content = createDefaultTemplateContent('Helvetica, sans-serif', { +const content = createDefaultTemplateContent("Helvetica, sans-serif", { width: 640, - backgroundColor: '#f5f5f5', + backgroundColor: "#f5f5f5", }); ``` diff --git a/apps/docs/de/guide/display-conditions.md b/apps/docs/de/guide/display-conditions.md index 96c9d87..3259b93 100644 --- a/apps/docs/de/guide/display-conditions.md +++ b/apps/docs/de/guide/display-conditions.md @@ -12,23 +12,23 @@ Anzeigebedingungen erlauben es Benutzern, die Sichtbarkeit von Blöcken basieren Definieren Sie verfügbare Bedingungen über die Editor-Konfiguration: ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", displayConditions: { conditions: [ { - label: 'VIP Customers', - before: '{% if customer.vip == true %}', - after: '{% endif %}', - description: 'Only shown to VIP customers', + label: "VIP Customers", + before: "{% if customer.vip == true %}", + after: "{% endif %}", + description: "Only shown to VIP customers", }, { - label: 'Has Active Subscription', - before: '{% if subscription.active %}', - after: '{% endif %}', - description: 'Shown when user has an active subscription', + label: "Has Active Subscription", + before: "{% if subscription.active %}", + after: "{% endif %}", + description: "Shown when user has an active subscription", }, ], }, @@ -47,13 +47,13 @@ interface DisplayCondition { } ``` -| Eigenschaft | Erforderlich | Beschreibung | -|----------|----------|-------------| -| `label` | Ja | Anzeigename in der Editor-Oberfläche | -| `before` | Ja | Markup, das vor der Blockausgabe eingefügt wird | -| `after` | Ja | Markup, das nach der Blockausgabe eingefügt wird | -| `group` | Nein | Gruppenname zur Organisation von Bedingungen im Dropdown | -| `description` | Nein | Erklärender Text, der unter dem Label angezeigt wird | +| Eigenschaft | Erforderlich | Beschreibung | +| ------------- | ------------ | -------------------------------------------------------- | +| `label` | Ja | Anzeigename in der Editor-Oberfläche | +| `before` | Ja | Markup, das vor der Blockausgabe eingefügt wird | +| `after` | Ja | Markup, das nach der Blockausgabe eingefügt wird | +| `group` | Nein | Gruppenname zur Organisation von Bedingungen im Dropdown | +| `description` | Nein | Erklärender Text, der unter dem Label angezeigt wird | ## DisplayConditionsConfig diff --git a/apps/docs/de/guide/fonts.md b/apps/docs/de/guide/fonts.md index 76b5ecb..f362b91 100644 --- a/apps/docs/de/guide/fonts.md +++ b/apps/docs/de/guide/fonts.md @@ -10,46 +10,46 @@ Standardmäßig enthält der Editor eine Reihe gängiger websicherer Schriftarte Konfigurieren Sie, welche Schriftarten verfügbar sind, über die Option `fonts`: ```ts -import type { FontsConfig } from '@templatical/types'; +import type { FontsConfig } from "@templatical/types"; const fonts: FontsConfig = { - defaultFont: 'Inter', - defaultFallback: 'Arial, sans-serif', + defaultFont: "Inter", + defaultFallback: "Arial, sans-serif", customFonts: [ { - name: 'Inter', - url: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap', - fallback: 'Helvetica, Arial, sans-serif', + name: "Inter", + url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap", + fallback: "Helvetica, Arial, sans-serif", }, { - name: 'Merriweather', - url: 'https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap', - fallback: 'Georgia, serif', + name: "Merriweather", + url: "https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap", + fallback: "Georgia, serif", }, ], }; const editor = await init({ - container: '#editor', + container: "#editor", fonts, }); ``` ## FontsConfig -| Eigenschaft | Typ | Beschreibung | -|---|---|---| -| `defaultFont` | `string` | Schriftartname, der in neuen Templates standardmäßig ausgewählt ist | -| `defaultFallback` | `string` | Fallback-Stack, der verwendet wird, wenn eine benutzerdefinierte Schriftart nicht verfügbar ist | -| `customFonts` | `CustomFont[]` | Liste benutzerdefinierter Schriftarten, die registriert werden sollen | +| Eigenschaft | Typ | Beschreibung | +| ----------------- | -------------- | ----------------------------------------------------------------------------------------------- | +| `defaultFont` | `string` | Schriftartname, der in neuen Templates standardmäßig ausgewählt ist | +| `defaultFallback` | `string` | Fallback-Stack, der verwendet wird, wenn eine benutzerdefinierte Schriftart nicht verfügbar ist | +| `customFonts` | `CustomFont[]` | Liste benutzerdefinierter Schriftarten, die registriert werden sollen | ## CustomFont -| Eigenschaft | Typ | Beschreibung | -|---|---|---| -| `name` | `string` | Anzeigename in der Schriftauswahl | -| `url` | `string` | URL zum Schriftart-CSS (z. B. Google-Fonts-Link) | -| `fallback` | `string` | Optionaler Fallback-Font-Stack für diese Schriftart | +| Eigenschaft | Typ | Beschreibung | +| ----------- | -------- | --------------------------------------------------- | +| `name` | `string` | Anzeigename in der Schriftauswahl | +| `url` | `string` | URL zum Schriftart-CSS (z. B. Google-Fonts-Link) | +| `fallback` | `string` | Optionaler Fallback-Font-Stack für diese Schriftart | Benutzerdefinierte Schriftarten werden automatisch als ``-Deklarationen in der gerenderten MJML-Ausgabe eingefügt. diff --git a/apps/docs/de/guide/i18n.md b/apps/docs/de/guide/i18n.md index c0be1a9..5107170 100644 --- a/apps/docs/de/guide/i18n.md +++ b/apps/docs/de/guide/i18n.md @@ -12,32 +12,32 @@ Die Editor-Oberfläche unterstützt den Wechsel der Locale. Alle Labels, Tooltip Übergeben Sie die Option `locale` an `init()`: ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', - locale: 'de', + container: "#editor", + locale: "de", }); ``` ## Integrierte Locales -| Code | Sprache | -|---|---| +| Code | Sprache | +| ---- | ------------------- | | `en` | Englisch (Standard) | -| `de` | Deutsch | +| `de` | Deutsch | ## Locale-Auflösung Der Editor normalisiert Locale-Codes, indem er Regionssuffixe entfernt: -| Eingabe | Aufgelöst | -|---|---| -| `'en'` | `en` | -| `'en-US'` | `en` | -| `'en-GB'` | `en` | -| `'de-AT'` | `de` | -| `'fr'` | `en` (nicht unterstützt, fällt auf Englisch zurück) | +| Eingabe | Aufgelöst | +| --------- | --------------------------------------------------- | +| `'en'` | `en` | +| `'en-US'` | `en` | +| `'en-GB'` | `en` | +| `'de-AT'` | `de` | +| `'fr'` | `en` (nicht unterstützt, fällt auf Englisch zurück) | Wenn die aufgelöste Locale nicht unterstützt wird, fällt der Editor stillschweigend auf Englisch zurück. @@ -49,7 +49,7 @@ Locale-Dateien werden asynchron mit dynamischem `import()` geladen. Nur die akti async function switchLocale(newLocale: string) { editor.unmount(); editor = await init({ - container: '#editor', + container: "#editor", locale: newLocale, }); } @@ -111,17 +111,17 @@ Beispielstruktur für eine neue Locale: // packages/editor/src/i18n/locales/fr.ts export default { blocks: { - paragraph: 'Paragraphe', - image: 'Image', - button: 'Bouton', - section: 'Section', - divider: 'Séparateur', - spacer: 'Espacement', + paragraph: "Paragraphe", + image: "Image", + button: "Bouton", + section: "Section", + divider: "Séparateur", + spacer: "Espacement", // ... all keys from en.ts }, toolbar: { - duplicate: 'Dupliquer', - delete: 'Supprimer', + duplicate: "Dupliquer", + delete: "Supprimer", // ... }, // ... all sections from en.ts diff --git a/apps/docs/de/guide/images.md b/apps/docs/de/guide/images.md index 0f45bdc..649c458 100644 --- a/apps/docs/de/guide/images.md +++ b/apps/docs/de/guide/images.md @@ -18,10 +18,10 @@ Wenn der Callback `onRequestMedia` bereitgestellt wird, erscheint eine Durchsuch Der Editor ruft diese Funktion auf, wenn der Benutzer auf die Schaltfläche klickt. Geben Sie ein `MediaResult`-Objekt zurück oder `null`, wenn der Benutzer abbricht. Wenn `alt` angegeben ist, füllt der Editor automatisch den Alternativtext des Bildes aus. ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", async onRequestMedia() { // Öffnen Sie Ihr eigenes Modal, Ihren Datei-Browser oder Asset-Manager const image = await openMyMediaModal(); @@ -43,20 +43,19 @@ interface MediaResult { onRequestMedia?: (context?: MediaRequestContext) => Promise; ``` - ## Eigenschaften des Bildblocks Der Typ `ImageBlock` definiert alle konfigurierbaren Eigenschaften: -| Eigenschaft | Typ | Beschreibung | -|---|---|---| -| `src` | `string` | Quell-URL des Bildes | -| `alt` | `string` | Alternativtext für Barrierefreiheit | -| `width` | `number \| 'full'` | Bildbreite in Pixeln oder `'full'` für 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `linkUrl` | `string` (optional) | Umschließt das Bild mit einem Link | -| `linkOpenInNewTab` | `boolean` (optional) | Öffnet den Link in einem neuen Tab | -| `placeholderUrl` | `string` (optional) | Vorschaubild zur Entwurfszeit, wenn `src` ein Merge-Tag verwendet | +| Eigenschaft | Typ | Beschreibung | +| ------------------ | ------------------------------- | ----------------------------------------------------------------- | +| `src` | `string` | Quell-URL des Bildes | +| `alt` | `string` | Alternativtext für Barrierefreiheit | +| `width` | `number \| 'full'` | Bildbreite in Pixeln oder `'full'` für 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `linkUrl` | `string` (optional) | Umschließt das Bild mit einem Link | +| `linkOpenInNewTab` | `boolean` (optional) | Öffnet den Link in einem neuen Tab | +| `placeholderUrl` | `string` (optional) | Vorschaubild zur Entwurfszeit, wenn `src` ein Merge-Tag verwendet | ### Placeholder-URL diff --git a/apps/docs/de/guide/merge-tags.md b/apps/docs/de/guide/merge-tags.md index 5c48b5a..ee74e16 100644 --- a/apps/docs/de/guide/merge-tags.md +++ b/apps/docs/de/guide/merge-tags.md @@ -20,17 +20,17 @@ Beim Hovern über ein Tag wird der Rohwert hinter dem Label angezeigt. Die Eigenschaft `syntax` ist optional und standardmäßig `'liquid'`. ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", mergeTags: { tags: [ - { label: 'First Name', value: '{{first_name}}' }, - { label: 'Last Name', value: '{{last_name}}' }, - { label: 'Email', value: '{{email}}' }, - { label: 'Company', value: '{{company.name}}' }, - { label: 'Unsubscribe URL', value: '{{unsubscribe_url}}' }, + { label: "First Name", value: "{{first_name}}" }, + { label: "Last Name", value: "{{last_name}}" }, + { label: "Email", value: "{{email}}" }, + { label: "Company", value: "{{company.name}}" }, + { label: "Unsubscribe URL", value: "{{unsubscribe_url}}" }, ], }, }); @@ -56,15 +56,16 @@ Der `value` muss die Syntax-Trennzeichen enthalten. Zum Beispiel mit Liquid-Synt Templatical enthält vier integrierte Syntax-Presets. Die Einstellung `syntax` teilt dem Editor mit, wie sowohl Daten-Tags als auch Logik-Tags im Inhalt erkannt und hervorgehoben werden sollen. Jedes Preset definiert zwei Muster: + - **Daten-Tags** -- Variable Merge-Tags wie der Name oder die E-Mail eines Empfängers - **Logik-Tags** -- Kontrollflussanweisungen wie Bedingungen und Schleifen -| Preset | Daten-Tag | Logik-Tag | Plattform | -|--------|----------|-----------|----------| -| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | -| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | -| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | -| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | +| Preset | Daten-Tag | Logik-Tag | Plattform | +| -------------- | --------------------------------- | ------------------------------- | ------------------------------- | +| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | +| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | +| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | +| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | ```ts mergeTags: { @@ -90,34 +91,35 @@ Wie Daten-Tags werden Logik-Tags unverändert im gerenderten MJML durchgereicht Beispiele für Logik-Tags nach Preset: ::: code-group + ```html [Liquid] {% if customer.vip %} -

Exclusive offer just for you!

-{% endif %} - -{% for item in cart.items %} -

{{item.name}} - {{item.price}}

+

Exclusive offer just for you!

+{% endif %} {% for item in cart.items %} +

{{item.name}} - {{item.price}}

{% endfor %} ``` + ```html [Handlebars] {{#if hasSubscription}} -

Your plan renews on {{renewal_date}}

-{{/if}} - -{{#each products}} -

{{this.name}}

+

Your plan renews on {{renewal_date}}

+{{/if}} {{#each products}} +

{{this.name}}

{{/each}} ``` + ```html [Mailchimp] *|IF:VIP|* -

VIP discount applied

+

VIP discount applied

*|END:IF|* ``` + ```html [AMPscript] %%[IF @subscriber_type == "premium"]%% -

Premium content here

+

Premium content here

%%[ENDIF]%% ``` + ::: ## Benutzerdefinierte Syntax @@ -126,8 +128,8 @@ Wenn die integrierten Presets nicht zu Ihrer Plattform passen, definieren Sie ei ```ts interface SyntaxPreset { - value: RegExp; // matches data tags like ${user.name} - logic: RegExp; // matches logic tags like $[IF ...] + value: RegExp; // matches data tags like ${user.name} + logic: RegExp; // matches logic tags like $[IF ...] } ``` @@ -163,12 +165,10 @@ Um sie explizit zu deaktivieren, setzen Sie `autocomplete: false`: ```ts const editor = await init({ - container: '#editor', + container: "#editor", mergeTags: { autocomplete: false, - tags: [ - { label: 'Vorname', value: '{{first_name}}' }, - ], + tags: [{ label: "Vorname", value: "{{first_name}}" }], }, }); ``` @@ -181,7 +181,7 @@ Für große oder kontextabhängige Tag-Listen verwenden Sie den `onRequest`-Call ```ts const editor = await init({ - container: '#editor', + container: "#editor", mergeTags: { onRequest: async () => { const tag = await showMyMergeTagPicker(); diff --git a/apps/docs/de/guide/migration-from-beefree.md b/apps/docs/de/guide/migration-from-beefree.md index 14bb4f6..2b3f87a 100644 --- a/apps/docs/de/guide/migration-from-beefree.md +++ b/apps/docs/de/guide/migration-from-beefree.md @@ -20,17 +20,19 @@ npm install @templatical/import-beefree ## Verwendung ```ts -import { convertBeeFreeTemplate } from '@templatical/import-beefree'; +import { convertBeeFreeTemplate } from "@templatical/import-beefree"; // Laden Sie Ihr BeeFree-Template-JSON -const beefreeJson = await fetch('/api/beefree-templates/123').then(r => r.json()); +const beefreeJson = await fetch("/api/beefree-templates/123").then((r) => + r.json(), +); // In das Templatical-Format konvertieren const { content, report } = convertBeeFreeTemplate(beefreeJson); // Im Editor verwenden const editor = await init({ - container: '#editor', + container: "#editor", content, }); @@ -39,6 +41,7 @@ console.log(report); ``` Die Funktion gibt ein `ImportResult` zurück mit: + - `content` — den konvertierten `TemplateContent`, bereit für den Editor - `report` — einen Konvertierungsbericht mit dem Status jedes Blocks (`converted`, `approximated`, `html-fallback` oder `skipped`) @@ -46,21 +49,21 @@ Die Funktion gibt ein `ImportResult` zurück mit: BeeFree-Blocktypen werden den Templatical-Äquivalenten zugeordnet: -| BeeFree-Modul | Templatical-Block | Status | -|---|---|---| -| Text | `paragraph` | Konvertiert | -| Paragraph | `paragraph` | Konvertiert | -| Heading | `title` | Konvertiert | -| List | `paragraph` | Konvertiert | -| Image | `image` | Konvertiert | -| Button | `button` | Konvertiert | -| Divider | `divider` | Konvertiert | -| Spacer | `spacer` | Konvertiert | -| Social | `social` | Konvertiert (16 Plattformen zugeordnet) | -| Html | `html` | Konvertiert | -| Menu | `menu` | Angenähert (Stile können abweichen) | -| Video | `video` | Konvertiert | -| Table | `table` | Konvertiert | +| BeeFree-Modul | Templatical-Block | Status | +| ------------- | ----------------- | --------------------------------------- | +| Text | `paragraph` | Konvertiert | +| Paragraph | `paragraph` | Konvertiert | +| Heading | `title` | Konvertiert | +| List | `paragraph` | Konvertiert | +| Image | `image` | Konvertiert | +| Button | `button` | Konvertiert | +| Divider | `divider` | Konvertiert | +| Spacer | `spacer` | Konvertiert | +| Social | `social` | Konvertiert (16 Plattformen zugeordnet) | +| Html | `html` | Konvertiert | +| Menu | `menu` | Angenähert (Stile können abweichen) | +| Video | `video` | Konvertiert | +| Table | `table` | Konvertiert | Unbekannte Modultypen werden als Fallback in HTML-Blöcke konvertiert. @@ -68,13 +71,13 @@ Unbekannte Modultypen werden als Fallback in HTML-Blöcke konvertiert. BeeFree organisiert Inhalte in Zeilen mit Spalten. Diese werden einem Templatical-`SectionBlock` mit dem entsprechenden `ColumnLayout` zugeordnet: -| BeeFree-Spalten | Templatical-Layout | -|---|---| -| 1 Spalte (100%) | `'1'` | -| 2 gleiche Spalten | `'2'` | -| 3 gleiche Spalten | `'3'` | -| 2 Spalten (~33/66) | `'1-2'` | -| 2 Spalten (~66/33) | `'2-1'` | +| BeeFree-Spalten | Templatical-Layout | +| ------------------ | ------------------ | +| 1 Spalte (100%) | `'1'` | +| 2 gleiche Spalten | `'2'` | +| 3 gleiche Spalten | `'3'` | +| 2 Spalten (~33/66) | `'1-2'` | +| 2 Spalten (~66/33) | `'2-1'` | Spaltenbreiten, die nicht einem Standardverhältnis entsprechen, werden dem am nächsten liegenden verfügbaren Layout zugeordnet. diff --git a/apps/docs/de/guide/migration-from-html.md b/apps/docs/de/guide/migration-from-html.md index 776d87c..d2c4843 100644 --- a/apps/docs/de/guide/migration-from-html.md +++ b/apps/docs/de/guide/migration-from-html.md @@ -20,17 +20,17 @@ npm install @templatical/import-html ## Verwendung ```ts -import { convertHtmlTemplate } from '@templatical/import-html'; +import { convertHtmlTemplate } from "@templatical/import-html"; // Den rohen HTML-Quelltext einer E-Mail laden -const html = await fetch('/path/to/email.html').then((r) => r.text()); +const html = await fetch("/path/to/email.html").then((r) => r.text()); // In das Templatical-Format konvertieren const { content, report } = convertHtmlTemplate(html); // Im Editor verwenden const editor = await init({ - container: '#editor', + container: "#editor", content, }); @@ -39,25 +39,26 @@ console.log(report); ``` Die Funktion gibt ein `ImportResult` zurück mit: + - `content` — das konvertierte `TemplateContent`, bereit für den Editor - `report` — ein Konvertierungsbericht mit dem Status jedes Elements (`converted`, `approximated`, `html-fallback` oder `skipped`) ## Element-Mapping -| HTML-Element | Templatical-Block | Status | -|---|---|---| -| `

` – `

` | `title` | Konvertiert (Level erhalten) | -| `

` – `
` | `title` | Konvertiert (auf Level 4 begrenzt) | -| `

` / Text-`

` mit explizit gesetzter Höhe | `spacer` | Konvertiert | -| `` mit einem einzigen gestylten `` | `button` | Konvertiert (Cell-as-Button-Muster) | -| `` (Layout, mehrere Zeilen/Spalten) | `section` (eine pro ``) | Konvertiert | -| `
` (Datentabelle — nur Text in Zellen) | `html` | HTML-Fallback | -| Unbekannte / Custom-Elemente | `html` | HTML-Fallback | +| HTML-Element | Templatical-Block | Status | +| --------------------------------------------------------------------------------------------- | --------------------------- | ---------------------------------------- | +| `

` – `

` | `title` | Konvertiert (Level erhalten) | +| `

` – `
` | `title` | Konvertiert (auf Level 4 begrenzt) | +| `

` / Text-`

` mit explizit gesetzter Höhe | `spacer` | Konvertiert | +| `` mit einem einzigen gestylten `` | `button` | Konvertiert (Cell-as-Button-Muster) | +| `` (Layout, mehrere Zeilen/Spalten) | `section` (eine pro ``) | Konvertiert | +| `
` (Datentabelle — nur Text in Zellen) | `html` | HTML-Fallback | +| Unbekannte / Custom-Elemente | `html` | HTML-Fallback | Alles, was sich nicht zuordnen lässt, wird wortgetreu in einem HTML-Block erhalten — sichtbarer Inhalt geht nicht verloren. @@ -65,12 +66,12 @@ Alles, was sich nicht zuordnen lässt, wird wortgetreu in einem HTML-Block erhal Jeder `` einer Layout-Tabelle wird zu einem `SectionBlock`: -| Zellen pro Zeile | Templatical-Layout | -|---|---| -| 1 | `'1'` | -| 2 | `'2'` | -| 3 | `'3'` | -| 4+ | auf `'1'` reduziert mit Warnung | +| Zellen pro Zeile | Templatical-Layout | +| ---------------- | ------------------------------- | +| 1 | `'1'` | +| 2 | `'2'` | +| 3 | `'3'` | +| 4+ | auf `'1'` reduziert mit Warnung | Templatical-Sections können nicht verschachtelt werden. Tabellen, die in einem ``- und ``- und `
` verschachtelt sind, werden flachgelegt — ihre Blöcke wandern in die übergeordnete Zelle. @@ -121,11 +122,8 @@ console.log(report.summary); // { total: 12, converted: 10, approximated: 1, htmlFallback: 1, skipped: 0 } for (const entry of report.entries) { - if (entry.status === 'html-fallback') { - console.warn( - `Element <${entry.sourceTag}> als HTML erhalten:`, - entry.note, - ); + if (entry.status === "html-fallback") { + console.warn(`Element <${entry.sourceTag}> als HTML erhalten:`, entry.note); } } diff --git a/apps/docs/de/guide/migration-from-mjml.md b/apps/docs/de/guide/migration-from-mjml.md index b6470bc..56f0474 100644 --- a/apps/docs/de/guide/migration-from-mjml.md +++ b/apps/docs/de/guide/migration-from-mjml.md @@ -15,7 +15,7 @@ MJML → Templatical ist schwerer vollständig zu automatisieren als BeeFree → ## Was hier eigentlich passiert -Diese Migration ist etwas kontraintuitiv. Templaticals Renderer erzeugt *MJML als Ausgabe* — auf den ersten Blick sehen MJML und Templatical identisch aus. Aber: +Diese Migration ist etwas kontraintuitiv. Templaticals Renderer erzeugt _MJML als Ausgabe_ — auf den ersten Blick sehen MJML und Templatical identisch aus. Aber: - **MJML** ist eine Markup-Sprache. Du schreibst XML-ähnliche Tags (``, ``, ``) und der MJML-Compiler verwandelt das in tabellenbasiertes HTML. - **Templatical** speichert Templates als JSON-Baum mit typisierten Blöcken (`SectionBlock`, `ParagraphBlock` usw.) und rendert diesen Baum beim Export zu MJML. @@ -40,7 +40,7 @@ Die meisten MJML-Templates sind in 10–20 Minuten umgezogen, sobald du eines od Sobald du ein Template visuell nachgebaut hast: ```ts -import { renderToMjml } from '@templatical/renderer'; +import { renderToMjml } from "@templatical/renderer"; const mjml = await renderToMjml(content); // Vergleiche dieses MJML mit deinem ursprünglichen MJML-Quelltext. @@ -55,19 +55,19 @@ Hast du Hunderte MJML-Templates und willst automatische Konvertierung versuchen, Hier die grobe Form: ```ts -import { parse } from 'node-html-parser'; +import { parse } from "node-html-parser"; import { createSectionBlock, createTitleBlock, createParagraphBlock, createImageBlock, createButtonBlock, -} from '@templatical/types'; -import type { TemplateContent, Block } from '@templatical/types'; +} from "@templatical/types"; +import type { TemplateContent, Block } from "@templatical/types"; function mjmlToTemplate(mjml: string): TemplateContent { const root = parse(mjml); - const body = root.querySelector('mj-body'); + const body = root.querySelector("mj-body"); const blocks: Block[] = (body?.childNodes ?? []) .map((node) => convertNode(node)) @@ -76,17 +76,17 @@ function mjmlToTemplate(mjml: string): TemplateContent { return { blocks, settings: { - width: parseInt(body?.getAttribute('width') ?? '600'), - backgroundColor: body?.getAttribute('background-color') ?? '#ffffff', + width: parseInt(body?.getAttribute("width") ?? "600"), + backgroundColor: body?.getAttribute("background-color") ?? "#ffffff", }, }; } function convertNode(node: any): Block | null { switch (node.tagName?.toLowerCase()) { - case 'mj-section': + case "mj-section": return convertSection(node); - case 'mj-text': + case "mj-text": return convertText(node); // …weitere Cases — siehe Mapping-Tabelle unten default: @@ -101,22 +101,22 @@ Ein selbst geschriebener Parser wird Edge Cases übersehen — verschachtelte `m ## MJML-Tag-Mapping {#mjml-tag-mapping} -| MJML-Tag | Templatical-Block | Hinweise | -|---|---|---| -| `mj-section` (mit `mj-column`s) | `SectionBlock` mit `columns` | Mehrspaltige Layouts funktionieren gleich; Spaltenbreiten kommen aus MJMLs `width`-Attribut oder werden gleichmäßig verteilt. | -| `mj-column` | Section-Spalte | Eine Spalte hält eine Liste verschachtelter Blöcke. | -| `mj-text` | `ParagraphBlock` (oder `TitleBlock` bei einer Überschrift) | Anhand inline-styled Heading-Level entscheiden, ob Title oder Paragraph. | -| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, Padding. | -| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, Schrift, Padding. | -| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, Padding. | -| `mj-spacer` | `SpacerBlock` | `height`. | -| `mj-social` (mit `mj-social-element`) | `SocialIconsBlock` | Jedes `mj-social-element` → ein `SocialIcon`-Eintrag. | -| `mj-navbar` (mit `mj-navbar-link`) | `MenuBlock` | Jeder Link → `MenuItemData`. | -| `mj-table` | `TableBlock` | `
`-Zeilen/Zellen auf Templaticals Tabellen-Daten abbilden. | -| `mj-raw` | `HtmlBlock` | HTML-Pass-Through. | -| `mj-wrapper` | `SectionBlock` (oft) | Ein Wrapper ohne Spalten wird zu einer Section mit einer Spalte. | -| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (Fallback) | Templatical hat noch keine direkten Entsprechungen — das gerenderte HTML in einen rohen HTML-Block übernehmen oder auf den Importer warten. | -| `mj-head`-Inhalte | Template-`settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` mappen auf `TemplateSettings.preheaderText`, eigene Schriften und Theme-Overrides. | +| MJML-Tag | Templatical-Block | Hinweise | +| ---------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `mj-section` (mit `mj-column`s) | `SectionBlock` mit `columns` | Mehrspaltige Layouts funktionieren gleich; Spaltenbreiten kommen aus MJMLs `width`-Attribut oder werden gleichmäßig verteilt. | +| `mj-column` | Section-Spalte | Eine Spalte hält eine Liste verschachtelter Blöcke. | +| `mj-text` | `ParagraphBlock` (oder `TitleBlock` bei einer Überschrift) | Anhand inline-styled Heading-Level entscheiden, ob Title oder Paragraph. | +| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, Padding. | +| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, Schrift, Padding. | +| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, Padding. | +| `mj-spacer` | `SpacerBlock` | `height`. | +| `mj-social` (mit `mj-social-element`) | `SocialIconsBlock` | Jedes `mj-social-element` → ein `SocialIcon`-Eintrag. | +| `mj-navbar` (mit `mj-navbar-link`) | `MenuBlock` | Jeder Link → `MenuItemData`. | +| `mj-table` | `TableBlock` | `
`-Zeilen/Zellen auf Templaticals Tabellen-Daten abbilden. | +| `mj-raw` | `HtmlBlock` | HTML-Pass-Through. | +| `mj-wrapper` | `SectionBlock` (oft) | Ein Wrapper ohne Spalten wird zu einer Section mit einer Spalte. | +| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (Fallback) | Templatical hat noch keine direkten Entsprechungen — das gerenderte HTML in einen rohen HTML-Block übernehmen oder auf den Importer warten. | +| `mj-head`-Inhalte | Template-`settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` mappen auf `TemplateSettings.preheaderText`, eigene Schriften und Theme-Overrides. | ## Was sich nicht automatisch übertragen lässt diff --git a/apps/docs/de/guide/migration-from-unlayer.md b/apps/docs/de/guide/migration-from-unlayer.md index 8fcc9ee..e63f6c9 100644 --- a/apps/docs/de/guide/migration-from-unlayer.md +++ b/apps/docs/de/guide/migration-from-unlayer.md @@ -34,15 +34,15 @@ Die meisten Templates sind in 5–15 Minuten umgezogen, sobald du das erste oder Hast du **Dutzende oder Hunderte Templates** und willst nicht auf den offiziellen Importer warten, kannst du mit der [Mapping-Tabelle](#unlayer-modul-mapping) ein einmaliges Skript schreiben. Die Form ist überschaubar: ```ts -import { writeFileSync } from 'node:fs'; +import { writeFileSync } from "node:fs"; import { createTitleBlock, createParagraphBlock, createImageBlock, createButtonBlock, createSectionBlock, -} from '@templatical/types'; -import type { TemplateContent, Block } from '@templatical/types'; +} from "@templatical/types"; +import type { TemplateContent, Block } from "@templatical/types"; interface UnlayerDesign { body: { @@ -57,8 +57,8 @@ function convertUnlayerDesign(design: UnlayerDesign): TemplateContent { return { blocks, settings: { - width: parseInt(design.body.values.contentWidth ?? '600'), - backgroundColor: design.body.values.backgroundColor ?? '#ffffff', + width: parseInt(design.body.values.contentWidth ?? "600"), + backgroundColor: design.body.values.backgroundColor ?? "#ffffff", // …weitere Settings je nach Bedarf }, }; @@ -67,7 +67,9 @@ function convertUnlayerDesign(design: UnlayerDesign): TemplateContent { function convertRow(row: UnlayerRow): Block { // Spalten/Zellen auf Templaticals SectionBlock-Children abbilden. // Modulebene siehe Mapping-Tabelle unten. - return createSectionBlock({ /* … */ }); + return createSectionBlock({ + /* … */ + }); } ``` @@ -81,21 +83,21 @@ Dein Konvertierungs-Skript wird Iteration brauchen. Lass es zuerst auf einer kle Eine Richtungs-Referenz, keine vollständige Spezifikation. Unlayer hat Tarif-gebundene Module und Custom Blocks ohne direkte Entsprechungen. -| Unlayer-Modul | Templatical-Block | Hinweise | -|---|---|---| -| `row` (mit `columns`) | `SectionBlock` (mit `columns`) | Section = Reihe + mehrspaltiges Layout. Templatical-Sections unterstützen 1–4 Spalten, die auf Mobil stapeln. | -| `column` | Section-Spalte | Eine Spalte innerhalb einer Section, hält eine Liste von Blöcken. | -| `heading` | `TitleBlock` | Heading-Level (h1–h6) mappen auf Templaticals `level`-Property. | -| `text` | `ParagraphBlock` | Inline-Rich-Text. Verwende TipTap-kompatibles HTML für Runs. | -| `image` | `ImageBlock` | `src`, `alt`, `href`, `width`. Bilder über deine Medienbibliothek neu hosten. | -| `button` | `ButtonBlock` | `text`, `href`, `backgroundColor`, `color`, Padding. | -| `divider` | `DividerBlock` | Farbe, Breite, Padding. | -| `social` | `SocialIconsBlock` | Jedes Icon → ein `SocialIcon`-Eintrag mit `platform` und `href`. | -| `menu` | `MenuBlock` | Menüeinträge → `MenuItemData`-Einträge. | -| `html` | `HtmlBlock` | Roher HTML-Pass-Through. | -| `video` | `VideoBlock` | `src`, `thumbnail`, `href`. | -| Spacer | `SpacerBlock` | Nur vertikaler Abstand. | -| Custom-Module / Tarif-gebundene Module | `HtmlBlock` (Fallback) | In einen rohen HTML-Block konvertieren oder als [Custom Block](/de/guide/custom-blocks) implementieren, wenn er wiederverwendbar ist. | +| Unlayer-Modul | Templatical-Block | Hinweise | +| -------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| `row` (mit `columns`) | `SectionBlock` (mit `columns`) | Section = Reihe + mehrspaltiges Layout. Templatical-Sections unterstützen 1–4 Spalten, die auf Mobil stapeln. | +| `column` | Section-Spalte | Eine Spalte innerhalb einer Section, hält eine Liste von Blöcken. | +| `heading` | `TitleBlock` | Heading-Level (h1–h6) mappen auf Templaticals `level`-Property. | +| `text` | `ParagraphBlock` | Inline-Rich-Text. Verwende TipTap-kompatibles HTML für Runs. | +| `image` | `ImageBlock` | `src`, `alt`, `href`, `width`. Bilder über deine Medienbibliothek neu hosten. | +| `button` | `ButtonBlock` | `text`, `href`, `backgroundColor`, `color`, Padding. | +| `divider` | `DividerBlock` | Farbe, Breite, Padding. | +| `social` | `SocialIconsBlock` | Jedes Icon → ein `SocialIcon`-Eintrag mit `platform` und `href`. | +| `menu` | `MenuBlock` | Menüeinträge → `MenuItemData`-Einträge. | +| `html` | `HtmlBlock` | Roher HTML-Pass-Through. | +| `video` | `VideoBlock` | `src`, `thumbnail`, `href`. | +| Spacer | `SpacerBlock` | Nur vertikaler Abstand. | +| Custom-Module / Tarif-gebundene Module | `HtmlBlock` (Fallback) | In einen rohen HTML-Block konvertieren oder als [Custom Block](/de/guide/custom-blocks) implementieren, wenn er wiederverwendbar ist. | ## Was sich nicht automatisch übertragen lässt diff --git a/apps/docs/de/guide/programmatic-templates.md b/apps/docs/de/guide/programmatic-templates.md index a1c0082..77a74ec 100644 --- a/apps/docs/de/guide/programmatic-templates.md +++ b/apps/docs/de/guide/programmatic-templates.md @@ -12,7 +12,7 @@ Templatical bietet Factory-Funktionen für jeden Blocktyp. Verwenden Sie sie, um ## Leeres Template ```ts -import { createDefaultTemplateContent } from '@templatical/types'; +import { createDefaultTemplateContent } from "@templatical/types"; const content = createDefaultTemplateContent(); // { blocks: [], settings: { width: 600, backgroundColor: '#ffffff', fontFamily: 'Arial' } } @@ -21,7 +21,7 @@ const content = createDefaultTemplateContent(); `createDefaultTemplateContent()` akzeptiert einen optionalen Schriftfamilien-String: ```ts -const content = createDefaultTemplateContent('Georgia, serif'); +const content = createDefaultTemplateContent("Georgia, serif"); ``` ## Ein Template aufbauen @@ -36,7 +36,7 @@ import { createImageBlock, createButtonBlock, createDividerBlock, -} from '@templatical/types'; +} from "@templatical/types"; const content = createDefaultTemplateContent(); @@ -46,19 +46,19 @@ content.blocks = [ level: 1, }), createImageBlock({ - src: 'https://example.com/hero.jpg', - alt: 'Welcome hero image', - width: 'full', + src: "https://example.com/hero.jpg", + alt: "Welcome hero image", + width: "full", }), createDividerBlock(), createParagraphBlock({ - content: '

Thanks for signing up. Here is what happens next.

', + content: "

Thanks for signing up. Here is what happens next.

", }), createButtonBlock({ - text: 'Get Started', - url: 'https://example.com/dashboard', - backgroundColor: '#1a73e8', - textColor: '#ffffff', + text: "Get Started", + url: "https://example.com/dashboard", + backgroundColor: "#1a73e8", + textColor: "#ffffff", borderRadius: 6, }), ]; @@ -70,56 +70,56 @@ content.blocks = [ ```ts createTitleBlock({ - content: '

Welcome, {{name}}!

', + content: "

Welcome, {{name}}!

", level: 1, - textAlign: 'center', -}) + textAlign: "center", +}); ``` ### Paragraph ```ts createParagraphBlock({ - content: '

Thanks for signing up. Here is what happens next.

', -}) + content: "

Thanks for signing up. Here is what happens next.

", +}); ``` ### Image ```ts createImageBlock({ - src: 'https://cdn.example.com/hero.png', - alt: 'Hero banner', + src: "https://cdn.example.com/hero.png", + alt: "Hero banner", width: 560, - linkUrl: 'https://example.com', -}) + linkUrl: "https://example.com", +}); ``` ### Button ```ts createButtonBlock({ - text: 'Get Started', - url: 'https://example.com/signup', - backgroundColor: '#6366f1', + text: "Get Started", + url: "https://example.com/signup", + backgroundColor: "#6366f1", borderRadius: 8, -}) +}); ``` ### Divider ```ts createDividerBlock({ - lineStyle: 'dashed', - color: '#e5e7eb', + lineStyle: "dashed", + color: "#e5e7eb", thickness: 2, -}) +}); ``` ### Spacer ```ts -createSpacerBlock({ height: 40 }) +createSpacerBlock({ height: 40 }); ``` ### HTML @@ -127,20 +127,24 @@ createSpacerBlock({ height: 40 }) ```ts createHtmlBlock({ content: '
Custom markup
', -}) +}); ``` ### Social Icons ```ts createSocialIconsBlock({ - iconStyle: 'circle', - iconSize: 'large', + iconStyle: "circle", + iconSize: "large", icons: [ - { id: crypto.randomUUID(), platform: 'twitter', url: 'https://x.com/acme' }, - { id: crypto.randomUUID(), platform: 'github', url: 'https://github.com/acme' }, + { id: crypto.randomUUID(), platform: "twitter", url: "https://x.com/acme" }, + { + id: crypto.randomUUID(), + platform: "github", + url: "https://github.com/acme", + }, ], -}) +}); ``` ### Menu @@ -148,12 +152,33 @@ createSocialIconsBlock({ ```ts createMenuBlock({ items: [ - { id: crypto.randomUUID(), text: 'Home', url: 'https://example.com', openInNewTab: false, bold: false, underline: false }, - { id: crypto.randomUUID(), text: 'Blog', url: 'https://example.com/blog', openInNewTab: false, bold: false, underline: false }, - { id: crypto.randomUUID(), text: 'Docs', url: 'https://docs.example.com', openInNewTab: true, bold: false, underline: false }, + { + id: crypto.randomUUID(), + text: "Home", + url: "https://example.com", + openInNewTab: false, + bold: false, + underline: false, + }, + { + id: crypto.randomUUID(), + text: "Blog", + url: "https://example.com/blog", + openInNewTab: false, + bold: false, + underline: false, + }, + { + id: crypto.randomUUID(), + text: "Docs", + url: "https://docs.example.com", + openInNewTab: true, + bold: false, + underline: false, + }, ], - separator: '-', -}) + separator: "-", +}); ``` ### Table @@ -162,33 +187,51 @@ createMenuBlock({ createTableBlock({ hasHeaderRow: true, rows: [ - { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Plan' }, { id: crypto.randomUUID(), content: 'Price' }] }, - { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Starter' }, { id: crypto.randomUUID(), content: '$9/mo' }] }, - { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Pro' }, { id: crypto.randomUUID(), content: '$29/mo' }] }, + { + id: crypto.randomUUID(), + cells: [ + { id: crypto.randomUUID(), content: "Plan" }, + { id: crypto.randomUUID(), content: "Price" }, + ], + }, + { + id: crypto.randomUUID(), + cells: [ + { id: crypto.randomUUID(), content: "Starter" }, + { id: crypto.randomUUID(), content: "$9/mo" }, + ], + }, + { + id: crypto.randomUUID(), + cells: [ + { id: crypto.randomUUID(), content: "Pro" }, + { id: crypto.randomUUID(), content: "$29/mo" }, + ], + }, ], -}) +}); ``` ### Video ```ts createVideoBlock({ - url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', - thumbnailUrl: 'https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg', - alt: 'Product demo video', -}) + url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + thumbnailUrl: "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", + alt: "Product demo video", +}); ``` ### Section ```ts createSectionBlock({ - columns: '2', + columns: "2", children: [ - [createParagraphBlock({ content: '

Left column

' })], - [createImageBlock({ src: 'https://cdn.example.com/photo.jpg' })], + [createParagraphBlock({ content: "

Left column

" })], + [createImageBlock({ src: "https://cdn.example.com/photo.jpg" })], ], -}) +}); ``` Die Eigenschaft `columns` akzeptiert: `'1'` (einspaltig), `'2'` (zwei gleiche), `'3'` (drei gleiche), `'2-1'` (zwei Drittel / ein Drittel), `'1-2'` (ein Drittel / zwei Drittel). Siehe [Sektionen und Spalten](/de/guide/sections-and-columns) für alle Details. @@ -204,9 +247,9 @@ Die Eigenschaft `columns` akzeptiert: `'1'` (einspaltig), `'2'` (zwei gleiche), Erstellen Sie einen beliebigen Block anhand einer Typzeichenkette: ```ts -import { createBlock } from '@templatical/types'; +import { createBlock } from "@templatical/types"; -const block = createBlock('title'); // TitleBlock with defaults +const block = createBlock("title"); // TitleBlock with defaults ``` ### Klonen @@ -214,7 +257,7 @@ const block = createBlock('title'); // TitleBlock with defaults Deep-cloning eines Blocks mit einer neuen ID: ```ts -import { cloneBlock } from '@templatical/types'; +import { cloneBlock } from "@templatical/types"; const copy = cloneBlock(existingBlock); // copy.id !== existingBlock.id @@ -225,7 +268,13 @@ const copy = cloneBlock(existingBlock); Einen `Block`-Union auf einen bestimmten Typ eingrenzen: ```ts -import { isTitle, isParagraph, isImage, isButton, isSection } from '@templatical/types'; +import { + isTitle, + isParagraph, + isImage, + isButton, + isSection, +} from "@templatical/types"; if (isTitle(block)) { console.log(block.level); // TypeScript knows this is TitleBlock @@ -250,17 +299,17 @@ Template-Einstellungen steuern die globalen Eigenschaften der E-Mail: const content = createDefaultTemplateContent(); content.settings.width = 640; -content.settings.backgroundColor = '#f5f5f5'; -content.settings.fontFamily = 'Helvetica, Arial, sans-serif'; -content.settings.preheaderText = 'Your weekly digest is here'; +content.settings.backgroundColor = "#f5f5f5"; +content.settings.fontFamily = "Helvetica, Arial, sans-serif"; +content.settings.preheaderText = "Your weekly digest is here"; ``` -| Einstellung | Typ | Beschreibung | -|---|---|---| -| `width` | `number` | E-Mail-Breite in Pixeln | -| `backgroundColor` | `string` | Äußere Hintergrundfarbe | -| `fontFamily` | `string` | Standard-Font-Stack | -| `preheaderText` | `string` | Vorschautext, der in der Posteingangsliste angezeigt wird | +| Einstellung | Typ | Beschreibung | +| ----------------- | -------- | --------------------------------------------------------- | +| `width` | `number` | E-Mail-Breite in Pixeln | +| `backgroundColor` | `string` | Äußere Hintergrundfarbe | +| `fontFamily` | `string` | Standard-Font-Stack | +| `preheaderText` | `string` | Vorschautext, der in der Posteingangsliste angezeigt wird | Für Standardwerte und wie Sie diese anpassen können, siehe [Block- & Template-Standardwerte](/de/guide/defaults). @@ -269,10 +318,10 @@ Für Standardwerte und wie Sie diese anpassen können, siehe [Block- & Template- Übergeben Sie zuvor gespeichertes JSON zurück an den Editor: ```ts -const saved = await fetch('/api/templates/123').then(r => r.json()); +const saved = await fetch("/api/templates/123").then((r) => r.json()); const editor = await init({ - container: '#editor', + container: "#editor", content: saved, }); ``` diff --git a/apps/docs/de/guide/sections-and-columns.md b/apps/docs/de/guide/sections-and-columns.md index f5e6f42..9343d9b 100644 --- a/apps/docs/de/guide/sections-and-columns.md +++ b/apps/docs/de/guide/sections-and-columns.md @@ -15,16 +15,16 @@ Bleiben Sie für die meisten E-Mails bei 1-2 Spalten. Dreispaltige Layouts werde Die Eigenschaft `columns` akzeptiert eines von fünf Layout-Presets: -| Wert | Beschreibung | Spaltenbreiten | -|-------|-------------|---------------| -| `'1'` | Einzelne Spalte | 100% | -| `'2'` | Zwei gleiche Spalten | 50% / 50% | -| `'3'` | Drei gleiche Spalten | 33% / 33% / 33% | -| `'2-1'` | Zwei Drittel / ein Drittel | 66% / 33% | -| `'1-2'` | Ein Drittel / zwei Drittel | 33% / 66% | +| Wert | Beschreibung | Spaltenbreiten | +| ------- | -------------------------- | --------------- | +| `'1'` | Einzelne Spalte | 100% | +| `'2'` | Zwei gleiche Spalten | 50% / 50% | +| `'3'` | Drei gleiche Spalten | 33% / 33% / 33% | +| `'2-1'` | Zwei Drittel / ein Drittel | 66% / 33% | +| `'1-2'` | Ein Drittel / zwei Drittel | 33% / 66% | ```ts -type ColumnLayout = '1' | '2' | '3' | '2-1' | '1-2'; +type ColumnLayout = "1" | "2" | "3" | "2-1" | "1-2"; ``` ## Sektionen erstellen @@ -37,19 +37,19 @@ import { createTitleBlock, createParagraphBlock, createImageBlock, -} from '@templatical/types'; +} from "@templatical/types"; // Leere zweispaltige Sektion -const section = createSectionBlock({ columns: '2' }); +const section = createSectionBlock({ columns: "2" }); // Sektion mit vorbefüllten Spalten const hero = createSectionBlock({ - columns: '1-2', + columns: "1-2", children: [ - [createImageBlock({ src: 'https://cdn.example.com/logo.png', width: 120 })], + [createImageBlock({ src: "https://cdn.example.com/logo.png", width: 120 })], [ - createTitleBlock({ content: '

Welcome

', level: 1 }), - createParagraphBlock({ content: '

Get started in minutes.

' }), + createTitleBlock({ content: "

Welcome

", level: 1 }), + createParagraphBlock({ content: "

Get started in minutes.

" }), ], ], }); @@ -62,15 +62,15 @@ const hero = createSectionBlock({ ```ts // Für ein '2'-Layout: section.children = [ - [blockA, blockB], // Linke Spalte - [blockC], // Rechte Spalte + [blockA, blockB], // Linke Spalte + [blockC], // Rechte Spalte ]; // Für ein '3'-Layout: section.children = [ - [blockA], // Links - [blockB], // Mitte - [blockC], // Rechts + [blockA], // Links + [blockB], // Mitte + [blockC], // Rechts ]; ``` @@ -84,8 +84,8 @@ Um einen Block programmatisch zu einer bestimmten Spalte hinzuzufügen: // Eine Schaltfläche zur zweiten Spalte hinzufügen (Index 1) section.children[1].push( createButtonBlock({ - text: 'Learn More', - url: 'https://example.com/docs', + text: "Learn More", + url: "https://example.com/docs", }), ); ``` @@ -102,7 +102,7 @@ Sie können die Eigenschaft `visibility` für einzelne Blöcke innerhalb von Spa ```ts const block = createParagraphBlock({ - content: '

Desktop only sidebar content

', + content: "

Desktop only sidebar content

", }); block.visibility = { @@ -119,10 +119,10 @@ Siehe [Styling](/de/guide/styling) für weitere Informationen zu responsiven Üb Sektionen unterstützen die gleichen `BlockStyles` wie andere Blöcke. Häufige Anwendungsfälle sind das Festlegen einer Hintergrundfarbe oder eines Paddings für die gesamte Zeile: ```ts -const section = createSectionBlock({ columns: '1' }); +const section = createSectionBlock({ columns: "1" }); section.styles = { - backgroundColor: '#f8fafc', + backgroundColor: "#f8fafc", padding: { top: 32, right: 24, bottom: 32, left: 24 }, margin: { top: 0, right: 0, bottom: 0, left: 0 }, }; diff --git a/apps/docs/de/guide/shadow-dom.md b/apps/docs/de/guide/shadow-dom.md new file mode 100644 index 0000000..e4766a9 --- /dev/null +++ b/apps/docs/de/guide/shadow-dom.md @@ -0,0 +1,197 @@ +--- +title: Shadow DOM +description: Wie Templatical den Editor mit Shadow DOM vom Host-Seiten-CSS isoliert und wann Sie sich abmelden sollten. +--- + +# Shadow DOM + +Templatical wird standardmäßig innerhalb eines [Shadow DOM](https://developer.mozilla.org/de/docs/Web/API/Web_components/Using_shadow_DOM) eingebunden. Die Shadow-Grenze isoliert die Chrome-, Canvas- und Rich-Text-Inhalte des Editors vom CSS Ihrer Host-Seite — selbst globale Resets wie `* { color: red !important }` können sie nicht überschreiben. + +Diese Seite ist die kanonische Referenz für das Isolationsmodell. Wenn Sie den Editor nur stylen möchten, springen Sie zum [Theming-Leitfaden](./theming). + +## So funktioniert es + +Wenn Sie `init()` oder `initCloud()` aufrufen und ein Container-Element übergeben, geht Templatical folgendermaßen vor: + +1. Ruft `container.attachShadow({ mode: 'open' })` auf, um einen Shadow Root auf Ihrem Element zu erstellen. +2. Hängt die Vue-App in den Shadow Root ein, statt die Kinder des Containers direkt zu ersetzen. +3. Übernimmt ein gemeinsames `CSSStyleSheet`, das jedes Tailwind-Utility, jeden SFC-` - - -
- -
-
+ + + + Templatical Editor + + + +
+ +
+
- - + await fetch("/api/templates", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ content, mjml }), + }); + }; + + ``` @@ -61,7 +78,7 @@ Your backend receives both the JSON (store it to let users edit later) and the M ::: info Shadow DOM by default The editor mounts inside a Shadow DOM, so host page CSS cannot cascade into editor elements. Use a `
` — or any [shadow-host-eligible element](/api/editor#container-element-requirements) — as the container; elements like ``, ``, or `` cannot host a shadow root. -Pass `shadowDom: false` to opt out if you need an unusual container, target editor internals from `document.querySelector`, or support Firefox <101 / Safari <16.4. +Pass `shadowDom: false` to opt out if you need an unusual container, target editor internals from `document.querySelector`, or support Firefox <101 / Safari <16.4. See the [Shadow DOM guide](/guide/shadow-dom) for the full trade-off list and theming via `:host`. ::: ## Next steps diff --git a/apps/docs/guide/blocks.md b/apps/docs/guide/blocks.md index a71e3a3..fe850f9 100644 --- a/apps/docs/guide/blocks.md +++ b/apps/docs/guide/blocks.md @@ -13,110 +13,110 @@ To create blocks programmatically, see [Programmatic Templates](/guide/programma ## Choosing the right block -| Need | Block | Notes | -|------|-------|-------| -| Headings, titles | [Title](#title) | Fixed-size headings (H1-H4) with block-level formatting | -| Body text, paragraphs | [Paragraph](#paragraph) | Rich text with inline formatting via TipTap | -| Photos, banners, logos | [Image](#image) | Optional link wrapping, responsive width | -| Call-to-action | [Button](#button) | Bulletproof buttons that work in all email clients | -| Multi-column layout | [Section](#section) | The only block that holds other blocks | -| Visual separation | [Divider](#divider) | Horizontal line with style options | -| Vertical spacing | [Spacer](#spacer) | Empty space between blocks | -| Social links | [Social Icons](#social-icons) | 16 platforms, 5 icon styles | -| Navigation links | [Menu](#menu) | Horizontal link list with separators | -| Tabular data | [Table](#table) | Data table with optional header styling | -| Video preview | [Video](#video) | Clickable thumbnail (email clients don't support embedded video) | -| Raw markup | [HTML](#html) | Escape hatch for custom code | -| Domain-specific content | [Custom](#custom) | Your own block types with fields and Liquid templates | +| Need | Block | Notes | +| ----------------------- | ----------------------------- | ---------------------------------------------------------------- | +| Headings, titles | [Title](#title) | Fixed-size headings (H1-H4) with block-level formatting | +| Body text, paragraphs | [Paragraph](#paragraph) | Rich text with inline formatting via TipTap | +| Photos, banners, logos | [Image](#image) | Optional link wrapping, responsive width | +| Call-to-action | [Button](#button) | Bulletproof buttons that work in all email clients | +| Multi-column layout | [Section](#section) | The only block that holds other blocks | +| Visual separation | [Divider](#divider) | Horizontal line with style options | +| Vertical spacing | [Spacer](#spacer) | Empty space between blocks | +| Social links | [Social Icons](#social-icons) | 16 platforms, 5 icon styles | +| Navigation links | [Menu](#menu) | Horizontal link list with separators | +| Tabular data | [Table](#table) | Data table with optional header styling | +| Video preview | [Video](#video) | Clickable thumbnail (email clients don't support embedded video) | +| Raw markup | [HTML](#html) | Escape hatch for custom code | +| Domain-specific content | [Custom](#custom) | Your own block types with fields and Liquid templates | ## Title A heading block with fixed size levels. Use titles for headings, section headers, and other prominent text. -| Property | Type | Description | -|----------|------|-------------| -| `content` | `string` | HTML content | -| `level` | `1 \| 2 \| 3 \| 4` | Heading level (H1=36px, H2=28px, H3=22px, H4=18px) | -| `color` | `string` | Text color | -| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `fontFamily` | `string` | Font family override | +| Property | Type | Description | +| ------------ | ------------------------------- | -------------------------------------------------- | +| `content` | `string` | HTML content | +| `level` | `1 \| 2 \| 3 \| 4` | Heading level (H1=36px, H2=28px, H3=22px, H4=18px) | +| `color` | `string` | Text color | +| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `fontFamily` | `string` | Font family override | ## Paragraph Body text rendered as HTML. The editor uses [Tiptap](https://tiptap.dev) for inline editing with formatting controls (bold, italic, links, alignment, font size, color, etc.). All formatting is applied inline -- there are no block-level formatting properties. -| Property | Type | Description | -|----------|------|-------------| +| Property | Type | Description | +| --------- | -------- | ------------ | | `content` | `string` | HTML content | ## Image Displays an image with optional link wrapping. -| Property | Type | Description | -|----------|------|-------------| -| `src` | `string` | Image URL | -| `alt` | `string` | Alt text | -| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `linkUrl` | `string` | Wraps image in a link | -| `linkOpenInNewTab` | `boolean` | Link target behavior | -| `placeholderUrl` | `string` | Placeholder shown in the editor when `src` uses a merge tag | +| Property | Type | Description | +| ------------------ | ------------------------------- | ----------------------------------------------------------- | +| `src` | `string` | Image URL | +| `alt` | `string` | Alt text | +| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `linkUrl` | `string` | Wraps image in a link | +| `linkOpenInNewTab` | `boolean` | Link target behavior | +| `placeholderUrl` | `string` | Placeholder shown in the editor when `src` uses a merge tag | ## Button A call-to-action button with customizable appearance. -| Property | Type | Description | -|----------|------|-------------| -| `text` | `string` | Button label | -| `url` | `string` | Link URL | -| `backgroundColor` | `string` | Button background color | -| `textColor` | `string` | Button text color | -| `borderRadius` | `number` | Corner radius in px | -| `fontSize` | `number` | Font size in px | -| `buttonPadding` | `SpacingValue` | Inner padding | -| `fontFamily` | `string` | Font family override | -| `openInNewTab` | `boolean` | Link target behavior | +| Property | Type | Description | +| ----------------- | -------------- | ----------------------- | +| `text` | `string` | Button label | +| `url` | `string` | Link URL | +| `backgroundColor` | `string` | Button background color | +| `textColor` | `string` | Button text color | +| `borderRadius` | `number` | Corner radius in px | +| `fontSize` | `number` | Font size in px | +| `buttonPadding` | `SpacingValue` | Inner padding | +| `fontFamily` | `string` | Font family override | +| `openInNewTab` | `boolean` | Link target behavior | ## Divider A horizontal line separator. -| Property | Type | Description | -|----------|------|-------------| -| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Line style | -| `color` | `string` | Line color | -| `thickness` | `number` | Line thickness in px | -| `width` | `number \| 'full'` | Line width in px, or `'full'` for 100% | +| Property | Type | Description | +| ----------- | --------------------------------- | -------------------------------------- | +| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Line style | +| `color` | `string` | Line color | +| `thickness` | `number` | Line thickness in px | +| `width` | `number \| 'full'` | Line width in px, or `'full'` for 100% | ## Spacer Empty vertical space. -| Property | Type | Description | -|----------|------|-------------| +| Property | Type | Description | +| -------- | -------- | ------------ | | `height` | `number` | Height in px | ## HTML Injects raw HTML into the template. Use this for content that cannot be expressed with other block types. -| Property | Type | Description | -|----------|------|-------------| +| Property | Type | Description | +| --------- | -------- | --------------- | | `content` | `string` | Raw HTML markup | ## Social Icons A row of social media icons linking to platform profiles. -| Property | Type | Description | -|----------|------|-------------| -| `icons` | `SocialIcon[]` | List of social icons | -| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visual style | -| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon size | -| `spacing` | `number` | Space between icons in px | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| Property | Type | Description | +| ----------- | ------------------------------------------------------------ | ------------------------- | +| `icons` | `SocialIcon[]` | List of social icons | +| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visual style | +| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon size | +| `spacing` | `number` | Space between icons in px | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | 16 platforms are supported: Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, Pinterest, Email, WhatsApp, Telegram, Discord, Snapchat, Reddit, GitHub, Dribbble, and Behance. @@ -134,17 +134,17 @@ interface SocialIcon { A horizontal navigation menu with text links. -| Property | Type | Description | -|----------|------|-------------| -| `items` | `MenuItemData[]` | Menu items | -| `fontSize` | `number` | Font size in px | -| `fontFamily` | `string` | Font family override | -| `color` | `string` | Text color | -| `linkColor` | `string` (optional) | Link color | -| `textAlign` | `'left' \| 'center' \| 'right'` | Alignment | -| `separator` | `string` | Character between items | -| `separatorColor` | `string` | Separator color | -| `spacing` | `number` | Space around separator | +| Property | Type | Description | +| ---------------- | ------------------------------- | ----------------------- | +| `items` | `MenuItemData[]` | Menu items | +| `fontSize` | `number` | Font size in px | +| `fontFamily` | `string` | Font family override | +| `color` | `string` | Text color | +| `linkColor` | `string` (optional) | Link color | +| `textAlign` | `'left' \| 'center' \| 'right'` | Alignment | +| `separator` | `string` | Character between items | +| `separatorColor` | `string` | Separator color | +| `spacing` | `number` | Space around separator | Each `MenuItemData` has: @@ -164,18 +164,18 @@ interface MenuItemData { A data table with optional header row styling. -| Property | Type | Description | -|----------|------|-------------| -| `rows` | `TableRowData[]` | Table rows | -| `hasHeaderRow` | `boolean` | Style first row as header | -| `headerBackgroundColor` | `string` (optional) | Header row background | -| `borderColor` | `string` | Border color | -| `borderWidth` | `number` | Border width in px | -| `cellPadding` | `number` | Cell padding in px | -| `fontSize` | `number` | Font size in px | -| `fontFamily` | `string` | Font family override | -| `color` | `string` | Text color | -| `textAlign` | `'left' \| 'center' \| 'right'` | Cell text alignment | +| Property | Type | Description | +| ----------------------- | ------------------------------- | ------------------------- | +| `rows` | `TableRowData[]` | Table rows | +| `hasHeaderRow` | `boolean` | Style first row as header | +| `headerBackgroundColor` | `string` (optional) | Header row background | +| `borderColor` | `string` | Border color | +| `borderWidth` | `number` | Border width in px | +| `cellPadding` | `number` | Cell padding in px | +| `fontSize` | `number` | Font size in px | +| `fontFamily` | `string` | Font family override | +| `color` | `string` | Text color | +| `textAlign` | `'left' \| 'center' \| 'right'` | Cell text alignment | ## Video @@ -185,32 +185,32 @@ Displays a video thumbnail that links to the video URL. Email clients do not support embedded video playback. The renderer outputs a clickable thumbnail image that links to the video URL. Always provide a good `thumbnailUrl` -- it's the only thing recipients see in their inbox. ::: -| Property | Type | Description | -|----------|------|-------------| -| `url` | `string` | Video URL (YouTube, Vimeo, etc.) | -| `thumbnailUrl` | `string` | Thumbnail image URL | -| `alt` | `string` | Alt text for thumbnail | -| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `openInNewTab` | `boolean` | Link target behavior | -| `placeholderUrl` | `string` | Editor-only placeholder | +| Property | Type | Description | +| ---------------- | ------------------------------- | ----------------------------------------- | +| `url` | `string` | Video URL (YouTube, Vimeo, etc.) | +| `thumbnailUrl` | `string` | Thumbnail image URL | +| `alt` | `string` | Alt text for thumbnail | +| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `openInNewTab` | `boolean` | Link target behavior | +| `placeholderUrl` | `string` | Editor-only placeholder | ## Section A layout container that holds one or more columns. See [Sections and Columns](/guide/sections-and-columns) for full details. -| Property | Type | Description | -|----------|------|-------------| -| `columns` | `ColumnLayout` | Column layout preset | -| `children` | `Block[][]` | Array of block arrays, one per column | +| Property | Type | Description | +| ---------- | -------------- | ------------------------------------- | +| `columns` | `ColumnLayout` | Column layout preset | +| `children` | `Block[][]` | Array of block arrays, one per column | ## Custom A user-defined block type powered by field definitions and a Liquid template. See [Custom Blocks](/guide/custom-blocks) for full details. -| Property | Type | Description | -|----------|------|-------------| -| `customType` | `string` | Unique identifier for the custom block type | -| `fieldValues` | `Record` | Current values for defined fields | -| `renderedHtml` | `string` | Cached rendered output | -| `dataSourceFetched` | `boolean` | Whether the data source has been fetched | +| Property | Type | Description | +| ------------------- | ------------------------- | ------------------------------------------- | +| `customType` | `string` | Unique identifier for the custom block type | +| `fieldValues` | `Record` | Current values for defined fields | +| `renderedHtml` | `string` | Cached rendered output | +| `dataSourceFetched` | `boolean` | Whether the data source has been fetched | diff --git a/apps/docs/guide/custom-blocks.md b/apps/docs/guide/custom-blocks.md index 2cb5828..c8e26aa 100644 --- a/apps/docs/guide/custom-blocks.md +++ b/apps/docs/guide/custom-blocks.md @@ -8,7 +8,7 @@ description: Define your own block types with custom fields, Liquid templates, a Custom blocks let you extend Templatical with your own block types. Define a set of fields, write a Liquid template for rendering, and optionally connect a data source. Users interact with custom blocks through the same drag-and-drop interface as built-in blocks. ::: warning Shadow DOM and host-side queries -Custom blocks render inside the editor's Shadow DOM by default. If your custom block needs to be reached from host page code (e.g. to wire up a third-party widget by ID), `document.querySelector` calls into the editor will not find it — walk the shadow root via `container.shadowRoot.querySelector(...)` instead, or opt out with `shadowDom: false`. +Custom blocks render inside the editor's Shadow DOM by default. If your custom block needs to be reached from host page code (e.g. to wire up a third-party widget by ID), `document.querySelector` calls into the editor will not find it — walk the shadow root via `container.shadowRoot.querySelector(...)` instead, or opt out with `shadowDom: false`. See the [Shadow DOM guide](./shadow-dom) for the full host-integration story. ::: ## Defining a custom block @@ -16,23 +16,36 @@ Custom blocks render inside the editor's Shadow DOM by default. If your custom b Pass custom block definitions through the editor config. The example below creates a "Testimonial" block with a quote, author details, avatar, and star rating. Once registered, users can drag it from the block palette into their template and edit each field from the settings panel. ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", customBlocks: [ { - type: 'testimonial', - name: 'Testimonial', + type: "testimonial", + name: "Testimonial", icon: '', - description: 'Customer quote with photo and rating', + description: "Customer quote with photo and rating", fields: [ - { key: 'quote', label: 'Quote', type: 'textarea' }, - { key: 'authorName', label: 'Author Name', type: 'text' }, - { key: 'authorTitle', label: 'Author Title', type: 'text' }, - { key: 'avatar', label: 'Avatar', type: 'image' }, - { key: 'rating', label: 'Rating (1-5)', type: 'number', min: 1, max: 5, step: 1, default: 5 }, - { key: 'showRating', label: 'Show Rating', type: 'boolean', default: true }, + { key: "quote", label: "Quote", type: "textarea" }, + { key: "authorName", label: "Author Name", type: "text" }, + { key: "authorTitle", label: "Author Title", type: "text" }, + { key: "avatar", label: "Avatar", type: "image" }, + { + key: "rating", + label: "Rating (1-5)", + type: "number", + min: 1, + max: 5, + step: 1, + default: 5, + }, + { + key: "showRating", + label: "Show Rating", + type: "boolean", + default: true, + }, ], template: `
@@ -99,15 +112,15 @@ interface CustomBlockDefinition { } ``` -| Property | Required | Description | -|----------|----------|-------------| -| `type` | Yes | Unique identifier (used as `customType` on block instances) | -| `name` | Yes | Display name in the block palette | -| `icon` | No | Inline SVG string, image URL, or base64 data URI for the palette icon | -| `description` | No | Tooltip or subtitle in the palette | -| `fields` | Yes | Array of field definitions | -| `template` | Yes | Liquid template string for rendering | -| `dataSource` | No | External data fetching configuration | +| Property | Required | Description | +| ------------- | -------- | --------------------------------------------------------------------- | +| `type` | Yes | Unique identifier (used as `customType` on block instances) | +| `name` | Yes | Display name in the block palette | +| `icon` | No | Inline SVG string, image URL, or base64 data URI for the palette icon | +| `description` | No | Tooltip or subtitle in the palette | +| `fields` | Yes | Array of field definitions | +| `template` | Yes | Liquid template string for rendering | +| `dataSource` | No | External data fetching configuration | ## Field types @@ -125,16 +138,16 @@ interface CustomBlockFieldBase { All field types extend this base. The `key` is used as the variable name in your Liquid template. Additional properties depend on the field `type`: -| Property | Applies to | Description | -|----------|------------|-------------| -| `required` | All | Mark the field as required | -| `placeholder` | All | Placeholder text for the input | -| `readOnly` | All | Prevent user editing (useful with data sources) | -| `default` | All | Default value when the block is created | -| `min`, `max`, `step` | `number` | Numeric constraints | -| `options` | `select` | Array of `{ label, value }` choices | -| `fields` | `repeatable` | Sub-field definitions | -| `minItems`, `maxItems` | `repeatable` | Item count bounds | +| Property | Applies to | Description | +| ---------------------- | ------------ | ----------------------------------------------- | +| `required` | All | Mark the field as required | +| `placeholder` | All | Placeholder text for the input | +| `readOnly` | All | Prevent user editing (useful with data sources) | +| `default` | All | Default value when the block is created | +| `min`, `max`, `step` | `number` | Numeric constraints | +| `options` | `select` | Array of `{ label, value }` choices | +| `fields` | `repeatable` | Sub-field definitions | +| `minItems`, `maxItems` | `repeatable` | Item count bounds | ### text @@ -308,7 +321,9 @@ Custom blocks become even more powerful when backed by an API data source. Inste ```ts interface DataSourceConfig { label: string; - onFetch: (context: DataSourceFetchContext) => Promise | null>; + onFetch: ( + context: DataSourceFetchContext, + ) => Promise | null>; } interface DataSourceFetchContext { @@ -370,26 +385,36 @@ An event invitation block with a schedule built using repeatable fields: ```ts const eventCard: CustomBlockDefinition = { - type: 'event-card', - name: 'Event Card', + type: "event-card", + name: "Event Card", icon: '', - description: 'Event details with schedule and RSVP', + description: "Event details with schedule and RSVP", fields: [ - { key: 'eventName', label: 'Event Name', type: 'text', default: 'Untitled Event' }, - { key: 'date', label: 'Date', type: 'text', default: 'January 1, 2026' }, - { key: 'venue', label: 'Venue', type: 'text' }, - { key: 'venueAddress', label: 'Venue Address', type: 'text' }, - { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, - { key: 'rsvpUrl', label: 'RSVP URL', type: 'text' }, { - key: 'schedule', - label: 'Schedule', - type: 'repeatable', + key: "eventName", + label: "Event Name", + type: "text", + default: "Untitled Event", + }, + { key: "date", label: "Date", type: "text", default: "January 1, 2026" }, + { key: "venue", label: "Venue", type: "text" }, + { key: "venueAddress", label: "Venue Address", type: "text" }, + { + key: "accentColor", + label: "Accent Color", + type: "color", + default: "#4f46e5", + }, + { key: "rsvpUrl", label: "RSVP URL", type: "text" }, + { + key: "schedule", + label: "Schedule", + type: "repeatable", minItems: 1, maxItems: 10, fields: [ - { key: 'time', label: 'Time', type: 'text' }, - { key: 'session', label: 'Session', type: 'text' }, + { key: "time", label: "Time", type: "text" }, + { key: "session", label: "Session", type: "text" }, ], }, ], @@ -422,26 +447,39 @@ A pricing block with a feature list and CTA button: ```ts const pricingTier: CustomBlockDefinition = { - type: 'pricing-tier', - name: 'Pricing Tier', + type: "pricing-tier", + name: "Pricing Tier", icon: '', - description: 'Pricing card with features list', + description: "Pricing card with features list", fields: [ - { key: 'planName', label: 'Plan Name', type: 'text', default: 'Pro' }, - { key: 'price', label: 'Price', type: 'text', default: '$29/mo' }, - { key: 'highlighted', label: 'Highlighted', type: 'boolean', default: false }, - { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, - { key: 'ctaLabel', label: 'Button Label', type: 'text', default: 'Get Started' }, - { key: 'ctaUrl', label: 'Button URL', type: 'text' }, + { key: "planName", label: "Plan Name", type: "text", default: "Pro" }, + { key: "price", label: "Price", type: "text", default: "$29/mo" }, + { + key: "highlighted", + label: "Highlighted", + type: "boolean", + default: false, + }, { - key: 'features', - label: 'Features', - type: 'repeatable', + key: "accentColor", + label: "Accent Color", + type: "color", + default: "#4f46e5", + }, + { + key: "ctaLabel", + label: "Button Label", + type: "text", + default: "Get Started", + }, + { key: "ctaUrl", label: "Button URL", type: "text" }, + { + key: "features", + label: "Features", + type: "repeatable", minItems: 1, maxItems: 8, - fields: [ - { key: 'text', label: 'Feature', type: 'text' }, - ], + fields: [{ key: "text", label: "Feature", type: "text" }], }, ], template: ` diff --git a/apps/docs/guide/defaults.md b/apps/docs/guide/defaults.md index 0e091b8..7b9b349 100644 --- a/apps/docs/guide/defaults.md +++ b/apps/docs/guide/defaults.md @@ -12,29 +12,31 @@ Block properties (colors, font sizes, padding, placeholder text, etc.) are hardc Pass a `blockDefaults` object to `init()`. Each key maps to a block type and accepts a partial override of that block's properties: ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", blockDefaults: { - title: { color: '#000000' }, + title: { color: "#000000" }, paragraph: {}, button: { - backgroundColor: '#ff6600', - textColor: '#ffffff', + backgroundColor: "#ff6600", + textColor: "#ffffff", styles: { padding: { top: 20, bottom: 20 } }, }, - divider: { color: '#eeeeee', thickness: 2 }, - image: { alt: 'Brand image' }, + divider: { color: "#eeeeee", thickness: 2 }, + image: { alt: "Brand image" }, }, }); ``` Defaults apply when: + - Dragging a block from the sidebar - Calling `createAndAddBlock()` programmatically Defaults do **not** apply when: + - Duplicating an existing block (the source block's values are preserved) - Loading saved content from the API @@ -57,29 +59,29 @@ Arrays are **replaced**, not merged. For example, setting `table.rows` replaces ### Supported Block Types -| Key | Block Type | -|-----|-----------| -| `title` | Title | -| `paragraph` | Paragraph | -| `image` | Image | -| `button` | Button | -| `divider` | Divider | -| `section` | Section | -| `video` | Video | -| `social` | Social Icons | -| `spacer` | Spacer | -| `html` | HTML | -| `menu` | Menu | -| `table` | Table | +| Key | Block Type | +| ----------- | ------------ | +| `title` | Title | +| `paragraph` | Paragraph | +| `image` | Image | +| `button` | Button | +| `divider` | Divider | +| `section` | Section | +| `video` | Video | +| `social` | Social Icons | +| `spacer` | Spacer | +| `html` | HTML | +| `menu` | Menu | +| `table` | Table | Custom blocks are not affected by `blockDefaults`. They use their own `default` values defined in the `CustomBlockDefinition` field configuration. ### TypeScript Type ```ts -import type { BlockDefaults } from '@templatical/editor'; +import type { BlockDefaults } from "@templatical/editor"; // or -import type { BlockDefaults } from '@templatical/types'; +import type { BlockDefaults } from "@templatical/types"; ``` Each block type key accepts `Partial>` — you can override any property except `id` and `type`, which are always generated automatically. See [Block Types](/guide/blocks) for the full list of available properties per block. @@ -90,12 +92,12 @@ Pass a `templateDefaults` object to override the default template settings used ```ts const editor = await init({ - container: '#editor', + container: "#editor", templateDefaults: { width: 640, - backgroundColor: '#f5f5f5', - fontFamily: 'Helvetica, sans-serif', - preheaderText: 'Check out our latest news', + backgroundColor: "#f5f5f5", + fontFamily: "Helvetica, sans-serif", + preheaderText: "Check out our latest news", }, }); ``` @@ -109,19 +111,19 @@ In other words, `templateDefaults` are fallbacks for missing content, not overri ### Available Settings -| Property | Default | Description | -|----------|---------|-------------| -| `width` | `600` | Template width in pixels | +| Property | Default | Description | +| ----------------- | --------- | ------------------------- | +| `width` | `600` | Template width in pixels | | `backgroundColor` | `#ffffff` | Template background color | -| `fontFamily` | `Arial` | Default font family | -| `preheaderText` | — | Email preheader text | +| `fontFamily` | `Arial` | Default font family | +| `preheaderText` | — | Email preheader text | ### TypeScript Type ```ts -import type { TemplateDefaults } from '@templatical/editor'; +import type { TemplateDefaults } from "@templatical/editor"; // or -import type { TemplateDefaults } from '@templatical/types'; +import type { TemplateDefaults } from "@templatical/types"; ``` ## Built-in Default Constants @@ -135,7 +137,7 @@ import { TITLE_BLOCK_DEFAULTS, PARAGRAPH_BLOCK_DEFAULTS, BUTTON_BLOCK_DEFAULTS, -} from '@templatical/types'; +} from "@templatical/types"; // Inspect the defaults for a single block type console.log(TITLE_BLOCK_DEFAULTS); @@ -148,7 +150,7 @@ console.log(DEFAULT_TEMPLATE_DEFAULTS); // Build a custom preset by extending a single block's defaults const myButtonDefaults = { ...BUTTON_BLOCK_DEFAULTS, - backgroundColor: '#ff6600', + backgroundColor: "#ff6600", borderRadius: 8, }; ``` @@ -157,27 +159,27 @@ const myButtonDefaults = { **Per-block constants** — each contains the default property values for that block type (excluding `id`, `type`, and `styles`): -| Constant | Block Type | -|----------|-----------| -| `TITLE_BLOCK_DEFAULTS` | Title | -| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | -| `IMAGE_BLOCK_DEFAULTS` | Image | -| `BUTTON_BLOCK_DEFAULTS` | Button | -| `DIVIDER_BLOCK_DEFAULTS` | Divider | -| `SECTION_BLOCK_DEFAULTS` | Section | -| `VIDEO_BLOCK_DEFAULTS` | Video | +| Constant | Block Type | +| ----------------------------- | ------------ | +| `TITLE_BLOCK_DEFAULTS` | Title | +| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | +| `IMAGE_BLOCK_DEFAULTS` | Image | +| `BUTTON_BLOCK_DEFAULTS` | Button | +| `DIVIDER_BLOCK_DEFAULTS` | Divider | +| `SECTION_BLOCK_DEFAULTS` | Section | +| `VIDEO_BLOCK_DEFAULTS` | Video | | `SOCIAL_ICONS_BLOCK_DEFAULTS` | Social Icons | -| `SPACER_BLOCK_DEFAULTS` | Spacer | -| `HTML_BLOCK_DEFAULTS` | HTML | -| `MENU_BLOCK_DEFAULTS` | Menu | -| `TABLE_BLOCK_DEFAULTS` | Table | +| `SPACER_BLOCK_DEFAULTS` | Spacer | +| `HTML_BLOCK_DEFAULTS` | HTML | +| `MENU_BLOCK_DEFAULTS` | Menu | +| `TABLE_BLOCK_DEFAULTS` | Table | **Combined constants:** -| Constant | Description | -|----------|-------------| -| `DEFAULT_BLOCK_DEFAULTS` | All block type defaults in a single object (keys match `BlockDefaults` interface) | -| `DEFAULT_TEMPLATE_DEFAULTS` | Template settings defaults (`width`, `backgroundColor`, `fontFamily`) | +| Constant | Description | +| --------------------------- | --------------------------------------------------------------------------------- | +| `DEFAULT_BLOCK_DEFAULTS` | All block type defaults in a single object (keys match `BlockDefaults` interface) | +| `DEFAULT_TEMPLATE_DEFAULTS` | Template settings defaults (`width`, `backgroundColor`, `fontFamily`) | These constants are the single source of truth used internally by the factory functions. If you only need to override a few properties, you don't need to reference them — just pass your overrides to `blockDefaults` and they'll be deep-merged with these values. @@ -186,23 +188,28 @@ These constants are the single source of truth used internally by the factory fu The underlying factory functions also accept defaults directly: ```ts -import { createBlock, createTitleBlock, createParagraphBlock, createDefaultTemplateContent } from '@templatical/types'; -import type { BlockDefaults } from '@templatical/types'; +import { + createBlock, + createTitleBlock, + createParagraphBlock, + createDefaultTemplateContent, +} from "@templatical/types"; +import type { BlockDefaults } from "@templatical/types"; // Single block with partial overrides -const title = createTitleBlock({ level: 2, color: '#000' }); -const paragraph = createParagraphBlock({ content: '

Hello

' }); +const title = createTitleBlock({ level: 2, color: "#000" }); +const paragraph = createParagraphBlock({ content: "

Hello

" }); // Via createBlock with full defaults map const defaults: BlockDefaults = { - title: { color: '#000000' }, - button: { backgroundColor: '#ff6600' }, + title: { color: "#000000" }, + button: { backgroundColor: "#ff6600" }, }; -const block = createBlock('title', defaults); +const block = createBlock("title", defaults); // Template with custom settings -const content = createDefaultTemplateContent('Helvetica, sans-serif', { +const content = createDefaultTemplateContent("Helvetica, sans-serif", { width: 640, - backgroundColor: '#f5f5f5', + backgroundColor: "#f5f5f5", }); ``` diff --git a/apps/docs/guide/display-conditions.md b/apps/docs/guide/display-conditions.md index 8e1b906..732d587 100644 --- a/apps/docs/guide/display-conditions.md +++ b/apps/docs/guide/display-conditions.md @@ -12,23 +12,23 @@ Display conditions allow users to change block visibility based on conditions. W Define available conditions through the editor config: ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", displayConditions: { conditions: [ { - label: 'VIP Customers', - before: '{% if customer.vip == true %}', - after: '{% endif %}', - description: 'Only shown to VIP customers', + label: "VIP Customers", + before: "{% if customer.vip == true %}", + after: "{% endif %}", + description: "Only shown to VIP customers", }, { - label: 'Has Active Subscription', - before: '{% if subscription.active %}', - after: '{% endif %}', - description: 'Shown when user has an active subscription', + label: "Has Active Subscription", + before: "{% if subscription.active %}", + after: "{% endif %}", + description: "Shown when user has an active subscription", }, ], }, @@ -47,13 +47,13 @@ interface DisplayCondition { } ``` -| Property | Required | Description | -|----------|----------|-------------| -| `label` | Yes | Display name shown in the editor UI | -| `before` | Yes | Markup inserted before the block output | -| `after` | Yes | Markup inserted after the block output | -| `group` | No | Group name for organizing conditions in the dropdown | -| `description` | No | Explanatory text shown below the label | +| Property | Required | Description | +| ------------- | -------- | ---------------------------------------------------- | +| `label` | Yes | Display name shown in the editor UI | +| `before` | Yes | Markup inserted before the block output | +| `after` | Yes | Markup inserted after the block output | +| `group` | No | Group name for organizing conditions in the dropdown | +| `description` | No | Explanatory text shown below the label | ## DisplayConditionsConfig diff --git a/apps/docs/guide/fonts.md b/apps/docs/guide/fonts.md index 7a662a4..69aad1a 100644 --- a/apps/docs/guide/fonts.md +++ b/apps/docs/guide/fonts.md @@ -10,46 +10,46 @@ By default, the editor includes a set of common web-safe fonts (Arial, Georgia, Configure which fonts are available using the `fonts` option: ```ts -import type { FontsConfig } from '@templatical/types'; +import type { FontsConfig } from "@templatical/types"; const fonts: FontsConfig = { - defaultFont: 'Inter', - defaultFallback: 'Arial, sans-serif', + defaultFont: "Inter", + defaultFallback: "Arial, sans-serif", customFonts: [ { - name: 'Inter', - url: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap', - fallback: 'Helvetica, Arial, sans-serif', + name: "Inter", + url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap", + fallback: "Helvetica, Arial, sans-serif", }, { - name: 'Merriweather', - url: 'https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap', - fallback: 'Georgia, serif', + name: "Merriweather", + url: "https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap", + fallback: "Georgia, serif", }, ], }; const editor = await init({ - container: '#editor', + container: "#editor", fonts, }); ``` ## FontsConfig -| Property | Type | Description | -|---|---|---| -| `defaultFont` | `string` | Font name selected by default in new templates | -| `defaultFallback` | `string` | Fallback stack used when a custom font is unavailable | -| `customFonts` | `CustomFont[]` | List of custom fonts to register | +| Property | Type | Description | +| ----------------- | -------------- | ----------------------------------------------------- | +| `defaultFont` | `string` | Font name selected by default in new templates | +| `defaultFallback` | `string` | Fallback stack used when a custom font is unavailable | +| `customFonts` | `CustomFont[]` | List of custom fonts to register | ## CustomFont -| Property | Type | Description | -|---|---|---| -| `name` | `string` | Display name in the font picker | -| `url` | `string` | URL to the font CSS (e.g., Google Fonts link) | -| `fallback` | `string` | Optional fallback font stack for this font | +| Property | Type | Description | +| ---------- | -------- | --------------------------------------------- | +| `name` | `string` | Display name in the font picker | +| `url` | `string` | URL to the font CSS (e.g., Google Fonts link) | +| `fallback` | `string` | Optional fallback font stack for this font | Custom fonts are automatically included as `` declarations in the rendered MJML output. diff --git a/apps/docs/guide/i18n.md b/apps/docs/guide/i18n.md index 0536b1c..7791408 100644 --- a/apps/docs/guide/i18n.md +++ b/apps/docs/guide/i18n.md @@ -12,32 +12,32 @@ The editor UI supports locale switching. All labels, tooltips, placeholders, and Pass the `locale` option to `init()`: ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', - locale: 'de', + container: "#editor", + locale: "de", }); ``` ## Built-in locales -| Code | Language | -|---|---| +| Code | Language | +| ---- | ----------------- | | `en` | English (default) | -| `de` | German | +| `de` | German | ## Locale resolution The editor normalizes locale codes by stripping region suffixes: -| Input | Resolved | -|---|---| -| `'en'` | `en` | -| `'en-US'` | `en` | -| `'en-GB'` | `en` | -| `'de-AT'` | `de` | -| `'fr'` | `en` (unsupported, falls back to English) | +| Input | Resolved | +| --------- | ----------------------------------------- | +| `'en'` | `en` | +| `'en-US'` | `en` | +| `'en-GB'` | `en` | +| `'de-AT'` | `de` | +| `'fr'` | `en` (unsupported, falls back to English) | If the resolved locale is not supported, the editor falls back to English silently. @@ -49,7 +49,7 @@ Locale files are loaded asynchronously using dynamic `import()`. Only the active async function switchLocale(newLocale: string) { editor.unmount(); editor = await init({ - container: '#editor', + container: "#editor", locale: newLocale, }); } @@ -111,17 +111,17 @@ Example structure for a new locale: // packages/editor/src/i18n/locales/fr.ts export default { blocks: { - paragraph: 'Paragraphe', - image: 'Image', - button: 'Bouton', - section: 'Section', - divider: 'Séparateur', - spacer: 'Espacement', + paragraph: "Paragraphe", + image: "Image", + button: "Bouton", + section: "Section", + divider: "Séparateur", + spacer: "Espacement", // ... all keys from en.ts }, toolbar: { - duplicate: 'Dupliquer', - delete: 'Supprimer', + duplicate: "Dupliquer", + delete: "Supprimer", // ... }, // ... all sections from en.ts diff --git a/apps/docs/guide/images.md b/apps/docs/guide/images.md index 4c3b4b3..22538f2 100644 --- a/apps/docs/guide/images.md +++ b/apps/docs/guide/images.md @@ -18,10 +18,10 @@ When the `onRequestMedia` callback is provided, a browse button appears alongsid The editor calls this function whenever the user clicks the button. Return a `MediaResult` object, or `null` if the user cancels. When `alt` is provided, the editor automatically fills in the image's alt text. ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", async onRequestMedia() { // Open your own modal, file browser, or asset manager const image = await openMyMediaModal(); @@ -43,20 +43,19 @@ interface MediaResult { onRequestMedia?: (context?: MediaRequestContext) => Promise; ``` - ## Image Block Properties The `ImageBlock` type defines all configurable properties: -| Property | Type | Description | -|---|---|---| -| `src` | `string` | Image source URL | -| `alt` | `string` | Alt text for accessibility | -| `width` | `number \| 'full'` | Image width in pixels, or `'full'` for 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `linkUrl` | `string` (optional) | Wraps the image in a link | -| `linkOpenInNewTab` | `boolean` (optional) | Opens the link in a new tab | -| `placeholderUrl` | `string` (optional) | Design-time preview image when `src` uses a merge tag | +| Property | Type | Description | +| ------------------ | ------------------------------- | ----------------------------------------------------- | +| `src` | `string` | Image source URL | +| `alt` | `string` | Alt text for accessibility | +| `width` | `number \| 'full'` | Image width in pixels, or `'full'` for 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `linkUrl` | `string` (optional) | Wraps the image in a link | +| `linkOpenInNewTab` | `boolean` (optional) | Opens the link in a new tab | +| `placeholderUrl` | `string` (optional) | Design-time preview image when `src` uses a merge tag | ### Placeholder URL diff --git a/apps/docs/guide/merge-tags.md b/apps/docs/guide/merge-tags.md index 545b565..4daeeef 100644 --- a/apps/docs/guide/merge-tags.md +++ b/apps/docs/guide/merge-tags.md @@ -20,17 +20,17 @@ Hovering over a tag reveals the raw value behind the label. The `syntax` property is optional and defaults to `'liquid'`. ```ts -import { init } from '@templatical/editor'; +import { init } from "@templatical/editor"; const editor = await init({ - container: '#editor', + container: "#editor", mergeTags: { tags: [ - { label: 'First Name', value: '{{first_name}}' }, - { label: 'Last Name', value: '{{last_name}}' }, - { label: 'Email', value: '{{email}}' }, - { label: 'Company', value: '{{company.name}}' }, - { label: 'Unsubscribe URL', value: '{{unsubscribe_url}}' }, + { label: "First Name", value: "{{first_name}}" }, + { label: "Last Name", value: "{{last_name}}" }, + { label: "Email", value: "{{email}}" }, + { label: "Company", value: "{{company.name}}" }, + { label: "Unsubscribe URL", value: "{{unsubscribe_url}}" }, ], }, }); @@ -56,15 +56,16 @@ The `value` must include the syntax delimiters. For example, with Liquid syntax: Templatical includes four built-in syntax presets. The `syntax` setting tells the editor how to detect and highlight both data tags and logic tags in content. Each preset defines two patterns: + - **Data tags** -- variable merge tags like a recipient's name or email - **Logic tags** -- control flow statements like conditionals and loops -| Preset | Data tag | Logic tag | Platform | -|--------|----------|-----------|----------| -| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | -| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | -| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | -| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | +| Preset | Data tag | Logic tag | Platform | +| -------------- | --------------------------------- | ------------------------------- | ------------------------------- | +| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | +| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | +| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | +| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | ```ts mergeTags: { @@ -90,34 +91,35 @@ Like data tags, logic tags pass through unchanged in the rendered MJML — your Examples of logic tags by preset: ::: code-group + ```html [Liquid] {% if customer.vip %} -

Exclusive offer just for you!

-{% endif %} - -{% for item in cart.items %} -

{{item.name}} - {{item.price}}

+

Exclusive offer just for you!

+{% endif %} {% for item in cart.items %} +

{{item.name}} - {{item.price}}

{% endfor %} ``` + ```html [Handlebars] {{#if hasSubscription}} -

Your plan renews on {{renewal_date}}

-{{/if}} - -{{#each products}} -

{{this.name}}

+

Your plan renews on {{renewal_date}}

+{{/if}} {{#each products}} +

{{this.name}}

{{/each}} ``` + ```html [Mailchimp] *|IF:VIP|* -

VIP discount applied

+

VIP discount applied

*|END:IF|* ``` + ```html [AMPscript] %%[IF @subscriber_type == "premium"]%% -

Premium content here

+

Premium content here

%%[ENDIF]%% ``` + ::: ## Custom syntax @@ -126,8 +128,8 @@ If the built-in presets don't match your platform, define a custom syntax with t ```ts interface SyntaxPreset { - value: RegExp; // matches data tags like ${user.name} - logic: RegExp; // matches logic tags like $[IF ...] + value: RegExp; // matches data tags like ${user.name} + logic: RegExp; // matches logic tags like $[IF ...] } ``` @@ -163,12 +165,10 @@ To opt out explicitly, set `autocomplete: false`: ```ts const editor = await init({ - container: '#editor', + container: "#editor", mergeTags: { autocomplete: false, - tags: [ - { label: 'First Name', value: '{{first_name}}' }, - ], + tags: [{ label: "First Name", value: "{{first_name}}" }], }, }); ``` @@ -181,7 +181,7 @@ For large or context-dependent tag lists, use the `onRequest` callback instead o ```ts const editor = await init({ - container: '#editor', + container: "#editor", mergeTags: { onRequest: async () => { const tag = await showMyMergeTagPicker(); diff --git a/apps/docs/guide/migration-from-beefree.md b/apps/docs/guide/migration-from-beefree.md index e699752..004cd53 100644 --- a/apps/docs/guide/migration-from-beefree.md +++ b/apps/docs/guide/migration-from-beefree.md @@ -20,17 +20,19 @@ npm install @templatical/import-beefree ## Usage ```ts -import { convertBeeFreeTemplate } from '@templatical/import-beefree'; +import { convertBeeFreeTemplate } from "@templatical/import-beefree"; // Load your BeeFree template JSON -const beefreeJson = await fetch('/api/beefree-templates/123').then(r => r.json()); +const beefreeJson = await fetch("/api/beefree-templates/123").then((r) => + r.json(), +); // Convert to Templatical format const { content, report } = convertBeeFreeTemplate(beefreeJson); // Use in the editor const editor = await init({ - container: '#editor', + container: "#editor", content, }); @@ -39,6 +41,7 @@ console.log(report); ``` The function returns an `ImportResult` with: + - `content` — the converted `TemplateContent` ready for the editor - `report` — a conversion report with the status of each block (`converted`, `approximated`, `html-fallback`, or `skipped`) @@ -46,21 +49,21 @@ The function returns an `ImportResult` with: BeeFree block types map to Templatical equivalents: -| BeeFree Module | Templatical Block | Status | -|---|---|---| -| Text | `paragraph` | Converted | -| Paragraph | `paragraph` | Converted | -| Heading | `title` | Converted | -| List | `paragraph` | Converted | -| Image | `image` | Converted | -| Button | `button` | Converted | -| Divider | `divider` | Converted | -| Spacer | `spacer` | Converted | -| Social | `social` | Converted (16 platforms mapped) | -| Html | `html` | Converted | -| Menu | `menu` | Approximated (styles may differ) | -| Video | `video` | Converted | -| Table | `table` | Converted | +| BeeFree Module | Templatical Block | Status | +| -------------- | ----------------- | -------------------------------- | +| Text | `paragraph` | Converted | +| Paragraph | `paragraph` | Converted | +| Heading | `title` | Converted | +| List | `paragraph` | Converted | +| Image | `image` | Converted | +| Button | `button` | Converted | +| Divider | `divider` | Converted | +| Spacer | `spacer` | Converted | +| Social | `social` | Converted (16 platforms mapped) | +| Html | `html` | Converted | +| Menu | `menu` | Approximated (styles may differ) | +| Video | `video` | Converted | +| Table | `table` | Converted | Unknown module types are converted to HTML blocks as a fallback. @@ -68,13 +71,13 @@ Unknown module types are converted to HTML blocks as a fallback. BeeFree organizes content into rows with columns. These map to Templatical's `SectionBlock` with the appropriate `ColumnLayout`: -| BeeFree Columns | Templatical Layout | -|---|---| -| 1 column (100%) | `'1'` | -| 2 equal columns | `'2'` | -| 3 equal columns | `'3'` | -| 2 columns (~33/66) | `'1-2'` | -| 2 columns (~66/33) | `'2-1'` | +| BeeFree Columns | Templatical Layout | +| ------------------ | ------------------ | +| 1 column (100%) | `'1'` | +| 2 equal columns | `'2'` | +| 3 equal columns | `'3'` | +| 2 columns (~33/66) | `'1-2'` | +| 2 columns (~66/33) | `'2-1'` | Column widths that don't match a standard ratio are mapped to the closest available layout. diff --git a/apps/docs/guide/migration-from-html.md b/apps/docs/guide/migration-from-html.md index 55bfed0..0435b8c 100644 --- a/apps/docs/guide/migration-from-html.md +++ b/apps/docs/guide/migration-from-html.md @@ -20,17 +20,17 @@ npm install @templatical/import-html ## Usage ```ts -import { convertHtmlTemplate } from '@templatical/import-html'; +import { convertHtmlTemplate } from "@templatical/import-html"; // Load the raw HTML source of an email -const html = await fetch('/path/to/email.html').then((r) => r.text()); +const html = await fetch("/path/to/email.html").then((r) => r.text()); // Convert to Templatical format const { content, report } = convertHtmlTemplate(html); // Use in the editor const editor = await init({ - container: '#editor', + container: "#editor", content, }); @@ -39,6 +39,7 @@ console.log(report); ``` The function returns an `ImportResult` with: + - `content` — the converted `TemplateContent` ready for the editor - `report` — a conversion report with the status of each element (`converted`, `approximated`, `html-fallback`, or `skipped`) @@ -46,20 +47,20 @@ The function returns an `ImportResult` with: HTML elements map to Templatical equivalents: -| HTML Element | Templatical Block | Status | -|---|---|---| -| `

` – `

` | `title` | Converted (level preserved) | -| `

` – `
` | `title` | Converted (clamped to level 4) | -| `

` / text-only `

` with explicit height | `spacer` | Converted | -| `` containing a single styled `` | `button` | Converted (cell-as-button pattern) | -| `` (layout, multi-row/column) | `section` (one per ``) | Converted | -| `
` (data table — text-only cells) | `html` | HTML fallback | -| Unknown / custom elements | `html` | HTML fallback | +| HTML Element | Templatical Block | Status | +| --------------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------- | +| `

` – `

` | `title` | Converted (level preserved) | +| `

` – `
` | `title` | Converted (clamped to level 4) | +| `

` / text-only `

` with explicit height | `spacer` | Converted | +| `` containing a single styled `` | `button` | Converted (cell-as-button pattern) | +| `` (layout, multi-row/column) | `section` (one per ``) | Converted | +| `
` (data table — text-only cells) | `html` | HTML fallback | +| Unknown / custom elements | `html` | HTML fallback | Anything that can't be mapped is preserved verbatim inside an HTML block, so no visible content is lost. @@ -67,12 +68,12 @@ Anything that can't be mapped is preserved verbatim inside an HTML block, so no Each `` in a layout table becomes a `SectionBlock`. Cell counts map directly: -| Cells per row | Templatical Layout | -|---|---| -| 1 | `'1'` | -| 2 | `'2'` | -| 3 | `'3'` | -| 4+ | flattened to `'1'` with a warning | +| Cells per row | Templatical Layout | +| ------------- | --------------------------------- | +| 1 | `'1'` | +| 2 | `'2'` | +| 3 | `'3'` | +| 4+ | flattened to `'1'` with a warning | Templatical sections cannot nest. Tables nested inside a `` and `` and `
` are flattened — their inner blocks are merged into the parent cell. @@ -127,11 +128,8 @@ console.log(report.summary); // { total: 12, converted: 10, approximated: 1, htmlFallback: 1, skipped: 0 } for (const entry of report.entries) { - if (entry.status === 'html-fallback') { - console.warn( - `Element <${entry.sourceTag}> preserved as HTML:`, - entry.note, - ); + if (entry.status === "html-fallback") { + console.warn(`Element <${entry.sourceTag}> preserved as HTML:`, entry.note); } } diff --git a/apps/docs/guide/migration-from-mjml.md b/apps/docs/guide/migration-from-mjml.md index 1ffa8ff..dd14d8d 100644 --- a/apps/docs/guide/migration-from-mjml.md +++ b/apps/docs/guide/migration-from-mjml.md @@ -15,7 +15,7 @@ MJML → Templatical is harder to fully automate than BeeFree → Templatical, b ## What's actually happening here -This is a slightly counter-intuitive migration. Templatical's renderer produces *MJML output* — so on the surface, MJML and Templatical look identical. But: +This is a slightly counter-intuitive migration. Templatical's renderer produces _MJML output_ — so on the surface, MJML and Templatical look identical. But: - **MJML** is a markup language. You write XML-like tags (``, ``, ``) and the MJML compiler turns them into table-based HTML. - **Templatical** stores templates as a JSON tree of typed blocks (`SectionBlock`, `ParagraphBlock`, etc.) and renders that tree to MJML at export time. @@ -40,7 +40,7 @@ Most MJML templates port in 10–20 minutes once you've done one or two. Once you've rebuilt a template visually: ```ts -import { renderToMjml } from '@templatical/renderer'; +import { renderToMjml } from "@templatical/renderer"; const mjml = await renderToMjml(content); // Compare this MJML against your original MJML source. @@ -55,19 +55,19 @@ If you have hundreds of MJML templates and want to attempt automated conversion Here's the rough shape: ```ts -import { parse } from 'node-html-parser'; +import { parse } from "node-html-parser"; import { createSectionBlock, createTitleBlock, createParagraphBlock, createImageBlock, createButtonBlock, -} from '@templatical/types'; -import type { TemplateContent, Block } from '@templatical/types'; +} from "@templatical/types"; +import type { TemplateContent, Block } from "@templatical/types"; function mjmlToTemplate(mjml: string): TemplateContent { const root = parse(mjml); - const body = root.querySelector('mj-body'); + const body = root.querySelector("mj-body"); const blocks: Block[] = (body?.childNodes ?? []) .map((node) => convertNode(node)) @@ -76,17 +76,17 @@ function mjmlToTemplate(mjml: string): TemplateContent { return { blocks, settings: { - width: parseInt(body?.getAttribute('width') ?? '600'), - backgroundColor: body?.getAttribute('background-color') ?? '#ffffff', + width: parseInt(body?.getAttribute("width") ?? "600"), + backgroundColor: body?.getAttribute("background-color") ?? "#ffffff", }, }; } function convertNode(node: any): Block | null { switch (node.tagName?.toLowerCase()) { - case 'mj-section': + case "mj-section": return convertSection(node); - case 'mj-text': + case "mj-text": return convertText(node); // …more cases — see the mapping table below default: @@ -101,22 +101,22 @@ A handwritten parser will miss edge cases — nested `mj-wrapper`, custom compon ## MJML tag mapping {#mjml-tag-mapping} -| MJML tag | Templatical block | Notes | -|---|---|---| -| `mj-section` (containing `mj-column`s) | `SectionBlock` with `columns` | Multi-column layouts work the same way; column widths come from MJML's `width` attribute or are equally distributed. | -| `mj-column` | Section column | A column holds a list of nested blocks. | -| `mj-text` | `ParagraphBlock` (or `TitleBlock` if it's a heading) | Use heading-level inline styles to decide between Title and Paragraph. | -| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, padding. | -| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, font, padding. | -| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, padding. | -| `mj-spacer` | `SpacerBlock` | `height`. | -| `mj-social` (with `mj-social-element`) | `SocialIconsBlock` | Each `mj-social-element` → a `SocialIcon` entry. | -| `mj-navbar` (with `mj-navbar-link`) | `MenuBlock` | Each link → `MenuItemData`. | -| `mj-table` | `TableBlock` | Map `
` rows/cells to Templatical's table data. | -| `mj-raw` | `HtmlBlock` | Pass-through HTML. | -| `mj-wrapper` | `SectionBlock` (often) | A wrapper without columns becomes a section with one column. | -| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (fallback) | Templatical doesn't have direct equivalents yet — convert the rendered HTML to a raw HTML block, or wait for the importer. | -| `mj-head` content | Template `settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` map to `TemplateSettings.preheaderText`, custom fonts, and theme overrides. | +| MJML tag | Templatical block | Notes | +| ---------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `mj-section` (containing `mj-column`s) | `SectionBlock` with `columns` | Multi-column layouts work the same way; column widths come from MJML's `width` attribute or are equally distributed. | +| `mj-column` | Section column | A column holds a list of nested blocks. | +| `mj-text` | `ParagraphBlock` (or `TitleBlock` if it's a heading) | Use heading-level inline styles to decide between Title and Paragraph. | +| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, padding. | +| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, font, padding. | +| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, padding. | +| `mj-spacer` | `SpacerBlock` | `height`. | +| `mj-social` (with `mj-social-element`) | `SocialIconsBlock` | Each `mj-social-element` → a `SocialIcon` entry. | +| `mj-navbar` (with `mj-navbar-link`) | `MenuBlock` | Each link → `MenuItemData`. | +| `mj-table` | `TableBlock` | Map `
` rows/cells to Templatical's table data. | +| `mj-raw` | `HtmlBlock` | Pass-through HTML. | +| `mj-wrapper` | `SectionBlock` (often) | A wrapper without columns becomes a section with one column. | +| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (fallback) | Templatical doesn't have direct equivalents yet — convert the rendered HTML to a raw HTML block, or wait for the importer. | +| `mj-head` content | Template `settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` map to `TemplateSettings.preheaderText`, custom fonts, and theme overrides. | ## Things that don't map automatically diff --git a/apps/docs/guide/migration-from-unlayer.md b/apps/docs/guide/migration-from-unlayer.md index b2d582d..8809941 100644 --- a/apps/docs/guide/migration-from-unlayer.md +++ b/apps/docs/guide/migration-from-unlayer.md @@ -20,17 +20,19 @@ npm install @templatical/import-unlayer ## Usage ```ts -import { convertUnlayerTemplate } from '@templatical/import-unlayer'; +import { convertUnlayerTemplate } from "@templatical/import-unlayer"; // Load your Unlayer design JSON (whatever editor.saveDesign returned) -const unlayerJson = await fetch('/api/unlayer-templates/123').then(r => r.json()); +const unlayerJson = await fetch("/api/unlayer-templates/123").then((r) => + r.json(), +); // Convert to Templatical format const { content, report } = convertUnlayerTemplate(unlayerJson); // Use in the editor const editor = await init({ - container: '#editor', + container: "#editor", content, }); @@ -39,6 +41,7 @@ console.log(report); ``` The function returns an `ImportResult` with: + - `content` — the converted `TemplateContent` ready for the editor - `report` — a conversion report with the status of each content node (`converted`, `approximated`, `html-fallback`, or `skipped`) @@ -46,19 +49,19 @@ The function returns an `ImportResult` with: Unlayer content types map to Templatical equivalents: -| Unlayer Content | Templatical Block | Status | -|---|---|---| -| Text | `paragraph` | Converted | -| Heading | `title` | Converted | -| Image | `image` | Converted | -| Button | `button` | Converted | -| Divider | `divider` | Converted | -| Html | `html` | Converted | -| Menu | `menu` | Approximated (styles may differ) | -| Social | `social` | Converted (16 platforms mapped) | -| Video | `video` | Converted | -| Timer | `html` | HTML fallback (rebuild manually) | -| Form | — | Skipped | +| Unlayer Content | Templatical Block | Status | +| --------------- | ----------------- | -------------------------------- | +| Text | `paragraph` | Converted | +| Heading | `title` | Converted | +| Image | `image` | Converted | +| Button | `button` | Converted | +| Divider | `divider` | Converted | +| Html | `html` | Converted | +| Menu | `menu` | Approximated (styles may differ) | +| Social | `social` | Converted (16 platforms mapped) | +| Video | `video` | Converted | +| Timer | `html` | HTML fallback (rebuild manually) | +| Form | — | Skipped | Unknown content types are converted to HTML blocks as a fallback. @@ -66,14 +69,14 @@ Unknown content types are converted to HTML blocks as a fallback. Unlayer organizes content into rows with columns whose widths come from a `cells` weight array. These map to Templatical's `SectionBlock` with the appropriate `ColumnLayout`: -| Unlayer cells | Templatical Layout | -|---|---| -| `[1]` (single column) | flattened — no section wrapper | -| `[1, 1]` (equal halves) | `'2'` | -| `[1, 1, 1]` (equal thirds) | `'3'` | -| `[1, 2]` | `'1-2'` | -| `[2, 1]` | `'2-1'` | -| 4+ cells | flattened to single column with a warning | +| Unlayer cells | Templatical Layout | +| -------------------------- | ----------------------------------------- | +| `[1]` (single column) | flattened — no section wrapper | +| `[1, 1]` (equal halves) | `'2'` | +| `[1, 1, 1]` (equal thirds) | `'3'` | +| `[1, 2]` | `'1-2'` | +| `[2, 1]` | `'2-1'` | +| 4+ cells | flattened to single column with a warning | Cell ratios that don't match a standard layout are mapped to the closest available one. diff --git a/apps/docs/guide/programmatic-templates.md b/apps/docs/guide/programmatic-templates.md index 3f3aa25..8386a97 100644 --- a/apps/docs/guide/programmatic-templates.md +++ b/apps/docs/guide/programmatic-templates.md @@ -12,7 +12,7 @@ Templatical provides factory functions for every block type. Use them to build t ## Blank template ```ts -import { createDefaultTemplateContent } from '@templatical/types'; +import { createDefaultTemplateContent } from "@templatical/types"; const content = createDefaultTemplateContent(); // { blocks: [], settings: { width: 600, backgroundColor: '#ffffff', fontFamily: 'Arial' } } @@ -21,7 +21,7 @@ const content = createDefaultTemplateContent(); `createDefaultTemplateContent()` accepts an optional font family string: ```ts -const content = createDefaultTemplateContent('Georgia, serif'); +const content = createDefaultTemplateContent("Georgia, serif"); ``` ## Building a template @@ -36,7 +36,7 @@ import { createImageBlock, createButtonBlock, createDividerBlock, -} from '@templatical/types'; +} from "@templatical/types"; const content = createDefaultTemplateContent(); @@ -46,19 +46,19 @@ content.blocks = [ level: 1, }), createImageBlock({ - src: 'https://example.com/hero.jpg', - alt: 'Welcome hero image', - width: 'full', + src: "https://example.com/hero.jpg", + alt: "Welcome hero image", + width: "full", }), createDividerBlock(), createParagraphBlock({ - content: '

Thanks for signing up. Here is what happens next.

', + content: "

Thanks for signing up. Here is what happens next.

", }), createButtonBlock({ - text: 'Get Started', - url: 'https://example.com/dashboard', - backgroundColor: '#1a73e8', - textColor: '#ffffff', + text: "Get Started", + url: "https://example.com/dashboard", + backgroundColor: "#1a73e8", + textColor: "#ffffff", borderRadius: 6, }), ]; @@ -70,56 +70,56 @@ content.blocks = [ ```ts createTitleBlock({ - content: '

Welcome, {{name}}!

', + content: "

Welcome, {{name}}!

", level: 1, - textAlign: 'center', -}) + textAlign: "center", +}); ``` ### Paragraph ```ts createParagraphBlock({ - content: '

Thanks for signing up. Here is what happens next.

', -}) + content: "

Thanks for signing up. Here is what happens next.

", +}); ``` ### Image ```ts createImageBlock({ - src: 'https://cdn.example.com/hero.png', - alt: 'Hero banner', + src: "https://cdn.example.com/hero.png", + alt: "Hero banner", width: 560, - linkUrl: 'https://example.com', -}) + linkUrl: "https://example.com", +}); ``` ### Button ```ts createButtonBlock({ - text: 'Get Started', - url: 'https://example.com/signup', - backgroundColor: '#6366f1', + text: "Get Started", + url: "https://example.com/signup", + backgroundColor: "#6366f1", borderRadius: 8, -}) +}); ``` ### Divider ```ts createDividerBlock({ - lineStyle: 'dashed', - color: '#e5e7eb', + lineStyle: "dashed", + color: "#e5e7eb", thickness: 2, -}) +}); ``` ### Spacer ```ts -createSpacerBlock({ height: 40 }) +createSpacerBlock({ height: 40 }); ``` ### HTML @@ -127,20 +127,24 @@ createSpacerBlock({ height: 40 }) ```ts createHtmlBlock({ content: '
Custom markup
', -}) +}); ``` ### Social Icons ```ts createSocialIconsBlock({ - iconStyle: 'circle', - iconSize: 'large', + iconStyle: "circle", + iconSize: "large", icons: [ - { id: crypto.randomUUID(), platform: 'twitter', url: 'https://x.com/acme' }, - { id: crypto.randomUUID(), platform: 'github', url: 'https://github.com/acme' }, + { id: crypto.randomUUID(), platform: "twitter", url: "https://x.com/acme" }, + { + id: crypto.randomUUID(), + platform: "github", + url: "https://github.com/acme", + }, ], -}) +}); ``` ### Menu @@ -148,12 +152,33 @@ createSocialIconsBlock({ ```ts createMenuBlock({ items: [ - { id: crypto.randomUUID(), text: 'Home', url: 'https://example.com', openInNewTab: false, bold: false, underline: false }, - { id: crypto.randomUUID(), text: 'Blog', url: 'https://example.com/blog', openInNewTab: false, bold: false, underline: false }, - { id: crypto.randomUUID(), text: 'Docs', url: 'https://docs.example.com', openInNewTab: true, bold: false, underline: false }, + { + id: crypto.randomUUID(), + text: "Home", + url: "https://example.com", + openInNewTab: false, + bold: false, + underline: false, + }, + { + id: crypto.randomUUID(), + text: "Blog", + url: "https://example.com/blog", + openInNewTab: false, + bold: false, + underline: false, + }, + { + id: crypto.randomUUID(), + text: "Docs", + url: "https://docs.example.com", + openInNewTab: true, + bold: false, + underline: false, + }, ], - separator: '-', -}) + separator: "-", +}); ``` ### Table @@ -162,33 +187,51 @@ createMenuBlock({ createTableBlock({ hasHeaderRow: true, rows: [ - { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Plan' }, { id: crypto.randomUUID(), content: 'Price' }] }, - { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Starter' }, { id: crypto.randomUUID(), content: '$9/mo' }] }, - { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Pro' }, { id: crypto.randomUUID(), content: '$29/mo' }] }, + { + id: crypto.randomUUID(), + cells: [ + { id: crypto.randomUUID(), content: "Plan" }, + { id: crypto.randomUUID(), content: "Price" }, + ], + }, + { + id: crypto.randomUUID(), + cells: [ + { id: crypto.randomUUID(), content: "Starter" }, + { id: crypto.randomUUID(), content: "$9/mo" }, + ], + }, + { + id: crypto.randomUUID(), + cells: [ + { id: crypto.randomUUID(), content: "Pro" }, + { id: crypto.randomUUID(), content: "$29/mo" }, + ], + }, ], -}) +}); ``` ### Video ```ts createVideoBlock({ - url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', - thumbnailUrl: 'https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg', - alt: 'Product demo video', -}) + url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + thumbnailUrl: "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", + alt: "Product demo video", +}); ``` ### Section ```ts createSectionBlock({ - columns: '2', + columns: "2", children: [ - [createParagraphBlock({ content: '

Left column

' })], - [createImageBlock({ src: 'https://cdn.example.com/photo.jpg' })], + [createParagraphBlock({ content: "

Left column

" })], + [createImageBlock({ src: "https://cdn.example.com/photo.jpg" })], ], -}) +}); ``` The `columns` property accepts: `'1'` (single), `'2'` (two equal), `'3'` (three equal), `'2-1'` (two-thirds / one-third), `'1-2'` (one-third / two-thirds). See [Sections and Columns](/guide/sections-and-columns) for full details. @@ -204,9 +247,9 @@ The `columns` property accepts: `'1'` (single), `'2'` (two equal), `'3'` (three Create any block by type string: ```ts -import { createBlock } from '@templatical/types'; +import { createBlock } from "@templatical/types"; -const block = createBlock('title'); // TitleBlock with defaults +const block = createBlock("title"); // TitleBlock with defaults ``` ### Cloning @@ -214,7 +257,7 @@ const block = createBlock('title'); // TitleBlock with defaults Deep-clone a block with a new ID: ```ts -import { cloneBlock } from '@templatical/types'; +import { cloneBlock } from "@templatical/types"; const copy = cloneBlock(existingBlock); // copy.id !== existingBlock.id @@ -225,7 +268,13 @@ const copy = cloneBlock(existingBlock); Narrow a `Block` union to a specific type: ```ts -import { isTitle, isParagraph, isImage, isButton, isSection } from '@templatical/types'; +import { + isTitle, + isParagraph, + isImage, + isButton, + isSection, +} from "@templatical/types"; if (isTitle(block)) { console.log(block.level); // TypeScript knows this is TitleBlock @@ -250,17 +299,17 @@ Template settings control the global properties of the email: const content = createDefaultTemplateContent(); content.settings.width = 640; -content.settings.backgroundColor = '#f5f5f5'; -content.settings.fontFamily = 'Helvetica, Arial, sans-serif'; -content.settings.preheaderText = 'Your weekly digest is here'; +content.settings.backgroundColor = "#f5f5f5"; +content.settings.fontFamily = "Helvetica, Arial, sans-serif"; +content.settings.preheaderText = "Your weekly digest is here"; ``` -| Setting | Type | Description | -|---|---|---| -| `width` | `number` | Email width in pixels | -| `backgroundColor` | `string` | Outer background color | -| `fontFamily` | `string` | Default font stack | -| `preheaderText` | `string` | Preview text shown in inbox list | +| Setting | Type | Description | +| ----------------- | -------- | -------------------------------- | +| `width` | `number` | Email width in pixels | +| `backgroundColor` | `string` | Outer background color | +| `fontFamily` | `string` | Default font stack | +| `preheaderText` | `string` | Preview text shown in inbox list | For default values and how to customize them, see [Block & Template Defaults](/guide/defaults). @@ -269,10 +318,10 @@ For default values and how to customize them, see [Block & Template Defaults](/g Pass previously saved JSON back to the editor: ```ts -const saved = await fetch('/api/templates/123').then(r => r.json()); +const saved = await fetch("/api/templates/123").then((r) => r.json()); const editor = await init({ - container: '#editor', + container: "#editor", content: saved, }); ``` diff --git a/apps/docs/guide/sections-and-columns.md b/apps/docs/guide/sections-and-columns.md index b7c1890..db8907a 100644 --- a/apps/docs/guide/sections-and-columns.md +++ b/apps/docs/guide/sections-and-columns.md @@ -15,16 +15,16 @@ Stick to 1-2 columns for most emails. Three-column layouts become cramped on mob The `columns` property accepts one of five layout presets: -| Value | Description | Column widths | -|-------|-------------|---------------| -| `'1'` | Single column | 100% | -| `'2'` | Two equal columns | 50% / 50% | -| `'3'` | Three equal columns | 33% / 33% / 33% | -| `'2-1'` | Two-thirds / one-third | 66% / 33% | -| `'1-2'` | One-third / two-thirds | 33% / 66% | +| Value | Description | Column widths | +| ------- | ---------------------- | --------------- | +| `'1'` | Single column | 100% | +| `'2'` | Two equal columns | 50% / 50% | +| `'3'` | Three equal columns | 33% / 33% / 33% | +| `'2-1'` | Two-thirds / one-third | 66% / 33% | +| `'1-2'` | One-third / two-thirds | 33% / 66% | ```ts -type ColumnLayout = '1' | '2' | '3' | '2-1' | '1-2'; +type ColumnLayout = "1" | "2" | "3" | "2-1" | "1-2"; ``` ## Creating sections @@ -37,19 +37,19 @@ import { createTitleBlock, createParagraphBlock, createImageBlock, -} from '@templatical/types'; +} from "@templatical/types"; // Empty two-column section -const section = createSectionBlock({ columns: '2' }); +const section = createSectionBlock({ columns: "2" }); // Section with pre-populated columns const hero = createSectionBlock({ - columns: '1-2', + columns: "1-2", children: [ - [createImageBlock({ src: 'https://cdn.example.com/logo.png', width: 120 })], + [createImageBlock({ src: "https://cdn.example.com/logo.png", width: 120 })], [ - createTitleBlock({ content: '

Welcome

', level: 1 }), - createParagraphBlock({ content: '

Get started in minutes.

' }), + createTitleBlock({ content: "

Welcome

", level: 1 }), + createParagraphBlock({ content: "

Get started in minutes.

" }), ], ], }); @@ -62,15 +62,15 @@ const hero = createSectionBlock({ ```ts // For a '2' layout: section.children = [ - [blockA, blockB], // Left column - [blockC], // Right column + [blockA, blockB], // Left column + [blockC], // Right column ]; // For a '3' layout: section.children = [ - [blockA], // Left - [blockB], // Center - [blockC], // Right + [blockA], // Left + [blockB], // Center + [blockC], // Right ]; ``` @@ -84,8 +84,8 @@ To programmatically add a block to a specific column: // Add a button to the second column (index 1) section.children[1].push( createButtonBlock({ - text: 'Learn More', - url: 'https://example.com/docs', + text: "Learn More", + url: "https://example.com/docs", }), ); ``` @@ -102,7 +102,7 @@ You can use the `visibility` property on individual blocks within columns to sho ```ts const block = createParagraphBlock({ - content: '

Desktop only sidebar content

', + content: "

Desktop only sidebar content

", }); block.visibility = { @@ -119,10 +119,10 @@ See [Styling](/guide/styling) for more on responsive overrides and block visibil Sections support the same `BlockStyles` as other blocks. Common use cases include setting a background color or padding on the entire row: ```ts -const section = createSectionBlock({ columns: '1' }); +const section = createSectionBlock({ columns: "1" }); section.styles = { - backgroundColor: '#f8fafc', + backgroundColor: "#f8fafc", padding: { top: 32, right: 24, bottom: 32, left: 24 }, margin: { top: 0, right: 0, bottom: 0, left: 0 }, }; diff --git a/apps/docs/guide/shadow-dom.md b/apps/docs/guide/shadow-dom.md new file mode 100644 index 0000000..4a2508a --- /dev/null +++ b/apps/docs/guide/shadow-dom.md @@ -0,0 +1,197 @@ +--- +title: Shadow DOM +description: How Templatical isolates the editor from host page CSS using Shadow DOM, and when to opt out. +--- + +# Shadow DOM + +Templatical mounts inside a [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) by default. The shadow boundary isolates the editor's chrome, canvas, and rich-text content from your host page's CSS — even global resets like `* { color: red !important }` cannot cascade past it. + +This page is the canonical reference for the isolation model. If you just want to theme the editor, jump to the [theming guide](./theming). + +## How it works + +When you call `init()` or `initCloud()` and pass a container element, Templatical: + +1. Calls `container.attachShadow({ mode: 'open' })` to create a shadow root on your element. +2. Mounts the Vue app inside the shadow root instead of replacing the container's children directly. +3. Adopts a single shared `CSSStyleSheet` containing every Tailwind utility, every SFC ` - - -
- -
-
+ + + + Templatical Editor + + + +
+ +
+
- - + await fetch('/api/templates', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content, mjml }), + }); + }; + + ``` diff --git a/apps/docs/de/guide/blocks.md b/apps/docs/de/guide/blocks.md index 7b80cc7..c7bd598 100644 --- a/apps/docs/de/guide/blocks.md +++ b/apps/docs/de/guide/blocks.md @@ -13,110 +13,110 @@ Um Blöcke programmatisch zu erstellen, siehe [Programmatische Templates](/de/gu ## Den richtigen Block auswählen -| Bedarf | Block | Hinweise | -| -------------------------- | ----------------------------- | ----------------------------------------------------------------------------- | -| Überschriften, Titel | [Title](#title) | Überschriften mit fester Größe (H1-H4) mit Formatierung auf Blockebene | -| Fließtext, Absätze | [Paragraph](#paragraph) | Rich Text mit Inline-Formatierung über TipTap | -| Fotos, Banner, Logos | [Image](#image) | Optionales Link-Wrapping, responsive Breite | -| Call-to-Action | [Button](#button) | Bulletproof-Schaltflächen, die in allen E-Mail-Clients funktionieren | -| Mehrspaltiges Layout | [Section](#section) | Der einzige Block, der andere Blöcke enthält | -| Visuelle Trennung | [Divider](#divider) | Horizontale Linie mit Stiloptionen | -| Vertikaler Abstand | [Spacer](#spacer) | Leerraum zwischen Blöcken | -| Social Links | [Social Icons](#social-icons) | 16 Plattformen, 5 Icon-Stile | -| Navigationslinks | [Menu](#menu) | Horizontale Linkliste mit Trennzeichen | -| Tabellarische Daten | [Table](#table) | Datentabelle mit optionaler Kopfzeilenformatierung | -| Video-Vorschau | [Video](#video) | Klickbares Thumbnail (E-Mail-Clients unterstützen keine eingebetteten Videos) | -| Rohes Markup | [HTML](#html) | Notausgang für benutzerdefinierten Code | -| Domänenspezifische Inhalte | [Custom](#custom) | Ihre eigenen Blocktypen mit Feldern und Liquid-Templates | +| Bedarf | Block | Hinweise | +|------|-------|-------| +| Überschriften, Titel | [Title](#title) | Überschriften mit fester Größe (H1-H4) mit Formatierung auf Blockebene | +| Fließtext, Absätze | [Paragraph](#paragraph) | Rich Text mit Inline-Formatierung über TipTap | +| Fotos, Banner, Logos | [Image](#image) | Optionales Link-Wrapping, responsive Breite | +| Call-to-Action | [Button](#button) | Bulletproof-Schaltflächen, die in allen E-Mail-Clients funktionieren | +| Mehrspaltiges Layout | [Section](#section) | Der einzige Block, der andere Blöcke enthält | +| Visuelle Trennung | [Divider](#divider) | Horizontale Linie mit Stiloptionen | +| Vertikaler Abstand | [Spacer](#spacer) | Leerraum zwischen Blöcken | +| Social Links | [Social Icons](#social-icons) | 16 Plattformen, 5 Icon-Stile | +| Navigationslinks | [Menu](#menu) | Horizontale Linkliste mit Trennzeichen | +| Tabellarische Daten | [Table](#table) | Datentabelle mit optionaler Kopfzeilenformatierung | +| Video-Vorschau | [Video](#video) | Klickbares Thumbnail (E-Mail-Clients unterstützen keine eingebetteten Videos) | +| Rohes Markup | [HTML](#html) | Notausgang für benutzerdefinierten Code | +| Domänenspezifische Inhalte | [Custom](#custom) | Ihre eigenen Blocktypen mit Feldern und Liquid-Templates | ## Title Ein Überschriften-Block mit festen Größenstufen. Verwenden Sie Titel für Überschriften, Sektionsüberschriften und andere prominente Texte. -| Eigenschaft | Typ | Beschreibung | -| ------------ | ------------------------------- | ------------------------------------------------------ | -| `content` | `string` | HTML-Inhalt | -| `level` | `1 \| 2 \| 3 \| 4` | Überschriftsebene (H1=36px, H2=28px, H3=22px, H4=18px) | -| `color` | `string` | Textfarbe | -| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `content` | `string` | HTML-Inhalt | +| `level` | `1 \| 2 \| 3 \| 4` | Überschriftsebene (H1=36px, H2=28px, H3=22px, H4=18px) | +| `color` | `string` | Textfarbe | +| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | ## Paragraph Fließtext, der als HTML gerendert wird. Der Editor verwendet [Tiptap](https://tiptap.dev) für die Inline-Bearbeitung mit Formatierungssteuerungen (fett, kursiv, Links, Ausrichtung, Schriftgröße, Farbe usw.). Jede Formatierung wird inline angewendet -- es gibt keine Formatierungseigenschaften auf Blockebene. -| Eigenschaft | Typ | Beschreibung | -| ----------- | -------- | ------------ | -| `content` | `string` | HTML-Inhalt | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `content` | `string` | HTML-Inhalt | ## Image Zeigt ein Bild mit optionalem Link-Wrapping an. -| Eigenschaft | Typ | Beschreibung | -| ------------------ | ------------------------------- | ----------------------------------------------------------------------------- | -| `src` | `string` | Bild-URL | -| `alt` | `string` | Alternativtext | -| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `linkUrl` | `string` | Umschließt das Bild mit einem Link | -| `linkOpenInNewTab` | `boolean` | Verhalten des Linkziels | -| `placeholderUrl` | `string` | Platzhalter, der im Editor angezeigt wird, wenn `src` ein Merge-Tag verwendet | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `src` | `string` | Bild-URL | +| `alt` | `string` | Alternativtext | +| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `linkUrl` | `string` | Umschließt das Bild mit einem Link | +| `linkOpenInNewTab` | `boolean` | Verhalten des Linkziels | +| `placeholderUrl` | `string` | Platzhalter, der im Editor angezeigt wird, wenn `src` ein Merge-Tag verwendet | ## Button Eine Call-to-Action-Schaltfläche mit anpassbarem Erscheinungsbild. -| Eigenschaft | Typ | Beschreibung | -| ----------------- | -------------- | --------------------------------- | -| `text` | `string` | Schaltflächentext | -| `url` | `string` | Link-URL | -| `backgroundColor` | `string` | Hintergrundfarbe der Schaltfläche | -| `textColor` | `string` | Textfarbe der Schaltfläche | -| `borderRadius` | `number` | Eckenradius in px | -| `fontSize` | `number` | Schriftgröße in px | -| `buttonPadding` | `SpacingValue` | Innerer Abstand | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | -| `openInNewTab` | `boolean` | Verhalten des Linkziels | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `text` | `string` | Schaltflächentext | +| `url` | `string` | Link-URL | +| `backgroundColor` | `string` | Hintergrundfarbe der Schaltfläche | +| `textColor` | `string` | Textfarbe der Schaltfläche | +| `borderRadius` | `number` | Eckenradius in px | +| `fontSize` | `number` | Schriftgröße in px | +| `buttonPadding` | `SpacingValue` | Innerer Abstand | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| `openInNewTab` | `boolean` | Verhalten des Linkziels | ## Divider Ein horizontaler Linientrenner. -| Eigenschaft | Typ | Beschreibung | -| ----------- | --------------------------------- | ----------------------------------------- | -| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Linienstil | -| `color` | `string` | Linienfarbe | -| `thickness` | `number` | Liniendicke in px | -| `width` | `number \| 'full'` | Linienbreite in px oder `'full'` für 100% | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Linienstil | +| `color` | `string` | Linienfarbe | +| `thickness` | `number` | Liniendicke in px | +| `width` | `number \| 'full'` | Linienbreite in px oder `'full'` für 100% | ## Spacer Leerer vertikaler Raum. -| Eigenschaft | Typ | Beschreibung | -| ----------- | -------- | ------------ | -| `height` | `number` | Höhe in px | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `height` | `number` | Höhe in px | ## HTML Fügt rohes HTML in das Template ein. Verwenden Sie dies für Inhalte, die mit anderen Blocktypen nicht ausgedrückt werden können. -| Eigenschaft | Typ | Beschreibung | -| ----------- | -------- | ----------------- | -| `content` | `string` | Rohes HTML-Markup | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `content` | `string` | Rohes HTML-Markup | ## Social Icons Eine Reihe von Social-Media-Icons, die zu Plattformprofilen verlinken. -| Eigenschaft | Typ | Beschreibung | -| ----------- | ------------------------------------------------------------ | ---------------------------- | -| `icons` | `SocialIcon[]` | Liste der Social Icons | -| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visueller Stil | -| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon-Größe | -| `spacing` | `number` | Abstand zwischen Icons in px | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `icons` | `SocialIcon[]` | Liste der Social Icons | +| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visueller Stil | +| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon-Größe | +| `spacing` | `number` | Abstand zwischen Icons in px | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | 16 Plattformen werden unterstützt: Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, Pinterest, E-Mail, WhatsApp, Telegram, Discord, Snapchat, Reddit, GitHub, Dribbble und Behance. @@ -134,17 +134,17 @@ interface SocialIcon { Ein horizontales Navigationsmenü mit Textlinks. -| Eigenschaft | Typ | Beschreibung | -| ---------------- | ------------------------------- | --------------------------------- | -| `items` | `MenuItemData[]` | Menüeinträge | -| `fontSize` | `number` | Schriftgröße in px | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | -| `color` | `string` | Textfarbe | -| `linkColor` | `string` (optional) | Linkfarbe | -| `textAlign` | `'left' \| 'center' \| 'right'` | Ausrichtung | -| `separator` | `string` | Zeichen zwischen Einträgen | -| `separatorColor` | `string` | Farbe des Trennzeichens | -| `spacing` | `number` | Abstand um das Trennzeichen | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `items` | `MenuItemData[]` | Menüeinträge | +| `fontSize` | `number` | Schriftgröße in px | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| `color` | `string` | Textfarbe | +| `linkColor` | `string` (optional) | Linkfarbe | +| `textAlign` | `'left' \| 'center' \| 'right'` | Ausrichtung | +| `separator` | `string` | Zeichen zwischen Einträgen | +| `separatorColor` | `string` | Farbe des Trennzeichens | +| `spacing` | `number` | Abstand um das Trennzeichen | Jedes `MenuItemData` hat: @@ -164,18 +164,18 @@ interface MenuItemData { Eine Datentabelle mit optionaler Formatierung der Kopfzeile. -| Eigenschaft | Typ | Beschreibung | -| ----------------------- | ------------------------------- | ------------------------------------- | -| `rows` | `TableRowData[]` | Tabellenzeilen | -| `hasHeaderRow` | `boolean` | Erste Zeile als Kopfzeile formatieren | -| `headerBackgroundColor` | `string` (optional) | Hintergrund der Kopfzeile | -| `borderColor` | `string` | Rahmenfarbe | -| `borderWidth` | `number` | Rahmenbreite in px | -| `cellPadding` | `number` | Zellenabstand in px | -| `fontSize` | `number` | Schriftgröße in px | -| `fontFamily` | `string` | Überschreibung der Schriftfamilie | -| `color` | `string` | Textfarbe | -| `textAlign` | `'left' \| 'center' \| 'right'` | Textausrichtung der Zelle | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `rows` | `TableRowData[]` | Tabellenzeilen | +| `hasHeaderRow` | `boolean` | Erste Zeile als Kopfzeile formatieren | +| `headerBackgroundColor` | `string` (optional) | Hintergrund der Kopfzeile | +| `borderColor` | `string` | Rahmenfarbe | +| `borderWidth` | `number` | Rahmenbreite in px | +| `cellPadding` | `number` | Zellenabstand in px | +| `fontSize` | `number` | Schriftgröße in px | +| `fontFamily` | `string` | Überschreibung der Schriftfamilie | +| `color` | `string` | Textfarbe | +| `textAlign` | `'left' \| 'center' \| 'right'` | Textausrichtung der Zelle | ## Video @@ -185,32 +185,32 @@ Zeigt ein Video-Thumbnail an, das zur Video-URL verlinkt. E-Mail-Clients unterstützen keine eingebettete Videowiedergabe. Der Renderer gibt ein klickbares Thumbnail-Bild aus, das zur Video-URL verlinkt. Stellen Sie immer eine gute `thumbnailUrl` bereit -- das ist das Einzige, was Empfänger in ihrem Posteingang sehen. ::: -| Eigenschaft | Typ | Beschreibung | -| ---------------- | ------------------------------- | ------------------------------------------ | -| `url` | `string` | Video-URL (YouTube, Vimeo usw.) | -| `thumbnailUrl` | `string` | URL des Thumbnail-Bildes | -| `alt` | `string` | Alternativtext für das Thumbnail | -| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `openInNewTab` | `boolean` | Verhalten des Linkziels | -| `placeholderUrl` | `string` | Nur im Editor sichtbarer Platzhalter | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `url` | `string` | Video-URL (YouTube, Vimeo usw.) | +| `thumbnailUrl` | `string` | URL des Thumbnail-Bildes | +| `alt` | `string` | Alternativtext für das Thumbnail | +| `width` | `number \| 'full'` | Anzeigebreite in px oder `'full'` für 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `openInNewTab` | `boolean` | Verhalten des Linkziels | +| `placeholderUrl` | `string` | Nur im Editor sichtbarer Platzhalter | ## Section Ein Layout-Container, der eine oder mehrere Spalten enthält. Siehe [Sektionen und Spalten](/de/guide/sections-and-columns) für alle Details. -| Eigenschaft | Typ | Beschreibung | -| ----------- | -------------- | ---------------------------------------- | -| `columns` | `ColumnLayout` | Preset für das Spaltenlayout | -| `children` | `Block[][]` | Array von Block-Arrays, eines pro Spalte | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `columns` | `ColumnLayout` | Preset für das Spaltenlayout | +| `children` | `Block[][]` | Array von Block-Arrays, eines pro Spalte | ## Custom Ein benutzerdefinierter Blocktyp, der durch Felddefinitionen und ein Liquid-Template angetrieben wird. Siehe [Benutzerdefinierte Blöcke](/de/guide/custom-blocks) für alle Details. -| Eigenschaft | Typ | Beschreibung | -| ------------------- | ------------------------- | ------------------------------------------------------- | -| `customType` | `string` | Eindeutige Kennung für den benutzerdefinierten Blocktyp | -| `fieldValues` | `Record` | Aktuelle Werte für definierte Felder | -| `renderedHtml` | `string` | Zwischengespeichertes gerendertes Ergebnis | -| `dataSourceFetched` | `boolean` | Ob die Datenquelle abgerufen wurde | +| Eigenschaft | Typ | Beschreibung | +|----------|------|-------------| +| `customType` | `string` | Eindeutige Kennung für den benutzerdefinierten Blocktyp | +| `fieldValues` | `Record` | Aktuelle Werte für definierte Felder | +| `renderedHtml` | `string` | Zwischengespeichertes gerendertes Ergebnis | +| `dataSourceFetched` | `boolean` | Ob die Datenquelle abgerufen wurde | diff --git a/apps/docs/de/guide/custom-blocks.md b/apps/docs/de/guide/custom-blocks.md index 3ef3126..c32dad6 100644 --- a/apps/docs/de/guide/custom-blocks.md +++ b/apps/docs/de/guide/custom-blocks.md @@ -16,36 +16,23 @@ Benutzerdefinierte Blöcke rendern standardmäßig innerhalb des Shadow DOM des Übergeben Sie benutzerdefinierte Blockdefinitionen über die Editor-Konfiguration. Das folgende Beispiel erstellt einen "Testimonial"-Block mit einem Zitat, Autorendetails, Avatar und einer Sternebewertung. Nach der Registrierung können Benutzer ihn aus der Block-Palette in ihr Template ziehen und jedes Feld im Einstellungsbereich bearbeiten. ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', customBlocks: [ { - type: "testimonial", - name: "Testimonial", + type: 'testimonial', + name: 'Testimonial', icon: '', - description: "Customer quote with photo and rating", + description: 'Customer quote with photo and rating', fields: [ - { key: "quote", label: "Quote", type: "textarea" }, - { key: "authorName", label: "Author Name", type: "text" }, - { key: "authorTitle", label: "Author Title", type: "text" }, - { key: "avatar", label: "Avatar", type: "image" }, - { - key: "rating", - label: "Rating (1-5)", - type: "number", - min: 1, - max: 5, - step: 1, - default: 5, - }, - { - key: "showRating", - label: "Show Rating", - type: "boolean", - default: true, - }, + { key: 'quote', label: 'Quote', type: 'textarea' }, + { key: 'authorName', label: 'Author Name', type: 'text' }, + { key: 'authorTitle', label: 'Author Title', type: 'text' }, + { key: 'avatar', label: 'Avatar', type: 'image' }, + { key: 'rating', label: 'Rating (1-5)', type: 'number', min: 1, max: 5, step: 1, default: 5 }, + { key: 'showRating', label: 'Show Rating', type: 'boolean', default: true }, ], template: ` @@ -112,15 +99,15 @@ interface CustomBlockDefinition { } ``` -| Eigenschaft | Erforderlich | Beschreibung | -| ------------- | ------------ | ------------------------------------------------------------------------ | -| `type` | Ja | Eindeutige Kennung (wird als `customType` in Block-Instanzen verwendet) | -| `name` | Ja | Anzeigename in der Block-Palette | -| `icon` | Nein | Inline-SVG-String, Bild-URL oder base64-Daten-URI für das Palettensymbol | -| `description` | Nein | Tooltip oder Untertitel in der Palette | -| `fields` | Ja | Array von Felddefinitionen | -| `template` | Ja | Liquid-Template-String für das Rendering | -| `dataSource` | Nein | Konfiguration für das Abrufen externer Daten | +| Eigenschaft | Erforderlich | Beschreibung | +|----------|----------|-------------| +| `type` | Ja | Eindeutige Kennung (wird als `customType` in Block-Instanzen verwendet) | +| `name` | Ja | Anzeigename in der Block-Palette | +| `icon` | Nein | Inline-SVG-String, Bild-URL oder base64-Daten-URI für das Palettensymbol | +| `description` | Nein | Tooltip oder Untertitel in der Palette | +| `fields` | Ja | Array von Felddefinitionen | +| `template` | Ja | Liquid-Template-String für das Rendering | +| `dataSource` | Nein | Konfiguration für das Abrufen externer Daten | ## Feldtypen @@ -138,16 +125,16 @@ interface CustomBlockFieldBase { Alle Feldtypen erweitern diese Basis. Der `key` wird als Variablenname in Ihrem Liquid-Template verwendet. Zusätzliche Eigenschaften hängen vom Feld-`type` ab: -| Eigenschaft | Gilt für | Beschreibung | -| ---------------------- | ------------ | ---------------------------------------------------------- | -| `required` | Alle | Feld als erforderlich markieren | -| `placeholder` | Alle | Platzhaltertext für die Eingabe | -| `readOnly` | Alle | Benutzerbearbeitung verhindern (nützlich bei Datenquellen) | -| `default` | Alle | Standardwert bei Erstellung des Blocks | -| `min`, `max`, `step` | `number` | Numerische Einschränkungen | -| `options` | `select` | Array von `{ label, value }`-Auswahlmöglichkeiten | -| `fields` | `repeatable` | Unterfelddefinitionen | -| `minItems`, `maxItems` | `repeatable` | Grenzen für die Anzahl der Einträge | +| Eigenschaft | Gilt für | Beschreibung | +|----------|------------|-------------| +| `required` | Alle | Feld als erforderlich markieren | +| `placeholder` | Alle | Platzhaltertext für die Eingabe | +| `readOnly` | Alle | Benutzerbearbeitung verhindern (nützlich bei Datenquellen) | +| `default` | Alle | Standardwert bei Erstellung des Blocks | +| `min`, `max`, `step` | `number` | Numerische Einschränkungen | +| `options` | `select` | Array von `{ label, value }`-Auswahlmöglichkeiten | +| `fields` | `repeatable` | Unterfelddefinitionen | +| `minItems`, `maxItems` | `repeatable` | Grenzen für die Anzahl der Einträge | ### text @@ -321,9 +308,7 @@ Benutzerdefinierte Blöcke werden noch leistungsfähiger, wenn sie durch eine AP ```ts interface DataSourceConfig { label: string; - onFetch: ( - context: DataSourceFetchContext, - ) => Promise | null>; + onFetch: (context: DataSourceFetchContext) => Promise | null>; } interface DataSourceFetchContext { @@ -385,36 +370,26 @@ Ein Einladungsblock für eine Veranstaltung mit einem Zeitplan, der mit wiederho ```ts const eventCard: CustomBlockDefinition = { - type: "event-card", - name: "Event Card", + type: 'event-card', + name: 'Event Card', icon: '', - description: "Event details with schedule and RSVP", + description: 'Event details with schedule and RSVP', fields: [ + { key: 'eventName', label: 'Event Name', type: 'text', default: 'Untitled Event' }, + { key: 'date', label: 'Date', type: 'text', default: 'January 1, 2026' }, + { key: 'venue', label: 'Venue', type: 'text' }, + { key: 'venueAddress', label: 'Venue Address', type: 'text' }, + { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, + { key: 'rsvpUrl', label: 'RSVP URL', type: 'text' }, { - key: "eventName", - label: "Event Name", - type: "text", - default: "Untitled Event", - }, - { key: "date", label: "Date", type: "text", default: "January 1, 2026" }, - { key: "venue", label: "Venue", type: "text" }, - { key: "venueAddress", label: "Venue Address", type: "text" }, - { - key: "accentColor", - label: "Accent Color", - type: "color", - default: "#4f46e5", - }, - { key: "rsvpUrl", label: "RSVP URL", type: "text" }, - { - key: "schedule", - label: "Schedule", - type: "repeatable", + key: 'schedule', + label: 'Schedule', + type: 'repeatable', minItems: 1, maxItems: 10, fields: [ - { key: "time", label: "Time", type: "text" }, - { key: "session", label: "Session", type: "text" }, + { key: 'time', label: 'Time', type: 'text' }, + { key: 'session', label: 'Session', type: 'text' }, ], }, ], @@ -447,39 +422,26 @@ Ein Preisblock mit einer Funktionsliste und CTA-Schaltfläche: ```ts const pricingTier: CustomBlockDefinition = { - type: "pricing-tier", - name: "Pricing Tier", + type: 'pricing-tier', + name: 'Pricing Tier', icon: '', - description: "Pricing card with features list", + description: 'Pricing card with features list', fields: [ - { key: "planName", label: "Plan Name", type: "text", default: "Pro" }, - { key: "price", label: "Price", type: "text", default: "$29/mo" }, - { - key: "highlighted", - label: "Highlighted", - type: "boolean", - default: false, - }, + { key: 'planName', label: 'Plan Name', type: 'text', default: 'Pro' }, + { key: 'price', label: 'Price', type: 'text', default: '$29/mo' }, + { key: 'highlighted', label: 'Highlighted', type: 'boolean', default: false }, + { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, + { key: 'ctaLabel', label: 'Button Label', type: 'text', default: 'Get Started' }, + { key: 'ctaUrl', label: 'Button URL', type: 'text' }, { - key: "accentColor", - label: "Accent Color", - type: "color", - default: "#4f46e5", - }, - { - key: "ctaLabel", - label: "Button Label", - type: "text", - default: "Get Started", - }, - { key: "ctaUrl", label: "Button URL", type: "text" }, - { - key: "features", - label: "Features", - type: "repeatable", + key: 'features', + label: 'Features', + type: 'repeatable', minItems: 1, maxItems: 8, - fields: [{ key: "text", label: "Feature", type: "text" }], + fields: [ + { key: 'text', label: 'Feature', type: 'text' }, + ], }, ], template: ` diff --git a/apps/docs/de/guide/defaults.md b/apps/docs/de/guide/defaults.md index a57f4e8..80f103d 100644 --- a/apps/docs/de/guide/defaults.md +++ b/apps/docs/de/guide/defaults.md @@ -12,31 +12,29 @@ Blockeigenschaften (Farben, Schriftgrößen, Padding, Platzhaltertexte usw.) sin Übergeben Sie ein `blockDefaults`-Objekt an `init()`. Jeder Schlüssel wird einem Blocktyp zugeordnet und akzeptiert eine partielle Überschreibung der Eigenschaften dieses Blocks: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', blockDefaults: { - title: { color: "#000000" }, + title: { color: '#000000' }, paragraph: {}, button: { - backgroundColor: "#ff6600", - textColor: "#ffffff", + backgroundColor: '#ff6600', + textColor: '#ffffff', styles: { padding: { top: 20, bottom: 20 } }, }, - divider: { color: "#eeeeee", thickness: 2 }, - image: { alt: "Brand image" }, + divider: { color: '#eeeeee', thickness: 2 }, + image: { alt: 'Brand image' }, }, }); ``` Standardwerte werden angewendet bei: - - Ziehen eines Blocks aus der Seitenleiste - Programmatischem Aufruf von `createAndAddBlock()` Standardwerte werden **nicht** angewendet bei: - - Duplizieren eines vorhandenen Blocks (die Werte des Quellblocks bleiben erhalten) - Laden gespeicherter Inhalte aus der API @@ -59,29 +57,29 @@ Arrays werden **ersetzt**, nicht zusammengeführt. Beispielsweise ersetzt das Se ### Unterstützte Blocktypen -| Schlüssel | Blocktyp | -| ----------- | ------------ | -| `title` | Title | -| `paragraph` | Paragraph | -| `image` | Image | -| `button` | Button | -| `divider` | Divider | -| `section` | Section | -| `video` | Video | -| `social` | Social Icons | -| `spacer` | Spacer | -| `html` | HTML | -| `menu` | Menu | -| `table` | Table | +| Schlüssel | Blocktyp | +|-----|-----------| +| `title` | Title | +| `paragraph` | Paragraph | +| `image` | Image | +| `button` | Button | +| `divider` | Divider | +| `section` | Section | +| `video` | Video | +| `social` | Social Icons | +| `spacer` | Spacer | +| `html` | HTML | +| `menu` | Menu | +| `table` | Table | Benutzerdefinierte Blöcke sind von `blockDefaults` nicht betroffen. Sie verwenden ihre eigenen `default`-Werte, die in der `CustomBlockDefinition`-Feldkonfiguration definiert sind. ### TypeScript-Typ ```ts -import type { BlockDefaults } from "@templatical/editor"; +import type { BlockDefaults } from '@templatical/editor'; // oder -import type { BlockDefaults } from "@templatical/types"; +import type { BlockDefaults } from '@templatical/types'; ``` Jeder Blocktyp-Schlüssel akzeptiert `Partial>` — Sie können jede Eigenschaft außer `id` und `type` überschreiben, die immer automatisch generiert werden. Siehe [Blocktypen](/de/guide/blocks) für die vollständige Liste der verfügbaren Eigenschaften pro Block. @@ -92,12 +90,12 @@ Jeder Blocktyp-Schlüssel akzeptiert `Partial>` ```ts const editor = await init({ - container: "#editor", + container: '#editor', templateDefaults: { width: 640, - backgroundColor: "#f5f5f5", - fontFamily: "Helvetica, sans-serif", - preheaderText: "Check out our latest news", + backgroundColor: '#f5f5f5', + fontFamily: 'Helvetica, sans-serif', + preheaderText: 'Check out our latest news', }, }); ``` @@ -111,19 +109,19 @@ Mit anderen Worten, `templateDefaults` sind Fallbacks für fehlenden Inhalt, kei ### Verfügbare Einstellungen -| Eigenschaft | Standard | Beschreibung | -| ----------------- | --------- | ------------------------------ | -| `width` | `600` | Template-Breite in Pixeln | +| Eigenschaft | Standard | Beschreibung | +|----------|---------|-------------| +| `width` | `600` | Template-Breite in Pixeln | | `backgroundColor` | `#ffffff` | Hintergrundfarbe des Templates | -| `fontFamily` | `Arial` | Standard-Schriftfamilie | -| `preheaderText` | — | E-Mail-Preheader-Text | +| `fontFamily` | `Arial` | Standard-Schriftfamilie | +| `preheaderText` | — | E-Mail-Preheader-Text | ### TypeScript-Typ ```ts -import type { TemplateDefaults } from "@templatical/editor"; +import type { TemplateDefaults } from '@templatical/editor'; // oder -import type { TemplateDefaults } from "@templatical/types"; +import type { TemplateDefaults } from '@templatical/types'; ``` ## Integrierte Standardkonstanten @@ -137,7 +135,7 @@ import { TITLE_BLOCK_DEFAULTS, PARAGRAPH_BLOCK_DEFAULTS, BUTTON_BLOCK_DEFAULTS, -} from "@templatical/types"; +} from '@templatical/types'; // Die Standardwerte für einen einzelnen Blocktyp inspizieren console.log(TITLE_BLOCK_DEFAULTS); @@ -150,7 +148,7 @@ console.log(DEFAULT_TEMPLATE_DEFAULTS); // Ein benutzerdefiniertes Preset erstellen, indem die Standardwerte eines einzelnen Blocks erweitert werden const myButtonDefaults = { ...BUTTON_BLOCK_DEFAULTS, - backgroundColor: "#ff6600", + backgroundColor: '#ff6600', borderRadius: 8, }; ``` @@ -159,27 +157,27 @@ const myButtonDefaults = { **Pro-Block-Konstanten** — jede enthält die Standardwerte der Eigenschaften für diesen Blocktyp (ausgenommen `id`, `type` und `styles`): -| Konstante | Blocktyp | -| ----------------------------- | ------------ | -| `TITLE_BLOCK_DEFAULTS` | Title | -| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | -| `IMAGE_BLOCK_DEFAULTS` | Image | -| `BUTTON_BLOCK_DEFAULTS` | Button | -| `DIVIDER_BLOCK_DEFAULTS` | Divider | -| `SECTION_BLOCK_DEFAULTS` | Section | -| `VIDEO_BLOCK_DEFAULTS` | Video | +| Konstante | Blocktyp | +|----------|-----------| +| `TITLE_BLOCK_DEFAULTS` | Title | +| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | +| `IMAGE_BLOCK_DEFAULTS` | Image | +| `BUTTON_BLOCK_DEFAULTS` | Button | +| `DIVIDER_BLOCK_DEFAULTS` | Divider | +| `SECTION_BLOCK_DEFAULTS` | Section | +| `VIDEO_BLOCK_DEFAULTS` | Video | | `SOCIAL_ICONS_BLOCK_DEFAULTS` | Social Icons | -| `SPACER_BLOCK_DEFAULTS` | Spacer | -| `HTML_BLOCK_DEFAULTS` | HTML | -| `MENU_BLOCK_DEFAULTS` | Menu | -| `TABLE_BLOCK_DEFAULTS` | Table | +| `SPACER_BLOCK_DEFAULTS` | Spacer | +| `HTML_BLOCK_DEFAULTS` | HTML | +| `MENU_BLOCK_DEFAULTS` | Menu | +| `TABLE_BLOCK_DEFAULTS` | Table | **Kombinierte Konstanten:** -| Konstante | Beschreibung | -| --------------------------- | -------------------------------------------------------------------------------------------------------------- | -| `DEFAULT_BLOCK_DEFAULTS` | Alle Blocktyp-Standardwerte in einem einzigen Objekt (Schlüssel entsprechen der `BlockDefaults`-Schnittstelle) | -| `DEFAULT_TEMPLATE_DEFAULTS` | Standardwerte der Template-Einstellungen (`width`, `backgroundColor`, `fontFamily`) | +| Konstante | Beschreibung | +|----------|-------------| +| `DEFAULT_BLOCK_DEFAULTS` | Alle Blocktyp-Standardwerte in einem einzigen Objekt (Schlüssel entsprechen der `BlockDefaults`-Schnittstelle) | +| `DEFAULT_TEMPLATE_DEFAULTS` | Standardwerte der Template-Einstellungen (`width`, `backgroundColor`, `fontFamily`) | Diese Konstanten sind die einzige Quelle der Wahrheit, die intern von den Factory-Funktionen verwendet wird. Wenn Sie nur einige Eigenschaften überschreiben müssen, brauchen Sie sie nicht zu referenzieren — übergeben Sie einfach Ihre Überschreibungen an `blockDefaults` und sie werden mit diesen Werten deep-merged. @@ -188,28 +186,23 @@ Diese Konstanten sind die einzige Quelle der Wahrheit, die intern von den Factor Die zugrunde liegenden Factory-Funktionen akzeptieren Standardwerte auch direkt: ```ts -import { - createBlock, - createTitleBlock, - createParagraphBlock, - createDefaultTemplateContent, -} from "@templatical/types"; -import type { BlockDefaults } from "@templatical/types"; +import { createBlock, createTitleBlock, createParagraphBlock, createDefaultTemplateContent } from '@templatical/types'; +import type { BlockDefaults } from '@templatical/types'; // Einzelner Block mit partiellen Überschreibungen -const title = createTitleBlock({ level: 2, color: "#000" }); -const paragraph = createParagraphBlock({ content: "

Hello

" }); +const title = createTitleBlock({ level: 2, color: '#000' }); +const paragraph = createParagraphBlock({ content: '

Hello

' }); // Über createBlock mit vollständiger Standardwert-Map const defaults: BlockDefaults = { - title: { color: "#000000" }, - button: { backgroundColor: "#ff6600" }, + title: { color: '#000000' }, + button: { backgroundColor: '#ff6600' }, }; -const block = createBlock("title", defaults); +const block = createBlock('title', defaults); // Template mit benutzerdefinierten Einstellungen -const content = createDefaultTemplateContent("Helvetica, sans-serif", { +const content = createDefaultTemplateContent('Helvetica, sans-serif', { width: 640, - backgroundColor: "#f5f5f5", + backgroundColor: '#f5f5f5', }); ``` diff --git a/apps/docs/de/guide/display-conditions.md b/apps/docs/de/guide/display-conditions.md index 3259b93..96c9d87 100644 --- a/apps/docs/de/guide/display-conditions.md +++ b/apps/docs/de/guide/display-conditions.md @@ -12,23 +12,23 @@ Anzeigebedingungen erlauben es Benutzern, die Sichtbarkeit von Blöcken basieren Definieren Sie verfügbare Bedingungen über die Editor-Konfiguration: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', displayConditions: { conditions: [ { - label: "VIP Customers", - before: "{% if customer.vip == true %}", - after: "{% endif %}", - description: "Only shown to VIP customers", + label: 'VIP Customers', + before: '{% if customer.vip == true %}', + after: '{% endif %}', + description: 'Only shown to VIP customers', }, { - label: "Has Active Subscription", - before: "{% if subscription.active %}", - after: "{% endif %}", - description: "Shown when user has an active subscription", + label: 'Has Active Subscription', + before: '{% if subscription.active %}', + after: '{% endif %}', + description: 'Shown when user has an active subscription', }, ], }, @@ -47,13 +47,13 @@ interface DisplayCondition { } ``` -| Eigenschaft | Erforderlich | Beschreibung | -| ------------- | ------------ | -------------------------------------------------------- | -| `label` | Ja | Anzeigename in der Editor-Oberfläche | -| `before` | Ja | Markup, das vor der Blockausgabe eingefügt wird | -| `after` | Ja | Markup, das nach der Blockausgabe eingefügt wird | -| `group` | Nein | Gruppenname zur Organisation von Bedingungen im Dropdown | -| `description` | Nein | Erklärender Text, der unter dem Label angezeigt wird | +| Eigenschaft | Erforderlich | Beschreibung | +|----------|----------|-------------| +| `label` | Ja | Anzeigename in der Editor-Oberfläche | +| `before` | Ja | Markup, das vor der Blockausgabe eingefügt wird | +| `after` | Ja | Markup, das nach der Blockausgabe eingefügt wird | +| `group` | Nein | Gruppenname zur Organisation von Bedingungen im Dropdown | +| `description` | Nein | Erklärender Text, der unter dem Label angezeigt wird | ## DisplayConditionsConfig diff --git a/apps/docs/de/guide/fonts.md b/apps/docs/de/guide/fonts.md index f362b91..76b5ecb 100644 --- a/apps/docs/de/guide/fonts.md +++ b/apps/docs/de/guide/fonts.md @@ -10,46 +10,46 @@ Standardmäßig enthält der Editor eine Reihe gängiger websicherer Schriftarte Konfigurieren Sie, welche Schriftarten verfügbar sind, über die Option `fonts`: ```ts -import type { FontsConfig } from "@templatical/types"; +import type { FontsConfig } from '@templatical/types'; const fonts: FontsConfig = { - defaultFont: "Inter", - defaultFallback: "Arial, sans-serif", + defaultFont: 'Inter', + defaultFallback: 'Arial, sans-serif', customFonts: [ { - name: "Inter", - url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap", - fallback: "Helvetica, Arial, sans-serif", + name: 'Inter', + url: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap', + fallback: 'Helvetica, Arial, sans-serif', }, { - name: "Merriweather", - url: "https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap", - fallback: "Georgia, serif", + name: 'Merriweather', + url: 'https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap', + fallback: 'Georgia, serif', }, ], }; const editor = await init({ - container: "#editor", + container: '#editor', fonts, }); ``` ## FontsConfig -| Eigenschaft | Typ | Beschreibung | -| ----------------- | -------------- | ----------------------------------------------------------------------------------------------- | -| `defaultFont` | `string` | Schriftartname, der in neuen Templates standardmäßig ausgewählt ist | -| `defaultFallback` | `string` | Fallback-Stack, der verwendet wird, wenn eine benutzerdefinierte Schriftart nicht verfügbar ist | -| `customFonts` | `CustomFont[]` | Liste benutzerdefinierter Schriftarten, die registriert werden sollen | +| Eigenschaft | Typ | Beschreibung | +|---|---|---| +| `defaultFont` | `string` | Schriftartname, der in neuen Templates standardmäßig ausgewählt ist | +| `defaultFallback` | `string` | Fallback-Stack, der verwendet wird, wenn eine benutzerdefinierte Schriftart nicht verfügbar ist | +| `customFonts` | `CustomFont[]` | Liste benutzerdefinierter Schriftarten, die registriert werden sollen | ## CustomFont -| Eigenschaft | Typ | Beschreibung | -| ----------- | -------- | --------------------------------------------------- | -| `name` | `string` | Anzeigename in der Schriftauswahl | -| `url` | `string` | URL zum Schriftart-CSS (z. B. Google-Fonts-Link) | -| `fallback` | `string` | Optionaler Fallback-Font-Stack für diese Schriftart | +| Eigenschaft | Typ | Beschreibung | +|---|---|---| +| `name` | `string` | Anzeigename in der Schriftauswahl | +| `url` | `string` | URL zum Schriftart-CSS (z. B. Google-Fonts-Link) | +| `fallback` | `string` | Optionaler Fallback-Font-Stack für diese Schriftart | Benutzerdefinierte Schriftarten werden automatisch als ``-Deklarationen in der gerenderten MJML-Ausgabe eingefügt. diff --git a/apps/docs/de/guide/i18n.md b/apps/docs/de/guide/i18n.md index 5107170..c0be1a9 100644 --- a/apps/docs/de/guide/i18n.md +++ b/apps/docs/de/guide/i18n.md @@ -12,32 +12,32 @@ Die Editor-Oberfläche unterstützt den Wechsel der Locale. Alle Labels, Tooltip Übergeben Sie die Option `locale` an `init()`: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", - locale: "de", + container: '#editor', + locale: 'de', }); ``` ## Integrierte Locales -| Code | Sprache | -| ---- | ------------------- | +| Code | Sprache | +|---|---| | `en` | Englisch (Standard) | -| `de` | Deutsch | +| `de` | Deutsch | ## Locale-Auflösung Der Editor normalisiert Locale-Codes, indem er Regionssuffixe entfernt: -| Eingabe | Aufgelöst | -| --------- | --------------------------------------------------- | -| `'en'` | `en` | -| `'en-US'` | `en` | -| `'en-GB'` | `en` | -| `'de-AT'` | `de` | -| `'fr'` | `en` (nicht unterstützt, fällt auf Englisch zurück) | +| Eingabe | Aufgelöst | +|---|---| +| `'en'` | `en` | +| `'en-US'` | `en` | +| `'en-GB'` | `en` | +| `'de-AT'` | `de` | +| `'fr'` | `en` (nicht unterstützt, fällt auf Englisch zurück) | Wenn die aufgelöste Locale nicht unterstützt wird, fällt der Editor stillschweigend auf Englisch zurück. @@ -49,7 +49,7 @@ Locale-Dateien werden asynchron mit dynamischem `import()` geladen. Nur die akti async function switchLocale(newLocale: string) { editor.unmount(); editor = await init({ - container: "#editor", + container: '#editor', locale: newLocale, }); } @@ -111,17 +111,17 @@ Beispielstruktur für eine neue Locale: // packages/editor/src/i18n/locales/fr.ts export default { blocks: { - paragraph: "Paragraphe", - image: "Image", - button: "Bouton", - section: "Section", - divider: "Séparateur", - spacer: "Espacement", + paragraph: 'Paragraphe', + image: 'Image', + button: 'Bouton', + section: 'Section', + divider: 'Séparateur', + spacer: 'Espacement', // ... all keys from en.ts }, toolbar: { - duplicate: "Dupliquer", - delete: "Supprimer", + duplicate: 'Dupliquer', + delete: 'Supprimer', // ... }, // ... all sections from en.ts diff --git a/apps/docs/de/guide/images.md b/apps/docs/de/guide/images.md index 649c458..0f45bdc 100644 --- a/apps/docs/de/guide/images.md +++ b/apps/docs/de/guide/images.md @@ -18,10 +18,10 @@ Wenn der Callback `onRequestMedia` bereitgestellt wird, erscheint eine Durchsuch Der Editor ruft diese Funktion auf, wenn der Benutzer auf die Schaltfläche klickt. Geben Sie ein `MediaResult`-Objekt zurück oder `null`, wenn der Benutzer abbricht. Wenn `alt` angegeben ist, füllt der Editor automatisch den Alternativtext des Bildes aus. ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', async onRequestMedia() { // Öffnen Sie Ihr eigenes Modal, Ihren Datei-Browser oder Asset-Manager const image = await openMyMediaModal(); @@ -43,19 +43,20 @@ interface MediaResult { onRequestMedia?: (context?: MediaRequestContext) => Promise; ``` + ## Eigenschaften des Bildblocks Der Typ `ImageBlock` definiert alle konfigurierbaren Eigenschaften: -| Eigenschaft | Typ | Beschreibung | -| ------------------ | ------------------------------- | ----------------------------------------------------------------- | -| `src` | `string` | Quell-URL des Bildes | -| `alt` | `string` | Alternativtext für Barrierefreiheit | -| `width` | `number \| 'full'` | Bildbreite in Pixeln oder `'full'` für 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | -| `linkUrl` | `string` (optional) | Umschließt das Bild mit einem Link | -| `linkOpenInNewTab` | `boolean` (optional) | Öffnet den Link in einem neuen Tab | -| `placeholderUrl` | `string` (optional) | Vorschaubild zur Entwurfszeit, wenn `src` ein Merge-Tag verwendet | +| Eigenschaft | Typ | Beschreibung | +|---|---|---| +| `src` | `string` | Quell-URL des Bildes | +| `alt` | `string` | Alternativtext für Barrierefreiheit | +| `width` | `number \| 'full'` | Bildbreite in Pixeln oder `'full'` für 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontale Ausrichtung | +| `linkUrl` | `string` (optional) | Umschließt das Bild mit einem Link | +| `linkOpenInNewTab` | `boolean` (optional) | Öffnet den Link in einem neuen Tab | +| `placeholderUrl` | `string` (optional) | Vorschaubild zur Entwurfszeit, wenn `src` ein Merge-Tag verwendet | ### Placeholder-URL diff --git a/apps/docs/de/guide/merge-tags.md b/apps/docs/de/guide/merge-tags.md index ee74e16..5c48b5a 100644 --- a/apps/docs/de/guide/merge-tags.md +++ b/apps/docs/de/guide/merge-tags.md @@ -20,17 +20,17 @@ Beim Hovern über ein Tag wird der Rohwert hinter dem Label angezeigt. Die Eigenschaft `syntax` ist optional und standardmäßig `'liquid'`. ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', mergeTags: { tags: [ - { label: "First Name", value: "{{first_name}}" }, - { label: "Last Name", value: "{{last_name}}" }, - { label: "Email", value: "{{email}}" }, - { label: "Company", value: "{{company.name}}" }, - { label: "Unsubscribe URL", value: "{{unsubscribe_url}}" }, + { label: 'First Name', value: '{{first_name}}' }, + { label: 'Last Name', value: '{{last_name}}' }, + { label: 'Email', value: '{{email}}' }, + { label: 'Company', value: '{{company.name}}' }, + { label: 'Unsubscribe URL', value: '{{unsubscribe_url}}' }, ], }, }); @@ -56,16 +56,15 @@ Der `value` muss die Syntax-Trennzeichen enthalten. Zum Beispiel mit Liquid-Synt Templatical enthält vier integrierte Syntax-Presets. Die Einstellung `syntax` teilt dem Editor mit, wie sowohl Daten-Tags als auch Logik-Tags im Inhalt erkannt und hervorgehoben werden sollen. Jedes Preset definiert zwei Muster: - - **Daten-Tags** -- Variable Merge-Tags wie der Name oder die E-Mail eines Empfängers - **Logik-Tags** -- Kontrollflussanweisungen wie Bedingungen und Schleifen -| Preset | Daten-Tag | Logik-Tag | Plattform | -| -------------- | --------------------------------- | ------------------------------- | ------------------------------- | -| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | -| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | -| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | -| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | +| Preset | Daten-Tag | Logik-Tag | Plattform | +|--------|----------|-----------|----------| +| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | +| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | +| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | +| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | ```ts mergeTags: { @@ -91,35 +90,34 @@ Wie Daten-Tags werden Logik-Tags unverändert im gerenderten MJML durchgereicht Beispiele für Logik-Tags nach Preset: ::: code-group - ```html [Liquid] {% if customer.vip %} -

Exclusive offer just for you!

-{% endif %} {% for item in cart.items %} -

{{item.name}} - {{item.price}}

+

Exclusive offer just for you!

+{% endif %} + +{% for item in cart.items %} +

{{item.name}} - {{item.price}}

{% endfor %} ``` - ```html [Handlebars] {{#if hasSubscription}} -

Your plan renews on {{renewal_date}}

-{{/if}} {{#each products}} -

{{this.name}}

+

Your plan renews on {{renewal_date}}

+{{/if}} + +{{#each products}} +

{{this.name}}

{{/each}} ``` - ```html [Mailchimp] *|IF:VIP|* -

VIP discount applied

+

VIP discount applied

*|END:IF|* ``` - ```html [AMPscript] %%[IF @subscriber_type == "premium"]%% -

Premium content here

+

Premium content here

%%[ENDIF]%% ``` - ::: ## Benutzerdefinierte Syntax @@ -128,8 +126,8 @@ Wenn die integrierten Presets nicht zu Ihrer Plattform passen, definieren Sie ei ```ts interface SyntaxPreset { - value: RegExp; // matches data tags like ${user.name} - logic: RegExp; // matches logic tags like $[IF ...] + value: RegExp; // matches data tags like ${user.name} + logic: RegExp; // matches logic tags like $[IF ...] } ``` @@ -165,10 +163,12 @@ Um sie explizit zu deaktivieren, setzen Sie `autocomplete: false`: ```ts const editor = await init({ - container: "#editor", + container: '#editor', mergeTags: { autocomplete: false, - tags: [{ label: "Vorname", value: "{{first_name}}" }], + tags: [ + { label: 'Vorname', value: '{{first_name}}' }, + ], }, }); ``` @@ -181,7 +181,7 @@ Für große oder kontextabhängige Tag-Listen verwenden Sie den `onRequest`-Call ```ts const editor = await init({ - container: "#editor", + container: '#editor', mergeTags: { onRequest: async () => { const tag = await showMyMergeTagPicker(); diff --git a/apps/docs/de/guide/migration-from-beefree.md b/apps/docs/de/guide/migration-from-beefree.md index 2b3f87a..14bb4f6 100644 --- a/apps/docs/de/guide/migration-from-beefree.md +++ b/apps/docs/de/guide/migration-from-beefree.md @@ -20,19 +20,17 @@ npm install @templatical/import-beefree ## Verwendung ```ts -import { convertBeeFreeTemplate } from "@templatical/import-beefree"; +import { convertBeeFreeTemplate } from '@templatical/import-beefree'; // Laden Sie Ihr BeeFree-Template-JSON -const beefreeJson = await fetch("/api/beefree-templates/123").then((r) => - r.json(), -); +const beefreeJson = await fetch('/api/beefree-templates/123').then(r => r.json()); // In das Templatical-Format konvertieren const { content, report } = convertBeeFreeTemplate(beefreeJson); // Im Editor verwenden const editor = await init({ - container: "#editor", + container: '#editor', content, }); @@ -41,7 +39,6 @@ console.log(report); ``` Die Funktion gibt ein `ImportResult` zurück mit: - - `content` — den konvertierten `TemplateContent`, bereit für den Editor - `report` — einen Konvertierungsbericht mit dem Status jedes Blocks (`converted`, `approximated`, `html-fallback` oder `skipped`) @@ -49,21 +46,21 @@ Die Funktion gibt ein `ImportResult` zurück mit: BeeFree-Blocktypen werden den Templatical-Äquivalenten zugeordnet: -| BeeFree-Modul | Templatical-Block | Status | -| ------------- | ----------------- | --------------------------------------- | -| Text | `paragraph` | Konvertiert | -| Paragraph | `paragraph` | Konvertiert | -| Heading | `title` | Konvertiert | -| List | `paragraph` | Konvertiert | -| Image | `image` | Konvertiert | -| Button | `button` | Konvertiert | -| Divider | `divider` | Konvertiert | -| Spacer | `spacer` | Konvertiert | -| Social | `social` | Konvertiert (16 Plattformen zugeordnet) | -| Html | `html` | Konvertiert | -| Menu | `menu` | Angenähert (Stile können abweichen) | -| Video | `video` | Konvertiert | -| Table | `table` | Konvertiert | +| BeeFree-Modul | Templatical-Block | Status | +|---|---|---| +| Text | `paragraph` | Konvertiert | +| Paragraph | `paragraph` | Konvertiert | +| Heading | `title` | Konvertiert | +| List | `paragraph` | Konvertiert | +| Image | `image` | Konvertiert | +| Button | `button` | Konvertiert | +| Divider | `divider` | Konvertiert | +| Spacer | `spacer` | Konvertiert | +| Social | `social` | Konvertiert (16 Plattformen zugeordnet) | +| Html | `html` | Konvertiert | +| Menu | `menu` | Angenähert (Stile können abweichen) | +| Video | `video` | Konvertiert | +| Table | `table` | Konvertiert | Unbekannte Modultypen werden als Fallback in HTML-Blöcke konvertiert. @@ -71,13 +68,13 @@ Unbekannte Modultypen werden als Fallback in HTML-Blöcke konvertiert. BeeFree organisiert Inhalte in Zeilen mit Spalten. Diese werden einem Templatical-`SectionBlock` mit dem entsprechenden `ColumnLayout` zugeordnet: -| BeeFree-Spalten | Templatical-Layout | -| ------------------ | ------------------ | -| 1 Spalte (100%) | `'1'` | -| 2 gleiche Spalten | `'2'` | -| 3 gleiche Spalten | `'3'` | -| 2 Spalten (~33/66) | `'1-2'` | -| 2 Spalten (~66/33) | `'2-1'` | +| BeeFree-Spalten | Templatical-Layout | +|---|---| +| 1 Spalte (100%) | `'1'` | +| 2 gleiche Spalten | `'2'` | +| 3 gleiche Spalten | `'3'` | +| 2 Spalten (~33/66) | `'1-2'` | +| 2 Spalten (~66/33) | `'2-1'` | Spaltenbreiten, die nicht einem Standardverhältnis entsprechen, werden dem am nächsten liegenden verfügbaren Layout zugeordnet. diff --git a/apps/docs/de/guide/migration-from-html.md b/apps/docs/de/guide/migration-from-html.md index d2c4843..776d87c 100644 --- a/apps/docs/de/guide/migration-from-html.md +++ b/apps/docs/de/guide/migration-from-html.md @@ -20,17 +20,17 @@ npm install @templatical/import-html ## Verwendung ```ts -import { convertHtmlTemplate } from "@templatical/import-html"; +import { convertHtmlTemplate } from '@templatical/import-html'; // Den rohen HTML-Quelltext einer E-Mail laden -const html = await fetch("/path/to/email.html").then((r) => r.text()); +const html = await fetch('/path/to/email.html').then((r) => r.text()); // In das Templatical-Format konvertieren const { content, report } = convertHtmlTemplate(html); // Im Editor verwenden const editor = await init({ - container: "#editor", + container: '#editor', content, }); @@ -39,26 +39,25 @@ console.log(report); ``` Die Funktion gibt ein `ImportResult` zurück mit: - - `content` — das konvertierte `TemplateContent`, bereit für den Editor - `report` — ein Konvertierungsbericht mit dem Status jedes Elements (`converted`, `approximated`, `html-fallback` oder `skipped`) ## Element-Mapping -| HTML-Element | Templatical-Block | Status | -| --------------------------------------------------------------------------------------------- | --------------------------- | ---------------------------------------- | -| `

` – `

` | `title` | Konvertiert (Level erhalten) | -| `

` – `
` | `title` | Konvertiert (auf Level 4 begrenzt) | -| `

` / Text-`

` mit explizit gesetzter Höhe | `spacer` | Konvertiert | -| `` mit einem einzigen gestylten `` | `button` | Konvertiert (Cell-as-Button-Muster) | -| `` (Layout, mehrere Zeilen/Spalten) | `section` (eine pro ``) | Konvertiert | -| `
` (Datentabelle — nur Text in Zellen) | `html` | HTML-Fallback | -| Unbekannte / Custom-Elemente | `html` | HTML-Fallback | +| HTML-Element | Templatical-Block | Status | +|---|---|---| +| `

` – `

` | `title` | Konvertiert (Level erhalten) | +| `

` – `
` | `title` | Konvertiert (auf Level 4 begrenzt) | +| `

` / Text-`

` mit explizit gesetzter Höhe | `spacer` | Konvertiert | +| `` mit einem einzigen gestylten `` | `button` | Konvertiert (Cell-as-Button-Muster) | +| `` (Layout, mehrere Zeilen/Spalten) | `section` (eine pro ``) | Konvertiert | +| `
` (Datentabelle — nur Text in Zellen) | `html` | HTML-Fallback | +| Unbekannte / Custom-Elemente | `html` | HTML-Fallback | Alles, was sich nicht zuordnen lässt, wird wortgetreu in einem HTML-Block erhalten — sichtbarer Inhalt geht nicht verloren. @@ -66,12 +65,12 @@ Alles, was sich nicht zuordnen lässt, wird wortgetreu in einem HTML-Block erhal Jeder `` einer Layout-Tabelle wird zu einem `SectionBlock`: -| Zellen pro Zeile | Templatical-Layout | -| ---------------- | ------------------------------- | -| 1 | `'1'` | -| 2 | `'2'` | -| 3 | `'3'` | -| 4+ | auf `'1'` reduziert mit Warnung | +| Zellen pro Zeile | Templatical-Layout | +|---|---| +| 1 | `'1'` | +| 2 | `'2'` | +| 3 | `'3'` | +| 4+ | auf `'1'` reduziert mit Warnung | Templatical-Sections können nicht verschachtelt werden. Tabellen, die in einem ``- und ``- und `
` verschachtelt sind, werden flachgelegt — ihre Blöcke wandern in die übergeordnete Zelle. @@ -122,8 +121,11 @@ console.log(report.summary); // { total: 12, converted: 10, approximated: 1, htmlFallback: 1, skipped: 0 } for (const entry of report.entries) { - if (entry.status === "html-fallback") { - console.warn(`Element <${entry.sourceTag}> als HTML erhalten:`, entry.note); + if (entry.status === 'html-fallback') { + console.warn( + `Element <${entry.sourceTag}> als HTML erhalten:`, + entry.note, + ); } } diff --git a/apps/docs/de/guide/migration-from-mjml.md b/apps/docs/de/guide/migration-from-mjml.md index 56f0474..b6470bc 100644 --- a/apps/docs/de/guide/migration-from-mjml.md +++ b/apps/docs/de/guide/migration-from-mjml.md @@ -15,7 +15,7 @@ MJML → Templatical ist schwerer vollständig zu automatisieren als BeeFree → ## Was hier eigentlich passiert -Diese Migration ist etwas kontraintuitiv. Templaticals Renderer erzeugt _MJML als Ausgabe_ — auf den ersten Blick sehen MJML und Templatical identisch aus. Aber: +Diese Migration ist etwas kontraintuitiv. Templaticals Renderer erzeugt *MJML als Ausgabe* — auf den ersten Blick sehen MJML und Templatical identisch aus. Aber: - **MJML** ist eine Markup-Sprache. Du schreibst XML-ähnliche Tags (``, ``, ``) und der MJML-Compiler verwandelt das in tabellenbasiertes HTML. - **Templatical** speichert Templates als JSON-Baum mit typisierten Blöcken (`SectionBlock`, `ParagraphBlock` usw.) und rendert diesen Baum beim Export zu MJML. @@ -40,7 +40,7 @@ Die meisten MJML-Templates sind in 10–20 Minuten umgezogen, sobald du eines od Sobald du ein Template visuell nachgebaut hast: ```ts -import { renderToMjml } from "@templatical/renderer"; +import { renderToMjml } from '@templatical/renderer'; const mjml = await renderToMjml(content); // Vergleiche dieses MJML mit deinem ursprünglichen MJML-Quelltext. @@ -55,19 +55,19 @@ Hast du Hunderte MJML-Templates und willst automatische Konvertierung versuchen, Hier die grobe Form: ```ts -import { parse } from "node-html-parser"; +import { parse } from 'node-html-parser'; import { createSectionBlock, createTitleBlock, createParagraphBlock, createImageBlock, createButtonBlock, -} from "@templatical/types"; -import type { TemplateContent, Block } from "@templatical/types"; +} from '@templatical/types'; +import type { TemplateContent, Block } from '@templatical/types'; function mjmlToTemplate(mjml: string): TemplateContent { const root = parse(mjml); - const body = root.querySelector("mj-body"); + const body = root.querySelector('mj-body'); const blocks: Block[] = (body?.childNodes ?? []) .map((node) => convertNode(node)) @@ -76,17 +76,17 @@ function mjmlToTemplate(mjml: string): TemplateContent { return { blocks, settings: { - width: parseInt(body?.getAttribute("width") ?? "600"), - backgroundColor: body?.getAttribute("background-color") ?? "#ffffff", + width: parseInt(body?.getAttribute('width') ?? '600'), + backgroundColor: body?.getAttribute('background-color') ?? '#ffffff', }, }; } function convertNode(node: any): Block | null { switch (node.tagName?.toLowerCase()) { - case "mj-section": + case 'mj-section': return convertSection(node); - case "mj-text": + case 'mj-text': return convertText(node); // …weitere Cases — siehe Mapping-Tabelle unten default: @@ -101,22 +101,22 @@ Ein selbst geschriebener Parser wird Edge Cases übersehen — verschachtelte `m ## MJML-Tag-Mapping {#mjml-tag-mapping} -| MJML-Tag | Templatical-Block | Hinweise | -| ---------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| `mj-section` (mit `mj-column`s) | `SectionBlock` mit `columns` | Mehrspaltige Layouts funktionieren gleich; Spaltenbreiten kommen aus MJMLs `width`-Attribut oder werden gleichmäßig verteilt. | -| `mj-column` | Section-Spalte | Eine Spalte hält eine Liste verschachtelter Blöcke. | -| `mj-text` | `ParagraphBlock` (oder `TitleBlock` bei einer Überschrift) | Anhand inline-styled Heading-Level entscheiden, ob Title oder Paragraph. | -| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, Padding. | -| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, Schrift, Padding. | -| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, Padding. | -| `mj-spacer` | `SpacerBlock` | `height`. | -| `mj-social` (mit `mj-social-element`) | `SocialIconsBlock` | Jedes `mj-social-element` → ein `SocialIcon`-Eintrag. | -| `mj-navbar` (mit `mj-navbar-link`) | `MenuBlock` | Jeder Link → `MenuItemData`. | -| `mj-table` | `TableBlock` | `
`-Zeilen/Zellen auf Templaticals Tabellen-Daten abbilden. | -| `mj-raw` | `HtmlBlock` | HTML-Pass-Through. | -| `mj-wrapper` | `SectionBlock` (oft) | Ein Wrapper ohne Spalten wird zu einer Section mit einer Spalte. | -| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (Fallback) | Templatical hat noch keine direkten Entsprechungen — das gerenderte HTML in einen rohen HTML-Block übernehmen oder auf den Importer warten. | -| `mj-head`-Inhalte | Template-`settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` mappen auf `TemplateSettings.preheaderText`, eigene Schriften und Theme-Overrides. | +| MJML-Tag | Templatical-Block | Hinweise | +|---|---|---| +| `mj-section` (mit `mj-column`s) | `SectionBlock` mit `columns` | Mehrspaltige Layouts funktionieren gleich; Spaltenbreiten kommen aus MJMLs `width`-Attribut oder werden gleichmäßig verteilt. | +| `mj-column` | Section-Spalte | Eine Spalte hält eine Liste verschachtelter Blöcke. | +| `mj-text` | `ParagraphBlock` (oder `TitleBlock` bei einer Überschrift) | Anhand inline-styled Heading-Level entscheiden, ob Title oder Paragraph. | +| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, Padding. | +| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, Schrift, Padding. | +| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, Padding. | +| `mj-spacer` | `SpacerBlock` | `height`. | +| `mj-social` (mit `mj-social-element`) | `SocialIconsBlock` | Jedes `mj-social-element` → ein `SocialIcon`-Eintrag. | +| `mj-navbar` (mit `mj-navbar-link`) | `MenuBlock` | Jeder Link → `MenuItemData`. | +| `mj-table` | `TableBlock` | `
`-Zeilen/Zellen auf Templaticals Tabellen-Daten abbilden. | +| `mj-raw` | `HtmlBlock` | HTML-Pass-Through. | +| `mj-wrapper` | `SectionBlock` (oft) | Ein Wrapper ohne Spalten wird zu einer Section mit einer Spalte. | +| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (Fallback) | Templatical hat noch keine direkten Entsprechungen — das gerenderte HTML in einen rohen HTML-Block übernehmen oder auf den Importer warten. | +| `mj-head`-Inhalte | Template-`settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` mappen auf `TemplateSettings.preheaderText`, eigene Schriften und Theme-Overrides. | ## Was sich nicht automatisch übertragen lässt diff --git a/apps/docs/de/guide/migration-from-unlayer.md b/apps/docs/de/guide/migration-from-unlayer.md index e63f6c9..8fcc9ee 100644 --- a/apps/docs/de/guide/migration-from-unlayer.md +++ b/apps/docs/de/guide/migration-from-unlayer.md @@ -34,15 +34,15 @@ Die meisten Templates sind in 5–15 Minuten umgezogen, sobald du das erste oder Hast du **Dutzende oder Hunderte Templates** und willst nicht auf den offiziellen Importer warten, kannst du mit der [Mapping-Tabelle](#unlayer-modul-mapping) ein einmaliges Skript schreiben. Die Form ist überschaubar: ```ts -import { writeFileSync } from "node:fs"; +import { writeFileSync } from 'node:fs'; import { createTitleBlock, createParagraphBlock, createImageBlock, createButtonBlock, createSectionBlock, -} from "@templatical/types"; -import type { TemplateContent, Block } from "@templatical/types"; +} from '@templatical/types'; +import type { TemplateContent, Block } from '@templatical/types'; interface UnlayerDesign { body: { @@ -57,8 +57,8 @@ function convertUnlayerDesign(design: UnlayerDesign): TemplateContent { return { blocks, settings: { - width: parseInt(design.body.values.contentWidth ?? "600"), - backgroundColor: design.body.values.backgroundColor ?? "#ffffff", + width: parseInt(design.body.values.contentWidth ?? '600'), + backgroundColor: design.body.values.backgroundColor ?? '#ffffff', // …weitere Settings je nach Bedarf }, }; @@ -67,9 +67,7 @@ function convertUnlayerDesign(design: UnlayerDesign): TemplateContent { function convertRow(row: UnlayerRow): Block { // Spalten/Zellen auf Templaticals SectionBlock-Children abbilden. // Modulebene siehe Mapping-Tabelle unten. - return createSectionBlock({ - /* … */ - }); + return createSectionBlock({ /* … */ }); } ``` @@ -83,21 +81,21 @@ Dein Konvertierungs-Skript wird Iteration brauchen. Lass es zuerst auf einer kle Eine Richtungs-Referenz, keine vollständige Spezifikation. Unlayer hat Tarif-gebundene Module und Custom Blocks ohne direkte Entsprechungen. -| Unlayer-Modul | Templatical-Block | Hinweise | -| -------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -| `row` (mit `columns`) | `SectionBlock` (mit `columns`) | Section = Reihe + mehrspaltiges Layout. Templatical-Sections unterstützen 1–4 Spalten, die auf Mobil stapeln. | -| `column` | Section-Spalte | Eine Spalte innerhalb einer Section, hält eine Liste von Blöcken. | -| `heading` | `TitleBlock` | Heading-Level (h1–h6) mappen auf Templaticals `level`-Property. | -| `text` | `ParagraphBlock` | Inline-Rich-Text. Verwende TipTap-kompatibles HTML für Runs. | -| `image` | `ImageBlock` | `src`, `alt`, `href`, `width`. Bilder über deine Medienbibliothek neu hosten. | -| `button` | `ButtonBlock` | `text`, `href`, `backgroundColor`, `color`, Padding. | -| `divider` | `DividerBlock` | Farbe, Breite, Padding. | -| `social` | `SocialIconsBlock` | Jedes Icon → ein `SocialIcon`-Eintrag mit `platform` und `href`. | -| `menu` | `MenuBlock` | Menüeinträge → `MenuItemData`-Einträge. | -| `html` | `HtmlBlock` | Roher HTML-Pass-Through. | -| `video` | `VideoBlock` | `src`, `thumbnail`, `href`. | -| Spacer | `SpacerBlock` | Nur vertikaler Abstand. | -| Custom-Module / Tarif-gebundene Module | `HtmlBlock` (Fallback) | In einen rohen HTML-Block konvertieren oder als [Custom Block](/de/guide/custom-blocks) implementieren, wenn er wiederverwendbar ist. | +| Unlayer-Modul | Templatical-Block | Hinweise | +|---|---|---| +| `row` (mit `columns`) | `SectionBlock` (mit `columns`) | Section = Reihe + mehrspaltiges Layout. Templatical-Sections unterstützen 1–4 Spalten, die auf Mobil stapeln. | +| `column` | Section-Spalte | Eine Spalte innerhalb einer Section, hält eine Liste von Blöcken. | +| `heading` | `TitleBlock` | Heading-Level (h1–h6) mappen auf Templaticals `level`-Property. | +| `text` | `ParagraphBlock` | Inline-Rich-Text. Verwende TipTap-kompatibles HTML für Runs. | +| `image` | `ImageBlock` | `src`, `alt`, `href`, `width`. Bilder über deine Medienbibliothek neu hosten. | +| `button` | `ButtonBlock` | `text`, `href`, `backgroundColor`, `color`, Padding. | +| `divider` | `DividerBlock` | Farbe, Breite, Padding. | +| `social` | `SocialIconsBlock` | Jedes Icon → ein `SocialIcon`-Eintrag mit `platform` und `href`. | +| `menu` | `MenuBlock` | Menüeinträge → `MenuItemData`-Einträge. | +| `html` | `HtmlBlock` | Roher HTML-Pass-Through. | +| `video` | `VideoBlock` | `src`, `thumbnail`, `href`. | +| Spacer | `SpacerBlock` | Nur vertikaler Abstand. | +| Custom-Module / Tarif-gebundene Module | `HtmlBlock` (Fallback) | In einen rohen HTML-Block konvertieren oder als [Custom Block](/de/guide/custom-blocks) implementieren, wenn er wiederverwendbar ist. | ## Was sich nicht automatisch übertragen lässt diff --git a/apps/docs/de/guide/programmatic-templates.md b/apps/docs/de/guide/programmatic-templates.md index 77a74ec..a1c0082 100644 --- a/apps/docs/de/guide/programmatic-templates.md +++ b/apps/docs/de/guide/programmatic-templates.md @@ -12,7 +12,7 @@ Templatical bietet Factory-Funktionen für jeden Blocktyp. Verwenden Sie sie, um ## Leeres Template ```ts -import { createDefaultTemplateContent } from "@templatical/types"; +import { createDefaultTemplateContent } from '@templatical/types'; const content = createDefaultTemplateContent(); // { blocks: [], settings: { width: 600, backgroundColor: '#ffffff', fontFamily: 'Arial' } } @@ -21,7 +21,7 @@ const content = createDefaultTemplateContent(); `createDefaultTemplateContent()` akzeptiert einen optionalen Schriftfamilien-String: ```ts -const content = createDefaultTemplateContent("Georgia, serif"); +const content = createDefaultTemplateContent('Georgia, serif'); ``` ## Ein Template aufbauen @@ -36,7 +36,7 @@ import { createImageBlock, createButtonBlock, createDividerBlock, -} from "@templatical/types"; +} from '@templatical/types'; const content = createDefaultTemplateContent(); @@ -46,19 +46,19 @@ content.blocks = [ level: 1, }), createImageBlock({ - src: "https://example.com/hero.jpg", - alt: "Welcome hero image", - width: "full", + src: 'https://example.com/hero.jpg', + alt: 'Welcome hero image', + width: 'full', }), createDividerBlock(), createParagraphBlock({ - content: "

Thanks for signing up. Here is what happens next.

", + content: '

Thanks for signing up. Here is what happens next.

', }), createButtonBlock({ - text: "Get Started", - url: "https://example.com/dashboard", - backgroundColor: "#1a73e8", - textColor: "#ffffff", + text: 'Get Started', + url: 'https://example.com/dashboard', + backgroundColor: '#1a73e8', + textColor: '#ffffff', borderRadius: 6, }), ]; @@ -70,56 +70,56 @@ content.blocks = [ ```ts createTitleBlock({ - content: "

Welcome, {{name}}!

", + content: '

Welcome, {{name}}!

', level: 1, - textAlign: "center", -}); + textAlign: 'center', +}) ``` ### Paragraph ```ts createParagraphBlock({ - content: "

Thanks for signing up. Here is what happens next.

", -}); + content: '

Thanks for signing up. Here is what happens next.

', +}) ``` ### Image ```ts createImageBlock({ - src: "https://cdn.example.com/hero.png", - alt: "Hero banner", + src: 'https://cdn.example.com/hero.png', + alt: 'Hero banner', width: 560, - linkUrl: "https://example.com", -}); + linkUrl: 'https://example.com', +}) ``` ### Button ```ts createButtonBlock({ - text: "Get Started", - url: "https://example.com/signup", - backgroundColor: "#6366f1", + text: 'Get Started', + url: 'https://example.com/signup', + backgroundColor: '#6366f1', borderRadius: 8, -}); +}) ``` ### Divider ```ts createDividerBlock({ - lineStyle: "dashed", - color: "#e5e7eb", + lineStyle: 'dashed', + color: '#e5e7eb', thickness: 2, -}); +}) ``` ### Spacer ```ts -createSpacerBlock({ height: 40 }); +createSpacerBlock({ height: 40 }) ``` ### HTML @@ -127,24 +127,20 @@ createSpacerBlock({ height: 40 }); ```ts createHtmlBlock({ content: '
Custom markup
', -}); +}) ``` ### Social Icons ```ts createSocialIconsBlock({ - iconStyle: "circle", - iconSize: "large", + iconStyle: 'circle', + iconSize: 'large', icons: [ - { id: crypto.randomUUID(), platform: "twitter", url: "https://x.com/acme" }, - { - id: crypto.randomUUID(), - platform: "github", - url: "https://github.com/acme", - }, + { id: crypto.randomUUID(), platform: 'twitter', url: 'https://x.com/acme' }, + { id: crypto.randomUUID(), platform: 'github', url: 'https://github.com/acme' }, ], -}); +}) ``` ### Menu @@ -152,33 +148,12 @@ createSocialIconsBlock({ ```ts createMenuBlock({ items: [ - { - id: crypto.randomUUID(), - text: "Home", - url: "https://example.com", - openInNewTab: false, - bold: false, - underline: false, - }, - { - id: crypto.randomUUID(), - text: "Blog", - url: "https://example.com/blog", - openInNewTab: false, - bold: false, - underline: false, - }, - { - id: crypto.randomUUID(), - text: "Docs", - url: "https://docs.example.com", - openInNewTab: true, - bold: false, - underline: false, - }, + { id: crypto.randomUUID(), text: 'Home', url: 'https://example.com', openInNewTab: false, bold: false, underline: false }, + { id: crypto.randomUUID(), text: 'Blog', url: 'https://example.com/blog', openInNewTab: false, bold: false, underline: false }, + { id: crypto.randomUUID(), text: 'Docs', url: 'https://docs.example.com', openInNewTab: true, bold: false, underline: false }, ], - separator: "-", -}); + separator: '-', +}) ``` ### Table @@ -187,51 +162,33 @@ createMenuBlock({ createTableBlock({ hasHeaderRow: true, rows: [ - { - id: crypto.randomUUID(), - cells: [ - { id: crypto.randomUUID(), content: "Plan" }, - { id: crypto.randomUUID(), content: "Price" }, - ], - }, - { - id: crypto.randomUUID(), - cells: [ - { id: crypto.randomUUID(), content: "Starter" }, - { id: crypto.randomUUID(), content: "$9/mo" }, - ], - }, - { - id: crypto.randomUUID(), - cells: [ - { id: crypto.randomUUID(), content: "Pro" }, - { id: crypto.randomUUID(), content: "$29/mo" }, - ], - }, + { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Plan' }, { id: crypto.randomUUID(), content: 'Price' }] }, + { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Starter' }, { id: crypto.randomUUID(), content: '$9/mo' }] }, + { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Pro' }, { id: crypto.randomUUID(), content: '$29/mo' }] }, ], -}); +}) ``` ### Video ```ts createVideoBlock({ - url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", - thumbnailUrl: "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", - alt: "Product demo video", -}); + url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + thumbnailUrl: 'https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg', + alt: 'Product demo video', +}) ``` ### Section ```ts createSectionBlock({ - columns: "2", + columns: '2', children: [ - [createParagraphBlock({ content: "

Left column

" })], - [createImageBlock({ src: "https://cdn.example.com/photo.jpg" })], + [createParagraphBlock({ content: '

Left column

' })], + [createImageBlock({ src: 'https://cdn.example.com/photo.jpg' })], ], -}); +}) ``` Die Eigenschaft `columns` akzeptiert: `'1'` (einspaltig), `'2'` (zwei gleiche), `'3'` (drei gleiche), `'2-1'` (zwei Drittel / ein Drittel), `'1-2'` (ein Drittel / zwei Drittel). Siehe [Sektionen und Spalten](/de/guide/sections-and-columns) für alle Details. @@ -247,9 +204,9 @@ Die Eigenschaft `columns` akzeptiert: `'1'` (einspaltig), `'2'` (zwei gleiche), Erstellen Sie einen beliebigen Block anhand einer Typzeichenkette: ```ts -import { createBlock } from "@templatical/types"; +import { createBlock } from '@templatical/types'; -const block = createBlock("title"); // TitleBlock with defaults +const block = createBlock('title'); // TitleBlock with defaults ``` ### Klonen @@ -257,7 +214,7 @@ const block = createBlock("title"); // TitleBlock with defaults Deep-cloning eines Blocks mit einer neuen ID: ```ts -import { cloneBlock } from "@templatical/types"; +import { cloneBlock } from '@templatical/types'; const copy = cloneBlock(existingBlock); // copy.id !== existingBlock.id @@ -268,13 +225,7 @@ const copy = cloneBlock(existingBlock); Einen `Block`-Union auf einen bestimmten Typ eingrenzen: ```ts -import { - isTitle, - isParagraph, - isImage, - isButton, - isSection, -} from "@templatical/types"; +import { isTitle, isParagraph, isImage, isButton, isSection } from '@templatical/types'; if (isTitle(block)) { console.log(block.level); // TypeScript knows this is TitleBlock @@ -299,17 +250,17 @@ Template-Einstellungen steuern die globalen Eigenschaften der E-Mail: const content = createDefaultTemplateContent(); content.settings.width = 640; -content.settings.backgroundColor = "#f5f5f5"; -content.settings.fontFamily = "Helvetica, Arial, sans-serif"; -content.settings.preheaderText = "Your weekly digest is here"; +content.settings.backgroundColor = '#f5f5f5'; +content.settings.fontFamily = 'Helvetica, Arial, sans-serif'; +content.settings.preheaderText = 'Your weekly digest is here'; ``` -| Einstellung | Typ | Beschreibung | -| ----------------- | -------- | --------------------------------------------------------- | -| `width` | `number` | E-Mail-Breite in Pixeln | -| `backgroundColor` | `string` | Äußere Hintergrundfarbe | -| `fontFamily` | `string` | Standard-Font-Stack | -| `preheaderText` | `string` | Vorschautext, der in der Posteingangsliste angezeigt wird | +| Einstellung | Typ | Beschreibung | +|---|---|---| +| `width` | `number` | E-Mail-Breite in Pixeln | +| `backgroundColor` | `string` | Äußere Hintergrundfarbe | +| `fontFamily` | `string` | Standard-Font-Stack | +| `preheaderText` | `string` | Vorschautext, der in der Posteingangsliste angezeigt wird | Für Standardwerte und wie Sie diese anpassen können, siehe [Block- & Template-Standardwerte](/de/guide/defaults). @@ -318,10 +269,10 @@ Für Standardwerte und wie Sie diese anpassen können, siehe [Block- & Template- Übergeben Sie zuvor gespeichertes JSON zurück an den Editor: ```ts -const saved = await fetch("/api/templates/123").then((r) => r.json()); +const saved = await fetch('/api/templates/123').then(r => r.json()); const editor = await init({ - container: "#editor", + container: '#editor', content: saved, }); ``` diff --git a/apps/docs/de/guide/sections-and-columns.md b/apps/docs/de/guide/sections-and-columns.md index 9343d9b..f5e6f42 100644 --- a/apps/docs/de/guide/sections-and-columns.md +++ b/apps/docs/de/guide/sections-and-columns.md @@ -15,16 +15,16 @@ Bleiben Sie für die meisten E-Mails bei 1-2 Spalten. Dreispaltige Layouts werde Die Eigenschaft `columns` akzeptiert eines von fünf Layout-Presets: -| Wert | Beschreibung | Spaltenbreiten | -| ------- | -------------------------- | --------------- | -| `'1'` | Einzelne Spalte | 100% | -| `'2'` | Zwei gleiche Spalten | 50% / 50% | -| `'3'` | Drei gleiche Spalten | 33% / 33% / 33% | -| `'2-1'` | Zwei Drittel / ein Drittel | 66% / 33% | -| `'1-2'` | Ein Drittel / zwei Drittel | 33% / 66% | +| Wert | Beschreibung | Spaltenbreiten | +|-------|-------------|---------------| +| `'1'` | Einzelne Spalte | 100% | +| `'2'` | Zwei gleiche Spalten | 50% / 50% | +| `'3'` | Drei gleiche Spalten | 33% / 33% / 33% | +| `'2-1'` | Zwei Drittel / ein Drittel | 66% / 33% | +| `'1-2'` | Ein Drittel / zwei Drittel | 33% / 66% | ```ts -type ColumnLayout = "1" | "2" | "3" | "2-1" | "1-2"; +type ColumnLayout = '1' | '2' | '3' | '2-1' | '1-2'; ``` ## Sektionen erstellen @@ -37,19 +37,19 @@ import { createTitleBlock, createParagraphBlock, createImageBlock, -} from "@templatical/types"; +} from '@templatical/types'; // Leere zweispaltige Sektion -const section = createSectionBlock({ columns: "2" }); +const section = createSectionBlock({ columns: '2' }); // Sektion mit vorbefüllten Spalten const hero = createSectionBlock({ - columns: "1-2", + columns: '1-2', children: [ - [createImageBlock({ src: "https://cdn.example.com/logo.png", width: 120 })], + [createImageBlock({ src: 'https://cdn.example.com/logo.png', width: 120 })], [ - createTitleBlock({ content: "

Welcome

", level: 1 }), - createParagraphBlock({ content: "

Get started in minutes.

" }), + createTitleBlock({ content: '

Welcome

', level: 1 }), + createParagraphBlock({ content: '

Get started in minutes.

' }), ], ], }); @@ -62,15 +62,15 @@ const hero = createSectionBlock({ ```ts // Für ein '2'-Layout: section.children = [ - [blockA, blockB], // Linke Spalte - [blockC], // Rechte Spalte + [blockA, blockB], // Linke Spalte + [blockC], // Rechte Spalte ]; // Für ein '3'-Layout: section.children = [ - [blockA], // Links - [blockB], // Mitte - [blockC], // Rechts + [blockA], // Links + [blockB], // Mitte + [blockC], // Rechts ]; ``` @@ -84,8 +84,8 @@ Um einen Block programmatisch zu einer bestimmten Spalte hinzuzufügen: // Eine Schaltfläche zur zweiten Spalte hinzufügen (Index 1) section.children[1].push( createButtonBlock({ - text: "Learn More", - url: "https://example.com/docs", + text: 'Learn More', + url: 'https://example.com/docs', }), ); ``` @@ -102,7 +102,7 @@ Sie können die Eigenschaft `visibility` für einzelne Blöcke innerhalb von Spa ```ts const block = createParagraphBlock({ - content: "

Desktop only sidebar content

", + content: '

Desktop only sidebar content

', }); block.visibility = { @@ -119,10 +119,10 @@ Siehe [Styling](/de/guide/styling) für weitere Informationen zu responsiven Üb Sektionen unterstützen die gleichen `BlockStyles` wie andere Blöcke. Häufige Anwendungsfälle sind das Festlegen einer Hintergrundfarbe oder eines Paddings für die gesamte Zeile: ```ts -const section = createSectionBlock({ columns: "1" }); +const section = createSectionBlock({ columns: '1' }); section.styles = { - backgroundColor: "#f8fafc", + backgroundColor: '#f8fafc', padding: { top: 32, right: 24, bottom: 32, left: 24 }, margin: { top: 0, right: 0, bottom: 0, left: 0 }, }; diff --git a/apps/docs/de/guide/styling.md b/apps/docs/de/guide/styling.md index c11bc11..2512155 100644 --- a/apps/docs/de/guide/styling.md +++ b/apps/docs/de/guide/styling.md @@ -21,16 +21,16 @@ interface BlockStyles { ``` ```ts -import { createParagraphBlock } from "@templatical/types"; +import { createParagraphBlock } from '@templatical/types'; const block = createParagraphBlock({ - content: "

Styled text

", + content: '

Styled text

', }); block.styles = { padding: { top: 16, right: 24, bottom: 16, left: 24 }, margin: { top: 0, right: 0, bottom: 0, left: 0 }, - backgroundColor: "#f0f9ff", + backgroundColor: '#f0f9ff', }; ``` @@ -69,11 +69,11 @@ interface ResponsiveStyles { Der Editor verwendet diese Viewport-Breiten im Vorschaumodus: -| Viewport | Vorschaubreite | -| -------- | -------------------------------- | -| Desktop | Template-Breite (Standard 600px) | -| Tablet | 768px | -| Mobile | 375px | +| Viewport | Vorschaubreite | +|----------|---------------| +| Desktop | Template-Breite (Standard 600px) | +| Tablet | 768px | +| Mobile | 375px | Responsive Überschreibungen werden mit den Basisstilen zusammengeführt. Geben Sie nur die Eigenschaften an, die Sie ändern möchten: @@ -136,24 +136,24 @@ Die meisten CSS-Eigenschaften funktionieren nicht zuverlässig über E-Mail-Clie Neben einzelnen Blockstilen hat das Template selbst globale Einstellungen, die das Gesamtlayout beeinflussen. -| Einstellung | Typ | Beschreibung | -| ----------------- | -------- | ------------------------------------------------- | -| `width` | `number` | Template-Inhaltsbreite in px (typischerweise 600) | -| `backgroundColor` | `string` | Äußere Hintergrundfarbe hinter dem Template | -| `fontFamily` | `string` | Standard-Schriftfamilie für alle Blöcke | +| Einstellung | Typ | Beschreibung | +|---------|------|-------------| +| `width` | `number` | Template-Inhaltsbreite in px (typischerweise 600) | +| `backgroundColor` | `string` | Äußere Hintergrundfarbe hinter dem Template | +| `fontFamily` | `string` | Standard-Schriftfamilie für alle Blöcke | Diese werden über die `init()`-Konfiguration des Editors oder durch direkte Änderung des Template-JSON konfiguriert: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', content: { settings: { width: 640, - backgroundColor: "#f8fafc", - fontFamily: "Inter, system-ui, sans-serif", + backgroundColor: '#f8fafc', + fontFamily: 'Inter, system-ui, sans-serif', }, blocks: [], }, diff --git a/apps/docs/de/license-faq.md b/apps/docs/de/license-faq.md index c3b81c6..040832a 100644 --- a/apps/docs/de/license-faq.md +++ b/apps/docs/de/license-faq.md @@ -25,15 +25,15 @@ FSL-1.1-**MIT** ist die Variante, die nach zwei Jahren automatisch zur MIT-Lizen ## Welches Paket nutzt welche Lizenz? -| Paket | Lizenz | -| ----------------------------- | -------------------------------------------------------------------------------------------- | -| `@templatical/editor` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (wird nach 2 Jahren MIT) | -| `@templatical/core` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (wird nach 2 Jahren MIT) | -| `@templatical/media-library` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (wird nach 2 Jahren MIT) | -| `@templatical/types` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | -| `@templatical/renderer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | -| `@templatical/import-beefree` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | -| `@templatical/import-unlayer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| Paket | Lizenz | +|---|---| +| `@templatical/editor` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (wird nach 2 Jahren MIT) | +| `@templatical/core` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (wird nach 2 Jahren MIT) | +| `@templatical/media-library` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (wird nach 2 Jahren MIT) | +| `@templatical/types` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| `@templatical/renderer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| `@templatical/import-beefree` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| `@templatical/import-unlayer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | Die Aufteilung sorgt dafür, dass alles, was du in dein eigenes Backend oder eine Codegen-Pipeline einbinden würdest (Types, Renderer, Importer), unter permissiver MIT-Lizenz steht und keine Future-License-Überlegungen nötig sind. @@ -41,7 +41,7 @@ Die Aufteilung sorgt dafür, dass alles, was du in dein eigenes Backend oder ein **Ja.** Du kannst Templatical in jedes kommerzielle Produkt einbetten — kostenpflichtiges SaaS, interne Tools, On-Premise-Software, Agentur-Builds, was auch immer — ohne uns zu bezahlen und ohne um Erlaubnis zu fragen. -Die einzige Einschränkung steht in der LICENSE: Du darfst Templatical nicht nehmen, einen anderen Namen draufkleben und _das_ als gehosteten E-Mail-Editor-Service anbieten, der mit uns konkurriert. +Die einzige Einschränkung steht in der LICENSE: Du darfst Templatical nicht nehmen, einen anderen Namen draufkleben und *das* als gehosteten E-Mail-Editor-Service anbieten, der mit uns konkurriert. ## Darf ich Templatical in mein SaaS einbetten? @@ -63,16 +63,16 @@ Bist du dir nicht sicher, ob dein Anwendungsfall die Linie überschreitet, [öff ## Was bedeutet "konkurrierende Nutzung" konkret? -Eine "konkurrierende Nutzung" ist eine, bei der der E-Mail-Editor _das_ Produkt ist. Ein paar Beispiele aus der Praxis: +Eine "konkurrierende Nutzung" ist eine, bei der der E-Mail-Editor *das* Produkt ist. Ein paar Beispiele aus der Praxis: -| Anwendungsfall | Erlaubt? | -| ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -| Du baust Templatical in dein CRM ein, damit Nutzer Kampagnen-E-Mails verfassen | ✅ Ja — der Editor ist eine Funktion deines CRMs. | -| Du integrierst Templatical in eine Transactional-E-Mail-API, damit Kunden Templates designen, bevor sie über deine API senden | ✅ Ja — der Editor ist eine Funktion deiner Sende-Plattform. | -| Du baust ein Newsletter-SaaS, in dem Templatical eines von mehreren Kompositionswerkzeugen ist | ✅ Ja — der Editor ist eine Funktion deines Newsletter-Produkts. | -| Du bettest den Editor in das interne Marketing-Portal deines Unternehmens ein | ✅ Ja — interne Nutzung ist uneingeschränkt. | -| Du forkst Templatical, brandest es um und verkaufst Abos eines gehosteten Templatical-Klons | ❌ Nein — das ist ein konkurrierender Managed Service. | -| Du baust "Templatical Cloud, aber günstiger" und bietest es als SaaS an | ❌ Nein — das ist ein konkurrierender Managed Service. | +| Anwendungsfall | Erlaubt? | +|---|---| +| Du baust Templatical in dein CRM ein, damit Nutzer Kampagnen-E-Mails verfassen | ✅ Ja — der Editor ist eine Funktion deines CRMs. | +| Du integrierst Templatical in eine Transactional-E-Mail-API, damit Kunden Templates designen, bevor sie über deine API senden | ✅ Ja — der Editor ist eine Funktion deiner Sende-Plattform. | +| Du baust ein Newsletter-SaaS, in dem Templatical eines von mehreren Kompositionswerkzeugen ist | ✅ Ja — der Editor ist eine Funktion deines Newsletter-Produkts. | +| Du bettest den Editor in das interne Marketing-Portal deines Unternehmens ein | ✅ Ja — interne Nutzung ist uneingeschränkt. | +| Du forkst Templatical, brandest es um und verkaufst Abos eines gehosteten Templatical-Klons | ❌ Nein — das ist ein konkurrierender Managed Service. | +| Du baust "Templatical Cloud, aber günstiger" und bietest es als SaaS an | ❌ Nein — das ist ein konkurrierender Managed Service. | Die Faustregel: Wenn du Templatical aus deinem Produkt entfernst, hast du dann immer noch ein Produkt? Wenn ja, bist du auf der sicheren Seite. diff --git a/apps/docs/de/quality/accessibility/getting-started.md b/apps/docs/de/quality/accessibility/getting-started.md index 12bb0b3..720db5a 100644 --- a/apps/docs/de/quality/accessibility/getting-started.md +++ b/apps/docs/de/quality/accessibility/getting-started.md @@ -3,23 +3,18 @@ ## Installation ::: code-group - ```bash [npm] npm install @templatical/quality ``` - ```bash [pnpm] pnpm add @templatical/quality ``` - ```bash [yarn] yarn add @templatical/quality ``` - ```bash [bun] bun add @templatical/quality ``` - ::: ## In den Editor einbinden diff --git a/apps/docs/de/quality/accessibility/headless-usage.md b/apps/docs/de/quality/accessibility/headless-usage.md index 909391a..d1020a9 100644 --- a/apps/docs/de/quality/accessibility/headless-usage.md +++ b/apps/docs/de/quality/accessibility/headless-usage.md @@ -49,9 +49,7 @@ for (const [name, content] of Object.entries(templates)) { console.error(`✖ ${name}: ${issues.length} issue(s)`); for (const issue of issues) { const where = issue.blockId ? `block ${issue.blockId}` : "template"; - console.error( - ` [${issue.severity}] ${issue.ruleId} (${where}): ${issue.message}`, - ); + console.error(` [${issue.severity}] ${issue.ruleId} (${where}): ${issue.message}`); } } @@ -65,7 +63,9 @@ Mit `tsx scripts/lint-templates.ts` ausführen und in den CI-Workflow einhängen Ein Team möchte vielleicht in CI nur Errors, in der Entwicklung aber die volle Info-Stufe: ```ts -const SEVERITIES = process.env.CI ? ["error"] : ["error", "warning", "info"]; +const SEVERITIES = process.env.CI + ? ["error"] + : ["error", "warning", "info"]; const issues = lintAccessibility(content).filter((i) => SEVERITIES.includes(i.severity), diff --git a/apps/docs/de/quality/accessibility/index.md b/apps/docs/de/quality/accessibility/index.md index 38bb040..9bb8bb0 100644 --- a/apps/docs/de/quality/accessibility/index.md +++ b/apps/docs/de/quality/accessibility/index.md @@ -63,23 +63,18 @@ Das Paket macht keine Vorgaben zur UI. Das `useAccessibilityLint`-Composable des ## Installation ::: code-group - ```bash [npm] npm install @templatical/quality ``` - ```bash [pnpm] pnpm add @templatical/quality ``` - ```bash [yarn] yarn add @templatical/quality ``` - ```bash [bun] bun add @templatical/quality ``` - ::: Das Paket ist ein **optionaler Peer** von `@templatical/editor`. Installieren Sie es, um den Sidebar-Tab und die Canvas-Badges zu aktivieren. Lassen Sie es weg, bleibt der Editor schlank – der dynamische Import ist gegated und tree-shakeable, sodass der Linter-Chunk nie heruntergeladen wird. diff --git a/apps/docs/de/quality/accessibility/options.md b/apps/docs/de/quality/accessibility/options.md index 063d04e..8c09ea7 100644 --- a/apps/docs/de/quality/accessibility/options.md +++ b/apps/docs/de/quality/accessibility/options.md @@ -16,7 +16,7 @@ type Severity = "error" | "warning" | "info" | "off"; ## `disabled` | Standard | `false` | -| -------- | ------- | +|---|---| Bei `true`: @@ -28,9 +28,9 @@ Verwenden Sie das, wenn ein Mandant sich explizit ausklingt oder wenn Sie das OS ## `locale` -| Standard (Headless) | `'en'` | -| ------------------- | ------------------------------ | -| Editor | folgt stets `init({ locale })` | +| Standard (Headless) | `'en'` | +|---|---| +| Editor | folgt stets `init({ locale })` | Steuert die Meldungstexte, die der Linter zurückgibt (`messages/{locale}.ts`), und wird von den lokalisierungsabhängigen Regeln verwendet (`link-vague-text`, `button-vague-label`, `img-linked-no-context`). Fällt auf `en` zurück, wenn die Locale (oder ihre Basissprache) nicht mitgeliefert wird. @@ -55,7 +55,7 @@ Das Wörterbuch ist eine Vereinigung aller registrierten Locales – ein deutsch ## `rules` | Standard | `{}` | -| -------- | ---- | +|---|---| Pro-Regel-Schweregrad-Override. Setzen Sie eine Regel auf `'off'`, um sie ganz zu deaktivieren. Setzen Sie sie auf einen anderen Schweregrad, um die Standard-Klassifikation zu beugen: @@ -72,16 +72,16 @@ Das Override greift, bevor die Regel läuft – deaktivierte Regeln werden also ## `thresholds` | Standard | siehe unten | -| -------- | ----------- | +|---|---| Numerische Stellschrauben, die einige Regeln konsultieren: -| Schwellwert | Standard | Verwendet von | -| ------------------ | -------- | --------------------- | -| `altMaxLength` | `125` | `img-alt-too-long` | -| `minFontSize` | `14` | `text-too-small` | -| `allCapsMinLength` | `20` | `text-all-caps` | -| `minTouchTargetPx` | `44` | `button-touch-target` | +| Schwellwert | Standard | Verwendet von | +|---|---|---| +| `altMaxLength` | `125` | `img-alt-too-long` | +| `minFontSize` | `14` | `text-too-small` | +| `allCapsMinLength` | `20` | `text-all-caps` | +| `minTouchTargetPx` | `44` | `button-touch-target` | Einen Wert überschreiben, ohne die anderen zu verlieren – partielles Mergen ist eingebaut: diff --git a/apps/docs/de/quality/accessibility/rule-catalog.md b/apps/docs/de/quality/accessibility/rule-catalog.md index 61a6951..58d7f52 100644 --- a/apps/docs/de/quality/accessibility/rule-catalog.md +++ b/apps/docs/de/quality/accessibility/rule-catalog.md @@ -4,49 +4,50 @@ Die 19 Regeln, die `@templatical/quality` mitliefert, gruppiert nach Prüfbereic ## Bilder -| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | -| -------------------------------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `img-missing-alt` | error | – | Fehlender Alt-Text – Screenreader sagen einen undefinierten oder leeren Alt-Text als Dateinamen an oder überspringen das Bild ganz. E-Mail-Clients blockieren Bilder zudem standardmäßig; Alt-Text ist das, was 30–50 % der Empfänger zuerst sehen. [1](https://www.w3.org/WAI/tutorials/images/) | -| `img-alt-is-filename` | warning | ja | Alt-Text sieht aus wie ein Dateiname – Dateinamen wie 'IMG_1234.jpg' oder 'Screen Shot 2026.png' tragen keine Bedeutung. Ersetzen Sie ihn durch eine kurze Beschreibung des Bildinhalts. | -| `img-alt-too-long` | warning | – | Alt-Text ist zu lang – Screenreader pausieren innerhalb des Alt-Textes nicht. Lange Alt-Strings werden zu einer Sprechwand. Bleiben Sie unter ~125 Zeichen; zusätzlichen Kontext in den umgebenden Text legen. | -| `img-decorative-needs-empty-alt` | info | ja | Dekoratives Bild hat Alt-Text – Dekorative Bilder sollten von Screenreadern übersprungen werden. `alt=''` (leer) signalisiert diese Absicht. Nicht-leerer Alt-Text auf einem dekorativen Bild ist ein Widerspruch. | -| `img-linked-no-context` | warning | – | Verlinktes Bild ohne Zielkontext – Wenn ein Bild zugleich Link ist, dient der Alt-Text als Linktext. Beschreibt er nur das Bild, raten die Nutzer, wohin der Link führt. | +| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | +|---|---|---|---| +| `img-missing-alt` | error | – | Fehlender Alt-Text – Screenreader sagen einen undefinierten oder leeren Alt-Text als Dateinamen an oder überspringen das Bild ganz. E-Mail-Clients blockieren Bilder zudem standardmäßig; Alt-Text ist das, was 30–50 % der Empfänger zuerst sehen. [1](https://www.w3.org/WAI/tutorials/images/) | +| `img-alt-is-filename` | warning | ja | Alt-Text sieht aus wie ein Dateiname – Dateinamen wie 'IMG_1234.jpg' oder 'Screen Shot 2026.png' tragen keine Bedeutung. Ersetzen Sie ihn durch eine kurze Beschreibung des Bildinhalts. | +| `img-alt-too-long` | warning | – | Alt-Text ist zu lang – Screenreader pausieren innerhalb des Alt-Textes nicht. Lange Alt-Strings werden zu einer Sprechwand. Bleiben Sie unter ~125 Zeichen; zusätzlichen Kontext in den umgebenden Text legen. | +| `img-decorative-needs-empty-alt` | info | ja | Dekoratives Bild hat Alt-Text – Dekorative Bilder sollten von Screenreadern übersprungen werden. `alt=''` (leer) signalisiert diese Absicht. Nicht-leerer Alt-Text auf einem dekorativen Bild ist ein Widerspruch. | +| `img-linked-no-context` | warning | – | Verlinktes Bild ohne Zielkontext – Wenn ein Bild zugleich Link ist, dient der Alt-Text als Linktext. Beschreibt er nur das Bild, raten die Nutzer, wohin der Link führt. | ## Überschriften -| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | -| --------------------- | -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `heading-empty` | error | – | Überschrift ohne Text – Leere Überschriften erzeugen stille Landmarken für Screenreader-Nutzer, die per Überschrift navigieren. Entweder Text ergänzen oder den Block entfernen. | -| `heading-skip-level` | error | – | Überschriftenebene übersprungen – Überspringen von Ebenen (z. B. H1 → H3) zerstört das Dokumentgerüst, auf das assistive Technologien zur Navigation angewiesen sind. Immer nur eine Ebene weiter. | -| `heading-multiple-h1` | warning | – | Mehrere H1-Überschriften – Eine E-Mail sollte eine einzige H1 haben, die die Nachricht benennt. Mehrere H1 verwirren das Dokumentgerüst und schwächen die Landmark-Navigation. | +| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | +|---|---|---|---| +| `heading-empty` | error | – | Überschrift ohne Text – Leere Überschriften erzeugen stille Landmarken für Screenreader-Nutzer, die per Überschrift navigieren. Entweder Text ergänzen oder den Block entfernen. | +| `heading-skip-level` | error | – | Überschriftenebene übersprungen – Überspringen von Ebenen (z. B. H1 → H3) zerstört das Dokumentgerüst, auf das assistive Technologien zur Navigation angewiesen sind. Immer nur eine Ebene weiter. | +| `heading-multiple-h1` | warning | – | Mehrere H1-Überschriften – Eine E-Mail sollte eine einzige H1 haben, die die Nachricht benennt. Mehrere H1 verwirren das Dokumentgerüst und schwächen die Landmark-Navigation. | ## Links -| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | -| -------------------------- | -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `link-empty` | error | – | Link ohne zugänglichen Text – Ein Link ohne sichtbaren Text und ohne verschachteltes Bild mit Alt-Text ist für Screenreader unsichtbar und für viele Nutzer nicht klickbar. | -| `link-vague-text` | warning | – | Vager Linktext – Phrasen wie "hier klicken" oder "mehr lesen" sagen Screenreader-Nutzern nichts, wenn sie aus dem Kontext gelistet werden. Verwenden Sie beschreibenden Linktext, der das Ziel benennt. Äußere Satzzeichen und dekorative Symbole werden vor dem Abgleich entfernt; `Hier klicken!`, `→ hier klicken` und `»hier klicken«` werden also alle erkannt. | -| `link-href-empty` | error | – | Link mit leerem href – Ein Anker ohne Ziel (leeres href oder '#') ist defekt – Empfänger klicken und nichts passiert, oder die Seite springt nach oben. | -| `link-target-blank-no-rel` | warning | ja | `target="_blank"` ohne `rel="noopener"` – Links, die in einem neuen Tab öffnen und kein `rel='noopener'`/`rel='noreferrer'` setzen, lassen das Ziel über `window.opener` die Ursprungsseite manipulieren. Eine kleine, aber reale Sicherheits-/Privatsphäre-Falle. | +| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | +|---|---|---|---| +| `link-empty` | error | – | Link ohne zugänglichen Text – Ein Link ohne sichtbaren Text und ohne verschachteltes Bild mit Alt-Text ist für Screenreader unsichtbar und für viele Nutzer nicht klickbar. | +| `link-vague-text` | warning | – | Vager Linktext – Phrasen wie "hier klicken" oder "mehr lesen" sagen Screenreader-Nutzern nichts, wenn sie aus dem Kontext gelistet werden. Verwenden Sie beschreibenden Linktext, der das Ziel benennt. Äußere Satzzeichen und dekorative Symbole werden vor dem Abgleich entfernt; `Hier klicken!`, `→ hier klicken` und `»hier klicken«` werden also alle erkannt. | +| `link-href-empty` | error | – | Link mit leerem href – Ein Anker ohne Ziel (leeres href oder '#') ist defekt – Empfänger klicken und nichts passiert, oder die Seite springt nach oben. | +| `link-target-blank-no-rel` | warning | ja | `target="_blank"` ohne `rel="noopener"` – Links, die in einem neuen Tab öffnen und kein `rel='noopener'`/`rel='noreferrer'` setzen, lassen das Ziel über `window.opener` die Ursprungsseite manipulieren. Eine kleine, aber reale Sicherheits-/Privatsphäre-Falle. | ## Text -| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | -| ------------------- | -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `text-all-caps` | warning | – | Großbuchstaben im Fließtext – Längere Großbuchstaben-Passagen werden von manchen Screenreadern Buchstabe für Buchstabe gelesen und verlangsamen das visuelle Lesen um 10–20 %. Verwenden Sie Groß-/Kleinschreibung im Fließtext; Großbuchstaben nur für kurze Labels. | -| `text-low-contrast` | error | – | Überschriftenkontrast zu niedrig – WCAG AA verlangt 4,5:1 für Fließtext und 3:1 für großen Text (18pt / ~24px). Überschriften ≥24px (H1, H2) bekommen den entspannten 3:1-Schwellwert; H3 (22px) und H4 (18px) erfordern 4,5:1. Die Lockerung für Fettschrift wird nicht angewendet – TipTap legt Fett inline im HTML ab, nicht als strukturiertes Feld. Darunter wird der Text für sehbehinderte Nutzer und bei hellem Außenlicht unleserlich. Es werden nur Title-Blöcke geprüft; die Farbe von Absätzen liegt im Inline-HTML. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | -| `text-too-small` | warning | – | Text zu klein – Fließtext unter 14px wird auf Mobilgeräten schwer lesbar. Manche Clients zoomen oder skalieren kleine Schriften zudem unvorhersehbar. Bleiben Sie bei 14px oder größer. | +| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | +|---|---|---|---| +| `text-all-caps` | warning | – | Großbuchstaben im Fließtext – Längere Großbuchstaben-Passagen werden von manchen Screenreadern Buchstabe für Buchstabe gelesen und verlangsamen das visuelle Lesen um 10–20 %. Verwenden Sie Groß-/Kleinschreibung im Fließtext; Großbuchstaben nur für kurze Labels. | +| `text-low-contrast` | error | – | Überschriftenkontrast zu niedrig – WCAG AA verlangt 4,5:1 für Fließtext und 3:1 für großen Text (18pt / ~24px). Überschriften ≥24px (H1, H2) bekommen den entspannten 3:1-Schwellwert; H3 (22px) und H4 (18px) erfordern 4,5:1. Die Lockerung für Fettschrift wird nicht angewendet – TipTap legt Fett inline im HTML ab, nicht als strukturiertes Feld. Darunter wird der Text für sehbehinderte Nutzer und bei hellem Außenlicht unleserlich. Es werden nur Title-Blöcke geprüft; die Farbe von Absätzen liegt im Inline-HTML. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | +| `text-too-small` | warning | – | Text zu klein – Fließtext unter 14px wird auf Mobilgeräten schwer lesbar. Manche Clients zoomen oder skalieren kleine Schriften zudem unvorhersehbar. Bleiben Sie bei 14px oder größer. | ## Buttons -| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | -| --------------------- | -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `button-vague-label` | warning | – | Vages Button-Label – Ein Button mit "Hier klicken" oder "Senden" sagt nichts darüber, was passieren wird. Verwenden Sie handlungsorientierte Labels, die den Ausgang benennen ("Ticket kaufen", "Passwort zurücksetzen"). Gleiche Behandlung äußerer Satzzeichen wie bei `link-vague-text` – `Senden!`, `→ OK` und `»klick«` werden alle erkannt. | -| `button-touch-target` | warning | – | Touch-Ziel des Buttons zu klein – WCAG 2.5.5 (AAA) und die UX-Richtlinien von Apple/Google empfehlen mindestens 44×44px. Kleinere Buttons führen mobil zu Fehl-Taps. | -| `button-low-contrast` | error | – | Button-Textkontrast zu niedrig – Gleiche WCAG-AA-Schwellen wie bei `text-low-contrast`: 4,5:1 normal, 3:1 bei Buttons mit `fontSize >= 24` (WCAG-Großtext). Standard-Buttons (15px) verlangen den strikten Wert. Buttons, die das nicht erfüllen, sind für sehbehinderte Nutzer und bei hellem Außenlicht unleserlich. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | +| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | +|---|---|---|---| +| `button-vague-label` | warning | – | Vages Button-Label – Ein Button mit "Hier klicken" oder "Senden" sagt nichts darüber, was passieren wird. Verwenden Sie handlungsorientierte Labels, die den Ausgang benennen ("Ticket kaufen", "Passwort zurücksetzen"). Gleiche Behandlung äußerer Satzzeichen wie bei `link-vague-text` – `Senden!`, `→ OK` und `»klick«` werden alle erkannt. | +| `button-touch-target` | warning | – | Touch-Ziel des Buttons zu klein – WCAG 2.5.5 (AAA) und die UX-Richtlinien von Apple/Google empfehlen mindestens 44×44px. Kleinere Buttons führen mobil zu Fehl-Taps. | +| `button-low-contrast` | error | – | Button-Textkontrast zu niedrig – Gleiche WCAG-AA-Schwellen wie bei `text-low-contrast`: 4,5:1 normal, 3:1 bei Buttons mit `fontSize >= 24` (WCAG-Großtext). Standard-Buttons (15px) verlangen den strikten Wert. Buttons, die das nicht erfüllen, sind für sehbehinderte Nutzer und bei hellem Außenlicht unleserlich. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | ## Struktur -| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | -| ------------------- | -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `missing-preheader` | info | – | Fehlender Preheader-Text – Der Preheader ist der Vorschau-Snippet, der in den meisten Postfächern neben der Betreffzeile erscheint. Ohne ihn sehen Empfänger ein Fragment der ersten Überschrift oder ein verirrtes Alt-Tag – eine vertane Chance, Kontext zu setzen. | +| Regel | Standard-Schweregrad | Auto-Fix | Was geprüft wird | +|---|---|---|---| +| `missing-preheader` | info | – | Fehlender Preheader-Text – Der Preheader ist der Vorschau-Snippet, der in den meisten Postfächern neben der Betreffzeile erscheint. Ohne ihn sehen Empfänger ein Fragment der ersten Überschrift oder ein verirrtes Alt-Tag – eine vertane Chance, Kontext zu setzen. | + diff --git a/apps/docs/de/quality/accessibility/severity-and-fixes.md b/apps/docs/de/quality/accessibility/severity-and-fixes.md index ca34d5d..ae93761 100644 --- a/apps/docs/de/quality/accessibility/severity-and-fixes.md +++ b/apps/docs/de/quality/accessibility/severity-and-fixes.md @@ -4,12 +4,12 @@ Jede Regel emittiert ein `A11yIssue` mit einem von vier Schweregraden: -| Schweregrad | Bedeutung | UI | -| ----------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | -| `error` | Harter Barrierefreiheits-Fehler. Empfänger könnten von der Nachricht ausgeschlossen werden. | Roter Punkt auf dem Canvas, Gruppe "Fehler" in der Sidebar. | -| `warning` | Wahrscheinliches Problem – beheben, sofern Sie es nicht besser wissen. | Gelber Punkt, Gruppe "Warnungen". | -| `info` | Empfehlung; kein Defekt. | Kein Canvas-Badge, Gruppe "Info". | -| `off` | Override – deaktiviert die Regel komplett. | Nichts. | +| Schweregrad | Bedeutung | UI | +|---|---|---| +| `error` | Harter Barrierefreiheits-Fehler. Empfänger könnten von der Nachricht ausgeschlossen werden. | Roter Punkt auf dem Canvas, Gruppe "Fehler" in der Sidebar. | +| `warning` | Wahrscheinliches Problem – beheben, sofern Sie es nicht besser wissen. | Gelber Punkt, Gruppe "Warnungen". | +| `info` | Empfehlung; kein Defekt. | Kein Canvas-Badge, Gruppe "Info". | +| `off` | Override – deaktiviert die Regel komplett. | Nichts. | Der Schweregrad ist pro Regel über `options.rules` konfigurierbar – die Spalte "Severity" im Katalog ist nur der Standard. diff --git a/apps/docs/de/showcase.md b/apps/docs/de/showcase.md index c1f2924..85e0dd9 100644 --- a/apps/docs/de/showcase.md +++ b/apps/docs/de/showcase.md @@ -16,7 +16,6 @@ Das sind die Integrationsmuster, die wir am häufigsten sehen. Jedes beschreibt, Du baust (oder erweiterst) eine Transactional-E-Mail-API — Postmark, Resend, SES-Wrapper, interne Sende-Plattformen. Kunden binden ihren Code an, brauchen aber einen Weg, die Templates zu designen und zu versionieren, die die API verschickt. **Templatical passt, weil:** - - Templates sind JSON — versionierbar, diff-bar, KI-freundlich, leicht neben den Daten deiner Kunden zu speichern. - Die MJML-Ausgabe rendert konsistent über Outlook, Gmail, Apple Mail und den Long Tail an Clients hinweg. - Der Renderer läuft in Node — du kannst zur Versandzeit serverseitig zu HTML kompilieren, ohne eine anbietergebundene Render-API. @@ -28,7 +27,6 @@ Du baust (oder erweiterst) eine Transactional-E-Mail-API — Postmark, Resend, S Du baust ein Mailchimp-ähnliches Produkt, ein Automatisierungs-Tool oder eine Creator-Newsletter-Plattform. E-Mail-Komposition ist eine von mehreren Funktionen, die deine Kunden brauchen. **Templatical passt, weil:** - - Der Drop-in-Editor wird mit einem Funktionsaufruf gemountet — kein Umbau deines bestehenden Dashboards. - Theming über Design-Tokens sorgt dafür, dass die E-Mails deiner Kunden sich nativ in deine Marke einfügen, nicht in Templaticals. - Anzeigebedingungen und Merge-Tags sind eingebaut — wichtig für personalisierungslastige Newsletter-Nutzung. @@ -41,7 +39,6 @@ Du baust ein Mailchimp-ähnliches Produkt, ein Automatisierungs-Tool oder eine C Du baust ein CRM, ein Sales-Engagement-Tool oder eine Marketing-Automation-Plattform, in der Kunden gebrandete E-Mails als einen von vielen Workflows versenden. **Templatical passt, weil:** - - Es ist kein separater "E-Mail-Designer"-Tab — es bettet sich inline neben Kontakten, Kampagnen-Builder und Automation-Flows ein. - Eigene Blöcke lassen dich Daten deines Produkts (Kontaktfelder, Deal-Infos, berechnete Werte) als first-class Inhaltsblöcke anbieten, die Kunden in E-Mails ziehen können. - Echtzeit-Zusammenarbeit erlaubt es Sales- und Marketing-Teams, Kampagnen-Templates gemeinsam zu bearbeiten, ohne sich gegenseitig zu überschreiben. @@ -53,7 +50,6 @@ Du baust ein CRM, ein Sales-Engagement-Tool oder eine Marketing-Automation-Platt Du baust kein kundenfacings Produkt — du brauchst ein kontrolliertes Werkzeug, mit dem dein Team Transactional- und Marketing-E-Mails designt, mit dem JSON-Output gespeichert in euren eigenen Systemen. **Templatical passt, weil:** - - Vollständig selbst hostbar. Das OSS-SDK hat keine zwingende Cloud-Abhängigkeit. - Zweisprachig (en/de) out of the box, mit einem klaren Weg, weitere Locales für internationale Teams hinzuzufügen. - TypeScript-strict end-to-end, was die Anbindung an bestehende interne Tools und Codegen-Pipelines vereinfacht. @@ -65,7 +61,6 @@ Du baust kein kundenfacings Produkt — du brauchst ein kontrolliertes Werkzeug, Du bettest gar keinen visuellen Editor ein — du willst Templates programmatisch aus Daten erzeugen, in der Source Control versionieren und durch eine deterministische Pipeline rendern. **Templatical passt, weil:** - - Das Block-System hat Factory-Funktionen (`createTitleBlock`, `createImageBlock`, …) und vollständige TypeScript-Typen — du kannst Templates komplett in Code mit voller Typsicherheit bauen. - Der Renderer ist eine reine Funktion ohne DOM-Abhängigkeit. Lauffähig in Serverless, Edge oder Node. @@ -78,7 +73,6 @@ Siehe [Programmatische Templates](/de/guide/programmatic-templates) für einen v Wenn dein Team Templatical produktiv einsetzt, freuen wir uns, dich hier zu featuren. Öffne einen Pull Request mit deinem Eintrag oder [starte eine Diskussion](https://github.com/templatical/sdk/discussions), und wir machen es gemeinsam. Was wir auflisten: - - Eine kurze Produktbeschreibung (ein Satz) - Dein Logo und ein Link - Optional: ein Absatz dazu, wie ihr Templatical nutzt (welche Pakete, welche Funktionen, irgendetwas Bemerkenswertes) diff --git a/apps/docs/getting-started/how-rendering-works.md b/apps/docs/getting-started/how-rendering-works.md index 9b1676a..7ea22cb 100644 --- a/apps/docs/getting-started/how-rendering-works.md +++ b/apps/docs/getting-started/how-rendering-works.md @@ -56,17 +56,17 @@ Templatical separates template editing from template rendering. The editor produ 3. **HTML** -- The final output. MJML compiles to a complete HTML document with inline styles, nested tables, and client-specific workarounds. **You compile this step yourself** using any MJML library: - | Language | Library | - | -------- | ----------------------------------------------------------------------------- | - | Node.js | [mjml](https://www.npmjs.com/package/mjml) (official) | - | PHP | [spatie/mjml-php](https://github.com/spatie/mjml-php) | - | Python | [mrml-python](https://github.com/jdrouet/mrml/tree/main/packages/mrml-python) | - | Ruby | [mrml-ruby](https://github.com/hardpixel/mrml-ruby) | - | Rust | [mrml](https://github.com/jdrouet/mrml) | - | .NET | [Mjml.Net](https://github.com/SebastianStehle/mjml-net) | - | Elixir | [mjml_nif](https://github.com/adoptoposs/mjml_nif) | - - See the full list on [mjml.io/community](https://mjml.io/community). + | Language | Library | + |----------|---------| + | Node.js | [mjml](https://www.npmjs.com/package/mjml) (official) | + | PHP | [spatie/mjml-php](https://github.com/spatie/mjml-php) | + | Python | [mrml-python](https://github.com/jdrouet/mrml/tree/main/packages/mrml-python) | + | Ruby | [mrml-ruby](https://github.com/hardpixel/mrml-ruby) | + | Rust | [mrml](https://github.com/jdrouet/mrml) | + | .NET | [Mjml.Net](https://github.com/SebastianStehle/mjml-net) | + | Elixir | [mjml_nif](https://github.com/adoptoposs/mjml_nif) | + + See the full list on [mjml.io/community](https://mjml.io/community). ## Why MJML? diff --git a/apps/docs/getting-started/quick-start.md b/apps/docs/getting-started/quick-start.md index ad3b7ed..d5026be 100644 --- a/apps/docs/getting-started/quick-start.md +++ b/apps/docs/getting-started/quick-start.md @@ -16,60 +16,43 @@ npm install @templatical/editor @templatical/renderer ```html - - - - Templatical Editor - - - -
- -
-
+ + + + Templatical Editor + + + +
+ +
+
- - + await fetch('/api/templates', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content, mjml }), + }); + }; + + ``` diff --git a/apps/docs/guide/blocks.md b/apps/docs/guide/blocks.md index fe850f9..a71e3a3 100644 --- a/apps/docs/guide/blocks.md +++ b/apps/docs/guide/blocks.md @@ -13,110 +13,110 @@ To create blocks programmatically, see [Programmatic Templates](/guide/programma ## Choosing the right block -| Need | Block | Notes | -| ----------------------- | ----------------------------- | ---------------------------------------------------------------- | -| Headings, titles | [Title](#title) | Fixed-size headings (H1-H4) with block-level formatting | -| Body text, paragraphs | [Paragraph](#paragraph) | Rich text with inline formatting via TipTap | -| Photos, banners, logos | [Image](#image) | Optional link wrapping, responsive width | -| Call-to-action | [Button](#button) | Bulletproof buttons that work in all email clients | -| Multi-column layout | [Section](#section) | The only block that holds other blocks | -| Visual separation | [Divider](#divider) | Horizontal line with style options | -| Vertical spacing | [Spacer](#spacer) | Empty space between blocks | -| Social links | [Social Icons](#social-icons) | 16 platforms, 5 icon styles | -| Navigation links | [Menu](#menu) | Horizontal link list with separators | -| Tabular data | [Table](#table) | Data table with optional header styling | -| Video preview | [Video](#video) | Clickable thumbnail (email clients don't support embedded video) | -| Raw markup | [HTML](#html) | Escape hatch for custom code | -| Domain-specific content | [Custom](#custom) | Your own block types with fields and Liquid templates | +| Need | Block | Notes | +|------|-------|-------| +| Headings, titles | [Title](#title) | Fixed-size headings (H1-H4) with block-level formatting | +| Body text, paragraphs | [Paragraph](#paragraph) | Rich text with inline formatting via TipTap | +| Photos, banners, logos | [Image](#image) | Optional link wrapping, responsive width | +| Call-to-action | [Button](#button) | Bulletproof buttons that work in all email clients | +| Multi-column layout | [Section](#section) | The only block that holds other blocks | +| Visual separation | [Divider](#divider) | Horizontal line with style options | +| Vertical spacing | [Spacer](#spacer) | Empty space between blocks | +| Social links | [Social Icons](#social-icons) | 16 platforms, 5 icon styles | +| Navigation links | [Menu](#menu) | Horizontal link list with separators | +| Tabular data | [Table](#table) | Data table with optional header styling | +| Video preview | [Video](#video) | Clickable thumbnail (email clients don't support embedded video) | +| Raw markup | [HTML](#html) | Escape hatch for custom code | +| Domain-specific content | [Custom](#custom) | Your own block types with fields and Liquid templates | ## Title A heading block with fixed size levels. Use titles for headings, section headers, and other prominent text. -| Property | Type | Description | -| ------------ | ------------------------------- | -------------------------------------------------- | -| `content` | `string` | HTML content | -| `level` | `1 \| 2 \| 3 \| 4` | Heading level (H1=36px, H2=28px, H3=22px, H4=18px) | -| `color` | `string` | Text color | -| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `fontFamily` | `string` | Font family override | +| Property | Type | Description | +|----------|------|-------------| +| `content` | `string` | HTML content | +| `level` | `1 \| 2 \| 3 \| 4` | Heading level (H1=36px, H2=28px, H3=22px, H4=18px) | +| `color` | `string` | Text color | +| `textAlign` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `fontFamily` | `string` | Font family override | ## Paragraph Body text rendered as HTML. The editor uses [Tiptap](https://tiptap.dev) for inline editing with formatting controls (bold, italic, links, alignment, font size, color, etc.). All formatting is applied inline -- there are no block-level formatting properties. -| Property | Type | Description | -| --------- | -------- | ------------ | +| Property | Type | Description | +|----------|------|-------------| | `content` | `string` | HTML content | ## Image Displays an image with optional link wrapping. -| Property | Type | Description | -| ------------------ | ------------------------------- | ----------------------------------------------------------- | -| `src` | `string` | Image URL | -| `alt` | `string` | Alt text | -| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `linkUrl` | `string` | Wraps image in a link | -| `linkOpenInNewTab` | `boolean` | Link target behavior | -| `placeholderUrl` | `string` | Placeholder shown in the editor when `src` uses a merge tag | +| Property | Type | Description | +|----------|------|-------------| +| `src` | `string` | Image URL | +| `alt` | `string` | Alt text | +| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `linkUrl` | `string` | Wraps image in a link | +| `linkOpenInNewTab` | `boolean` | Link target behavior | +| `placeholderUrl` | `string` | Placeholder shown in the editor when `src` uses a merge tag | ## Button A call-to-action button with customizable appearance. -| Property | Type | Description | -| ----------------- | -------------- | ----------------------- | -| `text` | `string` | Button label | -| `url` | `string` | Link URL | -| `backgroundColor` | `string` | Button background color | -| `textColor` | `string` | Button text color | -| `borderRadius` | `number` | Corner radius in px | -| `fontSize` | `number` | Font size in px | -| `buttonPadding` | `SpacingValue` | Inner padding | -| `fontFamily` | `string` | Font family override | -| `openInNewTab` | `boolean` | Link target behavior | +| Property | Type | Description | +|----------|------|-------------| +| `text` | `string` | Button label | +| `url` | `string` | Link URL | +| `backgroundColor` | `string` | Button background color | +| `textColor` | `string` | Button text color | +| `borderRadius` | `number` | Corner radius in px | +| `fontSize` | `number` | Font size in px | +| `buttonPadding` | `SpacingValue` | Inner padding | +| `fontFamily` | `string` | Font family override | +| `openInNewTab` | `boolean` | Link target behavior | ## Divider A horizontal line separator. -| Property | Type | Description | -| ----------- | --------------------------------- | -------------------------------------- | -| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Line style | -| `color` | `string` | Line color | -| `thickness` | `number` | Line thickness in px | -| `width` | `number \| 'full'` | Line width in px, or `'full'` for 100% | +| Property | Type | Description | +|----------|------|-------------| +| `lineStyle` | `'solid' \| 'dashed' \| 'dotted'` | Line style | +| `color` | `string` | Line color | +| `thickness` | `number` | Line thickness in px | +| `width` | `number \| 'full'` | Line width in px, or `'full'` for 100% | ## Spacer Empty vertical space. -| Property | Type | Description | -| -------- | -------- | ------------ | +| Property | Type | Description | +|----------|------|-------------| | `height` | `number` | Height in px | ## HTML Injects raw HTML into the template. Use this for content that cannot be expressed with other block types. -| Property | Type | Description | -| --------- | -------- | --------------- | +| Property | Type | Description | +|----------|------|-------------| | `content` | `string` | Raw HTML markup | ## Social Icons A row of social media icons linking to platform profiles. -| Property | Type | Description | -| ----------- | ------------------------------------------------------------ | ------------------------- | -| `icons` | `SocialIcon[]` | List of social icons | -| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visual style | -| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon size | -| `spacing` | `number` | Space between icons in px | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| Property | Type | Description | +|----------|------|-------------| +| `icons` | `SocialIcon[]` | List of social icons | +| `iconStyle` | `'solid' \| 'outlined' \| 'rounded' \| 'square' \| 'circle'` | Visual style | +| `iconSize` | `'small' \| 'medium' \| 'large'` | Icon size | +| `spacing` | `number` | Space between icons in px | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | 16 platforms are supported: Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, Pinterest, Email, WhatsApp, Telegram, Discord, Snapchat, Reddit, GitHub, Dribbble, and Behance. @@ -134,17 +134,17 @@ interface SocialIcon { A horizontal navigation menu with text links. -| Property | Type | Description | -| ---------------- | ------------------------------- | ----------------------- | -| `items` | `MenuItemData[]` | Menu items | -| `fontSize` | `number` | Font size in px | -| `fontFamily` | `string` | Font family override | -| `color` | `string` | Text color | -| `linkColor` | `string` (optional) | Link color | -| `textAlign` | `'left' \| 'center' \| 'right'` | Alignment | -| `separator` | `string` | Character between items | -| `separatorColor` | `string` | Separator color | -| `spacing` | `number` | Space around separator | +| Property | Type | Description | +|----------|------|-------------| +| `items` | `MenuItemData[]` | Menu items | +| `fontSize` | `number` | Font size in px | +| `fontFamily` | `string` | Font family override | +| `color` | `string` | Text color | +| `linkColor` | `string` (optional) | Link color | +| `textAlign` | `'left' \| 'center' \| 'right'` | Alignment | +| `separator` | `string` | Character between items | +| `separatorColor` | `string` | Separator color | +| `spacing` | `number` | Space around separator | Each `MenuItemData` has: @@ -164,18 +164,18 @@ interface MenuItemData { A data table with optional header row styling. -| Property | Type | Description | -| ----------------------- | ------------------------------- | ------------------------- | -| `rows` | `TableRowData[]` | Table rows | -| `hasHeaderRow` | `boolean` | Style first row as header | -| `headerBackgroundColor` | `string` (optional) | Header row background | -| `borderColor` | `string` | Border color | -| `borderWidth` | `number` | Border width in px | -| `cellPadding` | `number` | Cell padding in px | -| `fontSize` | `number` | Font size in px | -| `fontFamily` | `string` | Font family override | -| `color` | `string` | Text color | -| `textAlign` | `'left' \| 'center' \| 'right'` | Cell text alignment | +| Property | Type | Description | +|----------|------|-------------| +| `rows` | `TableRowData[]` | Table rows | +| `hasHeaderRow` | `boolean` | Style first row as header | +| `headerBackgroundColor` | `string` (optional) | Header row background | +| `borderColor` | `string` | Border color | +| `borderWidth` | `number` | Border width in px | +| `cellPadding` | `number` | Cell padding in px | +| `fontSize` | `number` | Font size in px | +| `fontFamily` | `string` | Font family override | +| `color` | `string` | Text color | +| `textAlign` | `'left' \| 'center' \| 'right'` | Cell text alignment | ## Video @@ -185,32 +185,32 @@ Displays a video thumbnail that links to the video URL. Email clients do not support embedded video playback. The renderer outputs a clickable thumbnail image that links to the video URL. Always provide a good `thumbnailUrl` -- it's the only thing recipients see in their inbox. ::: -| Property | Type | Description | -| ---------------- | ------------------------------- | ----------------------------------------- | -| `url` | `string` | Video URL (YouTube, Vimeo, etc.) | -| `thumbnailUrl` | `string` | Thumbnail image URL | -| `alt` | `string` | Alt text for thumbnail | -| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `openInNewTab` | `boolean` | Link target behavior | -| `placeholderUrl` | `string` | Editor-only placeholder | +| Property | Type | Description | +|----------|------|-------------| +| `url` | `string` | Video URL (YouTube, Vimeo, etc.) | +| `thumbnailUrl` | `string` | Thumbnail image URL | +| `alt` | `string` | Alt text for thumbnail | +| `width` | `number \| 'full'` | Display width in px, or `'full'` for 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `openInNewTab` | `boolean` | Link target behavior | +| `placeholderUrl` | `string` | Editor-only placeholder | ## Section A layout container that holds one or more columns. See [Sections and Columns](/guide/sections-and-columns) for full details. -| Property | Type | Description | -| ---------- | -------------- | ------------------------------------- | -| `columns` | `ColumnLayout` | Column layout preset | -| `children` | `Block[][]` | Array of block arrays, one per column | +| Property | Type | Description | +|----------|------|-------------| +| `columns` | `ColumnLayout` | Column layout preset | +| `children` | `Block[][]` | Array of block arrays, one per column | ## Custom A user-defined block type powered by field definitions and a Liquid template. See [Custom Blocks](/guide/custom-blocks) for full details. -| Property | Type | Description | -| ------------------- | ------------------------- | ------------------------------------------- | -| `customType` | `string` | Unique identifier for the custom block type | -| `fieldValues` | `Record` | Current values for defined fields | -| `renderedHtml` | `string` | Cached rendered output | -| `dataSourceFetched` | `boolean` | Whether the data source has been fetched | +| Property | Type | Description | +|----------|------|-------------| +| `customType` | `string` | Unique identifier for the custom block type | +| `fieldValues` | `Record` | Current values for defined fields | +| `renderedHtml` | `string` | Cached rendered output | +| `dataSourceFetched` | `boolean` | Whether the data source has been fetched | diff --git a/apps/docs/guide/custom-blocks.md b/apps/docs/guide/custom-blocks.md index c8e26aa..850bd45 100644 --- a/apps/docs/guide/custom-blocks.md +++ b/apps/docs/guide/custom-blocks.md @@ -16,36 +16,23 @@ Custom blocks render inside the editor's Shadow DOM by default. If your custom b Pass custom block definitions through the editor config. The example below creates a "Testimonial" block with a quote, author details, avatar, and star rating. Once registered, users can drag it from the block palette into their template and edit each field from the settings panel. ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', customBlocks: [ { - type: "testimonial", - name: "Testimonial", + type: 'testimonial', + name: 'Testimonial', icon: '', - description: "Customer quote with photo and rating", + description: 'Customer quote with photo and rating', fields: [ - { key: "quote", label: "Quote", type: "textarea" }, - { key: "authorName", label: "Author Name", type: "text" }, - { key: "authorTitle", label: "Author Title", type: "text" }, - { key: "avatar", label: "Avatar", type: "image" }, - { - key: "rating", - label: "Rating (1-5)", - type: "number", - min: 1, - max: 5, - step: 1, - default: 5, - }, - { - key: "showRating", - label: "Show Rating", - type: "boolean", - default: true, - }, + { key: 'quote', label: 'Quote', type: 'textarea' }, + { key: 'authorName', label: 'Author Name', type: 'text' }, + { key: 'authorTitle', label: 'Author Title', type: 'text' }, + { key: 'avatar', label: 'Avatar', type: 'image' }, + { key: 'rating', label: 'Rating (1-5)', type: 'number', min: 1, max: 5, step: 1, default: 5 }, + { key: 'showRating', label: 'Show Rating', type: 'boolean', default: true }, ], template: ` @@ -112,15 +99,15 @@ interface CustomBlockDefinition { } ``` -| Property | Required | Description | -| ------------- | -------- | --------------------------------------------------------------------- | -| `type` | Yes | Unique identifier (used as `customType` on block instances) | -| `name` | Yes | Display name in the block palette | -| `icon` | No | Inline SVG string, image URL, or base64 data URI for the palette icon | -| `description` | No | Tooltip or subtitle in the palette | -| `fields` | Yes | Array of field definitions | -| `template` | Yes | Liquid template string for rendering | -| `dataSource` | No | External data fetching configuration | +| Property | Required | Description | +|----------|----------|-------------| +| `type` | Yes | Unique identifier (used as `customType` on block instances) | +| `name` | Yes | Display name in the block palette | +| `icon` | No | Inline SVG string, image URL, or base64 data URI for the palette icon | +| `description` | No | Tooltip or subtitle in the palette | +| `fields` | Yes | Array of field definitions | +| `template` | Yes | Liquid template string for rendering | +| `dataSource` | No | External data fetching configuration | ## Field types @@ -138,16 +125,16 @@ interface CustomBlockFieldBase { All field types extend this base. The `key` is used as the variable name in your Liquid template. Additional properties depend on the field `type`: -| Property | Applies to | Description | -| ---------------------- | ------------ | ----------------------------------------------- | -| `required` | All | Mark the field as required | -| `placeholder` | All | Placeholder text for the input | -| `readOnly` | All | Prevent user editing (useful with data sources) | -| `default` | All | Default value when the block is created | -| `min`, `max`, `step` | `number` | Numeric constraints | -| `options` | `select` | Array of `{ label, value }` choices | -| `fields` | `repeatable` | Sub-field definitions | -| `minItems`, `maxItems` | `repeatable` | Item count bounds | +| Property | Applies to | Description | +|----------|------------|-------------| +| `required` | All | Mark the field as required | +| `placeholder` | All | Placeholder text for the input | +| `readOnly` | All | Prevent user editing (useful with data sources) | +| `default` | All | Default value when the block is created | +| `min`, `max`, `step` | `number` | Numeric constraints | +| `options` | `select` | Array of `{ label, value }` choices | +| `fields` | `repeatable` | Sub-field definitions | +| `minItems`, `maxItems` | `repeatable` | Item count bounds | ### text @@ -321,9 +308,7 @@ Custom blocks become even more powerful when backed by an API data source. Inste ```ts interface DataSourceConfig { label: string; - onFetch: ( - context: DataSourceFetchContext, - ) => Promise | null>; + onFetch: (context: DataSourceFetchContext) => Promise | null>; } interface DataSourceFetchContext { @@ -385,36 +370,26 @@ An event invitation block with a schedule built using repeatable fields: ```ts const eventCard: CustomBlockDefinition = { - type: "event-card", - name: "Event Card", + type: 'event-card', + name: 'Event Card', icon: '', - description: "Event details with schedule and RSVP", + description: 'Event details with schedule and RSVP', fields: [ + { key: 'eventName', label: 'Event Name', type: 'text', default: 'Untitled Event' }, + { key: 'date', label: 'Date', type: 'text', default: 'January 1, 2026' }, + { key: 'venue', label: 'Venue', type: 'text' }, + { key: 'venueAddress', label: 'Venue Address', type: 'text' }, + { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, + { key: 'rsvpUrl', label: 'RSVP URL', type: 'text' }, { - key: "eventName", - label: "Event Name", - type: "text", - default: "Untitled Event", - }, - { key: "date", label: "Date", type: "text", default: "January 1, 2026" }, - { key: "venue", label: "Venue", type: "text" }, - { key: "venueAddress", label: "Venue Address", type: "text" }, - { - key: "accentColor", - label: "Accent Color", - type: "color", - default: "#4f46e5", - }, - { key: "rsvpUrl", label: "RSVP URL", type: "text" }, - { - key: "schedule", - label: "Schedule", - type: "repeatable", + key: 'schedule', + label: 'Schedule', + type: 'repeatable', minItems: 1, maxItems: 10, fields: [ - { key: "time", label: "Time", type: "text" }, - { key: "session", label: "Session", type: "text" }, + { key: 'time', label: 'Time', type: 'text' }, + { key: 'session', label: 'Session', type: 'text' }, ], }, ], @@ -447,39 +422,26 @@ A pricing block with a feature list and CTA button: ```ts const pricingTier: CustomBlockDefinition = { - type: "pricing-tier", - name: "Pricing Tier", + type: 'pricing-tier', + name: 'Pricing Tier', icon: '', - description: "Pricing card with features list", + description: 'Pricing card with features list', fields: [ - { key: "planName", label: "Plan Name", type: "text", default: "Pro" }, - { key: "price", label: "Price", type: "text", default: "$29/mo" }, - { - key: "highlighted", - label: "Highlighted", - type: "boolean", - default: false, - }, + { key: 'planName', label: 'Plan Name', type: 'text', default: 'Pro' }, + { key: 'price', label: 'Price', type: 'text', default: '$29/mo' }, + { key: 'highlighted', label: 'Highlighted', type: 'boolean', default: false }, + { key: 'accentColor', label: 'Accent Color', type: 'color', default: '#4f46e5' }, + { key: 'ctaLabel', label: 'Button Label', type: 'text', default: 'Get Started' }, + { key: 'ctaUrl', label: 'Button URL', type: 'text' }, { - key: "accentColor", - label: "Accent Color", - type: "color", - default: "#4f46e5", - }, - { - key: "ctaLabel", - label: "Button Label", - type: "text", - default: "Get Started", - }, - { key: "ctaUrl", label: "Button URL", type: "text" }, - { - key: "features", - label: "Features", - type: "repeatable", + key: 'features', + label: 'Features', + type: 'repeatable', minItems: 1, maxItems: 8, - fields: [{ key: "text", label: "Feature", type: "text" }], + fields: [ + { key: 'text', label: 'Feature', type: 'text' }, + ], }, ], template: ` diff --git a/apps/docs/guide/defaults.md b/apps/docs/guide/defaults.md index 7b9b349..0e091b8 100644 --- a/apps/docs/guide/defaults.md +++ b/apps/docs/guide/defaults.md @@ -12,31 +12,29 @@ Block properties (colors, font sizes, padding, placeholder text, etc.) are hardc Pass a `blockDefaults` object to `init()`. Each key maps to a block type and accepts a partial override of that block's properties: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', blockDefaults: { - title: { color: "#000000" }, + title: { color: '#000000' }, paragraph: {}, button: { - backgroundColor: "#ff6600", - textColor: "#ffffff", + backgroundColor: '#ff6600', + textColor: '#ffffff', styles: { padding: { top: 20, bottom: 20 } }, }, - divider: { color: "#eeeeee", thickness: 2 }, - image: { alt: "Brand image" }, + divider: { color: '#eeeeee', thickness: 2 }, + image: { alt: 'Brand image' }, }, }); ``` Defaults apply when: - - Dragging a block from the sidebar - Calling `createAndAddBlock()` programmatically Defaults do **not** apply when: - - Duplicating an existing block (the source block's values are preserved) - Loading saved content from the API @@ -59,29 +57,29 @@ Arrays are **replaced**, not merged. For example, setting `table.rows` replaces ### Supported Block Types -| Key | Block Type | -| ----------- | ------------ | -| `title` | Title | -| `paragraph` | Paragraph | -| `image` | Image | -| `button` | Button | -| `divider` | Divider | -| `section` | Section | -| `video` | Video | -| `social` | Social Icons | -| `spacer` | Spacer | -| `html` | HTML | -| `menu` | Menu | -| `table` | Table | +| Key | Block Type | +|-----|-----------| +| `title` | Title | +| `paragraph` | Paragraph | +| `image` | Image | +| `button` | Button | +| `divider` | Divider | +| `section` | Section | +| `video` | Video | +| `social` | Social Icons | +| `spacer` | Spacer | +| `html` | HTML | +| `menu` | Menu | +| `table` | Table | Custom blocks are not affected by `blockDefaults`. They use their own `default` values defined in the `CustomBlockDefinition` field configuration. ### TypeScript Type ```ts -import type { BlockDefaults } from "@templatical/editor"; +import type { BlockDefaults } from '@templatical/editor'; // or -import type { BlockDefaults } from "@templatical/types"; +import type { BlockDefaults } from '@templatical/types'; ``` Each block type key accepts `Partial>` — you can override any property except `id` and `type`, which are always generated automatically. See [Block Types](/guide/blocks) for the full list of available properties per block. @@ -92,12 +90,12 @@ Pass a `templateDefaults` object to override the default template settings used ```ts const editor = await init({ - container: "#editor", + container: '#editor', templateDefaults: { width: 640, - backgroundColor: "#f5f5f5", - fontFamily: "Helvetica, sans-serif", - preheaderText: "Check out our latest news", + backgroundColor: '#f5f5f5', + fontFamily: 'Helvetica, sans-serif', + preheaderText: 'Check out our latest news', }, }); ``` @@ -111,19 +109,19 @@ In other words, `templateDefaults` are fallbacks for missing content, not overri ### Available Settings -| Property | Default | Description | -| ----------------- | --------- | ------------------------- | -| `width` | `600` | Template width in pixels | +| Property | Default | Description | +|----------|---------|-------------| +| `width` | `600` | Template width in pixels | | `backgroundColor` | `#ffffff` | Template background color | -| `fontFamily` | `Arial` | Default font family | -| `preheaderText` | — | Email preheader text | +| `fontFamily` | `Arial` | Default font family | +| `preheaderText` | — | Email preheader text | ### TypeScript Type ```ts -import type { TemplateDefaults } from "@templatical/editor"; +import type { TemplateDefaults } from '@templatical/editor'; // or -import type { TemplateDefaults } from "@templatical/types"; +import type { TemplateDefaults } from '@templatical/types'; ``` ## Built-in Default Constants @@ -137,7 +135,7 @@ import { TITLE_BLOCK_DEFAULTS, PARAGRAPH_BLOCK_DEFAULTS, BUTTON_BLOCK_DEFAULTS, -} from "@templatical/types"; +} from '@templatical/types'; // Inspect the defaults for a single block type console.log(TITLE_BLOCK_DEFAULTS); @@ -150,7 +148,7 @@ console.log(DEFAULT_TEMPLATE_DEFAULTS); // Build a custom preset by extending a single block's defaults const myButtonDefaults = { ...BUTTON_BLOCK_DEFAULTS, - backgroundColor: "#ff6600", + backgroundColor: '#ff6600', borderRadius: 8, }; ``` @@ -159,27 +157,27 @@ const myButtonDefaults = { **Per-block constants** — each contains the default property values for that block type (excluding `id`, `type`, and `styles`): -| Constant | Block Type | -| ----------------------------- | ------------ | -| `TITLE_BLOCK_DEFAULTS` | Title | -| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | -| `IMAGE_BLOCK_DEFAULTS` | Image | -| `BUTTON_BLOCK_DEFAULTS` | Button | -| `DIVIDER_BLOCK_DEFAULTS` | Divider | -| `SECTION_BLOCK_DEFAULTS` | Section | -| `VIDEO_BLOCK_DEFAULTS` | Video | +| Constant | Block Type | +|----------|-----------| +| `TITLE_BLOCK_DEFAULTS` | Title | +| `PARAGRAPH_BLOCK_DEFAULTS` | Paragraph | +| `IMAGE_BLOCK_DEFAULTS` | Image | +| `BUTTON_BLOCK_DEFAULTS` | Button | +| `DIVIDER_BLOCK_DEFAULTS` | Divider | +| `SECTION_BLOCK_DEFAULTS` | Section | +| `VIDEO_BLOCK_DEFAULTS` | Video | | `SOCIAL_ICONS_BLOCK_DEFAULTS` | Social Icons | -| `SPACER_BLOCK_DEFAULTS` | Spacer | -| `HTML_BLOCK_DEFAULTS` | HTML | -| `MENU_BLOCK_DEFAULTS` | Menu | -| `TABLE_BLOCK_DEFAULTS` | Table | +| `SPACER_BLOCK_DEFAULTS` | Spacer | +| `HTML_BLOCK_DEFAULTS` | HTML | +| `MENU_BLOCK_DEFAULTS` | Menu | +| `TABLE_BLOCK_DEFAULTS` | Table | **Combined constants:** -| Constant | Description | -| --------------------------- | --------------------------------------------------------------------------------- | -| `DEFAULT_BLOCK_DEFAULTS` | All block type defaults in a single object (keys match `BlockDefaults` interface) | -| `DEFAULT_TEMPLATE_DEFAULTS` | Template settings defaults (`width`, `backgroundColor`, `fontFamily`) | +| Constant | Description | +|----------|-------------| +| `DEFAULT_BLOCK_DEFAULTS` | All block type defaults in a single object (keys match `BlockDefaults` interface) | +| `DEFAULT_TEMPLATE_DEFAULTS` | Template settings defaults (`width`, `backgroundColor`, `fontFamily`) | These constants are the single source of truth used internally by the factory functions. If you only need to override a few properties, you don't need to reference them — just pass your overrides to `blockDefaults` and they'll be deep-merged with these values. @@ -188,28 +186,23 @@ These constants are the single source of truth used internally by the factory fu The underlying factory functions also accept defaults directly: ```ts -import { - createBlock, - createTitleBlock, - createParagraphBlock, - createDefaultTemplateContent, -} from "@templatical/types"; -import type { BlockDefaults } from "@templatical/types"; +import { createBlock, createTitleBlock, createParagraphBlock, createDefaultTemplateContent } from '@templatical/types'; +import type { BlockDefaults } from '@templatical/types'; // Single block with partial overrides -const title = createTitleBlock({ level: 2, color: "#000" }); -const paragraph = createParagraphBlock({ content: "

Hello

" }); +const title = createTitleBlock({ level: 2, color: '#000' }); +const paragraph = createParagraphBlock({ content: '

Hello

' }); // Via createBlock with full defaults map const defaults: BlockDefaults = { - title: { color: "#000000" }, - button: { backgroundColor: "#ff6600" }, + title: { color: '#000000' }, + button: { backgroundColor: '#ff6600' }, }; -const block = createBlock("title", defaults); +const block = createBlock('title', defaults); // Template with custom settings -const content = createDefaultTemplateContent("Helvetica, sans-serif", { +const content = createDefaultTemplateContent('Helvetica, sans-serif', { width: 640, - backgroundColor: "#f5f5f5", + backgroundColor: '#f5f5f5', }); ``` diff --git a/apps/docs/guide/display-conditions.md b/apps/docs/guide/display-conditions.md index 732d587..8e1b906 100644 --- a/apps/docs/guide/display-conditions.md +++ b/apps/docs/guide/display-conditions.md @@ -12,23 +12,23 @@ Display conditions allow users to change block visibility based on conditions. W Define available conditions through the editor config: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', displayConditions: { conditions: [ { - label: "VIP Customers", - before: "{% if customer.vip == true %}", - after: "{% endif %}", - description: "Only shown to VIP customers", + label: 'VIP Customers', + before: '{% if customer.vip == true %}', + after: '{% endif %}', + description: 'Only shown to VIP customers', }, { - label: "Has Active Subscription", - before: "{% if subscription.active %}", - after: "{% endif %}", - description: "Shown when user has an active subscription", + label: 'Has Active Subscription', + before: '{% if subscription.active %}', + after: '{% endif %}', + description: 'Shown when user has an active subscription', }, ], }, @@ -47,13 +47,13 @@ interface DisplayCondition { } ``` -| Property | Required | Description | -| ------------- | -------- | ---------------------------------------------------- | -| `label` | Yes | Display name shown in the editor UI | -| `before` | Yes | Markup inserted before the block output | -| `after` | Yes | Markup inserted after the block output | -| `group` | No | Group name for organizing conditions in the dropdown | -| `description` | No | Explanatory text shown below the label | +| Property | Required | Description | +|----------|----------|-------------| +| `label` | Yes | Display name shown in the editor UI | +| `before` | Yes | Markup inserted before the block output | +| `after` | Yes | Markup inserted after the block output | +| `group` | No | Group name for organizing conditions in the dropdown | +| `description` | No | Explanatory text shown below the label | ## DisplayConditionsConfig diff --git a/apps/docs/guide/fonts.md b/apps/docs/guide/fonts.md index 69aad1a..7a662a4 100644 --- a/apps/docs/guide/fonts.md +++ b/apps/docs/guide/fonts.md @@ -10,46 +10,46 @@ By default, the editor includes a set of common web-safe fonts (Arial, Georgia, Configure which fonts are available using the `fonts` option: ```ts -import type { FontsConfig } from "@templatical/types"; +import type { FontsConfig } from '@templatical/types'; const fonts: FontsConfig = { - defaultFont: "Inter", - defaultFallback: "Arial, sans-serif", + defaultFont: 'Inter', + defaultFallback: 'Arial, sans-serif', customFonts: [ { - name: "Inter", - url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap", - fallback: "Helvetica, Arial, sans-serif", + name: 'Inter', + url: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap', + fallback: 'Helvetica, Arial, sans-serif', }, { - name: "Merriweather", - url: "https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap", - fallback: "Georgia, serif", + name: 'Merriweather', + url: 'https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap', + fallback: 'Georgia, serif', }, ], }; const editor = await init({ - container: "#editor", + container: '#editor', fonts, }); ``` ## FontsConfig -| Property | Type | Description | -| ----------------- | -------------- | ----------------------------------------------------- | -| `defaultFont` | `string` | Font name selected by default in new templates | -| `defaultFallback` | `string` | Fallback stack used when a custom font is unavailable | -| `customFonts` | `CustomFont[]` | List of custom fonts to register | +| Property | Type | Description | +|---|---|---| +| `defaultFont` | `string` | Font name selected by default in new templates | +| `defaultFallback` | `string` | Fallback stack used when a custom font is unavailable | +| `customFonts` | `CustomFont[]` | List of custom fonts to register | ## CustomFont -| Property | Type | Description | -| ---------- | -------- | --------------------------------------------- | -| `name` | `string` | Display name in the font picker | -| `url` | `string` | URL to the font CSS (e.g., Google Fonts link) | -| `fallback` | `string` | Optional fallback font stack for this font | +| Property | Type | Description | +|---|---|---| +| `name` | `string` | Display name in the font picker | +| `url` | `string` | URL to the font CSS (e.g., Google Fonts link) | +| `fallback` | `string` | Optional fallback font stack for this font | Custom fonts are automatically included as `` declarations in the rendered MJML output. diff --git a/apps/docs/guide/i18n.md b/apps/docs/guide/i18n.md index 7791408..0536b1c 100644 --- a/apps/docs/guide/i18n.md +++ b/apps/docs/guide/i18n.md @@ -12,32 +12,32 @@ The editor UI supports locale switching. All labels, tooltips, placeholders, and Pass the `locale` option to `init()`: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", - locale: "de", + container: '#editor', + locale: 'de', }); ``` ## Built-in locales -| Code | Language | -| ---- | ----------------- | +| Code | Language | +|---|---| | `en` | English (default) | -| `de` | German | +| `de` | German | ## Locale resolution The editor normalizes locale codes by stripping region suffixes: -| Input | Resolved | -| --------- | ----------------------------------------- | -| `'en'` | `en` | -| `'en-US'` | `en` | -| `'en-GB'` | `en` | -| `'de-AT'` | `de` | -| `'fr'` | `en` (unsupported, falls back to English) | +| Input | Resolved | +|---|---| +| `'en'` | `en` | +| `'en-US'` | `en` | +| `'en-GB'` | `en` | +| `'de-AT'` | `de` | +| `'fr'` | `en` (unsupported, falls back to English) | If the resolved locale is not supported, the editor falls back to English silently. @@ -49,7 +49,7 @@ Locale files are loaded asynchronously using dynamic `import()`. Only the active async function switchLocale(newLocale: string) { editor.unmount(); editor = await init({ - container: "#editor", + container: '#editor', locale: newLocale, }); } @@ -111,17 +111,17 @@ Example structure for a new locale: // packages/editor/src/i18n/locales/fr.ts export default { blocks: { - paragraph: "Paragraphe", - image: "Image", - button: "Bouton", - section: "Section", - divider: "Séparateur", - spacer: "Espacement", + paragraph: 'Paragraphe', + image: 'Image', + button: 'Bouton', + section: 'Section', + divider: 'Séparateur', + spacer: 'Espacement', // ... all keys from en.ts }, toolbar: { - duplicate: "Dupliquer", - delete: "Supprimer", + duplicate: 'Dupliquer', + delete: 'Supprimer', // ... }, // ... all sections from en.ts diff --git a/apps/docs/guide/images.md b/apps/docs/guide/images.md index 22538f2..4c3b4b3 100644 --- a/apps/docs/guide/images.md +++ b/apps/docs/guide/images.md @@ -18,10 +18,10 @@ When the `onRequestMedia` callback is provided, a browse button appears alongsid The editor calls this function whenever the user clicks the button. Return a `MediaResult` object, or `null` if the user cancels. When `alt` is provided, the editor automatically fills in the image's alt text. ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', async onRequestMedia() { // Open your own modal, file browser, or asset manager const image = await openMyMediaModal(); @@ -43,19 +43,20 @@ interface MediaResult { onRequestMedia?: (context?: MediaRequestContext) => Promise; ``` + ## Image Block Properties The `ImageBlock` type defines all configurable properties: -| Property | Type | Description | -| ------------------ | ------------------------------- | ----------------------------------------------------- | -| `src` | `string` | Image source URL | -| `alt` | `string` | Alt text for accessibility | -| `width` | `number \| 'full'` | Image width in pixels, or `'full'` for 100% | -| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | -| `linkUrl` | `string` (optional) | Wraps the image in a link | -| `linkOpenInNewTab` | `boolean` (optional) | Opens the link in a new tab | -| `placeholderUrl` | `string` (optional) | Design-time preview image when `src` uses a merge tag | +| Property | Type | Description | +|---|---|---| +| `src` | `string` | Image source URL | +| `alt` | `string` | Alt text for accessibility | +| `width` | `number \| 'full'` | Image width in pixels, or `'full'` for 100% | +| `align` | `'left' \| 'center' \| 'right'` | Horizontal alignment | +| `linkUrl` | `string` (optional) | Wraps the image in a link | +| `linkOpenInNewTab` | `boolean` (optional) | Opens the link in a new tab | +| `placeholderUrl` | `string` (optional) | Design-time preview image when `src` uses a merge tag | ### Placeholder URL diff --git a/apps/docs/guide/merge-tags.md b/apps/docs/guide/merge-tags.md index 4daeeef..545b565 100644 --- a/apps/docs/guide/merge-tags.md +++ b/apps/docs/guide/merge-tags.md @@ -20,17 +20,17 @@ Hovering over a tag reveals the raw value behind the label. The `syntax` property is optional and defaults to `'liquid'`. ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', mergeTags: { tags: [ - { label: "First Name", value: "{{first_name}}" }, - { label: "Last Name", value: "{{last_name}}" }, - { label: "Email", value: "{{email}}" }, - { label: "Company", value: "{{company.name}}" }, - { label: "Unsubscribe URL", value: "{{unsubscribe_url}}" }, + { label: 'First Name', value: '{{first_name}}' }, + { label: 'Last Name', value: '{{last_name}}' }, + { label: 'Email', value: '{{email}}' }, + { label: 'Company', value: '{{company.name}}' }, + { label: 'Unsubscribe URL', value: '{{unsubscribe_url}}' }, ], }, }); @@ -56,16 +56,15 @@ The `value` must include the syntax delimiters. For example, with Liquid syntax: Templatical includes four built-in syntax presets. The `syntax` setting tells the editor how to detect and highlight both data tags and logic tags in content. Each preset defines two patterns: - - **Data tags** -- variable merge tags like a recipient's name or email - **Logic tags** -- control flow statements like conditionals and loops -| Preset | Data tag | Logic tag | Platform | -| -------------- | --------------------------------- | ------------------------------- | ------------------------------- | -| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | -| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | -| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | -| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | +| Preset | Data tag | Logic tag | Platform | +|--------|----------|-----------|----------| +| `'liquid'` | {{first_name}} | {% if vip %} | Shopify, Jekyll, Django, Jinja2 | +| `'handlebars'` | {{first_name}} | {{#if vip}} | Handlebars.js, Mandrill | +| `'mailchimp'` | `*\|FIRST_NAME\|*` | `*\|IF:VIP\|*` | Mailchimp | +| `'ampscript'` | `%%=first_name=%%` | `%%[IF @vip]%%` | Salesforce Marketing Cloud | ```ts mergeTags: { @@ -91,35 +90,34 @@ Like data tags, logic tags pass through unchanged in the rendered MJML — your Examples of logic tags by preset: ::: code-group - ```html [Liquid] {% if customer.vip %} -

Exclusive offer just for you!

-{% endif %} {% for item in cart.items %} -

{{item.name}} - {{item.price}}

+

Exclusive offer just for you!

+{% endif %} + +{% for item in cart.items %} +

{{item.name}} - {{item.price}}

{% endfor %} ``` - ```html [Handlebars] {{#if hasSubscription}} -

Your plan renews on {{renewal_date}}

-{{/if}} {{#each products}} -

{{this.name}}

+

Your plan renews on {{renewal_date}}

+{{/if}} + +{{#each products}} +

{{this.name}}

{{/each}} ``` - ```html [Mailchimp] *|IF:VIP|* -

VIP discount applied

+

VIP discount applied

*|END:IF|* ``` - ```html [AMPscript] %%[IF @subscriber_type == "premium"]%% -

Premium content here

+

Premium content here

%%[ENDIF]%% ``` - ::: ## Custom syntax @@ -128,8 +126,8 @@ If the built-in presets don't match your platform, define a custom syntax with t ```ts interface SyntaxPreset { - value: RegExp; // matches data tags like ${user.name} - logic: RegExp; // matches logic tags like $[IF ...] + value: RegExp; // matches data tags like ${user.name} + logic: RegExp; // matches logic tags like $[IF ...] } ``` @@ -165,10 +163,12 @@ To opt out explicitly, set `autocomplete: false`: ```ts const editor = await init({ - container: "#editor", + container: '#editor', mergeTags: { autocomplete: false, - tags: [{ label: "First Name", value: "{{first_name}}" }], + tags: [ + { label: 'First Name', value: '{{first_name}}' }, + ], }, }); ``` @@ -181,7 +181,7 @@ For large or context-dependent tag lists, use the `onRequest` callback instead o ```ts const editor = await init({ - container: "#editor", + container: '#editor', mergeTags: { onRequest: async () => { const tag = await showMyMergeTagPicker(); diff --git a/apps/docs/guide/migration-from-beefree.md b/apps/docs/guide/migration-from-beefree.md index 004cd53..e699752 100644 --- a/apps/docs/guide/migration-from-beefree.md +++ b/apps/docs/guide/migration-from-beefree.md @@ -20,19 +20,17 @@ npm install @templatical/import-beefree ## Usage ```ts -import { convertBeeFreeTemplate } from "@templatical/import-beefree"; +import { convertBeeFreeTemplate } from '@templatical/import-beefree'; // Load your BeeFree template JSON -const beefreeJson = await fetch("/api/beefree-templates/123").then((r) => - r.json(), -); +const beefreeJson = await fetch('/api/beefree-templates/123').then(r => r.json()); // Convert to Templatical format const { content, report } = convertBeeFreeTemplate(beefreeJson); // Use in the editor const editor = await init({ - container: "#editor", + container: '#editor', content, }); @@ -41,7 +39,6 @@ console.log(report); ``` The function returns an `ImportResult` with: - - `content` — the converted `TemplateContent` ready for the editor - `report` — a conversion report with the status of each block (`converted`, `approximated`, `html-fallback`, or `skipped`) @@ -49,21 +46,21 @@ The function returns an `ImportResult` with: BeeFree block types map to Templatical equivalents: -| BeeFree Module | Templatical Block | Status | -| -------------- | ----------------- | -------------------------------- | -| Text | `paragraph` | Converted | -| Paragraph | `paragraph` | Converted | -| Heading | `title` | Converted | -| List | `paragraph` | Converted | -| Image | `image` | Converted | -| Button | `button` | Converted | -| Divider | `divider` | Converted | -| Spacer | `spacer` | Converted | -| Social | `social` | Converted (16 platforms mapped) | -| Html | `html` | Converted | -| Menu | `menu` | Approximated (styles may differ) | -| Video | `video` | Converted | -| Table | `table` | Converted | +| BeeFree Module | Templatical Block | Status | +|---|---|---| +| Text | `paragraph` | Converted | +| Paragraph | `paragraph` | Converted | +| Heading | `title` | Converted | +| List | `paragraph` | Converted | +| Image | `image` | Converted | +| Button | `button` | Converted | +| Divider | `divider` | Converted | +| Spacer | `spacer` | Converted | +| Social | `social` | Converted (16 platforms mapped) | +| Html | `html` | Converted | +| Menu | `menu` | Approximated (styles may differ) | +| Video | `video` | Converted | +| Table | `table` | Converted | Unknown module types are converted to HTML blocks as a fallback. @@ -71,13 +68,13 @@ Unknown module types are converted to HTML blocks as a fallback. BeeFree organizes content into rows with columns. These map to Templatical's `SectionBlock` with the appropriate `ColumnLayout`: -| BeeFree Columns | Templatical Layout | -| ------------------ | ------------------ | -| 1 column (100%) | `'1'` | -| 2 equal columns | `'2'` | -| 3 equal columns | `'3'` | -| 2 columns (~33/66) | `'1-2'` | -| 2 columns (~66/33) | `'2-1'` | +| BeeFree Columns | Templatical Layout | +|---|---| +| 1 column (100%) | `'1'` | +| 2 equal columns | `'2'` | +| 3 equal columns | `'3'` | +| 2 columns (~33/66) | `'1-2'` | +| 2 columns (~66/33) | `'2-1'` | Column widths that don't match a standard ratio are mapped to the closest available layout. diff --git a/apps/docs/guide/migration-from-html.md b/apps/docs/guide/migration-from-html.md index 0435b8c..55bfed0 100644 --- a/apps/docs/guide/migration-from-html.md +++ b/apps/docs/guide/migration-from-html.md @@ -20,17 +20,17 @@ npm install @templatical/import-html ## Usage ```ts -import { convertHtmlTemplate } from "@templatical/import-html"; +import { convertHtmlTemplate } from '@templatical/import-html'; // Load the raw HTML source of an email -const html = await fetch("/path/to/email.html").then((r) => r.text()); +const html = await fetch('/path/to/email.html').then((r) => r.text()); // Convert to Templatical format const { content, report } = convertHtmlTemplate(html); // Use in the editor const editor = await init({ - container: "#editor", + container: '#editor', content, }); @@ -39,7 +39,6 @@ console.log(report); ``` The function returns an `ImportResult` with: - - `content` — the converted `TemplateContent` ready for the editor - `report` — a conversion report with the status of each element (`converted`, `approximated`, `html-fallback`, or `skipped`) @@ -47,20 +46,20 @@ The function returns an `ImportResult` with: HTML elements map to Templatical equivalents: -| HTML Element | Templatical Block | Status | -| --------------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------- | -| `

` – `

` | `title` | Converted (level preserved) | -| `

` – `
` | `title` | Converted (clamped to level 4) | -| `

` / text-only `

` with explicit height | `spacer` | Converted | -| `` containing a single styled `` | `button` | Converted (cell-as-button pattern) | -| `` (layout, multi-row/column) | `section` (one per ``) | Converted | -| `
` (data table — text-only cells) | `html` | HTML fallback | -| Unknown / custom elements | `html` | HTML fallback | +| HTML Element | Templatical Block | Status | +|---|---|---| +| `

` – `

` | `title` | Converted (level preserved) | +| `

` – `
` | `title` | Converted (clamped to level 4) | +| `

` / text-only `

` with explicit height | `spacer` | Converted | +| `` containing a single styled `` | `button` | Converted (cell-as-button pattern) | +| `` (layout, multi-row/column) | `section` (one per ``) | Converted | +| `
` (data table — text-only cells) | `html` | HTML fallback | +| Unknown / custom elements | `html` | HTML fallback | Anything that can't be mapped is preserved verbatim inside an HTML block, so no visible content is lost. @@ -68,12 +67,12 @@ Anything that can't be mapped is preserved verbatim inside an HTML block, so no Each `` in a layout table becomes a `SectionBlock`. Cell counts map directly: -| Cells per row | Templatical Layout | -| ------------- | --------------------------------- | -| 1 | `'1'` | -| 2 | `'2'` | -| 3 | `'3'` | -| 4+ | flattened to `'1'` with a warning | +| Cells per row | Templatical Layout | +|---|---| +| 1 | `'1'` | +| 2 | `'2'` | +| 3 | `'3'` | +| 4+ | flattened to `'1'` with a warning | Templatical sections cannot nest. Tables nested inside a `` and `` and `
` are flattened — their inner blocks are merged into the parent cell. @@ -128,8 +127,11 @@ console.log(report.summary); // { total: 12, converted: 10, approximated: 1, htmlFallback: 1, skipped: 0 } for (const entry of report.entries) { - if (entry.status === "html-fallback") { - console.warn(`Element <${entry.sourceTag}> preserved as HTML:`, entry.note); + if (entry.status === 'html-fallback') { + console.warn( + `Element <${entry.sourceTag}> preserved as HTML:`, + entry.note, + ); } } diff --git a/apps/docs/guide/migration-from-mjml.md b/apps/docs/guide/migration-from-mjml.md index dd14d8d..1ffa8ff 100644 --- a/apps/docs/guide/migration-from-mjml.md +++ b/apps/docs/guide/migration-from-mjml.md @@ -15,7 +15,7 @@ MJML → Templatical is harder to fully automate than BeeFree → Templatical, b ## What's actually happening here -This is a slightly counter-intuitive migration. Templatical's renderer produces _MJML output_ — so on the surface, MJML and Templatical look identical. But: +This is a slightly counter-intuitive migration. Templatical's renderer produces *MJML output* — so on the surface, MJML and Templatical look identical. But: - **MJML** is a markup language. You write XML-like tags (``, ``, ``) and the MJML compiler turns them into table-based HTML. - **Templatical** stores templates as a JSON tree of typed blocks (`SectionBlock`, `ParagraphBlock`, etc.) and renders that tree to MJML at export time. @@ -40,7 +40,7 @@ Most MJML templates port in 10–20 minutes once you've done one or two. Once you've rebuilt a template visually: ```ts -import { renderToMjml } from "@templatical/renderer"; +import { renderToMjml } from '@templatical/renderer'; const mjml = await renderToMjml(content); // Compare this MJML against your original MJML source. @@ -55,19 +55,19 @@ If you have hundreds of MJML templates and want to attempt automated conversion Here's the rough shape: ```ts -import { parse } from "node-html-parser"; +import { parse } from 'node-html-parser'; import { createSectionBlock, createTitleBlock, createParagraphBlock, createImageBlock, createButtonBlock, -} from "@templatical/types"; -import type { TemplateContent, Block } from "@templatical/types"; +} from '@templatical/types'; +import type { TemplateContent, Block } from '@templatical/types'; function mjmlToTemplate(mjml: string): TemplateContent { const root = parse(mjml); - const body = root.querySelector("mj-body"); + const body = root.querySelector('mj-body'); const blocks: Block[] = (body?.childNodes ?? []) .map((node) => convertNode(node)) @@ -76,17 +76,17 @@ function mjmlToTemplate(mjml: string): TemplateContent { return { blocks, settings: { - width: parseInt(body?.getAttribute("width") ?? "600"), - backgroundColor: body?.getAttribute("background-color") ?? "#ffffff", + width: parseInt(body?.getAttribute('width') ?? '600'), + backgroundColor: body?.getAttribute('background-color') ?? '#ffffff', }, }; } function convertNode(node: any): Block | null { switch (node.tagName?.toLowerCase()) { - case "mj-section": + case 'mj-section': return convertSection(node); - case "mj-text": + case 'mj-text': return convertText(node); // …more cases — see the mapping table below default: @@ -101,22 +101,22 @@ A handwritten parser will miss edge cases — nested `mj-wrapper`, custom compon ## MJML tag mapping {#mjml-tag-mapping} -| MJML tag | Templatical block | Notes | -| ---------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -| `mj-section` (containing `mj-column`s) | `SectionBlock` with `columns` | Multi-column layouts work the same way; column widths come from MJML's `width` attribute or are equally distributed. | -| `mj-column` | Section column | A column holds a list of nested blocks. | -| `mj-text` | `ParagraphBlock` (or `TitleBlock` if it's a heading) | Use heading-level inline styles to decide between Title and Paragraph. | -| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, padding. | -| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, font, padding. | -| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, padding. | -| `mj-spacer` | `SpacerBlock` | `height`. | -| `mj-social` (with `mj-social-element`) | `SocialIconsBlock` | Each `mj-social-element` → a `SocialIcon` entry. | -| `mj-navbar` (with `mj-navbar-link`) | `MenuBlock` | Each link → `MenuItemData`. | -| `mj-table` | `TableBlock` | Map `
` rows/cells to Templatical's table data. | -| `mj-raw` | `HtmlBlock` | Pass-through HTML. | -| `mj-wrapper` | `SectionBlock` (often) | A wrapper without columns becomes a section with one column. | -| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (fallback) | Templatical doesn't have direct equivalents yet — convert the rendered HTML to a raw HTML block, or wait for the importer. | -| `mj-head` content | Template `settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` map to `TemplateSettings.preheaderText`, custom fonts, and theme overrides. | +| MJML tag | Templatical block | Notes | +|---|---|---| +| `mj-section` (containing `mj-column`s) | `SectionBlock` with `columns` | Multi-column layouts work the same way; column widths come from MJML's `width` attribute or are equally distributed. | +| `mj-column` | Section column | A column holds a list of nested blocks. | +| `mj-text` | `ParagraphBlock` (or `TitleBlock` if it's a heading) | Use heading-level inline styles to decide between Title and Paragraph. | +| `mj-image` | `ImageBlock` | `src`, `alt`, `href`, `width`, padding. | +| `mj-button` | `ButtonBlock` | `href`, `background-color`, `color`, font, padding. | +| `mj-divider` | `DividerBlock` | `border-color`, `border-width`, padding. | +| `mj-spacer` | `SpacerBlock` | `height`. | +| `mj-social` (with `mj-social-element`) | `SocialIconsBlock` | Each `mj-social-element` → a `SocialIcon` entry. | +| `mj-navbar` (with `mj-navbar-link`) | `MenuBlock` | Each link → `MenuItemData`. | +| `mj-table` | `TableBlock` | Map `
` rows/cells to Templatical's table data. | +| `mj-raw` | `HtmlBlock` | Pass-through HTML. | +| `mj-wrapper` | `SectionBlock` (often) | A wrapper without columns becomes a section with one column. | +| `mj-hero`, `mj-carousel`, `mj-accordion` | `HtmlBlock` (fallback) | Templatical doesn't have direct equivalents yet — convert the rendered HTML to a raw HTML block, or wait for the importer. | +| `mj-head` content | Template `settings` | `mj-title`, `mj-preview`, `mj-attributes`, `mj-font`, `mj-style` map to `TemplateSettings.preheaderText`, custom fonts, and theme overrides. | ## Things that don't map automatically diff --git a/apps/docs/guide/migration-from-unlayer.md b/apps/docs/guide/migration-from-unlayer.md index 8809941..b2d582d 100644 --- a/apps/docs/guide/migration-from-unlayer.md +++ b/apps/docs/guide/migration-from-unlayer.md @@ -20,19 +20,17 @@ npm install @templatical/import-unlayer ## Usage ```ts -import { convertUnlayerTemplate } from "@templatical/import-unlayer"; +import { convertUnlayerTemplate } from '@templatical/import-unlayer'; // Load your Unlayer design JSON (whatever editor.saveDesign returned) -const unlayerJson = await fetch("/api/unlayer-templates/123").then((r) => - r.json(), -); +const unlayerJson = await fetch('/api/unlayer-templates/123').then(r => r.json()); // Convert to Templatical format const { content, report } = convertUnlayerTemplate(unlayerJson); // Use in the editor const editor = await init({ - container: "#editor", + container: '#editor', content, }); @@ -41,7 +39,6 @@ console.log(report); ``` The function returns an `ImportResult` with: - - `content` — the converted `TemplateContent` ready for the editor - `report` — a conversion report with the status of each content node (`converted`, `approximated`, `html-fallback`, or `skipped`) @@ -49,19 +46,19 @@ The function returns an `ImportResult` with: Unlayer content types map to Templatical equivalents: -| Unlayer Content | Templatical Block | Status | -| --------------- | ----------------- | -------------------------------- | -| Text | `paragraph` | Converted | -| Heading | `title` | Converted | -| Image | `image` | Converted | -| Button | `button` | Converted | -| Divider | `divider` | Converted | -| Html | `html` | Converted | -| Menu | `menu` | Approximated (styles may differ) | -| Social | `social` | Converted (16 platforms mapped) | -| Video | `video` | Converted | -| Timer | `html` | HTML fallback (rebuild manually) | -| Form | — | Skipped | +| Unlayer Content | Templatical Block | Status | +|---|---|---| +| Text | `paragraph` | Converted | +| Heading | `title` | Converted | +| Image | `image` | Converted | +| Button | `button` | Converted | +| Divider | `divider` | Converted | +| Html | `html` | Converted | +| Menu | `menu` | Approximated (styles may differ) | +| Social | `social` | Converted (16 platforms mapped) | +| Video | `video` | Converted | +| Timer | `html` | HTML fallback (rebuild manually) | +| Form | — | Skipped | Unknown content types are converted to HTML blocks as a fallback. @@ -69,14 +66,14 @@ Unknown content types are converted to HTML blocks as a fallback. Unlayer organizes content into rows with columns whose widths come from a `cells` weight array. These map to Templatical's `SectionBlock` with the appropriate `ColumnLayout`: -| Unlayer cells | Templatical Layout | -| -------------------------- | ----------------------------------------- | -| `[1]` (single column) | flattened — no section wrapper | -| `[1, 1]` (equal halves) | `'2'` | -| `[1, 1, 1]` (equal thirds) | `'3'` | -| `[1, 2]` | `'1-2'` | -| `[2, 1]` | `'2-1'` | -| 4+ cells | flattened to single column with a warning | +| Unlayer cells | Templatical Layout | +|---|---| +| `[1]` (single column) | flattened — no section wrapper | +| `[1, 1]` (equal halves) | `'2'` | +| `[1, 1, 1]` (equal thirds) | `'3'` | +| `[1, 2]` | `'1-2'` | +| `[2, 1]` | `'2-1'` | +| 4+ cells | flattened to single column with a warning | Cell ratios that don't match a standard layout are mapped to the closest available one. diff --git a/apps/docs/guide/programmatic-templates.md b/apps/docs/guide/programmatic-templates.md index 8386a97..3f3aa25 100644 --- a/apps/docs/guide/programmatic-templates.md +++ b/apps/docs/guide/programmatic-templates.md @@ -12,7 +12,7 @@ Templatical provides factory functions for every block type. Use them to build t ## Blank template ```ts -import { createDefaultTemplateContent } from "@templatical/types"; +import { createDefaultTemplateContent } from '@templatical/types'; const content = createDefaultTemplateContent(); // { blocks: [], settings: { width: 600, backgroundColor: '#ffffff', fontFamily: 'Arial' } } @@ -21,7 +21,7 @@ const content = createDefaultTemplateContent(); `createDefaultTemplateContent()` accepts an optional font family string: ```ts -const content = createDefaultTemplateContent("Georgia, serif"); +const content = createDefaultTemplateContent('Georgia, serif'); ``` ## Building a template @@ -36,7 +36,7 @@ import { createImageBlock, createButtonBlock, createDividerBlock, -} from "@templatical/types"; +} from '@templatical/types'; const content = createDefaultTemplateContent(); @@ -46,19 +46,19 @@ content.blocks = [ level: 1, }), createImageBlock({ - src: "https://example.com/hero.jpg", - alt: "Welcome hero image", - width: "full", + src: 'https://example.com/hero.jpg', + alt: 'Welcome hero image', + width: 'full', }), createDividerBlock(), createParagraphBlock({ - content: "

Thanks for signing up. Here is what happens next.

", + content: '

Thanks for signing up. Here is what happens next.

', }), createButtonBlock({ - text: "Get Started", - url: "https://example.com/dashboard", - backgroundColor: "#1a73e8", - textColor: "#ffffff", + text: 'Get Started', + url: 'https://example.com/dashboard', + backgroundColor: '#1a73e8', + textColor: '#ffffff', borderRadius: 6, }), ]; @@ -70,56 +70,56 @@ content.blocks = [ ```ts createTitleBlock({ - content: "

Welcome, {{name}}!

", + content: '

Welcome, {{name}}!

', level: 1, - textAlign: "center", -}); + textAlign: 'center', +}) ``` ### Paragraph ```ts createParagraphBlock({ - content: "

Thanks for signing up. Here is what happens next.

", -}); + content: '

Thanks for signing up. Here is what happens next.

', +}) ``` ### Image ```ts createImageBlock({ - src: "https://cdn.example.com/hero.png", - alt: "Hero banner", + src: 'https://cdn.example.com/hero.png', + alt: 'Hero banner', width: 560, - linkUrl: "https://example.com", -}); + linkUrl: 'https://example.com', +}) ``` ### Button ```ts createButtonBlock({ - text: "Get Started", - url: "https://example.com/signup", - backgroundColor: "#6366f1", + text: 'Get Started', + url: 'https://example.com/signup', + backgroundColor: '#6366f1', borderRadius: 8, -}); +}) ``` ### Divider ```ts createDividerBlock({ - lineStyle: "dashed", - color: "#e5e7eb", + lineStyle: 'dashed', + color: '#e5e7eb', thickness: 2, -}); +}) ``` ### Spacer ```ts -createSpacerBlock({ height: 40 }); +createSpacerBlock({ height: 40 }) ``` ### HTML @@ -127,24 +127,20 @@ createSpacerBlock({ height: 40 }); ```ts createHtmlBlock({ content: '
Custom markup
', -}); +}) ``` ### Social Icons ```ts createSocialIconsBlock({ - iconStyle: "circle", - iconSize: "large", + iconStyle: 'circle', + iconSize: 'large', icons: [ - { id: crypto.randomUUID(), platform: "twitter", url: "https://x.com/acme" }, - { - id: crypto.randomUUID(), - platform: "github", - url: "https://github.com/acme", - }, + { id: crypto.randomUUID(), platform: 'twitter', url: 'https://x.com/acme' }, + { id: crypto.randomUUID(), platform: 'github', url: 'https://github.com/acme' }, ], -}); +}) ``` ### Menu @@ -152,33 +148,12 @@ createSocialIconsBlock({ ```ts createMenuBlock({ items: [ - { - id: crypto.randomUUID(), - text: "Home", - url: "https://example.com", - openInNewTab: false, - bold: false, - underline: false, - }, - { - id: crypto.randomUUID(), - text: "Blog", - url: "https://example.com/blog", - openInNewTab: false, - bold: false, - underline: false, - }, - { - id: crypto.randomUUID(), - text: "Docs", - url: "https://docs.example.com", - openInNewTab: true, - bold: false, - underline: false, - }, + { id: crypto.randomUUID(), text: 'Home', url: 'https://example.com', openInNewTab: false, bold: false, underline: false }, + { id: crypto.randomUUID(), text: 'Blog', url: 'https://example.com/blog', openInNewTab: false, bold: false, underline: false }, + { id: crypto.randomUUID(), text: 'Docs', url: 'https://docs.example.com', openInNewTab: true, bold: false, underline: false }, ], - separator: "-", -}); + separator: '-', +}) ``` ### Table @@ -187,51 +162,33 @@ createMenuBlock({ createTableBlock({ hasHeaderRow: true, rows: [ - { - id: crypto.randomUUID(), - cells: [ - { id: crypto.randomUUID(), content: "Plan" }, - { id: crypto.randomUUID(), content: "Price" }, - ], - }, - { - id: crypto.randomUUID(), - cells: [ - { id: crypto.randomUUID(), content: "Starter" }, - { id: crypto.randomUUID(), content: "$9/mo" }, - ], - }, - { - id: crypto.randomUUID(), - cells: [ - { id: crypto.randomUUID(), content: "Pro" }, - { id: crypto.randomUUID(), content: "$29/mo" }, - ], - }, + { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Plan' }, { id: crypto.randomUUID(), content: 'Price' }] }, + { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Starter' }, { id: crypto.randomUUID(), content: '$9/mo' }] }, + { id: crypto.randomUUID(), cells: [{ id: crypto.randomUUID(), content: 'Pro' }, { id: crypto.randomUUID(), content: '$29/mo' }] }, ], -}); +}) ``` ### Video ```ts createVideoBlock({ - url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", - thumbnailUrl: "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", - alt: "Product demo video", -}); + url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + thumbnailUrl: 'https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg', + alt: 'Product demo video', +}) ``` ### Section ```ts createSectionBlock({ - columns: "2", + columns: '2', children: [ - [createParagraphBlock({ content: "

Left column

" })], - [createImageBlock({ src: "https://cdn.example.com/photo.jpg" })], + [createParagraphBlock({ content: '

Left column

' })], + [createImageBlock({ src: 'https://cdn.example.com/photo.jpg' })], ], -}); +}) ``` The `columns` property accepts: `'1'` (single), `'2'` (two equal), `'3'` (three equal), `'2-1'` (two-thirds / one-third), `'1-2'` (one-third / two-thirds). See [Sections and Columns](/guide/sections-and-columns) for full details. @@ -247,9 +204,9 @@ The `columns` property accepts: `'1'` (single), `'2'` (two equal), `'3'` (three Create any block by type string: ```ts -import { createBlock } from "@templatical/types"; +import { createBlock } from '@templatical/types'; -const block = createBlock("title"); // TitleBlock with defaults +const block = createBlock('title'); // TitleBlock with defaults ``` ### Cloning @@ -257,7 +214,7 @@ const block = createBlock("title"); // TitleBlock with defaults Deep-clone a block with a new ID: ```ts -import { cloneBlock } from "@templatical/types"; +import { cloneBlock } from '@templatical/types'; const copy = cloneBlock(existingBlock); // copy.id !== existingBlock.id @@ -268,13 +225,7 @@ const copy = cloneBlock(existingBlock); Narrow a `Block` union to a specific type: ```ts -import { - isTitle, - isParagraph, - isImage, - isButton, - isSection, -} from "@templatical/types"; +import { isTitle, isParagraph, isImage, isButton, isSection } from '@templatical/types'; if (isTitle(block)) { console.log(block.level); // TypeScript knows this is TitleBlock @@ -299,17 +250,17 @@ Template settings control the global properties of the email: const content = createDefaultTemplateContent(); content.settings.width = 640; -content.settings.backgroundColor = "#f5f5f5"; -content.settings.fontFamily = "Helvetica, Arial, sans-serif"; -content.settings.preheaderText = "Your weekly digest is here"; +content.settings.backgroundColor = '#f5f5f5'; +content.settings.fontFamily = 'Helvetica, Arial, sans-serif'; +content.settings.preheaderText = 'Your weekly digest is here'; ``` -| Setting | Type | Description | -| ----------------- | -------- | -------------------------------- | -| `width` | `number` | Email width in pixels | -| `backgroundColor` | `string` | Outer background color | -| `fontFamily` | `string` | Default font stack | -| `preheaderText` | `string` | Preview text shown in inbox list | +| Setting | Type | Description | +|---|---|---| +| `width` | `number` | Email width in pixels | +| `backgroundColor` | `string` | Outer background color | +| `fontFamily` | `string` | Default font stack | +| `preheaderText` | `string` | Preview text shown in inbox list | For default values and how to customize them, see [Block & Template Defaults](/guide/defaults). @@ -318,10 +269,10 @@ For default values and how to customize them, see [Block & Template Defaults](/g Pass previously saved JSON back to the editor: ```ts -const saved = await fetch("/api/templates/123").then((r) => r.json()); +const saved = await fetch('/api/templates/123').then(r => r.json()); const editor = await init({ - container: "#editor", + container: '#editor', content: saved, }); ``` diff --git a/apps/docs/guide/sections-and-columns.md b/apps/docs/guide/sections-and-columns.md index db8907a..b7c1890 100644 --- a/apps/docs/guide/sections-and-columns.md +++ b/apps/docs/guide/sections-and-columns.md @@ -15,16 +15,16 @@ Stick to 1-2 columns for most emails. Three-column layouts become cramped on mob The `columns` property accepts one of five layout presets: -| Value | Description | Column widths | -| ------- | ---------------------- | --------------- | -| `'1'` | Single column | 100% | -| `'2'` | Two equal columns | 50% / 50% | -| `'3'` | Three equal columns | 33% / 33% / 33% | -| `'2-1'` | Two-thirds / one-third | 66% / 33% | -| `'1-2'` | One-third / two-thirds | 33% / 66% | +| Value | Description | Column widths | +|-------|-------------|---------------| +| `'1'` | Single column | 100% | +| `'2'` | Two equal columns | 50% / 50% | +| `'3'` | Three equal columns | 33% / 33% / 33% | +| `'2-1'` | Two-thirds / one-third | 66% / 33% | +| `'1-2'` | One-third / two-thirds | 33% / 66% | ```ts -type ColumnLayout = "1" | "2" | "3" | "2-1" | "1-2"; +type ColumnLayout = '1' | '2' | '3' | '2-1' | '1-2'; ``` ## Creating sections @@ -37,19 +37,19 @@ import { createTitleBlock, createParagraphBlock, createImageBlock, -} from "@templatical/types"; +} from '@templatical/types'; // Empty two-column section -const section = createSectionBlock({ columns: "2" }); +const section = createSectionBlock({ columns: '2' }); // Section with pre-populated columns const hero = createSectionBlock({ - columns: "1-2", + columns: '1-2', children: [ - [createImageBlock({ src: "https://cdn.example.com/logo.png", width: 120 })], + [createImageBlock({ src: 'https://cdn.example.com/logo.png', width: 120 })], [ - createTitleBlock({ content: "

Welcome

", level: 1 }), - createParagraphBlock({ content: "

Get started in minutes.

" }), + createTitleBlock({ content: '

Welcome

', level: 1 }), + createParagraphBlock({ content: '

Get started in minutes.

' }), ], ], }); @@ -62,15 +62,15 @@ const hero = createSectionBlock({ ```ts // For a '2' layout: section.children = [ - [blockA, blockB], // Left column - [blockC], // Right column + [blockA, blockB], // Left column + [blockC], // Right column ]; // For a '3' layout: section.children = [ - [blockA], // Left - [blockB], // Center - [blockC], // Right + [blockA], // Left + [blockB], // Center + [blockC], // Right ]; ``` @@ -84,8 +84,8 @@ To programmatically add a block to a specific column: // Add a button to the second column (index 1) section.children[1].push( createButtonBlock({ - text: "Learn More", - url: "https://example.com/docs", + text: 'Learn More', + url: 'https://example.com/docs', }), ); ``` @@ -102,7 +102,7 @@ You can use the `visibility` property on individual blocks within columns to sho ```ts const block = createParagraphBlock({ - content: "

Desktop only sidebar content

", + content: '

Desktop only sidebar content

', }); block.visibility = { @@ -119,10 +119,10 @@ See [Styling](/guide/styling) for more on responsive overrides and block visibil Sections support the same `BlockStyles` as other blocks. Common use cases include setting a background color or padding on the entire row: ```ts -const section = createSectionBlock({ columns: "1" }); +const section = createSectionBlock({ columns: '1' }); section.styles = { - backgroundColor: "#f8fafc", + backgroundColor: '#f8fafc', padding: { top: 32, right: 24, bottom: 32, left: 24 }, margin: { top: 0, right: 0, bottom: 0, left: 0 }, }; diff --git a/apps/docs/guide/styling.md b/apps/docs/guide/styling.md index 941a4e2..30082b7 100644 --- a/apps/docs/guide/styling.md +++ b/apps/docs/guide/styling.md @@ -21,16 +21,16 @@ interface BlockStyles { ``` ```ts -import { createParagraphBlock } from "@templatical/types"; +import { createParagraphBlock } from '@templatical/types'; const block = createParagraphBlock({ - content: "

Styled text

", + content: '

Styled text

', }); block.styles = { padding: { top: 16, right: 24, bottom: 16, left: 24 }, margin: { top: 0, right: 0, bottom: 0, left: 0 }, - backgroundColor: "#f0f9ff", + backgroundColor: '#f0f9ff', }; ``` @@ -69,11 +69,11 @@ interface ResponsiveStyles { The editor uses these viewport widths in preview mode: -| Viewport | Preview Width | -| -------- | ------------------------------ | -| Desktop | Template width (default 600px) | -| Tablet | 768px | -| Mobile | 375px | +| Viewport | Preview Width | +|----------|---------------| +| Desktop | Template width (default 600px) | +| Tablet | 768px | +| Mobile | 375px | Responsive overrides merge with the base styles. Only specify the properties you want to change: @@ -136,24 +136,24 @@ Most CSS properties don't work reliably across email clients. Properties like `d Beyond individual block styles, the template itself has global settings that affect the overall layout. -| Setting | Type | Description | -| ----------------- | -------- | -------------------------------------------- | -| `width` | `number` | Template content width in px (typically 600) | -| `backgroundColor` | `string` | Outer background color behind the template | -| `fontFamily` | `string` | Default font family for all blocks | +| Setting | Type | Description | +|---------|------|-------------| +| `width` | `number` | Template content width in px (typically 600) | +| `backgroundColor` | `string` | Outer background color behind the template | +| `fontFamily` | `string` | Default font family for all blocks | These are configured through the editor's `init()` config or by modifying the template JSON directly: ```ts -import { init } from "@templatical/editor"; +import { init } from '@templatical/editor'; const editor = await init({ - container: "#editor", + container: '#editor', content: { settings: { width: 640, - backgroundColor: "#f8fafc", - fontFamily: "Inter, system-ui, sans-serif", + backgroundColor: '#f8fafc', + fontFamily: 'Inter, system-ui, sans-serif', }, blocks: [], }, diff --git a/apps/docs/license-faq.md b/apps/docs/license-faq.md index a0cb3f6..d92ba46 100644 --- a/apps/docs/license-faq.md +++ b/apps/docs/license-faq.md @@ -25,15 +25,15 @@ FSL-1.1-**MIT** is the variant that automatically converts to the MIT License af ## Which packages use which license? -| Package | License | -| ----------------------------- | ----------------------------------------------------------------------------------------------- | -| `@templatical/editor` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (becomes MIT after 2 years) | -| `@templatical/core` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (becomes MIT after 2 years) | -| `@templatical/media-library` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (becomes MIT after 2 years) | -| `@templatical/types` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | -| `@templatical/renderer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | -| `@templatical/import-beefree` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | -| `@templatical/import-unlayer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| Package | License | +|---|---| +| `@templatical/editor` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (becomes MIT after 2 years) | +| `@templatical/core` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (becomes MIT after 2 years) | +| `@templatical/media-library` | [FSL-1.1-MIT](https://github.com/templatical/sdk/blob/main/LICENSE) (becomes MIT after 2 years) | +| `@templatical/types` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| `@templatical/renderer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| `@templatical/import-beefree` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | +| `@templatical/import-unlayer` | [MIT](https://github.com/templatical/sdk/blob/main/LICENSE-MIT) | The split exists so anything you'd build into your own backend or codegen pipeline (types, renderer, importer) is permissive MIT and free of any future-license consideration. @@ -41,7 +41,7 @@ The split exists so anything you'd build into your own backend or codegen pipeli **Yes.** You can embed Templatical in any commercial product — paid SaaS, internal tools, on-premise software, agency builds, anything — without paying us and without asking permission. -The only restriction is the one in the LICENSE: you can't take Templatical, slap a different name on it, and offer _that_ as a hosted email-editor service that competes with us. +The only restriction is the one in the LICENSE: you can't take Templatical, slap a different name on it, and offer *that* as a hosted email-editor service that competes with us. ## Can I embed Templatical in my SaaS? @@ -63,16 +63,16 @@ If you're not sure whether your use case crosses the line, [open a discussion](h ## What does "competing use" mean concretely? -A "competing use" is one where the email editor _is_ the product. Some worked examples: +A "competing use" is one where the email editor *is* the product. Some worked examples: -| Use case | Allowed? | -| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -| You add Templatical to your CRM so users can compose campaign emails | ✅ Yes — email editor is a feature of your CRM. | -| You add Templatical to a transactional email API so customers design templates before sending via your API | ✅ Yes — email editor is a feature of your sending platform. | -| You build a newsletter SaaS where Templatical is one of several composition tools | ✅ Yes — email editor is a feature of your newsletter product. | -| You embed the editor inside your company's internal marketing portal | ✅ Yes — internal use is unrestricted. | -| You fork Templatical, rebrand it, and sell hosted Templatical-clone subscriptions | ❌ No — that's a competing managed service. | -| You build "Templatical Cloud, but cheaper" and offer it as a SaaS | ❌ No — that's a competing managed service. | +| Use case | Allowed? | +|---|---| +| You add Templatical to your CRM so users can compose campaign emails | ✅ Yes — email editor is a feature of your CRM. | +| You add Templatical to a transactional email API so customers design templates before sending via your API | ✅ Yes — email editor is a feature of your sending platform. | +| You build a newsletter SaaS where Templatical is one of several composition tools | ✅ Yes — email editor is a feature of your newsletter product. | +| You embed the editor inside your company's internal marketing portal | ✅ Yes — internal use is unrestricted. | +| You fork Templatical, rebrand it, and sell hosted Templatical-clone subscriptions | ❌ No — that's a competing managed service. | +| You build "Templatical Cloud, but cheaper" and offer it as a SaaS | ❌ No — that's a competing managed service. | The rule of thumb: if you remove Templatical from your product, do you still have a product? If yes, you're fine. diff --git a/apps/docs/quality/accessibility/getting-started.md b/apps/docs/quality/accessibility/getting-started.md index 19f04ab..19c906d 100644 --- a/apps/docs/quality/accessibility/getting-started.md +++ b/apps/docs/quality/accessibility/getting-started.md @@ -3,23 +3,18 @@ ## Install ::: code-group - ```bash [npm] npm install @templatical/quality ``` - ```bash [pnpm] pnpm add @templatical/quality ``` - ```bash [yarn] yarn add @templatical/quality ``` - ```bash [bun] bun add @templatical/quality ``` - ::: ## Wire into the editor diff --git a/apps/docs/quality/accessibility/headless-usage.md b/apps/docs/quality/accessibility/headless-usage.md index 4b72dcf..e1cd047 100644 --- a/apps/docs/quality/accessibility/headless-usage.md +++ b/apps/docs/quality/accessibility/headless-usage.md @@ -49,9 +49,7 @@ for (const [name, content] of Object.entries(templates)) { console.error(`✖ ${name}: ${issues.length} issue(s)`); for (const issue of issues) { const where = issue.blockId ? `block ${issue.blockId}` : "template"; - console.error( - ` [${issue.severity}] ${issue.ruleId} (${where}): ${issue.message}`, - ); + console.error(` [${issue.severity}] ${issue.ruleId} (${where}): ${issue.message}`); } } @@ -65,7 +63,9 @@ Run via `tsx scripts/lint-templates.ts` and wire it into your CI workflow. The T A team may want errors-only in CI but the full info-level output in development: ```ts -const SEVERITIES = process.env.CI ? ["error"] : ["error", "warning", "info"]; +const SEVERITIES = process.env.CI + ? ["error"] + : ["error", "warning", "info"]; const issues = lintAccessibility(content).filter((i) => SEVERITIES.includes(i.severity), diff --git a/apps/docs/quality/accessibility/index.md b/apps/docs/quality/accessibility/index.md index 4303291..7daa294 100644 --- a/apps/docs/quality/accessibility/index.md +++ b/apps/docs/quality/accessibility/index.md @@ -63,23 +63,18 @@ The package has no opinion on UI. The editor's `useAccessibilityLint` composable ## Install ::: code-group - ```bash [npm] npm install @templatical/quality ``` - ```bash [pnpm] pnpm add @templatical/quality ``` - ```bash [yarn] yarn add @templatical/quality ``` - ```bash [bun] bun add @templatical/quality ``` - ::: The package is an **optional peer** of `@templatical/editor`. Install it to turn on the sidebar tab and canvas badges. Skip it and the editor stays lean — the dynamic import is gated and tree-shakeable, so the linter chunk never downloads. diff --git a/apps/docs/quality/accessibility/options.md b/apps/docs/quality/accessibility/options.md index ded88e0..d21013b 100644 --- a/apps/docs/quality/accessibility/options.md +++ b/apps/docs/quality/accessibility/options.md @@ -16,7 +16,7 @@ type Severity = "error" | "warning" | "info" | "off"; ## `disabled` | Default | `false` | -| ------- | ------- | +|---|---| When `true`: @@ -28,9 +28,9 @@ Use this when a tenant has explicitly opted out, or to keep the default OSS bund ## `locale` -| Default (headless) | `'en'` | -| ------------------ | --------------------------------- | -| Editor | always matches `init({ locale })` | +| Default (headless) | `'en'` | +|---|---| +| Editor | always matches `init({ locale })` | Drives the message templates the linter returns (`messages/{locale}.ts`) and is accepted by the locale-aware rules (`link-vague-text`, `button-vague-label`, `img-linked-no-context`). Falls back to `en` when the locale (or its base language) isn't bundled. @@ -55,7 +55,7 @@ The dictionary is a union of every registered locale, so a German-locale email w ## `rules` | Default | `{}` | -| ------- | ---- | +|---|---| Per-rule severity override. Set a rule to `'off'` to disable it entirely. Set to a different severity to bend the default classification: @@ -72,16 +72,16 @@ The override applies before the rule runs, so disabled rules don't even execute. ## `thresholds` | Default | See below | -| ------- | --------- | +|---|---| Numeric knobs that some rules consult: -| Threshold | Default | Used by | -| ------------------ | ------- | --------------------- | -| `altMaxLength` | `125` | `img-alt-too-long` | -| `minFontSize` | `14` | `text-too-small` | -| `allCapsMinLength` | `20` | `text-all-caps` | -| `minTouchTargetPx` | `44` | `button-touch-target` | +| Threshold | Default | Used by | +|---|---|---| +| `altMaxLength` | `125` | `img-alt-too-long` | +| `minFontSize` | `14` | `text-too-small` | +| `allCapsMinLength` | `20` | `text-all-caps` | +| `minTouchTargetPx` | `44` | `button-touch-target` | Override one without losing the others — partial merging is built in: diff --git a/apps/docs/quality/accessibility/rule-catalog.md b/apps/docs/quality/accessibility/rule-catalog.md index b76adc2..f99e1d4 100644 --- a/apps/docs/quality/accessibility/rule-catalog.md +++ b/apps/docs/quality/accessibility/rule-catalog.md @@ -4,49 +4,50 @@ The 19 rules `@templatical/quality` ships, grouped by what they check. Each rule ## Images -| Rule | Default severity | Auto-fix | What it checks | -| -------------------------------- | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `img-missing-alt` | error | — | Missing alt text — Screen readers announce undefined or empty alt as the image filename or skip the image entirely. Email clients also block images by default; alt text is what 30–50% of recipients see first. [1](https://www.w3.org/WAI/tutorials/images/) | -| `img-alt-is-filename` | warning | yes | Alt text looks like a filename — Filenames like 'IMG_1234.jpg' or 'Screen Shot 2026.png' carry no useful meaning. Replace with a short description of what the image conveys. | -| `img-alt-too-long` | warning | — | Alt text is too long — Screen readers don't pause inside alt text. Long alt strings become a wall of speech. Aim for under ~125 characters; put extra context in surrounding copy. | -| `img-decorative-needs-empty-alt` | info | yes | Decorative image has alt text — Decorative images should be skipped by screen readers. Setting alt='' (empty) signals that intent. Non-empty alt on a decorative image is a contradiction. | -| `img-linked-no-context` | warning | — | Linked image has no destination context — When an image is also a link, alt text doubles as the link label. Describing only what the image shows leaves users guessing where the link goes. | +| Rule | Default severity | Auto-fix | What it checks | +|---|---|---|---| +| `img-missing-alt` | error | — | Missing alt text — Screen readers announce undefined or empty alt as the image filename or skip the image entirely. Email clients also block images by default; alt text is what 30–50% of recipients see first. [1](https://www.w3.org/WAI/tutorials/images/) | +| `img-alt-is-filename` | warning | yes | Alt text looks like a filename — Filenames like 'IMG_1234.jpg' or 'Screen Shot 2026.png' carry no useful meaning. Replace with a short description of what the image conveys. | +| `img-alt-too-long` | warning | — | Alt text is too long — Screen readers don't pause inside alt text. Long alt strings become a wall of speech. Aim for under ~125 characters; put extra context in surrounding copy. | +| `img-decorative-needs-empty-alt` | info | yes | Decorative image has alt text — Decorative images should be skipped by screen readers. Setting alt='' (empty) signals that intent. Non-empty alt on a decorative image is a contradiction. | +| `img-linked-no-context` | warning | — | Linked image has no destination context — When an image is also a link, alt text doubles as the link label. Describing only what the image shows leaves users guessing where the link goes. | ## Headings -| Rule | Default severity | Auto-fix | What it checks | -| --------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `heading-empty` | error | — | Heading has no text — Empty headings produce silent landmarks for screen-reader users navigating by heading. Either add text or remove the block. | -| `heading-skip-level` | error | — | Heading level skipped — Skipping heading levels (e.g. H1 → H3) breaks the document outline that assistive tech relies on for navigation. Step one level at a time. | -| `heading-multiple-h1` | warning | — | Multiple H1 headings — An email should have a single H1 that names the message. Multiple H1s confuse the document outline and weaken landmark navigation. | +| Rule | Default severity | Auto-fix | What it checks | +|---|---|---|---| +| `heading-empty` | error | — | Heading has no text — Empty headings produce silent landmarks for screen-reader users navigating by heading. Either add text or remove the block. | +| `heading-skip-level` | error | — | Heading level skipped — Skipping heading levels (e.g. H1 → H3) breaks the document outline that assistive tech relies on for navigation. Step one level at a time. | +| `heading-multiple-h1` | warning | — | Multiple H1 headings — An email should have a single H1 that names the message. Multiple H1s confuse the document outline and weaken landmark navigation. | ## Links -| Rule | Default severity | Auto-fix | What it checks | -| -------------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `link-empty` | error | — | Link has no accessible text — A link with no visible text and no nested image with alt is invisible to screen readers and unclickable for many users. | -| `link-vague-text` | warning | — | Vague link text — Phrases like 'click here' or 'read more' tell screen-reader users nothing when listed out of context. Use descriptive link text that names the destination. Outer punctuation and decorative symbols are stripped before matching, so `Click here!`, `→ click here`, and `»click here«` all flag. | -| `link-href-empty` | error | — | Link has empty href — An anchor with no destination (empty href or '#') is broken — recipients click and nothing happens, or the page jumps to top. | -| `link-target-blank-no-rel` | warning | yes | target="\_blank" missing rel="noopener" — Links opening in a new tab without rel='noopener' or rel='noreferrer' allow the destination to read window.opener and tamper with the originating page. A small but real security/privacy footgun. | +| Rule | Default severity | Auto-fix | What it checks | +|---|---|---|---| +| `link-empty` | error | — | Link has no accessible text — A link with no visible text and no nested image with alt is invisible to screen readers and unclickable for many users. | +| `link-vague-text` | warning | — | Vague link text — Phrases like 'click here' or 'read more' tell screen-reader users nothing when listed out of context. Use descriptive link text that names the destination. Outer punctuation and decorative symbols are stripped before matching, so `Click here!`, `→ click here`, and `»click here«` all flag. | +| `link-href-empty` | error | — | Link has empty href — An anchor with no destination (empty href or '#') is broken — recipients click and nothing happens, or the page jumps to top. | +| `link-target-blank-no-rel` | warning | yes | target="_blank" missing rel="noopener" — Links opening in a new tab without rel='noopener' or rel='noreferrer' allow the destination to read window.opener and tamper with the originating page. A small but real security/privacy footgun. | ## Text -| Rule | Default severity | Auto-fix | What it checks | -| ------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `text-all-caps` | warning | — | All-caps body text — Long stretches of all-caps text are read letter-by-letter by some screen readers and slow visual reading by 10–20%. Use sentence case for body copy; reserve caps for short labels. | -| `text-low-contrast` | error | — | Heading contrast is too low — WCAG AA requires 4.5:1 for body text and 3:1 for large text (18pt / ~24px). Headings ≥24px (H1, H2) get the relaxed 3:1 threshold; H3 (22px) and H4 (18px) require 4.5:1. The bold-text relaxation isn't applied — TipTap stores bold inline in HTML, not as a structured field. Below that, text becomes unreadable for low-vision users and in bright outdoor light. Only Title blocks are checked; paragraph color lives in inline HTML. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | -| `text-too-small` | warning | — | Text is too small — Email body text below 14px becomes hard to read on mobile. Some clients also auto-zoom or scale up small fonts in unpredictable ways. Stay at 14px or larger. | +| Rule | Default severity | Auto-fix | What it checks | +|---|---|---|---| +| `text-all-caps` | warning | — | All-caps body text — Long stretches of all-caps text are read letter-by-letter by some screen readers and slow visual reading by 10–20%. Use sentence case for body copy; reserve caps for short labels. | +| `text-low-contrast` | error | — | Heading contrast is too low — WCAG AA requires 4.5:1 for body text and 3:1 for large text (18pt / ~24px). Headings ≥24px (H1, H2) get the relaxed 3:1 threshold; H3 (22px) and H4 (18px) require 4.5:1. The bold-text relaxation isn't applied — TipTap stores bold inline in HTML, not as a structured field. Below that, text becomes unreadable for low-vision users and in bright outdoor light. Only Title blocks are checked; paragraph color lives in inline HTML. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | +| `text-too-small` | warning | — | Text is too small — Email body text below 14px becomes hard to read on mobile. Some clients also auto-zoom or scale up small fonts in unpredictable ways. Stay at 14px or larger. | ## Buttons -| Rule | Default severity | Auto-fix | What it checks | -| --------------------- | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `button-vague-label` | warning | — | Vague button label — A button labeled 'Click here' or 'Submit' tells the user nothing about what will happen. Use action-oriented labels that name the outcome ('Buy ticket', 'Reset password'). Same outer-punctuation handling as `link-vague-text` — `Submit!`, `→ OK`, and `»click«` all flag. | -| `button-touch-target` | warning | — | Button touch target is too small — WCAG 2.5.5 (AAA) and Apple/Google UX guidelines recommend touch targets of at least 44×44px. Smaller buttons cause mis-taps on mobile. | -| `button-low-contrast` | error | — | Button text contrast is too low — Same WCAG AA thresholds as `text-low-contrast`: 4.5:1 normally, 3:1 for buttons with `fontSize >= 24` (WCAG large text). Default-sized buttons (15px) require the strict ratio. Buttons that fail this become unreadable for users with low vision and in bright outdoor light. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | +| Rule | Default severity | Auto-fix | What it checks | +|---|---|---|---| +| `button-vague-label` | warning | — | Vague button label — A button labeled 'Click here' or 'Submit' tells the user nothing about what will happen. Use action-oriented labels that name the outcome ('Buy ticket', 'Reset password'). Same outer-punctuation handling as `link-vague-text` — `Submit!`, `→ OK`, and `»click«` all flag. | +| `button-touch-target` | warning | — | Button touch target is too small — WCAG 2.5.5 (AAA) and Apple/Google UX guidelines recommend touch targets of at least 44×44px. Smaller buttons cause mis-taps on mobile. | +| `button-low-contrast` | error | — | Button text contrast is too low — Same WCAG AA thresholds as `text-low-contrast`: 4.5:1 normally, 3:1 for buttons with `fontSize >= 24` (WCAG large text). Default-sized buttons (15px) require the strict ratio. Buttons that fail this become unreadable for users with low vision and in bright outdoor light. [1](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) | ## Structure -| Rule | Default severity | Auto-fix | What it checks | -| ------------------- | ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `missing-preheader` | info | — | Missing preheader text — The preheader is the preview snippet shown beside the subject line in most inboxes. Without it, recipients see a fragment of the first heading or a stray alt tag — a missed chance to set context. | +| Rule | Default severity | Auto-fix | What it checks | +|---|---|---|---| +| `missing-preheader` | info | — | Missing preheader text — The preheader is the preview snippet shown beside the subject line in most inboxes. Without it, recipients see a fragment of the first heading or a stray alt tag — a missed chance to set context. | + diff --git a/apps/docs/quality/accessibility/severity-and-fixes.md b/apps/docs/quality/accessibility/severity-and-fixes.md index 90c27f2..4dafa98 100644 --- a/apps/docs/quality/accessibility/severity-and-fixes.md +++ b/apps/docs/quality/accessibility/severity-and-fixes.md @@ -4,12 +4,12 @@ Every rule emits an `A11yIssue` with one of four severities: -| Severity | Meaning | UI | -| --------- | ----------------------------------------------------------------------- | ------------------------------------------------- | -| `error` | Hard accessibility failure. Recipient may be excluded from the message. | Red dot on canvas, "Errors" group in the sidebar. | -| `warning` | Likely problem — fix unless you know better. | Yellow dot, "Warnings" group. | -| `info` | Recommendation; not a defect. | No canvas badge, "Info" group. | -| `off` | Override — disables the rule entirely. | Nothing. | +| Severity | Meaning | UI | +|---|---|---| +| `error` | Hard accessibility failure. Recipient may be excluded from the message. | Red dot on canvas, "Errors" group in the sidebar. | +| `warning` | Likely problem — fix unless you know better. | Yellow dot, "Warnings" group. | +| `info` | Recommendation; not a defect. | No canvas badge, "Info" group. | +| `off` | Override — disables the rule entirely. | Nothing. | Severity is configurable per rule via `options.rules` — the catalog's "Severity" column is just the default. diff --git a/apps/docs/showcase.md b/apps/docs/showcase.md index d5a15c0..feb642e 100644 --- a/apps/docs/showcase.md +++ b/apps/docs/showcase.md @@ -16,7 +16,6 @@ These are the integration patterns we see most often. Each describes how Templat You're building (or extending) a transactional email API — Postmark, Resend, SES wrappers, internal sending platforms. Customers connect their codebase, then need a way to design and version the templates the API sends. **Templatical fits because:** - - Templates are JSON — versionable, diffable, AI-friendly, easy to store next to your customers' data. - MJML output renders consistently across Outlook, Gmail, Apple Mail, and the long tail of clients. - The renderer runs in Node, so you can compile to HTML server-side at send time without a vendor-hosted render API. @@ -28,7 +27,6 @@ You're building (or extending) a transactional email API — Postmark, Resend, S You're building a Mailchimp-style product, an automation tool, or a creator-newsletter platform. Email composition is one of several features your customers need. **Templatical fits because:** - - Drop-in editor mounts with one function call — no rewrite of your existing dashboard. - Theming via design tokens means your customers' emails feel native to your brand, not Templatical's. - Display conditions and merge tags are built in — important for personalization-heavy newsletter use. @@ -41,7 +39,6 @@ You're building a Mailchimp-style product, an automation tool, or a creator-news You're building a CRM, a sales-engagement tool, or a marketing automation platform where customers send branded emails as one workflow among many. **Templatical fits because:** - - It's not a separate "email designer" tab — it embeds inline alongside contact records, campaign builders, and automation flows. - Custom blocks let you surface your product's data (contact fields, deal info, calculated values) as first-class content blocks customers can drop into emails. - Real-time collaboration lets sales and marketing teams co-edit campaign templates without clobbering each other. @@ -53,7 +50,6 @@ You're building a CRM, a sales-engagement tool, or a marketing automation platfo You're not building a customer-facing product — you need a controlled tool for your team to design transactional and marketing emails, with the JSON output stored in your own systems. **Templatical fits because:** - - Self-hostable in full. The OSS SDK has no required cloud dependency. - Bilingual (en/de) out of the box, with a clean path to add more locales for international teams. - TypeScript-strict end-to-end, which makes it easy to wire into existing internal tooling and codegen pipelines. @@ -65,7 +61,6 @@ You're not building a customer-facing product — you need a controlled tool for You're not embedding a visual editor at all — you want to generate templates from data programmatically, version them in source control, and render them through a deterministic pipeline. **Templatical fits because:** - - The block system has factory functions (`createTitleBlock`, `createImageBlock`, …) and full TypeScript types — you can build templates entirely in code with full type safety. - The renderer is a pure function with no DOM dependency. Run it in serverless, edge, or Node. @@ -78,7 +73,6 @@ See [Programmatic Templates](/guide/programmatic-templates) for a full walkthrou If your team ships Templatical in production, we'd love to feature you here. Open a pull request adding your entry, or [start a discussion](https://github.com/templatical/sdk/discussions) and we'll do it together. What we list: - - A short product description (one sentence) - Your logo and a link - Optional: a one-paragraph note on how you use Templatical (which packages, which features, anything notable) diff --git a/packages/editor/README.md b/packages/editor/README.md index 15574c9..4bef589 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -35,11 +35,11 @@ If you call `editor.toMjml()` without the renderer installed, it throws a clear ## Usage ```ts -import { init } from "@templatical/editor"; -import "@templatical/editor/style.css"; +import { init } from '@templatical/editor'; +import '@templatical/editor/style.css'; const editor = await init({ - container: "#editor", + container: '#editor', onChange(content) { // content is JSON — store/version/sync however you want }, diff --git a/packages/media-library/README.md b/packages/media-library/README.md index a3bdc12..5ed6e70 100644 --- a/packages/media-library/README.md +++ b/packages/media-library/README.md @@ -22,16 +22,16 @@ Peer deps: `vue@^3.5`, `tailwindcss@^4`. ### Standalone visual SDK (mount anywhere) ```ts -import { init } from "@templatical/media-library"; -import "@templatical/media-library/style.css"; +import { init } from '@templatical/media-library'; +import '@templatical/media-library/style.css'; const media = await init({ - container: "#media", + container: '#media', auth: { - url: "https://your-app.com/api/templatical/token", + url: 'https://your-app.com/api/templatical/token', }, onSelect(item) { - console.log("Picked:", item.url); + console.log('Picked:', item.url); }, }); @@ -44,18 +44,18 @@ media.unmount(); Use `MediaLibraryModal` inside a Vue 3 app. See [docs](https://docs.templatical.com/cloud/media-library) for the full prop reference. ```ts -import { MediaLibraryModal } from "@templatical/media-library"; -import "@templatical/media-library/style.css"; +import { MediaLibraryModal } from '@templatical/media-library'; +import '@templatical/media-library/style.css'; ``` ### Composable (build your own UI) ```ts -import { AuthManager } from "@templatical/core/cloud"; -import { useMediaLibrary } from "@templatical/media-library"; +import { AuthManager } from '@templatical/core/cloud'; +import { useMediaLibrary } from '@templatical/media-library'; const authManager = new AuthManager({ - url: "https://your-app.com/api/templatical/token", + url: 'https://your-app.com/api/templatical/token', }); await authManager.initialize(); @@ -68,8 +68,8 @@ const lib = useMediaLibrary({ ### API client (low-level) ```ts -import { AuthManager } from "@templatical/core/cloud"; -import { MediaApiClient } from "@templatical/media-library"; +import { AuthManager } from '@templatical/core/cloud'; +import { MediaApiClient } from '@templatical/media-library'; const api = new MediaApiClient(authManager); const response = await api.browseMedia({ folder_id: null }); From 2f418f5edebff99626ea7ea19421245a242cb0d8 Mon Sep 17 00:00:00 2001 From: Orkhan Ahmadov Date: Tue, 12 May 2026 17:41:00 +0200 Subject: [PATCH 21/26] WIP --- README.md | 2 +- apps/docs/de/index.md | 10 +++++++++- apps/docs/index.md | 10 +++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c2128aa..f9ac622 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,11 @@ Things that are usually paid features in commercial editors — open-source in T ### And more - **Drop-in mount** — one `init()` call, one `unmount()`. No framework lock-in. +- **Style-isolated, both directions** — Shadow DOM by default keeps host CSS out of the editor; `tpl:` Tailwind prefix and no preflight reset keep editor styles out of your app. Drops into any page, any framework, any CMS — no resets, no conflicts. [Learn more →](https://docs.templatical.com/guide/shadow-dom) - **14 block types** — Title, Paragraph, Image, Button, Section, Divider, Spacer, Social Icons, Menu, Table, HTML, Video, Countdown, Custom. - **JSON templates** — portable, versionable, store anywhere, render anywhere. - **MJML output** — works with any email provider (Postmark, Resend, SES, Mailgun, anything). - **Framework-agnostic** — first-class examples for React, Vue, Svelte, Angular, vanilla. -- **Tailwind 4 with `tpl:` prefix** — no preflight reset, no style leaks into your app. - **Bilingual** — en/de built in, easy to add more locales. - **TypeScript strict** — full types for blocks, config, and callbacks. - **Battle-tested** — ~1,400 unit tests + Playwright E2E coverage. diff --git a/apps/docs/de/index.md b/apps/docs/de/index.md index fb727fd..7afca51 100644 --- a/apps/docs/de/index.md +++ b/apps/docs/de/index.md @@ -3,7 +3,7 @@ layout: home hero: name: Templatical text: E-Mail-Editor für Ihre Anwendung - tagline: Binden Sie einen produktionsreifen Drag-and-Drop-E-Mail-Editor in jede Webanwendung ein. Open Source, Framework-agnostisch und vollgepackt mit Funktionen, die in anderen Editoren typischerweise kostenpflichtigen Tarifen vorbehalten sind. + tagline: Binden Sie einen produktionsreifen Drag-and-Drop-E-Mail-Editor in jede Webanwendung ein — Host-CSS und Design-Systeme können ihn nicht beschädigen. Open Source, Framework-agnostisch und vollgepackt mit Funktionen, die in anderen Editoren typischerweise kostenpflichtigen Tarifen vorbehalten sind. actions: - theme: brand text: Loslegen @@ -36,4 +36,12 @@ features: details: Portables JSON, MJML-Ausgabe. Rendern Sie überall und versenden Sie über jeden Provider. link: /de/getting-started/how-rendering-works linkText: Wie das Rendering funktioniert → + - title: Standardmäßig style-isoliert + details: Shadow-DOM-Mount hält Host-CSS aus dem Editor und Editor-CSS aus Ihrer App heraus. Funktioniert in jeder Seite, jedem Framework, jedem CMS — ohne Resets, ohne Konflikte. + link: /de/guide/shadow-dom + linkText: So funktioniert die Isolation → + - title: Bestehende Templates übernehmen + details: Importer für BeeFree, Unlayer und rohes HTML. Migrieren Sie in Ihrem Tempo, kein Neuaufbau erforderlich. + link: /de/guide/migration-from-beefree + linkText: Von BeeFree migrieren → --- diff --git a/apps/docs/index.md b/apps/docs/index.md index ec600aa..9483a07 100644 --- a/apps/docs/index.md +++ b/apps/docs/index.md @@ -3,7 +3,7 @@ layout: home hero: name: Templatical text: Email Editor for Your App - tagline: Drop a production-ready drag-and-drop email editor into any web application. Open-source, framework-agnostic, and packed with features that are typically reserved for paid tiers in other editors. + tagline: Drop a production-ready drag-and-drop email editor into any web application — host CSS and design systems can't break it. Open-source, framework-agnostic, and packed with features that are typically reserved for paid tiers in other editors. actions: - theme: brand text: Get Started @@ -36,4 +36,12 @@ features: details: Portable JSON templates, MJML output. Render anywhere, send through any provider. link: /getting-started/how-rendering-works linkText: How rendering works → + - title: Style-isolated by default + details: Shadow DOM mount keeps host CSS out of the editor and editor CSS out of your app. Drop into any page, framework, or CMS — no resets, no conflicts. + link: /guide/shadow-dom + linkText: How isolation works → + - title: Bring your existing templates + details: Importers for BeeFree, Unlayer, and raw HTML. Migrate at your pace, no rebuild required. + link: /guide/migration-from-beefree + linkText: Migrate from BeeFree → --- From 9355746d224114d27ba6f4489cdb45ffdc330eab Mon Sep 17 00:00:00 2001 From: Orkhan Ahmadov Date: Tue, 12 May 2026 18:46:09 +0200 Subject: [PATCH 22/26] WIP --- apps/docs/.vitepress/config.ts | 4 ++-- apps/docs/de/guide/shadow-dom.md | 9 +++++++++ apps/docs/guide/shadow-dom.md | 9 +++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts index b7cb553..eb88e3a 100644 --- a/apps/docs/.vitepress/config.ts +++ b/apps/docs/.vitepress/config.ts @@ -98,10 +98,10 @@ const enSidebar: DefaultTheme.SidebarMulti = { text: "Customization", items: [ { text: "Theming", link: "/guide/theming" }, - { text: "Shadow DOM", link: "/guide/shadow-dom" }, { text: "Block & Template Defaults", link: "/guide/defaults" }, { text: "Custom Fonts", link: "/guide/fonts" }, { text: "Internationalization", link: "/guide/i18n" }, + { text: "Shadow DOM", link: "/guide/shadow-dom" }, ], }, { @@ -242,10 +242,10 @@ const deSidebar: DefaultTheme.SidebarMulti = { text: "Anpassung", items: [ { text: "Theming", link: "/de/guide/theming" }, - { text: "Shadow DOM", link: "/de/guide/shadow-dom" }, { text: "Block- & Template-Standards", link: "/de/guide/defaults" }, { text: "Benutzerdefinierte Schriften", link: "/de/guide/fonts" }, { text: "Internationalisierung", link: "/de/guide/i18n" }, + { text: "Shadow DOM", link: "/de/guide/shadow-dom" }, ], }, { diff --git a/apps/docs/de/guide/shadow-dom.md b/apps/docs/de/guide/shadow-dom.md index e4766a9..482e228 100644 --- a/apps/docs/de/guide/shadow-dom.md +++ b/apps/docs/de/guide/shadow-dom.md @@ -54,6 +54,15 @@ Das `tpl:`-Präfix schützt nur eine Richtung: Editor-Utilities können niemals Shadow DOM ist der einzige standardbasierte Weg, diese Kaskade zu blockieren. Derselbe Ansatz wird von Stripe Elements, Intercom-Widgets und den meisten einbettbaren Drittanbieter-UIs verwendet. Siehe [Issue #70](https://github.com/templatical/sdk/issues/70) für den ursprünglichen Bericht. +## Warum als Standard + +Shadow DOM garantiert, dass keine Stile zwischen Host-Seite und Editor-UI leaken — Host-CSS kann nicht in den Editor kaskadieren, und Editor-CSS kann nicht in Ihre App durchsickern. Templatical aktiviert diese Garantie standardmäßig, nicht als Opt-in, weil die Bedingungen, die sie notwendig machen, auf fast jede Host-Seite zutreffen: + +- **Globale Resets, Design-Systeme und Framework-Preflight sind überall.** Tailwind, Bootstrap, Material UI, Chakra, Mantine — jeder moderne Stack liefert Regeln wie `* { box-sizing: border-box }` und `body { font-family: … }` aus. Ohne die Shadow-Grenze würde jedes Editor-Element übernehmen, was Ihre Host-Seite definiert. +- **Der Canvas rendert E-Mail-ähnliche Inhalte, bei denen Typografie und Abstände am wichtigsten sind.** Ein Host-`body { font-family: Comic Sans }`, das in die Vorschau kaskadiert, beschädigt jede Vorlage, bevor sie exportiert wird. +- **Jeder Editor auf der Seite erhält seinen eigenen Scope.** Mit mehreren nebeneinander gemounteten Editoren (z. B. Entwurf + Vorschau, A/B-Vergleich) isoliert jeder Shadow Root Theming und Host-Targeting pro Instanz — Host-Overrides auf einem erreichen den anderen nicht. +- **Formularelemente in Toolbars und Dialogen würden Host-Styling übernehmen.** Texteingaben, Auswahl-Dropdowns und Buttons würden mit Padding, Schriften und Fokusringen der Host-Seite gerendert — pro Konsument unterschiedlich. + ## Kompromisse Was Sie aufgeben, wenn Sie innerhalb eines Shadow Root eingebunden werden: diff --git a/apps/docs/guide/shadow-dom.md b/apps/docs/guide/shadow-dom.md index 4a2508a..fafbe82 100644 --- a/apps/docs/guide/shadow-dom.md +++ b/apps/docs/guide/shadow-dom.md @@ -54,6 +54,15 @@ The `tpl:` prefix only protects one direction: editor utilities can never collid Shadow DOM is the only standards-based way to block that cascade. The same approach is used by Stripe Elements, Intercom widgets, and most embeddable third-party UIs. See [issue #70](https://github.com/templatical/sdk/issues/70) for the original report. +## Why it's the default + +Shadow DOM guarantees no style leaks between the host page and the editor UI — host CSS cannot cascade into the editor, and editor CSS cannot bleed into your app. Templatical enables that guarantee by default, rather than as an opt-in, because the conditions that make it necessary apply to nearly every host page: + +- **Global resets, design systems, and framework preflight are everywhere.** Tailwind, Bootstrap, Material UI, Chakra, Mantine — every modern stack ships rules like `* { box-sizing: border-box }` and `body { font-family: … }`. Without the shadow boundary, every editor element would inherit whatever your host page declares. +- **The canvas renders email-like content, where typography and spacing matter most.** A host `body { font-family: Comic Sans }` cascading into the preview corrupts every template before it is exported. +- **Each editor on the page gets its own scope.** With multiple editors mounted side by side (e.g. draft + preview, A/B comparison), each shadow root isolates theming and host targeting per instance — host overrides on one don't reach the other. +- **Form controls in toolbars and dialogs would inherit host styling.** Text inputs, select dropdowns, and buttons would render with the host page's padding, fonts, and focus rings — different per consumer. + ## Trade-offs What you give up by mounting inside a shadow root: From 462b96e0d14f4fbaec34c3910b415f8db0fdcea7 Mon Sep 17 00:00:00 2001 From: Orkhan Ahmadov Date: Tue, 12 May 2026 20:10:00 +0200 Subject: [PATCH 23/26] WIP --- apps/playground/e2e/pages/editor.page.ts | 30 +++++++++++++------ .../src/components/blocks/SectionBlock.vue | 2 ++ packages/editor/src/styles/index.css | 7 +++++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/apps/playground/e2e/pages/editor.page.ts b/apps/playground/e2e/pages/editor.page.ts index b42b5b4..339ff9e 100644 --- a/apps/playground/e2e/pages/editor.page.ts +++ b/apps/playground/e2e/pages/editor.page.ts @@ -200,7 +200,21 @@ export class EditorPage { const columns = section.locator('[class*="tpl:min-h-"]'); const target = columns.nth(colIndex); const countBefore = await section.locator(SELECTORS.block).count(); - await sidebarItem.dragTo(target); + // Section's draggable runs with `invertSwap: true` + `invertedSwapThreshold: + // 0.65`, so the column's center 65% is a no-swap zone. Aim at the bottom + // ~10% — well inside the outer swap zone for non-empty columns, and well + // inside the empty-insert threshold (60px) for empty columns. + const targetBox = await target.boundingBox(); + if (!targetBox) + throw new Error( + `Section ${sectionIndex} col ${colIndex} bounding box unavailable`, + ); + await sidebarItem.dragTo(target, { + targetPosition: { + x: targetBox.width / 2, + y: targetBox.height - Math.max(4, targetBox.height * 0.1), + }, + }); await expect .poll(() => section.locator(SELECTORS.block).count(), { timeout: 5000 }) .toBe(countBefore + 1); @@ -355,17 +369,15 @@ export class EditorPage { const startX = sourceBox.x + sourceBox.width / 2; const startY = sourceBox.y + sourceBox.height / 2; const endX = targetBox.x + targetBox.width / 2; - // Section's draggable uses default Sortable `swapThreshold: 1.0` (no - // invert-swap). A swap only fires once the pointer crosses the target's - // center toward the source — but ending too close to the target's edge - // overshoots into the neighbor's swap zone and causes the dropped item - // to land one slot past the intended position. 60% / 40% sits safely - // past center without crossing the boundary. + // Section's and canvas's draggables both run with `invertSwap: true` + + // `invertedSwapThreshold: 0.65`, so the swap zone is the outer ~17.5% + // of each edge (inner 65% is a dead zone). Aiming at 10% / 90% lands + // squarely in the active zone and lets Sortable fire the swap reliably. const endY = targetEdge === "top" - ? targetBox.y + targetBox.height * 0.4 + ? targetBox.y + targetBox.height * 0.1 : targetEdge === "bottom" - ? targetBox.y + targetBox.height * 0.6 + ? targetBox.y + targetBox.height * 0.9 : targetBox.y + targetBox.height / 2; // Sortable.js gates drag-start on a small initial movement (>1px) and diff --git a/packages/editor/src/components/blocks/SectionBlock.vue b/packages/editor/src/components/blocks/SectionBlock.vue index 79ae2e7..2fe8c24 100644 --- a/packages/editor/src/components/blocks/SectionBlock.vue +++ b/packages/editor/src/components/blocks/SectionBlock.vue @@ -125,6 +125,8 @@ function handleFetchData( }" :animation="150" ghost-class="tpl-ghost" + :invert-swap="true" + :inverted-swap-threshold="0.65" :empty-insert-threshold="20" class="tpl:min-h-[60px]" @update:model-value="(val: Block[]) => setColumnBlocks(colIndex, val)" diff --git a/packages/editor/src/styles/index.css b/packages/editor/src/styles/index.css index 19ddb95..762cd73 100644 --- a/packages/editor/src/styles/index.css +++ b/packages/editor/src/styles/index.css @@ -455,6 +455,13 @@ display: none !important; } +/* The sidebar palette is drag-source-only (`put: false` + `sort: false`), so + Sortable's ghost insertion line inside the palette list is misleading — + suggests the sidebar accepts drops when it cannot. Hide it. */ +.tpl-sidebar-rail .tpl-ghost { + display: none !important; +} + .tpl-dragging { @apply tpl:opacity-80; box-shadow: var(--tpl-shadow-xl); From a3a5bb64110351162c10ee81401671763cb1c4f8 Mon Sep 17 00:00:00 2001 From: Orkhan Ahmadov Date: Tue, 12 May 2026 20:22:53 +0200 Subject: [PATCH 24/26] WIP --- apps/playground/e2e/pages/editor.page.ts | 48 +++++++++++++++++------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/apps/playground/e2e/pages/editor.page.ts b/apps/playground/e2e/pages/editor.page.ts index 339ff9e..c76e763 100644 --- a/apps/playground/e2e/pages/editor.page.ts +++ b/apps/playground/e2e/pages/editor.page.ts @@ -201,20 +201,40 @@ export class EditorPage { const target = columns.nth(colIndex); const countBefore = await section.locator(SELECTORS.block).count(); // Section's draggable runs with `invertSwap: true` + `invertedSwapThreshold: - // 0.65`, so the column's center 65% is a no-swap zone. Aim at the bottom - // ~10% — well inside the outer swap zone for non-empty columns, and well - // inside the empty-insert threshold (60px) for empty columns. - const targetBox = await target.boundingBox(); - if (!targetBox) - throw new Error( - `Section ${sectionIndex} col ${colIndex} bounding box unavailable`, - ); - await sidebarItem.dragTo(target, { - targetPosition: { - x: targetBox.width / 2, - y: targetBox.height - Math.max(4, targetBox.height * 0.1), - }, - }); + // 0.65`, so swap zones are the outer ~17.5% of each existing block. + // Aiming at the COLUMN's bottom 10% can land in column whitespace below + // the last item (column has `min-h-[60px]`, items may not fill it), which + // misses every swap zone. Aim at the last item's bottom 10% instead; for + // an empty column, aim at the column center where `emptyInsertThreshold` + // applies. + const existingBlocks = this.getSectionColumnBlocks(sectionIndex, colIndex); + const existingCount = await existingBlocks.count(); + if (existingCount > 0) { + const lastBlock = existingBlocks.last(); + const lastBox = await lastBlock.boundingBox(); + if (!lastBox) + throw new Error( + `Section ${sectionIndex} col ${colIndex} last block bounding box unavailable`, + ); + await sidebarItem.dragTo(lastBlock, { + targetPosition: { + x: lastBox.width / 2, + y: lastBox.height - Math.max(4, lastBox.height * 0.1), + }, + }); + } else { + const targetBox = await target.boundingBox(); + if (!targetBox) + throw new Error( + `Section ${sectionIndex} col ${colIndex} bounding box unavailable`, + ); + await sidebarItem.dragTo(target, { + targetPosition: { + x: targetBox.width / 2, + y: targetBox.height / 2, + }, + }); + } await expect .poll(() => section.locator(SELECTORS.block).count(), { timeout: 5000 }) .toBe(countBefore + 1); From d28e3ccb13122688ee8cd966205ea2e6bcf7017f Mon Sep 17 00:00:00 2001 From: Orkhan Ahmadov Date: Tue, 12 May 2026 20:34:19 +0200 Subject: [PATCH 25/26] WIP --- apps/playground/e2e/pages/editor.page.ts | 36 ++++++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/playground/e2e/pages/editor.page.ts b/apps/playground/e2e/pages/editor.page.ts index c76e763..3dd87fd 100644 --- a/apps/playground/e2e/pages/editor.page.ts +++ b/apps/playground/e2e/pages/editor.page.ts @@ -441,14 +441,38 @@ export class EditorPage { await fromBlock.scrollIntoViewIfNeeded(); await toBlock.scrollIntoViewIfNeeded(); const fromBox = await fromBlock.boundingBox(); + if (!fromBox) throw new Error("Section reorder source bounds unavailable"); + + // Sortable replaces the dragged element with a zero-height ghost + // (`.tpl-ghost { height: 0 }`), so once the drag starts, the column + // collapses and the target block shifts up by the source's height. + // Pre-drag `toBox` coordinates would aim past the target's new position + // — drive the mouse to drag-start first, re-query `toBox`, then + // interpolate to the up-to-date target location. + const targetEdge = toChildIndex > fromChildIndex ? "bottom" : "top"; + const startX = fromBox.x + fromBox.width / 2; + const startY = fromBox.y + fromBox.height / 2; + await this.page.mouse.move(startX, startY); + await this.page.mouse.down(); + // Tiny nudge so Sortable's drag-start threshold fires and the ghost + // is inserted into the DOM, collapsing the column. + await this.page.mouse.move(startX + 4, startY + 4); + const toBox = await toBlock.boundingBox(); - if (!fromBox || !toBox) throw new Error("Section reorder bounds unavailable"); + if (!toBox) { + await this.page.mouse.up(); + throw new Error("Section reorder target bounds unavailable after drag start"); + } + const endX = toBox.x + toBox.width / 2; + const endY = + targetEdge === "top" + ? toBox.y + toBox.height * 0.1 + : toBox.y + toBox.height * 0.9; - await this.pointerDrive( - fromBox, - toBox, - toChildIndex > fromChildIndex ? "bottom" : "top", - ); + await this.page.mouse.move(endX, endY, { steps: 30 }); + await this.page.mouse.move(endX, endY); + await this.page.mouse.move(endX, endY); + await this.page.mouse.up(); await expect .poll( From fe69af5258724b9dd6a50959a963bad545693db9 Mon Sep 17 00:00:00 2001 From: Orkhan Ahmadov Date: Tue, 12 May 2026 20:43:14 +0200 Subject: [PATCH 26/26] WIP --- .github/workflows/ci.yml | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f82d5d..69ae941 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,13 @@ jobs: e2e: runs-on: ubuntu-latest + # Playwright's official image ships Chromium + every system dep + # preinstalled, skipping the ~5 min `playwright install-deps` step that + # fresh ubuntu-latest runners would otherwise pay every run (apt state + # isn't cacheable across runs). Image tag must match the pinned + # `@playwright/test` version in pnpm-lock.yaml. + container: + image: mcr.microsoft.com/playwright:v1.59.1-jammy needs: [build] strategy: fail-fast: false @@ -87,15 +94,6 @@ jobs: with: name: dist path: packages/ - - uses: actions/cache@v4 - id: playwright-cache - with: - path: ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - - run: pnpm exec playwright install --with-deps chromium - if: steps.playwright-cache.outputs.cache-hit != 'true' - - run: pnpm exec playwright install-deps chromium - if: steps.playwright-cache.outputs.cache-hit == 'true' - run: pnpm exec playwright test --shard=${{ matrix.shard }}/3 - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} @@ -115,19 +113,14 @@ jobs: # (after publish) and every feature PR's run still cover this surface. if: ${{ !startsWith(github.head_ref, 'changeset-release/') && !startsWith(github.ref_name, 'changeset-release/') }} runs-on: ubuntu-latest + # Same rationale as the `e2e` job — use the Playwright image to skip + # the system-dep install step. + container: + image: mcr.microsoft.com/playwright:v1.59.1-jammy needs: [build] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - - uses: actions/cache@v4 - id: playwright-cache - with: - path: ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - - run: pnpm exec playwright install --with-deps chromium - if: steps.playwright-cache.outputs.cache-hit != 'true' - - run: pnpm exec playwright install-deps chromium - if: steps.playwright-cache.outputs.cache-hit == 'true' - run: pnpm run test:e2e:consumer - uses: actions/upload-artifact@v4 if: failure()