@polderlabs/bizar 5.4.0 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bizar-dash/dist/assets/{EnvVarsSection-DqvB8ZcJ.js → EnvVarsSection-DhLHZ5ua.js} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsSection-DqvB8ZcJ.js.map → EnvVarsSection-DhLHZ5ua.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-DRXP2ERQ.js → MobileChat-9UpvDP6-.js} +1 -1
- package/bizar-dash/dist/assets/{MobileChat-DRXP2ERQ.js.map → MobileChat-9UpvDP6-.js.map} +1 -1
- package/bizar-dash/dist/assets/{MobileSettings-CVW9VhEo.js → MobileSettings-BFmN9ZWR.js} +1 -1
- package/bizar-dash/dist/assets/{MobileSettings-CVW9VhEo.js.map → MobileSettings-BFmN9ZWR.js.map} +1 -1
- package/bizar-dash/dist/assets/{Toast-CcZVVN4i.js → Toast-BlcKl_dN.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-CcZVVN4i.js.map → Toast-BlcKl_dN.js.map} +1 -1
- package/bizar-dash/dist/assets/{icons-OMEGx5KA.js → icons-CMPxNV8t.js} +124 -129
- package/bizar-dash/dist/assets/icons-CMPxNV8t.js.map +1 -0
- package/bizar-dash/dist/assets/{main-CMAGZouj.js → main-Dhrvp9Gt.js} +1 -1
- package/bizar-dash/dist/assets/{main-CMAGZouj.js.map → main-Dhrvp9Gt.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-DDY1lquG.js +1 -0
- package/bizar-dash/dist/assets/{mobile-XN2P7YfM.js.map → mobile-DDY1lquG.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-BjkoHh1W.js +2 -0
- package/bizar-dash/dist/assets/mobile-layout-BjkoHh1W.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-layout-D6V-7WnA.css → mobile-layout-CJnZNLy3.css} +1 -1
- package/bizar-dash/dist/assets/{useSlashCommands-iyoW95UB.js → useSlashCommands-eFCiWHVt.js} +2 -2
- package/bizar-dash/dist/assets/{useSlashCommands-iyoW95UB.js.map → useSlashCommands-eFCiWHVt.js.map} +1 -1
- package/bizar-dash/dist/index.html +7 -7
- package/bizar-dash/dist/mobile.html +4 -4
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/web/MobileApp.tsx +1 -10
- package/bizar-dash/src/web/styles/mobile-layout.css +7 -5
- package/bizar-dash/src/web/styles/settings.css +46 -0
- package/bizar-dash/tests/mobile-layout.test.tsx +36 -0
- package/package.json +1 -1
- package/bizar-dash/dist/assets/icons-OMEGx5KA.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-XN2P7YfM.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-DqTxvXZp.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-DqTxvXZp.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":";ksCAEO,SAASA,KAAMC,EAAsD,CAC1E,OAAOA,EAAM,OAAO,OAAO,EAAE,KAAK,GAAG,CACvC,CAGO,SAASC,EAAeC,EAAuD,CACpF,GAAI,CAACA,GAAMA,IAAO,EAAG,MAAO,GAC5B,MAAM,EAAI,OAAOA,GAAO,SAAWA,EAAK,IAAI,KAAKA,CAAE,EAAE,UACrD,GAAI,OAAO,MAAM,CAAC,EAAG,MAAO,GAC5B,MAAMC,EAAO,KAAK,MAAQ,EAE1B,OADIA,EAAO,GACPA,EAAO,IAAe,WACtBA,EAAO,KAAkB,GAAG,KAAK,MAAMA,EAAO,GAAM,CAAC,QACrDA,EAAO,MAAmB,GAAG,KAAK,MAAMA,EAAO,IAAS,CAAC,QACzDA,EAAO,EAAI,MAAmB,GAAG,KAAK,MAAMA,EAAO,KAAU,CAAC,QAC3D,IAAI,KAAK,CAAC,EAAE,oBACrB,CAGO,SAASC,GAAWF,EAAuD,CAChF,GAAI,CAACA,EAAI,MAAO,GAChB,MAAMG,EAAI,OAAOH,GAAO,SAAW,IAAI,KAAKA,CAAE,EAAI,IAAI,KAAKA,CAAE,EAC7D,OAAI,OAAO,MAAMG,EAAE,SAAS,EAAU,GAC/BA,EAAE,gBACX,CAGO,SAASC,GAAS,EAA8BC,EAAM,IAAa,CACxE,OAAK,EACE,EAAE,OAASA,EAAM,EAAE,MAAM,EAAGA,CAAG,EAAI,IAAM,EADjC,EAEjB,CAeO,MAAMC,GAAyC,CACpD,IAAK,kBACL,OAAQ,cACR,KAAM,cACR,ECxBMC,EAAY,mBACZC,GAAe,yBAEd,MAAMC,WAAiB,KAAM,CAGlC,YAAYC,EAAiBC,EAAgBC,EAAgB,CAC3D,MAAMF,CAAO,EAHfG,EAAA,eACAA,EAAA,aAGE,KAAK,KAAO,WACZ,KAAK,OAASF,EACd,KAAK,KAAOC,CACd,CACF,CAEA,MAAME,EAAU,CAAhB,cACED,EAAA,YAAO,QAGP,UAAmB,CACjB,GAAI,CACF,OAAO,aAAa,QAAQN,CAAS,GAAK,EAC5C,MAAQ,CACN,MAAO,EACT,CACF,CAGA,SAASQ,EAAqB,CAC5B,GAAI,CACEA,EAAO,aAAa,QAAQR,EAAWQ,CAAK,EAC3C,aAAa,WAAWR,CAAS,CACxC,MAAQ,CAER,CACF,CAYA,oBAA8B,CAC5B,GAAI,CACF,MAAMS,EAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM,EACnDC,EAAMD,EAAO,IAAI,OAAO,EAC9B,GAAI,CAACC,EAAK,MAAO,GACjB,KAAK,SAASA,CAAG,EAEjBD,EAAO,OAAO,OAAO,EACrB,MAAME,EAAYF,EAAO,WACnBG,EAASD,EACX,GAAG,OAAO,SAAS,QAAQ,IAAIA,CAAS,GACxC,OAAO,SAAS,SACpB,eAAQ,aAAa,KAAM,GAAIC,CAAM,EAC9B,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAGA,aAAaC,EAAsB,CACjC,MAAMC,EAAM,KAAK,KAAOD,EAClBH,EAAM,KAAK,WACjB,GAAI,CAACA,EAAK,OAAOI,EACjB,MAAMC,EAAMD,EAAI,SAAS,GAAG,EAAI,IAAM,IACtC,MAAO,GAAGA,CAAG,GAAGC,CAAG,SAAS,mBAAmBL,CAAG,CAAC,EACrD,CAYA,MAAM,iBAAmF,CACvF,GAAI,CACF,MAAMM,EAAI,MAAM,KAAK,IAA4D,cAAc,EAC/F,GAAI,CAAE,aAAa,QAAQf,GAAce,EAAE,SAAW,IAAM,GAAG,CAAG,MAAQ,CAAa,CACvF,OAAOA,CACT,MAAQ,CACN,MAAO,CAAE,SAAU,GAAO,SAAU,GAAM,KAAM,GAClD,CACF,CAGA,mBAA6B,CAC3B,GAAI,CAAE,OAAO,aAAa,QAAQf,EAAY,IAAM,GAAK,MAAQ,CAAE,MAAO,EAAM,CAClF,CAaA,MAAM,IAAOY,EAAcI,EAAkC,CAC3D,OAAO,KAAK,IAAO,MAAOJ,EAAM,OAAWI,CAAM,CACnD,CAQA,MAAM,KAAQJ,EAAcK,EAAgBD,EAAkC,CAC5E,OAAO,KAAK,IAAO,OAAQJ,EAAMK,EAAMD,CAAM,CAC/C,CAQA,MAAM,IAAOJ,EAAcK,EAAgBD,EAAkC,CAC3E,OAAO,KAAK,IAAO,MAAOJ,EAAMK,EAAMD,CAAM,CAC9C,CAQA,MAAM,MAASJ,EAAcK,EAAgBD,EAAkC,CAC7E,OAAO,KAAK,IAAO,QAASJ,EAAMK,EAAMD,CAAM,CAChD,CAQA,MAAM,IAAiBJ,EAAcI,EAAkC,CACrE,OAAO,KAAK,IAAO,SAAUJ,EAAM,OAAWI,CAAM,CACtD,CAEA,MAAc,IAAOE,EAAgBN,EAAcK,EAAgBD,EAAkC,CACnG,MAAMG,EAAkC,CAAE,eAAgB,oBACpDV,EAAM,KAAK,WACbA,IAAKU,EAAQ,cAAgB,UAAUV,CAAG,IAC9C,MAAMW,EAAoB,CAAE,OAAAF,EAAQ,QAAAC,CAAA,EACVF,GAAS,OACjCG,EAAK,KAAO,OAAOH,GAAS,SAAWA,EAAO,KAAK,UAAUA,CAAI,GAE/DD,MAAa,OAASA,GAC1B,MAAMD,EAAI,MAAM,MAAM,KAAK,KAAOH,EAAMQ,CAAI,EAE5C,IADWL,EAAE,QAAQ,IAAI,cAAc,GAAK,IACrC,SAAS,kBAAkB,EAAG,CACnC,MAAMX,EAAO,MAAMW,EAAE,OACrB,GAAI,CAACA,EAAE,GAAI,CACT,MAAMM,GACHjB,GAAQ,OAAOA,GAAS,UAAY,YAAaA,EAC7CA,EAA8B,QAC/B,SAAc,GAAGc,CAAM,IAAIN,CAAI,KAAKG,EAAE,MAAM,GAClD,MAAM,IAAId,GAASoB,EAAKN,EAAE,OAAQX,CAAI,CACxC,CACA,OAAOA,CACT,CACA,MAAMkB,EAAO,MAAMP,EAAE,OACrB,GAAI,CAACA,EAAE,GACL,MAAM,IAAId,GACR,GAAGiB,CAAM,IAAIN,CAAI,KAAKG,EAAE,MAAM,MAAMO,EAAK,MAAM,EAAG,GAAG,CAAC,GACtDP,EAAE,OACFO,CAAA,EAGJ,GAAI,CACF,OAAO,KAAK,MAAMA,CAAI,CACxB,MAAQ,CACN,OAAOA,CACT,CACF,CACF,CAEO,MAAMC,EAAM,IAAIjB,GAKvBiB,EAAI,qBAUJ,eAAsBC,GAAcF,EAA+B,CACjE,OAAOA,CACT,CC1NA,SAASG,IAAqB,CAC5B,GAAI,OAAO,SAAa,IAAa,MAAO,oBAC5C,MAAMC,EAAO,GAAG,SAAS,WAAa,SAAW,MAAQ,IAAI,MAAM,SAAS,IAAI,MAChF,IAAIjB,EAAM,GACV,GAAI,CACFA,EAAM,aAAa,QAAQV,CAAS,GAAK,EAC3C,MAAQ,CAER,CACA,OAAKU,EACE,GAAGiB,CAAI,UAAU,mBAAmBjB,CAAG,CAAC,GAD9BiB,CAEnB,CAEO,MAAMC,EAAG,CAYd,YAAYd,EAAc,CAXTR,EAAA,oBACTA,EAAA,oBAAe,KACfA,EAAA,0BAAqB,KACrBA,EAAA,UAAuB,MACvBA,EAAA,sBAAiB,KACRA,EAAA,yBAAoB,MAC7BA,EAAA,sBAAuD,MACvDA,EAAA,eAAoB,cACpBA,EAAA,iBAAmD,MACnDA,EAAA,cAAS,IAGf,KAAK,YAAcQ,EACnB,KAAK,SACP,CAEA,GAAGe,EAA8B,CAC/B,YAAK,SAAS,IAAIA,CAAO,EAClB,IAAM,CACX,KAAK,SAAS,OAAOA,CAAO,CAC9B,CACF,CAEA,SAASA,EAAoC,CAC3C,YAAK,eAAe,IAAIA,CAAO,EAE/BA,EAAQ,KAAK,OAAO,EACb,IAAM,CACX,KAAK,eAAe,OAAOA,CAAO,CACpC,CACF,CAEA,IAAI,QAAmB,CACrB,OAAO,KAAK,OACd,CAEA,KAAKP,EAAuB,CAC1B,OAAI,KAAK,IAAM,KAAK,GAAG,aAAe,UAAU,MAC9C,KAAK,GAAG,KAAK,KAAK,UAAUA,CAAG,CAAC,EACzB,IAEF,EACT,CAEA,OAAc,OACZ,KAAK,OAAS,GACV,KAAK,gBAAgB,aAAa,KAAK,cAAc,EACzD,KAAK,eAAiB,KAClB,KAAK,WAAW,cAAc,KAAK,SAAS,EAChD,KAAK,UAAY,KACjB,GAAI,EACFQ,EAAA,KAAK,KAAL,MAAAA,EAAS,OACX,MAAQ,CAER,CACA,KAAK,GAAK,KACV,KAAK,UAAU,cAAc,CAC/B,CAEQ,SAAgB,CACtB,GAAI,KAAK,OAAQ,OACb,KAAK,iBACP,aAAa,KAAK,cAAc,EAChC,KAAK,eAAiB,MAExB,KAAK,UAAU,YAAY,EAC3B,MAAMhB,EAAM,KAAK,aAAeY,GAAA,EAChC,IAAIK,EACJ,GAAI,CAGFA,EAAS,IAAI,UAAUjB,CAAG,EAC1B,KAAK,GAAKiB,CACZ,OAASC,EAAK,CACZ,QAAQ,KAAK,yBAA0BA,CAAG,EAC1C,KAAK,oBACL,MACF,CACAD,EAAO,iBAAiB,OAAQ,IAAM,CAChC,KAAK,KAAOA,GAAU,KAAK,SAC/B,KAAK,eAAiB,IACtB,KAAK,UAAU,WAAW,EAEtB,KAAK,WAAW,cAAc,KAAK,SAAS,EAChD,KAAK,UAAY,YAAY,IAAM,CAC7B,KAAK,UAAY,aAAa,KAAK,KAAK,CAAE,KAAM,OAAQ,CAC9D,EAAG,GAAM,EACX,CAAC,EACDA,EAAO,iBAAiB,QAAS,IAAM,CACjC,KAAK,KAAOA,IAChB,KAAK,GAAK,KACV,KAAK,UAAU,cAAc,EACzB,KAAK,YACP,cAAc,KAAK,SAAS,EAC5B,KAAK,UAAY,MAEd,KAAK,QAAQ,KAAK,oBACzB,CAAC,EACDA,EAAO,iBAAiB,QAAS,IAAM,CACjC,KAAK,KAAOA,GAEhB,QAAQ,KAAK,YAAY,CAC3B,CAAC,EACDA,EAAO,iBAAiB,UAAYE,GAAM,CACxC,IAAIX,EACJ,GAAI,CACFA,EAAM,KAAK,MAAMW,EAAE,IAAI,CACzB,MAAQ,CACN,QAAQ,KAAK,kBAAkB,EAC/B,MACF,CACA,UAAWC,KAAK,KAAK,SACnB,GAAI,CACFA,EAAEZ,CAAG,CACP,OAASU,EAAK,CACZ,QAAQ,MAAM,sBAAuBA,CAAG,CAC1C,CAEJ,CAAC,CACH,CAEQ,mBAA0B,CAC5B,KAAK,QAAU,KAAK,iBACxB,KAAK,eAAiB,WAAW,IAAM,CACrC,KAAK,eAAiB,KACtB,KAAK,SACP,EAAG,KAAK,cAAc,EACtB,KAAK,eAAiB,KAAK,IACzB,KAAK,eAAiB,IACtB,KAAK,mBAET,CAEQ,UAAUG,EAAmB,CACnC,KAAK,QAAUA,EACf,UAAWD,KAAK,KAAK,eACnB,GAAI,CACFA,EAAEC,CAAC,CACL,OAASH,EAAK,CACZ,QAAQ,MAAM,6BAA8BA,CAAG,CACjD,CAEJ,CACF,2GC4lBO,SAASI,GAAWC,EAAwD,CACjF,MAAMC,EAAO,OAAOD,GAAc,SAAWA,EAAYA,EAAU,KAC7DE,EACJD,IAAS,SACL,OAAO,OAAW,KAClB,OAAO,WAAW,+BAA+B,EAAE,QACjD,QACA,OACFA,IAAS,QACP,QACA,OACR,OAAI,OAAO,SAAa,MAClBC,IAAa,QACf,SAAS,gBAAgB,aAAa,aAAc,OAAO,EAE3D,SAAS,gBAAgB,gBAAgB,YAAY,GAGlDA,CACT,CAGO,SAASC,GAAiBC,EAAsB,CACrD,GAAI,OAAO,SAAa,IAAa,OACrC,MAAMC,EAAO,SAAS,gBACtBA,EAAK,MAAM,YAAY,WAAYD,EAAM,MAAM,EAC/CC,EAAK,MAAM,YAAY,YAAaD,EAAM,OAAO,EACjDC,EAAK,MAAM,YAAY,YAAaD,EAAM,OAAO,EACjDC,EAAK,MAAM,YAAY,UAAWD,EAAM,KAAK,EAC7CC,EAAK,MAAM,YAAY,SAAUD,EAAM,IAAI,EAE3CC,EAAK,MAAM,YAAY,cAAeC,GAAUF,EAAM,OAAQ,GAAI,CAAC,EACnEC,EAAK,MAAM,YAAY,kBAAmBC,GAAUF,EAAM,OAAQ,EAAG,CAAC,EAClEA,EAAM,YACRC,EAAK,MAAM,YAAY,cAAe,IAAID,EAAM,UAAU,6DAA6D,EAErHA,EAAM,UACRC,EAAK,MAAM,YAAY,mBAAoB,GAAGD,EAAM,QAAQ,IAAI,EAElEC,EAAK,QAAQ,YAAcD,EAAM,YAAc,OAAS,QACxDC,EAAK,QAAQ,WAAaD,EAAM,WAAa,OAAS,OACxD,CAEA,SAASE,GAAUC,EAAaC,EAAe,CAC7C,MAAMC,EAAI,qBAAqB,KAAKF,GAAO,EAAE,EAC7C,GAAI,CAACE,EAAG,MAAO,sBAAsBD,CAAK,IAC1C,MAAME,EAAI,SAASD,EAAE,CAAC,EAAG,EAAE,EACrB9B,EAAK+B,GAAK,GAAM,IAChBC,EAAKD,GAAK,EAAK,IACf,EAAIA,EAAI,IACd,MAAO,QAAQ/B,CAAC,KAAKgC,CAAC,KAAK,CAAC,KAAKH,CAAK,GACxC,CCpzBO,SAASI,GAAQ,CAAE,KAAAC,EAAO,KAAM,UAAAC,EAAW,MAAAC,GAAuB,CACvE,OACEC,MAAC,QACC,UAAW/D,EAAG,UAAW,WAAW4D,CAAI,GAAIC,CAAS,EACrD,KAAK,SACL,aAAYC,GAAS,WAG3B,k/BCNO,SAASE,GAAa,CAAE,YAAAC,EAAa,SAAAC,EAAU,UAAAC,EAAW,WAAAC,EAAY,SAAAC,GAAmB,CAC9F,cACG,UAAO,UAAWrE,EAAG,gBAAiBkE,GAAY,aAAa,EAC9D,UAAAH,MAAC,UACC,QAASE,EACT,UAAU,kBACV,aAAW,YACX,KAAK,SAEL,SAAAF,MAACO,GAAA,CAAK,KAAM,GAAI,UAGjB,MAAG,UAAU,sBACX,SAAAH,IAAc,WAAa,WAAa,QAC3C,EAECC,EACCL,MAAC,UACC,QAASK,EACT,UAAU,uBACV,aAAW,gBACX,KAAK,SAEL,SAAAL,MAACQ,EAAA,CAAE,KAAM,GAAI,IAGfR,MAAC,UACC,QAASM,EACT,UAAU,oBACV,aAAW,SACX,KAAK,SAEL,SAAAN,MAACS,GAAA,CAAO,KAAM,GAAI,GACpB,EAEJ,CAEJ,CC9BO,SAASC,GAAa,CAAE,KAAAC,EAAM,QAAAC,EAAS,KAAAC,EAAM,UAAAT,EAAW,YAAAU,GAAsB,CACnF,OACEC,OAAAC,WAAA,CACG,UAAAL,GACCX,MAAC,OACC,UAAU,wBACV,QAASY,EACT,cAAY,SAGhBG,OAAC,SACC,UAAW9E,EAAG,gBAAiB0E,GAAQ,SAAS,EAChD,KAAK,SACL,aAAW,OACX,aAAW,YAEX,UAAAI,OAAC,OAAI,UAAU,qBACb,UAAAf,MAAC,MAAG,qBAAS,EACbA,MAAC,UACC,QAASY,EACT,aAAW,aACX,KAAK,SACL,UAAU,sBAEV,SAAAZ,MAACQ,EAAA,CAAE,KAAM,GAAI,GACf,EACF,QACC,OAAI,UAAU,qBACZ,SAAAK,EAAK,IAAKI,GAAM,CACf,MAAMC,EAAOD,EAAE,KACf,OACEF,OAAC,UAEC,KAAK,MACL,gBAAeX,IAAca,EAAE,GAC/B,UAAWhF,EAAG,qBAAsBmE,IAAca,EAAE,IAAM,WAAW,EACrE,QAAS,IAAMH,EAAYG,EAAE,EAAE,EAC/B,KAAK,SAEL,UAAAjB,MAACkB,EAAA,CAAK,KAAM,GAAI,EAChBlB,MAAC,QAAM,SAAAiB,EAAE,MAAM,IARVA,EAAE,GAWb,CAAC,EACH,IACF,EACF,CAEJ,CCnDO,SAASE,GAAgB,CAAE,KAAAN,EAAM,UAAAT,EAAW,YAAAU,GAAsB,CACvE,OACEd,MAAC,OAAI,UAAU,oBAAoB,KAAK,UAAU,aAAW,qBAC1D,SAAAa,EAAK,IAAKI,GAAM,CACf,MAAMC,EAAOD,EAAE,KACf,OACEF,OAAC,UAEC,KAAK,MACL,gBAAeX,IAAca,EAAE,GAC/B,UAAWhF,EAAG,kBAAmBmE,IAAca,EAAE,IAAM,WAAW,EAClE,QAAS,IAAMH,EAAYG,EAAE,EAAE,EAC/B,KAAK,SAEL,UAAAjB,MAACkB,EAAA,CAAK,KAAM,GAAI,EAChBlB,MAAC,QAAK,UAAU,mBAAoB,WAAE,MAAM,IARvCiB,EAAE,GAWb,CAAC,EACH,CAEJ,CClBO,SAASG,GAAa,CAC3B,SAAAC,EACA,UAAAjB,EACA,YAAAU,EACA,KAAAD,EACA,SAAAS,EACA,eAAAC,EACA,SAAAjB,CACF,EAAU,CACR,KAAM,CAACkB,EAAYC,CAAa,EAAIC,WAAS,EAAK,EAC5C,CAACvB,EAAUwB,CAAW,EAAID,WAAS,EAAK,EAE9C,OACEX,OAAC,OAAI,UAAU,gBACb,UAAAf,MAACC,GAAA,CACC,YAAa,IAAMwB,EAAc,EAAI,EACrC,SAAAtB,EACA,UAAAC,EACA,WAAYA,IAAc,WAAamB,EAAiB,KACxD,SAAAjB,CAAA,GAGFN,MAACU,GAAA,CACC,KAAMc,EACN,QAAS,IAAMC,EAAc,EAAK,EAClC,KAAMH,EACN,UAAAlB,EACA,YAAcwB,GAAO,CACnBd,EAAYc,CAAE,EACdH,EAAc,EAAK,CACrB,IAGFzB,MAAC,QACC,UAAU,iBACV,SAAWpB,GAAM+C,EAAY/C,EAAE,cAAc,UAAY,EAAE,EAE1D,SAAAyC,CAAA,GAGHrB,MAACmB,GAAA,CAAgB,KAAAN,EAAY,UAAAT,EAAsB,YAAAU,CAAA,CAA0B,GAC/E,CAEJ,CCjDO,SAASe,EAAkB,CAAE,KAAAlB,EAAM,QAAAC,EAAS,MAAAkB,EAAO,SAAAT,EAAU,QAAAU,EAAS,UAAAC,EAAY,QAAiB,CACxG,MAAMC,EAAWC,SAAuB,IAAI,EACtCC,EAAiBD,SAA0B,IAAI,EAC/CE,EAAUC,QAAA,EACVC,EAAgBJ,SAA2B,IAAI,EAC/CK,EAAgBL,SAAsB,IAAI,EAC1CM,EAAeN,SAAO,CAAC,EACvBO,EAAiBP,SAAO,CAAC,EAEzBQ,EAAe,IAAM,OACzB,QAAOjE,EAAAwD,EAAS,UAAT,YAAAxD,EAAkB,iBACvB,8IACG,EACP,EA+EA,GA5EAkE,YAAU,IAAM,CACd,GAAI,CAAChC,EAAM,OACX2B,EAAc,QAAU,SAAS,yBAAyB,YAAc,SAAS,cAAgB,KAEjG,MAAMM,EAAShE,GAAqB,OAElC,GADIA,EAAE,MAAQ,UAAUgC,EAAA,EACpBhC,EAAE,MAAQ,MAAO,OAErB,MAAMiE,EAAY,MAAM,KAAKH,EAAA,CAAc,EAC3C,GAAIG,EAAU,SAAW,EAAG,CAC1BjE,EAAE,kBACFH,EAAAwD,EAAS,UAAT,MAAAxD,EAAkB,QAClB,MACF,CAEA,MAAMqE,EAAQD,EAAU,CAAC,EACnBE,EAAOF,EAAUA,EAAU,OAAS,CAAC,EACrCG,EAAS,SAAS,cAExB,GAAIpE,EAAE,SAAU,EACV,CAACoE,GAAUA,IAAWF,GAASE,IAAWf,EAAS,WACrDrD,EAAE,iBACFmE,GAAA,MAAAA,EAAM,SAER,MACF,CAEIC,IAAWD,IACbnE,EAAE,iBACFkE,GAAA,MAAAA,EAAO,QAEX,EAEA,cAAO,iBAAiB,UAAWF,CAAK,EACxC,sBAAsB,IAAM,WAC1BnE,EAAA0D,EAAe,UAAf,YAAA1D,EAAwB,YAAWwE,EAAAhB,EAAS,UAAT,MAAAgB,EAAkB,QACvD,CAAC,EAEM,IAAM,OACX,OAAO,oBAAoB,UAAWL,CAAK,GAC3CnE,EAAA6D,EAAc,UAAd,MAAA7D,EAAuB,OACzB,CACF,EAAG,CAACkC,EAAMC,CAAO,CAAC,EAGlB+B,YAAU,KACJhC,GACF8B,EAAe,QAAU,OAAO,QAChC,SAAS,gBAAgB,MAAM,SAAW,SAC1C,SAAS,KAAK,MAAM,SAAW,SAC/B,SAAS,KAAK,MAAM,SAAW,QAC/B,SAAS,KAAK,MAAM,IAAM,IAAIA,EAAe,OAAO,KACpD,SAAS,KAAK,MAAM,KAAO,IAC3B,SAAS,KAAK,MAAM,MAAQ,IAC5B,SAAS,KAAK,MAAM,MAAQ,SAE5B,SAAS,gBAAgB,MAAM,SAAW,GAC1C,SAAS,KAAK,MAAM,SAAW,GAC/B,SAAS,KAAK,MAAM,SAAW,GAC/B,SAAS,KAAK,MAAM,IAAM,GAC1B,SAAS,KAAK,MAAM,KAAO,GAC3B,SAAS,KAAK,MAAM,MAAQ,GAC5B,SAAS,KAAK,MAAM,MAAQ,IAEvB,IAAM,CACX,SAAS,gBAAgB,MAAM,SAAW,GAC1C,SAAS,KAAK,MAAM,SAAW,GAC/B,SAAS,KAAK,MAAM,SAAW,GAC/B,SAAS,KAAK,MAAM,IAAM,GAC1B,SAAS,KAAK,MAAM,KAAO,GAC3B,SAAS,KAAK,MAAM,MAAQ,GAC5B,SAAS,KAAK,MAAM,MAAQ,GAC5B,OAAO,SAAS,EAAGA,EAAe,OAAO,CAC3C,GACC,CAAC9B,CAAI,CAAC,EAEL,CAACA,EAAM,OAAO,KAElB,MAAMuC,EAAiBtE,GAAyC,OACzDA,EAAE,YACP2D,EAAc,QAAU3D,EAAE,QAC1B4D,EAAa,QAAU,GACvB/D,EAAAwD,EAAS,UAAT,MAAAxD,EAAkB,kBAAkBG,EAAE,WACxC,EAEMuE,EAAiBvE,GAAyC,CAC9D,GAAI2D,EAAc,SAAW,MAAQ,CAACN,EAAS,QAAS,OACxD,MAAMmB,EAAQ,KAAK,IAAI,EAAGxE,EAAE,QAAU2D,EAAc,OAAO,EAC3DC,EAAa,QAAUY,EACvBnB,EAAS,QAAQ,MAAM,UAAY,cAAcmB,CAAK,KACxD,EAEMC,EAAWzE,GAA0C,CACzD,GAAI,CAACqD,EAAS,QAAS,OACnBrD,GAAKqD,EAAS,QAAQ,kBAAkBrD,EAAE,SAAS,GACrDqD,EAAS,QAAQ,sBAAsBrD,EAAE,SAAS,EAEpD,MAAMwE,EAAQZ,EAAa,QAC3BP,EAAS,QAAQ,MAAM,UAAY,GACnCM,EAAc,QAAU,KACxBC,EAAa,QAAU,EACnBY,EAAQ,IAAIxC,EAAA,CAClB,EAEA,OACEZ,MAAC,OAAI,UAAU,uBAAuB,QAASY,EAC7C,SAAAG,OAAC,OACC,IAAKkB,EACL,UAAU,eACV,MAAO,CAAE,UAAAD,CAAA,EACT,QAAUpD,GAAMA,EAAE,kBAClB,KAAK,SACL,aAAU,GACV,aAAYkD,EAAQ,OAAY,QAChC,kBAAiBA,EAAQM,EAAU,OACnC,SAAU,GAGV,UAAApC,MAAC,OACC,UAAU,sBACV,cAAAkD,EACA,cAAAC,EACA,YAAaE,EACb,gBAAiBA,EACjB,cAAY,SAGdtC,OAAC,OAAI,UAAU,sBACZ,UAAAe,EAAQ9B,MAAC,MAAG,GAAIoC,EAAS,UAAU,qBAAsB,SAAAN,CAAA,CAAM,EAAQ9B,MAAC,QAAI,EAC7EA,MAAC,UACC,IAAKmC,EACL,KAAK,SACL,UAAU,kBACV,QAASvB,EACT,aAAW,QAEX,SAAAZ,MAACQ,EAAA,CAAE,KAAM,GAAI,GACf,EACF,EAEAR,MAAC,OAAI,UAAU,uBACZ,SAAAqB,CAAA,CACH,EAECU,GACC/B,MAAC,OAAI,UAAU,uBACZ,SAAA+B,CAAA,CACH,KAGN,CAEJ,CCxKA,MAAMuB,GAAc,CAAC,MAAO,QAAS,SAAU,KAAM,YAAa,MAAM,EAGlEC,GAAsC,CAC1C,IAAK,MACL,MAAO,QACP,OAAQ,SACR,GAAI,aACJ,UAAW,QACX,KAAM,MACR,EAEO,SAASC,GAAe,CAAE,SAAAC,EAAU,UAAAC,GAAoB,OAC7D,KAAM,CAACC,EAAQC,CAAS,EAAIlC,aAAyBjD,EAAAgF,EAAS,WAAT,YAAAhF,EAAmB,iBAAkB,EAAE,EACtF,CAACoF,EAASC,CAAU,EAAIpC,WAAS,EAAI,EACrC,CAACqC,EAAQC,CAAS,EAAItC,WAAoB,KAAK,EAC/C,CAACuC,EAAQC,CAAS,EAAIxC,WAAS,EAAK,EACpC,CAACyC,EAAQC,CAAS,EAAI1C,WAA8B,IAAI,EACxD,CAAC2C,EAAYC,CAAa,EAAI5C,WAAS,EAAE,EACzC,CAAC6C,EAAYC,CAAa,EAAI9C,WAAS,EAAK,EAElDiB,YAAU,IAAM,QACVlE,EAAAgF,EAAS,WAAT,MAAAhF,EAAmB,gBACrBmF,EAAUH,EAAS,SAAS,cAAc,EAC1CK,EAAW,EAAK,GAEhBW,EAAA,CAEJ,EAAG,CAAChB,EAAS,QAAQ,CAAC,EAEtB,MAAMgB,EAAa,SAAY,OAC7B,GAAI,CACF,MAAMzH,EAAO,MAAMmB,EAAI,IAAc,WAAW,EAChDyF,IAAUnF,EAAAzB,EAAK,WAAL,YAAAyB,EAAe,iBAAkB,EAAE,CAC/C,MAAQ,CAER,SACEqF,EAAW,EAAK,CAClB,CACF,EAEMY,EAAWX,IAAW,MAAQJ,EAASA,EAAO,OAAQ/E,GAAM,iBAChE,OAAImF,IAAW,SAAgBtF,EAAAG,EAAE,OAAF,YAAAH,EAAQ,WAAW,QAC9CsF,IAAW,UAAiBd,EAAArE,EAAE,OAAF,YAAAqE,EAAQ,WAAW,SAC/Cc,IAAW,OAAaY,EAAA/F,EAAE,OAAF,YAAA+F,EAAQ,SAAS,UAASC,EAAAhG,EAAE,OAAF,YAAAgG,EAAQ,SAAS,aACnEb,IAAW,aAAoBc,EAAAjG,EAAE,OAAF,YAAAiG,EAAQ,SAAS,aAChDd,IAAW,QAAee,EAAAlG,EAAE,OAAF,YAAAkG,EAAQ,SAAS,OACxC,EACT,CAAC,EAEKC,EAAStB,EAAS,QAAU,GAC5BuB,EAAQvB,EAAS,OAAS,GAC1BwB,EAAcD,EAAM,OAAQ/D,GAAMA,EAAE,SAAW,SAAWA,EAAE,SAAW,QAAQ,EAC/EiE,EAAYF,EAAM,OAAQ/D,GAAMA,EAAE,SAAW,MAAM,EAEnDkE,EAAe,SAAY,CAC/B,GAAI,GAACd,EAAW,QAAUE,GAC1B,CAAAC,EAAc,EAAI,EAClB,GAAI,CACF,MAAMrG,EAAI,KAAK,QAAS,CAAE,QAASkG,EAAW,OAAQ,MAAO,OAAQ,EACrEC,EAAc,EAAE,EAChB,MAAMZ,EAAA,EAAY,MAAM,MAAe,EACvC,MAAMe,EAAA,CACR,MAAQ,CAER,SACED,EAAc,EAAK,CACrB,EACF,EAEMY,EAAgB,SAAY,CAChC,MAAM1B,EAAA,EAAY,MAAM,MAAe,EACvC,MAAMe,EAAA,CACR,EAEA,OACE1D,OAAC,OAAI,UAAU,cAEb,UAAAA,OAAC,OAAI,UAAU,qBACb,UAAAf,MAAC,YACC,UAAU,sBACV,YAAY,kFACZ,MAAOqE,EACP,SAAWzF,GAAM0F,EAAc1F,EAAE,OAAO,KAAK,EAC7C,KAAM,IAERmC,OAAC,OAAI,UAAU,wBACb,UAAAf,MAAC,OAAI,UAAU,sBACZ,UAAC,oBAAqB,UAAW,WAAY,cAAe,aAAa,EAAE,IAAKqF,GAC/ErF,MAAC,UAEC,KAAK,SACL,UAAU,qBACV,QAAS,IAAMsE,EAAegB,GAAQA,EAAM,GAAGA,CAAG,IAAID,CAAI,GAAKA,CAAI,EAElE,SAAAA,CAAA,EALIA,CAAA,CAOR,EACH,EACAtE,OAAC,UACC,KAAK,SACL,UAAU,aACV,SAAU,CAACsD,EAAW,QAAUE,EAChC,QAASY,EAET,UAAAnF,MAACuF,GAAA,CAAK,KAAM,GAAI,EAAE,oBACpB,EACF,GACF,EAGAxE,OAAC,OAAI,UAAU,eACb,UAAAA,OAAC,OAAI,UAAU,cACb,UAAAf,MAAC,OAAI,UAAU,oBAAqB,SAAA+E,EAAO,OAAO,EAClD/E,MAAC,OAAI,UAAU,oBAAoB,kBAAM,GAC3C,EACAe,OAAC,OAAI,UAAU,cACb,UAAAf,MAAC,OAAI,UAAU,oBAAqB,SAAAiF,EAAY,OAAO,EACvDjF,MAAC,OAAI,UAAU,oBAAoB,kBAAM,GAC3C,EACAe,OAAC,OAAI,UAAU,cACb,UAAAf,MAAC,OAAI,UAAU,oBAAqB,SAAAkF,EAAU,OAAO,EACrDlF,MAAC,OAAI,UAAU,oBAAoB,gBAAI,GACzC,EACAe,OAAC,OAAI,UAAU,cACb,UAAAf,MAAC,OAAI,UAAU,oBAAqB,SAAAyD,EAAS,UAAU,QAAU,EAAE,EACnEzD,MAAC,OAAI,UAAU,oBAAoB,iBAAK,GAC1C,GACF,EAEC6D,GAAWF,EAAO,SAAW,GAC5B3D,MAAC,OAAI,UAAU,uCACb,SAAAA,MAAC,KAAE,6BAAiB,EACtB,EAIFe,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,MAAG,UAAU,uBAAuB,MAAO,CAAE,OAAQ,GAAK,2BAAe,EAC1Ee,OAAC,OAAI,MAAO,CAAE,QAAS,OAAQ,IAAK,GAClC,UAAAf,MAAC,UACC,KAAK,SACL,UAAU,kBACV,QAAS,IAAMkE,EAAWsB,GAAM,CAACA,CAAC,EAClC,aAAYvB,EAAS,SAAW,QAE/B,SAAAA,QAAUwB,GAAA,CAAK,KAAM,GAAI,EAAKzF,MAAC0F,GAAA,CAAM,KAAM,GAAI,IAElD1F,MAAC,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAASoF,EAAe,aAAW,UACnF,SAAApF,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,GACF,GACF,QAEC,OAAI,UAAU,uBACZ,SAAArC,GAAY,IAAKsC,GAChB5F,MAAC,UAEC,KAAK,SACL,UAAW,qBAAqB+D,IAAW6B,EAAI,SAAW,EAAE,GAC5D,QAAS,IAAM5B,EAAU4B,CAAC,EAEzB,YAAYA,CAAC,GALTA,CAAA,CAOR,EACH,EAGCb,EAAO,OAAS,GACfhE,OAAC,WAAQ,UAAU,iBACjB,UAAAA,OAAC,MAAG,UAAU,uBAAuB,UAAAf,MAAC6F,GAAA,CAAI,KAAM,GAAI,EAAE,WAAO,EAC7D7F,MAAC,OAAI,UAAU,mBACZ,WAAO,MAAM,EAAG,CAAC,EAAE,IAAK8F,GACvB/E,OAAC,OAAiB,UAAU,oBAC1B,UAAAf,MAAC,OAAI,UAAU,mBAAmB,cAAa8F,EAAE,QAAU,OAAQ,EACnE/E,OAAC,OAAI,UAAU,oBACb,UAAAf,MAAC,QAAK,UAAU,oBAAqB,SAAA8F,EAAE,KAAK,QAC3C,QAAK,UAAU,oBAAqB,SAAAA,EAAE,QAAU,OAAO,GAC1D,IALQA,EAAE,IAMZ,CACD,EACH,GACF,EAID,CAAC7B,GAAUS,EAAS,OAAS,GAC5B1E,MAAC,WAAQ,UAAU,iBACjB,SAAAA,MAAC,OAAI,UAAU,mBACZ,WAAS,MAAM,EAAG,EAAE,EAAE,IAAI,CAACpB,EAAGmH,IAC7BhF,OAAC,UAEC,KAAK,SACL,UAAU,oBACV,QAAS,IAAMqD,EAAUxF,CAAC,EAE1B,UAAAoB,MAAC,QAAK,UAAU,oBAAoB,YAAWpB,EAAE,KAAO,SAAA2E,GAAY3E,EAAE,IAAgC,GAAKA,EAAE,KAAK,QACjH,QAAK,UAAU,mBACb,SAAAoH,GAAmBpH,CAAC,EACvB,QACC,QAAK,UAAU,oBAAqB,SAAAzC,EAAeyC,EAAE,EAAE,EAAE,IATrD,GAAGA,EAAE,EAAE,IAAIA,EAAE,IAAI,IAAImH,CAAC,GAW9B,EACH,EACF,EAGD,CAAC9B,GAAUS,EAAS,SAAW,GAAKf,EAAO,OAAS,GACnD5C,OAAC,OAAI,UAAU,eACb,UAAAf,MAACiG,GAAA,CAAa,KAAM,GAAI,EACxBjG,MAAC,KAAE,iCAAqB,EACxBA,MAAC,KAAE,UAAU,QAAQ,mCAAuB,GAC9C,EAIDmE,GACCnE,MAAC6B,EAAA,CACC,KAAM,GACN,QAAS,IAAMuC,EAAU,IAAI,EAC7B,MAAOb,GAAYY,EAAO,IAAgC,GAAKA,EAAO,KAEtE,SAAApD,OAAC,OAAI,UAAU,2BACb,UAAAA,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,gBAAI,EAAOA,MAAC,QAAM,SAAAmE,EAAO,KAAK,GAAO,EACnFpD,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,gBAAI,EAAOA,MAAC,QAAM,aAAI,KAAKmE,EAAO,EAAE,EAAE,gBAAe,CAAE,GAAO,EAC3G,OAAO,QAAQA,CAAM,EACnB,OAAO,CAAC,CAACyB,CAAC,IAAM,CAAC,CAAC,KAAM,MAAM,EAAE,SAASA,CAAC,CAAC,EAC3C,IAAI,CAAC,CAACA,EAAGJ,CAAC,IACTzE,OAAC,OAAY,UAAU,yBACrB,UAAAf,MAAC,QAAM,SAAA4F,CAAA,CAAE,EACT5F,MAAC,QAAK,MAAO,CAAE,SAAU,GAAI,SAAU,IAAK,UAAW,aACpD,gBAAOwF,GAAM,SAAW,KAAK,UAAUA,CAAC,EAAI,OAAOA,CAAC,EACvD,IAJQI,CAKV,CACD,GACL,IAKHb,EAAO,SAAW,GAAKE,EAAY,SAAW,GAC7ClE,OAAC,OAAI,UAAU,eACb,UAAAf,MAACiG,GAAA,CAAa,KAAM,GAAI,EACxBjG,MAAC,KAAE,4BAAgB,EACnBA,MAAC,KAAE,UAAU,QAAQ,oDAAwC,GAC/D,GAEJ,CAEJ,CAEA,SAASgG,GAAmBpH,EAAyB,CACnD,MAAMgH,EAAIhH,EAAE,MAAQ,GACpB,OAAIgH,EAAE,SAAS,MAAM,EAAU,SAAShH,EAAE,OAASA,EAAE,IAAM,SAAS,GAChEgH,EAAE,SAAS,OAAO,EAAU,UAAUhH,EAAE,MAAQ,gBAAgB,GAChEgH,EAAE,SAAS,WAAW,EAAU,SAAShH,EAAE,MAAQ,SAAS,GAC5DgH,EAAE,SAAS,KAAK,EAAU,QAAQhH,EAAE,MAAQ,SAAS,GACrDgH,EAAE,SAAS,UAAU,EAAU,aAAahH,EAAE,MAAQ,WAAW,GAC9D,KAAK,UAAUA,CAAC,EAAE,MAAM,EAAG,EAAE,CACtC,CCpQO,SAASsH,EAAY,CAAE,KAAAvF,EAAM,QAAAC,EAAS,MAAAkB,EAAO,SAAAT,EAAU,QAAAU,GAAkB,CAC9E,MAAMoE,EAAWjE,SAAuB,IAAI,EACtCC,EAAiBD,SAA0B,IAAI,EAC/CI,EAAgBJ,SAA2B,IAAI,EAC/CE,EAAUC,QAAA,EACVI,EAAiBP,SAAO,CAAC,EA+D/B,OA7DAS,YAAU,IAAM,CACd,GAAI,CAAChC,EAAM,OAEX2B,EAAc,QAAU,SAAS,yBAAyB,YAAc,SAAS,cAAgB,KAEjG,MAAM8D,EAAoB,2IACpBxD,EAAShE,GAAqB,SAElC,GADIA,EAAE,MAAQ,UAAUgC,EAAA,EACpBhC,EAAE,MAAQ,MAAO,OAErB,MAAMiE,EAAY,MAAM,OAAKpE,EAAA0H,EAAS,UAAT,YAAA1H,EAAkB,iBAA8B2H,KAAsB,EAAE,EACrG,GAAIvD,EAAU,SAAW,EAAG,CAC1BjE,EAAE,kBACFqE,EAAAkD,EAAS,UAAT,MAAAlD,EAAkB,QAClB,MACF,CAEA,MAAMH,EAAQD,EAAU,CAAC,EACnBE,EAAOF,EAAUA,EAAU,OAAS,CAAC,EACrCG,EAAS,SAAS,cAExB,GAAIpE,EAAE,SAAU,EACV,CAACoE,GAAUA,IAAWF,GAASE,IAAWmD,EAAS,WACrDvH,EAAE,iBACFmE,GAAA,MAAAA,EAAM,SAER,MACF,CAEIC,IAAWD,IACbnE,EAAE,iBACFkE,GAAA,MAAAA,EAAO,QAEX,EAEA,cAAO,iBAAiB,UAAWF,CAAK,EACxCH,EAAe,QAAU,OAAO,QAChC,SAAS,gBAAgB,MAAM,SAAW,SAC1C,SAAS,KAAK,MAAM,SAAW,SAC/B,SAAS,KAAK,MAAM,SAAW,QAC/B,SAAS,KAAK,MAAM,IAAM,IAAIA,EAAe,OAAO,KACpD,SAAS,KAAK,MAAM,KAAO,IAC3B,SAAS,KAAK,MAAM,MAAQ,IAC5B,SAAS,KAAK,MAAM,MAAQ,OAC5B,sBAAsB,IAAM,WAC1BhE,EAAA0D,EAAe,UAAf,YAAA1D,EAAwB,YAAWwE,EAAAkD,EAAS,UAAT,MAAAlD,EAAkB,QACvD,CAAC,EACM,IAAM,OACX,OAAO,oBAAoB,UAAWL,CAAK,EAC3C,SAAS,gBAAgB,MAAM,SAAW,GAC1C,SAAS,KAAK,MAAM,SAAW,GAC/B,SAAS,KAAK,MAAM,SAAW,GAC/B,SAAS,KAAK,MAAM,IAAM,GAC1B,SAAS,KAAK,MAAM,KAAO,GAC3B,SAAS,KAAK,MAAM,MAAQ,GAC5B,SAAS,KAAK,MAAM,MAAQ,GAC5B,OAAO,SAAS,EAAGH,EAAe,OAAO,GACzChE,EAAA6D,EAAc,UAAd,MAAA7D,EAAuB,OACzB,CACF,EAAG,CAACkC,EAAMC,CAAO,CAAC,EAEbD,EAGHX,MAAC,OACC,UAAU,uBACV,QAAUpB,GAAM,CACVA,EAAE,SAAWA,EAAE,eAAegC,EAAA,CACpC,EAEA,SAAAG,OAAC,OACC,IAAKoF,EACL,UAAU,eACV,QAAUvH,GAAMA,EAAE,kBAClB,KAAK,SACL,aAAU,GACV,aAAYkD,EAAQ,OAAY,QAChC,kBAAiBA,EAAQM,EAAU,OACnC,SAAU,GAEV,UAAArB,OAAC,OAAI,UAAU,sBACZ,UAAAe,EAAQ9B,MAAC,MAAG,GAAIoC,EAAS,UAAU,qBAAsB,SAAAN,CAAA,CAAM,EAAQ9B,MAAC,QAAI,EAC7EA,MAAC,UACC,IAAKmC,EACL,KAAK,SACL,UAAU,kBACV,QAASvB,EACT,aAAW,QAEX,SAAAZ,MAACQ,EAAA,CAAE,KAAM,GAAI,GACf,EACF,EACAR,MAAC,OAAI,UAAU,uBACZ,SAAAqB,CAAA,CACH,EACCU,GACC/B,MAAC,OAAI,UAAU,uBACZ,SAAA+B,CAAA,CACH,IAEJ,GAvCc,IA0CpB,CCzGA,MAAMsE,GAAa,CAAC,YAAa,OAAQ,SAAU,WAAY,SAAU,UAAU,EAE7EC,GAA0C,CAC9C,UAAW,gBACX,KAAM,cACN,OAAQ,iBACR,SAAU,iBACV,OAAQ,eACR,SAAU,iBACZ,EAEO,SAASC,GAAa,CAAE,SAAA9C,EAAU,OAAA+C,EAAQ,YAAAC,EAAa,cAAAC,EAAe,UAAAhD,GAAoB,CAC/F,KAAM,CAACqB,EAAQ4B,CAAS,EAAIjF,WAAkB+B,EAAS,QAAU,EAAE,EAC7D,CAACI,EAASC,CAAU,EAAIpC,WAAS,CAAC+B,EAAS,MAAM,EACjD,CAACmD,EAAgBC,CAAiB,EAAInF,WAAS,EAAE,EACjD,CAACoF,EAAQC,CAAS,EAAIrF,WAAS,EAAE,EACjC,CAACsF,EAAaC,CAAc,EAAIvF,WAAuB,IAAI,EAC3D,CAACwF,EAASC,CAAU,EAAIzF,WAAS,EAAK,EACtC,CAAC0F,EAAcC,CAAe,EAAI3F,WAAS,EAAE,EAC7C,CAAC4F,EAAYC,CAAa,EAAI7F,WAAS,EAAK,EAElDiB,YAAU,IAAM,CACd,GAAI+D,EAAe,CACjB,MAAMZ,EAAIf,EAAO,KAAMyC,GAAOA,EAAG,OAASd,CAAa,EACnDZ,KAAkBA,CAAC,CACzB,CACF,EAAG,CAACY,EAAe3B,CAAM,CAAC,EAE1B,MAAM0C,EAAS,SAAY,CACzB,GAAI,CACF,MAAMzK,EAAO,MAAMmB,EAAI,IAAyB,SAAS,EACzDwI,EAAU3J,EAAK,QAAU,EAAE,CAC7B,MAAQ,CAER,SACE8G,EAAW,EAAK,CAClB,CACF,EAEMY,EAAWK,EAAO,OAAQe,GAAM,CACpC,GAAIc,GAAkBd,EAAE,WAAac,EAAgB,MAAO,GAC5D,GAAIE,EAAQ,CACV,MAAMY,EAAIZ,EAAO,cACjB,GAAI,CAAChB,EAAE,KAAK,cAAc,SAAS4B,CAAC,GAAK,EAAE5B,EAAE,aAAe,IAAI,cAAc,SAAS4B,CAAC,EAAG,MAAO,EACpG,CACA,MAAO,EACT,CAAC,EAEKC,EAAc,MAAOC,EAAcC,IAAmB,CAC1D,GAAI,CACF,MAAM1J,EAAI,KAAK,WAAW,mBAAmByJ,CAAI,CAAC,UAAW,CAAE,OAAAC,EAAQ,EACvEN,EAAc,EAAK,EACnBF,EAAgB,EAAE,CACpB,MAAQ,CAER,CACF,EAEMS,EAAe,MAAOF,GAAiB,CAC3C,GAAI,CACF,MAAMzJ,EAAI,KAAK,WAAW,mBAAmByJ,CAAI,CAAC,UAAU,EAC5D,MAAMH,EAAA,CACR,MAAQ,CAER,CACF,EAEMM,EAAc,MAAOH,GAAiB,CAC1C,GAAK,QAAQ,iBAAiBA,CAAI,IAAI,EACtC,GAAI,CACF,MAAMzJ,EAAI,IAAI,WAAW,mBAAmByJ,CAAI,CAAC,EAAE,EACnDjB,EAAWrB,GAAQA,EAAI,OAAQQ,GAAMA,EAAE,OAAS8B,CAAI,CAAC,EACrDX,EAAe,IAAI,EACnBvD,GAAA,MAAAA,GACF,MAAQ,CAER,CACF,EAEMsE,EAAc,MAAOhL,GAGrB,CACJ,GAAI,CACF,MAAMiL,EAAU,MAAM9J,EAAI,KAAY,UAAWnB,CAAI,EACrD2J,EAAWrB,GAAQ,CAAC,GAAGA,EAAK2C,CAAO,CAAC,EACpCd,EAAW,EAAK,EAChBzD,GAAA,MAAAA,GACF,MAAQ,CAER,CACF,EAEA,OACE3C,OAAC,OAAI,UAAU,cAEb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,iBACZ,MAAO8G,EACP,SAAWlI,GAAMmI,EAAUnI,EAAE,OAAO,KAAK,EACzC,MAAO,CAAE,KAAM,EAAE,SAElB,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAM6I,EAAA,EAAU,aAAW,UACpF,SAAAzH,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,QACC,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAMwB,EAAW,EAAI,EAAG,aAAW,YAC5F,eAACe,EAAA,CAAK,KAAM,GAAI,EAClB,GACF,EAGAnH,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,UACC,KAAK,SACL,UAAW,qBAAsB4G,EAA4B,GAAX,QAAa,GAC/D,QAAS,IAAMC,EAAkB,EAAE,EACpC,iBAGAR,GAAW,IAAK8B,GACfnI,MAAC,UAEC,KAAK,SACL,UAAW,qBAAqB4G,IAAmBuB,EAAI,SAAW,EAAE,GACpE,QAAS,IAAMtB,EAAkBsB,CAAC,EAEjC,SAAAA,CAAA,EALIA,CAAA,CAOR,GACH,EAECtE,EACC7D,MAAC,OAAI,UAAU,iBAAiB,eAAC,KAAE,oBAAQ,EAAI,EAC7C0E,EAAS,SAAW,EACtB3D,OAAC,OAAI,UAAU,eACb,UAAAf,MAAC6F,GAAA,CAAI,KAAM,GAAI,EACf7F,MAAC,KAAE,4BAAgB,GACrB,QAEC,OAAI,UAAU,mBACZ,SAAA0E,EAAS,IAAKoB,GACb/E,OAAC,UAEC,KAAK,SACL,UAAU,gDACV,QAAS,IAAM,CAAEkG,EAAenB,CAAC,EAAGW,EAAYX,EAAE,IAAI,CAAG,EAEzD,UAAA9F,MAAC,OAAI,UAAU,mBAAmB,cAAa8F,EAAE,QAAU,OAAQ,EACnE/E,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,QAAK,UAAU,oBAAqB,SAAA8F,EAAE,KAAK,EAC5C9F,MAAC,QAAK,UAAU,mBAAoB,WAAE,OAAS8F,EAAE,MAAQ,IAAI,GAC/D,EACCA,EAAE,UACD9F,MAAC,QACC,UAAU,oBACV,MAAO,CAAE,MAAOsG,GAAgBR,EAAE,QAAQ,GAAK,mBAE9C,SAAAA,EAAE,WAGP9F,MAAC,QAAK,UAAU,oBAAoB,cAAa8F,EAAE,QAAU,OAC1D,SAAAA,EAAE,QAAU,OACf,IApBKA,EAAE,KAsBV,EACH,EAIDkB,GACChH,MAAC6B,EAAA,CACC,KAAM,GACN,QAAS,IAAMoF,EAAe,IAAI,EAClC,MAAOD,EAAY,KACnB,QACEjG,OAAC,OAAI,UAAU,6BACb,UAAAA,OAAC,UACC,KAAK,SACL,UAAU,aACV,QAAS,IAAM,CAAEsG,EAAgB,EAAE,EAAGE,EAAc,EAAI,CAAG,EAE3D,UAAAvH,MAACyF,GAAA,CAAK,KAAM,GAAI,EAAE,cAElBuB,EAAY,SAAWA,EAAY,SAAW,SAAWA,EAAY,SAAW,YAChFjG,OAAC,UACC,KAAK,SACL,UAAU,kCACV,QAAS,IAAM+G,EAAad,EAAY,IAAI,EAE5C,UAAAhH,MAACoI,GAAA,CAAS,KAAM,GAAI,EAAE,cAG1BpI,MAAC,UACC,KAAK,SACL,UAAU,+BACV,QAAS,IAAM+H,EAAYf,EAAY,IAAI,EAE3C,SAAAhH,MAACqI,GAAA,CAAO,KAAM,GAAI,GACpB,EACF,EAGF,SAAAtH,OAAC,OAAI,UAAU,sBACb,UAAAA,OAAC,OAAI,UAAU,6BACb,UAAAf,MAAC,QAAK,UAAU,mBAAmB,cAAagH,EAAY,QAAU,OAAQ,QAC7E,QAAK,UAAU,kCAAmC,SAAAA,EAAY,QAAU,OAAO,EAC/EA,EAAY,UACXhH,MAAC,QACC,UAAU,oBACV,MAAO,CAAE,MAAOsG,GAAgBU,EAAY,QAAQ,GAEnD,SAAAA,EAAY,UACf,EAEJ,QACC,KAAE,UAAU,2BAA4B,SAAAA,EAAY,aAAe,kBAAkB,EACtFjG,OAAC,OAAI,UAAU,2BACb,UAAAA,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,iBAAK,QACV,QAAK,UAAU,OAAQ,SAAAgH,EAAY,OAAS,IAAI,GACnD,EACAjG,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,gBAAI,EACVA,MAAC,QAAM,SAAAgH,EAAY,MAAQ,WAAW,GACxC,EACAjG,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,gBAAI,EACVA,MAAC,QAAK,UAAU,OAAO,MAAO,CAAE,SAAU,IAAO,SAAAgH,EAAY,KAAK,GACpE,EACCA,EAAY,WACXjG,OAAC,OAAI,UAAU,yBAAyB,MAAO,CAAE,MAAO,gBACtD,UAAAf,MAAC,QAAK,sBAAU,EAChBA,MAAC,QAAM,SAAAgH,EAAY,UAAU,QAAQ,GACvC,EAEDA,EAAY,YAAc,MAAQA,EAAY,WAAa,GAC1DjG,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,wBAAY,SACjB,QAAM,eAAK,OAAOgH,EAAY,aAAe,GAAK,GAAG,EAAE,KAAC,GAC3D,EAEFjG,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,yBAAa,EACnBA,MAAC,QAAM,SAAA7D,EAAe6K,EAAY,KAAK,EAAE,GAC3C,GACF,GACF,IAKJjG,OAACmF,EAAA,CAAY,KAAMoB,EAAY,QAAS,IAAMC,EAAc,EAAK,EAAG,MAAO,UAAUP,GAAA,YAAAA,EAAa,IAAI,GAAI,QACxGjG,OAAC,UACC,KAAK,SACL,UAAU,aACV,MAAO,CAAE,MAAO,QAChB,SAAU,CAACqG,EAAa,OACxB,QAAS,IAAM,CAAMJ,GAAaW,EAAYX,EAAY,KAAMI,EAAa,MAAM,CAAG,EAEtF,UAAApH,MAACyF,GAAA,CAAK,KAAM,GAAI,EAAE,aAGpB,UAAA1E,OAAC,KAAE,UAAU,QAAQ,MAAO,CAAE,aAAc,IAAM,0BACxC,QAAK,UAAU,OAAQ,UAAAiG,GAAA,YAAAA,EAAa,QAAS,IAAI,GAC3D,EACAhH,MAAC,YACC,UAAU,eACV,KAAM,EACN,YAAY,6BACZ,MAAOoH,EACP,SAAWxI,GAAMyI,EAAgBzI,EAAE,OAAO,KAAK,EAC/C,UAAS,IACX,EACF,EAGAoB,MAACsI,GAAA,CACC,KAAMpB,EACN,QAAS,IAAMC,EAAW,EAAK,EAC/B,SAAUa,CAAA,EACZ,EACF,CAEJ,CAEA,SAASM,GAAc,CAAE,KAAA3H,EAAM,QAAAC,EAAS,SAAA2H,GAOrC,CACD,KAAM,CAACX,EAAMY,CAAO,EAAI9G,WAAS,EAAE,EAC7B,CAAC+G,EAAaC,CAAc,EAAIhH,WAAS,EAAE,EAC3C,CAACiH,EAAOC,CAAQ,EAAIlH,WAAS,EAAE,EAC/B,CAACzC,EAAM4J,CAAO,EAAInH,WAAS,UAAU,EACrC,CAACoH,EAAOC,CAAQ,EAAIrH,WAAS,SAAS,EACtC,CAACsH,EAAUC,CAAW,EAAIvH,WAAS,EAAE,EACrC,CAACwH,EAAMC,CAAO,EAAIzH,WAAS,EAAE,EAC7B,CAACmG,EAAQuB,CAAS,EAAI1H,WAAS,EAAE,EAEjCyD,EAAgBvG,GAAuB,CAC3CA,EAAE,iBACGgJ,EAAK,SACVW,EAAS,CACP,KAAMX,EAAK,OACX,YAAaa,EAAY,OACzB,MAAAE,EACA,KAAA1J,EACA,MAAA6J,EACA,MAAO,GACP,KAAMI,EAAK,MAAM,GAAG,EAAE,IAAKjI,GAAMA,EAAE,MAAM,EAAE,OAAO,OAAO,EACzD,SAAA+H,EACA,OAAAnB,CAAA,CACD,EACDW,EAAQ,EAAE,EACVE,EAAe,EAAE,EACjBE,EAAS,EAAE,EACXC,EAAQ,UAAU,EAClBE,EAAS,SAAS,EAClBE,EAAY,EAAE,EACdE,EAAQ,EAAE,EACVC,EAAU,EAAE,EACd,EAEA,aACGlD,EAAA,CAAY,KAAAvF,EAAY,QAAAC,EAAkB,MAAM,YAAY,QAC3DG,OAAC,UAAO,KAAK,SAAS,KAAK,iBAAiB,UAAU,aAAa,MAAO,CAAE,MAAO,QACjF,UAAAf,MAACkI,EAAA,CAAK,KAAM,GAAI,EAAE,WACpB,EAEA,gBAAC,QAAK,GAAG,iBAAiB,SAAU/C,EAAc,UAAU,mBAC1D,UAAAnF,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,SAAM,UAAU,eAAe,KAAK,OAAO,YAAY,WAAW,QAAQ,0BACzE,MAAO4H,EAAM,SAAWhJ,GAAM4J,EAAQ5J,EAAE,OAAO,KAAK,EAAG,UAAS,GAAC,SAAQ,KAC3EoB,MAAC,SAAM,UAAU,qBAAqB,uBAAW,EACjDA,MAAC,SAAM,UAAU,eAAe,KAAK,OAAO,YAAY,2BACtD,MAAOyI,EAAa,SAAW7J,GAAM8J,EAAe9J,EAAE,OAAO,KAAK,IACpEoB,MAAC,SAAM,UAAU,qBAAqB,iBAAK,EAC3CA,MAAC,SAAM,UAAU,eAAe,KAAK,OAAO,YAAY,qBACtD,MAAO2I,EAAO,SAAW/J,GAAMgK,EAAShK,EAAE,OAAO,KAAK,IACxDoB,MAAC,SAAM,UAAU,qBAAqB,oBAAQ,EAC9Ce,OAAC,UAAO,UAAU,eAAe,MAAOiI,EAAU,SAAWpK,GAAMqK,EAAYrK,EAAE,OAAO,KAAK,EAC3F,UAAAoB,MAAC,UAAO,MAAM,GAAG,gBAAI,EACpBqG,GAAW,IAAK8B,GAAMnI,MAAC,UAAe,MAAOmI,EAAI,SAAAA,CAAA,EAAdA,CAAgB,CAAS,GAC/D,EACAnI,MAAC,SAAM,UAAU,qBAAqB,kCAAsB,EAC5DA,MAAC,SAAM,UAAU,eAAe,KAAK,OAAO,YAAY,aACtD,MAAOkJ,EAAM,SAAWtK,GAAMuK,EAAQvK,EAAE,OAAO,KAAK,GAAG,EAC3D,EACF,CAEJ,CCrWO,SAASyK,GAAa,CAAE,OAAA7C,GAAiB,CAC9C,KAAM,CAAC8C,EAASC,CAAU,EAAI7H,WAAwB,EAAE,EAClD,CAACmC,EAASC,CAAU,EAAIpC,WAAS,EAAI,EACrC,CAAC8H,EAASC,CAAU,EAAI/H,WAAkC,EAAE,EAC5D,CAACgI,EAAQC,CAAS,EAAIjI,WAAS,EAAK,EAEpCkI,EAAO,SAAY,CACvB,GAAI,CACF,MAAM5M,EAAO,MAAMmB,EAAI,IAA6B,SAAS,EACvDmL,EAAyB,OAAO,QAAQtM,CAAI,EAAE,IAAI,CAAC,CAAC6M,EAAKC,CAAK,KAAO,CACzE,IAAAD,EACA,MAAAC,EACA,KAAMC,EAAWD,CAAK,GACtB,EACFP,EAAWD,CAAO,EAClBG,EAAW,OAAO,YAAY,OAAO,QAAQzM,CAAI,EAAE,IAAI,CAAC,CAAC4I,EAAGJ,CAAC,IAAM,CAACI,EAAGJ,CAAC,CAAC,CAAC,CAAC,CAC7E,MAAQ,CAER,SACE1B,EAAW,EAAK,CAClB,CACF,EAEAnB,YAAU,IAAM,CAAEiH,EAAA,CAAQ,EAAG,EAAE,EAE/B,MAAMG,EAAcvE,GACd,OAAOA,GAAM,UAAkB,UAC/B,OAAOA,GAAM,SAAiB,SAC9B,OAAOA,GAAM,SAAiB,SAC9B,MAAM,QAAQA,CAAC,EAAU,QACzBA,GAAK,OAAOA,GAAM,SAAiB,SAChC,UAGHwE,EAAe,CAACH,EAAaC,IAAmB,CACpDL,EAAYnE,IAAS,CAAE,GAAGA,EAAK,CAACuE,CAAG,EAAGC,CAAA,EAAQ,CAChD,EAEMG,EAAU,SAAY,CAC1BN,EAAU,EAAI,EACd,GAAI,CACF,MAAMxL,EAAI,MAAM,UAAWqL,CAAO,EAClC,MAAMI,EAAA,CACR,MAAQ,CAER,SACED,EAAU,EAAK,CACjB,CACF,EAEMO,EAAW,CAACL,EAAaM,IAA2B,CACxDV,EAAYnE,IAAS,CAAE,GAAGA,EAAK,CAACuE,CAAG,EAAGM,CAAA,EAAe,CACvD,EAEMC,EAAeC,GAAuB,CAC1C,MAAMP,EAAQN,EAAQa,EAAM,GAAG,EAC/B,OAAQA,EAAM,MACZ,IAAK,UACH,OACEtJ,OAAC,SAAM,UAAU,gBACf,UAAAf,MAAC,SACC,KAAK,WACL,QAAS,EAAQ8J,EACjB,SAAWlL,GAAMoL,EAAaK,EAAM,IAAKzL,EAAE,OAAO,OAAO,IAE3DoB,MAAC,QAAK,UAAU,uBAAuB,GACzC,EAEJ,IAAK,SACH,OACEA,MAAC,SACC,UAAU,+BACV,KAAK,SACL,UAAU,UACV,MAAO,OAAO8J,GAAS,EAAE,EACzB,SAAWlL,GAAMoL,EAAaK,EAAM,IAAK,OAAOzL,EAAE,OAAO,KAAK,CAAC,IAGrE,IAAK,SACH,OACEoB,MAAC,SACC,UAAU,+BACV,KAAK,OACL,MAAO,OAAO8J,GAAS,EAAE,EACzB,SAAWlL,GAAMoL,EAAaK,EAAM,IAAKzL,EAAE,OAAO,KAAK,IAG7D,IAAK,SACL,IAAK,QACH,OACEoB,MAAC,YACC,UAAU,+BACV,KAAM,EACN,MAAO,KAAK,UAAU8J,GAAS,KAAM,KAAM,CAAC,EAC5C,SAAWlL,GAAM,CACf,GAAI,CACFoL,EAAaK,EAAM,IAAK,KAAK,MAAMzL,EAAE,OAAO,KAAK,CAAC,CACpD,MAAQ,CAER,CACF,IAGN,QACE,aAAQ,QAAK,UAAU,4BAA6B,gBAAOkL,GAAS,MAAM,EAAE,EAElF,EAEA,OACE/I,OAAC,OAAI,UAAU,cACb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAA,OAAC,QAAK,MAAO,CAAE,SAAU,GAAI,MAAO,oBAAqB,KAAM,GAAM,UAAAuI,EAAQ,OAAO,mBAAe,QAClG,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAMM,EAAA,EAAQ,aAAW,UAClF,SAAA5J,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,EACA5E,OAAC,UACC,KAAK,SACL,UAAU,aACV,SAAU2I,EACV,QAAS,IAAMO,EAAA,EAEf,UAAAjK,MAACsK,GAAA,CAAK,KAAM,GAAI,EAAE,IAAEZ,EAAS,UAAY,aAC3C,EACF,EAEC7F,EACC7D,MAAC,OAAI,UAAU,iBAAiB,eAAC,KAAE,oBAAQ,EAAI,EAC7CsJ,EAAQ,SAAW,EACrBvI,OAAC,OAAI,UAAU,eACb,UAAAf,MAACuK,EAAA,CAAQ,KAAM,GAAI,EACnBvK,MAAC,KAAE,8BAAkB,GACvB,EAEAA,MAAC,OAAI,UAAU,qBACZ,SAAAsJ,EAAQ,IAAKe,GACZtJ,OAAC,OAAoB,UAAU,oBAC7B,UAAAA,OAAC,OAAI,UAAU,oBACb,UAAAf,MAAC,QAAK,UAAU,OAAQ,SAAAqK,EAAM,IAAI,EAClCrK,MAAC,QAAK,UAAU,qBAAsB,WAAM,KAAK,GACnD,QACC,OAAI,UAAU,sBACZ,SAAAoK,EAAYC,CAAK,EACpB,EACArK,MAAC,UACC,KAAK,SACL,UAAU,kBACV,QAAS,IAAMkK,EAASG,EAAM,IAAKA,EAAM,YAAY,EACrD,aAAW,mBACX,MAAM,QAEN,SAAArK,MAACwK,GAAA,CAAU,KAAM,GAAI,GACvB,GAhBQH,EAAM,GAiBhB,CACD,EACH,GAEJ,CAEJ,CCnIO,SAASI,GAAc,CAAE,OAAAjE,GAAiB,CAC/C,KAAM,CAAC8C,EAASC,CAAU,EAAI7H,WAAyB,EAAE,EACnD,CAACmC,EAASC,CAAU,EAAIpC,WAAS,EAAI,EACrC,CAACyC,EAAQC,CAAS,EAAI1C,WAA8B,IAAI,EACxD,CAACgJ,EAAOC,CAAQ,EAAIjJ,WAAS,EAAE,EAE/BkI,EAAO,SAAY,CACvB,GAAI,CACF,MAAM5M,EAAO,MAAMmB,EAAI,IAAqB,UAAU,EACtD,GAAI,MAAM,QAAQnB,EAAK,OAAO,EAAG,CAC/BuM,EAAWvM,EAAK,OAAO,EACvB,MACF,CAEA,GAAI,MAAM,QAAQA,EAAK,QAAQ,EAAG,CAChC,MAAM4N,MAA2B,IACjC,UAAWC,KAAS7N,EAAK,QAAU,GAAI,CACrC,MAAM8N,EAAMD,EAAM,UAClB,GAAI,CAACC,EAAK,SACV,MAAMC,EAAUH,EAAqB,IAAIE,CAAG,GACxC,CAACC,GAAW,IAAI,KAAKF,EAAM,EAAE,EAAE,UAAY,IAAI,KAAKE,EAAQ,EAAE,EAAE,YAClEH,EAAqB,IAAIE,EAAKD,CAAK,CAEvC,CAEAtB,EACEvM,EAAK,SAAS,IAAKgO,GAAY,CAC7B,MAAMC,EAAcL,EAAqB,IAAII,EAAQ,EAAE,EACvD,MAAO,CACL,GAAIA,EAAQ,GACZ,GAAIA,EAAQ,eAAgBC,GAAA,YAAAA,EAAa,KAAM,IAAI,KAAK,CAAC,EAAE,cAC3D,MAAOD,EAAQ,KACf,UAAWA,EAAQ,MAAM,MACzB,QAAS,GAAGA,EAAQ,IAAI,MAAMA,EAAQ,MAAM,IAAI,IAAIA,EAAQ,MAAM,KAAK,WAAWA,EAAQ,KAAK,QAAQA,EAAQ,QAAU,EAAI,GAAK,GAAG,GACrI,OAAQC,EAAc,GAAGA,EAAY,IAAI,GAAGA,EAAY,KAAO,MAAMA,EAAY,IAAI,GAAK,EAAE,GAAK,oBAErG,CAAC,GAEH,MACF,CAEA1B,EAAW,EAAE,CACf,MAAQ,CAER,SACEzF,EAAW,EAAK,CAClB,CACF,EAEAnB,YAAU,IAAM,CAAEiH,EAAA,CAAQ,EAAG,EAAE,EAE/B,MAAMsB,EAAkBC,GAAwB,CAC9C,GAAI,CAACA,EAAI,MAAO,IAChB,MAAMrM,EAAI,KAAK,MAAMqM,EAAK,GAAI,EAC9B,GAAIrM,EAAI,GAAI,MAAO,GAAGA,CAAC,IACvB,MAAMW,EAAI,KAAK,MAAMX,EAAI,EAAE,EACrBnB,EAAImB,EAAI,GACd,MAAO,GAAGW,CAAC,KAAK9B,CAAC,GACnB,EAEMrB,EAAcF,GACR,IAAI,KAAKA,CAAE,EACZ,iBAGLsI,EAAWgG,EAAM,OACnBpB,EAAQ,OAAQe,GAAU,CACxB,MAAM3C,EAAIgD,EAAM,cAChB,OAAQL,EAAM,OAAS,WAAW,cAAc,SAAS3C,CAAC,IACpD2C,EAAM,SAAW,IAAI,cAAc,SAAS3C,CAAC,IAC7C2C,EAAM,QAAU,IAAI,cAAc,SAAS3C,CAAC,CACpD,CAAC,EACD4B,EAEJ,OACEvI,OAAC,OAAI,UAAU,cACb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,kBACZ,MAAO0K,EACP,SAAW9L,GAAM+L,EAAS/L,EAAE,OAAO,KAAK,EACxC,MAAO,CAAE,KAAM,EAAE,SAElB,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAMgL,EAAA,EAAQ,aAAW,UAClF,SAAA5J,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,GACF,EAEC9B,EACC7D,MAAC,OAAI,UAAU,iBAAiB,eAAC,KAAE,oBAAQ,EAAI,EAC7C0E,EAAS,SAAW,EACtB3D,OAAC,OAAI,UAAU,eACb,UAAAf,MAACoL,GAAA,CAAQ,KAAM,GAAI,QAClB,KAAG,SAAA9B,EAAQ,SAAW,EAAI,kBAAoB,wBAAwB,GACzE,QAEC,OAAI,UAAU,mBACZ,SAAA5E,EAAS,IAAK9F,GACbmC,OAAC,UAEC,KAAK,SACL,UAAU,gDACV,QAAS,IAAMqD,EAAUxF,CAAC,EAE1B,UAAAoB,MAAC,OAAI,UAAU,mBAAmB,eAACoL,GAAA,CAAQ,KAAM,GAAI,EAAE,EACvDrK,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,QAAK,UAAU,oBAAqB,SAAApB,EAAE,OAAS,UAAU,EAC1DmC,OAAC,QAAK,UAAU,mBACb,UAAAzE,EAAWsC,EAAE,EAAE,EACfA,EAAE,SAAW,MAAMsM,EAAetM,EAAE,QAAQ,CAAC,GAAK,GAClDA,EAAE,UAAY,MAAMA,EAAE,SAAS,SAAW,IAC7C,GACF,IAbKA,EAAE,GAeV,EACH,EAIDuF,GACCpD,OAACc,EAAA,CAAkB,KAAM,GAAM,QAAS,IAAMuC,EAAU,IAAI,EAAG,MAAM,iBACnE,UAAArD,OAAC,OAAI,UAAU,2BACb,UAAAA,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,iBAAK,EAAOA,MAAC,QAAM,SAAAmE,EAAO,OAAS,IAAI,GAAO,EAC5FpD,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,mBAAO,EAAOA,MAAC,QAAM,SAAA1D,EAAW6H,EAAO,EAAE,EAAE,GAAO,EAChGpD,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,oBAAQ,EAAOA,MAAC,QAAM,SAAAkL,EAAe/G,EAAO,QAAQ,EAAE,GAAO,EAC3GpD,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,iBAAK,EAAOA,MAAC,QAAM,SAAAmE,EAAO,WAAa,EAAE,GAAO,GAChG,EACCA,EAAO,SACNpD,OAAC,OAAI,MAAO,CAAE,UAAW,IACvB,UAAAf,MAAC,MAAG,MAAO,CAAE,SAAU,GAAI,MAAO,oBAAqB,aAAc,GAAK,mBAAO,EACjFA,MAAC,KAAE,MAAO,CAAE,SAAU,IAAO,WAAO,QAAQ,GAC9C,EAEDmE,EAAO,QACNpD,OAAC,OAAI,MAAO,CAAE,UAAW,IACvB,UAAAf,MAAC,MAAG,MAAO,CAAE,SAAU,GAAI,MAAO,oBAAqB,aAAc,GAAK,kBAAM,SAC/E,OAAI,UAAU,OAAO,MAAO,CAAE,SAAU,GAAI,WAAY,WAAY,UAAW,YAAa,WAAY,mBAAoB,QAAS,EAAG,aAAc,GACpJ,UAAAmE,EAAO,OAAO,MAAM,EAAG,GAAI,EAAGA,EAAO,OAAO,OAAS,IAAO,IAAM,IACrE,GACF,GAEJ,GAEJ,CAEJ,CClLO,SAASkH,GAAW,CAAE,SAAA5H,EAAU,OAAA+C,GAAiB,CACtD,KAAM,CAAC8E,EAAMC,CAAO,EAAI7J,WAAgB+B,EAAS,MAAQ,EAAE,EACrD,CAACI,EAASC,CAAU,EAAIpC,WAAS,CAAC+B,EAAS,IAAI,EAC/C,CAACM,EAAQC,CAAS,EAAItC,WAAS,EAAE,EACjC,CAAC8J,EAAcC,CAAe,EAAI/J,WAAyC,KAAK,EAChF,CAACgK,EAAWC,CAAY,EAAIjK,WAAqB,IAAI,EAErD+F,EAAS,SAAY,CACzB,GAAI,CACF,MAAMzK,EAAO,MAAMmB,EAAI,IAAqB,OAAO,EACnDoN,EAAQvO,EAAK,MAAQ,EAAE,CACzB,MAAQ,CAER,SACE8G,EAAW,EAAK,CAClB,CACF,EAEAnB,YAAU,IAAM,CACVc,EAAS,MACX8H,EAAQ9H,EAAS,IAAI,EACrBK,EAAW,EAAK,GAEhB2D,EAAA,CAEJ,EAAG,CAAChE,EAAS,IAAI,CAAC,EAElB,MAAMmI,EAAY,MAAOhK,EAAYiK,IAAqB,CACxD,GAAI,CACF,MAAM1N,EAAI,MAAM,SAAS,mBAAmByD,CAAE,CAAC,GAAI,CAAE,QAAAiK,EAAS,EAC9DN,EAASjG,GAAQA,EAAI,IAAK7F,GAAOA,EAAE,KAAOmC,EAAK,CAAE,GAAGnC,EAAG,QAAAoM,CAAA,EAAYpM,CAAE,CAAC,GAClEiM,GAAA,YAAAA,EAAW,MAAO9J,GAAI+J,EAAclM,GAAMA,GAAI,CAAE,GAAGA,EAAG,QAAAoM,CAAA,CAAa,CACzE,MAAQ,CAER,CACF,EAEMnH,EAAW4G,EAAK,OAAQ7L,GAAM,CAElC,GADI+L,IAAiB,WAAa,CAAC/L,EAAE,SACjC+L,IAAiB,YAAc/L,EAAE,QAAS,MAAO,GACrD,GAAIsE,EAAQ,CACV,MAAM2D,EAAI3D,EAAO,cACjB,OAAOtE,EAAE,KAAK,cAAc,SAASiI,CAAC,GAAKjI,EAAE,YAAY,cAAc,SAASiI,CAAC,CACnF,CACA,MAAO,EACT,CAAC,EAED,OACE3G,OAAC,OAAI,UAAU,cACb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,eACZ,MAAO+D,EACP,SAAWnF,GAAMoF,EAAUpF,EAAE,OAAO,KAAK,EACzC,MAAO,CAAE,KAAM,EAAE,SAElB,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAM6I,EAAA,EAAU,aAAW,UACpF,SAAAzH,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,GACF,EAEA3F,MAAC,OAAI,UAAU,uBACX,UAAC,MAAO,UAAW,UAAU,EAAY,IAAKlB,GAC9CkB,MAAC,UAAe,KAAK,SAAS,UAAW,qBAAqBwL,IAAiB1M,EAAI,SAAW,EAAE,GAC9F,QAAS,IAAM2M,EAAgB3M,CAAC,EAC/B,SAAAA,EAAE,OAAO,CAAC,EAAE,cAAgBA,EAAE,MAAM,CAAC,GAF3BA,CAAA,CAId,EACH,EAEC+E,EACC7D,MAAC,OAAI,UAAU,iBAAiB,eAAC,KAAE,oBAAQ,EAAI,EAC7C0E,EAAS,SAAW,EACtB3D,OAAC,OAAI,UAAU,eACb,UAAAf,MAACuK,EAAA,CAAQ,KAAM,GAAI,EACnBvK,MAAC,KAAE,0BAAc,GACnB,QAEC,OAAI,UAAU,mBACZ,SAAA0E,EAAS,IAAKjF,GACbsB,OAAC,UAEC,KAAK,SACL,UAAU,gDACV,QAAS,IAAM4K,EAAalM,CAAC,EAE7B,UAAAO,MAAC,OAAI,UAAU,mBAAmB,eAACuK,EAAA,CAAQ,KAAM,GAAI,EAAE,EACvDxJ,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,QAAK,UAAU,oBAAqB,SAAAP,EAAE,KAAK,EAC5CsB,OAAC,QAAK,UAAU,mBAAmB,cAAEtB,EAAE,QAAQ,MAAIA,EAAE,QAAO,GAC9D,EACAO,MAAC,QAAK,UAAW,qBAAqBP,EAAE,QAAU,WAAa,WAAW,GACvE,SAAAA,EAAE,QAAU,KAAO,MACtB,IAZKA,EAAE,GAcV,EACH,EAGDiM,GACC1L,MAAC6B,EAAA,CACC,KAAM,GACN,QAAS,IAAM8J,EAAa,IAAI,EAChC,MAAOD,EAAU,KACjB,QACE1L,MAAC,OAAI,UAAU,6BACb,SAAAe,OAAC,UACC,KAAK,SACL,UAAU,aACV,MAAO,CAAE,KAAM,GACf,QAAS,IAAM,CAAM2K,GAAWE,EAAUF,EAAU,GAAI,CAACA,EAAU,OAAO,CAAG,EAE7E,UAAA1L,MAAC8L,GAAA,CAAM,KAAM,GAAI,EAAE,IAAEJ,EAAU,QAAU,UAAY,YAEzD,EAGF,SAAA3K,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,KAAE,UAAU,2BAA4B,SAAA0L,EAAU,aAAe,kBAAkB,EACpF3K,OAAC,OAAI,UAAU,2BACb,UAAAA,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,mBAAO,EAAOA,MAAC,QAAK,UAAU,OAAQ,WAAU,QAAQ,GAAO,EAC7Ge,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,kBAAM,EAAOA,MAAC,QAAM,SAAA0L,EAAU,OAAO,GAAO,EAC1F3K,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,iBAAK,EAAOA,MAAC,QAAK,UAAU,OAAQ,WAAU,MAAM,GAAO,EACzGe,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,gBAAI,EAAOA,MAAC,QAAM,SAAA0L,EAAU,KAAK,GAAO,EACtF3K,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,uBAAW,EAAOA,MAAC,QAAM,SAAA0L,EAAU,YAAY,OAAO,GAAO,EAC1GA,EAAU,aACT3K,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,qBAAS,EAAOA,MAAC,QAAM,aAAI,KAAK0L,EAAU,WAAW,EAAE,oBAAmB,CAAE,GAAO,GAErI,GACF,GACF,EAEJ,CAEJ,CC1IO,SAASK,GAAW,CAAE,SAAAtI,EAAU,WAAAuI,GAAqB,WAC1D,KAAM,CAAClF,EAAQC,CAAS,EAAIrF,WAAS,EAAE,EAEjC4H,EAAU,CACd,CACE,GAAI,YACJ,KAAM2C,EACN,MAAO,QACP,MAAOxI,EAAS,UAAU,QAAU,EACpC,KAAM,6CAER,CACE,GAAI,SACJ,KAAMoC,GACN,MAAO,SACP,QAAOpH,EAAAgF,EAAS,SAAT,YAAAhF,EAAiB,SAAU,EAClC,KAAM,sBAER,CACE,GAAI,SACJ,KAAM8L,EACN,MAAO,SACP,MAAO,KACP,KAAM,8BAER,CACE,GAAI,OACJ,KAAMA,EACN,MAAO,OACP,QAAOtH,EAAAQ,EAAS,OAAT,YAAAR,EAAe,SAAU,EAChC,KAAM,2BAER,CACE,GAAI,YACJ,KAAMiJ,GACN,MAAO,YACP,QAAOvH,EAAAlB,EAAS,YAAT,YAAAkB,EAAoB,SAAU,EACrC,KAAM,+BAER,CACE,GAAI,UACJ,KAAMyG,GACN,MAAO,UACP,MAAO,KACP,KAAM,2BAER,CACE,GAAI,SACJ,KAAMe,GACN,MAAO,SACP,MAAO,KACP,KAAM,iCACR,EAGIzH,EAAWoC,EAAO,OACpBwC,EAAQ,OAAQ1K,GAAMA,EAAE,MAAM,cAAc,SAASkI,EAAO,aAAa,GAAKlI,EAAE,KAAK,cAAc,SAASkI,EAAO,aAAa,CAAC,EACjIwC,EAEJ,OACEvI,OAAC,OAAI,UAAU,cAEb,UAAAf,MAAC,OAAI,UAAU,uBACb,SAAAA,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,UACZ,MAAO8G,EACP,SAAWlI,GAAMmI,EAAUnI,EAAE,OAAO,KAAK,EACzC,MAAO,CAAE,KAAM,EAAE,GAErB,QAGC,OAAI,UAAU,mBACZ,SAAA8F,EAAS,IAAK2F,GAAU,CACvB,MAAMnJ,EAAOmJ,EAAM,KACnB,OACEtJ,OAAC,UAEC,KAAK,SACL,UAAU,uBACV,QAAS,IAAMiL,EAAW3B,EAAM,EAAE,EAElC,UAAArK,MAAC,OAAI,UAAU,uBACb,eAACkB,EAAA,CAAK,KAAM,GAAI,EAClB,EACAH,OAAC,OAAI,UAAU,0BACb,UAAAA,OAAC,QAAK,UAAU,wBACb,UAAAsJ,EAAM,MACNA,EAAM,OAAS,MACdrK,MAAC,QAAK,UAAU,wBAAyB,WAAM,MAAM,GAEzD,EACAA,MAAC,QAAK,UAAU,uBAAwB,WAAM,KAAK,GACrD,EACAA,MAACoM,GAAA,CAAa,KAAM,GAAI,UAAU,wBAAwB,IAjBrD/B,EAAM,GAoBjB,CAAC,EACH,EAGArK,MAAC,OAAI,UAAU,qBACb,gBAAC,KAAE,KAAK,cAAc,UAAU,mBAAmB,+BAC/BA,MAACoM,GAAA,CAAa,KAAM,GAAI,GAC5C,EACF,GACF,CAEJ,CC1GA,MAAMC,GAAgB,CACpB,CAAE,GAAI,OAAQ,MAAO,OAAQ,MAAO,eACpC,CAAE,GAAI,OAAQ,MAAO,OAAQ,MAAO,mBACpC,CAAE,GAAI,WAAY,MAAO,WAAY,MAAO,iBAC5C,CAAE,GAAI,WAAY,MAAO,WAAY,MAAO,iBAC9C,EAEA,SAASC,GAAaC,EAAsB,OAC1C,QAAO9N,EAAA4N,GAAc,KAAMpL,GAAMA,EAAE,KAAOsL,CAAI,IAAvC,YAAA9N,EAA0C,QAAS,iBAC5D,CAEA,SAAS+N,GAAY,CAAE,KAAAD,EAAM,KAAA1M,EAAO,IAAuC,CACzE,OAAQ0M,EAAA,CACN,IAAK,OAAQ,aAAQE,GAAA,CAAY,KAAA5M,EAAY,MAAO,CAAE,MAAO,eAAiB,EAC9E,IAAK,OAAQ,aAAQ6M,GAAA,CAAW,KAAA7M,EAAY,MAAO,CAAE,MAAO,mBAAqB,EACjF,IAAK,WAAY,aAAQ4M,GAAA,CAAY,KAAA5M,EAAY,MAAO,CAAE,MAAO,iBAAmB,EACpF,IAAK,WAAY,aAAQ8M,GAAA,CAAW,KAAA9M,EAAY,MAAO,CAAE,MAAO,kBAAoB,EACpF,QAAS,OAAOG,MAAC0M,IAAW,KAAA7M,EAAY,EAE5C,CAEO,SAAS+M,GAAqB,CAAE,KAAAC,EAAM,OAAArG,GAAiB,CAC5D,KAAM,CAACsG,EAAQC,CAAS,EAAIrL,WAAwB,IAAI,EAClD,CAACsL,EAAMC,CAAO,EAAIvL,WAAyF,IAAI,EAC/G,CAACmC,EAASC,CAAU,EAAIpC,WAAS,EAAI,EACrC,CAACwL,EAAUC,CAAW,EAAIzL,WAAS,EAAK,EACxC,CAAC0L,EAAYC,CAAa,EAAI3L,WAA+B,IAAI,EACjE,CAAC4L,EAAcC,CAAe,EAAI7L,WAAS,EAAK,EAChD,CAAC8L,EAAgBC,CAAiB,EAAI/L,WAAS,EAAK,EACpD,CAACgM,EAAgBC,CAAiB,EAAIjM,WAAS,EAAK,EACpD,CAACkM,EAAaC,CAAc,EAAInM,WAAS,EAAE,EAE3C+F,EAAS,SAAY,CACzB3D,EAAW,EAAI,EACf,GAAI,CACF,MAAMgK,EAAO,MAAM3P,EAAI,IACrB,UAAU,mBAAmB0O,CAAI,CAAC,IAEpCE,EAAUe,EAAK,MAAM,EACrBb,EAAQa,EAAK,IAAI,CACnB,MAAQ,CAER,SACEhK,EAAW,EAAK,CAClB,CACF,EAEAnB,YAAU,IAAM,CAAE8E,EAAA,CAAU,EAAG,CAACoF,CAAI,CAAC,EAErC,MAAMkB,EAAa,MAAOxB,EAAczK,EAAekM,IAAoB,CACzE,GAAI,CACF,MAAMC,IAAQnB,GAAA,YAAAA,EAAQ,WAAY,IAAI,OAAO,CAACoB,EAAKC,IAAO,KAAK,IAAID,EAAKC,EAAG,GAAK,CAAC,EAAG,EAAE,EAChFC,IAAQtB,GAAA,YAAAA,EAAQ,WAAY,IAAI,OAAO,CAACoB,EAAKC,IAAO,KAAK,IAAID,EAAKC,EAAG,GAAK,CAAC,EAAG,EAAE,EACtF,MAAMhQ,EAAI,KAAK,UAAU,mBAAmB0O,CAAI,CAAC,YAAa,CAC5D,KAAAN,EACA,MAAAzK,EACA,QAAAkM,EACA,EAAG,GAAKC,EACR,EAAG,GAAKG,EACR,MAAO,IACP,OAAQ,IACT,EACD,MAAM3G,EAAA,EACNgG,EAAkB,EAAK,CACzB,MAAQ,CAER,CACF,EAEMY,EAAgB,MAAOzM,EAAY/D,IAAiC,CACxE,GAAI,CACF,MAAMM,EAAI,IAAI,UAAU,mBAAmB0O,CAAI,CAAC,aAAa,mBAAmBjL,CAAE,CAAC,GAAI/D,CAAI,EAC3F,MAAM4J,EAAA,EACN4F,EAAc,IAAI,CACpB,MAAQ,CAER,CACF,EAEMiB,EAAgB,MAAO1M,GAAe,CAC1C,GAAK,QAAQ,sBAAsB,EACnC,GAAI,CACF,MAAMzD,EAAI,IAAI,UAAU,mBAAmB0O,CAAI,CAAC,aAAa,mBAAmBjL,CAAE,CAAC,EAAE,EACrFyL,EAAc,IAAI,EAClB,MAAM5F,EAAA,CACR,MAAQ,CAER,CACF,EAEM8G,EAAa,MAAOrQ,EAAcsQ,IAA6B,CACnE,GAAI,CACF,MAAMhR,EAAOgR,EACT,UAAU,mBAAmB3B,CAAI,CAAC,aAAa,mBAAmB2B,CAAS,CAAC,YAC5E,UAAU,mBAAmB3B,CAAI,CAAC,YACtC,MAAM1O,EAAI,KAAKX,EAAM,CAAE,KAAAU,EAAM,UAAAsQ,EAAW,EACxC,MAAM/G,EAAA,EACNoG,EAAe,EAAE,EACjBF,EAAkB,EAAK,CACzB,MAAQ,CAER,CACF,EAEA,OAAI9J,GAAW,CAACiJ,GAAU,CAACE,EAEvBhN,MAAC,OAAI,UAAU,cACb,SAAAA,MAAC,OAAI,UAAU,iBAAiB,SAAAA,MAAC,KAAE,2BAAe,EAAI,EACxD,EAKFe,OAAC,OAAI,UAAU,iCAEb,UAAAA,OAAC,OAAI,UAAU,oBACb,UAAAA,OAAC,OAAI,UAAU,qBACb,UAAAf,MAAC,QAAK,UAAU,sBAAuB,SAAAgN,EAAK,OAASH,EAAK,EAC1D7M,MAAC,QAAK,UAAU,uBAAuB,cAAagN,EAAK,OAAS,WAAK,OAAO,GAChF,EACAjM,OAAC,OAAI,UAAU,wBACb,UAAAf,MAAC,UACC,KAAK,SACL,UAAW,mBAAmBkN,EAAW,SAAW,EAAE,GACtD,QAAS,IAAMC,EAAa3H,GAAM,CAACA,CAAC,EACpC,aAAY0H,EAAW,eAAiB,OAExC,SAAAlN,MAACyO,GAAA,CAAO,KAAM,GAAI,IAEpB1N,OAAC,UACC,KAAK,SACL,UAAU,kBACV,QAAS,IAAMwM,EAAiB/H,GAAM,CAACA,CAAC,EACxC,aAAW,WAEX,UAAAxF,MAAC0O,EAAA,CAAc,KAAM,GAAI,EACxB5B,EAAO,SAAS,OAAS,GACxB9M,MAAC,QAAK,UAAU,8BAA+B,SAAA8M,EAAO,SAAS,OAAO,KAGzEI,GACClN,MAAC,UACC,KAAK,SACL,UAAU,kBACV,QAAS,IAAMyN,EAAkB,EAAI,EACrC,aAAW,cAEX,SAAAzN,MAACkI,EAAA,CAAK,KAAM,GAAI,GAClB,EAEJ,GACF,EAGAnH,OAAC,OAAI,UAAU,yBACZ,UAAA+L,EAAO,SAAS,SAAW,GAC1B/L,OAAC,OAAI,UAAU,eACb,UAAAf,MAAC,KAAE,4BAAgB,EAClBkN,GACCnM,OAAC,UACC,KAAK,SACL,UAAU,aACV,QAAS,IAAM0M,EAAkB,EAAI,EAErC,UAAAzN,MAACkI,EAAA,CAAK,KAAM,GAAI,EAAE,iBACpB,EAEJ,EAEC4E,EAAO,SAAS,IAAKqB,GACtBpN,OAAC,UAEC,KAAK,SACL,UAAW,0BAAyBqM,GAAA,YAAAA,EAAY,MAAOe,EAAG,GAAK,WAAa,EAAE,GAC9E,MAAO,CAAE,YAAa7B,GAAa6B,EAAG,IAAI,GAC1C,QAAS,IAAM,CACTjB,GACFG,EAAcc,CAAE,CAEpB,EACA,gBAAcf,GAAA,YAAAA,EAAY,MAAOe,EAAG,GAEpC,UAAApN,OAAC,OAAI,UAAU,0BAA0B,MAAO,CAAE,WAAY,GAAGuL,GAAa6B,EAAG,IAAI,CAAC,MACpF,UAAAnO,MAACwM,GAAA,CAAY,KAAM2B,EAAG,KAAM,KAAM,GAAI,EACtCnO,MAAC,QAAK,UAAU,wBAAwB,MAAO,CAAE,MAAOsM,GAAa6B,EAAG,IAAI,GAAM,WAAG,KAAK,EACzFA,EAAG,QAAUA,EAAG,SAAW,cACzB,QAAK,UAAU,0BAA2B,SAAAA,EAAG,OAAO,GAEzD,QACC,OAAI,UAAU,yBAA0B,SAAAA,EAAG,OAAS,WAAW,EAC/DA,EAAG,SACFpN,OAAC,OAAI,UAAU,2BACZ,UAAAoN,EAAG,QAAQ,MAAM,EAAG,GAAG,EAAGA,EAAG,QAAQ,OAAS,IAAM,IAAM,IAC7D,EAEFpN,OAAC,OAAI,UAAU,4BACb,UAAAf,MAAC0O,EAAA,CAAc,KAAM,GAAI,EACxB5B,EAAO,SAAS,OAAQ3E,GAAMA,EAAE,YAAcgG,EAAG,EAAE,EAAE,QACxD,IA3BKA,EAAG,GA6BX,GACH,EAGCb,GACCvM,OAAC,OAAI,UAAU,yBACb,UAAAA,OAAC,OAAI,UAAU,gCACb,UAAAA,OAAC,MAAG,uBAAW+L,EAAO,SAAS,OAAO,KAAC,EACvC9M,MAAC,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAMuN,EAAgB,EAAK,EACpF,SAAAvN,MAACQ,EAAA,CAAE,KAAM,GAAI,EACf,GACF,EACAO,OAAC,OAAI,UAAU,6BACZ,UAAA+L,EAAO,SAAS,SAAW,SACzB,KAAE,UAAU,sBAAsB,4BAAgB,EAEpDA,EAAO,SAAS,IAAK3E,GACpBpH,OAAC,OAAe,UAAU,wBACxB,UAAAA,OAAC,OAAI,UAAU,6BACb,UAAAf,MAAC,QAAK,UAAU,OAAQ,SAAAmI,EAAE,OAAO,QAChC,QAAK,UAAU,QAAS,SAAAhM,EAAegM,EAAE,OAAO,EAAE,GACrD,EACAnI,MAAC,OAAI,UAAU,6BAA8B,WAAE,KAAK,IAL5CmI,EAAE,EAMZ,CACD,GACH,EACApH,OAAC,OAAI,UAAU,8BACb,UAAAf,MAAC,YACC,UAAU,eACV,KAAM,EACN,YAAY,iBACZ,MAAO4N,EACP,SAAWhP,GAAMiP,EAAejP,EAAE,OAAO,KAAK,IAEhDoB,MAAC,UACC,KAAK,SACL,UAAU,aACV,SAAU,CAAC4N,EAAY,OACvB,QAAS,IAAM,CAAEW,EAAWX,EAAY,OAAQ,IAAI,CAAG,EACxD,iBAED,EACF,GACF,EAIDR,GACCpN,MAAC6B,EAAA,CACC,KAAM,GACN,QAAS,IAAMwL,EAAc,IAAI,EACjC,MAAO,QAAQD,EAAW,OAASA,EAAW,IAAI,GAClD,QACErM,OAAC,OAAI,UAAU,6BACb,UAAAA,OAAC,UACC,KAAK,SACL,UAAU,+BACV,QAAS,IAAMuN,EAAclB,EAAW,EAAE,EAE1C,UAAApN,MAACqI,GAAA,CAAO,KAAM,GAAI,EAAE,aAEtBtH,OAAC,UACC,KAAK,SACL,UAAU,aACV,QAAS,IAAM4M,EAAkB,EAAI,EAErC,UAAA3N,MAAC0O,EAAA,CAAc,KAAM,GAAI,EAAE,aAC7B,EACF,EAGF,SAAA1O,MAAC2O,GAAA,CACC,QAASvB,EACT,OAASvP,GAASwQ,EAAcjB,EAAW,GAAIvP,CAAI,EACnD,QAAS,IAAMwP,EAAc,IAAI,GACnC,GAKJrN,MAAC4O,GAAA,CACC,KAAMpB,EACN,QAAS,IAAMC,EAAkB,EAAK,EACtC,MAAOM,CAAA,GAIT/N,MAACkG,EAAA,CAAY,KAAMwH,EAAgB,QAAS,IAAMC,EAAkB,EAAK,EAAG,MAAM,cAAc,QAC9F5M,OAAC,UACC,KAAK,SACL,UAAU,aACV,MAAO,CAAE,MAAO,QAChB,SAAU,CAAC6M,EAAY,OACvB,QAAS,IAAM,CAAEW,EAAWX,EAAY,QAAQR,GAAA,YAAAA,EAAY,KAAM,IAAI,EAAGO,EAAkB,EAAK,CAAG,EAEnG,UAAA3N,MAAC0O,EAAA,CAAc,KAAM,GAAI,EAAE,WAG7B,SAAA1O,MAAC,YACC,UAAU,eACV,KAAM,EACN,YAAY,gBACZ,MAAO4N,EACP,SAAWhP,GAAMiP,EAAejP,EAAE,OAAO,KAAK,EAC9C,UAAS,IACX,CACF,GACF,CAEJ,CAEA,SAAS+P,GAAgB,CACvB,QAAAE,EACA,OAAAC,EACA,QAAAlO,CACF,EAIG,CACD,KAAM,CAAC2L,EAAMwC,CAAO,EAAIrN,WAASmN,EAAQ,IAAI,EACvC,CAAC/M,EAAOkN,CAAQ,EAAItN,WAASmN,EAAQ,OAAS,EAAE,EAChD,CAACb,EAASiB,CAAU,EAAIvN,WAASmN,EAAQ,SAAW,EAAE,EACtD,CAAC9R,EAAQmS,CAAS,EAAIxN,WAASmN,EAAQ,QAAU,MAAM,EAE7D,OACE9N,OAAC,QACC,UAAU,mBACV,SAAWnC,GAAM,CACfA,EAAE,iBACFkQ,EAAO,CAAE,KAAAvC,EAAM,MAAAzK,EAAO,QAAAkM,EAAS,OAAAjR,EAAQ,EACvC6D,EAAA,CACF,EAEA,UAAAZ,MAAC,SAAM,UAAU,qBAAqB,gBAAI,EAC1CA,MAAC,UAAO,UAAU,eAAe,MAAOuM,EAAM,SAAW3N,GAAMmQ,EAAQnQ,EAAE,OAAO,KAAK,EAClF,SAAAyN,GAAc,IAAKpL,GAClBjB,MAAC,UAAkB,MAAOiB,EAAE,GAAK,SAAAA,EAAE,OAAtBA,EAAE,EAA0B,CAC1C,EACH,EACAjB,MAAC,SAAM,UAAU,qBAAqB,iBAAK,EAC3CA,MAAC,SACC,UAAU,eACV,KAAK,OACL,MAAO8B,EACP,SAAWlD,GAAMoQ,EAASpQ,EAAE,OAAO,KAAK,IAE1CoB,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,SACC,UAAU,eACV,KAAK,OACL,MAAOjD,EACP,SAAW6B,GAAMsQ,EAAUtQ,EAAE,OAAO,KAAK,EACzC,YAAY,0BAEdoB,MAAC,SAAM,UAAU,qBAAqB,8BAAkB,EACxDA,MAAC,YACC,UAAU,eACV,KAAM,EACN,MAAOgO,EACP,SAAWpP,GAAMqQ,EAAWrQ,EAAE,OAAO,KAAK,IAE5CoB,MAAC,UAAO,KAAK,SAAS,UAAU,aAAa,MAAO,CAAE,MAAO,OAAQ,UAAW,GAAK,gBAErF,IAGN,CAEA,SAAS4O,GAAgB,CACvB,KAAAjO,EACA,QAAAC,EACA,MAAAuO,CACF,EAIG,CACD,KAAM,CAAC5C,EAAMwC,CAAO,EAAIrN,WAAS,MAAM,EACjC,CAACI,EAAOkN,CAAQ,EAAItN,WAAS,EAAE,EAC/B,CAACsM,EAASiB,CAAU,EAAIvN,WAAS,EAAE,EAEnCyD,EAAgBvG,GAAuB,CAC3CA,EAAE,iBACGkD,EAAM,SACXqN,EAAM5C,EAAMzK,EAAM,OAAQkM,EAAQ,MAAM,EACxCgB,EAAS,EAAE,EACXC,EAAW,EAAE,EACf,EAEA,aACG/I,EAAA,CAAY,KAAAvF,EAAY,QAAAC,EAAkB,MAAM,cAAc,QAC7DG,OAAC,UAAO,KAAK,SAAS,KAAK,cAAc,UAAU,aAAa,MAAO,CAAE,MAAO,QAC9E,UAAAf,MAACkI,EAAA,CAAK,KAAM,GAAI,EAAE,QACpB,EAEA,gBAAC,QAAK,GAAG,cAAc,SAAU/C,EAAc,UAAU,mBACvD,UAAAnF,MAAC,SAAM,UAAU,qBAAqB,gBAAI,EAC1CA,MAAC,UAAO,UAAU,eAAe,MAAOuM,EAAM,SAAW3N,GAAMmQ,EAAQnQ,EAAE,OAAO,KAAK,EAClF,SAAAyN,GAAc,IAAKpL,GAClBjB,MAAC,UAAkB,MAAOiB,EAAE,GAAK,SAAAA,EAAE,OAAtBA,EAAE,EAA0B,CAC1C,EACH,EACAjB,MAAC,SAAM,UAAU,qBAAqB,mBAAO,EAC7CA,MAAC,SACC,UAAU,eACV,KAAK,OACL,YAAY,gBACZ,MAAO8B,EACP,SAAWlD,GAAMoQ,EAASpQ,EAAE,OAAO,KAAK,EACxC,UAAS,GACT,SAAQ,KAEVoB,MAAC,SAAM,UAAU,qBAAqB,8BAAkB,EACxDA,MAAC,YACC,UAAU,eACV,KAAM,EACN,YAAY,eACZ,MAAOgO,EACP,SAAWpP,GAAMqQ,EAAWrQ,EAAE,OAAO,KAAK,GAC5C,EACF,EACF,CAEJ,CCzaO,SAASwQ,GAAgB,CAAE,SAAA3L,EAAU,OAAA+C,EAAQ,eAAA6I,GAAyB,CAC3E,KAAM,CAACC,EAAWC,CAAY,EAAI7N,WAAqB+B,EAAS,WAAa,EAAE,EACzE,CAACI,EAASC,CAAU,EAAIpC,WAAS,CAAC+B,EAAS,SAAS,EACpD,CAACM,EAAQC,CAAS,EAAItC,WAAS,EAAE,EACjC,CAACwF,EAASC,CAAU,EAAIzF,WAAS,EAAK,EACtC,CAAC8N,EAASC,CAAU,EAAI/N,WAAS,EAAE,EACnC,CAACgO,EAAUC,CAAW,EAAIjO,WAAS,EAAE,EAErC+F,EAAS,SAAY,CACzB,GAAI,CACF,MAAMzK,EAAO,MAAMmB,EAAI,IAA+B,YAAY,EAClEoR,EAAavS,EAAK,WAAa,EAAE,CACnC,MAAQ,CAER,SACE8G,EAAW,EAAK,CAClB,CACF,EAEM8L,EAAa,SAAY,CAC7B,GAAKJ,EAAQ,OACb,GAAI,CACF,MAAMK,EAAS,MAAM1R,EAAI,KAAuB,aAAc,CAC5D,KAAMqR,EAAQ,OACd,MAAOE,EAAS,QAAU,OAC3B,EACDvI,EAAW,EAAK,EAChBsI,EAAW,EAAE,EACbE,EAAY,EAAE,EACdN,EAAeQ,EAAO,IAAI,CAC5B,MAAQ,CAER,CACF,EAEMnL,EAAWX,EAAO,OACpBuL,EAAU,OAAQQ,IACfA,EAAE,OAASA,EAAE,MAAQ,IAAI,cAAc,SAAS/L,EAAO,aAAa,GAEvEuL,EAEJ,OACEvO,OAAC,OAAI,UAAU,cACb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,oBACZ,MAAO+D,EACP,SAAWnF,GAAMoF,EAAUpF,EAAE,OAAO,KAAK,EACzC,MAAO,CAAE,KAAM,EAAE,SAElB,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAM6I,EAAA,EAAU,aAAW,UACpF,SAAAzH,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,GACF,QAGC,OAAI,MAAO,CAAE,aAAc,IAC1B,SAAA5E,OAAC,UACC,KAAK,SACL,UAAU,aACV,MAAO,CAAE,MAAO,QAChB,QAAS,IAAMoG,EAAW,EAAI,EAE9B,UAAAnH,MAACkI,EAAA,CAAK,KAAM,GAAI,EAAE,mBAEtB,EAECrE,EACC7D,MAAC,OAAI,UAAU,iBAAiB,eAAC,KAAE,oBAAQ,EAAI,EAC7C0E,EAAS,SAAW,EACtB3D,OAAC,OAAI,UAAU,eACb,UAAAf,MAACiM,EAAA,CAAS,KAAM,GAAI,EACpBjM,MAAC,KAAE,6BAAiB,GACtB,QAEC,OAAI,UAAU,mBACZ,SAAA0E,EAAS,IAAKoL,GACb/O,OAAC,UAEC,KAAK,SACL,UAAU,gDACV,QAAS,IAAMsO,EAAeS,EAAE,IAAI,EAEpC,UAAA9P,MAAC,OAAI,UAAU,mBAAmB,eAACiM,EAAA,CAAS,KAAM,GAAI,EAAE,EACxDlL,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,QAAK,UAAU,oBAAqB,SAAA8P,EAAE,OAASA,EAAE,KAAK,EACvD/O,OAAC,QAAK,UAAU,mBACb,UAAA+O,EAAE,OAAO,MAAIA,EAAE,OACfA,EAAE,cAAgB,KAAO,MAAMA,EAAE,YAAY,YAAc,GAC3DA,EAAE,cAAgB,KAAO,MAAMA,EAAE,YAAY,YAAc,IAC9D,GACF,EACA9P,MAAC,QAAK,UAAW,oBAAqB,cAAa8P,EAAE,OAAS,WAAE,OAAO,IAdlEA,EAAE,KAgBV,EACH,EAGF9P,MAACkG,EAAA,CAAY,KAAMgB,EAAS,QAAS,IAAMC,EAAW,EAAK,EAAG,MAAM,eAAe,eAChF,UAAO,KAAK,SAAS,KAAK,gBAAgB,UAAU,aAAa,MAAO,CAAE,MAAO,QAChF,UAAAnH,MAACkI,EAAA,CAAK,KAAM,GAAI,EAAE,WACpB,EAEA,SAAAnH,OAAC,QAAK,GAAG,gBAAgB,SAAWnC,GAAM,CAAEA,EAAE,iBAAkBgR,EAAA,CAAc,EAAG,UAAU,mBACzF,UAAA5P,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,SACC,UAAU,eACV,KAAK,OACL,YAAY,UACZ,QAAQ,0BACR,MAAOwP,EACP,SAAW5Q,GAAM6Q,EAAW7Q,EAAE,OAAO,KAAK,EAC1C,UAAS,GACT,SAAQ,KAEVoB,MAAC,SAAM,UAAU,qBAAqB,4BAAgB,EACtDA,MAAC,SACC,UAAU,eACV,KAAK,OACL,YAAY,cACZ,MAAO0P,EACP,SAAW9Q,GAAM+Q,EAAY/Q,EAAE,OAAO,KAAK,GAC7C,EACF,EACF,GACF,CAEJ,CCtHA,MAAMmR,GAAiC,CAAC,OAAQ,WAAY,MAAM,EAC5DC,GAA6C,CAAC,UAAW,QAAS,SAAS,EAE3EC,GAAY,CAAC,MAAO,mBAAoB,sBAAuB,gBAAiB,gBAAiB,YAAY,EAE7GC,GAAQ,MAAM,KAAK,CAAE,OAAQ,IAAM,CAACC,EAAGtR,KAAO,CAClD,MAAOA,EACP,MAAOA,IAAM,EAAI,QAAUA,EAAI,GAAK,GAAGA,CAAC,MAAQA,IAAM,GAAK,QAAU,GAAGA,EAAI,EAAE,KAChF,EAAE,EACIuR,GAAU,MAAM,KAAK,CAAE,OAAQ,IAAM,CAACD,EAAGpK,IAAMA,EAAI,CAAC,EACpDsK,GAAO,CACX,CAAE,MAAO,IAAK,MAAO,aACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,MACvB,EACMC,GAAiB,CACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,OACrB,CAAE,MAAO,IAAK,MAAO,MACrB,CAAE,MAAO,IAAK,MAAO,MACvB,EAEO,SAASC,GAAgB,CAAE,SAAA9M,GAAmB,OACnD,KAAM,CAAC+M,EAAWC,CAAY,EAAI/O,WAAqB+B,EAAS,WAAa,EAAE,EACzE,CAACI,EAASC,CAAU,EAAIpC,WAAS,CAAC+B,EAAS,SAAS,EACpD,CAACM,EAAQC,CAAS,EAAItC,WAAS,EAAE,EACjC,CAACgP,EAAgBC,CAAiB,EAAIjP,WAA0B,IAAI,EACpE,CAACkP,EAAQC,CAAS,EAAInP,WAAgD,CAAE,KAAM,GAAO,EAErF+F,EAAS,SAAY,CACzB,GAAI,CACF,MAAMzK,EAAO,MAAMmB,EAAI,IAA+B,YAAY,EAClEsS,EAAazT,EAAK,WAAa,EAAE,CACnC,MAAQ,CAER,SACE8G,EAAW,EAAK,CAClB,CACF,EAEAnB,YAAU,IAAM,CACVc,EAAS,WACXgN,EAAahN,EAAS,SAAS,EAC/BK,EAAW,EAAK,GAEhB2D,EAAA,CAEJ,EAAG,CAAChE,EAAS,SAAS,CAAC,EAIvB,MAAMqN,EAAiB,MAAOlP,EAAYiK,IAAqB,CAC7D,GAAI,CACF,MAAM1N,EAAI,MAAM,cAAc,mBAAmByD,CAAE,CAAC,GAAI,CAAE,QAAAiK,EAAS,EACnE4E,EAAcnL,GAAQA,EAAI,IAAKxG,GAAOA,EAAE,KAAO8C,EAAK,CAAE,GAAG9C,EAAG,QAAA+M,CAAA,EAAY/M,CAAE,CAAC,GACvE4R,GAAA,YAAAA,EAAgB,MAAO9O,GACzB+O,EAAmB7R,GAAOA,GAAI,CAAE,GAAGA,EAAG,QAAA+M,CAAA,CAAc,CAExD,MAAQ,CAER,CACF,EAEMkF,EAAiB,MAAOnP,GAAe,CAC3C,GAAK,QAAQ,uBAAuB,EACpC,GAAI,CACF,MAAMzD,EAAI,IAAI,cAAc,mBAAmByD,CAAE,CAAC,EAAE,EACpD6O,EAAcnL,GAAQA,EAAI,OAAQxG,GAAMA,EAAE,KAAO8C,CAAE,CAAC,GAChD8O,GAAA,YAAAA,EAAgB,MAAO9O,GAAI+O,EAAkB,IAAI,CACvD,MAAQ,CAER,CACF,EAGMK,EAAkB,MAAOpP,GAAe,CAC5C,GAAI,CACF,MAAMzD,EAAI,KAAK,cAAc,mBAAmByD,CAAE,CAAC,UAAU,CAC/D,MAAQ,CAER,CACF,EAEM8C,EAAWX,EAAO,OACpByM,EAAU,OAAQ1R,GAAMA,EAAE,KAAK,cAAc,SAASiF,EAAO,aAAa,CAAC,EAC3EyM,EAEJ,OACEzP,OAAC,OAAI,UAAU,cACb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,oBACZ,MAAO+D,EACP,SAAWnF,GAAMoF,EAAUpF,EAAE,OAAO,KAAK,EACzC,MAAO,CAAE,KAAM,EAAE,SAElB,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAM6I,EAAA,EAAU,aAAW,UACpF,SAAAzH,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,EACA3F,MAAC,UACC,KAAK,SACL,UAAU,kBACV,QAAS,IAAM6Q,EAAU,CAAE,KAAM,GAAM,EACvC,aAAW,eAEX,SAAA7Q,MAACkI,EAAA,CAAK,KAAM,GAAI,GAClB,EACF,EAECrE,EACC7D,MAAC,OAAI,UAAU,iBAAiB,eAAC,KAAE,oBAAQ,EAAI,EAC7C0E,EAAS,SAAW,EACtB3D,OAAC,OAAI,UAAU,eACb,UAAAf,MAACkM,GAAA,CAAM,KAAM,GAAI,EACjBlM,MAAC,KAAE,+BAAmB,GACxB,QAEC,OAAI,UAAU,mBACZ,SAAA0E,EAAS,IAAK5F,GACbiC,OAAC,UAEC,KAAK,SACL,UAAU,gDACV,QAAS,IAAM4P,EAAkB7R,CAAC,EAElC,UAAAkB,MAAC,OAAI,UAAU,mBAAmB,eAACkM,GAAA,CAAM,KAAM,GAAI,EAAE,EACrDnL,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,QAAK,UAAU,oBAAqB,SAAAlB,EAAE,KAAK,EAC5CiC,OAAC,QAAK,UAAU,mBACb,UAAAkQ,GAAiBnS,CAAC,EAAE,KAAG,IACvBA,EAAE,QAAU,QAAQoS,GAAgBpS,EAAE,QAASA,EAAE,QAAQ,CAAC,GAAK,eAClE,GACF,EACAkB,MAAC,QAAK,UAAW,qBAAqBlB,EAAE,QAAU,WAAa,WAAW,GACvE,SAAAA,EAAE,QAAU,KAAO,MACtB,IAfKA,EAAE,GAiBV,EACH,EAGD4R,GACC1Q,MAAC6B,EAAA,CACC,KAAM,GACN,QAAS,IAAM8O,EAAkB,IAAI,EACrC,MAAOD,EAAe,KACtB,QACE3P,OAAC,OAAI,UAAU,6BACb,UAAAA,OAAC,UACC,KAAK,SACL,UAAU,aACV,QAAS,IAAM,CAAM2P,GAAgBM,EAAgBN,EAAe,EAAE,CAAG,EAEzE,UAAA1Q,MAACyF,GAAA,CAAK,KAAM,GAAI,EAAE,kBAEpBzF,MAAC,UACC,KAAK,SACL,UAAW,cAAe0Q,EAAe,QAAe,uBAAL,EAA2B,GAC9E,QAAS,IAAM,CAAMA,GAAgBI,EAAeJ,EAAe,GAAI,CAACA,EAAe,OAAO,CAAG,EAEhG,SAAAA,EAAe,QAAU,UAAY,WAExC3P,OAAC,UACC,KAAK,SACL,UAAU,aACV,QAAS,IAAM,CACR2P,IACLG,EAAU,CAAE,KAAM,GAAM,QAASH,EAAgB,EACjDC,EAAkB,IAAI,EACxB,EAEA,UAAA3Q,MAACyO,GAAA,CAAO,KAAM,GAAI,EAAE,WAEtBzO,MAAC,UACC,KAAK,SACL,UAAU,+BACV,QAAS,IAAM,CAAM0Q,GAAgBK,EAAeL,EAAe,EAAE,CAAG,EAExE,SAAA1Q,MAACqI,GAAA,CAAO,KAAM,GAAI,GACpB,EACF,EAGF,eAAC,OAAI,UAAU,sBACb,SAAAtH,OAAC,OAAI,UAAU,2BACb,UAAAA,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,gBAAI,EACVA,MAAC,QAAM,SAAA0Q,EAAe,KAAK,GAC7B,EACA3P,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,oBAAQ,EACdA,MAAC,QAAK,UAAU,OAAQ,YAAiB0Q,CAAc,GAAKA,EAAe,SAAS,GACtF,EACCA,EAAe,UACd3P,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,oBAAQ,EACdA,MAAC,QAAK,UAAU,OAAQ,WAAe,SAAS,GAClD,EAEFe,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,kBAAM,SACX,QAAM,UAAA0Q,EAAe,OAAO,KAAK,KAAGA,EAAe,OAAO,QAAO,GACpE,EACCA,EAAe,OAAO,OAAS,SAAWA,EAAe,OAAO,QAC/D3P,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,kBAAM,EACZA,MAAC,QAAK,MAAO,CAAE,SAAU,IAAO,SAAA0Q,EAAe,OAAO,OAAO,GAC/D,IAEDjS,EAAAiS,EAAe,cAAf,YAAAjS,EAA4B,kBAC3BsC,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,uBAAW,SAChB,QAAK,uBAAW0Q,EAAe,YAAY,eAAiB,EAAE,eAAW,GAC5E,EAEF3P,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,oBAAQ,EACdA,MAAC,QAAM,SAAA0Q,EAAe,QAAUQ,GAAgBR,EAAe,QAASA,EAAe,QAAQ,EAAI,IAAI,GACzG,EACA3P,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,oBAAQ,EACdA,MAAC,QAAM,SAAA0Q,EAAe,QAAUS,GAAUT,EAAe,OAAO,EAAI,IAAI,GAC1E,EACCA,EAAe,YACd3P,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,uBAAW,EACjBA,MAAC,QAAK,MAAO,CAAE,SAAU,IAAO,WAAe,WAAW,GAC5D,EAED0Q,EAAe,WACd3P,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,sBAAU,EAChBA,MAAC,QAAK,MAAO,CAAE,SAAU,IAAO,WAAe,UAAU,GAC3D,GAEJ,EACF,IAIJA,MAACoR,GAAA,CACC,KAAMR,EAAO,KACb,QAASA,EAAO,QAChB,QAAS,IAAMC,EAAU,CAAE,KAAM,GAAO,EACxC,QAAS,IAAM,CACbA,EAAU,CAAE,KAAM,GAAO,EACzBpJ,EAAA,CACF,GACF,EACF,CAEJ,CAwBA,SAAS4J,GAAmBC,EAAiC,SAC3D,GAAI,CAACA,EACH,MAAO,CACL,KAAM,GACN,KAAM,OACN,WAAY,EACZ,SAAU,GACV,QAAS,IACT,UAAW,GACX,aAAc,IACd,OAAQ,GACR,SAAU,MACV,WAAY,QACZ,aAAc,GACd,aAAc,GACd,gBAAiB,GACjB,cAAe,EACf,QAAS,IAGb,MAAMC,EAAaD,EAAQ,OAAS,OAASE,GAAgBF,EAAQ,QAAQ,EAAI,KAC3EG,EAAiBH,EAAQ,OAAS,WAAaI,GAAoBJ,EAAQ,QAAQ,EAAI,KAC7F,MAAO,CACL,KAAMA,EAAQ,MAAQ,GACtB,KAAMA,EAAQ,KACd,YAAYC,GAAA,YAAAA,EAAY,SAAU,EAClC,UAAUA,GAAA,YAAAA,EAAY,OAAQ,EAC9B,SAASA,GAAA,YAAAA,EAAY,MAAO,IAC5B,WAAWE,GAAA,YAAAA,EAAgB,IAAK,GAChC,cAAcA,GAAA,YAAAA,EAAgB,OAAQ,IACtC,OAAQH,EAAQ,OAAS,OAASK,GAAgBL,EAAQ,QAAQ,EAAI,GACtE,SAAUA,EAAQ,UAAY,MAC9B,WAAYA,EAAQ,OAAO,KAC3B,aAAcA,EAAQ,OAAO,QAAU,GACvC,aAAcA,EAAQ,OAAO,QAAU,GACvC,gBAAiB,CAAC,GAAC7S,EAAA6S,EAAQ,cAAR,MAAA7S,EAAqB,iBACxC,cAAe,OAAO,UAASwE,EAAAqO,EAAQ,cAAR,YAAArO,EAAqB,aAAa,EAC7DqO,EAAQ,YAAa,cACrB,EACJ,QAASA,EAAQ,UAAY,GAEjC,CAEA,SAASM,GAAqB,EAAwB,CACpD,GAAI,EAAE,OAAS,OAAQ,MAAO,GAAG,EAAE,UAAU,IAAI,EAAE,QAAQ,QAAQ,EAAE,OAAO,GAC5E,GAAI,EAAE,OAAS,WAAY,MAAO,GAAG,EAAE,SAAS,GAAG,EAAE,YAAY,GACjE,GAAI,CAAC,EAAE,OAAQ,MAAO,GACtB,GAAI,CACF,OAAO,IAAI,KAAK,EAAE,MAAM,EAAE,aAC5B,MAAQ,CACN,OAAO,EAAE,MACX,CACF,CAEA,SAASR,GAAe,CACtB,KAAAzQ,EACA,QAAA2Q,EACA,QAAA1Q,EACA,QAAAiR,CACF,EAKG,CACD,KAAM,CAACC,EAAOC,CAAQ,EAAIrQ,WAAsB,IAAM2P,GAAmBC,CAAO,CAAC,EAC3E,CAAC/M,EAAYC,CAAa,EAAI9C,WAAS,EAAK,EAGlDiB,YAAU,IAAM,CACVhC,GAAMoR,EAASV,GAAmBC,CAAO,CAAC,CAChD,EAAG,CAAC3Q,EAAM2Q,CAAO,CAAC,EAElB,MAAMU,EAAS,CAA8BnI,EAAQC,IAA0B,CAC7EiI,EAAUzM,IAAS,CAAE,GAAGA,EAAK,CAACuE,CAAG,EAAGC,CAAA,EAAQ,CAC9C,EAEM3E,EAAe,MAAOvG,GAAuB,CAEjD,GADAA,EAAE,iBACE,CAACkT,EAAM,KAAK,OAAQ,OACxB,MAAMG,EAAWL,GAAqBE,CAAK,EAC3C,GAAKG,EACL,CAAAzN,EAAc,EAAI,EAClB,GAAI,CACF,MAAM0N,EAAU,CACd,KAAMJ,EAAM,KAAK,OACjB,KAAMA,EAAM,KACZ,SAAAG,EACA,SAAUH,EAAM,SAChB,OAAQ,CACN,KAAMA,EAAM,WACZ,OAAQA,EAAM,aAAa,OAC3B,GAAIA,EAAM,aAAe,SAAWA,EAAM,aAAa,OACnD,CAAE,OAAQA,EAAM,aAAa,MAAK,EAClC,EAAC,EAEP,YAAa,CACX,cAAeA,EAAM,cACrB,gBAAiBA,EAAM,iBAEzB,QAASA,EAAM,SAEbR,EACF,MAAMnT,EAAI,IAAI,cAAc,mBAAmBmT,EAAQ,EAAE,CAAC,GAAIY,CAAO,EAErE,MAAM/T,EAAI,KAAK,aAAc+T,CAAO,EAEtCL,EAAA,CACF,MAAQ,CAER,SACErN,EAAc,EAAK,CACrB,EACF,EAEA,OACExE,MAACkG,EAAA,CACC,KAAAvF,EACA,QAAAC,EACA,MAAO0Q,EAAU,QAAQA,EAAQ,IAAI,GAAK,eAC1C,QACEvQ,OAAC,UACC,KAAK,SACL,KAAK,uBACL,UAAU,aACV,MAAO,CAAE,MAAO,QAChB,SAAUwD,EAEV,UAAAvE,MAACkI,EAAA,CAAK,KAAM,GAAI,EAAE,IAAEoJ,EAAU,OAAS,YAI3C,gBAAC,QAAK,GAAG,uBAAuB,SAAUnM,EAAc,UAAU,mBAChE,UAAAnF,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,SACC,UAAU,eACV,KAAK,OACL,YAAY,gBACZ,MAAO8R,EAAM,KACb,SAAWlT,GAAMoT,EAAO,OAAQpT,EAAE,OAAO,KAAK,EAC9C,UAAS,GACT,SAAQ,KAGVoB,MAAC,SAAM,UAAU,qBAAqB,gBAAI,EAC1CA,MAAC,UACC,UAAU,eACV,MAAO8R,EAAM,KACb,SAAWlT,GAAMoT,EAAO,OAAQpT,EAAE,OAAO,KAAyB,EAEjE,SAAAmR,GAAM,IAAK9O,GAAMjB,MAAC,UAAe,MAAOiB,EAAI,SAAAA,CAAA,EAAdA,CAAgB,CAAS,IAGzD6Q,EAAM,OAAS,QACd/Q,OAAAC,WAAA,CACE,UAAAhB,MAAC,SAAM,UAAU,qBAAqB,uBAAW,EACjDA,MAAC,UACC,UAAU,eACV,MAAO8R,EAAM,QACb,SAAWlT,GAAMoT,EAAO,UAAWpT,EAAE,OAAO,KAAK,EAEhD,SAAAyR,GAAK,IAAK9T,GAAMyD,MAAC,UAAqB,MAAOzD,EAAE,MAAQ,SAAAA,EAAE,OAA5BA,EAAE,KAAgC,CAAS,IAG3EwE,OAAC,OAAI,UAAU,kBACb,UAAAA,OAAC,OAAI,MAAO,CAAE,KAAM,GAClB,UAAAf,MAAC,SAAM,UAAU,qBAAqB,gBAAI,EAC1CA,MAAC,UACC,UAAU,eACV,MAAO,OAAO8R,EAAM,QAAQ,EAC5B,SAAWlT,GAAMoT,EAAO,WAAY,SAASpT,EAAE,OAAO,MAAO,EAAE,CAAC,EAE/D,SAAAsR,GAAM,IAAKrR,SAAO,UAAqB,MAAO,OAAOA,EAAE,KAAK,EAAI,SAAAA,EAAE,OAApCA,EAAE,KAAwC,CAAS,GACpF,EACF,SACC,OAAI,MAAO,CAAE,KAAM,GAClB,UAAAmB,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,UACC,UAAU,eACV,MAAO,OAAO8R,EAAM,UAAU,EAC9B,SAAWlT,GAAMoT,EAAO,aAAc,SAASpT,EAAE,OAAO,MAAO,EAAE,CAAC,EAEjE,YAAQ,IAAKa,GAAMO,MAAC,UAAe,MAAO,OAAOP,CAAC,EAAI,gBAAOA,CAAC,EAAE,SAAS,EAAG,GAAG,GAA/CA,CAAiD,CAAS,GAC7F,EACF,GACF,GACF,EAGDqS,EAAM,OAAS,YACd/Q,OAAC,OAAI,UAAU,kBACb,UAAAA,OAAC,OAAI,MAAO,CAAE,KAAM,GAClB,UAAAf,MAAC,SAAM,UAAU,qBAAqB,iBAAK,EAC3CA,MAAC,SACC,UAAU,eACV,KAAK,SACL,IAAK,EACL,MAAO8R,EAAM,UACb,SAAWlT,GAAMoT,EAAO,YAAa,SAASpT,EAAE,OAAO,MAAO,EAAE,GAAK,CAAC,GACxE,EACF,SACC,OAAI,MAAO,CAAE,KAAM,GAClB,UAAAoB,MAAC,SAAM,UAAU,qBAAqB,gBAAI,EAC1CA,MAAC,UACC,UAAU,eACV,MAAO8R,EAAM,aACb,SAAWlT,GAAMoT,EAAO,eAAgBpT,EAAE,OAAO,KAAK,EAErD,SAAA0R,GAAe,IAAK6B,GAAMnS,MAAC,UAAqB,MAAOmS,EAAE,MAAQ,SAAAA,EAAE,OAA5BA,EAAE,KAAgC,CAAS,GACrF,EACF,GACF,EAGDL,EAAM,OAAS,QACd/Q,OAAAC,WAAA,CACE,UAAAhB,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,SACC,UAAU,eACV,KAAK,iBACL,MAAO8R,EAAM,OACb,SAAWlT,GAAMoT,EAAO,SAAUpT,EAAE,OAAO,KAAK,GAClD,EACF,EAGFoB,MAAC,SAAM,UAAU,qBAAqB,oBAAQ,EAC9CA,MAAC,UACC,UAAU,eACV,MAAOiQ,GAAU,SAAS6B,EAAM,QAAQ,EAAIA,EAAM,SAAW,MAC7D,SAAWlT,GAAMoT,EAAO,WAAYpT,EAAE,OAAO,KAAK,EAEjD,SAAAqR,GAAU,IAAKmC,GAAOpS,MAAC,UAAgB,MAAOoS,EAAK,SAAAA,CAAA,EAAhBA,CAAmB,CAAS,IAGlEpS,MAAC,SAAM,UAAU,qBAAqB,uBAAW,EACjDA,MAAC,UACC,UAAU,eACV,MAAO8R,EAAM,WACb,SAAWlT,GAAMoT,EAAO,aAAcpT,EAAE,OAAO,KAAmC,EAEjF,SAAAoR,GAAQ,IAAK/O,GAAMjB,MAAC,UAAe,MAAOiB,EAAI,SAAAA,CAAA,EAAdA,CAAgB,CAAS,IAG5DjB,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,SACC,UAAU,eACV,KAAK,OACL,YACE8R,EAAM,aAAe,UACjB,cACAA,EAAM,aAAe,QACnB,oBACA,aAER,MAAOA,EAAM,aACb,SAAWlT,GAAMoT,EAAO,eAAgBpT,EAAE,OAAO,KAAK,IAGvDkT,EAAM,aAAe,SACpB/Q,OAAAC,WAAA,CACE,UAAAhB,MAAC,SAAM,UAAU,qBAAqB,kBAAM,EAC5CA,MAAC,YACC,UAAU,eACV,KAAM,EACN,YAAY,4BACZ,MAAO8R,EAAM,aACb,SAAWlT,GAAMoT,EAAO,eAAgBpT,EAAE,OAAO,KAAK,GACxD,EACF,EAGFmC,OAAC,YAAS,UAAU,qBAClB,UAAAf,MAAC,UAAO,6BAAiB,EACzBe,OAAC,SAAM,UAAU,sBACf,UAAAf,MAAC,SACC,KAAK,WACL,QAAS8R,EAAM,gBACf,SAAWlT,GAAMoT,EAAO,kBAAmBpT,EAAE,OAAO,OAAO,IAE7DoB,MAAC,QAAK,iDAAqC,GAC7C,EACAA,MAAC,SAAM,UAAU,qBAAqB,eAAG,EACzCA,MAAC,SACC,UAAU,eACV,KAAK,SACL,IAAK,EACL,IAAK,GACL,MAAO8R,EAAM,cACb,SAAWlT,GAAMoT,EAAO,gBAAiB,SAASpT,EAAE,OAAO,MAAO,EAAE,GAAK,CAAC,EAC1E,SAAU,CAACkT,EAAM,iBACnB,EACF,EAEA/Q,OAAC,SAAM,UAAU,sBACf,UAAAf,MAAC,SACC,KAAK,WACL,QAAS8R,EAAM,QACf,SAAWlT,GAAMoT,EAAO,UAAWpT,EAAE,OAAO,OAAO,IAErDoB,MAAC,QAAK,mBAAO,GACf,GACF,GAGN,CAMA,SAASmR,GAAU/U,EAAoB,CACrC,MAAMC,EAAO,KAAK,MAAQ,IAAI,KAAKD,CAAE,EAAE,UACvC,OAAIC,EAAO,IAAc,WACrBA,EAAO,KAAgB,GAAG,KAAK,MAAMA,EAAO,GAAK,CAAC,QAClDA,EAAO,MAAiB,GAAG,KAAK,MAAMA,EAAO,IAAO,CAAC,QAClD,IAAI,KAAKD,CAAE,EAAE,oBACtB,CAEA,SAAS8U,GAAgBmB,EAAaD,EAA4B,CAChE,GAAI,CACF,OAAO,IAAI,KAAKC,CAAG,EAAE,eAAe,OAAW,CAC7C,SAAUD,GAAM,OAChB,QAAS,QACT,KAAM,UACN,OAAQ,UACR,MAAO,QACP,IAAK,UACN,CACH,MAAQ,CACN,OAAO,IAAI,KAAKC,CAAG,EAAE,gBACvB,CACF,CAEA,SAASb,GAAgBc,EAAoE,CAC3F,GAAI,OAAOA,GAAS,SAAU,OAAO,KACrC,MAAMpW,EAAQoW,EAAK,OAAO,MAAM,KAAK,EACrC,GAAIpW,EAAM,SAAW,EAAG,OAAO,KAC/B,KAAM,CAACuD,EAAGZ,EAAA,EAAO0T,CAAG,EAAIrW,EAClBsW,EAAS,SAAS/S,EAAG,EAAE,EACvBgT,EAAU,SAAS5T,EAAG,EAAE,EAE9B,MADI,CAAC,OAAO,SAAS2T,CAAM,GAAK,CAAC,OAAO,SAASC,CAAO,GACpDhT,IAAM,OAAO+S,CAAM,GAAK3T,IAAM,OAAO4T,CAAO,EAAU,KACnD,CACL,OAAQD,EACR,KAAMC,EACN,IAAKF,IAAQ,IAAM,IAAO,OAAO,SAAS,SAASA,EAAK,EAAE,CAAC,EAAIA,EAAM,IAEzE,CAEA,SAASb,GAAoB,EAA+C,CAC1E,GAAI,OAAO,GAAM,SAAU,OAAO,KAClC,MAAMjS,EAAI,mBAAmB,KAAK,EAAE,MAAM,EAC1C,OAAKA,EACE,CAAE,EAAG,SAASA,EAAE,CAAC,EAAG,EAAE,EAAG,KAAMA,EAAE,CAAC,EAAE,aAAY,EADxC,IAEjB,CAEA,SAASkS,GAAgBU,EAAqB,CAC5C,GAAI,CAACA,EAAK,MAAO,GACjB,GAAI,CACF,MAAM9V,EAAI,IAAI,KAAK8V,CAAG,EACtB,GAAI,OAAO,MAAM9V,EAAE,SAAS,EAAG,MAAO,GACtC,MAAMmW,EAAOhT,GAAc,OAAOA,CAAC,EAAE,SAAS,EAAG,GAAG,EACpD,MAAO,GAAGnD,EAAE,aAAa,IAAImW,EAAInW,EAAE,WAAa,CAAC,CAAC,IAAImW,EAAInW,EAAE,SAAS,CAAC,IAAImW,EAAInW,EAAE,UAAU,CAAC,IAAImW,EAAInW,EAAE,YAAY,CAAC,EACpH,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAAS0U,GAAiB,EAAqB,SAC7C,GAAI,EAAE,OAAS,OAAQ,OAAO,EAAE,SAChC,MAAM0B,EAASnB,GAAgB,EAAE,QAAQ,EACzC,GAAI,CAACmB,EAAQ,OAAO,EAAE,SACtB,MAAMJ,IAAM9T,EAAA4R,GAAK,KAAM,GAAM,EAAE,QAAUsC,EAAO,GAAG,IAAvC,YAAAlU,EAA0C,QAASkU,EAAO,IAChEC,IAAY3P,EAAAiN,GAAM,KAAMrR,GAAMA,EAAE,QAAU8T,EAAO,IAAI,IAAzC,YAAA1P,EAA4C,QAAS,GAAG0P,EAAO,IAAI,GAC/EE,EAAS,OAAOF,EAAO,MAAM,EAAE,SAAS,EAAG,GAAG,EACpD,OAAIJ,IAAQ,YAAoB,aAAaK,CAAS,KAAKC,CAAM,GAC1D,SAASN,CAAG,IAAIK,CAAS,KAAKC,CAAM,EAC7C,CC/pBA,MAAMC,GAAS,CAAC,MAAO,QAAS,SAAU,YAAa,WAAY,UAAU,EAGvEC,GAA+C,CACnD,MAAOtG,GACP,OAAQ5G,GACR,MAAOoG,EACP,SAAU+G,GACV,SAAUzI,CACZ,EAEO,SAAS0I,GAAkB,CAAE,KAAAtS,EAAM,QAAAC,EAAS,WAAAoL,GAAqB,CACtE,KAAM,CAACtB,EAAOC,CAAQ,EAAIjJ,WAAS,EAAE,EAC/B,CAACwR,EAAOC,CAAQ,EAAIzR,WAAgB,KAAK,EACzC,CAAC0R,EAASC,CAAU,EAAI3R,WAAyB,EAAE,EACnD,CAACmC,EAASC,CAAU,EAAIpC,WAAS,EAAK,EACtC4R,EAAWpR,SAAyB,IAAI,EAE9CS,YAAU,IAAM,CACVhC,IACFgK,EAAS,EAAE,EACX0I,EAAW,EAAE,EACb,sBAAsB,WAAM,OAAA5U,EAAA6U,EAAS,UAAT,YAAA7U,EAAkB,QAAO,EAEzD,EAAG,CAACkC,CAAI,CAAC,EAETgC,YAAU,IAAM,CACd,GAAI,CAAC+H,EAAM,OAAQ,CACjB2I,EAAW,EAAE,EACb,MACF,CACA,MAAME,EAAQ,WAAW,SAAY,CACnCzP,EAAW,EAAI,EACf,GAAI,CACF,MAAM9G,EAAO,MAAMmB,EAAI,IACrB,aAAa,mBAAmBuM,CAAK,CAAC,UAAUwI,CAAK,IAEvDG,EAAWrW,EAAK,SAAW,EAAE,CAC/B,MAAQ,CACNqW,EAAW,EAAE,CACf,SACEvP,EAAW,EAAK,CAClB,CACF,EAAG,GAAG,EACN,MAAO,IAAM,aAAayP,CAAK,CACjC,EAAG,CAAC7I,EAAOwI,CAAK,CAAC,EAEjB,MAAMM,EAAqB7V,GAAoB,CAC7C,MAAM8V,EAAO9V,EAAE,KACTiE,EAAK6R,EAAK,IAAMA,EAAK,MAAQA,EAAK,MAAQ,GAChDzH,EAAWrO,EAAE,KAAMiE,CAAE,EACrBhB,EAAA,CACF,EAEM8S,EAAUN,EAAQ,OAAuC,CAAClF,EAAKvQ,KAC9DuQ,EAAIvQ,EAAE,IAAI,IAAGuQ,EAAIvQ,EAAE,IAAI,EAAI,IAChCuQ,EAAIvQ,EAAE,IAAI,EAAE,KAAKA,CAAC,EACXuQ,GACN,EAAE,EAEL,OACElO,MAACkG,GAAY,KAAAvF,EAAY,QAAAC,EAAkB,MAAM,SAC/C,SAAAG,OAAC,OAAI,UAAU,0BAEb,UAAAA,OAAC,OAAI,UAAU,0BACb,UAAAf,MAACS,GAAA,CAAO,KAAM,GAAI,MAAO,CAAE,MAAO,oBAAqB,WAAY,EAAE,CAAG,EACxET,MAAC,SACC,IAAKsT,EACL,UAAU,sBACV,KAAK,OACL,YAAY,UACZ,MAAO5I,EACP,SAAW9L,GAAM+L,EAAS/L,EAAE,OAAO,KAAK,EACxC,aAAa,SACb,UAAYA,GAAM,CACZA,EAAE,MAAQ,UAAUgC,EAAA,EACpBhC,EAAE,MAAQ,SAAWwU,EAAQ,OAAS,GACxCI,EAAkBJ,EAAQ,CAAC,CAAC,CAEhC,IAED1I,GACC1K,MAAC,UACC,KAAK,SACL,UAAU,kBACV,QAAS,IAAM2K,EAAS,EAAE,EAC1B,aAAW,QAEX,SAAA3K,MAACQ,EAAA,CAAE,KAAM,GAAI,GACf,EAEJ,QAGC,OAAI,UAAU,uBACZ,SAAAsS,GAAO,IAAKhU,GACXkB,MAAC,UAEC,KAAK,SACL,UAAW,qBAAqBkT,IAAUpU,EAAI,SAAW,EAAE,GAC3D,QAAS,IAAMqU,EAASrU,CAAC,EAExB,SAAAA,IAAM,MAAQ,MAAQA,EAAE,OAAO,CAAC,EAAE,cAAgBA,EAAE,MAAM,CAAC,GALvDA,CAAA,CAOR,EACH,EAGAiC,OAAC,OAAI,UAAU,wBACZ,UAAA8C,GAAW7D,MAAC,OAAI,UAAU,wBAAwB,sBAAU,EAE5D,CAAC6D,GAAW6G,GAAS0I,EAAQ,SAAW,GACvCrS,OAAC,OAAI,UAAU,eACb,UAAAf,MAACS,GAAA,CAAO,KAAM,GAAI,EAClBT,MAAC,KAAE,4BAAgB,GACrB,EAGD,CAAC6D,GAAW,CAAC6G,GACZ1K,MAAC,OAAI,UAAU,eACb,SAAAA,MAAC,KAAE,iEAAqD,EAC1D,EAGD,OAAO,QAAQ0T,CAAO,EAAE,IAAI,CAAC,CAACnH,EAAMoH,CAAK,IAAM,CAC9C,MAAMzS,EAAO6R,GAAYxG,CAAI,GAAKN,EAClC,OACElL,OAAC,OAAe,UAAU,sBACxB,UAAAA,OAAC,MAAG,UAAU,4BACZ,UAAAf,MAACkB,EAAA,CAAK,KAAM,GAAI,EAAE,IAAEqL,EAAK,OAAO,CAAC,EAAE,cAAgBA,EAAK,MAAM,CAAC,EAAE,KAAGoH,EAAM,OAAO,KACnF,EACCA,EAAM,IAAI,CAAChW,EAAGoI,IAAM,CACnB,MAAM0N,EAAO9V,EAAE,KACf,OACEoD,OAAC,UAEC,KAAK,SACL,UAAU,4BACV,QAAS,IAAMyS,EAAkB7V,CAAC,EAElC,UAAAqC,MAAC,QAAK,UAAU,6BACb,SAAAyT,EAAK,OAASA,EAAK,MAAQA,EAAK,MAAQ,YAC3C,EACCA,EAAK,aACJzT,MAAC,QAAK,UAAU,4BACb,SAAAyT,EAAK,YAAY,MAAM,EAAG,EAAE,EAC/B,IAXG1N,CAAA,CAeX,CAAC,IAvBOwG,CAwBV,CAEJ,CAAC,GACH,GACF,EACF,CAEJ,CCpJA,MAAMlG,GAAa,CAAC,YAAa,YAAa,SAAU,UAAW,SAAU,OAAQ,WAAY,aAAa,EAEvG,SAASuN,GAAa,CAAE,SAAAnQ,EAAU,OAAA+C,GAAiB,CACxD,KAAM,CAACqN,EAAQC,CAAS,EAAIpS,WAAkB,EAAE,EAC1C,CAACmC,EAASC,CAAU,EAAIpC,WAAS,EAAI,EACrC,CAACqC,EAAQC,CAAS,EAAItC,WAAS,EAAE,EACjC,CAACkF,EAAgBC,CAAiB,EAAInF,WAAS,EAAE,EACjD,CAACqS,EAAaC,CAAc,EAAItS,WAAuB,IAAI,EAE3DkI,EAAO,SAAY,CACvB,GAAI,CACF,MAAM5M,EAAO,MAAMmB,EAAI,IAAyB,SAAS,EACzD2V,EAAU9W,EAAK,QAAU,EAAE,CAC7B,MAAQ,CAER,SACE8G,EAAW,EAAK,CAClB,CACF,EAEAnB,YAAU,IAAM,CAAEiH,EAAA,CAAQ,EAAG,EAAE,EAE/B,MAAMqK,EAAc,MAAOrS,EAAYiK,IAAqB,CAC1D,GAAI,CACF,MAAM1N,EAAI,MAAM,WAAW,mBAAmByD,CAAE,CAAC,GAAI,CAAE,QAAAiK,EAAS,EAChEiI,EAAWxO,GAAQA,EAAI,IAAKxG,GAAOA,EAAE,KAAO8C,EAAK,CAAE,GAAG9C,EAAG,QAAA+M,CAAA,EAAY/M,CAAE,CAAC,GACpEiV,GAAA,YAAAA,EAAa,MAAOnS,GAAIoS,EAAgBlV,GAAMA,GAAI,CAAE,GAAGA,EAAG,QAAA+M,CAAA,CAAa,CAC7E,MAAQ,CAER,CACF,EAEMnH,EAAWmP,EAAO,OAAQ/U,GAAM,CACpC,GAAI8H,GAAkB9H,EAAE,WAAa8H,EAAgB,MAAO,GAC5D,GAAI7C,EAAQ,CACV,MAAM2D,EAAI3D,EAAO,cACjB,OAAOjF,EAAE,KAAK,cAAc,SAAS4I,CAAC,GAAK5I,EAAE,YAAY,cAAc,SAAS4I,CAAC,CACnF,CACA,MAAO,EACT,CAAC,EAED,OACE3G,OAAC,OAAI,UAAU,cACb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,iBACZ,MAAO+D,EACP,SAAWnF,GAAMoF,EAAUpF,EAAE,OAAO,KAAK,EACzC,MAAO,CAAE,KAAM,EAAE,SAElB,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAMgL,EAAA,EAAQ,aAAW,UAClF,SAAA5J,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,GACF,EAEA5E,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,UAAO,KAAK,SAAS,UAAW,qBAAsB4G,EAA4B,GAAX,QAAa,GAAI,QAAS,IAAMC,EAAkB,EAAE,EAAG,eAAG,EACjIR,GAAW,IAAK8B,SACd,UAAe,KAAK,SAAS,UAAW,qBAAqBvB,IAAmBuB,EAAI,SAAW,EAAE,GAAI,QAAS,IAAMtB,EAAkBsB,CAAC,EACrI,SAAAA,CAAA,EADUA,CAEb,CACD,GACH,EAECtE,EACC7D,MAAC,OAAI,UAAU,iBAAiB,eAAC,KAAE,oBAAQ,EAAI,EAC7C0E,EAAS,SAAW,EACtB3D,OAAC,OAAI,UAAU,eACb,UAAAf,MAACkU,GAAA,CAAS,KAAM,GAAI,EACpBlU,MAAC,KAAE,4BAAgB,GACrB,QAEC,OAAI,UAAU,mBACZ,SAAA0E,EAAS,IAAK5F,GACbiC,OAAC,UAEC,KAAK,SACL,UAAU,gDACV,QAAS,IAAMiT,EAAelV,CAAC,EAE/B,UAAAkB,MAAC,OAAI,UAAU,mBAAmB,eAACkU,GAAA,CAAS,KAAM,GAAI,EAAE,EACxDnT,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,QAAK,UAAU,oBAAqB,SAAAlB,EAAE,KAAK,EAC5CiC,OAAC,QAAK,UAAU,mBAAoB,UAAAjC,EAAE,SAAS,OAAKA,EAAE,SAAQ,GAChE,EACAkB,MAAC,QAAK,UAAW,qBAAqBlB,EAAE,QAAU,WAAa,WAAW,GACvE,SAAAA,EAAE,QAAU,KAAO,MACtB,IAZKA,EAAE,GAcV,EACH,EAGDiV,GACC/T,MAAC6B,EAAA,CACC,KAAM,GACN,QAAS,IAAMmS,EAAe,IAAI,EAClC,MAAOD,EAAY,KACnB,QACEhT,OAAC,UACC,KAAK,SACL,UAAW,cAAcgT,EAAY,QAAU,uBAAyB,EAAE,GAC1E,MAAO,CAAE,MAAO,QAChB,QAAS,IAAM,CAAMA,GAAaE,EAAYF,EAAY,GAAI,CAACA,EAAY,OAAO,CAAG,EAErF,UAAA/T,MAAC8L,GAAA,CAAM,KAAM,GAAI,EAAE,IAAEiI,EAAY,QAAU,UAAY,YAI3D,SAAAhT,OAAC,OAAI,UAAU,sBACb,UAAAf,MAAC,KAAE,UAAU,2BAA4B,SAAA+T,EAAY,aAAe,kBAAkB,EACtFhT,OAAC,OAAI,UAAU,2BACb,UAAAA,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,oBAAQ,EAAOA,MAAC,QAAM,SAAA+T,EAAY,SAAS,GAAO,EAChGhT,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,mBAAO,EAAOA,MAAC,QAAK,UAAU,OAAQ,WAAY,QAAQ,GAAO,EAC/Ge,OAAC,OAAI,UAAU,yBAAyB,UAAAf,MAAC,QAAK,kBAAM,EAAOA,MAAC,QAAM,SAAA+T,EAAY,OAAO,GAAO,EAC3FA,EAAY,KAAK,OAAS,GACzBhT,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,gBAAI,QACT,QAAM,SAAA+T,EAAY,KAAK,KAAK,IAAI,EAAE,GACrC,GAEJ,GACF,GACF,EAEJ,CAEJ,CCrIA,MAAMI,GAAe,CAAC,SAAU,QAAS,UAAW,MAAM,EACpDC,EAAwC,CAC5C,OAAQ,SACR,MAAO,QACP,QAAS,UACT,KAAM,MACR,EACMC,GAAsC,CAC1C,OAAQ,QACR,MAAO,OACP,QAAS,SACT,KAAM,QACR,EAKA,SAASC,GAAYC,EAAqB,CACxC,MAAMC,EAAYD,EAAK,UAAY,GACnC,MAAO,GAAQC,EAAS,WAAaA,EAAS,aAChD,CAEA,SAASC,GAAiBF,EAAqB,OAC7C,MAAMC,EAAYD,EAAK,UAAY,GACnC,MAAO,IAAQ9V,EAAA+V,EAAS,cAAT,MAAA/V,EAAsB,QAAU+V,EAAS,WAC1D,CAEO,SAASE,GAAY,CAAE,SAAAjR,EAAU,UAAAC,EAAW,eAAAiR,EAAgB,cAAAC,EAAe,WAAAC,EAAY,eAAAxF,GAAyB,WACrH,KAAM,CAACrK,EAAO8P,CAAQ,EAAIpT,WAAiB+B,EAAS,OAAS,EAAE,EACzD,CAACI,EAASC,CAAU,EAAIpC,WAAS,CAAC+B,EAAS,KAAK,EAChD,CAACM,EAAQC,CAAS,EAAItC,WAAS,EAAE,EACjC,CAACqT,EAAcC,CAAe,EAAItT,WAAiB,QAAQ,EAC3D,CAACuT,EAAYC,CAAa,EAAIxT,WAAsB,IAAI,EACxD,CAACyT,EAAaC,CAAc,EAAI1T,WAAS,EAAK,EAE9C,CAAC2T,EAAgBC,CAAiB,EAAI5T,WAAwB,IAAI,EAClE,CAAC6T,EAAaC,CAAc,EAAI9T,WAAmB,EAAE,EACrD,CAAC+T,EAAiBC,CAAkB,EAAIhU,WAAiB,EAAE,EAC3D,CAACiU,EAAcC,CAAe,EAAIlU,WAAwE,IAAI,EAC9G,CAACmU,EAAiBC,CAAkB,EAAIpU,WAAS,EAAK,EACtD,CAACqU,EAAgBC,CAAiB,EAAItU,WAAS,EAAE,EACjD,CAACuU,EAAQC,CAAS,EAAIxU,WAA6C,SAAS,EAElFiB,YAAU,IAAM,OAKd,GAJIc,EAAS,QACXqR,EAASrR,EAAS,KAAK,EACvBK,EAAW,EAAK,GAEd6Q,EAAgB,CAClB,MAAM1T,GAAIxC,EAAAgF,EAAS,QAAT,YAAAhF,EAAgB,KAAMwC,GAAMA,EAAE,KAAO0T,GAC3C1T,KAAiBA,CAAC,CACxB,CACF,EAAG,CAACwC,EAAS,MAAOkR,CAAc,CAAC,EAEnChS,YAAU,IAAM,CACd,GAAIgS,EAAgB,CAClB,MAAM1T,EAAI+D,EAAM,KAAM/D,GAAMA,EAAE,KAAO0T,CAAc,EAC/C1T,KAAiBA,CAAC,CACxB,CACF,EAAG,CAAC0T,EAAgB3P,CAAK,CAAC,EAE1B,MAAMyC,EAAS,SAAY,CACzB,GAAI,CACF,MAAMzK,EAAO,MAAMmB,EAAI,IAAY,QAAQ,EAC3C2W,EAAS,MAAM,QAAQ9X,CAAI,EAAIA,EAAO,EAAE,CAC1C,MAAQ,CAER,SACE8G,EAAW,EAAK,CAClB,CACF,EAEMqS,GAAW,MAAOC,EAAgBC,IAAsB,CAC5DvB,EAAUxP,GACRA,EAAI,IAAKrE,GAAOA,EAAE,KAAOmV,EAAS,CAAE,GAAGnV,EAAG,OAAQoV,CAAA,EAAcpV,CAAE,IAEhEgU,GAAA,YAAAA,EAAY,MAAOmB,GACrBlB,EAAejU,GAAMA,GAAI,CAAE,GAAGA,EAAG,OAAQoV,CAAA,CAAe,EAE1D,GAAI,CACF,MAAMlY,EAAI,MAAM,UAAU,mBAAmBiY,CAAM,CAAC,UAAW,CAAE,OAAQC,CAAA,CAAW,CACtF,MAAQ,CACN,MAAM5O,EAAA,CACR,CACF,EAEM6O,EAAa,MAAOF,GAAmB,CAC3C,GAAK,QAAQ,mBAAmB,EAChC,GAAI,CACF,MAAMjY,EAAI,IAAI,UAAU,mBAAmBiY,CAAM,CAAC,EAAE,EACpDtB,EAAUxP,GAAQA,EAAI,OAAQrE,GAAMA,EAAE,KAAOmV,CAAM,CAAC,GAChDnB,GAAA,YAAAA,EAAY,MAAOmB,GAAQlB,EAAc,IAAI,CACnD,MAAQ,CACN,MAAMzN,EAAA,CACR,CACF,EAEM8O,GAAc,MAAOH,GAAmB,CAC5C,GAAI,CACF,MAAMjY,EAAI,KAAK,UAAU,mBAAmBiY,CAAM,CAAC,UAAU,EAC7DtB,EAAUxP,GAAQA,EAAI,OAAQrE,GAAMA,EAAE,KAAOmV,CAAM,CAAC,GAChDnB,GAAA,YAAAA,EAAY,MAAOmB,GAAQlB,EAAc,IAAI,CACnD,MAAQ,CAER,CACF,EAkBMsB,GAAS,CAAC,GAhBCxR,EAAM,OAAQ/D,GAAM,SACnC,GAAI8C,EAAO,OAAQ,CACjB,MAAM2D,EAAI3D,EAAO,cACjB,GAAI,EAAE9C,EAAE,OAAS,IAAI,cAAc,SAASyG,CAAC,GAAK,EAAEzG,EAAE,aAAe,IAAI,cAAc,SAASyG,CAAC,EAC/F,MAAO,EAEX,CACA,GAAIqO,EAAe,OAAQ,CACzB,MAAMU,EAAiBV,IAAmB,SACtC9S,GAAAxE,EAAAgF,EAAS,WAAT,YAAAhF,EAAmB,OAAnB,YAAAwE,EAAyB,eAAgByT,GAAqBjT,CAAQ,EACtEsS,EAAe,QAAQ,KAAM,EAAE,EACnC,IAAK9U,EAAE,UAAY,IAAI,gBAAkBwV,EAAe,cAAe,MAAO,EAChF,CACA,MAAO,EACT,CAAC,CAE0B,EAAE,KAAK,CAAC3Q,EAAG6Q,IAAM,CAC1C,OAAQV,EAAA,CACN,IAAK,WAAY,CACf,MAAMW,EAAQ,CAAE,KAAM,EAAG,OAAQ,EAAG,IAAK,GACzC,OAAQA,EAAM9Q,EAAE,QAA8B,GAAK,IAAM8Q,EAAMD,EAAE,QAA8B,GAAK,EACtG,CACA,IAAK,UAAW,OAAO,IAAI,KAAKA,EAAE,SAAS,EAAE,UAAY,IAAI,KAAK7Q,EAAE,SAAS,EAAE,UAC/E,IAAK,UACL,QACE,OAAO,IAAI,KAAK6Q,EAAE,SAAS,EAAE,UAAY,IAAI,KAAK7Q,EAAE,SAAS,EAAE,SAAQ,CAE7E,CAAC,EAEKb,GAAcuR,GAAO,OAAQvV,GAAMA,EAAE,SAAW8T,CAAY,EAC5DhQ,GAAStB,EAAS,QAAU,GAE5BoT,GAAa,MAAO/U,EAAe2G,EAAqBqO,EAAkBC,IAAqB,CACnG,GAAI,CACF,MAAMxC,GAAO,MAAMpW,EAAI,KAAW,SAAU,CAAE,MAAA2D,EAAO,YAAA2G,EAAa,SAAUqO,GAAY,KAAM,SAAAC,CAAA,CAAU,EACxGjC,EAAUxP,IAAQ,CAACiP,GAAM,GAAGjP,EAAG,CAAC,EAChC8P,EAAe,EAAK,CACtB,MAAQ,CAER,CACF,EAGM4B,GAAmB,MAAOZ,GAAmB,CACjDN,EAAmB,EAAI,EACvBJ,EAAmB,EAAE,EACrBE,EAAgB,IAAI,EACpBN,EAAkBc,CAAM,EACxB,GAAI,CAIF,MAAMa,IAHO,MAAM9Y,EAAI,IACrB,UAAU,mBAAmBiY,CAAM,CAAC,eAEpB,WAAa,IAAI,IAAKtQ,GAAMA,EAAE,EAAE,EAElD,GADA0P,EAAeyB,CAAG,EACdA,EAAI,OAAS,EAAG,CAClB,KAAM,CAACjK,EAAMgB,EAAO,EAAI,MAAM,QAAQ,IAAI,CACxC7P,EAAI,IAA4D,cAAc,mBAAmB8Y,EAAI,CAAC,CAAC,CAAC,EAAE,EAC1G,MAAM9Y,EAAI,aAAa,cAAc,mBAAmB8Y,EAAI,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,KAAMtZ,IAAMA,GAAE,MAAM,EACjG,EACDiY,EAAgB5I,CAAI,EACpB0I,EAAmB1H,EAAO,CAC5B,CACF,MAAQ,CAER,SACE8H,EAAmB,EAAK,CAC1B,CACF,EAEA,OAAIjS,QACM,OAAI,UAAU,iBAAiB,SAAA7D,MAAC,KAAE,oBAAQ,EAAI,EAItDe,OAAC,OAAI,UAAU,gCAEb,UAAAA,OAAC,OAAI,UAAU,uBACb,UAAAf,MAAC,SACC,UAAU,sBACV,KAAK,OACL,YAAY,gBACZ,MAAO+D,EACP,SAAWnF,GAAMoF,EAAUpF,EAAE,OAAO,KAAK,EACzC,MAAO,CAAE,KAAM,EAAE,SAElB,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAAS,IAAM6I,EAAA,EAAU,aAAW,UACpF,SAAAzH,MAAC2F,EAAA,CAAU,KAAM,GAAI,EACvB,GACF,EAGA5E,OAAC,OAAI,UAAU,uBACb,UAAAA,OAAC,UACC,UAAU,uBACV,MAAOgV,EACP,SAAWnX,GAAMoX,EAAkBpX,EAAE,OAAO,KAAK,EAEjD,UAAAoB,MAAC,UAAO,MAAM,GAAG,eAAG,EACpBA,MAAC,UAAO,MAAM,OAAO,gBAAI,EACxB+E,GAAO,IAAKe,GACX/E,OAAC,UAAoB,MAAO,IAAI+E,EAAE,IAAI,GAAI,cAAEA,EAAE,OAAjCA,EAAE,IAAoC,CACpD,KAEH/E,OAAC,UACC,UAAU,uBACV,MAAOkV,EACP,SAAWrX,GAAMsX,EAAUtX,EAAE,OAAO,KAA2C,EAE/E,UAAAoB,MAAC,UAAO,MAAM,UAAU,mBAAO,EAC/BA,MAAC,UAAO,MAAM,UAAU,mBAAO,EAC/BA,MAAC,UAAO,MAAM,WAAW,oBAAQ,IACnC,EACF,QAGC,OAAI,UAAU,qBACZ,SAAAmU,GAAa,IAAKrV,GAAM,CACvB,MAAMoY,EAAQV,GAAO,OAAQvV,GAAMA,EAAE,SAAWnC,CAAC,EAAE,OACnD,OACEiC,OAAC,UAEC,KAAK,SACL,UAAW,qBAAqBgU,IAAiBjW,EAAI,SAAW,EAAE,GAClE,QAAS,IAAMkW,EAAgBlW,CAAC,EAE/B,UAAAsV,EAActV,CAAC,EAAE,KAAGoY,EAAM,MALtBpY,CAAA,CAQX,CAAC,EACH,EAGEiC,OAAC,OAAI,UAAU,mBACZ,UAAAkE,GAAY,SAAW,GACtBlE,OAAC,OAAI,UAAU,oCACb,UAAAA,OAAC,KAAE,gBAAIqT,EAAcW,CAAY,EAAE,cAAc,WAAO,EACxD/U,MAAC,KAAE,UAAU,QAAQ,yCAA6B,GACpD,EAEHiF,GAAY,IAAKhE,UAChBF,cAAC,UAEC,KAAK,SACL,UAAU,mBACV,QAAS,IAAMmU,EAAcjU,CAAC,EAE9B,UAAAjB,MAAC,OACC,UAAU,eACV,MAAO,CAAE,WAAYtD,GAAeuE,EAAE,QAAQ,GAAK,cAAc,GAEnEF,OAAC,OAAI,UAAU,eACb,UAAAf,MAAC,OAAI,UAAU,aAAc,SAAAiB,EAAE,MAAM,EACrCF,OAAC,OAAI,UAAU,YACZ,UAAAE,EAAE,SAAW,IAAIA,EAAE,QAAQ,GAAK,aAChC,MACA9E,EAAe8E,EAAE,WAAaA,EAAE,SAAS,GACzCxC,EAAAwC,EAAE,eAAF,MAAAxC,EAAgB,OAAS,MAAMwC,EAAE,aAAa,MAAM,QAAU,IACjE,GACF,IAjBKA,EAAE,IAmBV,GACH,EAGAjB,MAAC,UACC,KAAK,SACL,UAAU,aACV,QAAS,IAAMoV,EAAe,EAAI,EAClC,aAAW,WAEX,SAAApV,MAACkI,EAAA,CAAK,KAAM,GAAI,IAIlBlI,MAAC6B,EAAA,CACC,KAAM,CAAC,CAACoT,EACR,QAAS,IAAM,CAAEC,EAAc,IAAI,EAAGN,GAAA,MAAAA,GAAmB,EACzD,MAAM,cACN,QACEK,GACElU,OAAC,OAAI,UAAU,6BACZ,UAAAkU,EAAW,SAAW,QACrBlU,OAAC,UACC,KAAK,SACL,UAAU,aACV,MAAO,CAAE,KAAM,GACf,QAAS,IAAM,CACTkU,GAAYkB,GAASlB,EAAW,GAAIZ,GAAYY,EAAW,MAAM,CAAC,CACxE,EACD,qBACUb,EAAcC,GAAYY,EAAW,MAAM,GAAK,QAAQ,KAGrElU,OAAC,UACC,KAAK,SACL,UAAU,kCACV,QAAS,IAAM,CACTkU,GAAYsB,GAAYtB,EAAW,EAAE,CAC3C,EAEA,UAAAjV,MAACmX,GAAA,CAAQ,KAAM,GAAI,EAAE,cAGtB7C,GAAYW,CAAU,GACrBlU,OAAC,UACC,KAAK,SACL,UAAU,kCACV,QAAS,IAAM,CAAE8T,GAAA,MAAAA,EAAaI,EAAW,GAAK,EAE9C,UAAAjV,MAAC0O,EAAA,CAAc,KAAM,GAAI,EAAE,WAI9B+F,GAAiBQ,CAAU,GAC1BlU,OAAC,UACC,KAAK,SACL,UAAU,kCACV,QAAS,IAAM,CAAEiW,GAAiB/B,EAAW,EAAE,CAAG,EAElD,UAAAjV,MAACiM,EAAA,CAAS,KAAM,GAAI,EAAE,eAG1BjM,MAAC,UACC,KAAK,SACL,UAAU,+BACV,QAAS,IAAM,CACTiV,GAAYqB,EAAWrB,EAAW,EAAE,CAC1C,EAEA,SAAAjV,MAACqI,GAAA,CAAO,KAAM,GAAI,GACpB,EACF,EAIH,SAAA4M,GACClU,OAAC,OAAI,UAAU,qBACb,UAAAA,OAAC,OAAI,UAAU,4BACb,UAAAf,MAAC,QAAK,UAAW,mCAAmCiV,EAAW,MAAM,GAClE,SAAAb,EAAca,EAAW,MAAM,GAAKA,EAAW,OAClD,EACAjV,MAAC,QAAK,UAAW,iCAAiCiV,EAAW,QAAQ,GAClE,WAAW,SACd,GACF,EACAjV,MAAC,MAAG,UAAU,2BAA4B,WAAW,MAAM,EAC1DiV,EAAW,aACVjV,MAAC,KAAE,UAAU,0BAA2B,WAAW,YAAY,EAEjEe,OAAC,OAAI,UAAU,0BACZ,UAAAkU,EAAW,UACVlU,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,oBAAQ,SACb,QAAK,cAAEiV,EAAW,UAAS,GAC9B,EAEDA,EAAW,SACVlU,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,eAAG,EACTA,MAAC,QAAM,aAAI,KAAKiV,EAAW,OAAO,EAAE,oBAAmB,CAAE,GAC3D,EAEDA,EAAW,WAAa,MACvBlU,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,sBAAU,SACf,QAAM,eAAK,MAAMiV,EAAW,UAAY,GAAK,EAAE,OAAG,GACrD,GAEDxW,GAAAwW,EAAW,eAAX,MAAAxW,GAAyB,OACxBsC,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,wBAAY,EAClBA,MAAC,QAAM,SAAAiV,EAAW,aAAa,OAAO,GACxC,EACE,MACHhS,GAAAgS,EAAW,WAAX,MAAAhS,GAAqB,OACpBlC,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,oBAAQ,EACdA,MAAC,QAAM,SAAAiV,EAAW,SAAS,OAAO,GACpC,EACE,KACJlU,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,mBAAO,EACbA,MAAC,QAAM,SAAA7D,EAAe8Y,EAAW,SAAS,EAAE,GAC9C,EACAlU,OAAC,OAAI,UAAU,yBACb,UAAAf,MAAC,QAAK,mBAAO,EACbA,MAAC,QAAM,SAAA7D,EAAe8Y,EAAW,SAAS,EAAE,GAC9C,GACF,GACF,IAKJjV,MAACoX,GAAA,CACC,KAAMjC,EACN,QAAS,IAAMC,EAAe,EAAK,EACnC,SAAUyB,GACV,OAAA9R,EAAA,GAIF/E,MAAC6B,EAAA,CACC,KAAM,CAAC,CAACwT,EACR,QAAS,IAAM,CAAEC,EAAkB,IAAI,EAAGI,EAAmB,EAAE,EAAGE,EAAgB,IAAI,CAAG,EACzF,OAAOD,GAAA,YAAAA,EAAc,OAAQ,WAC7B,QACEJ,EAAY,OAAS,EACnBvV,MAAC,OAAI,UAAU,6BACZ,SAAAuV,EAAY,IAAI,CAAC3T,EAAImE,IACpBhF,OAAC,UAEC,KAAK,SACL,UAAU,kCACV,MAAO,CAAE,KAAM,GACf,QAAS,IAAM,CAEV+U,EAAmB,EAAI,EACvB,QAAQ,IAAI,CACV3X,EAAI,IAA4D,cAAc,mBAAmByD,CAAE,CAAC,EAAE,EACrG,MAAMzD,EAAI,aAAa,cAAc,mBAAmByD,CAAE,CAAC,UAAU,CAAC,EAAE,KAAMjE,GAAMA,EAAE,MAAM,EAC9F,EAAE,KAAK,CAAC,CAACqP,EAAMgB,CAAO,IAAM,CAC3B4H,EAAgB5I,CAAI,EACpB0I,EAAmB1H,CAAO,EAC7B8H,EAAmB,EAAK,CAC1B,CAAC,EAAE,MAAM,IAAMA,EAAmB,EAAK,CAAC,CAC1C,EACD,sBACW/P,EAAI,IAjBTnE,CAAA,CAmBR,EACH,EACE,KAGN,SAAA5B,MAAC,OAAI,UAAU,yBACZ,WACCA,MAAC,OAAI,UAAU,iBAAiB,SAAAA,MAAC,KAAE,6BAAiB,EAAI,EACtDyV,EACFzV,MAAC,UACC,OAAQyV,EACR,UAAU,yBACV,QAAQ,2DACR,OAAOE,GAAA,YAAAA,EAAc,OAAQ,WAC7B,QAAQ,SAGV5U,OAAC,OAAI,UAAU,eACb,UAAAf,MAACiM,EAAA,CAAS,KAAM,GAAI,EACpBjM,MAAC,KAAE,UAAU,QAAQ,0CAA8B,GACrD,EAEJ,GACF,EACF,CAEJ,CAEA,SAAS0W,GAAqBjT,EAA4B,SACxD,QAAOR,GAAAxE,EAAAgF,EAAS,WAAT,YAAAhF,EAAmB,OAAnB,YAAAwE,EAAyB,eAAgB,MAClD,CAEA,SAASmU,GAAa,CACpB,KAAAzW,EACA,QAAAC,EACA,SAAA2H,EACA,OAAAxD,CACF,EAKG,CACD,KAAM,CAACjD,EAAOkN,CAAQ,EAAItN,WAAS,EAAE,EAC/B,CAAC+G,EAAaC,CAAc,EAAIhH,WAAS,EAAE,EAC3C,CAACoV,EAAUO,CAAW,EAAI3V,WAAS,EAAE,EACrC,CAACqV,EAAUO,CAAW,EAAI5V,WAAS,QAAQ,EAE3CyD,EAAgBvG,GAAuB,CAC3CA,EAAE,iBACGkD,EAAM,SACXyG,EAASzG,EAAM,OAAQ2G,EAAY,OAAQqO,EAAUC,CAAQ,EAC7D/H,EAAS,EAAE,EACXtG,EAAe,EAAE,EACjB2O,EAAY,EAAE,EACdC,EAAY,QAAQ,EACtB,EAEA,aACGpR,EAAA,CAAY,KAAAvF,EAAY,QAAAC,EAAkB,MAAM,WAAW,QAC1DG,OAAC,UAAO,KAAK,SAAS,KAAK,gBAAgB,UAAU,aAAa,MAAO,CAAE,MAAO,QAChF,UAAAf,MAACkI,EAAA,CAAK,KAAM,GAAI,EAAE,gBACpB,EAEA,gBAAC,QAAK,GAAG,gBAAgB,SAAU/C,EAAc,UAAU,mBACzD,UAAAnF,MAAC,SAAM,UAAU,qBAAqB,mBAAO,EAC7CA,MAAC,SACC,UAAU,eACV,KAAK,OACL,YAAY,aACZ,MAAO8B,EACP,SAAWlD,GAAMoQ,EAASpQ,EAAE,OAAO,KAAK,EACxC,UAAS,GACT,SAAQ,KAEVoB,MAAC,SAAM,UAAU,qBAAqB,uBAAW,EACjDA,MAAC,YACC,UAAU,eACV,KAAM,EACN,YAAY,uBACZ,MAAOyI,EACP,SAAW7J,GAAM8J,EAAe9J,EAAE,OAAO,KAAK,IAEhDoB,MAAC,SAAM,UAAU,qBAAqB,oBAAQ,EAC9Ce,OAAC,UACC,UAAU,eACV,MAAO+V,EACP,SAAWlY,GAAMyY,EAAYzY,EAAE,OAAO,KAAK,EAE3C,UAAAoB,MAAC,UAAO,MAAM,GAAG,sBAAU,EAC1B+E,EAAO,IAAKe,UACV,UAAoB,MAAOA,EAAE,KAAM,cAAEA,EAAE,OAA3BA,EAAE,IAA8B,CAC9C,KAEH9F,MAAC,SAAM,UAAU,qBAAqB,oBAAQ,EAC9Ce,OAAC,UACC,UAAU,eACV,MAAOgW,EACP,SAAWnY,GAAM0Y,EAAY1Y,EAAE,OAAO,KAAK,EAE3C,UAAAoB,MAAC,UAAO,MAAM,MAAM,eAAG,EACvBA,MAAC,UAAO,MAAM,SAAS,kBAAM,EAC7BA,MAAC,UAAO,MAAM,OAAO,gBAAI,IAC3B,EACF,EACF,CAEJ,CCrhBA,MAAMuX,GAAiBC,GAAM,KAAK,WAAM,OAAO,8BAAyB,gCAAE,KAAK/X,IAAM,CAAE,QAASA,EAAE,gBAAiB,CAAC,EAC9GgY,GAAaD,GAAM,KAAK,WAAM,OAAO,0BAAqB,oCAAE,KAAK/X,IAAM,CAAE,QAASA,EAAE,YAAa,CAAC,EAiBlGiY,GAAyB,CAC7B,CAAE,GAAI,WAAY,MAAO,WAAY,KAAMC,EAAA,EAC3C,CAAE,GAAI,OAAQ,MAAO,OAAQ,KAAMjJ,CAAA,EACnC,CAAE,GAAI,QAAS,MAAO,QAAS,KAAMjC,EAAA,EACrC,CAAE,GAAI,WAAY,MAAO,WAAY,KAAMmL,EAAA,EAC3C,CAAE,GAAI,OAAQ,MAAO,OAAQ,KAAMC,EAAA,CACrC,EAEA,SAASC,GAAwBC,EAA+C,CAC9E,MAAM/K,EAAO+K,EAAa,MAAQ,GAC5B3B,EAAS,OAAOpJ,EAAK,QAAW,SAAWA,EAAK,OAAS,KACzDH,EAAO,OAAOG,EAAK,MAAS,SAAWA,EAAK,KAAO,KACnDgL,EAAY,OAAOhL,EAAK,OAAU,SAAWA,EAAK,MAAQ,KAChE,GAAIoJ,EAAQ,MAAO,CAAE,GAAI,cAAe,OAAAA,CAAA,EACxC,GAAIvJ,EAAM,MAAO,CAAE,GAAI,oBAAqB,KAAAA,CAAA,EAC5C,GAAImL,EAAW,MAAO,CAAE,GAAI,eAAgB,KAAMA,CAAA,EAElD,MAAMC,EAAOF,EAAa,MAAQ,GAClC,OAAIE,EAAK,WAAW,cAAc,EAAU,CAAE,GAAI,oBAAqB,KAAM,mBAAmBA,EAAK,MAAM,EAAqB,CAAC,GAC7HA,EAAK,WAAW,SAAS,EAAU,CAAE,GAAI,cAAe,OAAQ,mBAAmBA,EAAK,MAAM,CAAgB,CAAC,GAC/GA,EAAK,WAAW,UAAU,EAAU,CAAE,GAAI,eAAgB,KAAM,mBAAmBA,EAAK,MAAM,CAAiB,CAAC,GAE7G,IACT,CAEO,SAASC,IAAY,CAC1B,KAAM,CAAC9X,EAAW+X,CAAY,EAAIzW,WAAoB,UAAU,EAC1D,CAAC0W,EAAOC,CAAQ,EAAI3W,WAAuB,EAAE,EAC7C,CAAC4W,EAAYC,CAAa,EAAI7W,WAAS,EAAK,EAC5C,CAACF,EAAYC,CAAa,EAAIC,WAAS,EAAK,EAC5C,CAAC8W,EAAmBC,CAAoB,EAAI/W,WAAwB,IAAI,EACxE,CAAC+B,EAAUiV,CAAW,EAAIhX,WAA0B,IAAI,EACxD,CAACiX,EAAUC,CAAW,EAAIlX,WAA0B,IAAI,EACxD,CAACmX,EAAWC,CAAY,EAAIpX,WAAwB,IAAI,EACxD,CAACmC,EAASC,CAAU,EAAIpC,WAAS,EAAI,EACrC,CAACqX,EAASC,CAAU,EAAItX,WAAS,CAAC,EAEpBuX,UAAoB,IAC/Bb,EAAMA,EAAM,OAAS,CAAC,GAAK,CAAE,GAAIhY,CAAA,EACvC,CAACA,EAAWgY,CAAK,CAAC,EAErB,MAAMc,EAAWC,cAAaC,GAAqB,CACjDf,EAAU/S,GAAQ,CAAC,GAAGA,EAAK8T,CAAI,CAAC,CAClC,EAAG,EAAE,EAECC,EAAUF,cAAY,IAAM,CAChCd,EAAU/S,GAAQA,EAAI,MAAM,EAAG,EAAE,CAAC,CACpC,EAAG,EAAE,EAECgU,EAAUH,cAAavX,GAAkB,CAC7CyW,EAAS,EAAE,EACXF,EAAavW,CAAE,CACjB,EAAG,EAAE,EAEC2X,EAAkBJ,cAAY,SAA2B,OAC7D,GAAI,CACF,MAAMK,EAAO,MAAMrb,EAAI,IAAc,WAAW,EAChDua,EAAYc,CAAI,GACZ/a,EAAA+a,EAAK,WAAL,MAAA/a,EAAe,MACjBma,EAAYY,EAAK,SAAS,IAAI,EAEhCV,EAAa,IAAI,CACnB,OAASna,EAAK,CACZ,MAAAma,EAAcna,EAAc,SAAW,+BAA+B,EAChEA,CACR,CACF,EAAG,EAAE,EAELgE,YAAU,IAAM,CACd,IAAI8W,EAAY,GAEhB,eAAQ,IAAI,CACVtb,EAAI,IAAc,WAAW,EAAE,MAAM,IAAM,IAAI,EAC/CA,EAAI,IAAwB,WAAW,EAAE,MAAM,IAAM,IAAI,EACzDA,EAAI,kBAAkB,MAAM,IAAM,IAAI,EACvC,EACE,KAAK,CAAC,CAACub,EAAMC,CAAgB,IAAM,OAClC,GAAIF,EAAW,OACf,MAAMG,GAAeD,GAAA,YAAAA,EAAkB,SAAQlb,EAAAib,GAAA,YAAAA,EAAM,WAAN,YAAAjb,EAAgB,OAAQ,KACnEib,KAAkBA,CAAI,EACtBE,KAA0BA,CAAY,EAExCd,EADE,CAACY,GAAQ,CAACE,EACC,gCAEA,IAF+B,CAIhD,CAAC,EACA,MAAOjb,GAAQ,CACT8a,GAAWX,EAAcna,EAAc,SAAW,+BAA+B,CACxF,CAAC,EACA,QAAQ,IAAM,CACR8a,GAAW3V,EAAW,EAAK,CAClC,CAAC,EAEI,IAAM,CACX2V,EAAY,EACd,CACF,EAAG,EAAE,EAEL9W,YAAU,IAAM,CACTgW,GAAA,MAAAA,EAAU,QACf5Z,GAAW4Z,EAAS,KAAK,EACzBxZ,GAAiBwZ,EAAS,KAAK,EACjC,EAAG,CAACA,GAAA,YAAAA,EAAU,KAAK,CAAC,EAEpBhW,YAAU,IAAM,OACd,KAAIlE,EAAAka,GAAA,YAAAA,EAAU,QAAV,YAAAla,EAAiB,QAAS,SAAU,OACxC,MAAMob,EAAM,OAAO,WAAW,+BAA+B,EACvDC,EAAW,IAAM,CAChBnB,GAAA,MAAAA,EAAU,QACf5Z,GAAW4Z,EAAS,KAAK,EACzBxZ,GAAiBwZ,EAAS,KAAK,EACjC,EACA,OAAAkB,EAAI,iBAAiB,SAAUC,CAAQ,EAChC,IAAMD,EAAI,oBAAoB,SAAUC,CAAQ,CACzD,EAAG,CAACnB,GAAA,YAAAA,EAAU,KAAK,CAAC,EAEpBhW,YAAU,IAAM,CACd,MAAMoX,EAAY,IAAM,CACtBf,EAAY1T,GAAQA,EAAM,CAAC,EAC3BiU,EAAA,EAAkB,MAAM,MAAe,CACzC,EACMS,EAAqB,IAAM,CAC3B,SAAS,kBAAoB,WAAWD,EAAA,CAC9C,EAEA,cAAO,iBAAiB,SAAUA,CAAS,EAC3C,OAAO,iBAAiB,WAAYA,CAAS,EAC7C,SAAS,iBAAiB,mBAAoBC,CAAkB,EAEzD,IAAM,CACX,OAAO,oBAAoB,SAAUD,CAAS,EAC9C,OAAO,oBAAoB,WAAYA,CAAS,EAChD,SAAS,oBAAoB,mBAAoBC,CAAkB,CACrE,CACF,EAAG,CAACT,CAAe,CAAC,EAEpB5W,YAAU,IAAM,CACd,MAAMsX,EAAK,IAAI1b,GACT2b,EAAaD,EAAG,GAAIhc,GAAmB,CAC3C,GAAIA,EAAI,OAAS,YAAc,SAAUA,GAAOA,EAAI,KAClDya,EAAapT,IAAS,CAAE,GAAIA,GAAQ,GAAkB,GAAGrH,EAAI,MAAO,UAC3DA,EAAI,OAAS,SACtBsb,EAAA,EAAkB,MAAM,MAAe,UAC9Btb,EAAI,OAAS,eAAgB,CACtC,MAAMkc,EAAWlc,EAAI,KACrBya,EAAapT,GAAQ,CACnB,GAAI,CAACA,EAAK,OAAOA,EACjB,MAAMN,GAASM,EAAI,OAAS,IAAI,IAAKiP,GAAUA,EAAK,KAAO4F,EAAS,GAAKA,EAAW5F,CAAK,EACnF6F,GAASpV,EAAM,KAAMuP,GAASA,EAAK,KAAO4F,EAAS,EAAE,EAC3D,MAAO,CAAE,GAAG7U,EAAK,MAAO8U,GAASpV,EAAQ,CAACmV,EAAU,GAAGnV,CAAK,EAC9D,CAAC,CACH,MAAW/G,EAAI,OAAS,eACtBya,EAAapT,GACXA,GACI,CAAE,GAAGA,EAAK,OAAQA,EAAI,OAAS,IAAI,OAAQiP,GAASA,EAAK,KAAOtW,EAAI,EAAE,EAE3E,EACQA,EAAI,OAAS,kBAClBA,EAAI,UAAU2a,EAAY3a,EAAI,QAAQ,EAE1CA,EAAI,OAAS,kBACbA,EAAI,OAAS,iBACbA,EAAI,OAAS,oBACbA,EAAI,OAAS,kBAEbsb,EAAA,EAAkB,MAAM,MAAe,GAC9Btb,EAAI,OAAS,gBAAkBA,EAAI,OAAS,oBACrDya,EAAapT,GAAQ,CACnB,GAAI,CAACA,EAAK,OAAOA,EACjB,MAAMP,GAAUO,EAAI,QAAU,IAAI,IAAK+U,GACrCA,EAAM,OAASpc,EAAI,MAAM,KAAOA,EAAI,MAAQoc,CAC7C,EACD,MAAO,CAAE,GAAG/U,EAAK,OAAAP,CAAA,CACnB,CAAC,CAEL,CAAC,EAED,MAAO,IAAM,CACXmV,EAAA,EACAD,EAAG,OACL,CACF,EAAG,CAACV,EAAiBR,CAAO,CAAC,EAE7B,MAAMuB,EAAkBnB,cAAavX,GAAe,EAC9CA,IAAO,YAAcA,IAAO,QAAUA,IAAO,SAAWA,IAAO,YAAcA,IAAO,SACtF0X,EAAQ1X,CAAE,CAEd,EAAG,CAAC0X,CAAO,CAAC,EAENiB,EAAiBpB,cAAY,CAAC5M,EAAc3K,EAAYoL,IAA0C,CACtG,GAAIT,IAAS,eAAgB,CAC3B,MAAMiO,EAAS1C,GAAwB,CAMrC,KAAMlW,GAAM,KACZ,KAAMoL,GAAQ,KACf,EACGwN,GACFtB,EAASsB,CAAM,EAEjB,MACF,CAEA,GAAIjO,IAAS,QAAU3K,EAAI,CACzBsX,EAAS,CAAE,GAAI,cAAe,OAAQtX,EAAI,EAC1C,MACF,CACA,GAAI2K,IAAS,cAAgB3K,EAAI,CAC/BsX,EAAS,CAAE,GAAI,oBAAqB,KAAMtX,EAAI,EAC9C,MACF,CACA,GAAI2K,IAAS,SAAW3K,EAAI,CAC1BsX,EAAS,CAAE,GAAI,eAAgB,KAAMtX,EAAI,EACzC,MACF,CACA,GAAI2K,IAAS,UAAW,CACtB+M,EAAQ,UAAU,EAClB,MACF,CACI/M,IAAS,WACX+M,EAAQ,UAAU,CAEtB,EAAG,CAACA,EAASJ,CAAQ,CAAC,EAEhBuB,EAAa,IAAuB,CACxC,GAAI,CAAChX,GAAY,CAACkV,EAAU,OAAO,KAEnC,GAAIP,EAAM,OAAS,EAAG,CACpB,MAAMsC,EAActC,EAAMA,EAAM,OAAS,CAAC,EAC1C,OAAQsC,EAAY,IAClB,IAAK,aACH,OAAO1a,MAACoP,GAAA,CAAgB,SAAA3L,EAAoB,OAAQ4V,EAAS,eAAiBxM,GAASqM,EAAS,CAAE,GAAI,oBAAqB,KAAArM,CAAA,CAAM,EAAG,EACtI,IAAK,SACH,aAAQtG,GAAA,CAAa,SAAA9C,EAAoB,OAAQ4V,EAAS,YAAczR,GAASsR,EAAS,CAAE,GAAI,eAAgB,KAAAtR,CAAA,CAAM,EAAG,UAAW2R,EAAiB,EACvJ,IAAK,SACH,OAAOvZ,MAAC4T,GAAA,CAAa,SAAAnQ,EAAoB,OAAQ4V,CAAA,CAAS,EAC5D,IAAK,OACH,OAAOrZ,MAACqL,GAAA,CAAW,SAAA5H,EAAoB,OAAQ4V,CAAA,CAAS,EAC1D,IAAK,YACH,OAAOrZ,MAACuQ,GAAA,CAAgB,SAAA9M,EAAoB,OAAQ4V,CAAA,CAAS,EAC/D,IAAK,UACH,OAAOrZ,MAACyK,GAAA,CAAc,OAAQ4O,CAAA,CAAS,EACzC,IAAK,SACH,OAAOrZ,MAACqJ,GAAA,CAAa,OAAQgQ,CAAA,CAAS,EACxC,IAAK,oBACH,aAAQzM,GAAA,CAAqB,KAAM8N,EAAY,KAAM,OAAQrB,EAAS,EACxE,IAAK,eACH,OACErZ,MAACuG,GAAA,CACC,SAAA9C,EACA,OAAQ4V,EACR,YAAa,OACb,UAAWE,EACX,cAAemB,EAAY,OAGjC,IAAK,cACH,OACE1a,MAAC0U,GAAA,CACC,SAAAjR,EACA,UAAW8V,EACX,eAAgBmB,EAAY,OAC5B,cAAerB,EACf,WAAajD,GAAW,CACtBqC,EAAqBrC,CAAM,EAC3BkD,EAAQ,MAAM,CAChB,IAGN,QACE,OAAO,KAEb,CAEA,OAAQlZ,EAAA,CACN,IAAK,WACH,OAAOJ,MAACwD,GAAA,CAAe,SAAAC,EAAoB,UAAW8V,CAAA,CAAiB,EACzE,IAAK,OACH,OACEvZ,MAAC2a,WAAA,CAAS,SAAU3a,MAAC,OAAI,UAAU,iBAAiB,SAAAA,MAACJ,GAAA,CAAQ,KAAK,KAAK,EAAE,EACvE,SAAAI,MAACyX,GAAA,CACC,SAAAhU,EACA,SAAAkV,EACA,cAAeH,EACf,cAAe,IAAMC,EAAqB,IAAI,IAElD,EAEJ,IAAK,QACH,OACEzY,MAAC0U,GAAA,CACC,SAAAjR,EACA,UAAW8V,EACX,WAAanD,GAAW,CACtBqC,EAAqBrC,CAAM,EAC3BkD,EAAQ,MAAM,CAChB,IAGN,IAAK,WACH,aACGqB,WAAA,CAAS,eAAW,OAAI,UAAU,iBAAiB,SAAA3a,MAACJ,GAAA,CAAQ,KAAK,KAAK,EAAE,EACvE,SAAAI,MAACuX,GAAA,CAAe,SAAAoB,EAAoB,SAAAlV,EAAoB,UAAW8V,EAAiB,EACtF,EAEJ,IAAK,OACH,OACEvZ,MAAC+L,GAAA,CACC,SAAAtI,EACA,WAAa7B,GAAO,EACdA,IAAO,cAAgBA,IAAO,UAAYA,IAAO,UAAYA,IAAO,QAAUA,IAAO,aAAeA,IAAO,WAAaA,IAAO,WACjIsX,EAAS,CAAE,GAAAtX,EAAI,CAEnB,IAGN,QACE,OAAO,KAEb,EAEA,OAAIiC,EAEA9C,OAAC,OAAI,UAAU,iBACb,UAAAf,MAACJ,GAAA,CAAQ,KAAK,KAAK,EACnBI,MAAC,KAAE,0BAAc,GACnB,EAIA6Y,GAAa,CAACpV,GAAY,CAACkV,EAE3B5X,OAAC,OAAI,UAAU,iBACb,UAAAf,MAAC,MAAG,iCAAqB,EACzBA,MAAC,KAAG,SAAA6Y,GAAa,gCAAgC,EACjD7Y,MAAC,KAAE,UAAU,QAAQ,4DAAgD,GACvE,EAKFe,OAAAC,WAAA,CACE,UAAAD,OAACK,GAAA,CACC,UAAAhB,EACA,YAAaka,EACb,KAAM5C,GACN,SAAUA,GACV,eAAgB,IAAM4B,EAAQ,UAAU,EACxC,SAAU,IAAMf,EAAc,EAAI,EAEjC,UAAAH,EAAM,OAAS,GACdpY,MAAC,UAAO,KAAK,SAAS,UAAU,kBAAkB,QAASqZ,EAAS,aAAW,UAAU,kBAEzF,EAEDoB,EAAA,CAAW,IAGdza,MAACiT,GAAA,CAAkB,KAAMqF,EAAY,QAAS,IAAMC,EAAc,EAAK,EAAG,WAAYgC,CAAA,CAAgB,GACxG,CAEJ","names":["cn","parts","formatRelative","ts","diff","formatTime","d","truncate","max","priorityColors","TOKEN_KEY","LOOPBACK_KEY","ApiError","message","status","data","__publicField","ApiClient","token","params","tok","newSearch","newUrl","path","url","sep","r","signal","body","method","headers","opts","msg","text","api","enhancePrompt","buildWsUrl","base","Ws","handler","_a","socket","err","e","h","s","applyTheme","themeName","mode","resolved","applyThemeTokens","theme","root","hexToRgba","hex","alpha","m","n","g","Spinner","size","className","label","jsx","MobileHeader","onMenuClick","scrolled","activeTab","onSettings","onSearch","Menu","X","Search","MobileDrawer","open","onClose","tabs","onTabChange","jsxs","Fragment","t","Icon","MobileBottomNav","MobileLayout","children","fullTabs","onExitSettings","drawerOpen","setDrawerOpen","useState","setScrolled","id","MobileBottomSheet","title","actions","maxHeight","sheetRef","useRef","closeButtonRef","titleId","useId","lastActiveRef","dragStartYRef","dragDeltaRef","scrollLockYRef","getFocusable","useEffect","onKey","focusable","first","last","active","_b","onPointerDown","onPointerMove","delta","endDrag","EVENT_KINDS","KIND_LABELS","MobileActivity","snapshot","onRefresh","events","setEvents","loading","setLoading","filter","setFilter","paused","setPaused","detail","setDetail","submitText","setSubmitText","submitting","setSubmitting","loadEvents","filtered","_c","_d","_e","_f","agents","tasks","activeTasks","doneTasks","handleSubmit","handleRefresh","chip","cur","Send","v","Play","Pause","RefreshCw","k","Bot","a","i","renderEventMessage","ActivityIcon","MobileModal","modalRef","focusableSelector","CATEGORIES","CATEGORY_COLORS","MobileAgents","onBack","onOpenAgent","selectedAgent","setAgents","categoryFilter","setCategoryFilter","search","setSearch","detailAgent","setDetailAgent","newOpen","setNewOpen","invokePrompt","setInvokePrompt","invokeOpen","setInvokeOpen","ag","reload","q","invokeAgent","name","prompt","restartAgent","deleteAgent","createAgent","created","Plus","c","RotateCw","Trash2","NewAgentModal","onCreate","setName","description","setDescription","model","setModel","setMode","color","setColor","category","setCategory","tags","setTags","setPrompt","MobileConfig","entries","setEntries","editing","setEditing","saving","setSaving","load","key","value","detectType","handleChange","saveAll","resetRow","defaultValue","renderInput","entry","Save","Sliders","RotateCcw","MobileHistory","query","setQuery","latestEventByProject","event","pid","current","project","latestEvent","formatDuration","ms","History","MobileMods","mods","setMods","statusFilter","setStatusFilter","detailMod","setDetailMod","toggleMod","enabled","Power","MobileMore","onNavigate","FileText","Clock","Settings","ChevronRight","ELEMENT_TYPES","elementColor","type","ElementIcon","CheckSquare","StickyNote","HelpCircle","MobileArtifactCanvas","slug","canvas","setCanvas","meta","setMeta","editMode","setEditMode","selectedEl","setSelectedEl","showComments","setShowComments","addElementOpen","setAddElementOpen","addCommentOpen","setAddCommentOpen","commentText","setCommentText","plan","addElement","content","maxX","acc","el","maxY","updateElement","deleteElement","addComment","elementId","Pencil","MessageSquare","ElementEditForm","AddElementModal","element","onSave","setType","setTitle","setContent","setStatus","onAdd","MobileArtifacts","onOpenArtifact","artifacts","setArtifacts","newSlug","setNewSlug","newTitle","setNewTitle","createPlan","result","p","TYPES","ACTIONS","TIMEZONES","HOURS","_","MINUTES","DOWS","INTERVAL_UNITS","MobileSchedules","schedules","setSchedules","detailSchedule","setDetailSchedule","editor","setEditor","toggleSchedule","deleteSchedule","triggerSchedule","humanizeSchedule","formatNextShort","formatRel","ScheduleEditor","initialEditorState","initial","parsedCron","parseSimpleCron","parsedInterval","parseSimpleInterval","toDatetimeLocal","composeScheduleValue","onSaved","state","setState","update","schedule","payload","u","tz","iso","expr","dow","minNum","hourNum","pad","parsed","hourLabel","minute","SCOPES","SCOPE_ICONS","Folder","MobileSearchModal","scope","setScope","results","setResults","inputRef","timer","handleResultClick","item","grouped","items","MobileSkills","skills","setSkills","detailSkill","setDetailSkill","toggleSkill","Sparkles","STATUS_ORDER","STATUS_LABELS","NEXT_STATUS","hasTaskChat","task","metadata","hasTaskArtifacts","MobileTasks","selectedTaskId","onCloseDetail","onOpenChat","setTasks","activeStatus","setActiveStatus","detailTask","setDetailTask","newTaskOpen","setNewTaskOpen","artifactTaskId","setArtifactTaskId","artifactIds","setArtifactIds","artifactContent","setArtifactContent","artifactMeta","setArtifactMeta","artifactLoading","setArtifactLoading","assigneeFilter","setAssigneeFilter","sortBy","setSortBy","moveTask","taskId","newStatus","deleteTask","archiveTask","sorted","targetAssignee","settingsDefaultAgent","b","order","createTask","assignee","priority","openTaskArtifact","ids","count","Archive","NewTaskModal","setAssignee","setPriority","MobileSettings","React","MobileChat","MAIN_TABS","Activity","SettingsIcon","Grid","parseNotificationTarget","notification","agentName","link","MobileApp","setActiveTab","stack","setStack","searchOpen","setSearchOpen","pendingChatTaskId","setPendingChatTaskId","setSnapshot","settings","setSettings","bootError","setBootError","wsEpoch","setWsEpoch","useMemo","pushView","useCallback","view","popView","goToTab","refreshSnapshot","next","cancelled","snap","settingsResponse","nextSettings","mql","onChange","reconnect","onVisibilityChange","ws","offMessage","nextTask","exists","agent","handleTabChange","handleNavigate","target","renderView","stackedView","Suspense"],"ignoreList":[],"sources":["../../src/web/lib/utils.ts","../../src/web/lib/api.ts","../../src/web/lib/ws.ts","../../src/web/lib/types.ts","../../src/web/components/Spinner.tsx","../../src/web/components/MobileHeader.tsx","../../src/web/components/MobileDrawer.tsx","../../src/web/components/MobileBottomNav.tsx","../../src/web/components/MobileLayout.tsx","../../src/web/mobile/components/MobileBottomSheet.tsx","../../src/web/mobile/views/MobileActivity.tsx","../../src/web/mobile/components/MobileModal.tsx","../../src/web/mobile/views/MobileAgents.tsx","../../src/web/mobile/views/MobileConfig.tsx","../../src/web/mobile/views/MobileHistory.tsx","../../src/web/mobile/views/MobileMods.tsx","../../src/web/mobile/views/MobileMore.tsx","../../src/web/mobile/views/MobileArtifactCanvas.tsx","../../src/web/mobile/views/MobileArtifacts.tsx","../../src/web/mobile/views/MobileSchedules.tsx","../../src/web/mobile/views/MobileSearchModal.tsx","../../src/web/mobile/views/MobileSkills.tsx","../../src/web/mobile/views/MobileTasks.tsx","../../src/web/MobileApp.tsx"],"sourcesContent":["// src/lib/utils.ts — tiny shared helpers.\n\nexport function cn(...parts: (string | false | null | undefined)[]): string {\n return parts.filter(Boolean).join(' ');\n}\n\n/** Compact relative time. */\nexport function formatRelative(ts: number | string | Date | undefined | null): string {\n if (!ts && ts !== 0) return '';\n const t = typeof ts === 'number' ? ts : new Date(ts).getTime();\n if (Number.isNaN(t)) return '';\n const diff = Date.now() - t;\n if (diff < 0) return 'just now';\n if (diff < 60_000) return 'just now';\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;\n if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;\n if (diff < 7 * 86_400_000) return `${Math.floor(diff / 86_400_000)}d ago`;\n return new Date(t).toLocaleDateString();\n}\n\n/** Full localized timestamp. */\nexport function formatTime(ts: number | string | Date | undefined | null): string {\n if (!ts) return '';\n const d = typeof ts === 'number' ? new Date(ts) : new Date(ts);\n if (Number.isNaN(d.getTime())) return '';\n return d.toLocaleString();\n}\n\n/** Trailing-suffix truncation. */\nexport function truncate(s: string | undefined | null, max = 160): string {\n if (!s) return '';\n return s.length > max ? s.slice(0, max) + '…' : s;\n}\n\n/** Debounce. */\nexport function debounce<T extends (...args: never[]) => void>(\n fn: T,\n ms = 200,\n): (...args: Parameters<T>) => void {\n let timer: ReturnType<typeof setTimeout> | null = null;\n return (...args: Parameters<T>) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => fn(...args), ms);\n };\n}\n\n/** Color helpers (status badges). */\nexport const priorityColors: Record<string, string> = {\n low: 'var(--text-dim)',\n normal: 'var(--info)',\n high: 'var(--error)',\n};\n\nexport const statusBadgeKind = (\n status: string,\n): 'success' | 'warning' | 'error' | 'info' | 'accent' | '' => {\n switch (status) {\n case 'approved':\n case 'done':\n return 'success';\n case 'draft':\n case 'queued':\n return '';\n case 'in-progress':\n case 'doing':\n return 'info';\n case 'rejected':\n return 'error';\n default:\n return 'accent';\n }\n};\n\n/** Cheap hash for change-detection on text (e.g. raw JSON). */\nexport function hashText(s: string): string {\n let h = 5381;\n for (let i = 0; i < s.length; i++) h = ((h << 5) + h) ^ s.charCodeAt(i);\n return (h >>> 0).toString(36);\n}\n\n/**\n * v3.7.1 — Auto-generate a task title from a prompt body when the user\n * didn't provide one. Strips markdown headers, takes the first non-empty\n * line, truncates to ~60 chars at a word boundary. Falls back to a\n * timestamped placeholder if there's nothing to summarize.\n */\nexport function autoTitleFromContent(\n body: string | undefined | null,\n fallback = true,\n): string {\n const raw = (body || '').replace(/\\r\\n?/g, '\\n').trim();\n if (!raw) {\n if (!fallback) return '';\n const now = new Date();\n const pad = (n: number) => String(n).padStart(2, '0');\n return `Untitled task — ${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}`;\n }\n // Take first non-empty line; strip leading markdown heading hashes + quotes.\n const firstLine = raw\n .split('\\n')\n .map((l) => l.replace(/^[\\s>#*\\-]+/, '').trim())\n .find((l) => l.length > 0) || '';\n if (!firstLine) {\n if (!fallback) return '';\n const now = new Date();\n const pad = (n: number) => String(n).padStart(2, '0');\n return `Untitled task — ${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}`;\n }\n if (firstLine.length <= 60) return firstLine;\n // Truncate at last word boundary within 60 chars.\n const cut = firstLine.slice(0, 60);\n const lastSpace = cut.lastIndexOf(' ');\n return (lastSpace > 20 ? cut.slice(0, lastSpace) : cut).trimEnd() + '…';\n}\n","// src/lib/api.ts — REST client. Single instance, type-safe wrappers.\n//\n// v3.6.2 — Token URL pickup for reverse-proxy / Tailscale Serve access.\n// When the dashboard is accessed via Tailscale Serve (or any reverse\n// proxy) the browser runs on a different origin than localhost. The server\n// sees the remote client through the local proxy and requires auth.\n// The `?token=…` URL param lets operators share a direct link that\n// automatically installs the token into localStorage so the dashboard\n// \"just works\" after the first paste — no separate localhost tab needed.\n//\n// v3.6.1 — Authentication is now loopback-aware on the server side.\n// The browser makes API calls over 127.0.0.1 (or via Tailscale serve\n// proxying to 127.0.0.1), and the server auto-trusts loopback\n// connections. So a fresh browser tab \"just works\" — no token paste\n// required for the normal case.\n//\n// The token still exists for non-loopback clients (external API\n// consumers, scripts) and as a defense-in-depth option via the\n// BIZAR_DASHBOARD_REQUIRE_AUTH=1 env var. The localStorage flow\n// remains: probe /api/auth/status on load, send the token header\n// if one is set, fall back to none if not (the server will trust\n// us anyway because we're loopback).\n//\n// For SSE routes, the token is also added as a `?token=...` query\n// parameter (browsers can't set custom headers on EventSource).\n// Again, loopback makes this optional but kept for safety.\n\nconst TOKEN_KEY = 'bizar-auth-token';\nconst LOOPBACK_KEY = 'bizar-loopback-trusted';\n\nexport class ApiError extends Error {\n status: number;\n data: unknown;\n constructor(message: string, status: number, data?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.data = data;\n }\n}\n\nclass ApiClient {\n base = '/api';\n\n /** Get the cached bearer token from localStorage, or '' if none. */\n getToken(): string {\n try {\n return localStorage.getItem(TOKEN_KEY) || '';\n } catch {\n return '';\n }\n }\n\n /** Persist the token. Called from the Settings tab after Reveal/Regenerate. */\n setToken(token: string): void {\n try {\n if (token) localStorage.setItem(TOKEN_KEY, token);\n else localStorage.removeItem(TOKEN_KEY);\n } catch {\n /* localStorage unavailable — token won't persist across reloads */\n }\n }\n\n /**\n * v3.6.2 — Check the URL search params for a `token` value and\n * persist it into localStorage if found. Strips the param from the\n * URL via `history.replaceState` so the token doesn't leak into\n * browser history or shared links.\n *\n * Returns `true` if a token was found and saved, `false` otherwise.\n * Safe to call multiple times (subsequent calls with no token in URL\n * return `false` and are no-ops).\n */\n pickupTokenFromUrl(): boolean {\n try {\n const params = new URLSearchParams(window.location.search);\n const tok = params.get('token');\n if (!tok) return false;\n this.setToken(tok);\n // Remove the token param from the URL without a page reload.\n params.delete('token');\n const newSearch = params.toString();\n const newUrl = newSearch\n ? `${window.location.pathname}?${newSearch}`\n : window.location.pathname;\n history.replaceState(null, '', newUrl);\n return true;\n } catch {\n return false;\n }\n }\n\n /** Build the URL with the token appended as ?token=… if present. */\n urlWithToken(path: string): string {\n const url = this.base + path;\n const tok = this.getToken();\n if (!tok) return url;\n const sep = url.includes('?') ? '&' : '?';\n return `${url}${sep}token=${encodeURIComponent(tok)}`;\n }\n\n /**\n * v3.6.1 — Probe the server's /api/auth/status endpoint and cache\n * whether the current browser is on a loopback-trusted connection.\n * Called once on app mount so the rest of the client can decide\n * whether to surface the \"paste your token\" UI in Settings or\n * just let the dashboard work.\n *\n * Returns a snapshot of the status; result is also cached for\n * subsequent calls to isLoopbackTrusted().\n */\n async probeAuthStatus(): Promise<{ required: boolean; loopback: boolean; peer: string }> {\n try {\n const r = await this.get<{ required: boolean; loopback: boolean; peer: string }>('/auth/status');\n try { localStorage.setItem(LOOPBACK_KEY, r.loopback ? '1' : '0'); } catch { /* noop */ }\n return r;\n } catch {\n return { required: false, loopback: true, peer: '' };\n }\n }\n\n /** Cached result of the last /api/auth/status probe. */\n isLoopbackTrusted(): boolean {\n try { return localStorage.getItem(LOOPBACK_KEY) !== '0'; } catch { return true; }\n }\n\n /**\n * Perform an authenticated GET against the dashboard server.\n *\n * IMPORTANT: do NOT include the `/api` prefix in `path` — the\n * wrapper adds it for you. Calling `api.get('/api/projects')` will\n * 404 because the server sees `/api/api/projects`. Use the\n * unprefixed form: `api.get('/projects')`.\n *\n * @example\n * const r = await api.get<ProjectList>('/projects');\n */\n async get<T>(path: string, signal?: AbortSignal): Promise<T> {\n return this.req<T>('GET', path, undefined, signal);\n }\n\n /**\n * Perform an authenticated POST against the dashboard server.\n *\n * IMPORTANT: do NOT include the `/api` prefix in `path` — the\n * wrapper adds it. Use the unprefixed form: `api.post('/fs/mkdir', body)`.\n */\n async post<T>(path: string, body?: unknown, signal?: AbortSignal): Promise<T> {\n return this.req<T>('POST', path, body, signal);\n }\n\n /**\n * Perform an authenticated PUT against the dashboard server.\n *\n * IMPORTANT: do NOT include the `/api` prefix in `path` — the\n * wrapper adds it. Use the unprefixed form: `api.put('/settings', body)`.\n */\n async put<T>(path: string, body?: unknown, signal?: AbortSignal): Promise<T> {\n return this.req<T>('PUT', path, body, signal);\n }\n\n /**\n * Perform an authenticated PATCH against the dashboard server.\n *\n * IMPORTANT: do NOT include the `/api` prefix in `path` — the\n * wrapper adds it. Use the unprefixed form: `api.patch('/settings', body)`.\n */\n async patch<T>(path: string, body?: unknown, signal?: AbortSignal): Promise<T> {\n return this.req<T>('PATCH', path, body, signal);\n }\n\n /**\n * Perform an authenticated DELETE against the dashboard server.\n *\n * IMPORTANT: do NOT include the `/api` prefix in `path` — the\n * wrapper adds it. Use the unprefixed form: `api.del('/projects/123')`.\n */\n async del<T = unknown>(path: string, signal?: AbortSignal): Promise<T> {\n return this.req<T>('DELETE', path, undefined, signal);\n }\n\n private async req<T>(method: string, path: string, body?: unknown, signal?: AbortSignal): Promise<T> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n const tok = this.getToken();\n if (tok) headers.Authorization = `Bearer ${tok}`;\n const opts: RequestInit = { method, headers };\n if (body !== undefined && body !== null) {\n opts.body = typeof body === 'string' ? body : JSON.stringify(body);\n }\n if (signal) opts.signal = signal;\n const r = await fetch(this.base + path, opts);\n const ct = r.headers.get('content-type') || '';\n if (ct.includes('application/json')) {\n const data = await r.json();\n if (!r.ok) {\n const msg =\n (data && typeof data === 'object' && 'message' in data\n ? (data as { message?: string }).message\n : undefined) || `${method} ${path}: ${r.status}`;\n throw new ApiError(msg, r.status, data);\n }\n return data as T;\n }\n const text = await r.text();\n if (!r.ok) {\n throw new ApiError(\n `${method} ${path}: ${r.status} — ${text.slice(0, 200)}`,\n r.status,\n text,\n );\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as T;\n }\n }\n}\n\nexport const api = new ApiClient();\n\n// v3.6.2 — Auto-pickup token from URL on module load so the dashboard\n// works through reverse proxies (Tailscale Serve, nginx, etc.) without\n// needing a separate localhost tab.\napi.pickupTokenFromUrl();\n\nexport { ApiClient, TOKEN_KEY };\n\n/**\n * Placeholder for the \"Enhance prompt\" feature.\n * TODO: call system LLM API to improve the prompt\n * Will use settings.systemLlm config\n * Endpoint: POST /api/enhance-prompt with { text }\n */\nexport async function enhancePrompt(text: string): Promise<string> {\n return text; // passthrough for now\n}\n","// src/lib/ws.ts — WebSocket with auto-reconnect + status + handlers.\n//\n// v3.6.0 — Bearer-token authentication. Browsers cannot set the\n// Authorization header on the native WebSocket constructor, so we\n// append the token as a `?token=<token>` query parameter on the\n// connection URL. The server (auth.mjs) accepts both forms.\nimport type { WsMessage, WsStatus } from './types';\nimport { TOKEN_KEY } from './api';\n\ntype Handler = (msg: WsMessage) => void;\ntype StatusHandler = (status: WsStatus) => void;\n\n/**\n * Build the WS URL with the auth token query param appended.\n * Returns just the bare /ws URL if no token is set (the server\n * will 401 and the connection will fail fast — the UI surfaces\n * \"auth required\" in that case).\n */\nfunction buildWsUrl(): string {\n if (typeof location === 'undefined') return 'ws://localhost/ws';\n const base = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`;\n let tok = '';\n try {\n tok = localStorage.getItem(TOKEN_KEY) || '';\n } catch {\n /* localStorage unavailable */\n }\n if (!tok) return base;\n return `${base}?token=${encodeURIComponent(tok)}`;\n}\n\nexport class Ws {\n private readonly urlOverride?: string;\n private handlers = new Set<Handler>();\n private statusHandlers = new Set<StatusHandler>();\n private ws: WebSocket | null = null;\n private reconnectDelay = 1000;\n private readonly maxReconnectDelay = 15000;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _status: WsStatus = 'connecting';\n private pingTimer: ReturnType<typeof setInterval> | null = null;\n private closed = false;\n\n constructor(url?: string) {\n this.urlOverride = url;\n this.connect();\n }\n\n on(handler: Handler): () => void {\n this.handlers.add(handler);\n return () => {\n this.handlers.delete(handler);\n };\n }\n\n onStatus(handler: StatusHandler): () => void {\n this.statusHandlers.add(handler);\n // Fire current status immediately\n handler(this._status);\n return () => {\n this.statusHandlers.delete(handler);\n };\n }\n\n get status(): WsStatus {\n return this._status;\n }\n\n send(msg: unknown): boolean {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(msg));\n return true;\n }\n return false;\n }\n\n close(): void {\n this.closed = true;\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n if (this.pingTimer) clearInterval(this.pingTimer);\n this.pingTimer = null;\n try {\n this.ws?.close();\n } catch {\n /* ignore */\n }\n this.ws = null;\n this.setStatus('disconnected');\n }\n\n private connect(): void {\n if (this.closed) return;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.setStatus('connecting');\n const url = this.urlOverride ?? buildWsUrl();\n let socket: WebSocket;\n try {\n // Re-resolve the URL on every (re)connect — the token may have\n // changed since the last attempt (e.g. after Regenerate).\n socket = new WebSocket(url);\n this.ws = socket;\n } catch (err) {\n console.warn('[ws] construct failed:', err);\n this.scheduleReconnect();\n return;\n }\n socket.addEventListener('open', () => {\n if (this.ws !== socket || this.closed) return;\n this.reconnectDelay = 1000;\n this.setStatus('connected');\n // Keep-alive ping every 30s\n if (this.pingTimer) clearInterval(this.pingTimer);\n this.pingTimer = setInterval(() => {\n if (this._status === 'connected') this.send({ type: 'ping' });\n }, 30_000);\n });\n socket.addEventListener('close', () => {\n if (this.ws !== socket) return;\n this.ws = null;\n this.setStatus('disconnected');\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = null;\n }\n if (!this.closed) this.scheduleReconnect();\n });\n socket.addEventListener('error', () => {\n if (this.ws !== socket) return;\n // 'close' will follow — keep this for logs\n console.warn('[ws] error');\n });\n socket.addEventListener('message', (e) => {\n let msg: WsMessage;\n try {\n msg = JSON.parse(e.data);\n } catch {\n console.warn('[ws] bad message');\n return;\n }\n for (const h of this.handlers) {\n try {\n h(msg);\n } catch (err) {\n console.error('[ws] handler error:', err);\n }\n }\n });\n }\n\n private scheduleReconnect(): void {\n if (this.closed || this.reconnectTimer) return;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, this.reconnectDelay);\n this.reconnectDelay = Math.min(\n this.reconnectDelay * 1.6,\n this.maxReconnectDelay,\n );\n }\n\n private setStatus(s: WsStatus): void {\n this._status = s;\n for (const h of this.statusHandlers) {\n try {\n h(s);\n } catch (err) {\n console.error('[ws] status handler error:', err);\n }\n }\n }\n}\n","// src/lib/types.ts — TypeScript types for the Bizar dashboard.\n// All shapes here mirror the JSON returned by src/server/api.mjs.\n\n/**\n * Dialog component types that can be rendered in the dashboard.\n * Aligned with `DialogComponent` in plugins/bizar/src/commands.ts.\n * If you add a new component here, mirror it there and in\n * dialog-store.mjs KNOWN_COMPONENTS.\n */\nexport type DialogComponent = 'visual-artifact' | 'visual-plan' | 'plan-create' | 'plan-list' | 'artifact-create' | 'artifact-list' | 'help' | 'audit' | 'generic';\n\n/**\n * A modal dialog request emitted by the slash-command plugin and\n * delivered to the dashboard via the `dialog:show` WS message.\n * Thor's plugin code emits these; the desktop Chat view (sibling scope)\n * mounts a component keyed by `component` and seeds it with `data`.\n */\nexport type DialogDescriptor = {\n id: string;\n title: string;\n /** Original slash command, e.g. \"/visual-plan on\". */\n command: string;\n /** Component key the Chat view mounts. One of: 'visual-plan' | 'plan-create' | 'plan-list' | 'help' | 'audit' | 'generic' */\n component: DialogComponent;\n /** Component-specific payload. Shape depends on `component`. */\n data?: Record<string, unknown>;\n};\n\nexport type ThemeName = 'dark' | 'light' | 'system';\n\nexport type ThemeSettings = {\n mode: ThemeName;\n accent: string;\n success: string;\n warning: string;\n error: string;\n info: string;\n fontFamily: string;\n fontSize: number;\n compactMode: boolean;\n animations: boolean;\n};\n\nexport type UiSettings = {\n layout: 'topnav' | 'sidebar' | 'both';\n showHeader: boolean;\n showStatusBar: boolean;\n defaultTab: string;\n accentColor?: string;\n};\n\nexport type Agent = {\n name: string;\n description: string;\n model: string;\n mode: string;\n file: string;\n path: string;\n mtime: number;\n tools?: string[];\n tags?: string[];\n category?: string;\n color?: string;\n prompt?: string;\n permissions?: unknown;\n // v3.1.0 — runtime status (server-attached)\n status?: 'idle' | 'working' | 'error' | 'stuck' | string;\n currentTaskId?: string | null;\n currentTaskStartedAt?: number;\n lastSeen?: number;\n heartbeat?: number;\n lastError?: { ts: number; message: string } | null;\n lastTask?: { id: string; finishedAt: number; status: string } | null;\n successRate?: number;\n tasksTotal?: number;\n tasksSucceeded?: number;\n tasksFailed?: number;\n isStuck?: boolean;\n // v3.2.0 — agent hierarchy (parent/level/role).\n level?: number;\n parent?: string | null;\n role?: string;\n};\n\nexport type ProjectRecord = {\n id: string;\n name: string;\n path: string;\n lastAccessed?: string;\n status: 'active' | 'inactive' | 'error' | string;\n summary?: string;\n};\n\nexport type ProjectsResponse = {\n projects: ProjectRecord[];\n active: string | null;\n};\n\n/** A single entry returned by GET /api/fs */\nexport type DirectoryEntry = {\n name: string;\n path: string;\n isDir: boolean;\n isSymlink: boolean;\n};\n\n/** Response shape for GET /api/fs */\nexport type DirectoryListing = {\n path: string;\n parent: string | null;\n entries: DirectoryEntry[];\n /** True when the directory had more than 500 entries (capped). */\n truncated?: boolean;\n /** Actual entry count when truncated. */\n totalEntries?: number;\n};\n\n/** Request shape for POST /api/fs/mkdir */\nexport type MkdirRequest = { parent: string; name: string };\n\n/** Response shape for POST /api/fs/mkdir */\nexport type MkdirResponse = { path: string; parent: string; name: string };\n\n/** Response shape for POST /api/projects/scan */\nexport type ScanResult = {\n added: Array<{ id: string; path: string; name: string }>;\n skipped: number;\n scanned: number;\n error?: string;\n};\n\n// v3.19.0 — Artifact (formerly Plan). A user-facing proposal that an agent\n// creates to surface design choices, options, or implementation paths.\n// The user reviews it and either approves, rejects, or modifies.\nexport type Artifact = {\n /** Unique slug — used in the file path (`plans/<slug>/`) and URL. */\n slug: string;\n /** Human-readable title shown in the artifacts list. */\n title: string;\n /** One-line summary shown under the title. */\n description: string;\n /** Lifecycle status. */\n status: 'draft' | 'pending-review' | 'approved' | 'rejected' | 'archived';\n /** What kind of artifact this is — drives the renderer. */\n kind: 'design' | 'plan' | 'spec' | 'mockup' | 'report' | 'freeform' | string;\n /** Agent or user that created this artifact. */\n author: string;\n /** User's decision on this artifact. */\n decision: 'pending' | 'approved' | 'rejected' | 'modified' | null;\n /** Optional user-supplied notes when approving/rejecting. */\n decisionNote: string | null;\n /** Filesystem source. */\n source: 'worktree' | 'global' | string;\n /** Element count (visual elements) — for plan-like artifacts. */\n elementCount: number | null;\n /** Comment count. */\n commentCount: number | null;\n /** Last-modified time. */\n mtime: number;\n /** Optional URL to the artifact's full view. */\n artifactUrl: string | null;\n /** Tags — free-form, used for filtering. */\n tags: string[];\n};\n\nexport type CanvasElement = {\n id: string;\n type: string;\n title?: string;\n content?: string;\n x: number;\n y: number;\n width: number;\n height: number;\n status?: string;\n};\n\nexport type CanvasConnection = {\n id: string;\n fromElementId?: string;\n from?: string;\n toElementId?: string;\n to?: string;\n label?: string;\n};\n\nexport type CanvasComment = {\n id: string;\n elementId?: string;\n text: string;\n author: string;\n created: string;\n thread?: { author: string; text: string }[];\n};\n\nexport type CanvasViewport = { x: number; y: number; zoom: number };\n\nexport type Canvas = {\n schemaVersion?: number;\n title: string;\n elements: CanvasElement[];\n connections: CanvasConnection[];\n comments: CanvasComment[];\n viewport: CanvasViewport;\n};\n\nexport type ConfigResponse = {\n path: string;\n data: unknown;\n raw: string;\n exists: boolean;\n};\n\nexport interface SystemLlmConfig {\n enabled: boolean;\n provider: string; // e.g. \"opencode\"\n model: string; // e.g. \"opencode/deepseek-v4-flash-free\"\n // api key is read from auth.json for the provider, not stored here\n}\n\nexport type Settings = {\n theme: ThemeSettings;\n ui: UiSettings;\n defaultAgent: string;\n defaultModel: string;\n notifications: {\n onAgentComplete: boolean;\n onPlanApproval: boolean;\n };\n dashboard: {\n autoLaunchWeb: boolean;\n projectsDirectory?: string;\n allowedRoots?: string[];\n };\n service: {\n enabled: boolean;\n autostart: boolean;\n };\n about: {\n version: string;\n homepage: string;\n license: string;\n };\n agents: {\n maxParallel: number;\n stuckThresholdMs: number;\n autoRestart: boolean;\n };\n // v3.19.0 — Personalization (free-form text shown to agents).\n personalization: {\n displayName: string;\n role: string;\n team: string;\n aboutMe: string;\n preferences: string;\n };\n // v3.19.0 — Workflow toggles.\n workflow: {\n /** Allow agents to create / mutate artifacts. */\n artifactsEnabled: boolean;\n /** When true, agents make decisions themselves instead of asking. */\n agentsDecideAutonomously: boolean;\n /** Per-tab override for autonomy (chat input). */\n chatAutonomous: boolean;\n };\n // v3.21.0 — System LLM calls (auto-title, enhance-prompt, summarization).\n systemLlm?: SystemLlmConfig;\n // v4.5.0 — MiniMax Token Plan integration. `apiKey` is the user's\n // Subscription Key (NOT a pay-as-you-go API key). `groupId` is usually\n // 'default' for an individual team. `baseUrl` is the Token Plan host\n // (the remains endpoint lives on www.*, not api.*). `chatBaseUrl` is\n // the chat-completions host (api.*/v1).\n minimax?: {\n enabled: boolean;\n apiKey: string;\n groupId: string;\n baseUrl: string;\n chatBaseUrl: string;\n };\n // v5.0.0 — Headroom context compression settings.\n headroom?: HeadroomSettings;\n};\n\nexport type HeadroomSettings = {\n enabled: boolean;\n autoInstall: boolean;\n port: number;\n host: string;\n outputShaper: boolean;\n telemetry: boolean;\n budget: number;\n backend: string;\n autoStart: boolean;\n autoWrap: boolean;\n routeAllProviders: boolean;\n};\n\nexport type SettingsResponse = {\n path: string;\n data: Settings;\n exists: boolean;\n};\n\nexport type OverviewCounts = {\n agents: number;\n plans: number;\n projects: number;\n sessions: number;\n activeProject?: string | null;\n};\n\nexport type OverviewVersions = {\n node: string;\n platform: string;\n projectRoot: string;\n bizarRoot: string;\n};\n\nexport type ActivityItem = {\n ts: string;\n kind: string;\n [k: string]: unknown;\n};\n\nexport type Overview = {\n counts: OverviewCounts;\n recentActivity: ActivityItem[];\n versions: OverviewVersions;\n generatedAt: string;\n};\n\nexport type ChatMessage = {\n id?: string;\n role: 'user' | 'assistant' | 'system' | string;\n content?: string;\n message?: string;\n agent?: string;\n ts?: string | number;\n pinned?: boolean;\n};\n\nexport type ChatSession = {\n id: string;\n file: string;\n mtime: number;\n size: number;\n /** 'bizar' sessions come from the per-project .jsonl store; 'opencode' come from opencode.db */\n source?: 'bizar' | 'opencode';\n /** Filled when source === 'opencode' — the URL to open that session in the opencode web UI */\n opencodeUrl?: string;\n /** Display title; defaults to id when absent */\n title?: string;\n};\n\nexport type ChatResponse = {\n messages: ChatMessage[];\n sessions: ChatSession[];\n};\n\nexport type Task = {\n id: string;\n title: string;\n description: string;\n status: 'queued' | 'doing' | 'done' | 'blocked' | 'archived' | string;\n tags: string[];\n priority: 'low' | 'normal' | 'high' | string;\n assignee?: string | null;\n parent?: string | null;\n dependencies?: string[];\n timeSpent?: number;\n recurring?: { cron?: string; lastGenerated?: string; freq?: 'daily' | 'weekly' | 'monthly' | string } | null;\n attachments?: string[];\n comments?: { id: string; text: string; createdAt: string }[];\n activity?: { id: string; type: string; ts: string; data?: unknown }[];\n archived?: boolean;\n workedBy?: string | null;\n dueDate?: string | null;\n // v3.2.0 — main-task fields from the task delegator.\n subtasks?: string[];\n metadata?: Record<string, unknown> | null;\n // v3.3.0 — progress fields surfaced from metadata.\n progress?: number;\n currentStep?: string | null;\n progressAgent?: string | null;\n progressHistory?: { ts: string; progress: number; step: string | null; agent?: string | null }[];\n createdAt: string;\n updatedAt: string;\n completedAt?: string | null;\n _timerStart?: number;\n};\n\nexport type Notification = {\n id: string;\n ts: string;\n severity: 'info' | 'success' | 'warning' | 'error';\n message: string;\n source: string;\n title?: string;\n link?: string | null;\n read?: boolean;\n meta?: Record<string, unknown> | null;\n};\n\nexport type NotificationStats = {\n total: number;\n unread: number;\n lastTs: string | null;\n counts: Record<string, number>;\n};\n\nexport type NotificationsResponse = {\n notifications: Notification[];\n stats: NotificationStats;\n};\n\nexport type CustomTheme = {\n name: string;\n colors: Partial<ThemeSettings>;\n createdAt?: string | null;\n};\n\nexport type BudgetCheck = {\n /** Skip the run if at least this many bg tasks are already running or pending. */\n maxConcurrent?: number;\n /** When true, the runner consults `maxConcurrent` and skips the run if the budget is exceeded. */\n skipIfBudgetLow?: boolean;\n};\n\nexport type ScheduleAction = {\n type: 'command' | 'agent' | 'webhook';\n target: string;\n /** Free-form prompt for `agent` actions — the text the agent will work from. */\n prompt?: string;\n /** Optional webhook method override (defaults to POST). */\n method?: string;\n /** Webhook body. May also carry `prompt` for an agent dispatch. */\n body?: unknown;\n /** Title used when this action dispatches an agent task. */\n title?: string;\n /** Display name override when dispatching. */\n name?: string;\n};\n\nexport type Schedule = {\n id: string;\n name: string;\n type: 'cron' | 'interval' | 'once';\n /** For `cron`: a 5-field cron expression. For `interval`: a \"30m\" / \"2h\" / \"1d\" string. For `once`: an ISO datetime. */\n schedule: string;\n /** IANA timezone for `cron` schedules. Defaults to \"UTC\" when absent. */\n timezone?: string;\n action: ScheduleAction;\n /** Optional budget pre-flight: skip the run when too many bg tasks are already in flight. */\n budgetCheck?: BudgetCheck;\n enabled: boolean;\n createdAt: string;\n updatedAt: string;\n lastRun: string | null;\n lastResult: 'success' | 'error' | 'skipped' | null;\n lastError: string | null;\n nextRun: string | null;\n history: { ts: string; result: string; error: string | null }[];\n};\n\nexport type Mod = {\n id: string;\n name: string;\n version: string;\n author: string;\n description: string;\n bizar: string;\n type: string;\n enabled: boolean;\n permissions: string[];\n entry: Record<string, string>;\n files: { category: string; name: string; path: string }[];\n path: string;\n installedAt: string | null;\n};\n\nexport type Provider = {\n id: string;\n name: string;\n baseURL: string;\n apiKey: string;\n models: string[];\n enabled: boolean;\n};\n\nexport type McpServer = {\n id: string;\n type?: 'local' | 'remote';\n command: string;\n args: string[];\n env: Record<string, string>;\n url?: string;\n headers?: Record<string, string>;\n oauth?: boolean;\n enabled: boolean;\n};\n\nexport type DiagnosticItem = {\n line: string;\n ts: string | null;\n};\n\nexport type Diagnostics = {\n version: string;\n uptime: number;\n uptimeMs: number;\n nodeVersion: string;\n platform: string;\n memory: { rss: number; heapUsed: number; heapTotal: number };\n counts: {\n agents: number;\n plans: number;\n tasks: number;\n projects: number;\n activeProject: string | null;\n mods: number;\n schedules: number;\n providers: number;\n mcps: number;\n };\n errors: DiagnosticItem[];\n service: { running: boolean; pid?: number; error?: string };\n};\n\nexport type TailscaleStatus = {\n installed: boolean;\n version: string | null;\n authenticated: boolean;\n backend: string;\n hostname: string;\n settings: { enabled: boolean; port: number; https: boolean; hostname: string };\n};\n\n// ────────────────────────────────────────────────────────────────────────\n// v6.0.0 — Doctor page types\n// ────────────────────────────────────────────────────────────────────────\n\nexport type DoctorStatus = 'ok' | 'warn' | 'fail';\n\nexport type DoctorCheck = {\n name: string;\n status: DoctorStatus;\n message: string;\n};\n\nexport type DoctorChecks = {\n system: DoctorCheck[];\n config: DoctorCheck[];\n services: DoctorCheck[];\n};\n\nexport type DoctorHealth = {\n ts: string;\n status: DoctorStatus;\n issues: DoctorCheck[];\n groups: DoctorChecks;\n};\n\nexport type DoctorCounts = {\n tasks: number;\n schedules: number;\n mods: number;\n providers: number;\n mcps: number;\n agents: number;\n projects: number;\n activeProject: string | null;\n workspaces: number;\n voiceNotes: number;\n evalRuns: number;\n backups: number;\n};\n\nexport type DoctorService = {\n running: boolean;\n port?: number;\n host?: string;\n pid?: number;\n uptime?: number;\n error?: string;\n baseUrl?: string | null;\n reachable?: boolean;\n path?: string | null;\n logPath?: string;\n logExists?: boolean;\n};\n\nexport type DoctorServices = {\n dashboard: DoctorService;\n headroom: DoctorService;\n lightrag: DoctorService;\n opencode: DoctorService;\n};\n\nexport type DoctorRecentError = {\n line: string;\n ts: string | null;\n tsMs: number | null;\n};\n\nexport type DoctorOpencode = {\n configExists: boolean;\n configPath: string;\n agentsDir: string;\n agentFiles: string[];\n dashboardConnected: boolean;\n};\n\nexport type DoctorDisk = {\n path: string;\n exists: boolean;\n sizeBytes: number;\n};\n\nexport type DoctorSnapshot = {\n timestamp: string;\n bizarVersion: string;\n nodeVersion: string;\n platform: string;\n arch: string;\n uptime: number;\n memory: NodeJS.MemoryUsage;\n disk: DoctorDisk;\n services: DoctorServices;\n counts: DoctorCounts;\n recentErrors: DoctorRecentError[];\n configHealth: DoctorCheck[];\n opencode: DoctorOpencode;\n checks: DoctorChecks;\n health: { status: DoctorStatus; issues: DoctorCheck[] };\n};\n\n/**\n * The categories the Doctor page renders. Order is the render order.\n * Each maps 1:1 to either a `DoctorChecks` group or a derived slice.\n */\nexport type DoctorCategory =\n | 'system'\n | 'services'\n | 'counts'\n | 'errors'\n | 'actions';\n\nexport type DoctorCheckOrInfo =\n | DoctorCheck\n | { kind: 'count'; label: string; value: number | string };\n\nexport type DoctorPanelProps = {\n category: DoctorCategory;\n /** Title shown in the card header. Optional — derived from category if omitted. */\n title?: string;\n /** Lucide icon for the header. Optional — derived from category if omitted. */\n icon?: React.ComponentType<{ size?: number; 'aria-hidden'?: boolean }>;\n /** Per-line entries. Each carries an implicit `ok | warn | fail` status pill. */\n checks: DoctorCheck[];\n /**\n * Plain key/value rows that aren't checks (e.g. counts). Rendered\n * as a compact two-column list below the check rows when present.\n */\n info?: Array<{ label: string; value: string | number }>;\n /** Meta line shown beneath the title. */\n meta?: string;\n /** Optional custom body — bypasses the default check-list render. */\n children?: React.ReactNode;\n className?: string;\n};\n\nexport type SearchResult = {\n type: string;\n score: number;\n item: Record<string, unknown>;\n};\n\n/**\n * One background-agent instance. Mirrors the JSON shape returned by\n * GET /api/background (src/server/routes/background.mjs). Status values:\n * - 'pending' — accepted by the runner, dispatch not yet started\n * - 'running' — actively executing in a tmux session\n * - 'done' — finished with success\n * - 'failed' — terminated with an error\n * - 'killed' — user-requested kill\n * - 'timed_out' — exceeded plugin stall/dispatch timeout\n */\nexport type BgInstance = {\n instanceId: string;\n status?: 'pending' | 'running' | 'done' | 'failed' | 'killed' | 'timed_out' | string;\n agent?: string;\n prompt?: string;\n startedAt?: number;\n completedAt?: number;\n currentStep?: string | null;\n progress?: number;\n toolCallCount?: number;\n tmuxSession?: string;\n tmuxActive?: boolean;\n sessionId?: string;\n error?: string;\n // Optional fields the activity timeline tracks — not required for this view.\n parentAgent?: string;\n parentInstanceId?: string;\n promptPreview?: string;\n resultPreview?: string;\n lastEventAt?: number;\n taskId?: string;\n};\n\nexport type BackgroundListResponse = {\n instances: BgInstance[];\n status: { dir: string; exists: boolean; count: number };\n};\n\nexport type BackgroundOutputResponse = {\n /** Captured stdout/stderr text (tail). */\n output?: string;\n /** True when the underlying log file is gone (e.g. after cleanup). */\n available?: boolean;\n /** Total captured bytes — useful for the modal header. */\n bytes?: number;\n /** Truncation flag set by the server when output was capped. */\n truncated?: boolean;\n};\n\nexport type BackgroundTmuxResponse = {\n /** Computed tmux session name (e.g. \"bg-r2u9q8\"). */\n session?: string;\n /** Local command the user can paste to attach. */\n attachCommand?: string;\n /** Whether the session exists in tmux right now. */\n exists?: boolean;\n /** Optional reason when exists === false. */\n reason?: string;\n};\n\nexport type Snapshot = {\n overview: Overview;\n agents: Agent[];\n artifacts: Artifact[];\n projects: ProjectRecord[];\n activeProject: ProjectRecord | null;\n config: ConfigResponse;\n settings: SettingsResponse;\n tasks: Task[];\n mods: Mod[];\n schedules: Schedule[];\n providers: Provider[];\n mcps: McpServer[];\n};\n\nexport type WsStatus = 'connecting' | 'connected' | 'disconnected';\n\nexport type WsMessage =\n | { type: 'snapshot'; ts: number; data: Snapshot }\n | { type: 'change'; event: string; path: string; ts: number }\n | { type: 'tasks:change'; task: Task }\n | { type: 'tasks:delete'; id: string }\n | { type: 'task:progress'; taskId: string; progress?: number; step?: string | null; agent?: string | null }\n | { type: 'settings:change'; settings: Settings }\n | { type: 'agents:change' }\n | { type: 'background:change'; action?: string; id?: string; sessionId?: string }\n | { type: 'background:cleanup'; deleted?: number }\n | { type: 'agent:status'; agent: Agent }\n | { type: 'agent:restarted'; agent: Agent }\n | { type: 'agent:stuck'; agents: { name: string }[] }\n | { type: 'artifact:change'; slug: string; deleted?: boolean }\n | { type: 'schedules:change' }\n | { type: 'project:change'; project?: ProjectRecord }\n | { type: 'chat:message'; message: ChatMessage }\n | { type: 'notification:new'; notification: Notification }\n | { type: 'notifications:change' }\n | { type: 'dialog:show'; dialog: DialogDescriptor }\n | { type: 'pong'; ts: number }\n | { type: 'ping' }\n | { type: 'refresh' };\n\n/** Resolve a theme to the actual key applied to <html data-theme=\"...\"> */\nexport function applyTheme(themeName: ThemeName | ThemeSettings): 'dark' | 'light' {\n const mode = typeof themeName === 'string' ? themeName : themeName.mode;\n const resolved: 'dark' | 'light' =\n mode === 'system'\n ? typeof window !== 'undefined' &&\n window.matchMedia('(prefers-color-scheme: light)').matches\n ? 'light'\n : 'dark'\n : mode === 'light'\n ? 'light'\n : 'dark';\n if (typeof document !== 'undefined') {\n if (resolved === 'light') {\n document.documentElement.setAttribute('data-theme', 'light');\n } else {\n document.documentElement.removeAttribute('data-theme');\n }\n }\n return resolved;\n}\n\n/** Apply the full theme object — sets CSS variables. */\nexport function applyThemeTokens(theme: ThemeSettings) {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n root.style.setProperty('--accent', theme.accent);\n root.style.setProperty('--success', theme.success);\n root.style.setProperty('--warning', theme.warning);\n root.style.setProperty('--error', theme.error);\n root.style.setProperty('--info', theme.info);\n // Derive accent-bg/border lightly\n root.style.setProperty('--accent-bg', hexToRgba(theme.accent, 0.12));\n root.style.setProperty('--accent-border', hexToRgba(theme.accent, 0.4));\n if (theme.fontFamily) {\n root.style.setProperty('--font-sans', `'${theme.fontFamily}', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif`);\n }\n if (theme.fontSize) {\n root.style.setProperty('--base-font-size', `${theme.fontSize}px`);\n }\n root.dataset.compactMode = theme.compactMode ? 'true' : 'false';\n root.dataset.animations = theme.animations ? 'true' : 'false';\n}\n\nfunction hexToRgba(hex: string, alpha: number) {\n const m = /^#?([0-9a-f]{6})$/i.exec(hex || '');\n if (!m) return `rgba(139, 92, 246, ${alpha})`;\n const n = parseInt(m[1], 16);\n const r = (n >> 16) & 255;\n const g = (n >> 8) & 255;\n const b = n & 255;\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","// src/components/Spinner.tsx — loading indicator.\n\nimport { cn } from '../lib/utils';\n\nexport type SpinnerProps = {\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n label?: string;\n};\n\nexport function Spinner({ size = 'md', className, label }: SpinnerProps) {\n return (\n <span\n className={cn('spinner', `spinner-${size}`, className)}\n role=\"status\"\n aria-label={label || 'Loading'}\n />\n );\n}\n","// src/web/components/MobileHeader.tsx — sticky top bar with menu, title, and actions.\nimport { Menu, Search, X } from 'lucide-react';\nimport { cn } from '../lib/utils';\n\ntype Props = {\n onMenuClick: () => void;\n scrolled: boolean;\n activeTab: string;\n onSettings?: (() => void) | null;\n onSearch?: () => void;\n};\n\nexport function MobileHeader({ onMenuClick, scrolled, activeTab, onSettings, onSearch }: Props) {\n return (\n <header className={cn('mobile-header', scrolled && 'is-scrolled')}>\n <button\n onClick={onMenuClick}\n className=\"mobile-menu-btn\"\n aria-label=\"Open menu\"\n type=\"button\"\n >\n <Menu size={20} />\n </button>\n\n <h1 className=\"mobile-header-title\">\n {activeTab === 'settings' ? 'Settings' : 'Bizar'}\n </h1>\n\n {onSettings ? (\n <button\n onClick={onSettings}\n className=\"mobile-settings-exit\"\n aria-label=\"Exit settings\"\n type=\"button\"\n >\n <X size={20} />\n </button>\n ) : (\n <button\n onClick={onSearch}\n className=\"mobile-search-btn\"\n aria-label=\"Search\"\n type=\"button\"\n >\n <Search size={20} />\n </button>\n )}\n </header>\n );\n}\n","// src/web/components/MobileDrawer.tsx — slide-in drawer for secondary navigation.\nimport { X } from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { cn } from '../lib/utils';\n\nexport type DrawerTab = {\n id: string;\n label: string;\n icon: LucideIcon;\n};\n\ntype Props = {\n open: boolean;\n onClose: () => void;\n tabs: DrawerTab[];\n activeTab: string;\n onTabChange: (id: string) => void;\n};\n\nexport function MobileDrawer({ open, onClose, tabs, activeTab, onTabChange }: Props) {\n return (\n <>\n {open && (\n <div\n className=\"mobile-drawer-overlay\"\n onClick={onClose}\n aria-hidden=\"true\"\n />\n )}\n <aside\n className={cn('mobile-drawer', open && 'is-open')}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"All views\"\n >\n <div className=\"mobile-drawer-head\">\n <h2>All Views</h2>\n <button\n onClick={onClose}\n aria-label=\"Close menu\"\n type=\"button\"\n className=\"mobile-drawer-close\"\n >\n <X size={20} />\n </button>\n </div>\n <div className=\"mobile-drawer-grid\">\n {tabs.map((t) => {\n const Icon = t.icon;\n return (\n <button\n key={t.id}\n role=\"tab\"\n aria-selected={activeTab === t.id}\n className={cn('mobile-drawer-item', activeTab === t.id && 'is-active')}\n onClick={() => onTabChange(t.id)}\n type=\"button\"\n >\n <Icon size={22} />\n <span>{t.label}</span>\n </button>\n );\n })}\n </div>\n </aside>\n </>\n );\n}\n","// src/web/components/MobileBottomNav.tsx — iOS/Android-style bottom tab bar.\nimport type { LucideIcon } from 'lucide-react';\nimport { cn } from '../lib/utils';\n\nexport type MobileTab = {\n id: string;\n label: string;\n icon: LucideIcon;\n};\n\ntype Props = {\n tabs: MobileTab[];\n activeTab: string;\n onTabChange: (id: string) => void;\n};\n\nexport function MobileBottomNav({ tabs, activeTab, onTabChange }: Props) {\n return (\n <nav className=\"mobile-bottom-nav\" role=\"tablist\" aria-label=\"Primary navigation\">\n {tabs.map((t) => {\n const Icon = t.icon;\n return (\n <button\n key={t.id}\n role=\"tab\"\n aria-selected={activeTab === t.id}\n className={cn('mobile-nav-item', activeTab === t.id && 'is-active')}\n onClick={() => onTabChange(t.id)}\n type=\"button\"\n >\n <Icon size={20} />\n <span className=\"mobile-nav-label\">{t.label}</span>\n </button>\n );\n })}\n </nav>\n );\n}\n","// src/web/components/MobileLayout.tsx — main mobile shell: header + drawer + content + bottom nav.\nimport { useState } from 'react';\nimport { MobileHeader } from './MobileHeader';\nimport { MobileDrawer, type DrawerTab } from './MobileDrawer';\nimport { MobileBottomNav, type MobileTab } from './MobileBottomNav';\n\n// Re-export DrawerTab so consumers only need to import from MobileLayout.\nexport { type DrawerTab } from './MobileDrawer';\n\ntype Props = {\n children: React.ReactNode;\n activeTab: string;\n onTabChange: (id: string) => void;\n tabs: MobileTab[];\n fullTabs: DrawerTab[];\n onExitSettings?: () => void;\n onSearch?: () => void;\n};\n\nexport function MobileLayout({\n children,\n activeTab,\n onTabChange,\n tabs,\n fullTabs,\n onExitSettings,\n onSearch,\n}: Props) {\n const [drawerOpen, setDrawerOpen] = useState(false);\n const [scrolled, setScrolled] = useState(false);\n\n return (\n <div className=\"mobile-layout\">\n <MobileHeader\n onMenuClick={() => setDrawerOpen(true)}\n scrolled={scrolled}\n activeTab={activeTab}\n onSettings={activeTab === 'settings' ? onExitSettings : null}\n onSearch={onSearch}\n />\n\n <MobileDrawer\n open={drawerOpen}\n onClose={() => setDrawerOpen(false)}\n tabs={fullTabs}\n activeTab={activeTab}\n onTabChange={(id) => {\n onTabChange(id);\n setDrawerOpen(false);\n }}\n />\n\n <main\n className=\"mobile-content\"\n onScroll={(e) => setScrolled(e.currentTarget.scrollTop > 10)}\n >\n {children}\n </main>\n\n <MobileBottomNav tabs={tabs} activeTab={activeTab} onTabChange={onTabChange} />\n </div>\n );\n}\n","// src/mobile/components/MobileBottomSheet.tsx — slide-up bottom sheet for mobile.\nimport { useEffect, useId, useRef, type PointerEvent as ReactPointerEvent } from 'react';\nimport { X } from 'lucide-react';\n\ntype Props = {\n open: boolean;\n onClose: () => void;\n title?: string;\n children: React.ReactNode;\n actions?: React.ReactNode;\n maxHeight?: string;\n};\n\nexport function MobileBottomSheet({ open, onClose, title, children, actions, maxHeight = '85vh' }: Props) {\n const sheetRef = useRef<HTMLDivElement>(null);\n const closeButtonRef = useRef<HTMLButtonElement>(null);\n const titleId = useId();\n const lastActiveRef = useRef<HTMLElement | null>(null);\n const dragStartYRef = useRef<number | null>(null);\n const dragDeltaRef = useRef(0);\n const scrollLockYRef = useRef(0);\n\n const getFocusable = () => {\n return sheetRef.current?.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n ) ?? [];\n };\n\n // Close on ESC\n useEffect(() => {\n if (!open) return;\n lastActiveRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n if (e.key !== 'Tab') return;\n\n const focusable = Array.from(getFocusable());\n if (focusable.length === 0) {\n e.preventDefault();\n sheetRef.current?.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n\n if (e.shiftKey) {\n if (!active || active === first || active === sheetRef.current) {\n e.preventDefault();\n last?.focus();\n }\n return;\n }\n\n if (active === last) {\n e.preventDefault();\n first?.focus();\n }\n };\n\n window.addEventListener('keydown', onKey);\n requestAnimationFrame(() => {\n closeButtonRef.current?.focus() ?? sheetRef.current?.focus();\n });\n\n return () => {\n window.removeEventListener('keydown', onKey);\n lastActiveRef.current?.focus();\n };\n }, [open, onClose]);\n\n // Prevent body scroll when open\n useEffect(() => {\n if (open) {\n scrollLockYRef.current = window.scrollY;\n document.documentElement.style.overflow = 'hidden';\n document.body.style.overflow = 'hidden';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${scrollLockYRef.current}px`;\n document.body.style.left = '0';\n document.body.style.right = '0';\n document.body.style.width = '100%';\n } else {\n document.documentElement.style.overflow = '';\n document.body.style.overflow = '';\n document.body.style.position = '';\n document.body.style.top = '';\n document.body.style.left = '';\n document.body.style.right = '';\n document.body.style.width = '';\n }\n return () => {\n document.documentElement.style.overflow = '';\n document.body.style.overflow = '';\n document.body.style.position = '';\n document.body.style.top = '';\n document.body.style.left = '';\n document.body.style.right = '';\n document.body.style.width = '';\n window.scrollTo(0, scrollLockYRef.current);\n };\n }, [open]);\n\n if (!open) return null;\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (!e.isPrimary) return;\n dragStartYRef.current = e.clientY;\n dragDeltaRef.current = 0;\n sheetRef.current?.setPointerCapture(e.pointerId);\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (dragStartYRef.current == null || !sheetRef.current) return;\n const delta = Math.max(0, e.clientY - dragStartYRef.current);\n dragDeltaRef.current = delta;\n sheetRef.current.style.transform = `translateY(${delta}px)`;\n };\n\n const endDrag = (e?: ReactPointerEvent<HTMLDivElement>) => {\n if (!sheetRef.current) return;\n if (e && sheetRef.current.hasPointerCapture(e.pointerId)) {\n sheetRef.current.releasePointerCapture(e.pointerId);\n }\n const delta = dragDeltaRef.current;\n sheetRef.current.style.transform = '';\n dragStartYRef.current = null;\n dragDeltaRef.current = 0;\n if (delta > 72) onClose();\n };\n\n return (\n <div className=\"mobile-sheet-overlay\" onClick={onClose}>\n <div\n ref={sheetRef}\n className=\"mobile-sheet\"\n style={{ maxHeight }}\n onClick={(e) => e.stopPropagation()}\n role=\"dialog\"\n aria-modal\n aria-label={title ? undefined : 'Sheet'}\n aria-labelledby={title ? titleId : undefined}\n tabIndex={-1}\n >\n {/* Drag handle */}\n <div\n className=\"mobile-sheet-handle\"\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n aria-hidden=\"true\"\n />\n\n <div className=\"mobile-sheet-header\">\n {title ? <h3 id={titleId} className=\"mobile-sheet-title\">{title}</h3> : <div />}\n <button\n ref={closeButtonRef}\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <X size={18} />\n </button>\n </div>\n\n <div className=\"mobile-sheet-content\">\n {children}\n </div>\n\n {actions && (\n <div className=\"mobile-sheet-actions\">\n {actions}\n </div>\n )}\n </div>\n </div>\n );\n}\n","// src/mobile/views/MobileActivity.tsx — enhanced activity with filters, event detail, and snapshot submit hero.\nimport { useEffect, useState } from 'react';\nimport { RefreshCw, Bot, Activity as ActivityIcon, Pause, Play, Send } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport { formatRelative } from '../../lib/utils';\nimport type { ActivityItem, Snapshot } from '../../lib/types';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\n\ntype Props = {\n snapshot: Snapshot;\n onRefresh: () => Promise<void>;\n};\n\nconst EVENT_KINDS = ['all', 'tasks', 'agents', 'bg', 'artifacts', 'mods'] as const;\ntype EventKind = typeof EVENT_KINDS[number];\n\nconst KIND_LABELS: Record<string, string> = {\n all: 'All',\n tasks: 'Tasks',\n agents: 'Agents',\n bg: 'Background',\n artifacts: 'Plans',\n mods: 'Mods',\n};\n\nexport function MobileActivity({ snapshot, onRefresh }: Props) {\n const [events, setEvents] = useState<ActivityItem[]>(snapshot.overview?.recentActivity || []);\n const [loading, setLoading] = useState(true);\n const [filter, setFilter] = useState<EventKind>('all');\n const [paused, setPaused] = useState(false);\n const [detail, setDetail] = useState<ActivityItem | null>(null);\n const [submitText, setSubmitText] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n useEffect(() => {\n if (snapshot.overview?.recentActivity) {\n setEvents(snapshot.overview.recentActivity);\n setLoading(false);\n } else {\n loadEvents();\n }\n }, [snapshot.overview]);\n\n const loadEvents = async () => {\n try {\n const data = await api.get<Snapshot>('/snapshot');\n setEvents(data.overview?.recentActivity || []);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n const filtered = filter === 'all' ? events : events.filter((e) => {\n if (filter === 'tasks') return e.kind?.startsWith('task');\n if (filter === 'agents') return e.kind?.startsWith('agent');\n if (filter === 'bg') return e.kind?.includes('bg') || e.kind?.includes('schedule');\n if (filter === 'artifacts') return e.kind?.includes('artifacts');\n if (filter === 'mods') return e.kind?.includes('mod');\n return true;\n });\n\n const agents = snapshot.agents || [];\n const tasks = snapshot.tasks || [];\n const activeTasks = tasks.filter((t) => t.status === 'doing' || t.status === 'queued');\n const doneTasks = tasks.filter((t) => t.status === 'done');\n\n const handleSubmit = async () => {\n if (!submitText.trim() || submitting) return;\n setSubmitting(true);\n try {\n await api.post('/chat', { message: submitText.trim(), agent: 'odin' });\n setSubmitText('');\n await onRefresh().catch(() => undefined);\n await loadEvents();\n } catch {\n // best-effort\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleRefresh = async () => {\n await onRefresh().catch(() => undefined);\n await loadEvents();\n };\n\n return (\n <div className=\"mobile-view\">\n {/* Snapshot Submit Hero */}\n <div className=\"mobile-submit-hero\">\n <textarea\n className=\"mobile-submit-input\"\n placeholder=\"What needs to be done? Describe a task, bug, or refactor for Odin to artifacts…\"\n value={submitText}\n onChange={(e) => setSubmitText(e.target.value)}\n rows={2}\n />\n <div className=\"mobile-submit-actions\">\n <div className=\"mobile-submit-chips\">\n {['Implement feature', 'Fix bug', 'Refactor', 'Investigate', 'Write tests'].map((chip) => (\n <button\n key={chip}\n type=\"button\"\n className=\"mobile-submit-chip\"\n onClick={() => setSubmitText((cur) => cur ? `${cur} ${chip}` : chip)}\n >\n {chip}\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n className=\"mobile-btn\"\n disabled={!submitText.trim() || submitting}\n onClick={handleSubmit}\n >\n <Send size={14} /> Submit to Odin\n </button>\n </div>\n </div>\n\n {/* Quick stats */}\n <div className=\"mobile-stats\">\n <div className=\"mobile-stat\">\n <div className=\"mobile-stat-value\">{agents.length}</div>\n <div className=\"mobile-stat-label\">Agents</div>\n </div>\n <div className=\"mobile-stat\">\n <div className=\"mobile-stat-value\">{activeTasks.length}</div>\n <div className=\"mobile-stat-label\">Active</div>\n </div>\n <div className=\"mobile-stat\">\n <div className=\"mobile-stat-value\">{doneTasks.length}</div>\n <div className=\"mobile-stat-label\">Done</div>\n </div>\n <div className=\"mobile-stat\">\n <div className=\"mobile-stat-value\">{snapshot.artifacts.length || 0}</div>\n <div className=\"mobile-stat-label\">Plans</div>\n </div>\n </div>\n\n {loading && events.length === 0 && (\n <div className=\"mobile-loading mobile-loading-inline\">\n <p>Loading activity…</p>\n </div>\n )}\n\n {/* Filter chips */}\n <div className=\"mobile-activity-header\">\n <h3 className=\"mobile-section-title\" style={{ margin: 0 }}>Recent Activity</h3>\n <div style={{ display: 'flex', gap: 4 }}>\n <button\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={() => setPaused((v) => !v)}\n aria-label={paused ? 'Resume' : 'Pause'}\n >\n {paused ? <Play size={14} /> : <Pause size={14} />}\n </button>\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={handleRefresh} aria-label=\"Refresh\">\n <RefreshCw size={14} />\n </button>\n </div>\n </div>\n\n <div className=\"mobile-search-scopes\">\n {EVENT_KINDS.map((k) => (\n <button\n key={k}\n type=\"button\"\n className={`mobile-scope-chip ${filter === k ? 'active' : ''}`}\n onClick={() => setFilter(k)}\n >\n {KIND_LABELS[k]}\n </button>\n ))}\n </div>\n\n {/* Active agents */}\n {agents.length > 0 && (\n <section className=\"mobile-section\">\n <h3 className=\"mobile-section-title\"><Bot size={14} /> Agents</h3>\n <div className=\"mobile-card-list\">\n {agents.slice(0, 6).map((a) => (\n <div key={a.name} className=\"mobile-agent-card\">\n <div className=\"mobile-agent-dot\" data-status={a.status || 'idle'} />\n <div className=\"mobile-agent-info\">\n <span className=\"mobile-agent-name\">{a.name}</span>\n <span className=\"mobile-agent-meta\">{a.status || 'idle'}</span>\n </div>\n </div>\n ))}\n </div>\n </section>\n )}\n\n {/* Event feed */}\n {!paused && filtered.length > 0 && (\n <section className=\"mobile-section\">\n <div className=\"mobile-card-list\">\n {filtered.slice(0, 30).map((e, i) => (\n <button\n key={`${e.ts}-${e.kind}-${i}`}\n type=\"button\"\n className=\"mobile-event-item\"\n onClick={() => setDetail(e)}\n >\n <span className=\"mobile-event-kind\" data-kind={e.kind}>{KIND_LABELS[e.kind as keyof typeof KIND_LABELS] || e.kind}</span>\n <span className=\"mobile-event-msg\">\n {renderEventMessage(e)}\n </span>\n <span className=\"mobile-event-time\">{formatRelative(e.ts)}</span>\n </button>\n ))}\n </div>\n </section>\n )}\n\n {!paused && filtered.length === 0 && events.length > 0 && (\n <div className=\"mobile-empty\">\n <ActivityIcon size={40} />\n <p>No matching activity.</p>\n <p className=\"muted\">Try a different filter.</p>\n </div>\n )}\n\n {/* Event detail sheet */}\n {detail && (\n <MobileBottomSheet\n open={true}\n onClose={() => setDetail(null)}\n title={KIND_LABELS[detail.kind as keyof typeof KIND_LABELS] || detail.kind}\n >\n <div className=\"mobile-agent-detail-meta\">\n <div className=\"mobile-task-detail-row\"><span>Kind</span><span>{detail.kind}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Time</span><span>{new Date(detail.ts).toLocaleString()}</span></div>\n {Object.entries(detail)\n .filter(([k]) => !['ts', 'kind'].includes(k))\n .map(([k, v]) => (\n <div key={k} className=\"mobile-task-detail-row\">\n <span>{k}</span>\n <span style={{ fontSize: 12, maxWidth: 200, wordBreak: 'break-all' }}>\n {typeof v === 'object' ? JSON.stringify(v) : String(v)}\n </span>\n </div>\n ))}\n </div>\n </MobileBottomSheet>\n )}\n\n {/* Empty state */}\n {agents.length === 0 && activeTasks.length === 0 && (\n <div className=\"mobile-empty\">\n <ActivityIcon size={40} />\n <p>No activity yet.</p>\n <p className=\"muted\">Start a task or chat to see things here.</p>\n </div>\n )}\n </div>\n );\n}\n\nfunction renderEventMessage(e: ActivityItem): string {\n const k = e.kind || '';\n if (k.includes('task')) return `Task: ${e.title || e.id || 'updated'}`;\n if (k.includes('agent')) return `Agent: ${e.name || 'status changed'}`;\n if (k.includes('artifacts')) return `Plan: ${e.slug || 'changed'}`;\n if (k.includes('mod')) return `Mod: ${e.name || 'changed'}`;\n if (k.includes('schedule')) return `Schedule: ${e.name || 'triggered'}`;\n return JSON.stringify(e).slice(0, 80);\n}\n","// src/mobile/components/MobileModal.tsx — full-screen mobile modal.\nimport { useEffect, useId, useRef } from 'react';\nimport { X } from 'lucide-react';\n\ntype Props = {\n open: boolean;\n onClose: () => void;\n title?: string;\n children: React.ReactNode;\n actions?: React.ReactNode;\n};\n\nexport function MobileModal({ open, onClose, title, children, actions }: Props) {\n const modalRef = useRef<HTMLDivElement>(null);\n const closeButtonRef = useRef<HTMLButtonElement>(null);\n const lastActiveRef = useRef<HTMLElement | null>(null);\n const titleId = useId();\n const scrollLockYRef = useRef(0);\n\n useEffect(() => {\n if (!open) return;\n\n lastActiveRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n\n const focusableSelector = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n if (e.key !== 'Tab') return;\n\n const focusable = Array.from(modalRef.current?.querySelectorAll<HTMLElement>(focusableSelector) ?? []);\n if (focusable.length === 0) {\n e.preventDefault();\n modalRef.current?.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n\n if (e.shiftKey) {\n if (!active || active === first || active === modalRef.current) {\n e.preventDefault();\n last?.focus();\n }\n return;\n }\n\n if (active === last) {\n e.preventDefault();\n first?.focus();\n }\n };\n\n window.addEventListener('keydown', onKey);\n scrollLockYRef.current = window.scrollY;\n document.documentElement.style.overflow = 'hidden';\n document.body.style.overflow = 'hidden';\n document.body.style.position = 'fixed';\n document.body.style.top = `-${scrollLockYRef.current}px`;\n document.body.style.left = '0';\n document.body.style.right = '0';\n document.body.style.width = '100%';\n requestAnimationFrame(() => {\n closeButtonRef.current?.focus() ?? modalRef.current?.focus();\n });\n return () => {\n window.removeEventListener('keydown', onKey);\n document.documentElement.style.overflow = '';\n document.body.style.overflow = '';\n document.body.style.position = '';\n document.body.style.top = '';\n document.body.style.left = '';\n document.body.style.right = '';\n document.body.style.width = '';\n window.scrollTo(0, scrollLockYRef.current);\n lastActiveRef.current?.focus();\n };\n }, [open, onClose]);\n\n if (!open) return null;\n\n return (\n <div\n className=\"mobile-modal-overlay\"\n onClick={(e) => {\n if (e.target === e.currentTarget) onClose();\n }}\n >\n <div\n ref={modalRef}\n className=\"mobile-modal\"\n onClick={(e) => e.stopPropagation()}\n role=\"dialog\"\n aria-modal\n aria-label={title ? undefined : 'Modal'}\n aria-labelledby={title ? titleId : undefined}\n tabIndex={-1}\n >\n <div className=\"mobile-modal-header\">\n {title ? <h2 id={titleId} className=\"mobile-modal-title\">{title}</h2> : <div />}\n <button\n ref={closeButtonRef}\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <X size={20} />\n </button>\n </div>\n <div className=\"mobile-modal-content\">\n {children}\n </div>\n {actions && (\n <div className=\"mobile-modal-actions\">\n {actions}\n </div>\n )}\n </div>\n </div>\n );\n}\n","// src/mobile/views/MobileAgents.tsx — agents list with detail bottom sheet.\nimport { useEffect, useState } from 'react';\nimport { Bot, Plus, RefreshCw, Play, Trash2, RotateCw } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport { formatRelative } from '../../lib/utils';\nimport type { Agent, Snapshot } from '../../lib/types';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\nimport { MobileModal } from '../components/MobileModal';\n\ntype Props = {\n snapshot: Snapshot;\n onBack: () => void;\n onOpenAgent: (name: string) => void;\n selectedAgent?: string;\n onRefresh?: () => Promise<void>;\n};\n\nconst CATEGORIES = ['reasoning', 'code', 'design', 'planning', 'gitops', 'analysis'];\n\nconst CATEGORY_COLORS: Record<string, string> = {\n reasoning: 'var(--accent)',\n code: 'var(--info)',\n design: 'var(--success)',\n planning: 'var(--warning)',\n gitops: 'var(--error)',\n analysis: 'var(--text-dim)',\n};\n\nexport function MobileAgents({ snapshot, onBack, onOpenAgent, selectedAgent, onRefresh }: Props) {\n const [agents, setAgents] = useState<Agent[]>(snapshot.agents || []);\n const [loading, setLoading] = useState(!snapshot.agents);\n const [categoryFilter, setCategoryFilter] = useState('');\n const [search, setSearch] = useState('');\n const [detailAgent, setDetailAgent] = useState<Agent | null>(null);\n const [newOpen, setNewOpen] = useState(false);\n const [invokePrompt, setInvokePrompt] = useState('');\n const [invokeOpen, setInvokeOpen] = useState(false);\n\n useEffect(() => {\n if (selectedAgent) {\n const a = agents.find((ag) => ag.name === selectedAgent);\n if (a) setDetailAgent(a);\n }\n }, [selectedAgent, agents]);\n\n const reload = async () => {\n try {\n const data = await api.get<{ agents: Agent[] }>('/agents');\n setAgents(data.agents || []);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n const filtered = agents.filter((a) => {\n if (categoryFilter && a.category !== categoryFilter) return false;\n if (search) {\n const q = search.toLowerCase();\n if (!a.name.toLowerCase().includes(q) && !(a.description || '').toLowerCase().includes(q)) return false;\n }\n return true;\n });\n\n const invokeAgent = async (name: string, prompt: string) => {\n try {\n await api.post(`/agents/${encodeURIComponent(name)}/invoke`, { prompt });\n setInvokeOpen(false);\n setInvokePrompt('');\n } catch {\n // best-effort\n }\n };\n\n const restartAgent = async (name: string) => {\n try {\n await api.post(`/agents/${encodeURIComponent(name)}/restart`);\n await reload();\n } catch {\n // best-effort\n }\n };\n\n const deleteAgent = async (name: string) => {\n if (!confirm(`Delete agent \"${name}\"?`)) return;\n try {\n await api.del(`/agents/${encodeURIComponent(name)}`);\n setAgents((cur) => cur.filter((a) => a.name !== name));\n setDetailAgent(null);\n onRefresh?.();\n } catch {\n // best-effort\n }\n };\n\n const createAgent = async (data: {\n name: string; description: string; model: string; mode: string;\n color: string; tools: string[]; tags: string[]; category: string; prompt: string;\n }) => {\n try {\n const created = await api.post<Agent>('/agents', data);\n setAgents((cur) => [...cur, created]);\n setNewOpen(false);\n onRefresh?.();\n } catch {\n // best-effort\n }\n };\n\n return (\n <div className=\"mobile-view\">\n {/* Toolbar */}\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search agents…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n style={{ flex: 1 }}\n />\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => reload()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => setNewOpen(true)} aria-label=\"New agent\">\n <Plus size={16} />\n </button>\n </div>\n\n {/* Category filter */}\n <div className=\"mobile-search-scopes\">\n <button\n type=\"button\"\n className={`mobile-scope-chip ${!categoryFilter ? 'active' : ''}`}\n onClick={() => setCategoryFilter('')}\n >\n All\n </button>\n {CATEGORIES.map((c) => (\n <button\n key={c}\n type=\"button\"\n className={`mobile-scope-chip ${categoryFilter === c ? 'active' : ''}`}\n onClick={() => setCategoryFilter(c)}\n >\n {c}\n </button>\n ))}\n </div>\n\n {loading ? (\n <div className=\"mobile-loading\"><p>Loading…</p></div>\n ) : filtered.length === 0 ? (\n <div className=\"mobile-empty\">\n <Bot size={40} />\n <p>No agents found.</p>\n </div>\n ) : (\n <div className=\"mobile-card-list\">\n {filtered.map((a) => (\n <button\n key={a.name}\n type=\"button\"\n className=\"mobile-list-item mobile-list-item-interactive\"\n onClick={() => { setDetailAgent(a); onOpenAgent(a.name); }}\n >\n <div className=\"mobile-agent-dot\" data-status={a.status || 'idle'} />\n <div className=\"mobile-list-content\">\n <span className=\"mobile-list-title\">{a.name}</span>\n <span className=\"mobile-list-meta\">{a.model || a.mode || '—'}</span>\n </div>\n {a.category && (\n <span\n className=\"mobile-list-badge\"\n style={{ color: CATEGORY_COLORS[a.category] || 'var(--text-dim)' }}\n >\n {a.category}\n </span>\n )}\n <span className=\"mobile-list-badge\" data-status={a.status || 'idle'}>\n {a.status || 'idle'}\n </span>\n </button>\n ))}\n </div>\n )}\n\n {/* Agent detail sheet */}\n {detailAgent && (\n <MobileBottomSheet\n open={true}\n onClose={() => setDetailAgent(null)}\n title={detailAgent.name}\n actions={\n <div className=\"mobile-task-detail-actions\">\n <button\n type=\"button\"\n className=\"mobile-btn\"\n onClick={() => { setInvokePrompt(''); setInvokeOpen(true); }}\n >\n <Play size={14} /> Invoke\n </button>\n {(detailAgent.isStuck || detailAgent.status === 'error' || detailAgent.status === 'working') && (\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-secondary\"\n onClick={() => restartAgent(detailAgent.name)}\n >\n <RotateCw size={14} /> Restart\n </button>\n )}\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-danger\"\n onClick={() => deleteAgent(detailAgent.name)}\n >\n <Trash2 size={14} />\n </button>\n </div>\n }\n >\n <div className=\"mobile-agent-detail\">\n <div className=\"mobile-agent-detail-status\">\n <span className=\"mobile-agent-dot\" data-status={detailAgent.status || 'idle'} />\n <span className=\"mobile-agent-detail-status-text\">{detailAgent.status || 'idle'}</span>\n {detailAgent.category && (\n <span\n className=\"mobile-list-badge\"\n style={{ color: CATEGORY_COLORS[detailAgent.category] }}\n >\n {detailAgent.category}\n </span>\n )}\n </div>\n <p className=\"mobile-agent-detail-desc\">{detailAgent.description || 'No description.'}</p>\n <div className=\"mobile-agent-detail-meta\">\n <div className=\"mobile-task-detail-row\">\n <span>Model</span>\n <span className=\"mono\">{detailAgent.model || '—'}</span>\n </div>\n <div className=\"mobile-task-detail-row\">\n <span>Mode</span>\n <span>{detailAgent.mode || 'subagent'}</span>\n </div>\n <div className=\"mobile-task-detail-row\">\n <span>Path</span>\n <span className=\"mono\" style={{ fontSize: 11 }}>{detailAgent.path}</span>\n </div>\n {detailAgent.lastError && (\n <div className=\"mobile-task-detail-row\" style={{ color: 'var(--error)' }}>\n <span>Last error</span>\n <span>{detailAgent.lastError.message}</span>\n </div>\n )}\n {detailAgent.tasksTotal != null && detailAgent.tasksTotal > 0 && (\n <div className=\"mobile-task-detail-row\">\n <span>Success rate</span>\n <span>{Math.round((detailAgent.successRate || 0) * 100)}%</span>\n </div>\n )}\n <div className=\"mobile-task-detail-row\">\n <span>Last modified</span>\n <span>{formatRelative(detailAgent.mtime)}</span>\n </div>\n </div>\n </div>\n </MobileBottomSheet>\n )}\n\n {/* Invoke modal */}\n <MobileModal open={invokeOpen} onClose={() => setInvokeOpen(false)} title={`Invoke ${detailAgent?.name}`} actions={\n <button\n type=\"button\"\n className=\"mobile-btn\"\n style={{ width: '100%' }}\n disabled={!invokePrompt.trim()}\n onClick={() => { if (detailAgent) invokeAgent(detailAgent.name, invokePrompt.trim()); }}\n >\n <Play size={14} /> Invoke\n </button>\n }>\n <p className=\"muted\" style={{ marginBottom: 12 }}>\n Model: <span className=\"mono\">{detailAgent?.model || '—'}</span>\n </p>\n <textarea\n className=\"mobile-input\"\n rows={5}\n placeholder=\"What should this agent do?\"\n value={invokePrompt}\n onChange={(e) => setInvokePrompt(e.target.value)}\n autoFocus\n />\n </MobileModal>\n\n {/* New agent modal */}\n <NewAgentModal\n open={newOpen}\n onClose={() => setNewOpen(false)}\n onCreate={createAgent}\n />\n </div>\n );\n}\n\nfunction NewAgentModal({ open, onClose, onCreate }: {\n open: boolean;\n onClose: () => void;\n onCreate: (data: {\n name: string; description: string; model: string; mode: string;\n color: string; tools: string[]; tags: string[]; category: string; prompt: string;\n }) => void;\n}) {\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [model, setModel] = useState('');\n const [mode, setMode] = useState('subagent');\n const [color, setColor] = useState('#8b5cf6');\n const [category, setCategory] = useState('');\n const [tags, setTags] = useState('');\n const [prompt, setPrompt] = useState('');\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) return;\n onCreate({\n name: name.trim(),\n description: description.trim(),\n model,\n mode,\n color,\n tools: [],\n tags: tags.split(',').map((t) => t.trim()).filter(Boolean),\n category,\n prompt,\n });\n setName('');\n setDescription('');\n setModel('');\n setMode('subagent');\n setColor('#8b5cf6');\n setCategory('');\n setTags('');\n setPrompt('');\n };\n\n return (\n <MobileModal open={open} onClose={onClose} title=\"New Agent\" actions={\n <button type=\"submit\" form=\"new-agent-form\" className=\"mobile-btn\" style={{ width: '100%' }}>\n <Plus size={14} /> Create\n </button>\n }>\n <form id=\"new-agent-form\" onSubmit={handleSubmit} className=\"mobile-task-form\">\n <label className=\"mobile-field-label\">Name *</label>\n <input className=\"mobile-input\" type=\"text\" placeholder=\"my-agent\" pattern=\"[a-z0-9][a-z0-9-]{0,63}\"\n value={name} onChange={(e) => setName(e.target.value)} autoFocus required />\n <label className=\"mobile-field-label\">Description</label>\n <input className=\"mobile-input\" type=\"text\" placeholder=\"What does this agent do?\"\n value={description} onChange={(e) => setDescription(e.target.value)} />\n <label className=\"mobile-field-label\">Model</label>\n <input className=\"mobile-input\" type=\"text\" placeholder=\"(provider default)\"\n value={model} onChange={(e) => setModel(e.target.value)} />\n <label className=\"mobile-field-label\">Category</label>\n <select className=\"mobile-input\" value={category} onChange={(e) => setCategory(e.target.value)}>\n <option value=\"\">None</option>\n {CATEGORIES.map((c) => <option key={c} value={c}>{c}</option>)}\n </select>\n <label className=\"mobile-field-label\">Tags (comma-separated)</label>\n <input className=\"mobile-input\" type=\"text\" placeholder=\"tag1, tag2\"\n value={tags} onChange={(e) => setTags(e.target.value)} />\n </form>\n </MobileModal>\n );\n}\n","// src/mobile/views/MobileConfig.tsx — config key-value editor with type-aware inputs.\nimport { useEffect, useState } from 'react';\nimport { Sliders, RefreshCw, RotateCcw, Save } from 'lucide-react';\nimport { api } from '../../lib/api';\n\ntype ConfigEntry = {\n key: string;\n value: unknown;\n type: 'boolean' | 'number' | 'string' | 'object' | 'array' | 'unknown';\n defaultValue?: unknown;\n};\n\ntype Props = {\n onBack: () => void;\n};\n\nexport function MobileConfig({ onBack }: Props) {\n const [entries, setEntries] = useState<ConfigEntry[]>([]);\n const [loading, setLoading] = useState(true);\n const [editing, setEditing] = useState<Record<string, unknown>>({});\n const [saving, setSaving] = useState(false);\n\n const load = async () => {\n try {\n const data = await api.get<Record<string, unknown>>('/config');\n const entries: ConfigEntry[] = Object.entries(data).map(([key, value]) => ({\n key,\n value,\n type: detectType(value),\n }));\n setEntries(entries);\n setEditing(Object.fromEntries(Object.entries(data).map(([k, v]) => [k, v])));\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => { load(); }, []);\n\n const detectType = (v: unknown): ConfigEntry['type'] => {\n if (typeof v === 'boolean') return 'boolean';\n if (typeof v === 'number') return 'number';\n if (typeof v === 'string') return 'string';\n if (Array.isArray(v)) return 'array';\n if (v && typeof v === 'object') return 'object';\n return 'unknown';\n };\n\n const handleChange = (key: string, value: unknown) => {\n setEditing((cur) => ({ ...cur, [key]: value }));\n };\n\n const saveAll = async () => {\n setSaving(true);\n try {\n await api.patch('/config', editing);\n await load();\n } catch {\n // best-effort\n } finally {\n setSaving(false);\n }\n };\n\n const resetRow = (key: string, defaultValue?: unknown) => {\n setEditing((cur) => ({ ...cur, [key]: defaultValue }));\n };\n\n const renderInput = (entry: ConfigEntry) => {\n const value = editing[entry.key];\n switch (entry.type) {\n case 'boolean':\n return (\n <label className=\"mobile-toggle\">\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => handleChange(entry.key, e.target.checked)}\n />\n <span className=\"mobile-toggle-slider\" />\n </label>\n );\n case 'number':\n return (\n <input\n className=\"mobile-input mobile-input-sm\"\n type=\"number\"\n inputMode=\"numeric\"\n value={String(value ?? '')}\n onChange={(e) => handleChange(entry.key, Number(e.target.value))}\n />\n );\n case 'string':\n return (\n <input\n className=\"mobile-input mobile-input-sm\"\n type=\"text\"\n value={String(value ?? '')}\n onChange={(e) => handleChange(entry.key, e.target.value)}\n />\n );\n case 'object':\n case 'array':\n return (\n <textarea\n className=\"mobile-input mobile-input-sm\"\n rows={2}\n value={JSON.stringify(value ?? null, null, 2)}\n onChange={(e) => {\n try {\n handleChange(entry.key, JSON.parse(e.target.value));\n } catch {\n // invalid JSON\n }\n }}\n />\n );\n default:\n return <span className=\"mobile-setting-value mono\">{String(value ?? 'null')}</span>;\n }\n };\n\n return (\n <div className=\"mobile-view\">\n <div className=\"mobile-tasks-toolbar\">\n <span style={{ fontSize: 14, color: 'var(--text-muted)', flex: 1 }}>{entries.length} config entries</span>\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => load()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n <button\n type=\"button\"\n className=\"mobile-btn\"\n disabled={saving}\n onClick={() => saveAll()}\n >\n <Save size={14} /> {saving ? 'Saving…' : 'Save all'}\n </button>\n </div>\n\n {loading ? (\n <div className=\"mobile-loading\"><p>Loading…</p></div>\n ) : entries.length === 0 ? (\n <div className=\"mobile-empty\">\n <Sliders size={40} />\n <p>No config entries.</p>\n </div>\n ) : (\n <div className=\"mobile-config-list\">\n {entries.map((entry) => (\n <div key={entry.key} className=\"mobile-config-row\">\n <div className=\"mobile-config-key\">\n <span className=\"mono\">{entry.key}</span>\n <span className=\"mobile-config-type\">{entry.type}</span>\n </div>\n <div className=\"mobile-config-value\">\n {renderInput(entry)}\n </div>\n <button\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={() => resetRow(entry.key, entry.defaultValue)}\n aria-label=\"Reset to default\"\n title=\"Reset\"\n >\n <RotateCcw size={14} />\n </button>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n","// src/mobile/views/MobileHistory.tsx — session history list.\nimport { useEffect, useState } from 'react';\nimport { History, RefreshCw } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\n\ntype HistoryEntry = {\n id: string;\n ts: string;\n duration?: number;\n agent?: string;\n taskCount?: number;\n summary?: string;\n output?: string;\n};\n\ntype HistoryEvent = {\n ts: string;\n kind: string;\n text?: string;\n projectId?: string;\n};\n\ntype ProjectHistory = {\n id: string;\n name: string;\n path: string;\n status: string;\n lastAccessed?: string | null;\n tasks: { total: number; done: number; doing: number; blocked: number; queued: number };\n plans: number;\n};\n\ntype HistoryResponse = {\n history?: HistoryEntry[];\n events?: HistoryEvent[];\n projects?: ProjectHistory[];\n};\n\ntype Props = {\n onBack: () => void;\n};\n\nexport function MobileHistory({ onBack }: Props) {\n const [entries, setEntries] = useState<HistoryEntry[]>([]);\n const [loading, setLoading] = useState(true);\n const [detail, setDetail] = useState<HistoryEntry | null>(null);\n const [query, setQuery] = useState('');\n\n const load = async () => {\n try {\n const data = await api.get<HistoryResponse>('/history');\n if (Array.isArray(data.history)) {\n setEntries(data.history);\n return;\n }\n\n if (Array.isArray(data.projects)) {\n const latestEventByProject = new Map<string, HistoryEvent>();\n for (const event of data.events || []) {\n const pid = event.projectId;\n if (!pid) continue;\n const current = latestEventByProject.get(pid);\n if (!current || new Date(event.ts).getTime() > new Date(current.ts).getTime()) {\n latestEventByProject.set(pid, event);\n }\n }\n\n setEntries(\n data.projects.map((project) => {\n const latestEvent = latestEventByProject.get(project.id);\n return {\n id: project.id,\n ts: project.lastAccessed || latestEvent?.ts || new Date(0).toISOString(),\n agent: project.name,\n taskCount: project.tasks.total,\n summary: `${project.path} · ${project.tasks.done}/${project.tasks.total} done · ${project.plans} plan${project.plans === 1 ? '' : 's'}`,\n output: latestEvent ? `${latestEvent.kind}${latestEvent.text ? ` — ${latestEvent.text}` : ''}` : 'No recent events.',\n } satisfies HistoryEntry;\n }),\n );\n return;\n }\n\n setEntries([]);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => { load(); }, []);\n\n const formatDuration = (ms?: number): string => {\n if (!ms) return '—';\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n const r = s % 60;\n return `${m}m ${r}s`;\n };\n\n const formatTime = (ts: string): string => {\n const d = new Date(ts);\n return d.toLocaleString();\n };\n\n const filtered = query.trim()\n ? entries.filter((entry) => {\n const q = query.toLowerCase();\n return (entry.agent || 'session').toLowerCase().includes(q)\n || (entry.summary || '').toLowerCase().includes(q)\n || (entry.output || '').toLowerCase().includes(q);\n })\n : entries;\n\n return (\n <div className=\"mobile-view\">\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search history…\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n style={{ flex: 1 }}\n />\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => load()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n </div>\n\n {loading ? (\n <div className=\"mobile-loading\"><p>Loading…</p></div>\n ) : filtered.length === 0 ? (\n <div className=\"mobile-empty\">\n <History size={40} />\n <p>{entries.length === 0 ? 'No history yet.' : 'No matching sessions.'}</p>\n </div>\n ) : (\n <div className=\"mobile-card-list\">\n {filtered.map((e) => (\n <button\n key={e.id}\n type=\"button\"\n className=\"mobile-list-item mobile-list-item-interactive\"\n onClick={() => setDetail(e)}\n >\n <div className=\"mobile-list-icon\"><History size={16} /></div>\n <div className=\"mobile-list-content\">\n <span className=\"mobile-list-title\">{e.agent || 'Session'}</span>\n <span className=\"mobile-list-meta\">\n {formatTime(e.ts)}\n {e.duration ? ` · ${formatDuration(e.duration)}` : ''}\n {e.taskCount ? ` · ${e.taskCount} tasks` : ''}\n </span>\n </div>\n </button>\n ))}\n </div>\n )}\n\n {/* Detail sheet */}\n {detail && (\n <MobileBottomSheet open={true} onClose={() => setDetail(null)} title=\"Session Detail\">\n <div className=\"mobile-agent-detail-meta\">\n <div className=\"mobile-task-detail-row\"><span>Agent</span><span>{detail.agent || '—'}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Started</span><span>{formatTime(detail.ts)}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Duration</span><span>{formatDuration(detail.duration)}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Tasks</span><span>{detail.taskCount || 0}</span></div>\n </div>\n {detail.summary && (\n <div style={{ marginTop: 12 }}>\n <h4 style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 4 }}>Summary</h4>\n <p style={{ fontSize: 13 }}>{detail.summary}</p>\n </div>\n )}\n {detail.output && (\n <div style={{ marginTop: 12 }}>\n <h4 style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 4 }}>Output</h4>\n <pre className=\"mono\" style={{ fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-all', background: 'var(--bg-elev-2)', padding: 8, borderRadius: 6 }}>\n {detail.output.slice(0, 1000)}{detail.output.length > 1000 ? '…' : ''}\n </pre>\n </div>\n )}\n </MobileBottomSheet>\n )}\n </div>\n );\n}\n","// src/mobile/views/MobileMods.tsx — mods list with detail sheet.\nimport { useEffect, useState } from 'react';\nimport { Sliders, RefreshCw, Power } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport type { Mod, Snapshot } from '../../lib/types';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\n\ntype Props = {\n snapshot: Snapshot;\n onBack: () => void;\n};\n\nexport function MobileMods({ snapshot, onBack }: Props) {\n const [mods, setMods] = useState<Mod[]>(snapshot.mods || []);\n const [loading, setLoading] = useState(!snapshot.mods);\n const [filter, setFilter] = useState('');\n const [statusFilter, setStatusFilter] = useState<'all' | 'enabled' | 'disabled'>('all');\n const [detailMod, setDetailMod] = useState<Mod | null>(null);\n\n const reload = async () => {\n try {\n const data = await api.get<{ mods: Mod[] }>('/mods');\n setMods(data.mods || []);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n if (snapshot.mods) {\n setMods(snapshot.mods);\n setLoading(false);\n } else {\n reload();\n }\n }, [snapshot.mods]);\n\n const toggleMod = async (id: string, enabled: boolean) => {\n try {\n await api.patch(`/mods/${encodeURIComponent(id)}`, { enabled });\n setMods((cur) => cur.map((m) => (m.id === id ? { ...m, enabled } : m)));\n if (detailMod?.id === id) setDetailMod((m) => m ? { ...m, enabled } : m);\n } catch {\n // best-effort\n }\n };\n\n const filtered = mods.filter((m) => {\n if (statusFilter === 'enabled' && !m.enabled) return false;\n if (statusFilter === 'disabled' && m.enabled) return false;\n if (filter) {\n const q = filter.toLowerCase();\n return m.name.toLowerCase().includes(q) || m.description.toLowerCase().includes(q);\n }\n return true;\n });\n\n return (\n <div className=\"mobile-view\">\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search mods…\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n style={{ flex: 1 }}\n />\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => reload()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n </div>\n\n <div className=\"mobile-search-scopes\">\n {(['all', 'enabled', 'disabled'] as const).map((s) => (\n <button key={s} type=\"button\" className={`mobile-scope-chip ${statusFilter === s ? 'active' : ''}`}\n onClick={() => setStatusFilter(s)}>\n {s.charAt(0).toUpperCase() + s.slice(1)}\n </button>\n ))}\n </div>\n\n {loading ? (\n <div className=\"mobile-loading\"><p>Loading…</p></div>\n ) : filtered.length === 0 ? (\n <div className=\"mobile-empty\">\n <Sliders size={40} />\n <p>No mods found.</p>\n </div>\n ) : (\n <div className=\"mobile-card-list\">\n {filtered.map((m) => (\n <button\n key={m.id}\n type=\"button\"\n className=\"mobile-list-item mobile-list-item-interactive\"\n onClick={() => setDetailMod(m)}\n >\n <div className=\"mobile-list-icon\"><Sliders size={16} /></div>\n <div className=\"mobile-list-content\">\n <span className=\"mobile-list-title\">{m.name}</span>\n <span className=\"mobile-list-meta\">v{m.version} · {m.author}</span>\n </div>\n <span className={`mobile-list-badge ${m.enabled ? 'badge-on' : 'badge-off'}`}>\n {m.enabled ? 'on' : 'off'}\n </span>\n </button>\n ))}\n </div>\n )}\n\n {detailMod && (\n <MobileBottomSheet\n open={true}\n onClose={() => setDetailMod(null)}\n title={detailMod.name}\n actions={\n <div className=\"mobile-task-detail-actions\">\n <button\n type=\"button\"\n className=\"mobile-btn\"\n style={{ flex: 1 }}\n onClick={() => { if (detailMod) toggleMod(detailMod.id, !detailMod.enabled); }}\n >\n <Power size={14} /> {detailMod.enabled ? 'Disable' : 'Enable'}\n </button>\n </div>\n }\n >\n <div className=\"mobile-agent-detail\">\n <p className=\"mobile-agent-detail-desc\">{detailMod.description || 'No description.'}</p>\n <div className=\"mobile-agent-detail-meta\">\n <div className=\"mobile-task-detail-row\"><span>Version</span><span className=\"mono\">{detailMod.version}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Author</span><span>{detailMod.author}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Bizar</span><span className=\"mono\">{detailMod.bizar}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Type</span><span>{detailMod.type}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Permissions</span><span>{detailMod.permissions.length}</span></div>\n {detailMod.installedAt && (\n <div className=\"mobile-task-detail-row\"><span>Installed</span><span>{new Date(detailMod.installedAt).toLocaleDateString()}</span></div>\n )}\n </div>\n </div>\n </MobileBottomSheet>\n )}\n </div>\n );\n}\n","// src/mobile/views/MobileMore.tsx — real navigation hub: Plans, Agents, Skills, Mods, Schedules, History, Config.\nimport { useState } from 'react';\nimport { Bot, FileText, Sliders, Clock, History, Settings, ChevronRight } from 'lucide-react';\nimport type { Snapshot } from '../../lib/types';\n\ntype Props = {\n snapshot: Snapshot;\n onNavigate: (id: string) => void;\n};\n\nexport function MobileMore({ snapshot, onNavigate }: Props) {\n const [search, setSearch] = useState('');\n\n const entries = [\n {\n id: 'artifacts',\n icon: FileText,\n label: 'Plans',\n count: snapshot.artifacts.length || 0,\n desc: 'Visual artifacts with elements & comments',\n },\n {\n id: 'agents',\n icon: Bot,\n label: 'Agents',\n count: snapshot.agents?.length || 0,\n desc: 'The Norse pantheon',\n },\n {\n id: 'skills',\n icon: Sliders,\n label: 'Skills',\n count: null,\n desc: 'Agent capabilities & tools',\n },\n {\n id: 'mods',\n icon: Sliders,\n label: 'Mods',\n count: snapshot.mods?.length || 0,\n desc: 'Installed modifications',\n },\n {\n id: 'schedules',\n icon: Clock,\n label: 'Schedules',\n count: snapshot.schedules?.length || 0,\n desc: 'Cron jobs & automated tasks',\n },\n {\n id: 'history',\n icon: History,\n label: 'History',\n count: null,\n desc: 'Past sessions & outputs',\n },\n {\n id: 'config',\n icon: Settings,\n label: 'Config',\n count: null,\n desc: 'Key-value configuration editor',\n },\n ];\n\n const filtered = search.trim()\n ? entries.filter((e) => e.label.toLowerCase().includes(search.toLowerCase()) || e.desc.toLowerCase().includes(search.toLowerCase()))\n : entries;\n\n return (\n <div className=\"mobile-view\">\n {/* Search */}\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n style={{ flex: 1 }}\n />\n </div>\n\n {/* Nav entries */}\n <div className=\"mobile-card-list\">\n {filtered.map((entry) => {\n const Icon = entry.icon;\n return (\n <button\n key={entry.id}\n type=\"button\"\n className=\"mobile-more-nav-item\"\n onClick={() => onNavigate(entry.id)}\n >\n <div className=\"mobile-more-nav-icon\">\n <Icon size={20} />\n </div>\n <div className=\"mobile-more-nav-content\">\n <span className=\"mobile-more-nav-label\">\n {entry.label}\n {entry.count != null && (\n <span className=\"mobile-more-nav-count\">{entry.count}</span>\n )}\n </span>\n <span className=\"mobile-more-nav-desc\">{entry.desc}</span>\n </div>\n <ChevronRight size={16} className=\"mobile-more-nav-arrow\" />\n </button>\n );\n })}\n </div>\n\n {/* Footer */}\n <div className=\"mobile-more-footer\">\n <a href=\"/?desktop=1\" className=\"mobile-more-link\">\n Switch to Desktop <ChevronRight size={14} />\n </a>\n </div>\n </div>\n );\n}\n","// src/mobile/views/MobileArtifactCanvas.tsx — mobile plan canvas: read-only + edit mode.\nimport { useEffect, useState } from 'react';\nimport { Plus, MessageSquare, Pencil, Trash2, CheckSquare, StickyNote, HelpCircle, X } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport { formatRelative } from '../../lib/utils';\nimport type { Canvas, CanvasElement } from '../../lib/types';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\nimport { MobileModal } from '../components/MobileModal';\n\ntype Props = {\n slug: string;\n onBack: () => void;\n};\n\nconst ELEMENT_TYPES = [\n { id: 'task', label: 'Task', color: 'var(--info)' },\n { id: 'note', label: 'Note', color: 'var(--text-dim)' },\n { id: 'decision', label: 'Decision', color: 'var(--accent)' },\n { id: 'question', label: 'Question', color: 'var(--warning)' },\n];\n\nfunction elementColor(type: string): string {\n return ELEMENT_TYPES.find((t) => t.id === type)?.color || 'var(--text-dim)';\n}\n\nfunction ElementIcon({ type, size = 12 }: { type: string; size?: number }) {\n switch (type) {\n case 'task': return <CheckSquare size={size} style={{ color: 'var(--info)' }} />;\n case 'note': return <StickyNote size={size} style={{ color: 'var(--text-dim)' }} />;\n case 'decision': return <CheckSquare size={size} style={{ color: 'var(--accent)' }} />;\n case 'question': return <HelpCircle size={size} style={{ color: 'var(--warning)' }} />;\n default: return <StickyNote size={size} />;\n }\n}\n\nexport function MobileArtifactCanvas({ slug, onBack }: Props) {\n const [canvas, setCanvas] = useState<Canvas | null>(null);\n const [meta, setMeta] = useState<{ title: string; status: string; tags: string[]; description?: string } | null>(null);\n const [loading, setLoading] = useState(true);\n const [editMode, setEditMode] = useState(false);\n const [selectedEl, setSelectedEl] = useState<CanvasElement | null>(null);\n const [showComments, setShowComments] = useState(false);\n const [addElementOpen, setAddElementOpen] = useState(false);\n const [addCommentOpen, setAddCommentOpen] = useState(false);\n const [commentText, setCommentText] = useState('');\n\n const reload = async () => {\n setLoading(true);\n try {\n const plan = await api.get<{ meta: typeof meta; canvas: Canvas }>(\n `/plans/${encodeURIComponent(slug)}`,\n );\n setCanvas(plan.canvas);\n setMeta(plan.meta);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => { reload(); }, [slug]);\n\n const addElement = async (type: string, title: string, content: string) => {\n try {\n const maxX = (canvas?.elements || []).reduce((acc, el) => Math.max(acc, el.x || 0), 40);\n const maxY = (canvas?.elements || []).reduce((acc, el) => Math.max(acc, el.y || 0), 40);\n await api.post(`/plans/${encodeURIComponent(slug)}/elements`, {\n type,\n title,\n content,\n x: 20 + maxX,\n y: 20 + maxY,\n width: 240,\n height: 120,\n });\n await reload();\n setAddElementOpen(false);\n } catch {\n // best-effort\n }\n };\n\n const updateElement = async (id: string, body: Partial<CanvasElement>) => {\n try {\n await api.put(`/plans/${encodeURIComponent(slug)}/elements/${encodeURIComponent(id)}`, body);\n await reload();\n setSelectedEl(null);\n } catch {\n // best-effort\n }\n };\n\n const deleteElement = async (id: string) => {\n if (!confirm('Delete this element?')) return;\n try {\n await api.del(`/plans/${encodeURIComponent(slug)}/elements/${encodeURIComponent(id)}`);\n setSelectedEl(null);\n await reload();\n } catch {\n // best-effort\n }\n };\n\n const addComment = async (text: string, elementId: string | null) => {\n try {\n const path = elementId\n ? `/plans/${encodeURIComponent(slug)}/elements/${encodeURIComponent(elementId)}/comments`\n : `/plans/${encodeURIComponent(slug)}/comments`;\n await api.post(path, { text, elementId });\n await reload();\n setCommentText('');\n setAddCommentOpen(false);\n } catch {\n // best-effort\n }\n };\n\n if (loading || !canvas || !meta) {\n return (\n <div className=\"mobile-view\">\n <div className=\"mobile-loading\"><p>Loading canvas…</p></div>\n </div>\n );\n }\n\n return (\n <div className=\"mobile-view mobile-view-canvas\">\n {/* Canvas top bar */}\n <div className=\"mobile-canvas-bar\">\n <div className=\"mobile-canvas-meta\">\n <span className=\"mobile-canvas-title\">{meta.title || slug}</span>\n <span className=\"mobile-canvas-status\" data-status={meta.status}>{meta.status}</span>\n </div>\n <div className=\"mobile-canvas-actions\">\n <button\n type=\"button\"\n className={`mobile-icon-btn ${editMode ? 'active' : ''}`}\n onClick={() => setEditMode((v) => !v)}\n aria-label={editMode ? 'Done editing' : 'Edit'}\n >\n <Pencil size={16} />\n </button>\n <button\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={() => setShowComments((v) => !v)}\n aria-label=\"Comments\"\n >\n <MessageSquare size={16} />\n {canvas.comments.length > 0 && (\n <span className=\"mobile-canvas-comment-count\">{canvas.comments.length}</span>\n )}\n </button>\n {editMode && (\n <button\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={() => setAddElementOpen(true)}\n aria-label=\"Add element\"\n >\n <Plus size={16} />\n </button>\n )}\n </div>\n </div>\n\n {/* Elements list */}\n <div className=\"mobile-canvas-elements\">\n {canvas.elements.length === 0 && (\n <div className=\"mobile-empty\">\n <p>No elements yet.</p>\n {editMode && (\n <button\n type=\"button\"\n className=\"mobile-btn\"\n onClick={() => setAddElementOpen(true)}\n >\n <Plus size={14} /> Add Element\n </button>\n )}\n </div>\n )}\n {canvas.elements.map((el) => (\n <button\n key={el.id}\n type=\"button\"\n className={`mobile-canvas-element ${selectedEl?.id === el.id ? 'selected' : ''}`}\n style={{ borderColor: elementColor(el.type) }}\n onClick={() => {\n if (editMode) {\n setSelectedEl(el);\n }\n }}\n aria-pressed={selectedEl?.id === el.id}\n >\n <div className=\"mobile-canvas-el-header\" style={{ background: `${elementColor(el.type)}18` }}>\n <ElementIcon type={el.type} size={12} />\n <span className=\"mobile-canvas-el-type\" style={{ color: elementColor(el.type) }}>{el.type}</span>\n {el.status && el.status !== 'open' && (\n <span className=\"mobile-canvas-el-status\">{el.status}</span>\n )}\n </div>\n <div className=\"mobile-canvas-el-title\">{el.title || 'Untitled'}</div>\n {el.content && (\n <div className=\"mobile-canvas-el-content\">\n {el.content.slice(0, 120)}{el.content.length > 120 ? '…' : ''}\n </div>\n )}\n <div className=\"mobile-canvas-el-comments\">\n <MessageSquare size={10} />\n {canvas.comments.filter((c) => c.elementId === el.id).length}\n </div>\n </button>\n ))}\n </div>\n\n {/* Comments panel */}\n {showComments && (\n <div className=\"mobile-canvas-comments\">\n <div className=\"mobile-canvas-comments-header\">\n <h4>Comments ({canvas.comments.length})</h4>\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => setShowComments(false)}>\n <X size={16} />\n </button>\n </div>\n <div className=\"mobile-canvas-comment-list\">\n {canvas.comments.length === 0 && (\n <p className=\"mobile-empty-inline\">No comments yet.</p>\n )}\n {canvas.comments.map((c) => (\n <div key={c.id} className=\"mobile-canvas-comment\">\n <div className=\"mobile-canvas-comment-head\">\n <span className=\"mono\">{c.author}</span>\n <span className=\"muted\">{formatRelative(c.created)}</span>\n </div>\n <div className=\"mobile-canvas-comment-text\">{c.text}</div>\n </div>\n ))}\n </div>\n <div className=\"mobile-canvas-comment-input\">\n <textarea\n className=\"mobile-input\"\n rows={2}\n placeholder=\"Add a comment…\"\n value={commentText}\n onChange={(e) => setCommentText(e.target.value)}\n />\n <button\n type=\"button\"\n className=\"mobile-btn\"\n disabled={!commentText.trim()}\n onClick={() => { addComment(commentText.trim(), null); }}\n >\n Post\n </button>\n </div>\n </div>\n )}\n\n {/* Element edit sheet */}\n {selectedEl && (\n <MobileBottomSheet\n open={true}\n onClose={() => setSelectedEl(null)}\n title={`Edit ${selectedEl.title || selectedEl.type}`}\n actions={\n <div className=\"mobile-task-detail-actions\">\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-danger\"\n onClick={() => deleteElement(selectedEl.id)}\n >\n <Trash2 size={14} /> Delete\n </button>\n <button\n type=\"button\"\n className=\"mobile-btn\"\n onClick={() => setAddCommentOpen(true)}\n >\n <MessageSquare size={14} /> Comment\n </button>\n </div>\n }\n >\n <ElementEditForm\n element={selectedEl}\n onSave={(body) => updateElement(selectedEl.id, body)}\n onClose={() => setSelectedEl(null)}\n />\n </MobileBottomSheet>\n )}\n\n {/* Add element modal */}\n <AddElementModal\n open={addElementOpen}\n onClose={() => setAddElementOpen(false)}\n onAdd={addElement}\n />\n\n {/* Add comment modal */}\n <MobileModal open={addCommentOpen} onClose={() => setAddCommentOpen(false)} title=\"Add Comment\" actions={\n <button\n type=\"button\"\n className=\"mobile-btn\"\n style={{ width: '100%' }}\n disabled={!commentText.trim()}\n onClick={() => { addComment(commentText.trim(), selectedEl?.id || null); setAddCommentOpen(false); }}\n >\n <MessageSquare size={14} /> Post\n </button>\n }>\n <textarea\n className=\"mobile-input\"\n rows={3}\n placeholder=\"Your comment…\"\n value={commentText}\n onChange={(e) => setCommentText(e.target.value)}\n autoFocus\n />\n </MobileModal>\n </div>\n );\n}\n\nfunction ElementEditForm({\n element,\n onSave,\n onClose,\n}: {\n element: CanvasElement;\n onSave: (body: Partial<CanvasElement>) => void;\n onClose: () => void;\n}) {\n const [type, setType] = useState(element.type);\n const [title, setTitle] = useState(element.title || '');\n const [content, setContent] = useState(element.content || '');\n const [status, setStatus] = useState(element.status || 'open');\n\n return (\n <form\n className=\"mobile-task-form\"\n onSubmit={(e) => {\n e.preventDefault();\n onSave({ type, title, content, status });\n onClose();\n }}\n >\n <label className=\"mobile-field-label\">Type</label>\n <select className=\"mobile-input\" value={type} onChange={(e) => setType(e.target.value)}>\n {ELEMENT_TYPES.map((t) => (\n <option key={t.id} value={t.id}>{t.label}</option>\n ))}\n </select>\n <label className=\"mobile-field-label\">Title</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n <label className=\"mobile-field-label\">Status</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n value={status}\n onChange={(e) => setStatus(e.target.value)}\n placeholder=\"open / done / blocked\"\n />\n <label className=\"mobile-field-label\">Content (markdown)</label>\n <textarea\n className=\"mobile-input\"\n rows={4}\n value={content}\n onChange={(e) => setContent(e.target.value)}\n />\n <button type=\"submit\" className=\"mobile-btn\" style={{ width: '100%', marginTop: 8 }}>\n Save\n </button>\n </form>\n );\n}\n\nfunction AddElementModal({\n open,\n onClose,\n onAdd,\n}: {\n open: boolean;\n onClose: () => void;\n onAdd: (type: string, title: string, content: string) => void;\n}) {\n const [type, setType] = useState('task');\n const [title, setTitle] = useState('');\n const [content, setContent] = useState('');\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!title.trim()) return;\n onAdd(type, title.trim(), content.trim());\n setTitle('');\n setContent('');\n };\n\n return (\n <MobileModal open={open} onClose={onClose} title=\"Add Element\" actions={\n <button type=\"submit\" form=\"add-el-form\" className=\"mobile-btn\" style={{ width: '100%' }}>\n <Plus size={14} /> Add\n </button>\n }>\n <form id=\"add-el-form\" onSubmit={handleSubmit} className=\"mobile-task-form\">\n <label className=\"mobile-field-label\">Type</label>\n <select className=\"mobile-input\" value={type} onChange={(e) => setType(e.target.value)}>\n {ELEMENT_TYPES.map((t) => (\n <option key={t.id} value={t.id}>{t.label}</option>\n ))}\n </select>\n <label className=\"mobile-field-label\">Title *</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n placeholder=\"Element title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n autoFocus\n required\n />\n <label className=\"mobile-field-label\">Content (markdown)</label>\n <textarea\n className=\"mobile-input\"\n rows={3}\n placeholder=\"Description…\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n />\n </form>\n </MobileModal>\n );\n}\n","// src/mobile/views/MobileArtifacts.tsx — plan list with tap-to-open.\nimport { useEffect, useState } from 'react';\nimport { FileText, Plus, RefreshCw } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport type { Artifact, Snapshot } from '../../lib/types';\nimport { MobileModal } from '../components/MobileModal';\n\ntype Props = {\n snapshot: Snapshot;\n onBack: () => void;\n onOpenArtifact: (slug: string) => void;\n};\n\nexport function MobileArtifacts({ snapshot, onBack, onOpenArtifact }: Props) {\n const [artifacts, setArtifacts] = useState<Artifact[]>(snapshot.artifacts || []);\n const [loading, setLoading] = useState(!snapshot.artifacts);\n const [filter, setFilter] = useState('');\n const [newOpen, setNewOpen] = useState(false);\n const [newSlug, setNewSlug] = useState('');\n const [newTitle, setNewTitle] = useState('');\n\n const reload = async () => {\n try {\n const data = await api.get<{ artifacts: Artifact[] }>('/artifacts');\n setArtifacts(data.artifacts || []);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n const createPlan = async () => {\n if (!newSlug.trim()) return;\n try {\n const result = await api.post<{ slug: string }>('/artifacts', {\n slug: newSlug.trim(),\n title: newTitle.trim() || undefined,\n });\n setNewOpen(false);\n setNewSlug('');\n setNewTitle('');\n onOpenArtifact(result.slug);\n } catch {\n // best-effort\n }\n };\n\n const filtered = filter.trim()\n ? artifacts.filter((p) =>\n (p.title || p.slug || '').toLowerCase().includes(filter.toLowerCase()),\n )\n : artifacts;\n\n return (\n <div className=\"mobile-view\">\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search artifacts…\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n style={{ flex: 1 }}\n />\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => reload()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n </div>\n\n {/* New plan */}\n <div style={{ marginBottom: 12 }}>\n <button\n type=\"button\"\n className=\"mobile-btn\"\n style={{ width: '100%' }}\n onClick={() => setNewOpen(true)}\n >\n <Plus size={14} /> New Artifact\n </button>\n </div>\n\n {loading ? (\n <div className=\"mobile-loading\"><p>Loading…</p></div>\n ) : filtered.length === 0 ? (\n <div className=\"mobile-empty\">\n <FileText size={40} />\n <p>No artifacts yet.</p>\n </div>\n ) : (\n <div className=\"mobile-card-list\">\n {filtered.map((p) => (\n <button\n key={p.slug}\n type=\"button\"\n className=\"mobile-list-item mobile-list-item-interactive\"\n onClick={() => onOpenArtifact(p.slug)}\n >\n <div className=\"mobile-list-icon\"><FileText size={16} /></div>\n <div className=\"mobile-list-content\">\n <span className=\"mobile-list-title\">{p.title || p.slug}</span>\n <span className=\"mobile-list-meta\">\n {p.status} · {p.source}\n {p.elementCount != null ? ` · ${p.elementCount} elements` : ''}\n {p.commentCount != null ? ` · ${p.commentCount} comments` : ''}\n </span>\n </div>\n <span className={`mobile-list-badge`} data-status={p.status}>{p.status}</span>\n </button>\n ))}\n </div>\n )}\n\n <MobileModal open={newOpen} onClose={() => setNewOpen(false)} title=\"New Artifact\" actions={\n <button type=\"submit\" form=\"new-plan-form\" className=\"mobile-btn\" style={{ width: '100%' }}>\n <Plus size={14} /> Create\n </button>\n }>\n <form id=\"new-plan-form\" onSubmit={(e) => { e.preventDefault(); createPlan(); }} className=\"mobile-task-form\">\n <label className=\"mobile-field-label\">Slug *</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n placeholder=\"my-plan\"\n pattern=\"[a-z0-9][a-z0-9-]{0,63}\"\n value={newSlug}\n onChange={(e) => setNewSlug(e.target.value)}\n autoFocus\n required\n />\n <label className=\"mobile-field-label\">Title (optional)</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n placeholder=\"My Artifact\"\n value={newTitle}\n onChange={(e) => setNewTitle(e.target.value)}\n />\n </form>\n </MobileModal>\n </div>\n );\n}\n","// src/mobile/views/MobileSchedules.tsx — schedules list with detail sheet, edit modal, and a reusable New/Edit modal.\n//\n// v3.9.0 — overhaul:\n// - PATCH for partial updates (toggle enabled, edit fields) is now\n// supported by the backend (see routes/schedules.mjs).\n// - NewScheduleModal accepts an optional `initial` schedule and submits\n// PUT when editing, POST when creating. The same modal handles both\n// flows so the operator doesn't context-switch between forms.\n// - Detail sheet exposes an Edit button so the mobile UI can edit any\n// schedule, not just toggle it.\n// - The structured pickers mirror the desktop (DOW, hour, minute,\n// timezone) but in a more compact layout suited to a phone screen.\nimport { useEffect, useState } from 'react';\nimport { Clock, Plus, RefreshCw, Trash2, Play, Pencil } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport type { Schedule, Snapshot } from '../../lib/types';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\nimport { MobileModal } from '../components/MobileModal';\n\ntype Props = {\n snapshot: Snapshot;\n onBack: () => void;\n};\n\nconst TYPES: Array<Schedule['type']> = ['cron', 'interval', 'once'];\nconst ACTIONS: Array<Schedule['action']['type']> = ['command', 'agent', 'webhook'];\n\nconst TIMEZONES = ['UTC', 'America/New_York', 'America/Los_Angeles', 'Europe/London', 'Europe/Berlin', 'Asia/Tokyo'];\n\nconst HOURS = Array.from({ length: 24 }, (_, h) => ({\n value: h,\n label: h === 0 ? '12 AM' : h < 12 ? `${h} AM` : h === 12 ? '12 PM' : `${h - 12} PM`,\n}));\nconst MINUTES = Array.from({ length: 12 }, (_, i) => i * 5); // 0,5,…,55\nconst DOWS = [\n { value: '*', label: 'Every day' },\n { value: '0', label: 'Sun' },\n { value: '1', label: 'Mon' },\n { value: '2', label: 'Tue' },\n { value: '3', label: 'Wed' },\n { value: '4', label: 'Thu' },\n { value: '5', label: 'Fri' },\n { value: '6', label: 'Sat' },\n];\nconst INTERVAL_UNITS = [\n { value: 's', label: 'sec' },\n { value: 'm', label: 'min' },\n { value: 'h', label: 'hr' },\n { value: 'd', label: 'day' },\n];\n\nexport function MobileSchedules({ snapshot }: Props) {\n const [schedules, setSchedules] = useState<Schedule[]>(snapshot.schedules || []);\n const [loading, setLoading] = useState(!snapshot.schedules);\n const [filter, setFilter] = useState('');\n const [detailSchedule, setDetailSchedule] = useState<Schedule | null>(null);\n const [editor, setEditor] = useState<{ open: boolean; initial?: Schedule }>({ open: false });\n\n const reload = async () => {\n try {\n const data = await api.get<{ schedules: Schedule[] }>('/schedules');\n setSchedules(data.schedules || []);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n if (snapshot.schedules) {\n setSchedules(snapshot.schedules);\n setLoading(false);\n } else {\n reload();\n }\n }, [snapshot.schedules]);\n\n // v3.9.0 — PATCH partial update (toggle enabled, edit name, etc.).\n // The backend supports PATCH (see routes/schedules.mjs).\n const toggleSchedule = async (id: string, enabled: boolean) => {\n try {\n await api.patch(`/schedules/${encodeURIComponent(id)}`, { enabled });\n setSchedules((cur) => cur.map((s) => (s.id === id ? { ...s, enabled } : s)));\n if (detailSchedule?.id === id) {\n setDetailSchedule((s) => (s ? { ...s, enabled } : s));\n }\n } catch {\n // best-effort\n }\n };\n\n const deleteSchedule = async (id: string) => {\n if (!confirm('Delete this schedule?')) return;\n try {\n await api.del(`/schedules/${encodeURIComponent(id)}`);\n setSchedules((cur) => cur.filter((s) => s.id !== id));\n if (detailSchedule?.id === id) setDetailSchedule(null);\n } catch {\n // best-effort\n }\n };\n\n // v3.9.0 — backend added /trigger as an alias of /run (mobile uses this).\n const triggerSchedule = async (id: string) => {\n try {\n await api.post(`/schedules/${encodeURIComponent(id)}/trigger`);\n } catch {\n // best-effort\n }\n };\n\n const filtered = filter.trim()\n ? schedules.filter((s) => s.name.toLowerCase().includes(filter.toLowerCase()))\n : schedules;\n\n return (\n <div className=\"mobile-view\">\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search schedules…\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n style={{ flex: 1 }}\n />\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => reload()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n <button\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={() => setEditor({ open: true })}\n aria-label=\"New schedule\"\n >\n <Plus size={16} />\n </button>\n </div>\n\n {loading ? (\n <div className=\"mobile-loading\"><p>Loading…</p></div>\n ) : filtered.length === 0 ? (\n <div className=\"mobile-empty\">\n <Clock size={40} />\n <p>No schedules found.</p>\n </div>\n ) : (\n <div className=\"mobile-card-list\">\n {filtered.map((s) => (\n <button\n key={s.id}\n type=\"button\"\n className=\"mobile-list-item mobile-list-item-interactive\"\n onClick={() => setDetailSchedule(s)}\n >\n <div className=\"mobile-list-icon\"><Clock size={16} /></div>\n <div className=\"mobile-list-content\">\n <span className=\"mobile-list-title\">{s.name}</span>\n <span className=\"mobile-list-meta\">\n {humanizeSchedule(s)} ·{' '}\n {s.nextRun ? `next ${formatNextShort(s.nextRun, s.timezone)}` : 'no next run'}\n </span>\n </div>\n <span className={`mobile-list-badge ${s.enabled ? 'badge-on' : 'badge-off'}`}>\n {s.enabled ? 'on' : 'off'}\n </span>\n </button>\n ))}\n </div>\n )}\n\n {detailSchedule && (\n <MobileBottomSheet\n open={true}\n onClose={() => setDetailSchedule(null)}\n title={detailSchedule.name}\n actions={\n <div className=\"mobile-task-detail-actions\">\n <button\n type=\"button\"\n className=\"mobile-btn\"\n onClick={() => { if (detailSchedule) triggerSchedule(detailSchedule.id); }}\n >\n <Play size={14} /> Trigger now\n </button>\n <button\n type=\"button\"\n className={`mobile-btn ${!detailSchedule.enabled ? '' : 'mobile-btn-secondary'}`}\n onClick={() => { if (detailSchedule) toggleSchedule(detailSchedule.id, !detailSchedule.enabled); }}\n >\n {detailSchedule.enabled ? 'Disable' : 'Enable'}\n </button>\n <button\n type=\"button\"\n className=\"mobile-btn\"\n onClick={() => {\n if (!detailSchedule) return;\n setEditor({ open: true, initial: detailSchedule });\n setDetailSchedule(null);\n }}\n >\n <Pencil size={14} /> Edit\n </button>\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-danger\"\n onClick={() => { if (detailSchedule) deleteSchedule(detailSchedule.id); }}\n >\n <Trash2 size={14} />\n </button>\n </div>\n }\n >\n <div className=\"mobile-agent-detail\">\n <div className=\"mobile-agent-detail-meta\">\n <div className=\"mobile-task-detail-row\">\n <span>Type</span>\n <span>{detailSchedule.type}</span>\n </div>\n <div className=\"mobile-task-detail-row\">\n <span>Schedule</span>\n <span className=\"mono\">{humanizeSchedule(detailSchedule) || detailSchedule.schedule}</span>\n </div>\n {detailSchedule.timezone && (\n <div className=\"mobile-task-detail-row\">\n <span>Timezone</span>\n <span className=\"mono\">{detailSchedule.timezone}</span>\n </div>\n )}\n <div className=\"mobile-task-detail-row\">\n <span>Action</span>\n <span>{detailSchedule.action.type}: {detailSchedule.action.target}</span>\n </div>\n {detailSchedule.action.type === 'agent' && detailSchedule.action.prompt && (\n <div className=\"mobile-task-detail-row\">\n <span>Prompt</span>\n <span style={{ fontSize: 12 }}>{detailSchedule.action.prompt}</span>\n </div>\n )}\n {detailSchedule.budgetCheck?.skipIfBudgetLow && (\n <div className=\"mobile-task-detail-row\">\n <span>Budget gate</span>\n <span>skip if ≥ {detailSchedule.budgetCheck.maxConcurrent ?? 6} concurrent</span>\n </div>\n )}\n <div className=\"mobile-task-detail-row\">\n <span>Next run</span>\n <span>{detailSchedule.nextRun ? formatNextShort(detailSchedule.nextRun, detailSchedule.timezone) : '—'}</span>\n </div>\n <div className=\"mobile-task-detail-row\">\n <span>Last run</span>\n <span>{detailSchedule.lastRun ? formatRel(detailSchedule.lastRun) : '—'}</span>\n </div>\n {detailSchedule.lastResult && (\n <div className=\"mobile-task-detail-row\">\n <span>Last result</span>\n <span style={{ fontSize: 12 }}>{detailSchedule.lastResult}</span>\n </div>\n )}\n {detailSchedule.lastError && (\n <div className=\"mobile-task-detail-row\">\n <span>Last error</span>\n <span style={{ fontSize: 12 }}>{detailSchedule.lastError}</span>\n </div>\n )}\n </div>\n </div>\n </MobileBottomSheet>\n )}\n\n <ScheduleEditor\n open={editor.open}\n initial={editor.initial}\n onClose={() => setEditor({ open: false })}\n onSaved={() => {\n setEditor({ open: false });\n reload();\n }}\n />\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Reusable mobile editor modal */\n/* ------------------------------------------------------------------ */\n\ntype EditorState = {\n name: string;\n type: Schedule['type'];\n cronMinute: number;\n cronHour: number;\n cronDow: string;\n intervalN: number;\n intervalUnit: string;\n onceAt: string;\n timezone: string;\n actionType: Schedule['action']['type'];\n actionTarget: string;\n actionPrompt: string;\n skipIfBudgetLow: boolean;\n maxConcurrent: number;\n enabled: boolean;\n};\n\nfunction initialEditorState(initial?: Schedule): EditorState {\n if (!initial) {\n return {\n name: '',\n type: 'cron',\n cronMinute: 0,\n cronHour: 13,\n cronDow: '0',\n intervalN: 30,\n intervalUnit: 'm',\n onceAt: '',\n timezone: 'UTC',\n actionType: 'agent',\n actionTarget: '',\n actionPrompt: '',\n skipIfBudgetLow: false,\n maxConcurrent: 6,\n enabled: true,\n };\n }\n const parsedCron = initial.type === 'cron' ? parseSimpleCron(initial.schedule) : null;\n const parsedInterval = initial.type === 'interval' ? parseSimpleInterval(initial.schedule) : null;\n return {\n name: initial.name || '',\n type: initial.type,\n cronMinute: parsedCron?.minute ?? 0,\n cronHour: parsedCron?.hour ?? 9,\n cronDow: parsedCron?.dow ?? '*',\n intervalN: parsedInterval?.n ?? 30,\n intervalUnit: parsedInterval?.unit ?? 'm',\n onceAt: initial.type === 'once' ? toDatetimeLocal(initial.schedule) : '',\n timezone: initial.timezone || 'UTC',\n actionType: initial.action.type,\n actionTarget: initial.action.target || '',\n actionPrompt: initial.action.prompt || '',\n skipIfBudgetLow: !!initial.budgetCheck?.skipIfBudgetLow,\n maxConcurrent: Number.isFinite(initial.budgetCheck?.maxConcurrent)\n ? initial.budgetCheck!.maxConcurrent!\n : 6,\n enabled: initial.enabled !== false,\n };\n}\n\nfunction composeScheduleValue(s: EditorState): string {\n if (s.type === 'cron') return `${s.cronMinute} ${s.cronHour} * * ${s.cronDow}`;\n if (s.type === 'interval') return `${s.intervalN}${s.intervalUnit}`;\n if (!s.onceAt) return '';\n try {\n return new Date(s.onceAt).toISOString();\n } catch {\n return s.onceAt;\n }\n}\n\nfunction ScheduleEditor({\n open,\n initial,\n onClose,\n onSaved,\n}: {\n open: boolean;\n initial?: Schedule;\n onClose: () => void;\n onSaved: () => void;\n}) {\n const [state, setState] = useState<EditorState>(() => initialEditorState(initial));\n const [submitting, setSubmitting] = useState(false);\n\n // Re-seed state when the modal is reopened with a different initial.\n useEffect(() => {\n if (open) setState(initialEditorState(initial));\n }, [open, initial]);\n\n const update = <K extends keyof EditorState>(key: K, value: EditorState[K]) => {\n setState((cur) => ({ ...cur, [key]: value }));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!state.name.trim()) return;\n const schedule = composeScheduleValue(state);\n if (!schedule) return;\n setSubmitting(true);\n try {\n const payload = {\n name: state.name.trim(),\n type: state.type,\n schedule,\n timezone: state.timezone,\n action: {\n type: state.actionType,\n target: state.actionTarget.trim(),\n ...(state.actionType === 'agent' && state.actionPrompt.trim()\n ? { prompt: state.actionPrompt.trim() }\n : {}),\n },\n budgetCheck: {\n maxConcurrent: state.maxConcurrent,\n skipIfBudgetLow: state.skipIfBudgetLow,\n },\n enabled: state.enabled,\n };\n if (initial) {\n await api.put(`/schedules/${encodeURIComponent(initial.id)}`, payload);\n } else {\n await api.post('/schedules', payload);\n }\n onSaved();\n } catch {\n // best-effort — modal stays open so the operator can retry.\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <MobileModal\n open={open}\n onClose={onClose}\n title={initial ? `Edit ${initial.name}` : 'New schedule'}\n actions={\n <button\n type=\"submit\"\n form=\"mobile-schedule-form\"\n className=\"mobile-btn\"\n style={{ width: '100%' }}\n disabled={submitting}\n >\n <Plus size={14} /> {initial ? 'Save' : 'Create'}\n </button>\n }\n >\n <form id=\"mobile-schedule-form\" onSubmit={handleSubmit} className=\"mobile-task-form\">\n <label className=\"mobile-field-label\">Name *</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n placeholder=\"Weekly review\"\n value={state.name}\n onChange={(e) => update('name', e.target.value)}\n autoFocus\n required\n />\n\n <label className=\"mobile-field-label\">Type</label>\n <select\n className=\"mobile-input\"\n value={state.type}\n onChange={(e) => update('type', e.target.value as Schedule['type'])}\n >\n {TYPES.map((t) => <option key={t} value={t}>{t}</option>)}\n </select>\n\n {state.type === 'cron' && (\n <>\n <label className=\"mobile-field-label\">Day of week</label>\n <select\n className=\"mobile-input\"\n value={state.cronDow}\n onChange={(e) => update('cronDow', e.target.value)}\n >\n {DOWS.map((d) => <option key={d.value} value={d.value}>{d.label}</option>)}\n </select>\n\n <div className=\"mobile-form-row\">\n <div style={{ flex: 1 }}>\n <label className=\"mobile-field-label\">Hour</label>\n <select\n className=\"mobile-input\"\n value={String(state.cronHour)}\n onChange={(e) => update('cronHour', parseInt(e.target.value, 10))}\n >\n {HOURS.map((h) => <option key={h.value} value={String(h.value)}>{h.label}</option>)}\n </select>\n </div>\n <div style={{ flex: 1 }}>\n <label className=\"mobile-field-label\">Minute</label>\n <select\n className=\"mobile-input\"\n value={String(state.cronMinute)}\n onChange={(e) => update('cronMinute', parseInt(e.target.value, 10))}\n >\n {MINUTES.map((m) => <option key={m} value={String(m)}>{String(m).padStart(2, '0')}</option>)}\n </select>\n </div>\n </div>\n </>\n )}\n\n {state.type === 'interval' && (\n <div className=\"mobile-form-row\">\n <div style={{ flex: 1 }}>\n <label className=\"mobile-field-label\">Every</label>\n <input\n className=\"mobile-input\"\n type=\"number\"\n min={1}\n value={state.intervalN}\n onChange={(e) => update('intervalN', parseInt(e.target.value, 10) || 1)}\n />\n </div>\n <div style={{ flex: 1 }}>\n <label className=\"mobile-field-label\">Unit</label>\n <select\n className=\"mobile-input\"\n value={state.intervalUnit}\n onChange={(e) => update('intervalUnit', e.target.value)}\n >\n {INTERVAL_UNITS.map((u) => <option key={u.value} value={u.value}>{u.label}</option>)}\n </select>\n </div>\n </div>\n )}\n\n {state.type === 'once' && (\n <>\n <label className=\"mobile-field-label\">Run at</label>\n <input\n className=\"mobile-input\"\n type=\"datetime-local\"\n value={state.onceAt}\n onChange={(e) => update('onceAt', e.target.value)}\n />\n </>\n )}\n\n <label className=\"mobile-field-label\">Timezone</label>\n <select\n className=\"mobile-input\"\n value={TIMEZONES.includes(state.timezone) ? state.timezone : 'UTC'}\n onChange={(e) => update('timezone', e.target.value)}\n >\n {TIMEZONES.map((tz) => <option key={tz} value={tz}>{tz}</option>)}\n </select>\n\n <label className=\"mobile-field-label\">Action type</label>\n <select\n className=\"mobile-input\"\n value={state.actionType}\n onChange={(e) => update('actionType', e.target.value as Schedule['action']['type'])}\n >\n {ACTIONS.map((t) => <option key={t} value={t}>{t}</option>)}\n </select>\n\n <label className=\"mobile-field-label\">Target</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n placeholder={\n state.actionType === 'webhook'\n ? 'https://...'\n : state.actionType === 'agent'\n ? 'agent or task ref'\n : 'echo hello'\n }\n value={state.actionTarget}\n onChange={(e) => update('actionTarget', e.target.value)}\n />\n\n {state.actionType === 'agent' && (\n <>\n <label className=\"mobile-field-label\">Prompt</label>\n <textarea\n className=\"mobile-input\"\n rows={3}\n placeholder=\"What should the agent do?\"\n value={state.actionPrompt}\n onChange={(e) => update('actionPrompt', e.target.value)}\n />\n </>\n )}\n\n <fieldset className=\"mobile-budget-card\">\n <legend>Budget pre-flight</legend>\n <label className=\"mobile-checkbox-row\">\n <input\n type=\"checkbox\"\n checked={state.skipIfBudgetLow}\n onChange={(e) => update('skipIfBudgetLow', e.target.checked)}\n />\n <span>Skip if too many bg tasks are running</span>\n </label>\n <label className=\"mobile-field-label\">Cap</label>\n <input\n className=\"mobile-input\"\n type=\"number\"\n min={1}\n max={64}\n value={state.maxConcurrent}\n onChange={(e) => update('maxConcurrent', parseInt(e.target.value, 10) || 6)}\n disabled={!state.skipIfBudgetLow}\n />\n </fieldset>\n\n <label className=\"mobile-checkbox-row\">\n <input\n type=\"checkbox\"\n checked={state.enabled}\n onChange={(e) => update('enabled', e.target.checked)}\n />\n <span>Enabled</span>\n </label>\n </form>\n </MobileModal>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction formatRel(ts: string): string {\n const diff = Date.now() - new Date(ts).getTime();\n if (diff < 60000) return 'just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return new Date(ts).toLocaleDateString();\n}\n\nfunction formatNextShort(iso: string, tz?: string | null): string {\n try {\n return new Date(iso).toLocaleString(undefined, {\n timeZone: tz || undefined,\n weekday: 'short',\n hour: 'numeric',\n minute: '2-digit',\n month: 'short',\n day: 'numeric',\n });\n } catch {\n return new Date(iso).toLocaleString();\n }\n}\n\nfunction parseSimpleCron(expr: string): { minute: number; hour: number; dow: string } | null {\n if (typeof expr !== 'string') return null;\n const parts = expr.trim().split(/\\s+/);\n if (parts.length !== 5) return null;\n const [m, h, , , dow] = parts;\n const minNum = parseInt(m, 10);\n const hourNum = parseInt(h, 10);\n if (!Number.isFinite(minNum) || !Number.isFinite(hourNum)) return null;\n if (m !== String(minNum) || h !== String(hourNum)) return null;\n return {\n minute: minNum,\n hour: hourNum,\n dow: dow === '*' ? '*' : (Number.isFinite(parseInt(dow, 10)) ? dow : '*'),\n };\n}\n\nfunction parseSimpleInterval(s: string): { n: number; unit: string } | null {\n if (typeof s !== 'string') return null;\n const m = /^(\\d+)([smhd])$/i.exec(s.trim());\n if (!m) return null;\n return { n: parseInt(m[1], 10), unit: m[2].toLowerCase() };\n}\n\nfunction toDatetimeLocal(iso: string): string {\n if (!iso) return '';\n try {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n } catch {\n return '';\n }\n}\n\nfunction humanizeSchedule(s: Schedule): string {\n if (s.type !== 'cron') return s.schedule;\n const parsed = parseSimpleCron(s.schedule);\n if (!parsed) return s.schedule;\n const dow = DOWS.find((d) => d.value === parsed.dow)?.label || parsed.dow;\n const hourLabel = HOURS.find((h) => h.value === parsed.hour)?.label || `${parsed.hour}`;\n const minute = String(parsed.minute).padStart(2, '0');\n if (dow === 'Every day') return `Every day ${hourLabel} :${minute}`;\n return `Every ${dow} ${hourLabel} :${minute}`;\n}","// src/mobile/views/MobileSearchModal.tsx — fullscreen search modal for mobile.\nimport { useEffect, useRef, useState } from 'react';\nimport { Search, X, FileText, Bot, CheckSquare, Folder, Sliders } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport type { SearchResult } from '../../lib/types';\nimport { MobileModal } from '../components/MobileModal';\n\ntype Props = {\n open: boolean;\n onClose: () => void;\n onNavigate: (type: string, id: string) => void;\n};\n\nconst SCOPES = ['all', 'tasks', 'agents', 'artifacts', 'projects', 'settings'] as const;\ntype Scope = typeof SCOPES[number];\n\nconst SCOPE_ICONS: Record<string, typeof FileText> = {\n tasks: CheckSquare,\n agents: Bot,\n plans: FileText,\n projects: Folder,\n settings: Sliders,\n};\n\nexport function MobileSearchModal({ open, onClose, onNavigate }: Props) {\n const [query, setQuery] = useState('');\n const [scope, setScope] = useState<Scope>('all');\n const [results, setResults] = useState<SearchResult[]>([]);\n const [loading, setLoading] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (open) {\n setQuery('');\n setResults([]);\n requestAnimationFrame(() => inputRef.current?.focus());\n }\n }, [open]);\n\n useEffect(() => {\n if (!query.trim()) {\n setResults([]);\n return;\n }\n const timer = setTimeout(async () => {\n setLoading(true);\n try {\n const data = await api.get<{ results: SearchResult[] }>(\n `/search?q=${encodeURIComponent(query)}&scope=${scope}`,\n );\n setResults(data.results || []);\n } catch {\n setResults([]);\n } finally {\n setLoading(false);\n }\n }, 250);\n return () => clearTimeout(timer);\n }, [query, scope]);\n\n const handleResultClick = (r: SearchResult) => {\n const item = r.item as Record<string, string>;\n const id = item.id || item.slug || item.name || '';\n onNavigate(r.type, id);\n onClose();\n };\n\n const grouped = results.reduce<Record<string, SearchResult[]>>((acc, r) => {\n if (!acc[r.type]) acc[r.type] = [];\n acc[r.type].push(r);\n return acc;\n }, {});\n\n return (\n <MobileModal open={open} onClose={onClose} title=\"Search\">\n <div className=\"mobile-search-container\">\n {/* Search input */}\n <div className=\"mobile-search-input-row\">\n <Search size={16} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />\n <input\n ref={inputRef}\n className=\"mobile-search-field\"\n type=\"text\"\n placeholder=\"Search…\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n enterKeyHint=\"search\"\n onKeyDown={(e) => {\n if (e.key === 'Escape') onClose();\n if (e.key === 'Enter' && results.length > 0) {\n handleResultClick(results[0]);\n }\n }}\n />\n {query && (\n <button\n type=\"button\"\n className=\"mobile-icon-btn\"\n onClick={() => setQuery('')}\n aria-label=\"Clear\"\n >\n <X size={16} />\n </button>\n )}\n </div>\n\n {/* Scope chips */}\n <div className=\"mobile-search-scopes\">\n {SCOPES.map((s) => (\n <button\n key={s}\n type=\"button\"\n className={`mobile-scope-chip ${scope === s ? 'active' : ''}`}\n onClick={() => setScope(s)}\n >\n {s === 'all' ? 'All' : s.charAt(0).toUpperCase() + s.slice(1)}\n </button>\n ))}\n </div>\n\n {/* Results */}\n <div className=\"mobile-search-results\">\n {loading && <div className=\"mobile-search-loading\">Searching…</div>}\n\n {!loading && query && results.length === 0 && (\n <div className=\"mobile-empty\">\n <Search size={32} />\n <p>No results found</p>\n </div>\n )}\n\n {!loading && !query && (\n <div className=\"mobile-empty\">\n <p>Type to search across tasks, agents, plans, and more.</p>\n </div>\n )}\n\n {Object.entries(grouped).map(([type, items]) => {\n const Icon = SCOPE_ICONS[type] || FileText;\n return (\n <div key={type} className=\"mobile-search-group\">\n <h4 className=\"mobile-search-group-title\">\n <Icon size={12} /> {type.charAt(0).toUpperCase() + type.slice(1)} ({items.length})\n </h4>\n {items.map((r, i) => {\n const item = r.item as Record<string, string>;\n return (\n <button\n key={i}\n type=\"button\"\n className=\"mobile-search-result-item\"\n onClick={() => handleResultClick(r)}\n >\n <span className=\"mobile-search-result-title\">\n {item.title || item.name || item.slug || '(unnamed)'}\n </span>\n {item.description && (\n <span className=\"mobile-search-result-meta\">\n {item.description.slice(0, 80)}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n })}\n </div>\n </div>\n </MobileModal>\n );\n}\n","// src/mobile/views/MobileSkills.tsx — skills list with detail sheet.\nimport { useEffect, useState } from 'react';\nimport { Sparkles, RefreshCw, Power } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport type { Snapshot } from '../../lib/types';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\n\ntype Skill = {\n id: string;\n name: string;\n description: string;\n category: string;\n tags: string[];\n version: string;\n source: string;\n enabled?: boolean;\n};\n\ntype Props = {\n snapshot: Snapshot;\n onBack: () => void;\n};\n\nconst CATEGORIES = ['languages', 'framework', 'design', 'testing', 'devops', 'data', 'security', 'integration'];\n\nexport function MobileSkills({ snapshot, onBack }: Props) {\n const [skills, setSkills] = useState<Skill[]>([]);\n const [loading, setLoading] = useState(true);\n const [filter, setFilter] = useState('');\n const [categoryFilter, setCategoryFilter] = useState('');\n const [detailSkill, setDetailSkill] = useState<Skill | null>(null);\n\n const load = async () => {\n try {\n const data = await api.get<{ skills: Skill[] }>('/skills');\n setSkills(data.skills || []);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => { load(); }, []);\n\n const toggleSkill = async (id: string, enabled: boolean) => {\n try {\n await api.patch(`/skills/${encodeURIComponent(id)}`, { enabled });\n setSkills((cur) => cur.map((s) => (s.id === id ? { ...s, enabled } : s)));\n if (detailSkill?.id === id) setDetailSkill((s) => s ? { ...s, enabled } : s);\n } catch {\n // best-effort\n }\n };\n\n const filtered = skills.filter((s) => {\n if (categoryFilter && s.category !== categoryFilter) return false;\n if (filter) {\n const q = filter.toLowerCase();\n return s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q);\n }\n return true;\n });\n\n return (\n <div className=\"mobile-view\">\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search skills…\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n style={{ flex: 1 }}\n />\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => load()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n </div>\n\n <div className=\"mobile-search-scopes\">\n <button type=\"button\" className={`mobile-scope-chip ${!categoryFilter ? 'active' : ''}`} onClick={() => setCategoryFilter('')}>All</button>\n {CATEGORIES.map((c) => (\n <button key={c} type=\"button\" className={`mobile-scope-chip ${categoryFilter === c ? 'active' : ''}`} onClick={() => setCategoryFilter(c)}>\n {c}\n </button>\n ))}\n </div>\n\n {loading ? (\n <div className=\"mobile-loading\"><p>Loading…</p></div>\n ) : filtered.length === 0 ? (\n <div className=\"mobile-empty\">\n <Sparkles size={40} />\n <p>No skills found.</p>\n </div>\n ) : (\n <div className=\"mobile-card-list\">\n {filtered.map((s) => (\n <button\n key={s.id}\n type=\"button\"\n className=\"mobile-list-item mobile-list-item-interactive\"\n onClick={() => setDetailSkill(s)}\n >\n <div className=\"mobile-list-icon\"><Sparkles size={16} /></div>\n <div className=\"mobile-list-content\">\n <span className=\"mobile-list-title\">{s.name}</span>\n <span className=\"mobile-list-meta\">{s.category} · v{s.version}</span>\n </div>\n <span className={`mobile-list-badge ${s.enabled ? 'badge-on' : 'badge-off'}`}>\n {s.enabled ? 'on' : 'off'}\n </span>\n </button>\n ))}\n </div>\n )}\n\n {detailSkill && (\n <MobileBottomSheet\n open={true}\n onClose={() => setDetailSkill(null)}\n title={detailSkill.name}\n actions={\n <button\n type=\"button\"\n className={`mobile-btn ${detailSkill.enabled ? 'mobile-btn-secondary' : ''}`}\n style={{ width: '100%' }}\n onClick={() => { if (detailSkill) toggleSkill(detailSkill.id, !detailSkill.enabled); }}\n >\n <Power size={14} /> {detailSkill.enabled ? 'Disable' : 'Enable'}\n </button>\n }\n >\n <div className=\"mobile-agent-detail\">\n <p className=\"mobile-agent-detail-desc\">{detailSkill.description || 'No description.'}</p>\n <div className=\"mobile-agent-detail-meta\">\n <div className=\"mobile-task-detail-row\"><span>Category</span><span>{detailSkill.category}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Version</span><span className=\"mono\">{detailSkill.version}</span></div>\n <div className=\"mobile-task-detail-row\"><span>Source</span><span>{detailSkill.source}</span></div>\n {detailSkill.tags.length > 0 && (\n <div className=\"mobile-task-detail-row\">\n <span>Tags</span>\n <span>{detailSkill.tags.join(', ')}</span>\n </div>\n )}\n </div>\n </div>\n </MobileBottomSheet>\n )}\n </div>\n );\n}\n","// src/mobile/views/MobileTasks.tsx — kanban-style task board with task detail.\nimport { useEffect, useState } from 'react';\nimport { RefreshCw, Plus, Trash2, Archive, MessageSquare, FileText } from 'lucide-react';\nimport { api } from '../../lib/api';\nimport { formatRelative, priorityColors } from '../../lib/utils';\nimport type { Snapshot, Task } from '../../lib/types';\nimport { MobileBottomSheet } from '../components/MobileBottomSheet';\nimport { MobileModal } from '../components/MobileModal';\n\ntype Props = {\n snapshot: Snapshot;\n onRefresh: () => Promise<void>;\n selectedTaskId?: string;\n onCloseDetail?: () => void;\n // v3.6.2 — Pipeline: callbacks to open chat or artifact for a task.\n onOpenChat?: (taskId: string) => void;\n onOpenArtifact?: (artifactId: string) => void;\n};\n\nconst STATUS_ORDER = ['queued', 'doing', 'blocked', 'done'];\nconst STATUS_LABELS: Record<string, string> = {\n queued: 'Queued',\n doing: 'Doing',\n blocked: 'Blocked',\n done: 'Done',\n};\nconst NEXT_STATUS: Record<string, string> = {\n queued: 'doing',\n doing: 'done',\n blocked: 'queued',\n done: 'queued',\n};\n\ntype TaskChatMeta = { sessionId?: string; bgInstanceId?: string };\ntype TaskArtifactMeta = { artifactIds?: string[]; artifactId?: string };\n\nfunction hasTaskChat(task: Task): boolean {\n const metadata = (task.metadata ?? {}) as TaskChatMeta;\n return Boolean(metadata.sessionId || metadata.bgInstanceId);\n}\n\nfunction hasTaskArtifacts(task: Task): boolean {\n const metadata = (task.metadata ?? {}) as TaskArtifactMeta;\n return Boolean(metadata.artifactIds?.length || metadata.artifactId);\n}\n\nexport function MobileTasks({ snapshot, onRefresh, selectedTaskId, onCloseDetail, onOpenChat, onOpenArtifact }: Props) {\n const [tasks, setTasks] = useState<Task[]>(snapshot.tasks || []);\n const [loading, setLoading] = useState(!snapshot.tasks);\n const [filter, setFilter] = useState('');\n const [activeStatus, setActiveStatus] = useState<string>('queued');\n const [detailTask, setDetailTask] = useState<Task | null>(null);\n const [newTaskOpen, setNewTaskOpen] = useState(false);\n // v3.6.2 — state for the mobile artifact viewer sheet.\n const [artifactTaskId, setArtifactTaskId] = useState<string | null>(null);\n const [artifactIds, setArtifactIds] = useState<string[]>([]);\n const [artifactContent, setArtifactContent] = useState<string>('');\n const [artifactMeta, setArtifactMeta] = useState<{ name?: string; contentType?: string; size?: number } | null>(null);\n const [artifactLoading, setArtifactLoading] = useState(false);\n const [assigneeFilter, setAssigneeFilter] = useState('');\n const [sortBy, setSortBy] = useState<'updated' | 'created' | 'priority'>('updated');\n\n useEffect(() => {\n if (snapshot.tasks) {\n setTasks(snapshot.tasks);\n setLoading(false);\n }\n if (selectedTaskId) {\n const t = snapshot.tasks?.find((t) => t.id === selectedTaskId);\n if (t) setDetailTask(t);\n }\n }, [snapshot.tasks, selectedTaskId]);\n\n useEffect(() => {\n if (selectedTaskId) {\n const t = tasks.find((t) => t.id === selectedTaskId);\n if (t) setDetailTask(t);\n }\n }, [selectedTaskId, tasks]);\n\n const reload = async () => {\n try {\n const data = await api.get<Task[]>('/tasks');\n setTasks(Array.isArray(data) ? data : []);\n } catch {\n // best-effort\n } finally {\n setLoading(false);\n }\n };\n\n const moveTask = async (taskId: string, newStatus: string) => {\n setTasks((cur) =>\n cur.map((t) => (t.id === taskId ? { ...t, status: newStatus } : t)),\n );\n if (detailTask?.id === taskId) {\n setDetailTask((t) => t ? { ...t, status: newStatus } : t);\n }\n try {\n await api.patch(`/tasks/${encodeURIComponent(taskId)}/status`, { status: newStatus });\n } catch {\n await reload();\n }\n };\n\n const deleteTask = async (taskId: string) => {\n if (!confirm('Delete this task?')) return;\n try {\n await api.del(`/tasks/${encodeURIComponent(taskId)}`);\n setTasks((cur) => cur.filter((t) => t.id !== taskId));\n if (detailTask?.id === taskId) setDetailTask(null);\n } catch {\n await reload();\n }\n };\n\n const archiveTask = async (taskId: string) => {\n try {\n await api.post(`/tasks/${encodeURIComponent(taskId)}/archive`);\n setTasks((cur) => cur.filter((t) => t.id !== taskId));\n if (detailTask?.id === taskId) setDetailTask(null);\n } catch {\n // best-effort\n }\n };\n\n const filtered = tasks.filter((t) => {\n if (filter.trim()) {\n const q = filter.toLowerCase();\n if (!(t.title || '').toLowerCase().includes(q) && !(t.description || '').toLowerCase().includes(q)) {\n return false;\n }\n }\n if (assigneeFilter.trim()) {\n const targetAssignee = assigneeFilter === 'mine'\n ? snapshot.settings?.data?.defaultAgent || settingsDefaultAgent(snapshot)\n : assigneeFilter.replace(/^@/, '');\n if ((t.assignee || '').toLowerCase() !== targetAssignee.toLowerCase()) return false;\n }\n return true;\n });\n\n const sorted = [...filtered].sort((a, b) => {\n switch (sortBy) {\n case 'priority': {\n const order = { high: 0, normal: 1, low: 2 };\n return (order[a.priority as keyof typeof order] ?? 1) - (order[b.priority as keyof typeof order] ?? 1);\n }\n case 'created': return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();\n case 'updated':\n default:\n return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();\n }\n });\n\n const activeTasks = sorted.filter((t) => t.status === activeStatus);\n const agents = snapshot.agents || [];\n\n const createTask = async (title: string, description: string, assignee: string, priority: string) => {\n try {\n const task = await api.post<Task>('/tasks', { title, description, assignee: assignee || null, priority });\n setTasks((cur) => [task, ...cur]);\n setNewTaskOpen(false);\n } catch {\n // best-effort\n }\n };\n\n // v3.6.2 — Open the artifact viewer for a task's artifact.\n const openTaskArtifact = async (taskId: string) => {\n setArtifactLoading(true);\n setArtifactContent('');\n setArtifactMeta(null);\n setArtifactTaskId(taskId);\n try {\n const artR = await api.get<{ artifacts: { id: string; name?: string; contentType?: string; size?: number }[] }>(\n `/tasks/${encodeURIComponent(taskId)}/artifacts`,\n );\n const ids = (artR.artifacts || []).map((a) => a.id);\n setArtifactIds(ids);\n if (ids.length > 0) {\n const [meta, content] = await Promise.all([\n api.get<{ name?: string; contentType?: string; size?: number }>(`/artifacts/${encodeURIComponent(ids[0])}`),\n fetch(api.urlWithToken(`/artifacts/${encodeURIComponent(ids[0])}/content`)).then((r) => r.text()),\n ]);\n setArtifactMeta(meta);\n setArtifactContent(content);\n }\n } catch {\n // best-effort\n } finally {\n setArtifactLoading(false);\n }\n };\n\n if (loading) {\n return <div className=\"mobile-loading\"><p>Loading…</p></div>;\n }\n\n return (\n <div className=\"mobile-view mobile-view-tasks\">\n {/* Toolbar */}\n <div className=\"mobile-tasks-toolbar\">\n <input\n className=\"mobile-search-input\"\n type=\"text\"\n placeholder=\"Search tasks…\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n style={{ flex: 1 }}\n />\n <button type=\"button\" className=\"mobile-icon-btn\" onClick={() => reload()} aria-label=\"Refresh\">\n <RefreshCw size={16} />\n </button>\n </div>\n\n {/* Filter + Sort row */}\n <div className=\"mobile-tasks-filters\">\n <select\n className=\"mobile-filter-select\"\n value={assigneeFilter}\n onChange={(e) => setAssigneeFilter(e.target.value)}\n >\n <option value=\"\">All</option>\n <option value=\"mine\">Mine</option>\n {agents.map((a) => (\n <option key={a.name} value={`@${a.name}`}>@{a.name}</option>\n ))}\n </select>\n <select\n className=\"mobile-filter-select\"\n value={sortBy}\n onChange={(e) => setSortBy(e.target.value as 'updated' | 'created' | 'priority')}\n >\n <option value=\"updated\">Updated</option>\n <option value=\"created\">Created</option>\n <option value=\"priority\">Priority</option>\n </select>\n </div>\n\n {/* Kanban segments */}\n <div className=\"mobile-kanban-tabs\">\n {STATUS_ORDER.map((s) => {\n const count = sorted.filter((t) => t.status === s).length;\n return (\n <button\n key={s}\n type=\"button\"\n className={`mobile-kanban-tab ${activeStatus === s ? 'active' : ''}`}\n onClick={() => setActiveStatus(s)}\n >\n {STATUS_LABELS[s]} ({count})\n </button>\n );\n })}\n </div>\n\n {/* Task list */}\n <div className=\"mobile-card-list\">\n {activeTasks.length === 0 && (\n <div className=\"mobile-empty mobile-empty-compact\">\n <p>No {STATUS_LABELS[activeStatus].toLowerCase()} tasks.</p>\n <p className=\"muted\">Create one or switch columns.</p>\n </div>\n )}\n {activeTasks.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n className=\"mobile-task-card\"\n onClick={() => setDetailTask(t)}\n >\n <div\n className=\"priority-dot\"\n style={{ background: priorityColors[t.priority] || 'var(--info)' }}\n />\n <div className=\"task-content\">\n <div className=\"task-title\">{t.title}</div>\n <div className=\"task-meta\">\n {t.assignee ? `@${t.assignee}` : 'unassigned'}\n {' · '}\n {formatRelative(t.updatedAt || t.createdAt)}\n {t.dependencies?.length ? ` · ${t.dependencies.length} deps` : ''}\n </div>\n </div>\n </button>\n ))}\n </div>\n\n {/* FAB */}\n <button\n type=\"button\"\n className=\"mobile-fab\"\n onClick={() => setNewTaskOpen(true)}\n aria-label=\"Add task\"\n >\n <Plus size={24} />\n </button>\n\n {/* Task Detail Sheet */}\n <MobileBottomSheet\n open={!!detailTask}\n onClose={() => { setDetailTask(null); onCloseDetail?.(); }}\n title=\"Task Detail\"\n actions={\n detailTask && (\n <div className=\"mobile-task-detail-actions\">\n {detailTask.status !== 'done' && (\n <button\n type=\"button\"\n className=\"mobile-btn\"\n style={{ flex: 1 }}\n onClick={() => {\n if (detailTask) moveTask(detailTask.id, NEXT_STATUS[detailTask.status]);\n }}\n >\n Move to {STATUS_LABELS[NEXT_STATUS[detailTask.status] || 'queued']}\n </button>\n )}\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-secondary\"\n onClick={() => {\n if (detailTask) archiveTask(detailTask.id);\n }}\n >\n <Archive size={14} /> Archive\n </button>\n {/* v3.6.2 — Pipeline: Chat button */}\n {hasTaskChat(detailTask) && (\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-secondary\"\n onClick={() => { onOpenChat?.(detailTask.id); }}\n >\n <MessageSquare size={14} /> Chat\n </button>\n )}\n {/* v3.6.2 — Pipeline: Artifact button */}\n {hasTaskArtifacts(detailTask) && (\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-secondary\"\n onClick={() => { openTaskArtifact(detailTask.id); }}\n >\n <FileText size={14} /> Artifact\n </button>\n )}\n <button\n type=\"button\"\n className=\"mobile-btn mobile-btn-danger\"\n onClick={() => {\n if (detailTask) deleteTask(detailTask.id);\n }}\n >\n <Trash2 size={14} />\n </button>\n </div>\n )\n }\n >\n {detailTask && (\n <div className=\"mobile-task-detail\">\n <div className=\"mobile-task-detail-header\">\n <span className={`mobile-task-status-badge status-${detailTask.status}`}>\n {STATUS_LABELS[detailTask.status] || detailTask.status}\n </span>\n <span className={`mobile-task-priority priority-${detailTask.priority}`}>\n {detailTask.priority}\n </span>\n </div>\n <h3 className=\"mobile-task-detail-title\">{detailTask.title}</h3>\n {detailTask.description && (\n <p className=\"mobile-task-detail-desc\">{detailTask.description}</p>\n )}\n <div className=\"mobile-task-detail-meta\">\n {detailTask.assignee && (\n <div className=\"mobile-task-detail-row\">\n <span>Assignee</span>\n <span>@{detailTask.assignee}</span>\n </div>\n )}\n {detailTask.dueDate && (\n <div className=\"mobile-task-detail-row\">\n <span>Due</span>\n <span>{new Date(detailTask.dueDate).toLocaleDateString()}</span>\n </div>\n )}\n {detailTask.timeSpent != null && (\n <div className=\"mobile-task-detail-row\">\n <span>Time spent</span>\n <span>{Math.round(detailTask.timeSpent / 60000)}min</span>\n </div>\n )}\n {detailTask.dependencies?.length ? (\n <div className=\"mobile-task-detail-row\">\n <span>Dependencies</span>\n <span>{detailTask.dependencies.length}</span>\n </div>\n ) : null}\n {detailTask.comments?.length ? (\n <div className=\"mobile-task-detail-row\">\n <span>Comments</span>\n <span>{detailTask.comments.length}</span>\n </div>\n ) : null}\n <div className=\"mobile-task-detail-row\">\n <span>Created</span>\n <span>{formatRelative(detailTask.createdAt)}</span>\n </div>\n <div className=\"mobile-task-detail-row\">\n <span>Updated</span>\n <span>{formatRelative(detailTask.updatedAt)}</span>\n </div>\n </div>\n </div>\n )}\n </MobileBottomSheet>\n\n {/* New Task Modal */}\n <NewTaskModal\n open={newTaskOpen}\n onClose={() => setNewTaskOpen(false)}\n onCreate={createTask}\n agents={agents}\n />\n\n {/* v3.6.2 — Mobile artifact viewer sheet */}\n <MobileBottomSheet\n open={!!artifactTaskId}\n onClose={() => { setArtifactTaskId(null); setArtifactContent(''); setArtifactMeta(null); }}\n title={artifactMeta?.name || 'Artifact'}\n actions={\n artifactIds.length > 1 ? (\n <div className=\"mobile-task-detail-actions\">\n {artifactIds.map((id, i) => (\n <button\n key={id}\n type=\"button\"\n className=\"mobile-btn mobile-btn-secondary\"\n style={{ flex: 1 }}\n onClick={() => {\n // Reload for the selected artifact\n setArtifactLoading(true);\n Promise.all([\n api.get<{ name?: string; contentType?: string; size?: number }>(`/artifacts/${encodeURIComponent(id)}`),\n fetch(api.urlWithToken(`/artifacts/${encodeURIComponent(id)}/content`)).then((r) => r.text()),\n ]).then(([meta, content]) => {\n setArtifactMeta(meta);\n setArtifactContent(content);\n setArtifactLoading(false);\n }).catch(() => setArtifactLoading(false));\n }}\n >\n Artifact {i + 1}\n </button>\n ))}\n </div>\n ) : null\n }\n >\n <div className=\"mobile-artifact-viewer\">\n {artifactLoading ? (\n <div className=\"mobile-loading\"><p>Loading artifact…</p></div>\n ) : artifactContent ? (\n <iframe\n srcDoc={artifactContent}\n className=\"mobile-artifact-iframe\"\n sandbox=\"allow-scripts allow-forms allow-popups allow-same-origin\"\n title={artifactMeta?.name || 'Artifact'}\n loading=\"lazy\"\n />\n ) : (\n <div className=\"mobile-empty\">\n <FileText size={32} />\n <p className=\"muted\">No artifact content available.</p>\n </div>\n )}\n </div>\n </MobileBottomSheet>\n </div>\n );\n}\n\nfunction settingsDefaultAgent(snapshot: Snapshot): string {\n return snapshot.settings?.data?.defaultAgent || 'odin';\n}\n\nfunction NewTaskModal({\n open,\n onClose,\n onCreate,\n agents,\n}: {\n open: boolean;\n onClose: () => void;\n onCreate: (title: string, description: string, assignee: string, priority: string) => void;\n agents: Snapshot['agents'];\n}) {\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [assignee, setAssignee] = useState('');\n const [priority, setPriority] = useState('normal');\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!title.trim()) return;\n onCreate(title.trim(), description.trim(), assignee, priority);\n setTitle('');\n setDescription('');\n setAssignee('');\n setPriority('normal');\n };\n\n return (\n <MobileModal open={open} onClose={onClose} title=\"New Task\" actions={\n <button type=\"submit\" form=\"new-task-form\" className=\"mobile-btn\" style={{ width: '100%' }}>\n <Plus size={14} /> Create Task\n </button>\n }>\n <form id=\"new-task-form\" onSubmit={handleSubmit} className=\"mobile-task-form\">\n <label className=\"mobile-field-label\">Title *</label>\n <input\n className=\"mobile-input\"\n type=\"text\"\n placeholder=\"Task title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n autoFocus\n required\n />\n <label className=\"mobile-field-label\">Description</label>\n <textarea\n className=\"mobile-input\"\n rows={3}\n placeholder=\"Optional description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n <label className=\"mobile-field-label\">Assignee</label>\n <select\n className=\"mobile-input\"\n value={assignee}\n onChange={(e) => setAssignee(e.target.value)}\n >\n <option value=\"\">Unassigned</option>\n {agents.map((a) => (\n <option key={a.name} value={a.name}>@{a.name}</option>\n ))}\n </select>\n <label className=\"mobile-field-label\">Priority</label>\n <select\n className=\"mobile-input\"\n value={priority}\n onChange={(e) => setPriority(e.target.value)}\n >\n <option value=\"low\">Low</option>\n <option value=\"normal\">Normal</option>\n <option value=\"high\">High</option>\n </select>\n </form>\n </MobileModal>\n );\n}\n","// src/MobileApp.tsx — mobile root with state-based routing + stack navigation.\nimport React, { useCallback, useEffect, useMemo, useState, Suspense } from 'react';\nimport {\n Activity,\n CheckSquare,\n Grid,\n MessageSquare,\n Settings as SettingsIcon,\n} from 'lucide-react';\nimport { Spinner } from './components/Spinner';\nimport { api } from './lib/api';\nimport type { Notification, Settings, Snapshot, WsMessage } from './lib/types';\nimport { applyTheme, applyThemeTokens } from './lib/types';\nimport { Ws } from './lib/ws';\nimport { MobileBottomNav, type MobileTab } from './mobile/MobileBottomNav';\nimport { MobileLayout, type DrawerTab } from './components/MobileLayout';\nimport { MobileActivity } from './mobile/views/MobileActivity';\nimport { MobileAgents } from './mobile/views/MobileAgents';\nimport { MobileConfig } from './mobile/views/MobileConfig';\nimport { MobileHistory } from './mobile/views/MobileHistory';\nimport { MobileMods } from './mobile/views/MobileMods';\nimport { MobileMore } from './mobile/views/MobileMore';\nimport { MobileArtifactCanvas } from './mobile/views/MobileArtifactCanvas';\nimport { MobileArtifacts } from './mobile/views/MobileArtifacts';\nimport { MobileSchedules } from './mobile/views/MobileSchedules';\nimport { MobileSearchModal } from './mobile/views/MobileSearchModal';\nimport { MobileSkills } from './mobile/views/MobileSkills';\nimport { MobileTasks } from './mobile/views/MobileTasks';\n\n// Lazy-loaded mobile-specific variants (code-split from main bundle)\nconst MobileSettings = React.lazy(() => import('./mobile/MobileSettings').then(m => ({ default: m.MobileSettings })));\nconst MobileChat = React.lazy(() => import('./mobile/MobileChat').then(m => ({ default: m.MobileChat })));\n\ntype MainTabId = 'activity' | 'chat' | 'tasks' | 'settings' | 'more';\n\nexport type MobileView =\n | { id: MainTabId }\n | { id: 'artifacts?' }\n | { id: 'agents' }\n | { id: 'skills' }\n | { id: 'mods' }\n | { id: 'schedules' }\n | { id: 'history' }\n | { id: 'config' }\n | { id: 'artifacts?-detail'; slug: string }\n | { id: 'agent-detail'; name: string }\n | { id: 'task-detail'; taskId: string };\n\nconst MAIN_TABS: MobileTab[] = [\n { id: 'activity', label: 'Activity', icon: Activity },\n { id: 'chat', label: 'Chat', icon: MessageSquare },\n { id: 'tasks', label: 'Tasks', icon: CheckSquare },\n { id: 'settings', label: 'Settings', icon: SettingsIcon },\n { id: 'more', label: 'More', icon: Grid },\n];\n\nfunction parseNotificationTarget(notification: Notification): MobileView | null {\n const meta = notification.meta ?? {};\n const taskId = typeof meta.taskId === 'string' ? meta.taskId : null;\n const slug = typeof meta.slug === 'string' ? meta.slug : null;\n const agentName = typeof meta.agent === 'string' ? meta.agent : null;\n if (taskId) return { id: 'task-detail', taskId };\n if (slug) return { id: 'artifacts?-detail', slug };\n if (agentName) return { id: 'agent-detail', name: agentName };\n\n const link = notification.link ?? '';\n if (link.startsWith('/artifacts?/')) return { id: 'artifacts?-detail', slug: decodeURIComponent(link.slice('/artifacts?/'.length)) };\n if (link.startsWith('/tasks/')) return { id: 'task-detail', taskId: decodeURIComponent(link.slice('/tasks/'.length)) };\n if (link.startsWith('/agents/')) return { id: 'agent-detail', name: decodeURIComponent(link.slice('/agents/'.length)) };\n\n return null;\n}\n\nexport function MobileApp() {\n const [activeTab, setActiveTab] = useState<MainTabId>('activity');\n const [stack, setStack] = useState<MobileView[]>([]);\n const [searchOpen, setSearchOpen] = useState(false);\n const [drawerOpen, setDrawerOpen] = useState(false);\n const [pendingChatTaskId, setPendingChatTaskId] = useState<string | null>(null);\n const [snapshot, setSnapshot] = useState<Snapshot | null>(null);\n const [settings, setSettings] = useState<Settings | null>(null);\n const [bootError, setBootError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [wsEpoch, setWsEpoch] = useState(0);\n\n const currentView = useMemo<MobileView>(() => {\n return stack[stack.length - 1] ?? { id: activeTab };\n }, [activeTab, stack]);\n\n const pushView = useCallback((view: MobileView) => {\n setStack((cur) => [...cur, view]);\n }, []);\n\n const popView = useCallback(() => {\n setStack((cur) => cur.slice(0, -1));\n }, []);\n\n const goToTab = useCallback((id: MainTabId) => {\n setStack([]);\n setActiveTab(id);\n }, []);\n\n const refreshSnapshot = useCallback(async (): Promise<void> => {\n try {\n const next = await api.get<Snapshot>('/snapshot');\n setSnapshot(next);\n if (next.settings?.data) {\n setSettings(next.settings.data);\n }\n setBootError(null);\n } catch (err) {\n setBootError((err as Error).message || 'Dashboard server unreachable.');\n throw err;\n }\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n\n Promise.all([\n api.get<Snapshot>('/snapshot').catch(() => null),\n api.get<{ data: Settings }>('/settings').catch(() => null),\n api.probeAuthStatus().catch(() => null),\n ])\n .then(([snap, settingsResponse]) => {\n if (cancelled) return;\n const nextSettings = settingsResponse?.data ?? snap?.settings?.data ?? null;\n if (snap) setSnapshot(snap);\n if (nextSettings) setSettings(nextSettings);\n if (!snap && !nextSettings) {\n setBootError('Dashboard server unreachable.');\n } else {\n setBootError(null);\n }\n })\n .catch((err) => {\n if (!cancelled) setBootError((err as Error).message || 'Dashboard server unreachable.');\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n if (!settings?.theme) return;\n applyTheme(settings.theme);\n applyThemeTokens(settings.theme);\n }, [settings?.theme]);\n\n useEffect(() => {\n if (settings?.theme?.mode !== 'system') return;\n const mql = window.matchMedia('(prefers-color-scheme: light)');\n const onChange = () => {\n if (!settings?.theme) return;\n applyTheme(settings.theme);\n applyThemeTokens(settings.theme);\n };\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [settings?.theme]);\n\n useEffect(() => {\n const reconnect = () => {\n setWsEpoch((cur) => cur + 1);\n refreshSnapshot().catch(() => undefined);\n };\n const onVisibilityChange = () => {\n if (document.visibilityState === 'visible') reconnect();\n };\n\n window.addEventListener('online', reconnect);\n window.addEventListener('pageshow', reconnect);\n document.addEventListener('visibilitychange', onVisibilityChange);\n\n return () => {\n window.removeEventListener('online', reconnect);\n window.removeEventListener('pageshow', reconnect);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n };\n }, [refreshSnapshot]);\n\n useEffect(() => {\n const ws = new Ws();\n const offMessage = ws.on((msg: WsMessage) => {\n if (msg.type === 'snapshot' && 'data' in msg && msg.data) {\n setSnapshot((cur) => ({ ...(cur ?? ({} as Snapshot)), ...msg.data }));\n } else if (msg.type === 'change') {\n refreshSnapshot().catch(() => undefined);\n } else if (msg.type === 'tasks:change') {\n const nextTask = msg.task;\n setSnapshot((cur) => {\n if (!cur) return cur;\n const tasks = (cur.tasks || []).map((task) => (task.id === nextTask.id ? nextTask : task));\n const exists = tasks.some((task) => task.id === nextTask.id);\n return { ...cur, tasks: exists ? tasks : [nextTask, ...tasks] };\n });\n } else if (msg.type === 'tasks:delete') {\n setSnapshot((cur) => (\n cur\n ? { ...cur, tasks: (cur.tasks || []).filter((task) => task.id !== msg.id) }\n : cur\n ));\n } else if (msg.type === 'settings:change') {\n if (msg.settings) setSettings(msg.settings);\n } else if (\n msg.type === 'project:change' ||\n msg.type === 'agents:change' ||\n msg.type === 'schedules:change' ||\n msg.type === 'artifact:change'\n ) {\n refreshSnapshot().catch(() => undefined);\n } else if (msg.type === 'agent:status' || msg.type === 'agent:restarted') {\n setSnapshot((cur) => {\n if (!cur) return cur;\n const agents = (cur.agents || []).map((agent) => (\n agent.name === msg.agent.name ? msg.agent : agent\n ));\n return { ...cur, agents };\n });\n }\n });\n\n return () => {\n offMessage();\n ws.close();\n };\n }, [refreshSnapshot, wsEpoch]);\n\n const handleTabChange = useCallback((id: string) => {\n if (id === 'activity' || id === 'chat' || id === 'tasks' || id === 'settings' || id === 'more') {\n goToTab(id);\n }\n }, [goToTab]);\n\n const handleNavigate = useCallback((type: string, id: string, meta?: Record<string, unknown> | null) => {\n if (type === 'notification') {\n const target = parseNotificationTarget({\n id: '',\n ts: '',\n severity: 'info',\n message: '',\n source: '',\n link: id || null,\n meta: meta ?? null,\n });\n if (target) {\n pushView(target);\n }\n return;\n }\n\n if (type === 'task' && id) {\n pushView({ id: 'task-detail', taskId: id });\n return;\n }\n if (type === 'artifacts?' && id) {\n pushView({ id: 'artifacts?-detail', slug: id });\n return;\n }\n if (type === 'agent' && id) {\n pushView({ id: 'agent-detail', name: id });\n return;\n }\n if (type === 'project') {\n goToTab('activity');\n return;\n }\n if (type === 'setting') {\n goToTab('settings');\n }\n }, [goToTab, pushView]);\n\n const renderView = (): React.ReactNode => {\n if (!snapshot || !settings) return null;\n\n if (stack.length > 0) {\n const stackedView = stack[stack.length - 1];\n switch (stackedView.id) {\n case 'artifacts?':\n return <MobileArtifacts snapshot={snapshot} onBack={popView} onOpenArtifact={(slug) => pushView({ id: 'artifacts?-detail', slug })} />;\n case 'agents':\n return <MobileAgents snapshot={snapshot} onBack={popView} onOpenAgent={(name) => pushView({ id: 'agent-detail', name })} onRefresh={refreshSnapshot} />;\n case 'skills':\n return <MobileSkills snapshot={snapshot} onBack={popView} />;\n case 'mods':\n return <MobileMods snapshot={snapshot} onBack={popView} />;\n case 'schedules':\n return <MobileSchedules snapshot={snapshot} onBack={popView} />;\n case 'history':\n return <MobileHistory onBack={popView} />;\n case 'config':\n return <MobileConfig onBack={popView} />;\n case 'artifacts?-detail':\n return <MobileArtifactCanvas slug={stackedView.slug} onBack={popView} />;\n case 'agent-detail':\n return (\n <MobileAgents\n snapshot={snapshot}\n onBack={popView}\n onOpenAgent={() => undefined}\n onRefresh={refreshSnapshot}\n selectedAgent={stackedView.name}\n />\n );\n case 'task-detail':\n return (\n <MobileTasks\n snapshot={snapshot}\n onRefresh={refreshSnapshot}\n selectedTaskId={stackedView.taskId}\n onCloseDetail={popView}\n onOpenChat={(taskId) => {\n setPendingChatTaskId(taskId);\n goToTab('chat');\n }}\n />\n );\n default:\n return null;\n }\n }\n\n switch (activeTab) {\n case 'activity':\n return <MobileActivity snapshot={snapshot} onRefresh={refreshSnapshot} />;\n case 'chat':\n return (\n <Suspense fallback={<div className=\"mobile-loading\"><Spinner size=\"lg\" /></div>}>\n <MobileChat\n snapshot={snapshot}\n settings={settings}\n initialTaskId={pendingChatTaskId}\n onClearTaskId={() => setPendingChatTaskId(null)}\n />\n </Suspense>\n );\n case 'tasks':\n return (\n <MobileTasks\n snapshot={snapshot}\n onRefresh={refreshSnapshot}\n onOpenChat={(taskId) => {\n setPendingChatTaskId(taskId);\n goToTab('chat');\n }}\n />\n );\n case 'settings':\n return (\n <Suspense fallback={<div className=\"mobile-loading\"><Spinner size=\"lg\" /></div>}>\n <MobileSettings settings={settings} snapshot={snapshot} onRefresh={refreshSnapshot} />\n </Suspense>\n );\n case 'more':\n return (\n <MobileMore\n snapshot={snapshot}\n onNavigate={(id) => {\n if (id === 'artifacts?' || id === 'agents' || id === 'skills' || id === 'mods' || id === 'schedules' || id === 'history' || id === 'config') {\n pushView({ id });\n }\n }}\n />\n );\n default:\n return null;\n }\n };\n\n if (loading) {\n return (\n <div className=\"mobile-loading\">\n <Spinner size=\"lg\" />\n <p>Loading Bizar…</p>\n </div>\n );\n }\n\n if (bootError || !snapshot || !settings) {\n return (\n <div className=\"mobile-loading\">\n <h2>Dashboard unavailable</h2>\n <p>{bootError || 'Dashboard server unreachable.'}</p>\n <p className=\"muted\">Make sure the Bizar dashboard server is running.</p>\n </div>\n );\n }\n\n return (\n <>\n <MobileLayout\n activeTab={activeTab}\n onTabChange={handleTabChange}\n tabs={MAIN_TABS}\n fullTabs={MAIN_TABS as DrawerTab[]}\n onExitSettings={() => goToTab('activity')}\n onSearch={() => setSearchOpen(true)}\n >\n {stack.length > 0 && (\n <button type=\"button\" className=\"mobile-back-btn\" onClick={popView} aria-label=\"Go back\">\n ← Back\n </button>\n )}\n {renderView()}\n </MobileLayout>\n\n <MobileSearchModal open={searchOpen} onClose={() => setSearchOpen(false)} onNavigate={handleNavigate} />\n </>\n );\n}\n"],"file":"assets/mobile-layout-BjkoHh1W.js"}
|