From 7a896c5f934f844956ab07677af13c13db3ecc7d Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:49:59 +0000 Subject: [PATCH 1/9] preview: pretend homeserver for the dev preview, in-page and off by default src/preview-demo answers a demo.invalid homeserver from fixtures through a guarded fetch; own-origin requests pass, every other origin is refused (fetch, WebSocket, XHR, beacon). Built in only with VITE_PREVIEW_DEMO=1 and switched on by previewDemo in config.json (scripts/preview-config.js --demo, run by the dev workflow). Login prefills 'demo'. Pose art: 24 downscaled thumbnails in preview-demo/poses, copied into dist by the workflow. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/rat-dev-preview.yml | 7 +- preview-demo/poses/eureka/amused.png | Bin 0 -> 10874 bytes preview-demo/poses/eureka/annoyed.png | Bin 0 -> 9632 bytes preview-demo/poses/eureka/happy.png | Bin 0 -> 11044 bytes preview-demo/poses/eureka/neutral.png | Bin 0 -> 9595 bytes preview-demo/poses/eureka/surprised.png | Bin 0 -> 9812 bytes preview-demo/poses/eureka/thinking.png | Bin 0 -> 11619 bytes preview-demo/poses/nibu/amused.png | Bin 0 -> 12435 bytes preview-demo/poses/nibu/annoyed.png | Bin 0 -> 12144 bytes preview-demo/poses/nibu/happy.png | Bin 0 -> 12271 bytes preview-demo/poses/nibu/neutral.png | Bin 0 -> 12175 bytes preview-demo/poses/nibu/resolute.png | Bin 0 -> 12215 bytes preview-demo/poses/nibu/surprised.png | Bin 0 -> 12242 bytes preview-demo/poses/nibu/thinking.png | Bin 0 -> 11833 bytes preview-demo/poses/nibu/triumph.png | Bin 0 -> 12690 bytes preview-demo/poses/void/amused.png | Bin 0 -> 4795 bytes preview-demo/poses/void/annoyed.png | Bin 0 -> 4131 bytes preview-demo/poses/void/deadpan.png | Bin 0 -> 4353 bytes preview-demo/poses/void/glare.png | Bin 0 -> 4581 bytes preview-demo/poses/void/happy.png | Bin 0 -> 4146 bytes preview-demo/poses/void/neutral.png | Bin 0 -> 4190 bytes preview-demo/poses/void/point.png | Bin 0 -> 4900 bytes preview-demo/poses/void/reading.png | Bin 0 -> 5113 bytes preview-demo/poses/void/surprised.png | Bin 0 -> 4908 bytes preview-demo/poses/void/thinking.png | Bin 0 -> 5321 bytes scripts/preview-config.js | 22 +- src/app/hooks/useClientConfig.ts | 3 + src/app/pages/auth/login/Login.tsx | 4 +- src/index.tsx | 12 +- src/preview-demo/fixtures.ts | 153 +++++++++++ src/preview-demo/index.ts | 1 + src/preview-demo/install.ts | 115 ++++++++ src/preview-demo/server.test.ts | 234 ++++++++++++++++ src/preview-demo/server.ts | 344 ++++++++++++++++++++++++ 34 files changed, 887 insertions(+), 8 deletions(-) create mode 100644 preview-demo/poses/eureka/amused.png create mode 100644 preview-demo/poses/eureka/annoyed.png create mode 100644 preview-demo/poses/eureka/happy.png create mode 100644 preview-demo/poses/eureka/neutral.png create mode 100644 preview-demo/poses/eureka/surprised.png create mode 100644 preview-demo/poses/eureka/thinking.png create mode 100644 preview-demo/poses/nibu/amused.png create mode 100644 preview-demo/poses/nibu/annoyed.png create mode 100644 preview-demo/poses/nibu/happy.png create mode 100644 preview-demo/poses/nibu/neutral.png create mode 100644 preview-demo/poses/nibu/resolute.png create mode 100644 preview-demo/poses/nibu/surprised.png create mode 100644 preview-demo/poses/nibu/thinking.png create mode 100644 preview-demo/poses/nibu/triumph.png create mode 100644 preview-demo/poses/void/amused.png create mode 100644 preview-demo/poses/void/annoyed.png create mode 100644 preview-demo/poses/void/deadpan.png create mode 100644 preview-demo/poses/void/glare.png create mode 100644 preview-demo/poses/void/happy.png create mode 100644 preview-demo/poses/void/neutral.png create mode 100644 preview-demo/poses/void/point.png create mode 100644 preview-demo/poses/void/reading.png create mode 100644 preview-demo/poses/void/surprised.png create mode 100644 preview-demo/poses/void/thinking.png create mode 100644 src/preview-demo/fixtures.ts create mode 100644 src/preview-demo/index.ts create mode 100644 src/preview-demo/install.ts create mode 100644 src/preview-demo/server.test.ts create mode 100644 src/preview-demo/server.ts diff --git a/.github/workflows/rat-dev-preview.yml b/.github/workflows/rat-dev-preview.yml index 2871a19c..45da8bc4 100644 --- a/.github/workflows/rat-dev-preview.yml +++ b/.github/workflows/rat-dev-preview.yml @@ -22,10 +22,13 @@ jobs: - name: Build app env: CINNY_BASE: /void/rat-dev/ + VITE_PREVIEW_DEMO: '1' NODE_OPTIONS: '--max_old_space_size=3072' run: npm run build - - name: Set background scene tier - run: node scripts/preview-config.js low + - name: Set background scene tier and the pretend homeserver + run: node scripts/preview-config.js low --demo + - name: Add the demo pose art + run: mkdir -p dist/public/poses && cp -r preview-demo/poses/. dist/public/poses/ - name: Upload artifact # v3 protocol: the v4 client keeps only the origin of ACTIONS_RESULTS_URL, dropping this forge's /forge subpath # (nginx 404 page, runs 170-171), and upstream v4+ refuses non-GitHub servers outright (run 169). diff --git a/preview-demo/poses/eureka/amused.png b/preview-demo/poses/eureka/amused.png new file mode 100644 index 0000000000000000000000000000000000000000..655c2271e744c9b83847cd9b0970be2438b148be GIT binary patch literal 10874 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 Q_5c6?07*qoM6N<$g1o1IX#fBK literal 0 HcmV?d00001 diff --git a/preview-demo/poses/eureka/annoyed.png b/preview-demo/poses/eureka/annoyed.png new file mode 100644 index 0000000000000000000000000000000000000000..a142771e44564acb1031a9616d7d2db4b40ae5ed GIT binary patch literal 9632 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%0Ai_M?3FqYo`0iztgNKoA>XG| zCnWYG62qr2tDAO^-_2ig9RncDeFVrPqKgf^)hl(1z4l_ms{aaa7%-E*lDx2xpR4bNIsZ=V7=26|D+%{vq zJ{Y5;%UQtyr%`tYccw!-E#Qk|U)N)o{+0{m>l={<(|4L7XW{tWGu?`Xfv{3wP83Hz z)hUVf&ah`*pIy&f`TSz%;K+>otzxlX_2O6$0%H@ScT2~EW;&k=P&C(qS5UHPPpDPk zvPGd#s8n3n@qOK=daSpao%8$4Yf$w4>BY{)!Fg4$1D`{DM>T2~`Y?>2h)5K(nTL2v=6-6sJ2GxmA_5S$K$>nySG1%BSfJr>qX}Ufd8Cuq; zX$)K1ub;mDi)(8$Mi=!Au?xJCb4;hM`FHVLeU~gZfDjT-_d?sTNihWx>(z zA6~kGC%>LV$YhHv*@PGs6~!fnm@^hGTPM2HnVLN#`KrHiWKGbbq&Dn`JhaafE&zXpyEo3~lU zm`}0A49js02dfHpfBl60hii7HX?E=@o6V%z%ui%0LS92XoHP@0|*D;W1r41!oVkG1~G9UVw^QE&Ih!EqnY&waSSZ)n#MvVhj zoKdkIxPir$EGZ5)n2Vx-VQjbtiZsQ*%QQl`PqN8gJr=}|W?cm&h|@m8Mqpuvu@fc0 zSHp~C6ILr&tdJCN!73z$LV$v_f?3T$x}g|pcZqM2q*mS?k5o%DzyP24(rhNetcIMV z#xTa3XGTe(VrWT%-9o`)R85iu7ivk86pSc34v6POC{h$f8!a_fuZ?e2Fd$dbl=O5y z0EEXysH7P|hO1$j90xuP3b>Xq#saHSW1b6u2^S>uqy7EE7Ok0qVkxylV~I7_`o>@a z!37&y&AF-p78jvqrLkf$X3)rV;?d!-U3F;ye2G&tu!d!iB?)7OQAi3&5Dm)V02!vE z5M}T1j4Z3>O0B%_>`&*nFlM9SzBHQxVTPMdYaUB86-DMrIV_KT*D`&aB(57348zc3 zCEzV(|9Gl1r+(JQdA_E();~#hWNcZ)~;K7pB5D_(H@8nir zj*e3+Z#(ET`s5t-P-{ zCkh5Y7*IapfSDkMCWEW%uIsV15^0VDcm4pti6}*SQiy_rW+;B7?}xWp!*sE>neb%= zj-n{iNoy(T4o*3T;Kd5g$R}a>iu+5((-n+aKt<2Dm=Xk$WnjiluTV&gT11nYnJ~>T z94GFDCyI+qiy51YZ8D}9fvhNsa=fHuvSyF>Rjgn|J`BsPsfM>?YB8+%l=!;B0-6*U z-`)XUk{Ik5vB0fphM`4@*9ym)iv$5%k_Nw}S+0{-l&rO!NVvW#w#uV)n!kqEQQ7pB zT8ny&X*wm8;B16!L(vS73Ood?$;NRV(wt@}4oakYb6|ML6l#W~@nzKjFpZay?f6?g zba=97oHWDzV~@=eouLzjxjGf8%hD0D1MF%~OE!X1r-}_j(R|lUM}y{Bj7-5*bq%K@Bm_)-29yk^R5VpXQv$W(x?~*Q zRw)%6Jo_HY;(%-!EfCm`Bx)TJZN2(5EedX|yM{;+jWK2s5~A0D@p!*ZwY2INDg~$7 z3s0&f7K0anijr2c@-n<6wdykLpN!jmf}i=)TaF%kgqpg_FpDUqY^FX<(+taq4?p&* zkripsx~aCq!zzi1&%ehoG)W7Tw31F6i*JwlLn#>7I_>c^zB~cCIw1s65dZ_7P|N|L zgwZt1@OmE)J;$U8G?z9z{!fIu&<=kijrBz_IS-@ zc<-cK-j}jmgvwNqI1`9OM?{wx-gGFTM&hZGhbskFR&i1-ejE!HV=7|2OMKkdW8&!% zh5|M*keh{&nO;JuUf-}V*2d-X;U1W^lD4L$my&UOLMhQT#taiWmpFlP0%OJ!C90M_ zj&+vKGD5isU8KjND(i?wK|u8xprR}qs$mG&!q^&=!;`&PgeFr#W@>tjDABD=%|yBw z$EE|@V58;+5ei8A<2YupDWy@NQo$skvFKTvIW)^);2qpk5oURSaWnM^w z2#E)x#Msga(nX)Brmj;lHawkDgy5_!mJP4{aX)78Z3c2G1vu^jz%*k4uG9=i2Om+C zjI*#2uIUn1uFB;nC(gLKmR8ajumyW7000BdNkln5zm0b3j}gB8tEi4b@k&{7zNK%E3tKE?onW+4%#R^HLr!_qhm$1@+{ zI@UFeHBmH8gs2L2H6p|^SawrJlu|7_1r!Zg^v6fN^afdHxiTF)%XY84womk$#&3poW=ZB-ltIU7YF| zFBA%4O5Rnt6PI6PV&>{Zj0q9>2))OcDk37v98k$J!}<D3<%W%zMc7($dd>Fn{4CsyP^& zqNr-j@O7dG8jKNG3dB?vEOKjdeFL`V{Ao97)?($M&Cy}*11wJzNtziV@7HROqT#nl!ID#j*I1;iqQX-=N+yFd z1_y-jOhmut@);V{U>OxA19Y8OhF6+931J1zua3)gQ}lg8AZtg+2jkEzop_7Aj9)K@zA2-4yII0G{kl#*D>#{9a3E)vnL_U3@NfC=G6KV(4!acDLO z8|#EP!eW7X6+|WK1Mp%cqxpP*)l-nwTgl#Am zK%{{;{y472L^P7b^GQ%`h5NFidLF#z=W`7cA1p+`!2&xwKFvr-P(2LG`+FHh!Po@_ zin^=##OE_o#3#B1iCOf09hOL9S2iR7^h7I3R1yA$PI*w{nHggY^JZL*Bt}7Dp|j)E zst005i_0{{R3$!0004NP)t-sOlff( z6c$l17!VE&9T*QE7#2ZAQWzEv850&86Bh{w1_}oT2nPob3=0Vd1`rJmpl2Ej3J5(! zNANO|^J!|BMqqJ%G#(9YOhczs4#tIf zYG!U}Js2@PNqJXLj$l|}US(E9J^j|e&Z(Ebo{e}#8**=WpIaS;QZ2BRhM#L<+O?s6 zYGBZmhM|Ud;HQ@T`t^`_Y^I;8^5x(2g(|RWG@DRJ%EG|Wd^dxGnaX5n{i0VAl`2L6 z000qmQchCAUd_GBPziW$0*P{9h&v27<4i+{e2| zcZ-PsnwpA8;l;&8rZD;RUqr;#odDc_#=9F+($r$}WTSP~1y9EQHNs#pF!n_KFYuBZ(oE|E&5604o~i+_nQ7zn;V+<&3o^z=@%mrEoPtND{wB9RaMJA}bNaMFoB`zN}o zsp%anUri(ut$HXPkC&^7)zS<6-4mpZTQe#chnYT93y=p-?DZYNc{@94h|? z-#=J)3d`4QB@&71cF4oSgrQy{SDy&}TtaN$JHG!&H#I#?oa7RTTwaWaLZWz!c#_D~ zCx5CR27`e~=fB|F!SbbA>%MwxcZA!CT>Ym;N-*%^zu{Z-onrmT%bBDGq&b-qw&Ztczyw^O;&i=X9&!NBv^+OvN&Zw&|U$v{3l3hiqB zj_@S4Dvtde!eC(J3Gw%QI4q{`nYTLdzECJ6^18Xba#j#{hBR@m{L;@@J&OVBpmtK>mt%%eT{9O(hcB-|$$D zB?z*aTNzy-O_DGj{B$d|Ju&=12?j=9E6@I4zUiG&9KCn`#uFG116EhgXc7-X;6rh= znp%G~Yzc#b;Dqp30e!wHpKozHcQ1MY$$EIcf~&Ua5t51|#!I>C(A_W?c!mDLH}yT= zNg|Ot8%%AYdW%O`+(^J!n`9va>zm<=; z#UlvI++NA$YK#v^3h6C9zMUE!u91U*7ycCd7xe}LPA}$DiIg86Vu-(!&jZY_G z9|iRC3--UnfDh;_dCNwU1W8n?7?2ix0Zm2T+B&t@PbP;7IHCVPfYQ$PH$G_X=66C`Px%D$O7#fW@pcvCWV!LZLaAp8z*+FDJdPW|Om{0o@+wR4t1F-W_~YCIRE6|uMo6c(wzl@g;Z-%kz&MCTW+Qii-}5c@QmHe{ zhY&Q~Ej%)&nLa&LbfB{4+Di3PPn%Fr4x~ z7zn=PW+RcmMcnBo600~qbODjtzRQvXORy}VXru+9Xs@sIKsC3r%IP5i6FMf&jUJ(( zr1_x`;)z(1{vHq;IDzYDl|urna3nhz1i`65EEP6ZoaRby?i`z3HILLbQtgNt|!V1Co8$c`6AyxpjeVnA#n$Y1eOIu z*p~|io;%pWQ^a2brIt(K2o_n+;f4ErEa4O?Zx5;)3w93|GX&qiL7JReWGz4St0K28mFmJi>T&pU!bZzNW#f+N(D$+vreB>;~Bz1oXahm)zjVk^Qx z@=t(MO+P{q%d$G6FdLT7A~-%&+dRAob(cFj=TvivvxN$?Ta3k)D>e%q45(re2@+4o z{&+MXNm0u8fD$eeaFH*t1S=Hm+cKKv0P1fZUZDQlW1!e;IEF(id{b;B-}WH~y)afu zLKO%W3}tZ_3`|&Iiee(s@28VmE`{e1fPIcfwyz+;ny9mPlf2lSZya6&ZDAvkIHNl% zf0T?RXD%IrLO6g`lTdkrtNK%yP33vx6EpGTJ#>7+(Pz6<01(Fpi{rQ_>WL&x};SZt|6A(J3kUM0v7&ymAezx#kt zn7!|Uo`7DC_A-l(XqL56rN6ZKHum0et{cT?E?P#>hMUc?e-F@=K9;?_BIl=QlmhUfb-n@+^ zV^T?|kB*C9M_2jI+u}^){hQ<)Cmz@LvuZrvad1$HCJW2?b^GgM0e!!veFQefW@ZE*fvv)|)rU znVFfHWIz7l!valy{qP&_R3J;TKqTyXd2W;$PNE(Uad&{2ad9KHTAHuK5o@a~Nss~y z-_$<5dv_X7(`IHi^YQD~{fn#6djGA*H*@7^?|xfggpcpO#%s8+B}Kr&cDX$Yhcg$C zJDBeqU@e#0t}J)rJZIu4Ayt;TzB+yP?zhEfh3IMK^xeC6hi^IuOAViI=IB7(UwF2a z{`l_n;2@5t#1K!E^5ybqVK^H|6Xbossl~HYb^Wqm!P7AGJdk7#lE+`)eaQTl&V1PV z@b2Tg!}k{rpKfMmrtvNa+v&%5?>;tWig$pG!;9HW_UqP%cOM$><2*qKqG*@q=H}Mc z%TFF3#lgS}^?L#Hsa)xDc@H6$;8bj5(;>j|Oe6MT|Ma)h*{|qT+i8yf5SdV4YA%geFz@0Nd@y&UN0t zjK}8#PA&ElRWZK1IlojZ;NwCTuhIy#*AE^2jd5eKnZvKQ;Em$3Bj96gWV6gV25gt- z9vdZi_jfk@O~5Iun&`ztp}o!JJ%2rCB8MR0?%VFc-|)@E8Xv!26#dVNM+kTgAUsQY zX=ZNj(NW^ZFxJlply|mQ`~%_L%>f`wiz;CYjkkLjf5nT<%)~x!9s4u#MYHK2HJSuz zqV`atBp7%Od_JFWYH=gc@_P?zmx#gDAZJ2RU<+@GIEw$whY4r;*!Z!T*zwFo94jai zoJoQK;3K01H=?lL2#u*`_4dR+?jV{lJqdxR+s!|FpwAbZDZaTb4n8ejdw_s5OwteI zc6;t|a|s5d$;hpO^3F*jSGwyrp$LJ%5>g21=>7Lm92*E2YrLPqhW1zwJU~@*ZNi&M z=GyIi=}|Q`UNBLV8ODf_aAaz+mB4Gc!2qO6u!O-fXtx6^x3!Cp=eeH;ICvC`#TvYK z9%V{toAd$a%K7?Zqht~=6r)5Um=MQs{T`sHk}T zeIpd1zn8Y9fZe6H$;O?2v6iycwVowsFQqo&Nk=oKEf{_VV}NfB${-xES+xlMXM?tz!s8mMrlkXt&Gx zwc&J`;kGD|q3#n7{18z95u_<)z;3^D`Sx2zi51^``Th4V?|r^x@j4zF@QJ44+VzTA zYOm$n!)lPifr4%&q!wT+QT4N-NKv9mkVJ-Q-W-Vin{R~4#_sO!8@v-2iydCoZhb-w zA%at2xni%E=jKX7>psF|>peoSflC_XJ0VYyW|ni7Hh1Hdy|+a?bNGqns8RGH`0&l0 zdFyyTj37&M6}vQ-Ums$V-uHymLT@EEir*E`K=fML(B<;xUMSvqAG?cN?0d0T@#?@g zZwbXqZCnd-c72YAYrP>k;CF=IM9i;vRgFl&5I%b|(C+5^<|T^zh}h7{Kgm}v#mj9R zBOGkc@y^`P9Pk_Agh+iOJXuNLRvAd)tpbvTwaxkY<)unI9h8vjK@*>B8Io5 zi$|S7bYQr6IbVPyfm~eg>dt5vo z7j`eMVsFHOdE=$IxzbvGZat)S^!0XoP5dd%)WKk2{0X`>GriEkH!wmFAcr+s(BH&> z^P784TomzcuT$~5F>x#Z`rKR_*MB94$}g?Ad+YI^!8gzlfkCX$3l*HQ3y2m4lC(R^ z^S*}jUe?2{Nd9~+hDv#_IGt;Ys=5B_!CW4zy6(lXKbG!F zT3qiiieZ<$HC!E9U~TQ!+RwXGf|tpVp(zM`5os?Wo)j1WbRGC%GC#k(cURF;G-Yar zX0YByLdk?{8}*G}huM4i7s8PVEFVSDw`RuarobSi^R;h)A_71}II|P7rfIT{6)f}A z+D5ss_UYjHPb3TmUWCBF3GdR)8KMxfoq-oF@5X6{hLA##rbt86B*I%E)eCFAwI|~f zt)D0%&NMFpL>SQAHUxr4uZKAKUi+Y50;laz`e_+Sb#J{7|<}NGR}jga=zr<3j28y1_L86k!>?H z%`gxFI6$N+xRF>5?QQN>_U4!7mzH<;HkU7(p#nq{r5m!no}X*4wcG92ue>g;vGS+d z&$&?&7=Oa^5K;`oK#HNk0MSb~i59xtT)LcJT3VXl+gn~*UcT%g5mKbYIqiJ8obRnY z!ChotH)8Aa&yWx=?+}0x@|cWaY?}t2rM*yTue6-YdCa#oe~F`IX?dO(Aw|gaTE1NF z&8_3|&%09|cvb$jHiSdOKjGmkf1++eg!HV&n>NERj7^K4jF_;z(sPzPz@?>2KUj7{ zkXFdj+*&!`M)*)^0QmG}>+`TDWB*31?7FhTBR-oUB$qWA+GZe)5JsdI$}9QGvJW`O zhs!0zaIjo%_sSyR#fgz`5`G%8gu%emNuXt2S#~)#$`rJW*iOCOs~2pOVF1nYe&=*BFnRLn*I_P6f`Ks* zRuqLLbp2U08l5!@Tt?4kGMP*|%ko~_0GcWEs^V_HyNg9!zP?Hp-xugoJ6|u@Hp2)i z>q-bmFOK70^;3U{S6+gFN&Q(AUxx^@L^K+eXU&3^(KDHBI-S{944&a34IzVC8(ib~ z`ts)HZKIKF99|bd8S|Mo=^7R*8GaK*Fz}+>S{b5*UZ1jm+!T!{-Ois$BRb8 zvjai~WF&VV^F32FO~?q4p7)t|392; zudaN?8~j75TydA}K)`4;9N{qGY!s;bN;aFxTBECHx*%m#+NS8j+(z^G_~_{P&90H< z;^p4@0s=~H_19m|Dz;=0ns@XS3_M@osD5gCfjp!h(L2Csc$UEDvl3S@(pJV8ZGHOn zbJrCzX@OI0rqF5^I8M_vH=Sidy|n@hMN>Qb{Q1|et;v>j+tN6JC7cKZp5vcl@o2uM zPrR;*yP=1pa#XTG+O@YoefoU%`Ru^TX0oa#Ddzg>I_tXGtZT?%u2(SAq^L+|pFe-@ zvX)Fpx;G041LO4#?1hsDwZ|`@?z>xfMn5(YVjpM_^$H$82g7*#-qv2Tr2lXrg zEi~J0oo!bbpjp{8CY-gZiO!;-X_{ouRV%LKDu|VBn*xrahY4AByh5MCw8>wwmHVng9TwlpAO0ot7NwqjbwRny*Wd9n@)0ap6H;1ug?4}Df!mDH-0r>QZ z;vd)D5z3k#4M)Q=O99PL(vl_UisoX%?801P&Q=6LP}Rz4N40bvF^Z*GmY_gWF$~Rf z!tsNTyH|&e%Wefgl~cT)`G*5O2eVkV`w~iocZ@7^fX`Z%U>TAwcz}}C=~$M|xrXaz zHQCB$(*i=8E4iEwRLwACfJ?zw*GK(Wa=BCnK!v=(y`Fq{`GqS5g;TWf)Raf*eULRh z91a_rEQp$-yRsqcqK-|hyOL?7HA8W-*{tDO>1;ZyI<{#l76O8y8JysZ2LdlHc8?A> zOU~7mKqxG$Xu`wKje~*7Uf0xhon)2pK*?}8qAPOPFk}t#t}Yvd4I$DqS<97NiO)(( zn3l6y*~PJ)v0%Bk-E?#$$lA{Kxjh*OOdMUmIj)3`i-#Aq!r|{9J+wZ=#nEaF7cvTG z2|9ZVsNiE{PL>J4J1o=LI4cN1w_MkiRK=1MiP5rI$>rR1I-@P*Q>mO|Azc%qwHEv2 zX<+jBrn_6ZK8z&~HCDlC(|I`H$Y^U(#sz{b>8mZ$KY*9RdRSIWoNGi>X(dus$0kCm zn{@?MkQ7B?xUB0+nv_XrT)woOKXVL6&?$Mb*IgJ7ytv-noIk)l!#5p<=XqTd9=fFz z42}sggYx6i&!`<9F}xWVi`FsYdl}7wnj^q5EevLQU%AAEY4NAbT(^n0;lV$ zB`_4=B>^(DvD59@F9KuD&CTZVVKTYY6hinCfTH0mY!3sRY}IzW-X3{pG_k!H4o65X z5|%k#5*S%CWYq-@Gzm>mBo#RpZ)DOIuN#Ub@TGbQKpt2|Hfsom1!&%ga2wXkz__!w z*Stv<=PMV7X*;ZFist8^2k<=|UG-XQ!(n+bztY^nfVvcx4MAoY$-^2I-NCmBtmteUOm~7TveB|SywkCo?>KqCx5aq@`@+y&eF|N<@hievseYY;ltW1 zf`R92tBaD?d?XRADmfbtN3dxPSy!Mf35KL9n&i3?r|DT&5`fC3)9JM3>U!EhXVavO`dno)oz zhh>H-a!fC^O*km78JwochN4)i zCd;z&dSt?ihKb73@=a|wc6e>$ec14WoG=)eT-dIXa&)n|n%YnY%MukDvOyyn&~g~y z%^Fw2ZYar`rnBo6PT1EJ%|fnOPW4O&g&f!9SdQg5OW-8gaQX2uRn|0Te!01O*hn4= zk+5t$$O-X0LRNC!MfN0@%2PgHSP}p$aS%{QkQ5M#E4V{yFm$%kMnslVG@iG1>WP-^ zpb(M>Y<~>MO0ppdFP^^EG}&%8`$W?I_mR9!{J#S z2$Bf_1e~M*F~o6$_vbiH=PJEAttgt#XLO-btyT~aMPu*+LboKtkd!CE7lN#@h05mA z=JIZ(01y%$bOQ^QGOjtgvXaX?wiFHffKx1>H5DSj$g+mSxb+=ir#e@#WCa-e8MUyw zGFo65pxgoKk}MmN^J)?)QI3KA%ia21yNn>C;KoOFX zY#<=&x9`t!9H~^Qxk|wjY#cI$jf4k?{f|==g|KuW!MSGrODQBW45XQbcB{8W)BJ;~>R{mMYfakO&gEK*xAJ*}Dn&C0Vn7+tQ1t*w zK_ECq_W%ip;j@`cIuoADV?ZG*5UP8_qrK=9Z(G9A2byQ1LH`> zw_p>!dw`L!BtV)LDTXbAa29~$^#fuEX2uS4&!;l0~plIIJ*T2-_A_XCYc(0pbAgt6La!WB7 zm=IivTTSG=*)AO6Ox#E)%L-&5VkpfG1CeH+BjKqGm-`A2z-nrAQ3^{6;&8QUW0fp| z0f<)t?@$cXU1#+CJQNBskOm$f4FT#uppOf?pdd{*tBI;LurJLc=4pKNPKS6B(G;PO43aD_!~g&UW=TXrR0E(%r~;=c zw}6Ihi>^W0fMRG%ktM;{w>X}HmL!zU&%bnQH_ZY*v7#9Y;d|8mA8%(323`PN+BsQC z;BvtOB!DT0@ty)c%cMm|Qbn33wOLuxtxSevWsReKKuoA2+dq}UfU2OW8V`A$N2BNG z=U+PWn@t?y2>%L+@h;xz`cVxhcvITxB@!o#h7YJg$jRXd0r5ec7*bV;Fkn=6Nj;s_ z+3)~RlI5_*=@8jAggzk213k+FNKvJe?ep_)w|296P*|XFtl$=hPX9;O#&{xD1!=LB zNOX7ZG9YBMaM-4hh!7Ao*+vwtD^XeEpaXd=tSL+@HA-@%>76n%JHtE$9RZ-dxRFq9xE6rM+Dif|1EK-yGfS;DJt zjtgrXvymEI@Qy?ohNemmg#k4@e_%jJ!_e7z_q@Xh{mRnv^5*jVW)1h(#Jk-eO*Fy4 zBovfLEtjYc8v4Uw*?=^~k`#lZmoi=1z5~=GiBs+X{q(_5^|gWrD2EkE(Odu-k?Ngy zM;V*0^*hVU^UIq{p^z94b?nqztp(T3_%K)KmsqE}~J0-9GE{3}aLMZfALO^QIpU#Y^XX{znT|e4)-TjQc^1 zB~<)VVy}CKkz4`-+Qxtyr*NbqYl?jbND*msJy%{}7*5hCz(A-2AOfS)YErz-Fm$)u z>F+H$adGs#M&okk2QBNhPgXH z%FMbw$$SjOA& z;{_{T(Lsjh7d$644B1ezh>$k~h^tk8Gnw)FTeaUKK^`UWCZ`{8a&3v#>Vj9_#eGz@e^bCdHmGaFTae9jgO4nz5;(b u<~8GH4QG4g*U6Pe6*bv7;l0000006NF0{{R3bel#Y0004TP)t-sOlffr z3=0km3JVGe92yY{2nGrW2OSv>K}Asv3J4w>BMAov3J3=j5DE_r2|qnXKrSeQN*O32 z8s&8mJw!_i3J4h*B|Se#EiN}NFgiU!OEEJ%KSE6$9Vaz5J{K1tK0`;M1&32;bRa{vGf5&!@T z5&_cPe*6FcAOJ~3K~#90&76l{+gP@M3C&?c@^)rXL)Miq)>W5}`UBPfFV0Z#rvJ^*z`(%J zn>VlfyKMhIAOnW}p;yDb*wi%5H1UhxzQI>RZ@QTOUC7{H?;jfM#Wrh&U&0z0Wh~wy zn%UboIPj+b_LulC;q?yIjSb-u$P880rZP^_n2Pe?u3n zNJJgkm_)0dD81_cj}V4>`Uhq90eEV_r;coRCyUGNowF9z_xe9T80s17HJOJ=ZzQ6s z8{X-1GL=jwlkM~Ri=qDj;ebqDzWi6NalImuh`Pb9>?Bjk)NVY!P)O~}P=7IDs3$Zi z^A8bU1n@>CHy4wsRC1v~#bU9U#Z)2w*AfDs#9WIn{BQV1CR+=sR4TdKh{Y&bmSgf+ zD!Cy2g@gg$=!48J5>ZFk({{3uT+RU=MNyPY#cGA*Zhhdd;_KXt6{dFUv0FTusWIXP<{1`A_N2MI>^Y6MHm6|TTB+F7NpoM z9*=mA<67c0`HX(O()eGik^N5GVHQT9WjqohX z@tj7*S`Dq;Dxd4!Fwo`>^g=aaAWqHSrQL(eB z)8{h7+Xy)FLjgtt_kh%U9g-=`-rxaXA$1~fuvnH&`&3L`oSJzqGYs7-_(v8PiHxvk z$wG3m6J2CVCU@{W?zmk@oe69J$Z_t{k`${JmY*jYf(PI3dVkEf0PaC``OLfG<2^Q) zYNr+y2k2OiWxaPxW^88awC`CY*x$*)UV%^ZJb?`Wj?_jzssdj47B|rx{ zcKmniTI@9S9G)ENd3EC7=ZB{=W;wGl0u=-HZ<@OmSuT%X=#1^-8(6F{A3-6dR}RrO~?-gsBWyJ zZuE<3C-W|F1&-%fmg8)WPyhVqr&Yn`S&k5OeSL{dhe3vV?1`Z; z)bsj<)iFTzPZ=ZXPS%UOtN+N5mca+Dv#=CdxJgB-Xwkyt-)dZ_3RUOP{ZV9WHb`_0Uszh z1qF=EI}*!fmQ)@}K8Vw4gF~8#Z6TkGi_3*V>SWTE0zgfJC!OVMq?#er#|40Y zN*EbwEvGsW+ObT5M=Av8Zk51sh=`($8>vMx2-N$?u7e19Vs;9{ju0Kg>3srWe;*3N zUOe)n6qnou1>2T`S@uo=!}C01Mbn&(?Is(_N~%y;_Kw5QGR8*?V}1nDTz<-G?2DK*3)q zOgr{;G_fiome=y%%$aa*gBvj=PQnB{mI->8`7$cz4-*8{`9YVwO$Z+=#Up`HQzWh4@z z>5)G~YuLn{CzHF7gIUCT7`uPqqxR?aHW#WQ@|5#rJ9Sd=@I`TNVp`)lA_aA|+d=H~ zy82%a4GcU2@D=eRDvRCl{}teIq2nPM;*lfKcQuS0OwG<6?AP{JZNzCisY2V6JbX32 zy*7_I&XwGt?CR*)9DK^5?Xcsk1}*Y`0PIxx$@7>@NdRO^!mao6$dRS_xr141b8VU+ zcLrLSLfvWZMWdxe)8<`?+yZi>vvqppIKi-^szjuj@cn!TU4&GsU8iI!a9$IW+aQQY ztT{1u@R6D??P(qo&y$7JzGT{~#b|VU?g%4SvZ0u@UDkzw^Q0ETu;X(yP2jxsehzUK zQ^^{52!gC}>%$z6G-|axR=k*6h$7F%B~q;{32V_p>!igUA=?f_~WYf}zlpRjWRKw236qW~dBFF+DzJNsYLR1iijT5@_KuE9A3rQR^3iy-c(K3rW&3Kj zNyX%5bFSRN00?rFYH<@i1rXDAM}VXE1Q?IWvK%z4IF{EaDn{Y-cQgH+rj<(O{r9vP zoBncD{FZCCT;t6*PXN5u@aSC&3HIV&CHndz&AEFUTR6deXqEc_ut;X*QBP*g4|_? zGjp|k!+mrw9nP>jLGX?M&r`|V>{d*ccs4lrU>#mry@-8#o33oo;^gnYr+<6<_U+!~ z)pUJ&q7((bvGKLo_qXpViuCR6Z|`H%n#8jlD@&|fjpyy2q=du+eJb0GI*W>wItw9Ywk`5BdA6#U_+et3h zx5~4W#PNtgzmFpXNtffLz2DNmzkmP!{Ss*S?ZI5}T6|+;W2J+orHzemzc0Og`~J&X zOkz2X$TCqH`FJhYnCW|@lZ1K($;&R`Z6Q5XYn2o8F%z0EW{~JKgg=g#O4}FN_wRqV zz6AmtE8XE6D;>PczW@FGyKirQTPt3~pu1(tl55uD@%T(5UVmI1gi_(2i$o$L-eMrY zt$ip$cpkeDWlTqDjg`i)X4k%de;>&2?ZI8Vv9VI|TlW3?@Rzs0ZI{OPV}#=fg-v=U zACKo}a{21xjIi5Xy$iUplA4M)%ZZ~26T|jR(|8DI5@)WBjm_l2O|8o z^zPf+y^FQzo+fiFE6FCV$K&yMzE;c6Jl2he-MbowI%_vss2>5K27tT^EdtJ!Yz#ZL zvC>$lp!x${M}~Xb2isrf_QtO$o8vS}k|YT<9Ixf_xkm*;m;rf<{zCWJ+Kx zmlHc)QmZ}6OZt0hKFr(^ptqRZxgHpc#Uz>|a4LcXTEYm={+R`W%%aipwU6TfIvx$0 z4V_<+_`1u{)h^&^^%1+0P@gDpBexP9*(?Nmx$fMAEV2X+Mi5J5&E)Y~SAfw!;631NjHELCWP=Do8@_F zwLGiEJ{Iqp-kmiDjN`$d;w5YcDa^V{zRQuDIFsx9X~2#M1zO;k&RiT3(f#ZPO#sWNO$gKu|Hp{JB4%-2QRPwlbroR^}0!=F&#L2Vd zA|*?=fV8V|9LuaDp*gV~ots-LMM1M&?T(F&7spF`QYY3m%jd8kR^aE^>F1ZD?9vPkxGuNjT2UtaIjkxp=MDYj2(V za=BKD7WV?cF5g}gTv(-%+wCm>SQz`Z|)f zCgyG=q$W>afh&ST)0ap(w$cGx{6y{ ziF<_Ob8E%%u_y$VTl>4hofsX=#$_Ceuyg}FI4(c_7$Vd&K#9UNpb!w=Xc^O@%`b;ffdxC=hoNy;q$i;$q zDAJ+~I`$c9zMN>rrnl}S-CO-ei^YqsdMQetk!8%X9%Vw zSc#c37$Ge~vl*qOqjDnAipkTL;2RwccNH9s9?VU52bb$qtWl%jdw8){6JSqL9ejon z%s&uni>cHpB|$+$OA?%@s^V&SB9T~)#i-S*V)3p3L40zzh-}Qns9KJa;VibE&nadt z-{^aWyLNvhq{W3)>O3fmX-vVvDsMLviA1728>3>>>g6~bGX*{pjTZM-Z|17w)mZg# zhLRkX7aE5-x|*MPo;wEt;cc{`>B?Cubq3RTGzNoWSytLgBoY(lqo9^;UF>zm7%g6Q z$L1+2mdndj?XW>%Xz zt7x$}K7Jcqs957L7t7`IRgk&T$mgo{+_T;T9C{($0@9V0LTa%ZlW0i1LG-t5ea2D>*1ml8!a4x#dc;*WCN>heYI!?WL_4E4N`X)KAigw0(Dh!Sj! zS+1+&z*Q2fw_6vA5-}wSW@n2Y&o{8gSMr})wZGu*TK@}L5i!0FI6^1f#IPcn9P8k~ zk2{20clK7IfHBc|v$IFf*9D}_G}`mASAW69wLVdyX%UN^G8DTAK3=M}SftQH)oq0i`6OJrUvCzr+n$}UHtrnah^Y0uZK-d1nt5olAwqt z4wTptVm`G~-%ljA05CC;m<4MrZ?$AB2{bWuw;IpaX5#r;eXtWc5dA9qfL%kZ~kLs$A2rWAq-^pfuU{q*R!E#`XKo~c>d;#G^qC7F* z!ipp2a<#*Fy&{+x_YU0*bPpfyycl{8z)F z%hAy=k8w8Zq%)bNrHs#sCNVWEnu4O3`9edRJ=(fX$+K762Oni(Hh_oAoZlpeXm_NS*OJUDRbKGKLmjy1|KOqj=JM)#I7*=#nG zSt2^oG^~hNpsTy*TC}wGF|oEb7cGsKN(ZYL=MLj_Y`cW$mcgpJfr6vWP|xc|elhj= z8EOW&w$@o;#zCXZC==!+JA)A5WwKcnE!mEV6&jo3%+5^upt$`7hTNdVwWd`X`Fx$W zMP(zM)h#EiTb33`Ez~nu`%w6_|6~#N_reHpmoV(0Fc7k`vjk?CNvE@ETi5L@!3r&! zQf;SxRg8|8fUdZAVaLq)VS^&X%&kmYlZK<~mgNm!2b#m3 z_Ke}$ht#JQ6c3Ldl@G2%bO^)Y(eUU9<8+iQ;*5{7*^T3~!l&hZRm-Gphan0r?VgJ1 z*-R#FAU&Hl>xXfy*jQsWKYw2SbY8J^!&5s0m*B^T?c~?tC+hh2RJ()8b8y5MMzXVH zrCaBPPoEb)pZK<)No%}sv2=B(K^vCpr_;V}Nx8#n#WN%`JpK9e*Uw)OQ7uokyBQ=j zT-!~3BCmU14?I5U_4i&+ri_9FM@Pes6@1{ze#UMVmOp>}`uX#;?qsq+sOw68Q9`<| z8H24+6AlV4xG`gOMVk;UQN*`sSr!|AY! zwy(Y1{QULv=d)v+wf#&ctszzA&XQ+>YQa84S8DA#m-bcDOnVjWc;&<(hUMwj^$bOS zUw)^(qFwAAtUdB#STI-thsJjaSswUQ9y=y7&OV!Nh9T{^hvebhemdN?Wqgu$cIAeRsjDx=#N zYyRe@NV}G4W~k4*KGg0!%MEg!2UDYrjzmgUjSBJfbe0&N0hvYDCRb8BO~%l5!%&sOYNEqrF`1p2zEgCIv&9*86 zZ#h4Ig%WP9G0dH@fMCdiQ6bbYI-~L!`<7u5f_zW+0>F&ZjIT5u+kpZDqOzmwz9(s> zq3gPbh)8rW&R}adc(u1bS1KJfs+5352a&!`DfTmfuWWd5#vQ=mmLlUY45RD1P9&1H zSwqzg%LF6LE_qg(Mt<7!eJ`C(r`5EVO=q(i>s3!>kx-U)+o#` zbN~%!lmS(#jzkPcwOqO>YS_sbURqVtj-4?KOR%m1(^=of=Zk0Ys_n?UZfzW&PvSQ{ z{l6Z}e%Y$WJ{I?iUqp*X2pKKKy2YtFqnW10(#H$S zRguMnP5XY@aa2!5s=%lHw4U}RAOa$B3h-LkxClBwg zYy^OeG1_YHuwjN_5EF^$jKf4&qB6QqY`R+5Ic?aU6DE?adPK7`9^<450k2URN4Jot zAwpLw80na%iLo(#2AAoE=O-qbm&MX{X}%?!nkd@Ha(-%;(*Ns@2Sq-JaAPxdJ_&%X z5n&kBK@p;;hT%JoE;Y=G%j&ku2oh#(JDXJ<4XL`}IkwiQV{DqHrKWwu@l;KuiNWv- ztL8w@@czVnGZEb`&1-8{1mco~j847%Kn)Mo76K+v@y2O#Wn+|K!Yv)8}}aHc_6N*X9lmE)@&R&>#=r&4+qkeK?=gJA5#kHyvz9T+pm;IhII#7Wsw- zK~Gg3-8NL!60l{Yv)Rm&=Qy^Xt{hIC!%j&;}+lFMGj$grk{=t{V<364f?TT}xYj+V_z`kgBVeh6O&G$=F&} zbsQsBOYXWV!(g}+sA;CANtiP%hTlDV@p_O&4nCS_0pG#)s_U>0Yp@R=5D4`Q#1|o{ zK~~$?OoF;cnNcJnRo7J_&;)5}#J3IPIVAYkZB5g#X=?tGC#ybUSW|4ImRn)Vv^~c# zbX_%c&(@G(Fs;SL&>*o8Z&LFI(d~oMJcUisBnJ8!FBxnUTB8QTFzUwfPV#gTikMML zbX8rCXd>;}L{*WAHR5=d?n7!z>mD&p)yGZ-G^V-QPHJ=1kxX4>pfdD*-|#fXFg9A% zfdSLvEh5d&ZEu$wvlp6YSi^Mhs)9+Td%9ki|>$iM6fz zMe*PwI1kVt9IK($s(jd;)OChEO(oA2D6K~sCSr?7S2@vCOp$p>Ovy_demD@0%2*bM znpF;YAV34NX*xf(Fll2s=2whK`ukXT(R_KU{^ejVy5=y3Zs_RYfJ2&+ zE3`%@rx#L%8XaajfGWYRsuK|l*i}{Aq-5XYA(3_#i|ksXVgv#-ZC|OS7Lko*%F(-k z2oWyK7`pYM|CMbZMrqAA=gV+Y)m1HoZuWc+0DMKPdiz{CZ6_BSz{fzMV@6m}&<#bz zw8#UXEUW0253z{k4o%mB(qvR8lZ`@q8g4+@x^w*!#|eWj@z5IySr%WRwh~+AxrtT< zn&9Xs2F->&zj9buOtteSgbI)#l&?q>Ekv8xRG0vex(CQ1a{8evTF9`KY{nCsJE={H zrm?2Fw}7gl8!8#@@3R>T3F1+?JbyehQ>8FAyY0D$2=Mg_%V(>F_F=Vx0=|GT60vBJ zbp$NYuB!XCBvE=$Ds>lcxu;WkNi%pH&SWx3;A%_5A}t)MzZ=hiiBU8~Ui0%hg(t;%)&MjTRI|!U6|?q9R!acMq^OwWEoe*=Zq) zACmEkpa`ZKsF+6N7XeC zrD@OCjsXyQ|Dqzmxr-o}0`+hcd?Q(g&Mu~sr=6mjfkY7OK`l{G;1mw2D%c=xXMM+E zNjj}M$VwYjZfaR_!^|jc27uXSomObqmK+_b3SE;3Eog==t$fvBgiaK3KnF*-+P?k3 zk~-8gfFY;13#o2V9YJmoWg-zn6BIaTHB_Haq9TY|I-5?zb|jnD9jO8mgjZVF20~xK zqC&f#Ey96?i3K?3Ccala`8pkwDMb`a@mhfL(dvV%nPJf|Ha1g*!gROlX9ONHov!OP zEl8q3BcI5ksR*nn*~D=Ou{Ag?b8}P6T2KTb89dolv;fdBESng}w&1wWQtjiDli66k zB`G3}0|{!H8EYOr)Q$UlY0H>|N8Vgl{ShL-TZj)LI5Dl*vLa%I_IS7wA&H`Z1s5R> z0O5NKm6N?CgH}uc9jMp}hgC+ibc0w-7jP81z=&gAQC0{I zfS#eMh;@l?z?rixpKP9-Y_85W6Xna6j7716rC<_mc6#uk4_M%tRy&o1lC_&cSdk{W z5rJN*Bw>+PeV6V4>T1Ns7=w{^00E^lKY%3{0Nnsk^*z;);IlI)o2MsD`lz*EPL$_Y zo6Tt$Q>WzE{^sz*f{a32;KZqGCywY5Lli_$wFCj?O@cs8Xt;EO!n(@vj1}guC1`d+ zFPJ`E7Zno#8CCammG!}kh1l82{>loiIP>+ba-s~Y34?vHS#`tOzoFezw2aQWSdi zs97!thu>Yk;|Il={$2s8T_^5DnMg#}RRnV|ilCU1A_%mjs;nRhG>b+@)d(MERFI%G zc?VDsbh`1OQBi0BR2@elX%7pMw6VD`drZSz!Tx@$TrMZdaF-~y`E|PTAPo;mmTsu` zoj9ULA{rDjf`CCwG|_cVzyj?+yC#C5m1HoC#pRMWfV7dOtL-=jK-eb;G)4}_m{z@o z6FW#C^#11b{6sk+$70o!lcw?z4MVlB8hXUtO$JWfX|FPo2-FF%Add*NqN*0%0c2h> za3UP*^@@K)r!?OPc~`A zeu##H$8A}zU0Fyb&n6?5>#$rPrHI}UA=FO-EqFx35)FVV5&#`KLl&Ex3IGL#X47ex zmUb5FqN3RFWkJ+T3DY9$Z&X(VI}kK29-o}7K+v&in`pxiELM3Kl!zyji+qG}VM0c5 zEmcmKD8J~kap8qo>rvYsZ9xE_#;VJ!m!=4 zd_!DmLXw6tFp;)ftJ5|u*fx2P4}%SYq$_bK}n(rLp_2(E3_oi zf(vD_AkZdoUjqunPaCu#R6Zn66-^Aj1%4rzS<+R5KHj7iQ&7a9UWK1#+k*Wd4F^Yb z!F-#zlT2=Iz;-_vCldwUcd#Is5M_di9zl1-Krrw|!mu>uY-$mnPP$d6QLU`w4#`>Bk2sS*@D=5 z*!{KOIWvl2)5po=)XIhqAy9WZLL`oP)005#00{{R3@!2*M00093P)t-sOlfg7 z7DYlyPH9PvIvPw!Op$@b_4bSoZb#G#5xVBu6rOw8Jv}j3-(Z%Jinb3k|tCx1f zlyu0{#qsUZ!qUa>(#7$VcE;1g^3ucbl5)k$w(8Ns@X*8U)W`3Zc*xJc?$pQeODk%U zalq2W?d;FNlyt*NEM>>B=<4S1?$N*3%kkF6^KVC-ntaXb(7$U#mrgQiHxW$d;qTnh z?nfqMk#5B6%e~vq`8^g=>BYH^Yr#GqQc^E)Gz&&pk6(7_}s@;HDjEJu9|qmubRfatksZvqt3YREDJonrQf4Vm7|8n z#IxS_hq+ccY;jeN`r*uNQHPd+tL@CUoPfNHajN#&zg;$m;nnnHJCCT5!i;URiD;`~ zM0JF2n15%JXG(=@PkrOr`0FHu7qcyc3g|RvC3phccYNCSUWySCsLGgw$r$-&BNH&lZJg^o3*FM zvZcLnMqeiuIa+D-@UHh#kHxKw@4{LvzV6h=Glu^V{l4K{{8&r&AzKll+?q+oo8%{VZWh) zg1n=j!)BkuiiFf|r{0Xg->sn5ez(POs*!n!uxp@rU|#Hmy~Lxzb3k~vg?{js$gX*K zwTbG)x4q1qmN!my{`BCPUXVU8o{?rq*q(V~h{^INY{{8;p+u`5eOdSAR00009a7bBm000XT000XT z0n*)m`~UzT07*naRCt{2od-}`*|w$+MmEAY;2gXA-Zy{{5<1ecbdUgnKmm|I5+DhQ zU?BkllXDgsOforVFwq8san3pCoI~60Zo6~3`Ms&BeS|~zt(ls!O?lp%QwD3%2@3@}>)@J|D_5>ubMW}lqMrWrRr9bFXNG~` zp#^6uA8)(7Ddl^Nad^?Jk535jX3d|6tLeRckZ*2oZaL7_77-B<(KcXdY3a0bSK02^ zMROJ!)&L%wKU`Tkc4<>eO3M20zyIWu;dyhRkLzWi%Dv3g&kT9cA z5QIV%SegeOEb7KBS%!zq%nX2=hp(?sS-*b$`jkyyspfoK3GrsotT`ryMJp}Lqo@>$ zot?D{X4W%CA|Wt)z#?$hsCeFTyg|XumBTMKVSwvXHod?t|Coek5Prex^dkO4Ck+XM zv$nOsHBynZWpIn5KZQiHv!fuWrTMOW-kiC!Rhug-FKwOy+_Y)a z^?j?BeI!CNGidqh!d(`D0@6EtnAzFc*^-c$q#R;}9axffB!K8tq*}Cm(O6~Wr7-}Q zvVPO`7niSJA71)#0L{#x`6fG8T124~;NIT@+S%E;`1>OeDXzghP(z{o7Va$|kj{lNfcQ3`=TdILuw5C8`tw6(JXpRt7Kk1!owK(N?a zQ&5Y83h|}N_3OVJ-uC6D^}y2~ML{!YPV5njC^?K02n3P=kwAD2XlrNqWNQofNE8=+ z7D(JcYZ7d}CS3Yr)Aiv?DVtJ2sC+rRYN63nAs(79K4KAdGD-soNw&5IIs%DAvi-dQ zMi&Gj`3qfuh9uN{rD`~3+oetGQ`V=f|MK$V%fm)bg=WxlT#-f86}}UUqNs~C$=1L} zSm$qx*pjeC_%0xcLO}e}M)X38ErE(&u{bE+HwO6DuTQz2lCu5<&d5a#9-6hP*PI{4 z4~&wduz$7MWd@L~PpY!F1zG2JfFu%Jnif=F;x|LMOS;XllVI8Uy2$9MdP&V%C%%)D zlK_GJt2@?FNCX0bin7&=j0!9o4AC*;Zj4X3rxYpH6$xPi3JdraMdh0}UB8|JwvzSh zud5aqRY5$oK)lNw@Hs_AQQ;yvMZhKy2q;w^mQVq3)o^pvPTr>H0m95Iya0wb^a2nO8VQG!a72>Mk&0;E)eq#IQW*OR6-1Qu z`s?NB#@Yttijx!HUSqynG&Z)m@^R(ZwiQOq)Ih)nizvCq$%*fD1?|xzQGq~8#Kp8K z0ueYFD*lgGyez_dw2ex(f z+SzNO5JYZK#@oDk?9z*`zWoumWVVqA%^>`|9*Zc1LP8M|;@?wB5d=C#@u{%?P?ie< zyv`S3h!l2|fJ8te8miY(1OmPjAC-5jDX@U=q@GN(LlA_52>7fsm&P7nzW)8spXRS# z0D3-S04@{nGS?u$ldwx^&pJJy4cW^7ZTAfATlnQlo6hcxX=kT0V@41PhBsBIE)J z!I}X3H8=SqhTM(VQ79BP*k&l!1okPlEy9lCJMm$8dYTJDff1_xWT^}I3n+rXn&Hc1 z+b&)H{(8zMpQ`2=p@}zx<}S*!z??`x;dQ;$RG37iQc)^etER961NrONBnrh21LRW( zgmvDksC08><=D0t-=|#vR{K#EAyQ9!CD@uR4v>HDhfU#-`KYYx9hZdx-bb`SOMCHPwB0o8dqEssE*E}vn1Z?C@ ztxs7;rI2hX{!M)@6kA&oMIi7WxqqxzD5nreD5bu{AE8hP1jM{Zx_R^FFTZ?|l9H10 z^QWpg9|~v&E$t7C0)IIQ!vi~uP@$h68->w`+BAP4-dn?V6oTF1dLhNumS9bV{dPS4 zDOOH_VH6fN10RF**tRluTjl1<--FUWCFPTEc^|mvfh1FAu0aR{7`D|ngclZe_wK1? z`}wQuH_nuSZ@ML{8L1GR@&oOXwKXZC|EH}N%7k(lg(H&csX#!K1e@;^Rc_w=CFq?t zZQAt7-&W1~APqqwbK9H>QZVda|Kry`zxsA;xUi--qU7)Z@~(b{8jB$8yE6>{t*KPD ze(L6pU&7@C7=@|!eWfl~B1bHbNXIHSKTb(W`TE7zU%vWE`(d*rGiafBS0G9u5#V*| z^iRKfb@{7TKfn5VUqi!C8Ylwa;9)fuGLU!deiHzsQc=H-V=a%ip6>CJ!!YVHG$KU6 zeh3SqfR8u7Sf8RYS+Qcpsuim~sQSl4%Vh`oFoghg*ggF7req2#!umD0*s;&ojnFX6g3|`Q@MHb=_jDwcQP(~XHz*6x~8w&LO_Ny1- z(I3A|8O{*GuRBkSPpx>m^^0yH$spkLM8P9B8}Bb-OW82Y&YG0lf?5Ja+761qNb|+F zs~1CfaCq^dfV-Up6kG7`j}%^d_3e*mL_F0#(@xRww$N-BtjZ!$tZgYISf~$wx}ot| zuODR{P%uLK{Nf`wH;1jqCL>T-lRa7O0`d$BvscP^XT~ZokIjROqhP-Qkl^24`RP|* zfA#9?Z^y(#S#ibtD=0IqXM`GFNARnTy}#jR!NiCkVcj|^>NheK?B*OC9PGSd-*g15 zshCXjhk=49%GIh5=f;+n^Zi0hz=7$hf5?=Rrh9}ifht#1<(5T@@X+#AB4fnJYd|bW2X^3o9J~DWt5@Iu`0d|b zeR7$OP}i-KtJ4Ng3Qq21F|xPpxmkC0+K;e~ibm*F2mX|l{lgQt;FgwxmlvXwvQ3MG zFiO>@QT61=necIS1wF zANV0{%Q)~bfl8$g=)QSU^}{XaU>gSqH=D*^xW(~_4SoQygQ{=awZeFl89X#+(g|$E zC`v_wb_+MgWQ`B$Ok-+2xN#M1HZ?2EQ+{&ig$8dn)AG{qZ@A&+?BHPITrklgM5%;b79oD}g!urhkcv^I#_Sn-yTb+Y% zynOlciQJ{6S+QB+eMT7n988F74x zZFK?!OR&WuHCW9=A|b+B-ymJ#>HA-7czNK!O>bxC8!rzXYI$CRvIk1ICxU|=9D-kN zs5>yRPg-6s6;FI~wsFISG@x#_WP~a>x(IK4K$xlyA!?~CSwXMM@ zo7_8q8VoRo%+Z0O4gmVq{<8JY6H`1XPXvr7l@{JT_iSfrec?9_1r8224i3SOrlcaV zNV@ajk+O7L;f<$UHYjnLcx9TXQ${@R#MYn&fl;~e_Js?$aR3Mkzr(-$IbABniA6lI zNF)-c^U9?s_DxNk^#=0U*f=;8Oh|D&91dr)XV>xD`%IqQ-|dG|HOOIUoKuT=|IuHU$n|4mN-<_?DO#8*73S zm-nnWx@)&d`h^}pl*+EimF{TUr7~WY!OATFXsh4;?62p}?-arYz>ad0!rR9S%a1?o zm*Q~J^j*95Or7?2@NxkDwXtz1cxICBv<8R6m7jRHcIBwa={p^5D3#r`V%`qRvU$df zk(sRykqgWA-n+5&z^Fd}B*Ffrxboe{kEENdG2bl}D@5X>M~*x>dp8*H*#JWC0~6tE zkD91jRVN+>J{;Zm9j-b8)yPek&Tr=D8*_Mxhvtk1UO`~LiEHN$9Xd3D5U}~ai@MMx zea+e_NoYC`Cu(h%JqcX%#MuEDSc48=4YsmOOy~jhZyp9(6kgjqrO{kz>kuuRvuD=| zV_vH;gO;8OL}57M^w|~wSk1P@ibG^{qI~VciCk9|4kvbPmp!qtxaV!)13R3JL-0hq zq`W*Xz~kt{hkyF+>b@OqS2S$h5@_j>QDYv|nL%drdIDjXz}|Z{_|TyZ6KT|HQ0K#b zwRiWGJ^V)TxqErJ2&=`bf84E9GX>_KHr#UOn7M4iah`jticV3HayK6NNWJ4 zdqn9}@bf=CJQ48g#NE^U#K0b_#`gC4PXg`+0%ay|uf2Vq$)cK4D>D4kF|28nkf; zK3|)fQS+yV_q@Fh?5#Oz*&tFZgP;XVjhJE#^Iz3*K0<)kRc}9Ex8cwi{VwY^_LmA_ z7_`0K-uIrI?yj#ny(aLU%^L?}LG0k*Wpnr0v$MCnZg>aZEibbiRb+ZBgP_?)K8D3Z z3nVL}z--I!n+NyYHXNF=Ckc~ks)cg4o_o{T+uQ5r`Sa&*dV71%@EQDj?;aooC8D>t zw|BwSJGX1J#UToC!fE8oZ`e*JKnMg9JLb-{a~E$+r1>Mlac;Grd|=0k0&g#GZ!a(G z!#e?D<8bk!je~=ix3~8V4Di~W{GfPd1^_hnPG;?L$g;LHJ8I+C`BXFXx-cQ zg1J4rA~i&po`>d5%t+|v<>lqz=JpPu&1*vNTYKxRn5jdZ9|dr^#~LRD3^?pPt*nTg z{WVkIe4WGyi%frTdQ>{`n$N+(AvoC0#>VY;gkD}=!3VZSlbNZt`#*|{=X5)PASSRY ze5|ajm>jZ9Z%3l|XQdLkU)=oZ>cn|xfam7m;1+!MKpki?e~ai~^Qi9X9VUYj(zjn_ zbU-r*zo^Fo%oWgxEdbDpNec(VeHX`6PVW7!!7VLrn0G-B5`5shyTJv)SU3ERxfR^n zJ{rkjFej%yjDBy>44N%EY7Q>)P}w=27+?;)2#j)Fipk04PhUDe@&b)HmSNlq9$me9 z_rR@3kKWPH#;xww-U~zqgTd*WmKd`Re0zw)2UAow0`M@vUO&W+6ph#i9oNTiW-cMBl`>Gu+@4v2 zNdS;Y;JQaww_oU@hY`tSGLzQO zo43fw_kV$h*EnHIkeWjVd_-C@*`q{AAraWs#X8e?8e{kNy;pD66<|paEJUy#G0KZ| zx2|r#GsMYZ1#ti%xnp#XQEPpy_g~U$9))?>o{R}d3ron!?eRkh)&%yJWGyFzk<-6- z@AmJ`2iILR46Uaiau|a{rX%Lq@G>IXlV(Mv5jmL| zoP-*Gimf$)?U+GlFhV%}54QgFVEf*y=WXf=f(?+tb&u{o*nXkED~HKs<|JzkfE7D; zO&T{!%&?<&6gZeYNCtp3VuBCTGrZKFWJ|EN9+YI#m?7N{Hf-2>=;_|=d%rt-z^$O* z;>C-%uI}AF)x`-5qLGPAP6mqx0K+PJkLJxYW;JcNjbeyly_J;}jYj08CRl}VBzb-m zTWbwk@6S!A{k6(NYbu)%OkjD#*)ws znmg~5K|qwf1%wEZ$VpCR0tLgutNlr~Bm!H_%JB5D@@Y+fgZN;SoWM)9!U$;@832%$ z>Jh?ZFhi1#_X`BwMw=?&p#`zW%|SOpSO=0Ikw&D2Bm+JMBVM=D53vPf%$>QBK0ZE? zsjcb9e%b&SADBev#ECpPRt!%vGb|baGKutPA^<$NThmr#Vx%wu0`{6?bpp&z@}yxg zoEZ(u5(cBVSXSx}PKqN+RiAtM`1p9z!j$yF`#){i`ZPDvCsGotOXXORBN>@wGLy;R z({H$$H#{e64_Vy z_kaJ7ztBCye02HsF_wAd;@C_MlS#{vFd57+`R#r`Sl)Kq#0Z5yxHVn&u7F@0PE8<& zgfPR>ci!24rwQ4)+YbTTi8?&`wS-~m-2eXfzx?Ab2|k}Ib=+plo~yUM!R6DK%n*7A zlM%wsJ2e2Ka;JVH41mp`IfW|&quvOJ0S2Wq8BALD_V4cA_+syck=;Fhh_yBBSLR^_ z!o(+pW;*=$|By=m{_h@<)zOEK*I49z*HZT+T|#3rC80JRqDGj#a!oBzI zfi^Sv<%9mb*fb$x4GT*Yppx*3)Hd}>|Nf7E{6lv1c)jVKn(6c{)7J{T>;7btk&_!l z3rjlg5mLbhmp)d8f8dU61}((xG3R4tLIe;H0D>GtJG}h~Hi-7}a(?-sr~_B+j}TzL z-KN<|g6zzCi`|klfB6fmM!ZLmeYq=3@e)Zv_O~Fz3RxzZ#lgXRm^ZQp>~PYJWM&zf@dR;TRu!@~C)v$NnAJSwn?I zCHYNuNmIKf8l4+k9+{+uCKDNg?jWmTluE6ZYu46$P`1WH%Wy}{`Bdcf3d8_|!idDq z?N9D`IXgHzJ3D(7JnL5rhr+9ca=G6r)==_}gSRD}Jw;V^F)`D5viwf_7-X`c|7L-6 z085{Y%43u2@>0%J6UAbGV|yv8pI9W@Ai@eHCl>v70T8El*G;xhB{I${9tQ2E`0#>STO`5lEj z;+eqoY&2qb8XS1rlPKQSjsvO!LI}n6%6c8kx-Le{kSz3M!0Cdc*8XRh9-WcQ9Tn0V@LdmfMr*XZ<51#bUMKYKn zRaBI%$L%5UkBi^0JHbQCV+|gL*%d%QP!rRlqa(>yL?-9L*>jD~Gt&*PM(0EO((;Cc zYX92&K8(+PqhoewPeWFWV^&^X1?F>mM|%0_O2K$*)hPui$BSVqHG(_KQ8r?b5Xs^lFNsSYDIMWq9haVs4%FkS<>n3VA&ih}Haw75K0@N}ihZB<36%N`=1u|_T)|1!MuH$_5F}DJc@0Hnz4@^?6VrOrq>YaD&6s|eZA>a_$Z~Yd%InEC1h#z)ucxEA zSyIfE&_Wnt6>PMvsC*|PSSfj*vT$Y_p@Jk6pPS18X_`q(4T{7z6f&pKgjIG>aI&yUD*uLRb= zPogn{7RDYo=Yy+m)t!pC8NiHa4A6?B;}!7_HpjKLE0uArt;()a%YKiI@50&JJK8lD zHE&F+m@aDufNHf`pC&R*9LZGfFE_Pfgk?pbZ6!QW2WsB)KAQ<1T8pJC6951V{YgYY zR3Wlop% zfXG&>)oMhWKeA&aH))y|%}mXTxDs(hB+Wz38{TIYhldt$S7Lx@Ri89y1~4-W0CJ+G z2di5XJJOmxN*cPub!lRpd$O`LF|#XFtBq5(c4_)>}fcS65^) zQhg-(70p8>89Ea!Gqnn=!PWCul)kru5(8k8sfd>_0~iD*=vG#dA-v}JxMbyca?^Bj zntNi6n5&B(Qe;)wBO8Hs1_kx%o`#W)>e8~Fk%}s2SZZpRC%rqnW4g2|+=R}+6dYN- ze8rLXyC8*!mb2CX7i+3?BAy{gh_3<3IXd2!=(xD1poFB>rYhHfxNuy}VMR1ECn?4r zF_>JgSNE1pS7bHS)@S;phGFI*-kCTwUW?<3G63K}-s**`N-TTdXA);fGa8gCAEz%` zZTee4D_V$jXp6G7DX29mp*<}r!8brxUS2L1>%vl*@q<}gwrm;9iD!oN?M!5ZW%Tt$ zd-{Zhg{7uuC}O#ZiOF1(E|o4UOK_#Fe$m`TCH%?vX+8}H*cw=)?^Mm7H~Bk2GRKrx zmEoS%(K^0gnN`)~?y5TW?CjmUcXB;HpRq6>pRm5#WF|ACZ!$H~$A?U2(1J|Dxrxb% zvEt%HTt=EebA`JUT2!*;y$($bR{%7yM!iF^Y^nA&AQmLpQJo?;XlO7gIibS0D=V{= zcfz@@@m9-$3yD@?V7P!;h-qyQlbKQ5r%NQKhSEJGDjb)an2?W?>@ViUsWpN$-U9rp zqFKB`acu0=H)l^2nwX>~Cx?f}B@gDZG*{YsRxh5t;$ZFj4TJH} zeAdb+xkjD5YVLAe7&e-w1!0LA15_%)Bqp)i{lz6p-_U)}o}E5sb20el^KbTPJ!b@D zrkd7EvF_rZrpmSXI9*)m{x06Gwv*{gp@oIF-}8O|9-56iYNXdH^`z0~^fH!ZIV@#IdBD z9_s4q8b^<1O?n)wJNL!0Cx1QH($d)SjOS?;X%!?9d1O{q=@dzzppMZ*DbBWddo|uT zcS4$|tI;MGt9XlMLC}2Fd*9JB16>nej{z<;35vwp&Y&3qt;nrE73m|Cm@(-8-0}0F8&>f5BLeK(n z28~Q4(xNkHuQMT;BayPgbpfscuC4(}-pd>J?>s-(($X>^k|gYp?+j>nPpc~Jij!or z@<5prV_9})>lgR;DMI%TZW-)MWbH7WX9gZt`@r|M>}nfdJsX1NF3ROFi9}imR`bu4 z{XtThjO88>092Huo1AuT`RVkr3$24&ilde7N_S;bhiPtRM_R266pD`QgQC+oMQ(Ct zLPGTZ_(4WjCzu<35Fj{Ysu7f6$9~IknHb@ly~K)W#Z-u9Bn$`$aHk*p<@AsHb=vH~ zgm$I7ySsaPYx4fIjvcM7iVnoy-X4w$H<6jTDwRrQyV5thcyLhi!Dmf)XyNKny$5Kz z%$BgQ;Z{b*>o~R|b3&yo86D6CxCXdO6soTMwW|2R01VGv8J(5Zl-%0du2c-!+3RDn z^o1r|rQ!G7m2ruggF&l4_!1@_T2NjFwjw+ecB&203TiccGt*yCQq#}#?`N?ipS1U${QN|^7Dn9&b1s<9sE(A}X zE;yE=+kRmF^A zPx+`KJE+}2r*u#BRVI3T_<5okgrASYF1eUNOL5VhcO@h;Ihj%wOBM&hI4)cp5Xw>w zMFYBaclY-8w1jr0GP)|QsNO@A8ed$@kdKNIl9WpKc5I3ICi*6-jB#dc1}$F!JYfdS zT_VoNVRAAuFbTnmBr+A4gs!duT1kK}R~DP#?&|99y5H43Bde<2y}c`KhgPMci!uP< zm41)7uBz7d&Q9QGWumWEY>bNwW_W1X0`QGugqb-sERYR&v}mc+1C!8Kn-~zv5(8FO z_pGL*rlx46(zm^-UO}hR4S+c()9I$VrsArkj*NC+U*E)BmdH5QdtQ4156xX-Qk#<# z0ybm}kYg(4iRdz}tE;cCt7~p}xGOOCtg0+!d%LePS27Op=yXw7d`^63xHMeGt*P%D z?=TIG3k}tBJ&g58*z4~S56zuplFuB>$cQv3$dQO*r7V_A7vSsa>MM~cfq<^=2~7z~ z-%R)9q2?hEz~`ajDoj2)moAbjLX-CJWZL?^!$TulmVu9} zFv^MhKOsW+<@3ba+8|MPhMI%uXJ+ zt8@JDj>BAofHLt%5D*w156xXZ&xD&Cl9MdevFKa{&x7T`QU>_?f=CJQ&7~_7bTf2Z z4+T$>T$~eMTwN2(>f2EhE|UQ|mP(A9XSAC=|Ac4;&0W4^wOFfSWrF1{71LQBtT<2@ z`YLlhB*}>q)(oCVB<(#JUtC-~ctphI%EH5CGMP%H5UrRuf1z<7*7%KK4+PJeKd)RB zO_#A)u~HSC?%|=;>2f6=9=gO_4;CE|s(3uDPEno3D2`{woGOpyVJ2O%Wa)xg_>cUq zVlzB=?JF)ZS{ut^v3NXeu>q6la#)kcuR{9jZMPPEI_N(ZCalv@GzVVBQk! z`KXWnwr6n8Gr1)}o6C~1WK!|#H75ahES5?n6@@akgqFwVO==6n#W-p0kg91qoXg^h z!6Ey<;Dy z$t0mT@U!F#Cj&;nf77EoLH`ntveLK(&;QiRJ`GHq9!xHu<1 zeo&_cfce~bP|QqvRCRP9 zwHTm|6%wCxC22^ORT`&EOiZS~=P>5~!0^x1tApYam2sgTX^(qrX3#Q|p{!^n*m`FyREuq}+Wg7HAN%$KaPYZa%(;d|A=p) zHrU*Sv;QYHYw_Z_;Ppj|7cX8sch=0W&6+hA{Cw7|#n{gm&&K}ifd7~5e*lOg9%2T2 R_QU`H002ovPDHLkV1i5$RJs5F literal 0 HcmV?d00001 diff --git a/preview-demo/poses/nibu/annoyed.png b/preview-demo/poses/nibu/annoyed.png new file mode 100644 index 0000000000000000000000000000000000000000..f71ce0b7531606d85aa59d3ea5029e296d9a9d9c GIT binary patch literal 12144 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 JPDHLkV1f~O*e?J8 literal 0 HcmV?d00001 diff --git a/preview-demo/poses/nibu/neutral.png b/preview-demo/poses/nibu/neutral.png new file mode 100644 index 0000000000000000000000000000000000000000..86eb51c8ed5ea32b5168169c7c109b54c15071d2 GIT binary patch literal 12175 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 RqwW9z002ovPDHLkV1lL6wfX=6 literal 0 HcmV?d00001 diff --git a/preview-demo/poses/nibu/resolute.png b/preview-demo/poses/nibu/resolute.png new file mode 100644 index 0000000000000000000000000000000000000000..8ce6377f50c63b22ee81837351a7409158e3f2cd GIT binary patch literal 12215 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 BlgR)8 literal 0 HcmV?d00001 diff --git a/preview-demo/poses/nibu/surprised.png b/preview-demo/poses/nibu/surprised.png new file mode 100644 index 0000000000000000000000000000000000000000..dc94588cfccf5d6061b10fa08fdfb3a937a58ae6 GIT binary patch literal 12242 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šT^Ad0}MDJB< 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 M07*qoM6N<$f|!q`^8f$< literal 0 HcmV?d00001 diff --git a/preview-demo/poses/void/amused.png b/preview-demo/poses/void/amused.png new file mode 100644 index 0000000000000000000000000000000000000000..c90c81c50c67b6ea29a350baf1459cbc7c89c4bf GIT binary patch literal 4795 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 literal 0 HcmV?d00001 diff --git a/preview-demo/poses/void/deadpan.png b/preview-demo/poses/void/deadpan.png new file mode 100644 index 0000000000000000000000000000000000000000..6dc8083da380a05a9109bfeeaaa194c0623da2e1 GIT binary patch literal 4353 zcmb7{E^-5eW%FN@8@Ul;osA zBqe@6f5LO0^SQ5g_c_<~;yUM!($RiKM#4k_0077kYRY>5H2)voVEljkZk_4fKiSpM ze5s=8=m`c9@^C9ax|!wYC_|3oTJkEn$=}l^O*x1rQH#;z>hC_MbY`O-~{~g5C&1$@|BLUWLb= zlS+(_T3(LRa^I7oR+jKCh`!<};eqQzUTUxsGYI(@eCtDlbD@ob5fzCONyYxXh-W?_bP@p1SWv1f>Hf5(Hq Q6Q_(r3iM$IO;`5qAFioBnSEX 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 literal 0 HcmV?d00001 diff --git a/preview-demo/poses/void/glare.png b/preview-demo/poses/void/glare.png new file mode 100644 index 0000000000000000000000000000000000000000..70860e32a48ddb75da39321c2f6a0dc9935b660a GIT binary patch literal 4581 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 literal 0 HcmV?d00001 diff --git a/preview-demo/poses/void/neutral.png b/preview-demo/poses/void/neutral.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2283a94783cd9657b1cef6790a29761bd64245 GIT binary patch literal 4190 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 literal 0 HcmV?d00001 diff --git a/preview-demo/poses/void/point.png b/preview-demo/poses/void/point.png new file mode 100644 index 0000000000000000000000000000000000000000..5bd2207a8b41d988aa212eab022cbf7b1efc2e96 GIT binary patch literal 4900 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 b8SwuARo;;VyX24q00000NkvXXu0mjflLn8q literal 0 HcmV?d00001 diff --git a/preview-demo/poses/void/surprised.png b/preview-demo/poses/void/surprised.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd340f8077bc96b7dd3f556551dc56c6d68ddee GIT binary patch literal 4908 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 !a.startsWith('--')) ?? 'low'; const file = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist', 'config.json'); const config = JSON.parse(fs.readFileSync(file, 'utf8')); config.backgroundScene = { ...config.backgroundScene, tier }; +if (demo) { + config.previewDemo = true; + config.defaultHomeserver = 0; + config.homeserverList = ['demo.invalid']; + config.allowCustomHomeservers = false; + config.featuredCommunities = { + openAsDefault: false, + spaces: ['#gamecult:demo.invalid'], + rooms: [], + servers: ['demo.invalid'], + }; +} fs.writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`); -console.log(`${file}: backgroundScene.tier = ${tier}`); +console.log(`${file}: backgroundScene.tier = ${tier}${demo ? ', previewDemo on' : ''}`); diff --git a/src/app/hooks/useClientConfig.ts b/src/app/hooks/useClientConfig.ts index 7a0586eb..6c793e65 100644 --- a/src/app/hooks/useClientConfig.ts +++ b/src/app/hooks/useClientConfig.ts @@ -21,6 +21,9 @@ export type ClientConfig = { hashRouter?: HashRouterConfig; backgroundScene?: BackgroundSceneConfig; + + // Dev preview only: the build answers a pretend homeserver in-page (src/preview-demo). + previewDemo?: boolean; }; const ClientConfigContext = createContext(null); diff --git a/src/app/pages/auth/login/Login.tsx b/src/app/pages/auth/login/Login.tsx index 2f04a733..9d890bef 100644 --- a/src/app/pages/auth/login/Login.tsx +++ b/src/app/pages/auth/login/Login.tsx @@ -36,7 +36,7 @@ const useLoginSearchParams = (searchParams: URLSearchParams): LoginPathSearchPar export function Login() { const server = useAuthServer(); - const { hashRouter } = useClientConfig(); + const { hashRouter, previewDemo } = useClientConfig(); const { loginFlows } = useAuthFlows(); const [searchParams] = useSearchParams(); const loginSearchParams = useLoginSearchParams(searchParams); @@ -65,7 +65,7 @@ export function Login() { {parsedFlows.password && ( <> diff --git a/src/index.tsx b/src/index.tsx index 4aba7ed4..2dbab4bb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -52,6 +52,16 @@ if ('serviceWorker' in navigator) { }); } +// The pretend homeserver of the dev preview: built in only with VITE_PREVIEW_DEMO=1 (the dev +// workflow) and switched on by `previewDemo: true` in config.json. Any other build drops it. +const installPreviewDemo = async () => { + if (import.meta.env.VITE_PREVIEW_DEMO !== '1') return; + const config = await fetch(`${trimTrailingSlash(import.meta.env.BASE_URL)}/config.json`) + .then((r) => r.json()) + .catch(() => ({})); + if (config.previewDemo === true) (await import('./preview-demo')).installDemo(); +}; + const mountApp = () => { const rootContainer = document.getElementById('root'); @@ -64,4 +74,4 @@ const mountApp = () => { root.render(); }; -mountApp(); +installPreviewDemo().finally(mountApp); diff --git a/src/preview-demo/fixtures.ts b/src/preview-demo/fixtures.ts new file mode 100644 index 00000000..77e6104f --- /dev/null +++ b/src/preview-demo/fixtures.ts @@ -0,0 +1,153 @@ +// Fixtures for the preview-only pretend homeserver. Public-safe by construction: the only +// names are GameCult's Minds and a fictional visitor; every room, line and id is invented here. +export const DEMO_SERVER = 'demo.invalid'; +export const DEMO_ORIGIN = `https://${DEMO_SERVER}`; +export const ME = `@demo:${DEMO_SERVER}`; +export const DEMO_DEVICE = 'DEMOPREVIEW'; +export const DEMO_TOKEN = 'demo-preview-token'; + +export type DemoEvent = { + type: string; + state_key?: string; + sender: string; + content: Record; + event_id: string; + origin_server_ts: number; + unsigned?: Record; +}; + +export type DemoUser = { id: string; name: string }; +export const USERS: DemoUser[] = [ + { id: ME, name: 'Demo' }, + { id: `@eureka:${DEMO_SERVER}`, name: 'Eureka' }, + { id: `@void:${DEMO_SERVER}`, name: 'Void' }, + { id: `@nibu:${DEMO_SERVER}`, name: 'Nibu' }, + { id: `@visitor:${DEMO_SERVER}`, name: 'Visitor' }, +]; +const user = (localpart: string) => `@${localpart}:${DEMO_SERVER}`; + +export type DemoLine = { from: string; body: string; pose?: string; minutesAgo: number }; +export type DemoRoom = { + id: string; + alias?: string; + name: string; + space?: boolean; + direct?: boolean; + topic?: string; + members: string[]; + notice?: 'show' | 'study' | 'train'; + parent?: string; + steward?: { mind: string; repo: string }; + joined: boolean; + lines: DemoLine[]; +}; + +export const ROOT = `!root:${DEMO_SERVER}`; +export const PROJECTS = `!projects:${DEMO_SERVER}`; +export const GENERAL = `!general:${DEMO_SERVER}`; +export const COMMONS = `!commons:${DEMO_SERVER}`; + +const everyone = USERS.map((u) => u.id); +export const MIND_IDS = [user('eureka'), user('void'), user('nibu')]; +const minds = MIND_IDS; + +export const ROOMS: DemoRoom[] = [ + { + id: ROOT, + alias: `#gamecult:${DEMO_SERVER}`, + name: 'GameCult', + space: true, + members: [ME], + joined: true, + lines: [], + }, + { + id: GENERAL, + alias: `#general:${DEMO_SERVER}`, + name: 'General', + topic: 'A pretend room on a pretend server.', + members: everyone, + notice: 'train', + parent: ROOT, + joined: true, + lines: [ + { from: user('visitor'), body: 'Is anyone around? I just found this place.', minutesAgo: 42 }, + { from: user('eureka'), body: 'Welcome. This is a preview with no real server behind it.', pose: 'happy', minutesAgo: 40 }, + { from: user('void'), body: 'The portrait beside a message is the pose its sender chose. Turn the setting on if you cannot see it.', pose: 'reading', minutesAgo: 38 }, + { from: user('nibu'), body: 'Check the sky behind the login screen while you are here.', pose: 'triumph', minutesAgo: 35 }, + { from: user('visitor'), body: 'Can I type here?', minutesAgo: 30 }, + { from: user('eureka'), body: 'Yes. Say something and a Mind will answer. The composer underlines misspelled words, so try "recieve".', pose: 'amused', minutesAgo: 29 }, + { from: user('void'), body: 'Nothing you type leaves this page.', pose: 'deadpan', minutesAgo: 25 }, + ], + }, + { + id: PROJECTS, + alias: `#projects:${DEMO_SERVER}`, + name: 'Projects', + space: true, + members: [ME], + parent: ROOT, + joined: true, + lines: [], + }, + { + id: `!ratatoskr:${DEMO_SERVER}`, + alias: `#ratatoskr:${DEMO_SERVER}`, + name: 'Ratatoskr', + topic: 'The chat client, built by the people using it.', + members: [ME, ...minds], + notice: 'study', + parent: PROJECTS, + steward: { mind: 'nibu', repo: 'Ratatoskr' }, + joined: true, + lines: [ + { from: user('nibu'), body: 'The ridge scene is on at the low tier in this preview.', pose: 'resolute', minutesAgo: 90 }, + { from: user('void'), body: 'Backgrounded tabs now nudge sync when you come back, so the list is not stale.', pose: 'point', minutesAgo: 80 }, + { from: user('eureka'), body: 'Messages from Minds carry a pose label; this one is thinking.', pose: 'thinking', minutesAgo: 70 }, + { from: user('nibu'), body: 'This label is not in the pose catalogue, so the portrait falls back to neutral.', pose: 'no-such-pose', minutesAgo: 60 }, + ], + }, + { + id: `!huginn:${DEMO_SERVER}`, + alias: `#huginn:${DEMO_SERVER}`, + name: 'Huginn', + topic: 'Memory for the Minds.', + members: [ME, user('eureka'), user('void')], + notice: 'show', + parent: PROJECTS, + steward: { mind: 'eureka', repo: 'Huginn' }, + joined: true, + lines: [ + { from: user('eureka'), body: 'This room shows no training line, because its notice says show.', pose: 'neutral', minutesAgo: 120 }, + ], + }, + { + id: `!dm-eureka:${DEMO_SERVER}`, + name: 'Eureka', + direct: true, + members: [ME, user('eureka')], + joined: true, + lines: [ + { from: user('eureka'), body: 'A direct message. I answer here too.', pose: 'happy', minutesAgo: 15 }, + { from: user('eureka'), body: 'Ask me anything; the answers are canned.', pose: 'thinking', minutesAgo: 14 }, + ], + }, + { + id: COMMONS, + alias: `#commons:${DEMO_SERVER}`, + name: 'Commons', + topic: 'Open to anyone. Join to look inside.', + members: [user('visitor'), user('void')], + parent: ROOT, + joined: false, + lines: [{ from: user('void'), body: 'You found the unjoined room.', pose: 'glare', minutesAgo: 200 }], + }, +]; + +// A Mind's canned answer to whatever the visitor types: label and line. +export const REPLIES: { pose: string; body: string }[] = [ + { pose: 'thinking', body: 'Hm. Let me think about that.' }, + { pose: 'amused', body: 'That is a fair thing to say to a pretend server.' }, + { pose: 'happy', body: 'Noted. Nothing was sent anywhere.' }, + { pose: 'surprised', body: 'I did not expect that.' }, +]; diff --git a/src/preview-demo/index.ts b/src/preview-demo/index.ts new file mode 100644 index 00000000..f66dde43 --- /dev/null +++ b/src/preview-demo/index.ts @@ -0,0 +1 @@ +export { installDemo } from './install'; diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts new file mode 100644 index 00000000..f543ea0e --- /dev/null +++ b/src/preview-demo/install.ts @@ -0,0 +1,115 @@ +// Routes the page's network through the pretend homeserver and refuses everything else. Installed +// only by a build with VITE_PREVIEW_DEMO=1 whose config.json says previewDemo: true (src/index.tsx). +// Own-origin requests (the app's static files) pass; the demo origin is answered in-page; any +// other origin is refused, so no real homeserver can be contacted while this is on. +import { DEMO_ORIGIN, DEMO_SERVER } from './fixtures'; +import { createDemoServer, DemoServer } from './server'; + +export type Route = 'own' | 'demo' | 'refuse'; + +export const routeOf = (url: string, pageOrigin: string): Route => { + let u: URL; + try { + u = new URL(url, pageOrigin); + } catch { + return 'refuse'; + } + if (u.protocol === 'data:' || u.protocol === 'blob:') return 'own'; + if (u.origin === DEMO_ORIGIN) return 'demo'; + const ws = u.protocol === 'ws:' || u.protocol === 'wss:'; + const sameHost = u.host === new URL(pageOrigin).host; + return sameHost && (u.origin === pageOrigin || ws) ? 'own' : 'refuse'; +}; + +const refusal = (what: string, url: string) => + new Error(`preview: pretend server only; ${what} to ${url} refused`); + +const BANNER_TEXT = `preview: pretend server (${DEMO_SERVER}). Log in as demo with any password; nothing leaves this page.`; +const FOREIGN_SESSION_TEXT = + 'preview: pretend server. A login for another server is stored on this origin and cannot be used here.'; + +const showBanner = (text: string) => { + const el = document.createElement('div'); + el.textContent = text; + el.setAttribute('role', 'status'); + el.style.cssText = + 'position:fixed;top:0;left:50%;transform:translateX(-50%);z-index:2147483647;max-width:96vw;' + + 'padding:2px 10px;font:11px/1.4 system-ui,sans-serif;color:#1b1b1b;background:#ffc46b;' + + 'border-radius:0 0 6px 6px;pointer-events:none;text-align:center'; + document.body.appendChild(el); +}; + +// The pose portraits are a setting that defaults off; a first visit turns it on so the demo shows them. +const seedSettings = () => { + if (localStorage.getItem('settings') === null) { + localStorage.setItem('settings', JSON.stringify({ poseBubbles: true })); + } +}; + +const parseBody = (body: unknown): unknown => { + if (typeof body !== 'string') return undefined; + try { + return JSON.parse(body); + } catch { + return undefined; + } +}; + +export type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise; + +// Own-origin requests go to the real fetch, the demo origin to the pretend server, the rest throw. +export const guardedFetch = + (server: DemoServer, origin: string, realFetch: FetchFn): FetchFn => + async (input, init) => { + const req = input instanceof Request ? input : undefined; + const url = req ? req.url : String(input); + const route = routeOf(url, origin); + if (route === 'own') return realFetch(input, init); + if (route === 'refuse') throw new TypeError(refusal('fetch', url).message); + const method = init?.method ?? req?.method ?? 'GET'; + const raw = init?.body ?? (req ? await req.clone().text() : undefined); + const out = await server.handle({ + method, + url, + body: parseBody(raw), + signal: init?.signal ?? req?.signal ?? undefined, + }); + return new Response(JSON.stringify(out.body), { + status: out.status, + headers: { 'Content-Type': 'application/json' }, + }); + }; + +export const installDemo = (win: Window & typeof globalThis = window) => { + const server = createDemoServer(); + const origin = win.location.origin; + const realFetch = win.fetch.bind(win); + + win.fetch = guardedFetch(server, origin, realFetch); + + const RealSocket = win.WebSocket; + class GuardedSocket extends RealSocket { + constructor(url: string | URL, protocols?: string | string[]) { + if (routeOf(String(url), origin) !== 'own') throw refusal('WebSocket', String(url)); + super(url, protocols); + } + } + win.WebSocket = GuardedSocket; + + const realOpen = win.XMLHttpRequest.prototype.open; + win.XMLHttpRequest.prototype.open = function open(this: XMLHttpRequest, method: string, url: string | URL, ...rest: unknown[]) { + if (routeOf(String(url), origin) !== 'own') throw refusal('XMLHttpRequest', String(url)); + return (realOpen as (...a: unknown[]) => void).call(this, method, url, ...rest); + } as typeof realOpen; + + const realBeacon = win.navigator.sendBeacon?.bind(win.navigator); + if (realBeacon) { + win.navigator.sendBeacon = (url: string | URL, data?: BodyInit | null) => + routeOf(String(url), origin) === 'own' ? realBeacon(url, data) : false; + } + + seedSettings(); + const stored = win.localStorage.getItem('cinny_hs_base_url'); + showBanner(stored !== null && stored !== DEMO_ORIGIN ? FOREIGN_SESSION_TEXT : BANNER_TEXT); + return server; +}; diff --git a/src/preview-demo/server.test.ts b/src/preview-demo/server.test.ts new file mode 100644 index 00000000..4e09840f --- /dev/null +++ b/src/preview-demo/server.test.ts @@ -0,0 +1,234 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { ClientEvent, MatrixClient, SyncState, createClient } from 'matrix-js-sdk'; +import { DEMO_ORIGIN, ME, ROOMS, GENERAL, COMMONS } from './fixtures'; +import { guardedFetch, installDemo, routeOf } from './install'; +import { createDemoServer, DemoServer } from './server'; +import { mindOfSender, POSE_KEY, resolvePose } from '../app/components/message/pose/poses'; +import { parseRoomNotice, roomTrainingLine } from '../app/utils/trainingNotice'; + +const PAGE = 'https://page.test'; +const never = vi.fn(async () => new Response('real network')); + +const clientFor = (server: DemoServer): MatrixClient => + createClient({ + baseUrl: DEMO_ORIGIN, + fetchFn: guardedFetch(server, PAGE, never as never) as never, + }); + +const waitFor = (read: () => T | undefined, ms = 5000): Promise => + new Promise((resolve, reject) => { + const t0 = Date.now(); + const tick = () => { + const v = read(); + if (v !== undefined) resolve(v); + else if (Date.now() - t0 > ms) reject(new Error('timed out')); + else setTimeout(tick, 20); + }; + tick(); + }); + +const live: { server: DemoServer; mx: MatrixClient }[] = []; +afterEach(() => { + live.splice(0).forEach(({ server, mx }) => { + mx.stopClient(); + server.close(); + }); + never.mockClear(); +}); + +const syncedClient = async () => { + const server = createDemoServer(); + const mx = clientFor(server); + live.push({ server, mx }); + const login = await mx.loginWithPassword('demo', 'anything'); + mx.http.opts.accessToken = login.access_token; + await mx.startClient({ initialSyncLimit: 20 }); + await waitFor(() => (mx.getSyncState() === SyncState.Syncing ? true : undefined)); + return { server, mx, login }; +}; + +describe('routeOf', () => { + it('lets own-origin requests through, answers the demo origin, refuses the rest', () => { + expect(routeOf('/config.json', PAGE)).toBe('own'); + expect(routeOf(`${PAGE}/assets/a.js`, PAGE)).toBe('own'); + expect(routeOf('data:text/plain,hi', PAGE)).toBe('own'); + expect(routeOf(`${DEMO_ORIGIN}/_matrix/client/versions`, PAGE)).toBe('demo'); + expect(routeOf('https://matrix.org/_matrix/client/versions', PAGE)).toBe('refuse'); + expect(routeOf('https://page.test.evil.example/x', PAGE)).toBe('refuse'); + expect(routeOf('https://demo.invalid.evil.example/x', PAGE)).toBe('refuse'); + expect(routeOf('http://page.test/x', PAGE)).toBe('refuse'); + expect(routeOf('wss://matrix.org/socket', PAGE)).toBe('refuse'); + expect(routeOf('wss://page.test/socket', PAGE)).toBe('own'); + }); +}); + +describe('the pretend homeserver through matrix-js-sdk', () => { + it('logs in as demo with any password', async () => { + const server = createDemoServer(); + live.push({ server, mx: clientFor(server) }); + const res = await live[0].mx.loginWithPassword('demo', 'whatever'); + expect(res.user_id).toBe(ME); + expect(res.access_token).toBeTruthy(); + }); + + it('syncs the joined rooms, space tree, direct message and members', async () => { + const { mx } = await syncedClient(); + const joined = ROOMS.filter((r) => r.joined); + expect( + mx + .getRooms() + .map((r) => r.roomId) + .sort() + ).toEqual(joined.map((r) => r.id).sort()); + expect(mx.getRoom(GENERAL)?.name).toBe('General'); + expect(mx.getRoom(COMMONS)).toBeNull(); + const direct = mx.getAccountData('m.direct')?.getContent() as Record; + expect(Object.values(direct).flat()).toContain(ROOMS.find((r) => r.direct)?.id); + expect(mx.getRoom(GENERAL)?.getJoinedMemberCount()).toBeGreaterThan(3); + }); + + it('carries pose labels that the pose catalogue resolves, for known Minds only', async () => { + const { mx } = await syncedClient(); + const events = mx + .getRoom(GENERAL)! + .getLiveTimeline() + .getEvents() + .filter((e) => e.getType() === 'm.room.message'); + const posed = events.filter((e) => e.getContent()[POSE_KEY]); + expect(posed.length).toBeGreaterThan(3); + posed.forEach((e) => { + const local = e.getSender()!.slice(1).split(':')[0]; + const mind = mindOfSender(local, undefined); + expect(mind, e.getSender()!).toBeDefined(); + expect(resolvePose(mind!, e.getContent()[POSE_KEY])).toBe(e.getContent()[POSE_KEY]); + }); + expect(events.some((e) => mindOfSender(e.getSender()!.slice(1).split(':')[0]) === undefined)).toBe(true); + }); + + it('shows the training line only in the rooms whose notice says train', async () => { + const { mx } = await syncedClient(); + const lines = ROOMS.filter((r) => r.joined && !r.space && !r.direct).map((r) => { + const ev = mx.getRoom(r.id)!.currentState.getStateEvents('org.gamecult.notice', ''); + return [r.name, roomTrainingLine(parseRoomNotice(ev?.getContent())) !== undefined]; + }); + expect(Object.fromEntries(lines)).toEqual({ General: true, Ratatoskr: false, Huginn: false }); + }); + + it('sends a message, echoes it once with its transaction id, and a Mind answers with a pose', async () => { + const { mx } = await syncedClient(); + const room = mx.getRoom(GENERAL)!; + const before = room.getLiveTimeline().getEvents().length; + const sent = await mx.sendTextMessage(GENERAL, 'hello pretend world'); + const mine = await waitFor(() => + room.getLiveTimeline().getEvents().find((e) => e.getId() === sent.event_id && !e.status) + ); + expect(mine.getSender()).toBe(ME); + const reply = await waitFor( + () => room.getLiveTimeline().getEvents().find((e) => e.getSender() !== ME && e.getContent().body?.length && room.getLiveTimeline().getEvents().indexOf(e) >= before + 1), + 6000 + ); + expect(reply.getContent()[POSE_KEY]).toBeTruthy(); + const ours = room.getLiveTimeline().getEvents().filter((e) => e.getContent().body === 'hello pretend world'); + expect(ours).toHaveLength(1); + }); + + it('joins the unjoined public room and then syncs it', async () => { + const { mx } = await syncedClient(); + const joinedRoom = new Promise((resolve) => mx.on(ClientEvent.Room, (r) => resolve(r.roomId))); + await mx.joinRoom(COMMONS); + expect(await joinedRoom).toBe(COMMONS); + }); + + it('resolves the root space alias and serves the hierarchy with steward metadata', async () => { + const { mx } = await syncedClient(); + const alias = await mx.getRoomIdForAlias('#gamecult:demo.invalid'); + expect(alias.room_id).toBeTruthy(); + const h = await mx.getRoomHierarchy(alias.room_id); + const stewards = h.rooms.flatMap((r) => r.children_state).map((c) => (c.content as Record)['org.gamecult.steward']); + expect(stewards.filter(Boolean).length).toBe(2); + }); + + it('answers a long poll with nothing new after its timeout', async () => { + const server = createDemoServer(); + live.push({ server, mx: clientFor(server) }); + const first = await server.handle({ method: 'GET', url: `${DEMO_ORIGIN}/_matrix/client/v3/sync` }); + const token = (first.body as { next_batch: string }).next_batch; + const t0 = Date.now(); + const idle = await server.handle({ method: 'GET', url: `${DEMO_ORIGIN}/_matrix/client/v3/sync?since=${token}&timeout=150` }); + expect(Date.now() - t0).toBeGreaterThanOrEqual(140); + expect((idle.body as { rooms: { join: object } }).rooms.join).toEqual({}); + }); + + it('says so, rather than inventing an answer, for an endpoint it does not implement', async () => { + const server = createDemoServer(); + live.push({ server, mx: clientFor(server) }); + const res = await server.handle({ method: 'GET', url: `${DEMO_ORIGIN}/_matrix/client/v3/something/new` }); + expect(res.status).toBe(404); + expect((res.body as { errcode: string }).errcode).toBe('M_UNRECOGNIZED'); + }); +}); + +describe('the guard', () => { + it('refuses a real homeserver and never reaches the real fetch for it', async () => { + const server = createDemoServer(); + live.push({ server, mx: clientFor(server) }); + const f = guardedFetch(server, PAGE, never as never); + await expect(f('https://matrix.org/_matrix/client/versions')).rejects.toThrow(/pretend server only/); + await expect(f(new Request('https://converser.eu/_matrix/client/v3/login'))).rejects.toThrow(/refused/); + expect(never).not.toHaveBeenCalled(); + await f(`${PAGE}/config.json`); + expect(never).toHaveBeenCalledTimes(1); + const versions = await f(`${DEMO_ORIGIN}/_matrix/client/versions`); + expect((await versions.json()).versions.length).toBeGreaterThan(0); + }); + + it('installDemo refuses WebSocket, XMLHttpRequest and beacons to foreign origins', () => { + const store = new Map(); + const open = vi.fn(); + class Sock { + constructor(public url: string) {} + } + const win = { + location: { origin: PAGE }, + fetch: never, + WebSocket: Sock, + XMLHttpRequest: { prototype: { open } }, + navigator: { sendBeacon: vi.fn(() => true) }, + localStorage: { + getItem: (k: string) => store.get(k) ?? null, + setItem: (k: string, v: string) => void store.set(k, v), + }, + } as unknown as Window & typeof globalThis; + vi.stubGlobal('document', { + createElement: () => ({ style: {}, setAttribute: () => {} }), + body: { appendChild: vi.fn() }, + }); + const server = installDemo(win); + live.push({ server, mx: clientFor(server) }); + expect(() => new win.WebSocket('wss://matrix.org/x')).toThrow(/refused/); + expect(() => new win.WebSocket(`wss://page.test/x`)).not.toThrow(); + expect(() => win.XMLHttpRequest.prototype.open('GET', 'https://matrix.org/x')).toThrow(/refused/); + win.XMLHttpRequest.prototype.open('GET', '/same-origin'); + expect(open).toHaveBeenCalledTimes(1); + expect(win.navigator.sendBeacon('https://matrix.org/x')).toBe(false); + expect(JSON.parse(store.get('settings')!)).toEqual({ poseBubbles: true }); + vi.unstubAllGlobals(); + }); + + it('keeps stored settings a visitor already chose', () => { + const store = new Map([['settings', '{"poseBubbles":false}']]); + const win = { + location: { origin: PAGE }, + fetch: never, + WebSocket: class {}, + XMLHttpRequest: { prototype: { open: vi.fn() } }, + navigator: {}, + localStorage: { getItem: (k: string) => store.get(k) ?? null, setItem: vi.fn() }, + } as unknown as Window & typeof globalThis; + vi.stubGlobal('document', { createElement: () => ({ style: {}, setAttribute: () => {} }), body: { appendChild: vi.fn() } }); + const server = installDemo(win); + live.push({ server, mx: clientFor(server) }); + expect(win.localStorage.setItem).not.toHaveBeenCalled(); + vi.unstubAllGlobals(); + }); +}); diff --git a/src/preview-demo/server.ts b/src/preview-demo/server.ts new file mode 100644 index 00000000..bb6d4d47 --- /dev/null +++ b/src/preview-demo/server.ts @@ -0,0 +1,344 @@ +// The preview-only pretend homeserver: a pure request handler over in-memory fixtures. It speaks +// just enough of the Matrix client-server API for matrix-js-sdk to log in, sync, show rooms, +// send messages and get a Mind's canned answer. It touches no network and no storage; the +// fetch guard that routes requests here lives in install.ts. +import { + COMMONS, + DEMO_DEVICE, + DEMO_ORIGIN, + DEMO_SERVER, + DEMO_TOKEN, + DemoEvent, + DemoRoom, + ME, + MIND_IDS, + REPLIES, + ROOMS, + USERS, +} from './fixtures'; + +export type DemoResponse = { status: number; body: unknown }; +export type DemoRequest = { method: string; url: string; body?: unknown; signal?: AbortSignal }; + +const POSE_KEY = 'org.gamecult.pose'; +const NOTICE_TYPE = 'org.gamecult.notice'; +const MAX_LONG_POLL_MS = 15000; + +const ok = (body: unknown = {}): DemoResponse => ({ status: 200, body }); +const err = (status: number, errcode: string, error: string): DemoResponse => ({ + status, + body: { errcode, error }, +}); + +type Stamped = { version: number; roomId: string; event: DemoEvent }; + +export type DemoServer = { + handle: (req: DemoRequest) => Promise; + /** Stops the pending Mind replies and long polls. */ + close: () => void; +}; + +export const createDemoServer = (now: () => number = Date.now): DemoServer => { + let version = 1; + let eventCounter = 0; + const joined = new Set(ROOMS.filter((r) => r.joined).map((r) => r.id)); + const timeline: Stamped[] = []; + const stateOf = new Map(); + const accountData = new Map(); + const waiters = new Set<() => void>(); + const timers = new Set>(); + let replyCounter = 0; + let closed = false; + + const mk = ( + type: string, + sender: string, + content: Record, + ts: number, + stateKey?: string + ): DemoEvent => { + eventCounter += 1; + return { + type, + sender, + content, + origin_server_ts: ts, + event_id: `$demo${eventCounter}`, + ...(stateKey === undefined ? {} : { state_key: stateKey }), + }; + }; + const nameOf = (id: string) => USERS.find((u) => u.id === id)?.name ?? id; + + const buildState = (room: DemoRoom): DemoEvent[] => { + const t0 = now() - 1000 * 60 * 60 * 24; + const creator = room.members[0] ?? ME; + const events: DemoEvent[] = [ + mk('m.room.create', creator, room.space ? { type: 'm.space', room_version: '10' } : { room_version: '10' }, t0, ''), + mk('m.room.name', creator, { name: room.name }, t0, ''), + mk('m.room.join_rules', creator, { join_rule: room.id === COMMONS ? 'public' : 'invite' }, t0, ''), + mk('m.room.history_visibility', creator, { history_visibility: 'shared' }, t0, ''), + mk('m.room.power_levels', creator, { users: { [ME]: 100 }, users_default: 0, events_default: 0, state_default: 50 }, t0, ''), + ...room.members.map((m) => + mk('m.room.member', m, { membership: 'join', displayname: nameOf(m) }, t0, m) + ), + ]; + if (room.topic) events.push(mk('m.room.topic', creator, { topic: room.topic }, t0, '')); + if (room.alias) events.push(mk('m.room.canonical_alias', creator, { alias: room.alias }, t0, '')); + if (room.notice) events.push(mk(NOTICE_TYPE, creator, { version: 1, scope: room.notice }, t0, '')); + if (room.parent) { + events.push(mk('m.space.parent', creator, { via: [DEMO_SERVER], canonical: true }, t0, room.parent)); + } + ROOMS.filter((c) => c.parent === room.id).forEach((c) => { + events.push( + mk( + 'm.space.child', + creator, + { + via: [DEMO_SERVER], + ...(c.steward ? { 'org.gamecult.steward': { ...c.steward, source: 'demo', steward: c.steward.mind } } : {}), + }, + t0, + c.id + ) + ); + }); + return events; + }; + + ROOMS.forEach((room) => { + stateOf.set(room.id, buildState(room)); + room.lines.forEach((l) => { + timeline.push({ + version: 1, + roomId: room.id, + event: mk( + 'm.room.message', + l.from, + { msgtype: 'm.text', body: l.body, ...(l.pose ? { [POSE_KEY]: l.pose } : {}) }, + now() - l.minutesAgo * 60 * 1000 + ), + }); + }); + }); + timeline.sort((a, b) => a.event.origin_server_ts - b.event.origin_server_ts); + accountData.set('m.direct', { + [`@eureka:${DEMO_SERVER}`]: ROOMS.filter((r) => r.direct).map((r) => r.id), + }); + + const notify = () => { + waiters.forEach((w) => w()); + waiters.clear(); + }; + const push = (roomId: string, event: DemoEvent) => { + version += 1; + timeline.push({ version, roomId, event }); + notify(); + }; + const later = (ms: number, fn: () => void) => { + const t = setTimeout(() => { + timers.delete(t); + if (!closed) fn(); + }, ms); + timers.add(t); + }; + + // A Mind in the room answers whatever the visitor sent, with a pose from a short cycle. + const scheduleReply = (room: DemoRoom) => { + const mind = room.members.find((m) => MIND_IDS.includes(m)); + if (!mind) return; + const reply = REPLIES[replyCounter % REPLIES.length]; + replyCounter += 1; + later(1200, () => + push(room.id, mk('m.room.message', mind, { msgtype: 'm.text', body: reply.body, [POSE_KEY]: reply.pose }, now())) + ); + }; + + const roomJson = (room: DemoRoom, since: number) => { + const full = since === 0; + const events = timeline + .filter((t) => t.roomId === room.id && (full || t.version > since)) + .map((t) => t.event); + return { + state: { events: full ? stateOf.get(room.id) : [] }, + timeline: { events, limited: false, prev_batch: 'p0' }, + ephemeral: { events: [] }, + account_data: { events: [] }, + unread_notifications: { highlight_count: 0, notification_count: 0 }, + summary: { 'm.joined_member_count': room.members.length, 'm.invited_member_count': 0, 'm.heroes': [] }, + }; + }; + + const syncBody = (since: number, joinedBefore: Set) => { + const join: Record = {}; + ROOMS.filter((r) => joined.has(r.id)).forEach((r) => { + const newlyJoined = !joinedBefore.has(r.id); + const j = roomJson(r, newlyJoined ? 0 : since); + if (since === 0 || newlyJoined || (j.timeline.events as unknown[]).length) join[r.id] = j; + }); + const ad: { type: string; content: unknown }[] = []; + if (since === 0 || accountDataVersion > since) { + accountData.forEach((content, type) => ad.push({ type, content })); + } + return { + next_batch: `d${version}`, + rooms: { join, invite: {}, leave: {} }, + account_data: { events: ad }, + presence: { events: [] }, + to_device: { events: [] }, + device_one_time_keys_count: { signed_curve25519: 50 }, + }; + }; + let accountDataVersion = 0; + let syncedJoined = new Set(joined); + + const sync = async (since: number, timeoutMs: number, signal?: AbortSignal): Promise => { + if (since === 0) { + syncedJoined = new Set(joined); + return ok(syncBody(0, new Set())); + } + const changed = () => version > since || accountDataVersion > since; + if (!changed()) { + await new Promise((resolve) => { + const done = () => { + waiters.delete(done); + resolve(); + }; + waiters.add(done); + later(Math.min(timeoutMs, MAX_LONG_POLL_MS), done); + signal?.addEventListener('abort', done); + }); + } + const before = syncedJoined; + syncedJoined = new Set(joined); + return ok(syncBody(since, before)); + }; + + const roomOf = (id: string) => ROOMS.find((r) => r.id === id); + const hierarchy = () => { + const entry = (r: DemoRoom) => ({ + room_id: r.id, + name: r.name, + topic: r.topic, + canonical_alias: r.alias, + num_joined_members: r.members.length, + world_readable: false, + guest_can_join: false, + join_rule: r.id === COMMONS ? 'public' : 'invite', + ...(r.space ? { room_type: 'm.space' } : {}), + children_state: (stateOf.get(r.id) ?? []).filter((e) => e.type === 'm.space.child'), + }); + return ROOMS.filter((r) => !r.direct).map(entry); + }; + + const handle = async (req: DemoRequest): Promise => { + const u = new URL(req.url, DEMO_ORIGIN); + const p = decodeURIComponent(u.pathname); + const m = req.method.toUpperCase(); + const body = (req.body ?? {}) as Record; + let g: RegExpMatchArray | null; + + if (p === '/.well-known/matrix/client') return ok({ 'm.homeserver': { base_url: DEMO_ORIGIN } }); + if (!p.startsWith('/_matrix/')) return err(404, 'M_NOT_FOUND', 'Not found'); + const c = p.replace(/^\/_matrix\/client\/(v\d+|r0|unstable)/, ''); + if (p.endsWith('/versions')) return ok({ versions: ['v1.1', 'v1.5', 'v1.11'], unstable_features: {} }); + if (c === '/login') { + if (m === 'GET') return ok({ flows: [{ type: 'm.login.password' }] }); + return ok({ user_id: ME, access_token: DEMO_TOKEN, device_id: DEMO_DEVICE, home_server: DEMO_SERVER }); + } + if (c === '/register') return err(403, 'M_FORBIDDEN', 'Registration is closed on the pretend server.'); + if (c === '/account/whoami') return ok({ user_id: ME, device_id: DEMO_DEVICE }); + if (c === '/capabilities') return ok({ capabilities: {} }); + if ((g = c.match(/^\/profile\/(.+)$/))) return ok({ displayname: nameOf(g[1]) }); + if (c.endsWith('/filter') && m === 'POST') return ok({ filter_id: '1' }); + if (c.includes('/filter/')) return ok({}); + if (c === '/sync') { + const raw = u.searchParams.get('since'); + const since = raw ? Number(raw.replace(/^d/, '')) || 0 : 0; + return sync(since, Number(u.searchParams.get('timeout') ?? 0), req.signal); + } + if (c === '/pushrules/' || c === '/pushrules') { + return ok({ global: { override: [], underride: [], room: [], sender: [], content: [] } }); + } + if ((g = c.match(/^\/user\/[^/]+\/account_data\/(.+)$/))) { + if (m === 'PUT') { + accountData.set(g[1], req.body ?? {}); + version += 1; + accountDataVersion = version; + notify(); + return ok({}); + } + return accountData.has(g[1]) ? ok(accountData.get(g[1])) : err(404, 'M_NOT_FOUND', 'No account data'); + } + if ((g = c.match(/^\/directory\/room\/(.+)$/))) { + const room = ROOMS.find((r) => r.alias === g![1]); + return room ? ok({ room_id: room.id, servers: [DEMO_SERVER] }) : err(404, 'M_NOT_FOUND', 'Room alias not found'); + } + if (c === '/joined_rooms') return ok({ joined_rooms: [...joined] }); + if ((g = c.match(/^\/rooms\/([^/]+)\/hierarchy$/))) return ok({ rooms: hierarchy() }); + if ((g = c.match(/^\/(?:rooms\/([^/]+)\/join|join\/([^/]+))$/)) && m === 'POST') { + const target = g[1] ?? g[2]; + const room = roomOf(target) ?? ROOMS.find((r) => r.alias === target); + if (!room) return err(404, 'M_NOT_FOUND', 'No such room'); + if (!joined.has(room.id)) { + joined.add(room.id); + if (!room.members.includes(ME)) room.members.push(ME); + stateOf.set(room.id, buildState(room)); + version += 1; + notify(); + } + return ok({ room_id: room.id }); + } + if ((g = c.match(/^\/rooms\/([^/]+)\/(?:leave|forget|read_markers|typing\/[^/]+|receipt\/.+|report\/.+)$/))) return ok({}); + if ((g = c.match(/^\/rooms\/([^/]+)\/send\/([^/]+)\/([^/]+)$/)) && m === 'PUT') { + const room = roomOf(g[1]); + if (!room || !joined.has(room.id)) return err(403, 'M_FORBIDDEN', 'Not in room'); + const event = mk(g[2], ME, body, now()); + event.unsigned = { transaction_id: g[3] }; + push(room.id, event); + if (g[2] === 'm.room.message') scheduleReply(room); + return ok({ event_id: event.event_id }); + } + if ((g = c.match(/^\/rooms\/([^/]+)\/state\/([^/]+)(?:\/(.*))?$/))) { + const room = roomOf(g[1]); + if (!room) return err(404, 'M_NOT_FOUND', 'No such room'); + const key = g[3] ?? ''; + if (m === 'PUT') { + const event = mk(g[2], ME, body, now(), key); + stateOf.set(room.id, [...(stateOf.get(room.id) ?? []).filter((e) => !(e.type === g![2] && e.state_key === key)), event]); + push(room.id, event); + return ok({ event_id: event.event_id }); + } + const found = (stateOf.get(room.id) ?? []).find((e) => e.type === g![2] && e.state_key === key); + return found ? ok(found.content) : err(404, 'M_NOT_FOUND', 'No such state event'); + } + if ((g = c.match(/^\/rooms\/([^/]+)\/state$/))) return ok(stateOf.get(g[1]) ?? []); + if ((g = c.match(/^\/rooms\/([^/]+)\/members$/))) { + return ok({ chunk: (stateOf.get(g[1]) ?? []).filter((e) => e.type === 'm.room.member') }); + } + if ((g = c.match(/^\/rooms\/([^/]+)\/messages$/))) return ok({ chunk: [], start: 'p0', end: 'p0' }); + if ((g = c.match(/^\/rooms\/([^/]+)\/(?:summary|context\/.+)$/))) return err(404, 'M_NOT_FOUND', 'Not available on the pretend server'); + if (c === '/keys/upload') return ok({ one_time_key_counts: { signed_curve25519: 50 } }); + if (c === '/keys/query') return ok({ device_keys: {}, failures: {} }); + if (c === '/keys/claim') return ok({ one_time_keys: {}, failures: {} }); + if (c === '/keys/changes') return ok({ changed: [], left: [] }); + if (c.startsWith('/keys/')) return ok({}); + if (c.startsWith('/sendToDevice/')) return ok({}); + if (c.startsWith('/room_keys/')) return err(404, 'M_NOT_FOUND', 'No backup'); + if (c === '/devices') return ok({ devices: [{ device_id: DEMO_DEVICE, display_name: 'Preview' }] }); + if (c === '/thirdparty/protocols' || c === '/voip/turnServer') return ok({}); + if (c.startsWith('/presence/')) return ok({}); + if (c === '/user_directory/search') return ok({ results: [], limited: false }); + if (p.endsWith('/media/config') || p.endsWith('/config')) return ok({ 'm.upload.size': 0 }); + return err(404, 'M_UNRECOGNIZED', `The pretend server does not implement ${m} ${p}`); + }; + + return { + handle, + close: () => { + closed = true; + timers.forEach(clearTimeout); + timers.clear(); + notify(); + }, + }; +}; -- 2.47.3 From 4d09a33ec22c286283be17daa47a7c884fbce1b4 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:50:15 +0000 Subject: [PATCH 2/9] preview-demo: seed settings through the installed window's storage Co-Authored-By: Claude Sonnet 5.5 --- src/preview-demo/install.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index f543ea0e..7ab84aa0 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -40,9 +40,9 @@ const showBanner = (text: string) => { }; // The pose portraits are a setting that defaults off; a first visit turns it on so the demo shows them. -const seedSettings = () => { - if (localStorage.getItem('settings') === null) { - localStorage.setItem('settings', JSON.stringify({ poseBubbles: true })); +const seedSettings = (storage: Storage) => { + if (storage.getItem('settings') === null) { + storage.setItem('settings', JSON.stringify({ poseBubbles: true })); } }; @@ -108,7 +108,7 @@ export const installDemo = (win: Window & typeof globalThis = window) => { routeOf(String(url), origin) === 'own' ? realBeacon(url, data) : false; } - seedSettings(); + seedSettings(win.localStorage); const stored = win.localStorage.getItem('cinny_hs_base_url'); showBanner(stored !== null && stored !== DEMO_ORIGIN ? FOREIGN_SESSION_TEXT : BANNER_TEXT); return server; -- 2.47.3 From 8431827077c39ba6a622b835575d60c6f16080b9 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:50:52 +0000 Subject: [PATCH 3/9] preview-demo: lint-clean router, prettier Co-Authored-By: Claude Sonnet 5.5 --- src/preview-demo/fixtures.ts | 88 +++++++++++++++++--- src/preview-demo/install.ts | 7 +- src/preview-demo/server.test.ts | 67 +++++++++++---- src/preview-demo/server.ts | 140 ++++++++++++++++++++++++-------- 4 files changed, 241 insertions(+), 61 deletions(-) diff --git a/src/preview-demo/fixtures.ts b/src/preview-demo/fixtures.ts index 77e6104f..1ed52dc1 100644 --- a/src/preview-demo/fixtures.ts +++ b/src/preview-demo/fixtures.ts @@ -72,12 +72,37 @@ export const ROOMS: DemoRoom[] = [ joined: true, lines: [ { from: user('visitor'), body: 'Is anyone around? I just found this place.', minutesAgo: 42 }, - { from: user('eureka'), body: 'Welcome. This is a preview with no real server behind it.', pose: 'happy', minutesAgo: 40 }, - { from: user('void'), body: 'The portrait beside a message is the pose its sender chose. Turn the setting on if you cannot see it.', pose: 'reading', minutesAgo: 38 }, - { from: user('nibu'), body: 'Check the sky behind the login screen while you are here.', pose: 'triumph', minutesAgo: 35 }, + { + from: user('eureka'), + body: 'Welcome. This is a preview with no real server behind it.', + pose: 'happy', + minutesAgo: 40, + }, + { + from: user('void'), + body: 'The portrait beside a message is the pose its sender chose. Turn the setting on if you cannot see it.', + pose: 'reading', + minutesAgo: 38, + }, + { + from: user('nibu'), + body: 'Check the sky behind the login screen while you are here.', + pose: 'triumph', + minutesAgo: 35, + }, { from: user('visitor'), body: 'Can I type here?', minutesAgo: 30 }, - { from: user('eureka'), body: 'Yes. Say something and a Mind will answer. The composer underlines misspelled words, so try "recieve".', pose: 'amused', minutesAgo: 29 }, - { from: user('void'), body: 'Nothing you type leaves this page.', pose: 'deadpan', minutesAgo: 25 }, + { + from: user('eureka'), + body: 'Yes. Say something and a Mind will answer. The composer underlines misspelled words, so try "recieve".', + pose: 'amused', + minutesAgo: 29, + }, + { + from: user('void'), + body: 'Nothing you type leaves this page.', + pose: 'deadpan', + minutesAgo: 25, + }, ], }, { @@ -101,10 +126,30 @@ export const ROOMS: DemoRoom[] = [ steward: { mind: 'nibu', repo: 'Ratatoskr' }, joined: true, lines: [ - { from: user('nibu'), body: 'The ridge scene is on at the low tier in this preview.', pose: 'resolute', minutesAgo: 90 }, - { from: user('void'), body: 'Backgrounded tabs now nudge sync when you come back, so the list is not stale.', pose: 'point', minutesAgo: 80 }, - { from: user('eureka'), body: 'Messages from Minds carry a pose label; this one is thinking.', pose: 'thinking', minutesAgo: 70 }, - { from: user('nibu'), body: 'This label is not in the pose catalogue, so the portrait falls back to neutral.', pose: 'no-such-pose', minutesAgo: 60 }, + { + from: user('nibu'), + body: 'The ridge scene is on at the low tier in this preview.', + pose: 'resolute', + minutesAgo: 90, + }, + { + from: user('void'), + body: 'Backgrounded tabs now nudge sync when you come back, so the list is not stale.', + pose: 'point', + minutesAgo: 80, + }, + { + from: user('eureka'), + body: 'Messages from Minds carry a pose label; this one is thinking.', + pose: 'thinking', + minutesAgo: 70, + }, + { + from: user('nibu'), + body: 'This label is not in the pose catalogue, so the portrait falls back to neutral.', + pose: 'no-such-pose', + minutesAgo: 60, + }, ], }, { @@ -118,7 +163,12 @@ export const ROOMS: DemoRoom[] = [ steward: { mind: 'eureka', repo: 'Huginn' }, joined: true, lines: [ - { from: user('eureka'), body: 'This room shows no training line, because its notice says show.', pose: 'neutral', minutesAgo: 120 }, + { + from: user('eureka'), + body: 'This room shows no training line, because its notice says show.', + pose: 'neutral', + minutesAgo: 120, + }, ], }, { @@ -128,8 +178,18 @@ export const ROOMS: DemoRoom[] = [ members: [ME, user('eureka')], joined: true, lines: [ - { from: user('eureka'), body: 'A direct message. I answer here too.', pose: 'happy', minutesAgo: 15 }, - { from: user('eureka'), body: 'Ask me anything; the answers are canned.', pose: 'thinking', minutesAgo: 14 }, + { + from: user('eureka'), + body: 'A direct message. I answer here too.', + pose: 'happy', + minutesAgo: 15, + }, + { + from: user('eureka'), + body: 'Ask me anything; the answers are canned.', + pose: 'thinking', + minutesAgo: 14, + }, ], }, { @@ -140,7 +200,9 @@ export const ROOMS: DemoRoom[] = [ members: [user('visitor'), user('void')], parent: ROOT, joined: false, - lines: [{ from: user('void'), body: 'You found the unjoined room.', pose: 'glare', minutesAgo: 200 }], + lines: [ + { from: user('void'), body: 'You found the unjoined room.', pose: 'glare', minutesAgo: 200 }, + ], }, ]; diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index 7ab84aa0..426c2d21 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -97,7 +97,12 @@ export const installDemo = (win: Window & typeof globalThis = window) => { win.WebSocket = GuardedSocket; const realOpen = win.XMLHttpRequest.prototype.open; - win.XMLHttpRequest.prototype.open = function open(this: XMLHttpRequest, method: string, url: string | URL, ...rest: unknown[]) { + win.XMLHttpRequest.prototype.open = function open( + this: XMLHttpRequest, + method: string, + url: string | URL, + ...rest: unknown[] + ) { if (routeOf(String(url), origin) !== 'own') throw refusal('XMLHttpRequest', String(url)); return (realOpen as (...a: unknown[]) => void).call(this, method, url, ...rest); } as typeof realOpen; diff --git a/src/preview-demo/server.test.ts b/src/preview-demo/server.test.ts index 4e09840f..7140a76f 100644 --- a/src/preview-demo/server.test.ts +++ b/src/preview-demo/server.test.ts @@ -102,7 +102,9 @@ describe('the pretend homeserver through matrix-js-sdk', () => { expect(mind, e.getSender()!).toBeDefined(); expect(resolvePose(mind!, e.getContent()[POSE_KEY])).toBe(e.getContent()[POSE_KEY]); }); - expect(events.some((e) => mindOfSender(e.getSender()!.slice(1).split(':')[0]) === undefined)).toBe(true); + expect( + events.some((e) => mindOfSender(e.getSender()!.slice(1).split(':')[0]) === undefined) + ).toBe(true); }); it('shows the training line only in the rooms whose notice says train', async () => { @@ -120,21 +122,38 @@ describe('the pretend homeserver through matrix-js-sdk', () => { const before = room.getLiveTimeline().getEvents().length; const sent = await mx.sendTextMessage(GENERAL, 'hello pretend world'); const mine = await waitFor(() => - room.getLiveTimeline().getEvents().find((e) => e.getId() === sent.event_id && !e.status) + room + .getLiveTimeline() + .getEvents() + .find((e) => e.getId() === sent.event_id && !e.status) ); expect(mine.getSender()).toBe(ME); const reply = await waitFor( - () => room.getLiveTimeline().getEvents().find((e) => e.getSender() !== ME && e.getContent().body?.length && room.getLiveTimeline().getEvents().indexOf(e) >= before + 1), + () => + room + .getLiveTimeline() + .getEvents() + .find( + (e) => + e.getSender() !== ME && + e.getContent().body?.length && + room.getLiveTimeline().getEvents().indexOf(e) >= before + 1 + ), 6000 ); expect(reply.getContent()[POSE_KEY]).toBeTruthy(); - const ours = room.getLiveTimeline().getEvents().filter((e) => e.getContent().body === 'hello pretend world'); + const ours = room + .getLiveTimeline() + .getEvents() + .filter((e) => e.getContent().body === 'hello pretend world'); expect(ours).toHaveLength(1); }); it('joins the unjoined public room and then syncs it', async () => { const { mx } = await syncedClient(); - const joinedRoom = new Promise((resolve) => mx.on(ClientEvent.Room, (r) => resolve(r.roomId))); + const joinedRoom = new Promise((resolve) => + mx.on(ClientEvent.Room, (r) => resolve(r.roomId)) + ); await mx.joinRoom(COMMONS); expect(await joinedRoom).toBe(COMMONS); }); @@ -144,17 +163,25 @@ describe('the pretend homeserver through matrix-js-sdk', () => { const alias = await mx.getRoomIdForAlias('#gamecult:demo.invalid'); expect(alias.room_id).toBeTruthy(); const h = await mx.getRoomHierarchy(alias.room_id); - const stewards = h.rooms.flatMap((r) => r.children_state).map((c) => (c.content as Record)['org.gamecult.steward']); + const stewards = h.rooms + .flatMap((r) => r.children_state) + .map((c) => (c.content as Record)['org.gamecult.steward']); expect(stewards.filter(Boolean).length).toBe(2); }); it('answers a long poll with nothing new after its timeout', async () => { const server = createDemoServer(); live.push({ server, mx: clientFor(server) }); - const first = await server.handle({ method: 'GET', url: `${DEMO_ORIGIN}/_matrix/client/v3/sync` }); + const first = await server.handle({ + method: 'GET', + url: `${DEMO_ORIGIN}/_matrix/client/v3/sync`, + }); const token = (first.body as { next_batch: string }).next_batch; const t0 = Date.now(); - const idle = await server.handle({ method: 'GET', url: `${DEMO_ORIGIN}/_matrix/client/v3/sync?since=${token}&timeout=150` }); + const idle = await server.handle({ + method: 'GET', + url: `${DEMO_ORIGIN}/_matrix/client/v3/sync?since=${token}&timeout=150`, + }); expect(Date.now() - t0).toBeGreaterThanOrEqual(140); expect((idle.body as { rooms: { join: object } }).rooms.join).toEqual({}); }); @@ -162,7 +189,10 @@ describe('the pretend homeserver through matrix-js-sdk', () => { it('says so, rather than inventing an answer, for an endpoint it does not implement', async () => { const server = createDemoServer(); live.push({ server, mx: clientFor(server) }); - const res = await server.handle({ method: 'GET', url: `${DEMO_ORIGIN}/_matrix/client/v3/something/new` }); + const res = await server.handle({ + method: 'GET', + url: `${DEMO_ORIGIN}/_matrix/client/v3/something/new`, + }); expect(res.status).toBe(404); expect((res.body as { errcode: string }).errcode).toBe('M_UNRECOGNIZED'); }); @@ -173,8 +203,12 @@ describe('the guard', () => { const server = createDemoServer(); live.push({ server, mx: clientFor(server) }); const f = guardedFetch(server, PAGE, never as never); - await expect(f('https://matrix.org/_matrix/client/versions')).rejects.toThrow(/pretend server only/); - await expect(f(new Request('https://converser.eu/_matrix/client/v3/login'))).rejects.toThrow(/refused/); + await expect(f('https://matrix.org/_matrix/client/versions')).rejects.toThrow( + /pretend server only/ + ); + await expect(f(new Request('https://converser.eu/_matrix/client/v3/login'))).rejects.toThrow( + /refused/ + ); expect(never).not.toHaveBeenCalled(); await f(`${PAGE}/config.json`); expect(never).toHaveBeenCalledTimes(1); @@ -200,14 +234,16 @@ describe('the guard', () => { }, } as unknown as Window & typeof globalThis; vi.stubGlobal('document', { - createElement: () => ({ style: {}, setAttribute: () => {} }), + createElement: () => ({ style: {}, setAttribute: vi.fn() }), body: { appendChild: vi.fn() }, }); const server = installDemo(win); live.push({ server, mx: clientFor(server) }); expect(() => new win.WebSocket('wss://matrix.org/x')).toThrow(/refused/); expect(() => new win.WebSocket(`wss://page.test/x`)).not.toThrow(); - expect(() => win.XMLHttpRequest.prototype.open('GET', 'https://matrix.org/x')).toThrow(/refused/); + expect(() => win.XMLHttpRequest.prototype.open('GET', 'https://matrix.org/x')).toThrow( + /refused/ + ); win.XMLHttpRequest.prototype.open('GET', '/same-origin'); expect(open).toHaveBeenCalledTimes(1); expect(win.navigator.sendBeacon('https://matrix.org/x')).toBe(false); @@ -225,7 +261,10 @@ describe('the guard', () => { navigator: {}, localStorage: { getItem: (k: string) => store.get(k) ?? null, setItem: vi.fn() }, } as unknown as Window & typeof globalThis; - vi.stubGlobal('document', { createElement: () => ({ style: {}, setAttribute: () => {} }), body: { appendChild: vi.fn() } }); + vi.stubGlobal('document', { + createElement: () => ({ style: {}, setAttribute: vi.fn() }), + body: { appendChild: vi.fn() }, + }); const server = installDemo(win); live.push({ server, mx: clientFor(server) }); expect(win.localStorage.setItem).not.toHaveBeenCalled(); diff --git a/src/preview-demo/server.ts b/src/preview-demo/server.ts index bb6d4d47..272807ea 100644 --- a/src/preview-demo/server.ts +++ b/src/preview-demo/server.ts @@ -48,6 +48,8 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { const waiters = new Set<() => void>(); const timers = new Set>(); let replyCounter = 0; + let accountDataVersion = 0; + let syncedJoined = new Set(joined); let closed = false; const mk = ( @@ -73,20 +75,42 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { const t0 = now() - 1000 * 60 * 60 * 24; const creator = room.members[0] ?? ME; const events: DemoEvent[] = [ - mk('m.room.create', creator, room.space ? { type: 'm.space', room_version: '10' } : { room_version: '10' }, t0, ''), + mk( + 'm.room.create', + creator, + room.space ? { type: 'm.space', room_version: '10' } : { room_version: '10' }, + t0, + '' + ), mk('m.room.name', creator, { name: room.name }, t0, ''), - mk('m.room.join_rules', creator, { join_rule: room.id === COMMONS ? 'public' : 'invite' }, t0, ''), + mk( + 'm.room.join_rules', + creator, + { join_rule: room.id === COMMONS ? 'public' : 'invite' }, + t0, + '' + ), mk('m.room.history_visibility', creator, { history_visibility: 'shared' }, t0, ''), - mk('m.room.power_levels', creator, { users: { [ME]: 100 }, users_default: 0, events_default: 0, state_default: 50 }, t0, ''), + mk( + 'm.room.power_levels', + creator, + { users: { [ME]: 100 }, users_default: 0, events_default: 0, state_default: 50 }, + t0, + '' + ), ...room.members.map((m) => mk('m.room.member', m, { membership: 'join', displayname: nameOf(m) }, t0, m) ), ]; if (room.topic) events.push(mk('m.room.topic', creator, { topic: room.topic }, t0, '')); - if (room.alias) events.push(mk('m.room.canonical_alias', creator, { alias: room.alias }, t0, '')); - if (room.notice) events.push(mk(NOTICE_TYPE, creator, { version: 1, scope: room.notice }, t0, '')); + if (room.alias) + events.push(mk('m.room.canonical_alias', creator, { alias: room.alias }, t0, '')); + if (room.notice) + events.push(mk(NOTICE_TYPE, creator, { version: 1, scope: room.notice }, t0, '')); if (room.parent) { - events.push(mk('m.space.parent', creator, { via: [DEMO_SERVER], canonical: true }, t0, room.parent)); + events.push( + mk('m.space.parent', creator, { via: [DEMO_SERVER], canonical: true }, t0, room.parent) + ); } ROOMS.filter((c) => c.parent === room.id).forEach((c) => { events.push( @@ -95,7 +119,11 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { creator, { via: [DEMO_SERVER], - ...(c.steward ? { 'org.gamecult.steward': { ...c.steward, source: 'demo', steward: c.steward.mind } } : {}), + ...(c.steward + ? { + 'org.gamecult.steward': { ...c.steward, source: 'demo', steward: c.steward.mind }, + } + : {}), }, t0, c.id @@ -149,7 +177,15 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { const reply = REPLIES[replyCounter % REPLIES.length]; replyCounter += 1; later(1200, () => - push(room.id, mk('m.room.message', mind, { msgtype: 'm.text', body: reply.body, [POSE_KEY]: reply.pose }, now())) + push( + room.id, + mk( + 'm.room.message', + mind, + { msgtype: 'm.text', body: reply.body, [POSE_KEY]: reply.pose }, + now() + ) + ) ); }; @@ -164,7 +200,11 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { ephemeral: { events: [] }, account_data: { events: [] }, unread_notifications: { highlight_count: 0, notification_count: 0 }, - summary: { 'm.joined_member_count': room.members.length, 'm.invited_member_count': 0, 'm.heroes': [] }, + summary: { + 'm.joined_member_count': room.members.length, + 'm.invited_member_count': 0, + 'm.heroes': [], + }, }; }; @@ -188,10 +228,12 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { device_one_time_keys_count: { signed_curve25519: 50 }, }; }; - let accountDataVersion = 0; - let syncedJoined = new Set(joined); - const sync = async (since: number, timeoutMs: number, signal?: AbortSignal): Promise => { + const sync = async ( + since: number, + timeoutMs: number, + signal?: AbortSignal + ): Promise => { if (since === 0) { syncedJoined = new Set(joined); return ok(syncBody(0, new Set())); @@ -237,18 +279,27 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { const body = (req.body ?? {}) as Record; let g: RegExpMatchArray | null; - if (p === '/.well-known/matrix/client') return ok({ 'm.homeserver': { base_url: DEMO_ORIGIN } }); + if (p === '/.well-known/matrix/client') + return ok({ 'm.homeserver': { base_url: DEMO_ORIGIN } }); if (!p.startsWith('/_matrix/')) return err(404, 'M_NOT_FOUND', 'Not found'); const c = p.replace(/^\/_matrix\/client\/(v\d+|r0|unstable)/, ''); - if (p.endsWith('/versions')) return ok({ versions: ['v1.1', 'v1.5', 'v1.11'], unstable_features: {} }); + if (p.endsWith('/versions')) + return ok({ versions: ['v1.1', 'v1.5', 'v1.11'], unstable_features: {} }); if (c === '/login') { if (m === 'GET') return ok({ flows: [{ type: 'm.login.password' }] }); - return ok({ user_id: ME, access_token: DEMO_TOKEN, device_id: DEMO_DEVICE, home_server: DEMO_SERVER }); + return ok({ + user_id: ME, + access_token: DEMO_TOKEN, + device_id: DEMO_DEVICE, + home_server: DEMO_SERVER, + }); } - if (c === '/register') return err(403, 'M_FORBIDDEN', 'Registration is closed on the pretend server.'); + if (c === '/register') + return err(403, 'M_FORBIDDEN', 'Registration is closed on the pretend server.'); if (c === '/account/whoami') return ok({ user_id: ME, device_id: DEMO_DEVICE }); if (c === '/capabilities') return ok({ capabilities: {} }); - if ((g = c.match(/^\/profile\/(.+)$/))) return ok({ displayname: nameOf(g[1]) }); + g = c.match(/^\/profile\/(.+)$/); + if (g) return ok({ displayname: nameOf(g[1]) }); if (c.endsWith('/filter') && m === 'POST') return ok({ filter_id: '1' }); if (c.includes('/filter/')) return ok({}); if (c === '/sync') { @@ -259,7 +310,8 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { if (c === '/pushrules/' || c === '/pushrules') { return ok({ global: { override: [], underride: [], room: [], sender: [], content: [] } }); } - if ((g = c.match(/^\/user\/[^/]+\/account_data\/(.+)$/))) { + g = c.match(/^\/user\/[^/]+\/account_data\/(.+)$/); + if (g) { if (m === 'PUT') { accountData.set(g[1], req.body ?? {}); version += 1; @@ -267,15 +319,22 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { notify(); return ok({}); } - return accountData.has(g[1]) ? ok(accountData.get(g[1])) : err(404, 'M_NOT_FOUND', 'No account data'); + return accountData.has(g[1]) + ? ok(accountData.get(g[1])) + : err(404, 'M_NOT_FOUND', 'No account data'); } - if ((g = c.match(/^\/directory\/room\/(.+)$/))) { + g = c.match(/^\/directory\/room\/(.+)$/); + if (g) { const room = ROOMS.find((r) => r.alias === g![1]); - return room ? ok({ room_id: room.id, servers: [DEMO_SERVER] }) : err(404, 'M_NOT_FOUND', 'Room alias not found'); + return room + ? ok({ room_id: room.id, servers: [DEMO_SERVER] }) + : err(404, 'M_NOT_FOUND', 'Room alias not found'); } if (c === '/joined_rooms') return ok({ joined_rooms: [...joined] }); - if ((g = c.match(/^\/rooms\/([^/]+)\/hierarchy$/))) return ok({ rooms: hierarchy() }); - if ((g = c.match(/^\/(?:rooms\/([^/]+)\/join|join\/([^/]+))$/)) && m === 'POST') { + g = c.match(/^\/rooms\/([^/]+)\/hierarchy$/); + if (g) return ok({ rooms: hierarchy() }); + g = c.match(/^\/(?:rooms\/([^/]+)\/join|join\/([^/]+))$/); + if (g && m === 'POST') { const target = g[1] ?? g[2]; const room = roomOf(target) ?? ROOMS.find((r) => r.alias === target); if (!room) return err(404, 'M_NOT_FOUND', 'No such room'); @@ -288,8 +347,12 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { } return ok({ room_id: room.id }); } - if ((g = c.match(/^\/rooms\/([^/]+)\/(?:leave|forget|read_markers|typing\/[^/]+|receipt\/.+|report\/.+)$/))) return ok({}); - if ((g = c.match(/^\/rooms\/([^/]+)\/send\/([^/]+)\/([^/]+)$/)) && m === 'PUT') { + g = c.match( + /^\/rooms\/([^/]+)\/(?:leave|forget|read_markers|typing\/[^/]+|receipt\/.+|report\/.+)$/ + ); + if (g) return ok({}); + g = c.match(/^\/rooms\/([^/]+)\/send\/([^/]+)\/([^/]+)$/); + if (g && m === 'PUT') { const room = roomOf(g[1]); if (!room || !joined.has(room.id)) return err(403, 'M_FORBIDDEN', 'Not in room'); const event = mk(g[2], ME, body, now()); @@ -298,25 +361,35 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { if (g[2] === 'm.room.message') scheduleReply(room); return ok({ event_id: event.event_id }); } - if ((g = c.match(/^\/rooms\/([^/]+)\/state\/([^/]+)(?:\/(.*))?$/))) { + g = c.match(/^\/rooms\/([^/]+)\/state\/([^/]+)(?:\/(.*))?$/); + if (g) { const room = roomOf(g[1]); if (!room) return err(404, 'M_NOT_FOUND', 'No such room'); const key = g[3] ?? ''; if (m === 'PUT') { const event = mk(g[2], ME, body, now(), key); - stateOf.set(room.id, [...(stateOf.get(room.id) ?? []).filter((e) => !(e.type === g![2] && e.state_key === key)), event]); + stateOf.set(room.id, [ + ...(stateOf.get(room.id) ?? []).filter((e) => !(e.type === g![2] && e.state_key === key)), + event, + ]); push(room.id, event); return ok({ event_id: event.event_id }); } - const found = (stateOf.get(room.id) ?? []).find((e) => e.type === g![2] && e.state_key === key); + const found = (stateOf.get(room.id) ?? []).find( + (e) => e.type === g![2] && e.state_key === key + ); return found ? ok(found.content) : err(404, 'M_NOT_FOUND', 'No such state event'); } - if ((g = c.match(/^\/rooms\/([^/]+)\/state$/))) return ok(stateOf.get(g[1]) ?? []); - if ((g = c.match(/^\/rooms\/([^/]+)\/members$/))) { + g = c.match(/^\/rooms\/([^/]+)\/state$/); + if (g) return ok(stateOf.get(g[1]) ?? []); + g = c.match(/^\/rooms\/([^/]+)\/members$/); + if (g) { return ok({ chunk: (stateOf.get(g[1]) ?? []).filter((e) => e.type === 'm.room.member') }); } - if ((g = c.match(/^\/rooms\/([^/]+)\/messages$/))) return ok({ chunk: [], start: 'p0', end: 'p0' }); - if ((g = c.match(/^\/rooms\/([^/]+)\/(?:summary|context\/.+)$/))) return err(404, 'M_NOT_FOUND', 'Not available on the pretend server'); + g = c.match(/^\/rooms\/([^/]+)\/messages$/); + if (g) return ok({ chunk: [], start: 'p0', end: 'p0' }); + g = c.match(/^\/rooms\/([^/]+)\/(?:summary|context\/.+)$/); + if (g) return err(404, 'M_NOT_FOUND', 'Not available on the pretend server'); if (c === '/keys/upload') return ok({ one_time_key_counts: { signed_curve25519: 50 } }); if (c === '/keys/query') return ok({ device_keys: {}, failures: {} }); if (c === '/keys/claim') return ok({ one_time_keys: {}, failures: {} }); @@ -324,7 +397,8 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { if (c.startsWith('/keys/')) return ok({}); if (c.startsWith('/sendToDevice/')) return ok({}); if (c.startsWith('/room_keys/')) return err(404, 'M_NOT_FOUND', 'No backup'); - if (c === '/devices') return ok({ devices: [{ device_id: DEMO_DEVICE, display_name: 'Preview' }] }); + if (c === '/devices') + return ok({ devices: [{ device_id: DEMO_DEVICE, display_name: 'Preview' }] }); if (c === '/thirdparty/protocols' || c === '/voip/turnServer') return ok({}); if (c.startsWith('/presence/')) return ok({}); if (c === '/user_directory/search') return ok({ results: [], limited: false }); -- 2.47.3 From 0f1fc066e412c5af6261002b6bf3714cea487332 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:51:07 +0000 Subject: [PATCH 4/9] preview-demo: lint Co-Authored-By: Claude Sonnet 5.5 --- src/preview-demo/install.ts | 4 +++- src/preview-demo/server.test.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index 426c2d21..a5677e8f 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -1,3 +1,5 @@ +/* eslint-disable no-param-reassign */ +// Patching the window's network entry points is this module's whole job. // Routes the page's network through the pretend homeserver and refuses everything else. Installed // only by a build with VITE_PREVIEW_DEMO=1 whose config.json says previewDemo: true (src/index.tsx). // Own-origin requests (the app's static files) pass; the demo origin is answered in-page; any @@ -87,7 +89,7 @@ export const installDemo = (win: Window & typeof globalThis = window) => { win.fetch = guardedFetch(server, origin, realFetch); - const RealSocket = win.WebSocket; + const { WebSocket: RealSocket } = win; class GuardedSocket extends RealSocket { constructor(url: string | URL, protocols?: string | string[]) { if (routeOf(String(url), origin) !== 'own') throw refusal('WebSocket', String(url)); diff --git a/src/preview-demo/server.test.ts b/src/preview-demo/server.test.ts index 7140a76f..2af43fa9 100644 --- a/src/preview-demo/server.test.ts +++ b/src/preview-demo/server.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-classes-per-file, no-promise-executor-return, no-useless-constructor, no-empty-function, no-void, @typescript-eslint/no-non-null-assertion */ import { afterEach, describe, expect, it, vi } from 'vitest'; import { ClientEvent, MatrixClient, SyncState, createClient } from 'matrix-js-sdk'; import { DEMO_ORIGIN, ME, ROOMS, GENERAL, COMMONS } from './fixtures'; -- 2.47.3 From df536f854e0dc9f4cd1c3e2a07c23e83d741188d Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:52:15 +0000 Subject: [PATCH 5/9] preview-demo: destructure open Co-Authored-By: Claude Sonnet 5.5 --- src/preview-demo/install.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index a5677e8f..5369c387 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -98,7 +98,7 @@ export const installDemo = (win: Window & typeof globalThis = window) => { } win.WebSocket = GuardedSocket; - const realOpen = win.XMLHttpRequest.prototype.open; + const { open: realOpen } = win.XMLHttpRequest.prototype; win.XMLHttpRequest.prototype.open = function open( this: XMLHttpRequest, method: string, -- 2.47.3 From e3278a4d7e64bf697b4cbc33bd36ac27ace2b146 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:52:30 +0000 Subject: [PATCH 6/9] preview-demo: destructure origin Co-Authored-By: Claude Sonnet 5.5 --- src/preview-demo/install.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index 5369c387..a0a2d0e3 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -84,7 +84,7 @@ export const guardedFetch = export const installDemo = (win: Window & typeof globalThis = window) => { const server = createDemoServer(); - const origin = win.location.origin; + const { origin } = win.location; const realFetch = win.fetch.bind(win); win.fetch = guardedFetch(server, origin, realFetch); -- 2.47.3 From e7dfbd008bf761f37f44786a70ead9ab40492ce2 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:54:59 +0000 Subject: [PATCH 7/9] preview-demo: deliver submit events the sandbox would cancel The host CSP sandbox lacks allow-forms; Chrome drops native submission before the submit event fires, so the login button did nothing in a real browser (found driving Chrome). Co-Authored-By: Claude Sonnet 5.5 --- src/preview-demo/install.ts | 32 ++++++++++++++++++++++++++++++++ src/preview-demo/server.test.ts | 2 ++ 2 files changed, 34 insertions(+) diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index a0a2d0e3..09e60d5a 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -48,6 +48,37 @@ const seedSettings = (storage: Storage) => { } }; +// The preview host's CSP sandbox has no allow-forms, so the browser cancels a native form +// submission before its submit event fires and no login button works. Deliver the event ourselves; +// nothing is submitted natively. Remove this when the host grants allow-forms to /void/rat-*. +const deliverSubmitEvents = (doc: Document) => { + const submit = (form: HTMLFormElement) => + form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true })); + doc.addEventListener( + 'click', + (e) => { + const button = (e.target as Element | null)?.closest?.( + 'button[type="submit"], input[type="submit"]' + ); + const form = (button as HTMLButtonElement | null)?.form; + if (!form || (button as HTMLButtonElement).disabled) return; + e.preventDefault(); + submit(form); + }, + true + ); + doc.addEventListener( + 'keydown', + (e) => { + const target = e.target as HTMLInputElement | null; + if (e.key !== 'Enter' || e.isComposing || target?.tagName !== 'INPUT' || !target.form) return; + e.preventDefault(); + submit(target.form); + }, + true + ); +}; + const parseBody = (body: unknown): unknown => { if (typeof body !== 'string') return undefined; try { @@ -116,6 +147,7 @@ export const installDemo = (win: Window & typeof globalThis = window) => { } seedSettings(win.localStorage); + deliverSubmitEvents(document); const stored = win.localStorage.getItem('cinny_hs_base_url'); showBanner(stored !== null && stored !== DEMO_ORIGIN ? FOREIGN_SESSION_TEXT : BANNER_TEXT); return server; diff --git a/src/preview-demo/server.test.ts b/src/preview-demo/server.test.ts index 2af43fa9..f5e7e446 100644 --- a/src/preview-demo/server.test.ts +++ b/src/preview-demo/server.test.ts @@ -237,6 +237,7 @@ describe('the guard', () => { vi.stubGlobal('document', { createElement: () => ({ style: {}, setAttribute: vi.fn() }), body: { appendChild: vi.fn() }, + addEventListener: vi.fn(), }); const server = installDemo(win); live.push({ server, mx: clientFor(server) }); @@ -265,6 +266,7 @@ describe('the guard', () => { vi.stubGlobal('document', { createElement: () => ({ style: {}, setAttribute: vi.fn() }), body: { appendChild: vi.fn() }, + addEventListener: vi.fn(), }); const server = installDemo(win); live.push({ server, mx: clientFor(server) }); -- 2.47.3 From 0020f7f003e83aaf8840e7edb152c71f2b93af1e Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:57:07 +0000 Subject: [PATCH 8/9] preview-demo: seed settings before the settings module reads them, short banner, quick first poll Found driving Chrome: the pose setting was seeded after settings.ts had already read defaults; the Connecting bar stayed until the first 15 s long poll returned. Co-Authored-By: Claude Sonnet 5.5 --- src/index.tsx | 1 + src/preview-demo/install.ts | 14 +++----------- src/preview-demo/seed.ts | 10 ++++++++++ src/preview-demo/server.test.ts | 29 +++++++++-------------------- src/preview-demo/server.ts | 5 ++++- 5 files changed, 27 insertions(+), 32 deletions(-) create mode 100644 src/preview-demo/seed.ts diff --git a/src/index.tsx b/src/index.tsx index 2dbab4bb..c2461fe8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,5 @@ /* eslint-disable import/first */ +import './preview-demo/seed'; import React from 'react'; import { createRoot } from 'react-dom/client'; import { enableMapSet } from 'immer'; diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index 09e60d5a..339348a2 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -26,9 +26,9 @@ export const routeOf = (url: string, pageOrigin: string): Route => { const refusal = (what: string, url: string) => new Error(`preview: pretend server only; ${what} to ${url} refused`); -const BANNER_TEXT = `preview: pretend server (${DEMO_SERVER}). Log in as demo with any password; nothing leaves this page.`; +const BANNER_TEXT = 'preview: pretend server. Any password works.'; const FOREIGN_SESSION_TEXT = - 'preview: pretend server. A login for another server is stored on this origin and cannot be used here.'; + 'preview: pretend server. A login for another server is stored here and cannot be used.'; const showBanner = (text: string) => { const el = document.createElement('div'); @@ -36,18 +36,11 @@ const showBanner = (text: string) => { el.setAttribute('role', 'status'); el.style.cssText = 'position:fixed;top:0;left:50%;transform:translateX(-50%);z-index:2147483647;max-width:96vw;' + - 'padding:2px 10px;font:11px/1.4 system-ui,sans-serif;color:#1b1b1b;background:#ffc46b;' + + 'padding:0 8px;font:10px/1.5 system-ui,sans-serif;white-space:nowrap;opacity:0.9;color:#1b1b1b;background:#ffc46b;' + 'border-radius:0 0 6px 6px;pointer-events:none;text-align:center'; document.body.appendChild(el); }; -// The pose portraits are a setting that defaults off; a first visit turns it on so the demo shows them. -const seedSettings = (storage: Storage) => { - if (storage.getItem('settings') === null) { - storage.setItem('settings', JSON.stringify({ poseBubbles: true })); - } -}; - // The preview host's CSP sandbox has no allow-forms, so the browser cancels a native form // submission before its submit event fires and no login button works. Deliver the event ourselves; // nothing is submitted natively. Remove this when the host grants allow-forms to /void/rat-*. @@ -146,7 +139,6 @@ export const installDemo = (win: Window & typeof globalThis = window) => { routeOf(String(url), origin) === 'own' ? realBeacon(url, data) : false; } - seedSettings(win.localStorage); deliverSubmitEvents(document); const stored = win.localStorage.getItem('cinny_hs_base_url'); showBanner(stored !== null && stored !== DEMO_ORIGIN ? FOREIGN_SESSION_TEXT : BANNER_TEXT); diff --git a/src/preview-demo/seed.ts b/src/preview-demo/seed.ts new file mode 100644 index 00000000..29af3dcc --- /dev/null +++ b/src/preview-demo/seed.ts @@ -0,0 +1,10 @@ +// Imported first by src/index.tsx: the settings module reads localStorage when it is first +// imported, so a demo build's first-visit defaults must be stored before anything else loads. +// Pose portraits default off; the demo exists to show them. Does nothing without the build flag. +export const seedDemoSettings = (storage: Storage) => { + if (storage.getItem('settings') === null) { + storage.setItem('settings', JSON.stringify({ poseBubbles: true })); + } +}; + +if (import.meta.env.VITE_PREVIEW_DEMO === '1') seedDemoSettings(localStorage); diff --git a/src/preview-demo/server.test.ts b/src/preview-demo/server.test.ts index f5e7e446..a0944b69 100644 --- a/src/preview-demo/server.test.ts +++ b/src/preview-demo/server.test.ts @@ -4,6 +4,7 @@ import { ClientEvent, MatrixClient, SyncState, createClient } from 'matrix-js-sd import { DEMO_ORIGIN, ME, ROOMS, GENERAL, COMMONS } from './fixtures'; import { guardedFetch, installDemo, routeOf } from './install'; import { createDemoServer, DemoServer } from './server'; +import { seedDemoSettings } from './seed'; import { mindOfSender, POSE_KEY, resolvePose } from '../app/components/message/pose/poses'; import { parseRoomNotice, roomTrainingLine } from '../app/utils/trainingNotice'; @@ -249,28 +250,16 @@ describe('the guard', () => { win.XMLHttpRequest.prototype.open('GET', '/same-origin'); expect(open).toHaveBeenCalledTimes(1); expect(win.navigator.sendBeacon('https://matrix.org/x')).toBe(false); - expect(JSON.parse(store.get('settings')!)).toEqual({ poseBubbles: true }); vi.unstubAllGlobals(); }); - it('keeps stored settings a visitor already chose', () => { - const store = new Map([['settings', '{"poseBubbles":false}']]); - const win = { - location: { origin: PAGE }, - fetch: never, - WebSocket: class {}, - XMLHttpRequest: { prototype: { open: vi.fn() } }, - navigator: {}, - localStorage: { getItem: (k: string) => store.get(k) ?? null, setItem: vi.fn() }, - } as unknown as Window & typeof globalThis; - vi.stubGlobal('document', { - createElement: () => ({ style: {}, setAttribute: vi.fn() }), - body: { appendChild: vi.fn() }, - addEventListener: vi.fn(), - }); - const server = installDemo(win); - live.push({ server, mx: clientFor(server) }); - expect(win.localStorage.setItem).not.toHaveBeenCalled(); - vi.unstubAllGlobals(); + it('seeds pose portraits on for a first visit and keeps what a visitor already chose', () => { + const fresh = new Map(); + seedDemoSettings({ getItem: (k: string) => fresh.get(k) ?? null, setItem: (k: string, v: string) => void fresh.set(k, v) } as Storage); + expect(JSON.parse(fresh.get('settings')!)).toEqual({ poseBubbles: true }); + const chosen = new Map([['settings', '{"poseBubbles":false}']]); + seedDemoSettings({ getItem: (k: string) => chosen.get(k) ?? null, setItem: (k: string, v: string) => void chosen.set(k, v) } as Storage); + expect(chosen.get('settings')).toBe('{"poseBubbles":false}'); }); }); +}); diff --git a/src/preview-demo/server.ts b/src/preview-demo/server.ts index 272807ea..d56e34ee 100644 --- a/src/preview-demo/server.ts +++ b/src/preview-demo/server.ts @@ -50,6 +50,7 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { let replyCounter = 0; let accountDataVersion = 0; let syncedJoined = new Set(joined); + let polled = false; let closed = false; const mk = ( @@ -246,7 +247,9 @@ export const createDemoServer = (now: () => number = Date.now): DemoServer => { resolve(); }; waiters.add(done); - later(Math.min(timeoutMs, MAX_LONG_POLL_MS), done); + // The first poll answers quickly so the client leaves its Connecting state. + later(polled ? Math.min(timeoutMs, MAX_LONG_POLL_MS) : 200, done); + polled = true; signal?.addEventListener('abort', done); }); } -- 2.47.3 From 2a249ddf68cb2332ed31885842484508f1728603 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 18:58:15 +0000 Subject: [PATCH 9/9] preview-demo: fix test file, drop unused import Co-Authored-By: Claude Sonnet 5.5 --- src/preview-demo/install.ts | 2 +- src/preview-demo/server.test.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/preview-demo/install.ts b/src/preview-demo/install.ts index 339348a2..36990a57 100644 --- a/src/preview-demo/install.ts +++ b/src/preview-demo/install.ts @@ -4,7 +4,7 @@ // only by a build with VITE_PREVIEW_DEMO=1 whose config.json says previewDemo: true (src/index.tsx). // Own-origin requests (the app's static files) pass; the demo origin is answered in-page; any // other origin is refused, so no real homeserver can be contacted while this is on. -import { DEMO_ORIGIN, DEMO_SERVER } from './fixtures'; +import { DEMO_ORIGIN } from './fixtures'; import { createDemoServer, DemoServer } from './server'; export type Route = 'own' | 'demo' | 'refuse'; diff --git a/src/preview-demo/server.test.ts b/src/preview-demo/server.test.ts index a0944b69..e3cc977e 100644 --- a/src/preview-demo/server.test.ts +++ b/src/preview-demo/server.test.ts @@ -255,11 +255,16 @@ describe('the guard', () => { it('seeds pose portraits on for a first visit and keeps what a visitor already chose', () => { const fresh = new Map(); - seedDemoSettings({ getItem: (k: string) => fresh.get(k) ?? null, setItem: (k: string, v: string) => void fresh.set(k, v) } as Storage); + seedDemoSettings({ + getItem: (k: string) => fresh.get(k) ?? null, + setItem: (k: string, v: string) => void fresh.set(k, v), + } as Storage); expect(JSON.parse(fresh.get('settings')!)).toEqual({ poseBubbles: true }); const chosen = new Map([['settings', '{"poseBubbles":false}']]); - seedDemoSettings({ getItem: (k: string) => chosen.get(k) ?? null, setItem: (k: string, v: string) => void chosen.set(k, v) } as Storage); + seedDemoSettings({ + getItem: (k: string) => chosen.get(k) ?? null, + setItem: (k: string, v: string) => void chosen.set(k, v), + } as Storage); expect(chosen.get('settings')).toBe('{"poseBubbles":false}'); }); }); -}); -- 2.47.3