From bbdd51fac3f86fbe021e60a7b6f354ed30a19a7e Mon Sep 17 00:00:00 2001 From: Sawko Date: Mon, 3 Aug 2026 17:32:29 +0200 Subject: [PATCH] feat(web): complete EN/VI chrome UI toggle with persistence Persist language in localStorage, translate filter/empty/loading chrome strings, add studio3d parity, sync Android WebView assets, and add screenshot evidence for issue #8. Fixes #8 Co-authored-by: Cursor --- docs/screenshots/issue-8-ui-en.png | Bin 0 -> 35218 bytes docs/screenshots/issue-8-ui-vi.png | Bin 0 -> 45288 bytes .../src/main/assets/beear/assets/app.css | 33 +++ .../src/main/assets/beear/assets/app.js | 259 +++++++++++++++--- .../main/assets/beear/assets/static-api.js | 105 +++++++ .../src/main/assets/beear/assets/studio3d.css | 14 + .../src/main/assets/beear/assets/studio3d.js | 190 ++++++++++++- .../src/main/assets/beear/index.html | 25 +- .../src/main/assets/beear/studio3d.html | 28 +- packages/web/assets/app.js | 61 ++++- packages/web/assets/studio3d.js | 116 +++++++- packages/web/studio3d.html | 17 +- packages/web/tests/i18n.test.mjs | 63 +++++ 13 files changed, 838 insertions(+), 73 deletions(-) create mode 100644 docs/screenshots/issue-8-ui-en.png create mode 100644 docs/screenshots/issue-8-ui-vi.png create mode 100644 packages/android/beear-webview/src/main/assets/beear/assets/static-api.js create mode 100644 packages/web/tests/i18n.test.mjs diff --git a/docs/screenshots/issue-8-ui-en.png b/docs/screenshots/issue-8-ui-en.png new file mode 100644 index 0000000000000000000000000000000000000000..efbca81902c1decc3c851a16febef5bb8f710e9f GIT binary patch literal 35218 zcmeFZXHb(}*e>e(D#|M&C?W#VEFfJ$K)Qf{bm^T4Qlv|-0Y!>*lwKp!L`tNF8Wich z2M8hb&_i#5e(lPoyT zupHYTFWuKT(O5fC+kKXTp_jLG_y)f0!i6}NMlaL9&VTwwRY#_KlE5$(O;i zzdwvV6ha_K-=h8xp1L7ADT57k4IjbwAU#XBaCC66Z1%=yFFe7dVXhWG6Mhu@Vzv>L z?YNs4X&oNRZ&D}au%6)HRmNvHce=e{n5S#&l@} ztwZdFX>ue}@@bhg{|mjl83$cb;fZwLY-A4 zF7SmUwhF7Cr9gI5ev=*(&@or;$Fn*Dly1Y6PIPJ&olT3acCzP@^Na=2>p z96nn0riiwwN0Td&%-#Qd`{0|Xj=pG`?wi(!DZa2H6&4gce)^b4tEJdvLIBL_^-ZiP zcJX(BBI)uXxgrgUc2Lo|LHxEwy6@3bzlZn`#!7vp%Z)M_ z=xH~6=KH2#NmRt;-IkUIE`;`+%bd3&8dxBG)_sO1g?f?7RYnVV-aznGzl^uA*Me7S zGPk#C);Xm-J#1MUl2=7XzrX5Jpbn9|W;pTgEJg zu_XUMD=#nV?3%t8$G7hoy2KnXR&i{ebfVvCQ!lN zNqdP#el$q@xsEMzi?oh*>@rBd!zfMpkB79M3uX!PTSE>^&G^aTO&MzSEac4b!#q+1s}N3GbJcb@s-hy|!BNip(aMr<%WYd_`i6guJS&IJLjW z%|_C}C7h>4y%$DS+>#$Wr3Et)M_GHRnJba&6IBCVs-yCGh^w%4KaB12%9BLq89n0q!c;e_GYk@4uG01mU z+L&KXF_`^03W5x_q|WbTs8gS(TKL6EIdOfmY&3N@nZy4H_i!tRpNGBlZg;qKKKmpu zZP$d(ouLAaes%4pR2F6;e*J~d-b(u)X?jTmFD`vS?f5R*Zv9dNq{iX(f+^1mmXI1aGmXxpJUe}etRqM3)O;x|7qZ?_|Sl@^=bH^d?+j;yrI{Nil+al`; zzgEqgdenT_@i)a}&&hVFfDg@KorBq{EKO%$(=U6}3fn&O8XJj16ZV#CX6kM0Y+rTv zj99LGGu&Sr>q%aXW|2G^cKK6oFrK69hQkIDH`U@#p1PDG)^T|E;jg8))fjuTq&Oj~ z|9}M%CG^KoGu&BDPfeRl;>A>34S$w9`6OLW$743ja`eo!Q&vGi<#sQZ-iRzU*r>Vt z#-1!l~}n5V-Pm`YN^Az8>I1Jd{jb`wdwrf_vi8EP$U zZ7;m|4-7L6qt{+bDzoWignsnqbRZAJC|_ybcQV{AD-5}Zj6aTBAvn*CXI~Auw`Vn; z=7HA8kYd?64UzwR&(TOIRm?Ujwt>lHlKPkfrcH=s&hnhUzjQoG!S?WcP|np_CF-Y-5kTaW(GW03ZJcP5rL zJ~2`o06xT_0qO7#-ldYB`zvB;OWaYFr@Pt?#@? zLEh!(2+#_vG3oPj?|oJF`GC4Pnwx>A9*d#8egaZL{_pUhjF8EA(=tDC@1X-(!ye{4 zGf(=}9*D`$><%FIRVm5Q(9H9*2RyZ%JUlLk>*1Z4T0XgMN9b=2Q)3FMH^d%YmMt*4 zexSSV9t*qQ77}%pPPj;`P!_GHMsUYJDwTUg-uy*+tWbw*w!|2*!x8>489rSD(y=N` z>(MPL=*7^OV_82H%jNE$YUqRGE0-J%8V#m~T56F_tfxD>E(!{vL98zn!Im zA;C^lb%zTp6zQl_JLBrgIGgO^^o120}Aq+g1Gn7rZ&99F>jj5yr_pgUwZCN8X#?u!33eI2=Da^!RL3je7?v zoa3$doxQcuyHM?;QD-OTnlPSEyX|B!hvP5$(Bdj&+F6b;rCnx|_r@j^wtA3QmGcSg zqUWWZbIh_b)OKRya5G^NUix-kJ!erG^q~t|#HUZ+=bhlm#O_BD_jb~f5$w;`)*_R= zmRumb$qM_*hQ|K{QPVQHO>LO8VGww!2U5#pwb7vLvq`^rp%938-5G@sye(z;sdQ!#CZ(+3l*BoG6koV zytlrmyA@4eR8T+hS%IZ8&#qJqaHE`%gO^7)Tr97rN!uiU^6IE5ZXSd6>8#Nm-e@C-C2`KpA;&Jq~hU;IGTDFzhgw(g^o~R zSg?YWOp|z|y=S%xL8^$VU!M+i=qkt=b_##GGFj%q*S1%g6%stXhr|UCcR)cKqSQfJ z{b^ILeYfJvOf`oiwV`qM?&Y*xne#~hW)zbq#Y*0sxF%9j^Xi~E&%TdxZK2GG%Kwz# zbl*+~t5m4dcXklTFU{h&9g&QSMvFA!F-XFzuHZ7S&Bne;dU=J`PgIr3MCH$EC%)z1 zraI?0!IOGRpDF%ruE5F3`B`%i`~!f|0qwCIsUgiE;`-W%>nF=lQ#*vw^HXbagz?f| zhhA&sA{6r3=X0~8(bF3&RI4b*S4~cac&V>m$;B75Yo5Z63aMO7j;u!NH+pV}rzNu-;!gzzqYC-&JI_6(Z$xA=op8^y6 zU9alPvv$>bJvDTd9oK;>UYx4W$3)V5gm?ayqR|TRSGJ1ZiL1MANQcT2H>vfi?-uD7 z&c4#MFi$jMVxsXICQPp%H&HSi8|t`6soAdK^8>4w4#_~e38sP>mGo))xVZLJGK$lb zKT2wCelL3SE=d*VCsPkJD6JTATVlICs6)%If`` zkdVOScO5_OR`X)pVt;j{Si3tr>5WjBU1DphI_*_?gnCJL0-xyXmR!U;nf%T7cHtzJ z!T_2LhFGx6@dpfVKy2Ki2I!=co&>RFNl$OO9t>~3-@==?(Y3hu-e*GJWY8?5RGVg3 zLA7qxhdZ)ax<7reMBBdit3{aJZh%o%Bm^hoggLYAghAAp3k*xYei*LoZ|gJNt7qll zhafjER0|vO@@R@V#=uVZ=N60Zw_{@OdeJ$b7U6W>OnsN(i(ZYW=zHosw6#~OBKwz$ zvhwxVPds+!eB`pVINm2b6lZsKq;ce&0TsXzhN#9~`G6 zr-6_5!U}yiE_z<{IU@rUAyh|f!SRXoO~lKlYtNdDp7(s4urq*HAN^Vz%M2=}0|S_* zrzdM3>&PU_^^(@b~R6yvQh0Y*w5ZB*0Ht1$pm& zfbDK%R?k7i?@0ZI^C3zx4-l|^tZpw#)H9|0K6`q``BdaDmv(Kzy!z&U7?s%Cfc9s@ zsNUKrzgVsCb}z%?Srxm+ovKARRziLAuGm+bd@>z7kC~hq6KqtPxJ@7A28#}WZEl1l zL%PlJY9D}B=jLe&_;2}dfKhK%G(wftlStQUt5sBCHq&@#H?qWpk&fGrO)6kG&AaYjJR{96RSmbm>2%9EPZB-xtRFo$l>yKHAoid}%i_~z3-)V><};eA#oRHAGU zb7x9?nDGF>$RVN!>*M9{Exq;0K%E9HBkJHE_WJu|Tyg&MRE!vDfbU^}H!pcl!ghj(KXk z-~fKvr_u`d*R~9pcySRvmM1j`)8q2|e0FyBHd^lS82Z6LTN- z@fs^`RAj80nwoIQu@&gGqZyykRz23|N(2}sPsOmxZu4}vfj)ig^aqn<*5NSz$6fX1 z^KP1|HPBKkwaDf`nKLONK>~Ct&1Q{R*A#9YaLo;KVoQ#pA8L3$xcw1xHcCF-sbY*w zF6ks7A4br*fesMr{p!}~!UU)`+jYDeOq*zU?4NzpGUDNlF9TG#Z?c!+M(#f411 z(Vw98ZZAuXkF{gEM4U#~TBWs{7eXM<2M}(7hFh4YA+qMyb^=yIZ#N)UUx2;{WC6#i zU!0g`sHn&M7$(~I@VuR-4i?B$?n^o8j4Fg#@+76DiP=xXSHvtoocIm%)nBa!1jAsl zPNB~AWsJOkM&ZcFhxCbZ+ipkeA}HSW;Yuu@*rOvkPS0Y1yUrw{f(W8EaYP7b{>b_P>2x$kj%vzx3+ zY`@@PpcB@u#t&B=y*gT5^x8Q0XR2)Wr>Ka4nfbtj=0sCMtR07lZ@1^Br0dov$W_;!l40cZ)-C& zxK+6FTH61e^9BQ(_#tz6WMm*p_ndFMdi#)CR)K3W5smP$V!NMH94WcAjTd*AGd7C= z9q$DJ+!@~{+rk&kj!!UB&TkgAfV-zWkpf{cnaqzZZrYynwywX(*(}>Zs*qzuRr98c z=@e--ct4SoyXX=fXP<>QxckhLlufEHWd-kD=pdLzQQ!QR4xYixf6)Z5UHFCmBl-Uc zF`V})O3feTasHhb8ZQ2y%&`9d#QgtLlqjbN?j{-7+v6TAHvLkJ-8~ycIH2Fy-@aY@XjJL5#$ot*Xa zkZu*~(!6F_2?=|p-1`vLc;dwizXH?O|7IJaAApCklA|P@rLJBL(4K!GFAsaRQp!2i zWD~=K`o^JdzyqKRYlm-zOMl|l06N0jpvk!Qh2Sy}nr*6_n_z1X9-T6T6^ zHb7~44slX@95D51dtPDL$re+3D96%74+)g=+#5icr2cj0`^jgW$!;?DbF|vZ%uGT3 z;oTGep>fsjb@6vkpFAm)^+798t|`Mk{rzRvsvEU*evGhS7y0prxeVU=Nj!wz?Rk#8 zVii8f!5bk$S6FOD#>wcq8;(Cy)UT|zND#LuN4gAhg-;84_7vc0k&X5L1pTzZXNx3G zAm2dvLa&S#uHH&i_C`&|+s&BP`mDvUlD|=;Dlsm|4-a?2o0+aY8@+jH)@6%E{anfV zKrP#Z#3U#;f<=J)O?QpoMI!$aamEayQ25~@is%rkm}oUy8ERQb!!1&3T-?#4mu`OV z_U&}1Jx0ODrzQW+e$cK_6U(q`c-H2)@nZyTP$hFe`8o{;bOT41QksHZal056RW!R` zG@9+wFLl2!vuOcG_tqXaQn>bm^X$ZS+}lN>nZ}&X-?fuJ%-~!0~jQU=qvuRwhS| z)jkHJz3f7-psQ`2Yz=va?IL&gicK1cNB?-yq54Men))8lZ%-^Z{}8SFM0VF+q@v2r zcWXeOI=BhtTvg?E@^&4!V)k%&<7T=f1td^VWZ2Hq2$$DHvIT?w=E0u)i`S%b1dJJs z&x#N+jle^NkJA`4vb^`t{0AyvXbv+E=qLSh9yj|2Lyb8sHTq=%cHtR z8OH2K^1Yfg{SKJaQD?acpU2}LGIMF)52JeZ=vI*)99x0k8n$7RQ}n;da8fQ;ujjG$ zw(XyXU&d*jeXz9s0S^hG(dq7YcDL#D;XZ%?4X6rl@$9Z1wm$LkxxFJXtYmfdB>3+O z?@u4r`;ohuxApii3U3TxcJ>TAkQWs-GNrusX7r4W8C~Y#Dtf#`kC5t~sx^_&Yb48| zmO)Wy?boj}2bqK>e|mP}u3^WnqxeCVgvV0H?~<`Gy}7nOiW<<93HJ@v@EbQi)A5_u zd#_B!WD-P5Ax?G{Uxw*6u<)>S!tuqhm|mji4fv)*E+So{a&kfNU>rr^fRr{!L-5T#I?(_&3l^UB7?=_x7WjrdAvM zCJaHCZt`z)#O8+QP+q$RHvC>MV%N<*37c!X%Qtu7%~-1|itV8=p;=xf{>vBN{dND7 z1!gQE+1W(;3{%XXsNC3V>}7wY{@^W_aT8YgtoH*)VxTpR2b=)ii@K>dqvD`qH-b0~zWhUXluR zaLk5rZ@S>0w*1NuA7m)o?bc9y+{r~Biknb88)JEmwLUl8cJG}LJn>k4!N>bEAC`<1 zWvKN^jgmpaT&&b6P)mJPX5n-Hz8nR`?(JUFYDY8O;_t1$<=P<)-jbUSGLc#n1z!3x z)Sd;DA-0a#T1bY<L=7>~LG*?`y{?&5T`w5GW)&9`0QG#ve4Yt22)H7wE{mhp?9 zMcW=UbQ|En=9|=ggk#xy+mHMscja*O)SJ@JV`sz=FlEz{;x;bdgM(ja^rxpuJ@)jj zD-iQ=(C!;s(tNd*r-Jf5bBlB6-$~DKlaAXUba{j&jIWpWGB{6Fx{WL=Wr|mMcsc>1 zbK~Yio?+8aje*M|z=J2*?Se+XhXu%1$ zOZ;?-^cKVL$eSElohs?xCUsoo%^*~VHjp6iO}1-wExbv$G-fZz6h`-Z%fLVydPPkh zJR$C~(}U+8^TyObbWBjc| zE*@3ERo%hT)AZ~23RRKSO4U})&1Q?=hpo@(11UIe0iN>G-{!)?&X}{&J<%Z6lyo{5 z_n!z5VWqO7itTm)1s$7D&$)+0M@5xmzVPO0rM6Z~{(84CGv+R*>Vs;6#Hu92@zZWE zuLaNTf18_kpr^OniF!dFXu!e2fvcNv;~f?|T)}vp=b~CS(!qvbOw4HY1~r_n*IF#K zxV7bN+nKw~TIUH5E;+;VWq7ZXwe<%;`k37>^}?D(Iupdt zvdXEaj`1c4bh?=17;GEyPWG=0&t+zqn~QS+)qQg8B|{y0ZFSyfaUm(18`qaAWw-D( zlyVh7W&6m)O0I0!0bv@2lE&OPJ$3u35OfRBN%E0tk?|-T{<9D&+~e&VC$oiBA$WT% z?C9k36cP#tYqV6VwzK{4+E4(5{lSBLG;wcYH$&L(FHWmZ7eN0jDD*7e%r_5}x~xu@eB7)IE^~32 zn~}5ou@pN8(1y_4R{$$|Fg{I|wk_0IrDP;ISD*L~Rx0IGcFv(9lgaGJu1J>vAo$HA z7C@;friqS@pP)n)78U};C*{77>)CrU#iYj8kQJuj^}1!M;S(!4@vm6-EC0~Y#K$m6 zXAWEUs|b|~KhH2)TJAi%5)<+9<%Fnaiw@?1`8hhr6 z2drp~%{@?h@}#U_s?Tch%TWEdNR^Adl}`el)xCmYxB0z(ptMXVbU^gjfh@k$IDC8W zAF>M<$UZzzq1D!=*^WJl3?QCOLt(kOxp8p=7(XhHjVCXznTChM`Q0;)!mTsRhnF(7 z^Lr1rw_~Ft>ojJ*OLS~dkTe6q$I?;<)H@%; z9OvhgWZ%HZm?S-wH8kK5*c61z*3$9;as8mq#kd~$l+vt^t2p|RMC(mZ-@qa<)4>l| zp0YnXvgJ#YM2&W&*$dLy+ZWOCHv>SI4Lm#tpWP|Z!Q1y+>6Jlet zhWJ#O@^w~yOd7x9T)E-nD6Fnz`q_Tm{ zhxHwmx!$d-tv2y(eHm3VLyK!0Vs`m=ZltEB21q!yfLa%d^p>0tigf$P8Q)H-^gae4 zz8de}_%v0k5LE^(`lBe+$;Hy2`tjr0H?oTSfPD<;24b1LimV|IrKEnN`XPB*`jy;G z>mt4UqMDIok{}N$#@u;i#VM-^T2Uc;ohBD;!9WE!eVl-m@?u?KAAWbHiF@P5L^4?7{O-HQXR9WRtgh9oYP!Ee-C)aCB6{_p^Rz^Km@PTdZ z9M42b`CT^Rl6celGM@NM2SG4X{gK1GcD2H8S%fq9S>7)B^NOmRHx0%d8k^?5pQaB- zraj9xb~T?2!vAMX7gjr_1#TZ7!2%5fe$OknmtL$wQ&Lg}9ue1w(E323`7jXiP!Su= z#=0uI+_S=aUXJ(3WdV}I_MW&d6_eFb@-d7z*%ZV$K1k|>3x6JWI!4=4{M$tZ{USFP z4y9Z{Y{ISjqSuU#ZVL(s0DG6yY0V8A%z;|g)4rCL@qYe+e*8X@3{3q_BX}b~thf0b ztQ}0gYyPQ23&Wk}zSV|uVM$AvA{*TOD~P?dVNBPL7O-PrVaRviok+cVomUPVd!9k9 z)G61o+6`IScX@jvNG-S1}IpRxL?(GDbor6qYIge#`r54xsT zT5Eq`qJQ5WU{JbtTEe2GSn6O-=-tVtJTEvOp#Im^*3#4IY{x3Mck(Dua8od{m)`ZR zg8+utgMhhzr8A%OqaOr`Zft@Fq|XgiB0CN{vhF zzXl};9FMZ!MDqrUJN^q=;_$aVm#q{WK9@SN-QEFg3&I)YIvN499jEFj+WK@7J;}gG zR~K-cTf^ECc#-KQ;2r8fx5Y9ql{%RZh0$;b(M;Iq<>ua%TuQ3$p^LPKzOHse$7f_r zRrnQ9J*6CCJGYpSeRv$GQ=+x<+ zWx7?9kZ=n$m`U(cV+bVh=qHioZRGHO+$1?_DYm2IyOU%XfUU|1)4~yg(^#_H6_Pj; z*|g0(M}W`PfXntaKxx)}DLnou%W+_|aY4^hoGs?(s+}+u_+NvdDAXKza~Iu=W*A`- z%XRK$7~RFozxt_HYDPkT(Y+jc(&+1+N*sLvJ)`nwFcW(KZc+Z9HeKM40Kfk4Zs&!{ zOrM_q4G`9b%eL}XyiHk|OHz{jn%L;wXJ7Rz@}se?YSj+t-9;3(Uf6AV>k!}U$G$Z< zZ_Qhmj|O_(HA>0|T3$P6NTGf#pQ#u^_tC96ZJ;vSp&UP#HXyZZ9V|?`>MTEAR=*#5 z{kzEopQ6Vd35V0}Wb?2CXH70_FXV|1mj^lq>d=*7bchlVb?=~(mD#JLY9^d|e71KF z0L<1u$3SGy&FwiMw(D*9mY7w{_F=Aoj^#g+^)vsyks0%O>Epd3V9_wSJN92pVlb%= zXinfo8@7IpdPvISl_Cx4^sKbBH2GryO2x#MvSYDrYT{^*bpj5;nAbFx-z(mqReuCp zU#u>}txGb6Bk9}wxB?8n!(_wm#nm{0gybxCWBfs6WFOUqU*ZpjZ^$MppADw;gIu|` zva-K{Dbc`}9_Vt@-??){xI%0UK*yL8sP0N6Wn?tiwf2jOIsA8IV?~4l3twzSn1h4z z+T~^rjjXdWJ4`i`-yzep$*IviK!nsSQf;DjDil1nT_TieX=$OLPA+nfnbocypHWHE zRsDRaFQuKG&5!FNRn-=`{kiNB?DfNHmz}SVa;e97>YIH0P+l>Xbe)sscxc#i4HMup zc>LD|Z-k|oY>nlFoyg(tiSptPT4~bW{5pV{0Czum^2F1<_+U7;!UpUb^5(%ePGQ$! z9fRioL1xBX36a(O9;ewFu}H_8p^H!lj*M#jRIK9dT;|v~aKB?W4B~Gi}kU{{(Hd|L`YbNRZL|s(gBulOq+*;(`Li>rD}m ze(&ASsdmD~d(48RK9A*@*ATdqw>(M$2+f%Z%d{4@=7}GP>2XYxCoPZ2J-0*{`5%WK*T&d^Uw|`a(K#=EQmYiLc znCTP1`CPtEum7jB>WAEX7E~caJg^fKtFgX9ZPbTm;~R{O1@x(d`pP`cPG-4o1=T^U zcf~i8JURgv3Y-(G(^Vfg+k^rg%d6asYJD5w&cDWE3r2pR8(gbG9VZVbYLwi(Ag-=E z&MNx#)$4k-Mt|6Q>-|B!1(*}K@w_?A`krCI-U9EvK=?tmJ@U2AI%ks`5?i z4@$c++iB;SWx)6p>5vY70RTh`nfo7@TAqd47KYoEBKwsNSpDgA^|>&SOeXO|^rYz7 zV`mp{V2kB%a{GZQSKe0uBQgZbE-P!%wgc=G{{KNITkHZ}xeq`@(H}}<5R+OzI_KhA$ys}^%e4Z{2(xh%-Z5wum_+Nm*2MqcX!Mqb@fsk`SW zvVL);A`W^bCQA&ux%|5Xa3+Qg7MlmAfKBwF2pA={ghxf~)Ofef&n24rT`x7CX7h0JYYu22M;+#{OBhKE=2*(SEfs!DZ@59c<)O++$VsPO3z9y~Z$kzUP?g&lYV zp4v`?$J+=ypUNS80WY>8z90L^8!EI8xa)Mprj+aY$o3y`l?-)PA~!cQS=-4*1WYp=F74K~(9NuP1}()j zdIWU0C4#92tqR)n?Pg{==}(j&g8$Q^^M7)b55o!83rhjSFrUa#I}eZp z@F4=?6wuLugtiH?U2A};cIsv&y@>pEt;}R(Yn-78m2QHViv7Ir@+2J2mw$UHCfF@r1gq&5mf~U}3=nO-6e$ z8yFi$-w}Sj_ni$WSpfZ8Tjzt?pm1#ic2KeIg2dPRw;FA0%y%dA6>TM4E_AC=j4Ps$_^69fOdD$I@MJsG4fj#(Eb;(7X91$62}M zxLUsoZv7IKmoHCUCkux2RlggQHGtI*3GsXxkuG2Q*ScocWPa z#R=lWncr=GCtGuKsh@%U$VQ>HkZENT7n7HL{V*nwZEEH`C+d zmREd$lOneLy2han=zqE|h}$eFd6_Y)k^!eLs4sO}$O}TpkaiF2uP#=S zc#Nx~j=n&e-D)#l-j2>NeZt0JDZXJ&ZaN@wJTQ$VloU~0B1x9@-<~` z+@cW0i14f@dKZO5M^*U}i;b#=a=y;sviW7R!T?Iljn3A6sh_V*E1vxx#nzL9>xX=5 z8dOumdc&e>RI6)!KC8lbP3sairS~b#yp{y=NX{Y8!JM9A0G0I3_E0=rx*i~=)JT!^ zFJHcFkd^WOF|%c{#~XBm*hz5G=w2ao$qy@?ZdsdN3A0NhFng@5tmr;+UISTVq9Sk& zazZL%BM)+!d}XX0asHsmUYgXI(gn2L-5jP}hH=xK!%zj+N!q_IJbLDOEtql@WmxaD zmRU~)XtK!LLj9Gh;5d!C>E*G~jb|P^9JJg?*anY7{bAgv*_OfI(|`~KD|nZw2-Dz! z11Yv5ptfN>y)dP+DSypO-qtc9z%wk$cv^%xximab2$k8&#(CmabTMhyHVh6)jVq8+BsQ3rpw##NS)Z-L0(-5X5fV_WQgekG&*$ zWAQ3i!&#<_slK?hsOD*1ks6wcwp3vH+4%&VjCc$z7NGd$eb>opS0nDM;lU=42#<~q z4{vnC5vg`>Q{9mO5TSEyY^kUab~Wg=o!z}Y`|@QR7LPepQdYkD&p(*P!&fL~xfYT+ z2RnOYn2>THp!w;`UM^il_3JF6+ikt*?d^YnB11H5@&FVOnB?_A zrH1=fa~wIBWX3Cfa0#{*Q2*06^K)Ps7@oRM?N|EHO>+Z3CV-xIrF?%F_h$q8lIvCk z-X<`$HA=v?DBhO+$M!B&j|KY>f?rg0qFv;9#V^yzOU!t;u%5`+_G>5<>Ts9h^fWSc zvi>0p8Cl@dgIz#30P(1z)1qRMnq7W^0B`v8sl$V@x;K?j z!Ad^gc1=*2FX6wRO?0ur*!gUp^191kxR4t+|1f7xjh&H5gf{2GG3qdMxnqBlqu&ti z_gMqS{jOH|h_~#;`j2kG)ELCw#)5#%fmj>YH*V|c*_(H{uXkD|yw|!WdJWh{b{4xX zU%E`mC@Mo8Y>FoRfb=WPAy+CL2HlsUI(KiMt}X?bWkjiWSEcSw;R_;Tfx4=iDnTr0 z?MC3I5K!Ej6&&0J=&MXpzFa6TvBl&*%w?-OOiPRRSWh8n?Yn&s%dNUU4PW-f)GSWb zeAo)8@xz;IX?+0#w!aBF=e?Mv*|TTk{6>%MSM{+Q;_vpBT@ZE$y1=aLe|Dr;grKu^E=jXgEMW%11P)(c#J z@>1fXwTieyb>i(h5Q17APpKnFXrf>5mt8tOy!5-h)hfnpezUgrR^wY$-wkto51p9m z-0cc}2nC%PBA^H~b1=y3)`+yKmD{`- zLVkT3Qc?jY%1{?t&ht3*9igVfg$5F<2LH_4Ek`B;+b>8-dw6ss=yB_4%KXL5-6`8w zi%{4=%W*z~MK(5yiUett!%mI+{$^ts@EKnp-Kh7pm45eJ zLvYw(6Xq{2)MKjA-A(xNaxUypttavpk8F0}o&P}}r{XiwUzS$5Qx#{xT zreLfU-i$8y-ySuSgkw2pWA36kD8U_+ftIu4ur1~q-`+LALQIT#yMJrHc-S*dADRhdpE_O zJG?F&$0%8yU!UjKgq>SR|2IGSuZ;ZgszeWu3A8-w2nR{8x=0HNMn^kaIb~Rd`W}y^-t! z>lFdY;K9KsXIG++_bOIEw+Lwb{m8US3@~fU z+wr{U1HdJ^tsOKzRK5i}327kife0U{bA$s%a=(4#RB{K5BM`Ek%t{V4#%qj|aPu>< z)=1;FmX@OvvQoqP6;Nk@;e_GNot{iV)3~CK1mV$QP02NZizoYc2H@>zX{cxw=~r1z z&C#qJ!Fj4-ci?k;1u_%p1#hL8NeO6C3JSM^Nudfp1$g(Hi^$B1$Q=K zn`b!sN9;TRl>_pyrN8I$3=$*)aFW#TglEBYWV6W{AOs-rGNBQ^!=KiUT<$Z7{NNyO zZtE4t`fkMaGfV6>y6je$xn!U0+j-%~WF$%eJJ%G04;Yin(?axYx+?mbGBa*%WT*kh zyHwm8RvrL3se^-qTU2o`f0lC*XZl(J8^WD{phf(zER&}XFq{8#Pv&=CR^|1GY zy9KC2R&rjW!=Kx@wX6#?A_zBMU*A%-Oo~#} z;x`)QM!?TCK?u@`lq3Vme`*z@z>u-5%0~@6`$)^f8R9bT;p%! zkzz^>V5T_P`wLib7QCsltEzO<$4WsLNiVfJeB;mMN~h5tAXp={Quob3E(e56;IV+} zDXt<82aFJsKC3;SKN^>ml(a+(TKA!Uu8@{Yy-#>7LFO+|hlvB7t)wK{(+?1vFk%k# z$x%4Te{0~d9hQ{&%*T4SOzGsH~6wHZZ0~)C+ZF z#VW|n(V>SN9QX;pm(;-l(5KOF>@0@z+|VG26=Q31tSFS^v<$V2Ms50hpN*DkR(|*S zYroFKU6zwYH=MEijjAr)j4;Aia5Bt;s$p8b{wEE|o!o>EZPSo~SHNGXg6ghrr)FO}2}pQ)W` zzWSLZTlRWdwV#{A*{AM&i6Iba2?^IVX)i*}{|iGVnM}®mRFOFD?IkRh7s7U^_- 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 literal 0 HcmV?d00001 diff --git a/packages/android/beear-webview/src/main/assets/beear/assets/app.css b/packages/android/beear-webview/src/main/assets/beear/assets/app.css index f28a9ba..ef4d32b 100644 --- a/packages/android/beear-webview/src/main/assets/beear/assets/app.css +++ b/packages/android/beear-webview/src/main/assets/beear/assets/app.css @@ -87,6 +87,19 @@ html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text); background: #000a; color: var(--accent2); padding: 4px 8px; border-radius: 999px; border: 1px solid var(--line); } +.stage-load { + position: absolute; inset: 0; display: flex; flex-direction: column; + align-items: center; justify-content: center; gap: 12px; + background: #060910cc; color: var(--text); font-size: 14px; font-weight: 600; + z-index: 5; pointer-events: none; transition: opacity 0.25s ease; +} +.stage-load.hidden { opacity: 0; visibility: hidden; } +.stage-load-spin { + width: 36px; height: 36px; border-radius: 50%; + border: 3px solid #243049; border-top-color: var(--accent2); + animation: beear-spin 0.8s linear infinite; +} +@keyframes beear-spin { to { transform: rotate(360deg); } } .consent { position: fixed; inset: 0; background: #000a; z-index: 40; display: grid; place-items: center; padding: 16px; @@ -110,3 +123,23 @@ html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text); .layout { grid-template-columns: 1fr; grid-template-rows: auto 1fr; } .rail { border-left: none; border-top: 1px solid var(--line); max-height: 42vh; } } + +.empty-state { + text-align: center; + padding: 40px 20px; + color: var(--muted); +} +.empty-state p { + margin: 0 0 16px; + font-size: 1.1rem; +} +.empty-state .suggestion { + display: inline-block; + background: var(--panel); + border: 1px dashed var(--line); + color: var(--text); + padding: 8px 16px; + border-radius: 6px; + font-size: 0.9rem; + margin-top: 12px; +} diff --git a/packages/android/beear-webview/src/main/assets/beear/assets/app.js b/packages/android/beear-webview/src/main/assets/beear/assets/app.js index 2b94833..951735a 100644 --- a/packages/android/beear-webview/src/main/assets/beear/assets/app.js +++ b/packages/android/beear-webview/src/main/assets/beear/assets/app.js @@ -27,7 +27,57 @@ let lang = "en"; let tracking = "geometric"; // geometric | mediapipe let faceMesh = null; let meshBusy = false; + +// Privacy & Bounty State Constraints (localStorage can throw in private mode) +let photoConsent = false; +try { + photoConsent = localStorage.getItem("beear_photo_consent") === "true"; + const savedLang = localStorage.getItem("beear_lang"); + if (savedLang === "en" || savedLang === "vi") lang = savedLang; +} catch (_) { + photoConsent = false; +} let sessionId = null; + +function isStaticDemo() { + return !!(globalThis.BeeARStatic && globalThis.BeeARStatic.detectStatic()); +} + +function absUrl(u) { + if (!u) return u; + try { + return new URL(u, location.href).href; + } catch (_) { + return u; + } +} + +function setStageLoad(msg) { + const el = document.getElementById("stage-load"); + const tx = document.getElementById("stage-load-text"); + if (!el) return; + if (msg == null) { + el.classList.add("hidden"); + return; + } + if (tx) tx.textContent = msg; + el.classList.remove("hidden"); +} + +/** Prefer light featured SKUs on GitHub Pages (Meshy GLBs are ~11MB each). */ +function pickDefaultFrame(list) { + if (!list?.length) return null; + const heavy = (f) => + f.heavy === true || /meshy/i.test(String(f.glb || f.glb_url || f.id || "")); + const featured = list.find((f) => f.featured && !heavy(f)); + if (featured) return featured; + if (isStaticDemo()) { + const light = list.find((f) => (f.glb_url || f.glb) && !heavy(f)); + if (light) return light; + } + return list.find((f) => f.featured) || list[0]; +} + const threeState = { ready: false, failed: false, @@ -50,7 +100,6 @@ const DEMO_FACES = [ id: "face-01", label: "Demo A", src: "/assets/demo-faces/face-01.jpg", - // calibrated for front portrait (pupil centers, image 0–1) left: [0.385, 0.428], right: [0.615, 0.428], }, @@ -90,6 +139,19 @@ const I18N = { trackGeo: "tracking: geometric", trackMp: "tracking: mediapipe", trackDemo: "tracking: demo photo", + cloudLocal: "🔒 Local Only", + cloudServer: "☁️ Cloud Enabled", + photoConsentLabel: "Allow face photo server upload & cloud features", + filterAll: "All", + filterGlasses: "Glasses", + filterAccessory: "Accessories", + emptyCatalog: "No frames match the current filter.", + emptyCatalogHint: "Try adjusting filters or check your connection.", + emptyGallery: "No snapshots yet", + loadingCatalog: "Loading catalog…", + loadingMesh: "Loading 3D mesh", + starting: "Starting BeeAR demo…", + studio3d: "🧑 3D Person", }, vi: { tagline: "Thử kính & phụ kiện ảo", @@ -114,6 +176,19 @@ const I18N = { trackGeo: "tracking: hình học", trackMp: "tracking: mediapipe", trackDemo: "tracking: ảnh demo", + cloudLocal: "🔒 Chỉ lưu cục bộ", + cloudServer: "☁️ Đã bật Cloud", + photoConsentLabel: "Cho phép tải ảnh mặt lên server & dùng tính năng đám mây", + filterAll: "Tất cả", + filterGlasses: "Kính", + filterAccessory: "Phụ kiện", + emptyCatalog: "Không có mẫu khớp bộ lọc hiện tại.", + emptyCatalogHint: "Thử đổi bộ lọc hoặc kiểm tra kết nối.", + emptyGallery: "Chưa có ảnh chụp", + loadingCatalog: "Đang tải danh mục…", + loadingMesh: "Đang tải mesh 3D", + starting: "Đang khởi động BeeAR…", + studio3d: "🧑 Người 3D", }, }; @@ -121,7 +196,14 @@ function t(k) { return (I18N[lang] || I18N.en)[k] || k; } +function persistLang() { + try { + localStorage.setItem("beear_lang", lang); + } catch (_) {} +} + function applyLang() { + document.documentElement.lang = lang; document.getElementById("tagline").textContent = t("tagline"); document.getElementById("lbl-catalog").textContent = t("catalog"); document.getElementById("lbl-pd").childNodes[0].textContent = t("pd") + " "; @@ -138,9 +220,31 @@ function applyLang() { document.getElementById("consent-body").textContent = t("consentBody"); document.getElementById("consent-ok").textContent = t("consentOk"); document.getElementById("consent-demo").textContent = t("consentDemo"); + + const filter = document.getElementById("filter"); + if (filter) { + const opts = filter.options; + for (let i = 0; i < opts.length; i++) { + const v = opts[i].value; + if (v === "") opts[i].textContent = t("filterAll"); + else if (v === "glasses") opts[i].textContent = t("filterGlasses"); + else if (v === "accessory") opts[i].textContent = t("filterAccessory"); + } + } + + const btn3d = document.getElementById("btn-3d"); + if (btn3d) btn3d.textContent = t("studio3d"); + + // Update cloud toggle and checkbox translation labels (optional DOM from consent PR) + const btnCloud = document.getElementById("btn-cloud"); + if (btnCloud) btnCloud.textContent = photoConsent ? t("cloudServer") : t("cloudLocal"); + const lblPhoto = document.getElementById("lbl-photo-consent"); + if (lblPhoto) lblPhoto.textContent = t("photoConsentLabel"); + if (mode === "demo") trackBadge.textContent = t("trackDemo"); else trackBadge.textContent = tracking === "mediapipe" ? t("trackMp") : t("trackGeo"); if (!selected) metaEl.textContent = t("select"); + if (mode === "idle" && hintEl) hintEl.textContent = t("hintIdle"); } function loadDemoImages() { @@ -269,7 +373,6 @@ function prepareGlbModel(scene) { scene.updateMatrixWorld(true); box = new THREE.Box3().setFromObject(scene); let size = box.getSize(new THREE.Vector3()); - // If mesh is deeper than wide, rotate so left-right is X (frame width) let baseYaw = 0; if (size.z > size.x * 1.15) { scene.rotation.y += Math.PI / 2; @@ -302,20 +405,34 @@ function prepareGlbModel(scene) { function requestGlbModel(frame) { if (!frame?.glb_url || threeState.models[frame.id] || threeState.loading[frame.id]) return; threeState.loading[frame.id] = true; + const heavy = frame.heavy || /meshy/i.test(String(frame.glb || frame.glb_url || "")); + if (heavy) setStageLoad(`${t("loadingMesh")} (${frame.name})…`); initThreeRenderer().then((ok) => { if (!ok) { threeState.loading[frame.id] = false; + if (heavy) setStageLoad(null); return; } + const url = absUrl(frame.glb_url); threeState.loader.load( - frame.glb_url, + url, (gltf) => { threeState.models[frame.id] = prepareGlbModel(gltf.scene); threeState.loading[frame.id] = false; + if (heavy) setStageLoad(null); + if (hintEl && heavy) { + hintEl.textContent = (hintEl.textContent || "") + " · 3D mesh ready"; + } + }, + (ev) => { + if (!heavy || !ev?.total) return; + const pct = Math.min(99, Math.round((100 * ev.loaded) / ev.total)); + setStageLoad(`${t("loadingMesh")} ${pct}%…`); }, - undefined, - () => { + (err) => { + console.warn("GLB load failed", frame.id, err); threeState.loading[frame.id] = false; + if (heavy) setStageLoad(null); }, ); }); @@ -330,10 +447,6 @@ function clearThreeFrame() { if (threeState.renderer) threeState.renderer.clear(); } -/** - * Render GLB into offscreen WebGL layer, then composite onto main 2D canvas - * so face + glasses stay pixel-aligned (true AR overlay) and snapshots include GLB. - */ function drawThreeFrame(frame, centerX, centerY, angle, overlayW, overlayH) { if (!frame?.glb_url || compareMode) return false; const model = threeState.models[frame.id]; @@ -348,7 +461,6 @@ function drawThreeFrame(frame, centerX, centerY, angle, overlayW, overlayH) { }); const size = model.userData.size || { x: 1, y: 1, z: 1 }; - // Fit primarily by frame width (IPD-scaled overlayW); cap height so thick meshes don't explode const scaleW = overlayW / Math.max(size.x, 0.01); const scaleH = (overlayH * 1.35) / Math.max(size.y, 0.01); const scale = Math.min(scaleW, scaleH) * 0.98; @@ -361,12 +473,10 @@ function drawThreeFrame(frame, centerX, centerY, angle, overlayW, overlayH) { canvas.height / 2 - centerY + yBias, 0, ); - // Front-facing glasses in orthographic AR (Z out of screen) model.rotation.set(Number(ar.pitch) || 0.08, Number(ar.yaw) || 0, -angle); model.scale.setScalar(scale * scaleBias); model.visible = true; - // Offscreen buffer only — never show separate CSS layer (avoids misalignment) threeState.layer.hidden = true; threeState.layer.style.display = "none"; threeState.renderer.setClearColor(0x000000, 0); @@ -380,7 +490,6 @@ function drawThreeFrame(frame, centerX, centerY, angle, overlayW, overlayH) { return true; } -/** Draw image cover-fit into canvas; return draw rect for landmark mapping */ function drawImageCover(img, w, h) { const ir = img.width / img.height; const cr = w / h; @@ -401,6 +510,10 @@ function drawImageCover(img, w, h) { } async function api(path, opts) { + const S = globalThis.BeeARStatic; + if (S && S.detectStatic()) { + return S.staticApi(path, opts); + } const r = await fetch(path, { headers: { "Content-Type": "application/json" }, ...opts, @@ -410,7 +523,12 @@ async function api(path, opts) { } async function ensureSession() { - if (sessionId) return sessionId; + // If consent is false, intercept and issue a strict mock fallback session ID + if (!photoConsent) { + sessionId = "local-session"; + return sessionId; + } + if (sessionId && sessionId !== "local-session") return sessionId; try { const s = await api("/api/sessions", { method: "POST", @@ -425,14 +543,27 @@ async function ensureSession() { async function loadCatalog(category = "") { const q = category ? `?category=${encodeURIComponent(category)}` : ""; - const data = await api("/api/catalog" + q); - frames = data.frames || []; - renderCatalog(); - if (!selected && frames[0]) selectFrame(frames[0].id, "A"); + setStageLoad(t("loadingCatalog")); + try { + const data = await api("/api/catalog" + q); + frames = data.frames || []; + renderCatalog(); + if (!selected) { + const first = pickDefaultFrame(frames); + if (first) selectFrame(first.id, "A"); + } + } finally { + // Keep overlay only if a heavy GLB is still downloading + if (!Object.values(threeState.loading).some(Boolean)) setStageLoad(null); + } } function renderCatalog() { catalogEl.innerHTML = ""; + if (frames.length === 0) { + catalogEl.innerHTML = `

${t("emptyCatalog")}

${t("emptyCatalogHint")}

`; + return; + } frames.forEach((f) => { const el = document.createElement("div"); let cls = "sku"; @@ -468,8 +599,10 @@ async function selectFrame(id, slot = "A") { } } if (f.glb_url) requestGlbModel(f); + const sid = await ensureSession(); - if (sid && slot === "A") { + // Ensure network sync logic runs ONLY if we have a valid server session + if (sid && sid !== "local-session" && slot === "A") { try { await api(`/api/sessions/${sid}/wishlist`, { method: "POST", @@ -498,7 +631,6 @@ function drawDemoFace(w, h) { if (img && img.complete && img.naturalWidth) { const { dx, dy, dw, dh } = drawImageCover(img, w, h); - // Map image-normalized eye landmarks → canvas-normalized const lx = dx + def.left[0] * dw; const ly = dy + def.left[1] * dh; const rx = dx + def.right[0] * dw; @@ -509,7 +641,6 @@ function drawDemoFace(w, h) { return; } - // Fallback cartoon only if photos failed to load const cx = w / 2, cy = h * 0.48; const rx = w * 0.22, @@ -549,10 +680,14 @@ function drawGlbOverlay(frame) { return drawThreeFrame(frame, center.x, center.y, metrics.angle, overlayW, overlayH); } -/** Delegate 2D overlay drawing to @beear/tryon lib, with optional GLB overlay first. */ function drawGlasses() { if (!TryOn) return; - if (selected?.glb_url && drawGlbOverlay(selected)) return; + // Light procedural GLBs composite as dark blocks on the photo path — use crisp SVG/canvas + // overlay for them. Reserve WebGL GLB composite for heavy/photoreal Meshy meshes. + const preferGlb = + selected?.glb_url && + (selected.heavy === true || /meshy/i.test(String(selected.glb || selected.id || ""))); + if (preferGlb && drawGlbOverlay(selected)) return; TryOn.drawGlassesOverlay(ctx, face, selected, selectedB, compareMode, pdMm); } @@ -619,7 +754,6 @@ function initMediaPipe() { faceMesh.onResults((results) => { const lm = results.multiFaceLandmarks && results.multiFaceLandmarks[0]; if (!lm) return; - // outer eye corners face.left = [lm[33].x, lm[33].y]; face.right = [lm[263].x, lm[263].y]; face.source = "mediapipe"; @@ -637,6 +771,9 @@ function initMediaPipe() { } async function startCamera() { + // Sync the inline UI modal checkbox state with current runtime preference + const cb = document.getElementById("photo-consent-cb"); + if (cb) cb.checked = photoConsent; consentEl.classList.remove("hidden"); } @@ -703,7 +840,7 @@ function saveGallery(items) { localStorage.setItem(galleryKey(), JSON.stringify(items.slice(0, 24))); } -function snapshot() { +async function snapshot() { const data = canvas.toDataURL("image/png"); const item = { id: Date.now(), @@ -712,9 +849,27 @@ function snapshot() { pdMm, data, }; + + // Local-only save pathway runs regardless of sync permission const items = loadGallery(); items.unshift(item); saveGallery(items); + + // Cloud processing backup path triggered strictly when permission is granted + if (photoConsent) { + const sid = await ensureSession(); + if (sid && sid !== "local-session") { + try { + await api(`/api/sessions/${sid}/snapshot`, { + method: "POST", + body: JSON.stringify({ image: data }), + }); + } catch (err) { + console.warn("Cloud infrastructure sync error / omitted pathway:", err); + } + } + } + const a = document.createElement("a"); a.download = `beear-${selected?.id || "shot"}-${item.id}.png`; a.href = data; @@ -725,7 +880,7 @@ function snapshot() { function renderGallery() { const items = loadGallery(); if (!items.length) { - galleryEl.innerHTML = '

No snapshots yet

'; + galleryEl.innerHTML = `

${t("emptyGallery")}

`; return; } galleryEl.innerHTML = items @@ -743,7 +898,7 @@ function renderGallery() { }); } -// UI wiring +// UI element event binding configuration document.getElementById("btn-cam").onclick = startCamera; document.getElementById("btn-demo").onclick = startDemo; const btnDemoNext = document.getElementById("btn-demo-next"); @@ -751,9 +906,27 @@ if (btnDemoNext) btnDemoNext.onclick = nextDemoFace; document.getElementById("btn-snap").onclick = snapshot; document.getElementById("btn-lang").onclick = () => { lang = lang === "en" ? "vi" : "en"; + persistLang(); applyLang(); + renderCatalog(); + if (!galleryEl.classList.contains("hidden")) renderGallery(); updateMeta(); }; + +// Interactive Contextual Toggle Button Logic +const btnCloudEl = document.getElementById("btn-cloud"); +if (btnCloudEl) { + btnCloudEl.onclick = () => { + photoConsent = !photoConsent; + try { + localStorage.setItem("beear_photo_consent", photoConsent); + } catch (_) {} + if (!photoConsent) sessionId = null; + applyLang(); + updateMeta(); + }; +} + document.getElementById("btn-compare").onclick = () => { compareMode = !compareMode; document.getElementById("btn-compare").classList.toggle("active", compareMode); @@ -770,18 +943,38 @@ document.getElementById("pd").oninput = (e) => { document.getElementById("pd-val").textContent = String(pdMm); updateMeta(); }; -document.getElementById("consent-ok").onclick = startCameraAfterConsent; + +// Update interaction execution pipeline on primary modal acknowledgement +document.getElementById("consent-ok").onclick = () => { + const cb = document.getElementById("photo-consent-cb"); + photoConsent = cb ? cb.checked : false; + localStorage.setItem("beear_photo_consent", photoConsent); + applyLang(); + startCameraAfterConsent(); +}; + document.getElementById("consent-demo").onclick = startDemo; +setStageLoad(t("starting")); applyLang(); -Promise.all([loadDemoImages(), loadCatalog().catch((e) => { - hintEl.textContent = "API error: " + e.message + " — is beear serve running?"; -})]) +Promise.all([ + loadDemoImages(), + loadCatalog().catch((e) => { + const msg = isStaticDemo() + ? "Catalog error: " + e.message + : "API error: " + e.message + " — is beear serve running?"; + hintEl.textContent = msg; + setStageLoad(msg); + }), +]) .then(() => { startDemo(); loop(); + if (!Object.values(threeState.loading).some(Boolean)) setStageLoad(null); }) - .catch(() => { + .catch((e) => { + console.error(e); startDemo(); loop(); - }); + setStageLoad(null); + }); \ No newline at end of file diff --git a/packages/android/beear-webview/src/main/assets/beear/assets/static-api.js b/packages/android/beear-webview/src/main/assets/beear/assets/static-api.js new file mode 100644 index 0000000..d7a70df --- /dev/null +++ b/packages/android/beear-webview/src/main/assets/beear/assets/static-api.js @@ -0,0 +1,105 @@ +/** + * Static / GitHub Pages catalog shim. + * When window.__BEEAR_STATIC__ is true (or hostname is *.github.io), + * /api/catalog* is served from ./catalog/frames.enriched.json. + */ +(function (global) { + "use strict"; + + function detectStatic() { + if (global.__BEEAR_STATIC__ === true) return true; + if (global.__BEEAR_STATIC__ === false) return false; + try { + const host = location.hostname || ""; + if (host.endsWith("github.io")) return true; + if (document.documentElement && document.documentElement.dataset.static === "1") return true; + } catch (_) {} + return false; + } + + let cache = null; + let base = "."; + + function setBase(b) { + base = String(b || ".").replace(/\/$/, "") || "."; + } + + function joinBase(rel) { + const b = String(base || ".").replace(/\/+$/, "") || "."; + const r = String(rel || "").replace(/^\.?\//, ""); + return b + "/" + r; + } + + async function loadCatalog() { + if (cache) return cache; + // Use joinBase so path rewrites for GitHub Pages never break this URL. + const url = joinBase("catalog/frames.enriched.json"); + const r = await fetch(url, { cache: "no-cache" }); + if (!r.ok) throw new Error("static catalog HTTP " + r.status + " " + url); + cache = await r.json(); + return cache; + } + + function filterFrames(frames, searchParams) { + let out = frames.slice(); + const cat = searchParams.get("category"); + const style = searchParams.get("style"); + if (cat) out = out.filter((f) => String(f.category || "") === cat); + if (style) out = out.filter((f) => String(f.style || "") === style); + return out; + } + + /** + * @param {string} path - absolute path like /api/catalog or /api/catalog/meta + * @param {RequestInit} [opts] + */ + async function staticApi(path, opts) { + const method = ((opts && opts.method) || "GET").toUpperCase(); + // Soft-mock sessions / wishlist on static demos + if (path.startsWith("/api/sessions")) { + if (method === "POST" && path === "/api/sessions") { + return { id: "static-demo", frame_ids: [], wishlist: [], note: "static" }; + } + if (path.includes("/wishlist")) { + return { ok: true, static: true }; + } + return { id: "static-demo", ok: true, static: true }; + } + + const cat = await loadCatalog(); + const u = new URL(path, "https://beear.local"); + if (u.pathname === "/api/catalog/meta") { + return { + version: cat.version, + person_models: cat.person_models || [], + glb_count: cat.glb_count || 0, + person_count: cat.person_count || 0, + frames: (cat.frames || []).length, + }; + } + const single = u.pathname.match(/^\/api\/catalog\/([^/]+)$/); + if (single) { + const id = decodeURIComponent(single[1]); + const f = (cat.frames || []).find((x) => x.id === id); + if (!f) throw new Error("frame not found: " + id); + return f; + } + if (u.pathname === "/api/catalog") { + return { + version: cat.version, + frames: filterFrames(cat.frames || [], u.searchParams), + person_models: cat.person_models || [], + glb_count: cat.glb_count || 0, + }; + } + throw new Error("static mode: unsupported API " + path); + } + + global.BeeARStatic = { + detectStatic, + setBase, + loadCatalog, + staticApi, + isStatic: detectStatic, + }; +})(typeof globalThis !== "undefined" ? globalThis : window); diff --git a/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.css b/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.css index cb74963..da54b78 100644 --- a/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.css +++ b/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.css @@ -48,6 +48,20 @@ html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text); background: #5b8cff33; color: #9ec1ff; border: 1px solid #5b8cff66; padding: 5px 10px; border-radius: 999px; } +.stage-load { + position: absolute; inset: 0; display: flex; flex-direction: column; + align-items: center; justify-content: center; gap: 12px; + background: #060910cc; color: var(--text); font-size: 14px; font-weight: 600; + z-index: 5; pointer-events: none; transition: opacity 0.25s ease; + text-align: center; padding: 16px; +} +.stage-load.hidden { opacity: 0; visibility: hidden; } +.stage-load-spin { + width: 40px; height: 40px; border-radius: 50%; + border: 3px solid #243049; border-top-color: var(--accent2); + animation: beear-spin 0.8s linear infinite; +} +@keyframes beear-spin { to { transform: rotate(360deg); } } .rail { border-left: 1px solid var(--line); background: var(--panel); display: flex; flex-direction: column; min-height: 0; diff --git a/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.js b/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.js index d48a978..b7857ae 100644 --- a/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.js +++ b/packages/android/beear-webview/src/main/assets/beear/assets/studio3d.js @@ -16,9 +16,104 @@ const yVal = document.getElementById("y-val"); const zVal = document.getElementById("z-val"); const btnAuto = document.getElementById("btn-auto"); const btnSnap = document.getElementById("btn-snap"); +const btnLang = document.getElementById("btn-lang"); const filterEl = document.getElementById("filter"); const personSelect = document.getElementById("person-select"); +const I18N = { + en: { + tagline: "Real 3D characters · glasses GLB try-on", + back2d: "← 2D / Camera", + autoOn: "Auto-rotate ON", + autoOff: "Auto-rotate OFF", + snap: "Snapshot", + hint: "Drag to orbit · scroll to zoom · pick person + frames", + badge: "3D try-on · GLB glasses", + person: "Person", + character: "Character", + frames: "Frames (3D)", + filterGlasses: "Glasses", + filterAll: "All", + scale: "Fit scale", + yOffset: "Y offset", + zDepth: "Z (depth)", + loadingCatalog: "Loading catalog…", + starting: "Starting 3D studio…", + metaOn: "on", + metaLocked: "3D locked", + }, + vi: { + tagline: "Nhân vật 3D · thử kính GLB", + back2d: "← 2D / Camera", + autoOn: "Tự xoay BẬT", + autoOff: "Tự xoay TẮT", + snap: "Chụp", + hint: "Kéo để xoay · cuộn để zoom · chọn người + kính", + badge: "Thử 3D · kính GLB", + person: "Người", + character: "Nhân vật", + frames: "Khung (3D)", + filterGlasses: "Kính", + filterAll: "Tất cả", + scale: "Tỷ lệ vừa", + yOffset: "Lệch Y", + zDepth: "Z (độ sâu)", + loadingCatalog: "Đang tải danh mục…", + starting: "Đang khởi động studio 3D…", + metaOn: "trên", + metaLocked: "3D khóa", + }, +}; + +let lang = "en"; +try { + const saved = localStorage.getItem("beear_lang"); + if (saved === "en" || saved === "vi") lang = saved; +} catch (_) {} + +function t(k) { + return (I18N[lang] || I18N.en)[k] || k; +} + +function persistLang() { + try { + localStorage.setItem("beear_lang", lang); + } catch (_) {} +} + +function applyLang() { + document.documentElement.lang = lang; + const tag = document.getElementById("tagline"); + if (tag) tag.textContent = t("tagline"); + const back = document.getElementById("btn-2d"); + if (back) back.textContent = t("back2d"); + if (btnLang) btnLang.textContent = lang === "en" ? "VI" : "EN"; + if (btnSnap) btnSnap.textContent = t("snap"); + if (btnAuto) btnAuto.textContent = (typeof state !== "undefined" && state.auto) ? t("autoOn") : t("autoOff"); + const hint = document.getElementById("hint"); + if (hint) hint.textContent = t("hint"); + const badge = document.getElementById("badge"); + if (badge) badge.textContent = t("badge"); + const lp = document.getElementById("lbl-person"); + if (lp) lp.textContent = t("person"); + const lc = document.getElementById("lbl-character"); + if (lc && lc.childNodes[0]) lc.childNodes[0].textContent = t("character") + " "; + const lf = document.getElementById("lbl-frames"); + if (lf) lf.textContent = t("frames"); + if (filterEl) { + for (const opt of filterEl.options) { + if (opt.value === "glasses") opt.textContent = t("filterGlasses"); + else if (opt.value === "") opt.textContent = t("filterAll"); + } + } + const ls = document.getElementById("lbl-scale"); + if (ls && ls.childNodes[0]) ls.childNodes[0].textContent = t("scale") + " "; + const ly = document.getElementById("lbl-y"); + if (ly && ly.childNodes[0]) ly.childNodes[0].textContent = t("yOffset") + " "; + const lz = document.getElementById("lbl-z"); + if (lz && lz.childNodes[0]) lz.childNodes[0].textContent = t("zDepth") + " "; +} + const state = { frames: [], selected: null, @@ -111,7 +206,42 @@ function resize() { } window.addEventListener("resize", resize); +function isStaticDemo() { + return !!(globalThis.BeeARStatic && globalThis.BeeARStatic.detectStatic()); +} + +function absUrl(u) { + if (!u) return u; + try { + return new URL(u, location.href).href; + } catch (_) { + return u; + } +} + +function setStageLoad(msg) { + const el = document.getElementById("stage-load"); + const tx = document.getElementById("stage-load-text"); + if (!el) return; + if (msg == null) { + el.classList.add("hidden"); + return; + } + if (tx) tx.textContent = msg; + el.classList.remove("hidden"); +} + +function isHeavyAsset(meta) { + if (!meta) return false; + if (meta.heavy === true) return true; + return /meshy|person_female|person_male/i.test(String(meta.glb || meta.glb_url || meta.id || "")); +} + async function api(path) { + const S = globalThis.BeeARStatic; + if (S && S.detectStatic()) { + return S.staticApi(path); + } const r = await fetch(path); if (!r.ok) throw new Error(await r.text()); return r.json(); @@ -242,8 +372,14 @@ function loadPerson(personMeta) { const meta = personMeta || state.activePerson; if (!meta) return Promise.resolve(false); state.activePerson = meta; - const url = meta.glb_url || `/catalog/glb/${meta.glb}`; + const url = absUrl(meta.glb_url || `./catalog/glb/${meta.glb}`); + const heavy = isHeavyAsset(meta); metaEl.textContent = `Loading ${meta.name}…`; + setStageLoad( + heavy + ? `Loading ${meta.name}… (~14MB full-body — first load is slow on Pages)` + : `Loading ${meta.name}…`, + ); const mount = (source) => { clearPerson(); @@ -258,6 +394,7 @@ function loadPerson(personMeta) { state.person = inst; placePerson(inst, meta); metaEl.textContent = `${meta.name} ready · pick glasses`; + setStageLoad(null); if (state.selected) loadGlasses(state.selected); }; @@ -275,10 +412,16 @@ function loadPerson(personMeta) { mount(model); resolve(true); }, - undefined, + (ev) => { + if (!ev?.total) return; + const pct = Math.min(99, Math.round((100 * ev.loaded) / ev.total)); + setStageLoad(`Loading ${meta.name}… ${pct}%`); + metaEl.textContent = `Loading ${meta.name}… ${pct}%`; + }, (err) => { console.error(err); - metaEl.textContent = `Failed to load person: ${meta.glb}`; + metaEl.textContent = `Failed to load person: ${meta.glb} (${url})`; + setStageLoad(`Failed: ${meta.glb}`); resolve(false); }, ); @@ -290,14 +433,16 @@ function loadGlasses(frame) { metaEl.textContent = `${frame?.name || "Frame"} has no GLB`; return; } - const url = frame.glb_url || `/catalog/glb/${frame.glb}`; + const url = absUrl(frame.glb_url || `./catalog/glb/${frame.glb}`); if (state.glassesCache[frame.id]) { attachGlasses(state.glassesCache[frame.id], frame); return; } if (state.loading[frame.id]) return; state.loading[frame.id] = true; + const heavy = isHeavyAsset(frame); metaEl.textContent = `Loading glasses ${frame.name}…`; + if (heavy) setStageLoad(`Loading glasses ${frame.name}… (~11MB HD mesh)`); loader.load( url, (gltf) => { @@ -306,12 +451,18 @@ function loadGlasses(frame) { state.glassesCache[frame.id] = model; state.loading[frame.id] = false; attachGlasses(model, frame); + if (heavy) setStageLoad(null); + }, + (ev) => { + if (!heavy || !ev?.total) return; + const pct = Math.min(99, Math.round((100 * ev.loaded) / ev.total)); + setStageLoad(`Loading glasses ${frame.name}… ${pct}%`); }, - undefined, (err) => { console.error(err); state.loading[frame.id] = false; metaEl.textContent = `GLB failed: ${frame.glb || frame.id}`; + if (heavy) setStageLoad(`GLB failed: ${frame.glb || frame.id}`); }, ); } @@ -344,7 +495,7 @@ function attachGlasses(model, frame) { } applyFit(); const personName = state.activePerson?.name || "person"; - metaEl.textContent = `${frame.name} on ${personName} · scale ${state.fitScale.toFixed(2)} · 3D locked`; + metaEl.textContent = `${frame.name} ${t("metaOn")} ${personName} · scale ${state.fitScale.toFixed(2)} · ${t("metaLocked")}`; } function applyFit() { @@ -419,6 +570,7 @@ function selectFrame(id) { } async function loadCatalog() { + setStageLoad(t("loadingCatalog")); const data = await api("/api/catalog"); state.frames = (data.frames || []).filter((f) => f.glb_url || f.glb || f.has_glb); if (!state.frames.length) state.frames = data.frames || []; @@ -430,9 +582,11 @@ async function loadCatalog() { } renderPersonSelect(); renderCatalog(); + // Prefer light featured frames on Pages; Meshy HD is opt-in (~11MB) const first = + state.frames.find((f) => f.featured && (f.glb_url || f.glb) && !isHeavyAsset(f)) || + state.frames.find((f) => (f.glb_url || f.glb) && !isHeavyAsset(f)) || state.frames.find((f) => f.featured && (f.glb_url || f.glb)) || - state.frames.find((f) => (f.id || "").includes("meshy") && (f.glb_url || f.glb)) || state.frames.find((f) => f.glb_url || f.glb) || state.frames[0]; if (first) selectFrame(first.id); @@ -462,8 +616,19 @@ filterEl.addEventListener("change", renderCatalog); btnAuto.addEventListener("click", () => { state.auto = !state.auto; btnAuto.classList.toggle("active", state.auto); - btnAuto.textContent = state.auto ? "Auto-rotate ON" : "Auto-rotate OFF"; + btnAuto.textContent = state.auto ? t("autoOn") : t("autoOff"); }); +if (btnLang) { + btnLang.addEventListener("click", () => { + lang = lang === "en" ? "vi" : "en"; + persistLang(); + applyLang(); + if (state.selected) { + const personName = state.activePerson?.name || "person"; + metaEl.textContent = `${state.selected.name} ${t("metaOn")} ${personName} · scale ${state.fitScale.toFixed(2)} · ${t("metaLocked")}`; + } + }); +} btnSnap.addEventListener("click", () => { const a = document.createElement("a"); a.download = `beear-3d-${state.activePerson?.id || "person"}-${state.selected?.id || "snap"}.png`; @@ -487,20 +652,27 @@ async function main() { resize(); ensureGlassesRoot(); btnAuto.classList.add("active"); - btnAuto.textContent = "Auto-rotate ON"; + applyLang(); + setStageLoad(t("starting")); await loadCatalog(); + // Default bust (tiny GLB) for fast Pages first paint; full-body Meshy still in dropdown const def = state.personModels.find((p) => p.default) || + state.personModels.find((p) => p.id === "person_bust") || + state.personModels.find((p) => !isHeavyAsset(p)) || state.personModels.find((p) => p.id === "person_female") || state.personModels[0]; if (def) { personSelect.value = def.id; await loadPerson(def); + } else { + setStageLoad(null); } requestAnimationFrame(tick); } main().catch((e) => { metaEl.textContent = String(e); + setStageLoad(String(e?.message || e)); console.error(e); }); diff --git a/packages/android/beear-webview/src/main/assets/beear/index.html b/packages/android/beear-webview/src/main/assets/beear/index.html index d83d0ce..0c109de 100644 --- a/packages/android/beear-webview/src/main/assets/beear/index.html +++ b/packages/android/beear-webview/src/main/assets/beear/index.html @@ -3,7 +3,11 @@ - BeeAR — Try on glasses + BeeAR — Virtual try-on demo + + + + @@ -11,6 +15,15 @@
+ GitHub 🧑 3D Person + + @@ -45,6 +61,10 @@
+
+
+ Loading catalog… +
Start camera or use Demo face · pick a frame
tracking: geometric
@@ -72,6 +92,7 @@

Catalog

+ @@ -85,4 +106,4 @@

Catalog

- + \ No newline at end of file diff --git a/packages/android/beear-webview/src/main/assets/beear/studio3d.html b/packages/android/beear-webview/src/main/assets/beear/studio3d.html index d17278d..513e975 100644 --- a/packages/android/beear-webview/src/main/assets/beear/studio3d.html +++ b/packages/android/beear-webview/src/main/assets/beear/studio3d.html @@ -4,7 +4,10 @@ BeeAR — 3D Person Try-On + + +
@@ -13,11 +16,13 @@
BeeAR 3D Studio -
Real 3D characters · glasses GLB try-on
+
Real 3D characters · glasses GLB try-on
- ← 2D / Camera + GitHub + ← 2D / Camera +
@@ -25,35 +30,39 @@
+
+
+ Loading 3D studio… +
Drag to orbit · scroll to zoom · pick person + frames
-
Meshy characters · GLB glasses
+
3D try-on · GLB glasses
GitHub - ← 2D / Camera + ← 2D / Camera +
@@ -38,30 +39,30 @@