agent-hustle-demo 1.0.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/README.md +429 -0
- package/dist/HustleChat-BC9wvWVA.d.ts +90 -0
- package/dist/HustleChat-BcrKkkyn.d.cts +90 -0
- package/dist/browser/hustle-react.js +14854 -0
- package/dist/browser/hustle-react.js.map +1 -0
- package/dist/components/index.cjs +3141 -0
- package/dist/components/index.cjs.map +1 -0
- package/dist/components/index.d.cts +20 -0
- package/dist/components/index.d.ts +20 -0
- package/dist/components/index.js +3112 -0
- package/dist/components/index.js.map +1 -0
- package/dist/hooks/index.cjs +845 -0
- package/dist/hooks/index.cjs.map +1 -0
- package/dist/hooks/index.d.cts +6 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.js +838 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hustle-Kj0X8qXC.d.cts +193 -0
- package/dist/hustle-Kj0X8qXC.d.ts +193 -0
- package/dist/index-ChUsRBwL.d.ts +152 -0
- package/dist/index-DE1N7C3W.d.cts +152 -0
- package/dist/index-DuPFrMZy.d.cts +214 -0
- package/dist/index-kFIdHjNw.d.ts +214 -0
- package/dist/index.cjs +3746 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +271 -0
- package/dist/index.d.ts +271 -0
- package/dist/index.js +3697 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/index.cjs +844 -0
- package/dist/providers/index.cjs.map +1 -0
- package/dist/providers/index.d.cts +5 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +838 -0
- package/dist/providers/index.js.map +1 -0
- package/package.json +80 -0
- package/src/components/AuthStatus.tsx +352 -0
- package/src/components/ConnectButton.tsx +421 -0
- package/src/components/HustleChat.tsx +1273 -0
- package/src/components/MarkdownContent.tsx +431 -0
- package/src/components/index.ts +15 -0
- package/src/hooks/index.ts +40 -0
- package/src/hooks/useEmblemAuth.ts +27 -0
- package/src/hooks/useHustle.ts +36 -0
- package/src/hooks/usePlugins.ts +135 -0
- package/src/index.ts +142 -0
- package/src/plugins/index.ts +48 -0
- package/src/plugins/migrateFun.ts +211 -0
- package/src/plugins/predictionMarket.ts +411 -0
- package/src/providers/EmblemAuthProvider.tsx +319 -0
- package/src/providers/HustleProvider.tsx +540 -0
- package/src/providers/index.ts +6 -0
- package/src/styles/index.ts +2 -0
- package/src/styles/tokens.ts +447 -0
- package/src/types/auth.ts +85 -0
- package/src/types/hustle.ts +217 -0
- package/src/types/index.ts +49 -0
- package/src/types/plugin.ts +180 -0
- package/src/utils/index.ts +122 -0
- package/src/utils/pluginRegistry.ts +375 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/providers/EmblemAuthProvider.tsx","../../src/utils/pluginRegistry.ts","../../src/hooks/usePlugins.ts","../../src/providers/HustleProvider.tsx"],"names":["existingSession","useState","useEffect","useCallback","createContext","useRef","value","error","jsx","useContext"],"mappings":";;;;;AAsBA,IAAI,iBAAA,GAA0C,IAAA;AAC9C,IAAI,iBAAA,GAAoB,KAAA;AAKxB,IAAM,iBAAA,GAAoB,cAAkD,MAAS,CAAA;AAmB9E,SAAS,kBAAA,CAAmB;AAAA,EACjC,QAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA,GAAQ;AACV,CAAA,EAA4B;AAE1B,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA6B,IAAI,CAAA;AAC/D,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAA2B,IAAI,CAAA;AACjE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA+B,iBAAiB,CAAA;AAG9E,EAAA,MAAM,WAAA,GAAc,OAAO,KAAK,CAAA;AAGhC,EAAA,MAAM,GAAA,GAAM,WAAA;AAAA,IACV,CAAC,YAAoB,IAAA,KAAoB;AACvC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,aAAA,EAAgB,OAAO,CAAA,CAAA,EAAI,GAAG,IAAI,CAAA;AAAA,MAChD;AAAA,IACF,CAAA;AAAA,IACA,CAAC,KAAK;AAAA,GACR;AAKA,EAAA,MAAM,cAAA,GAAiB,WAAA;AAAA,IACrB,OAAO,GAAA,KAAuB;AAC5B,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,YAAA,EAAa;AACpC,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,YAAA,CAAa,IAAI,CAAA;AACjB,UAAA,GAAA,CAAI,sBAAsB,IAAI,CAAA;AAAA,QAChC;AAAA,MACF,SAAS,GAAA,EAAK;AACZ,QAAA,GAAA,CAAI,+BAA+B,GAAG,CAAA;AAAA,MACxC;AAAA,IACF,CAAA;AAAA,IACA,CAAC,GAAG;AAAA,GACN;AAKA,EAAA,MAAM,iBAAA,GAAoB,WAAA;AAAA,IACxB,CAAC,YAAyB,GAAA,KAAuB;AAC/C,MAAA,GAAA,CAAI,2BAA2B,UAAU,CAAA;AACzC,MAAA,UAAA,CAAW,UAAU,CAAA;AACrB,MAAA,kBAAA,CAAmB,IAAI,CAAA;AACvB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,cAAA,CAAe,GAAG,CAAA;AAAA,IACpB,CAAA;AAAA,IACA,CAAC,KAAK,cAAc;AAAA,GACtB;AAKA,EAAA,MAAM,eAAA,GAAkB,WAAA;AAAA,IACtB,CAAC,GAAA,KAAe;AACd,MAAA,GAAA,CAAI,eAAe,GAAG,CAAA;AACtB,MAAA,QAAA,CAAS,GAAG,CAAA;AACZ,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,IACjB,CAAA;AAAA,IACA,CAAC,GAAG;AAAA,GACN;AAKA,EAAA,MAAM,oBAAA,GAAuB,YAAY,MAAM;AAC7C,IAAA,GAAA,CAAI,iBAAiB,CAAA;AACrB,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,IAAA,YAAA,CAAa,IAAI,CAAA;AAAA,EACnB,CAAA,EAAG,CAAC,GAAG,CAAC,CAAA;AAKR,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,IAAI,WAAA,CAAY,OAAA,IAAW,iBAAA,IAAqB,iBAAA,EAAmB;AACjE,MAAA,IAAI,iBAAA,IAAqB,CAAC,OAAA,EAAS;AACjC,QAAA,UAAA,CAAW,iBAAiB,CAAA;AAE5B,QAAA,MAAMA,gBAAAA,GAAkB,kBAAkB,UAAA,EAAW;AACrD,QAAA,IAAIA,gBAAAA,EAAiB;AACnB,UAAA,iBAAA,CAAkBA,kBAAiB,iBAAiB,CAAA;AAAA,QACtD;AAAA,MACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,IAAA,iBAAA,GAAoB,IAAA;AACpB,IAAA,GAAA,CAAI,gCAAgC,KAAK,CAAA;AAGzC,IAAA,MAAM,GAAA,GAAM,IAAI,aAAA,CAAc;AAAA,MAC5B,KAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,SAAA,EAAW,CAAC,UAAA,KAAe;AACzB,QAAA,iBAAA,CAAkB,YAA2B,GAAG,CAAA;AAAA,MAClD,CAAA;AAAA,MACA,OAAA,EAAS,CAAC,GAAA,KAAQ;AAChB,QAAA,eAAA,CAAgB,GAAG,CAAA;AAAA,MACrB;AAAA,KACD,CAAA;AAGD,IAAA,iBAAA,GAAoB,GAAA;AACpB,IAAA,iBAAA,GAAoB,KAAA;AACpB,IAAA,UAAA,CAAW,GAAG,CAAA;AAGd,IAAA,MAAM,eAAA,GAAkB,IAAI,UAAA,EAAW;AACvC,IAAA,IAAI,eAAA,EAAiB;AACnB,MAAA,GAAA,CAAI,wBAAwB,CAAA;AAC5B,MAAA,iBAAA,CAAkB,iBAAgC,GAAG,CAAA;AAAA,IACvD;AAGA,IAAA,MAAM,mBAAA,GAAsB,CAAC,cAAA,KAAuC;AAClE,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,UAAA,CAAW,cAAc,CAAA;AACzB,QAAA,kBAAA,CAAmB,IAAI,CAAA;AAAA,MACzB,CAAA,MAAO;AACL,QAAA,oBAAA,EAAqB;AAAA,MACvB;AAAA,IACF,CAAA;AAEA,IAAA,GAAA,CAAI,EAAA,CAAG,WAAW,mBAAmB,CAAA;AACrC,IAAA,GAAA,CAAI,EAAA,CAAG,kBAAkB,oBAAoB,CAAA;AAG7C,IAAA,OAAO,MAAM;AACX,MAAA,GAAA,CAAI,GAAA,CAAI,WAAW,mBAAmB,CAAA;AACtC,MAAA,GAAA,CAAI,GAAA,CAAI,kBAAkB,oBAAoB,CAAA;AAAA,IAChD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAU,KAAK,iBAAA,EAAmB,eAAA,EAAiB,oBAAA,EAAsB,OAAO,CAAC,CAAA;AAKpG,EAAA,MAAM,aAAA,GAAgB,YAAY,YAAY;AAC5C,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,QAAA,CAAS,IAAI,KAAA,CAAM,0BAA0B,CAAC,CAAA;AAC9C,MAAA;AAAA,IACF;AAEA,IAAA,GAAA,CAAI,oBAAoB,CAAA;AACxB,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AAEb,IAAA,IAAI;AACF,MAAA,MAAM,QAAQ,aAAA,EAAc;AAAA,IAE9B,SAAS,GAAA,EAAK;AACZ,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,QAAA,CAAS,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,2BAA2B,CAAC,CAAA;AAAA,IAC9E;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,GAAG,CAAC,CAAA;AAKjB,EAAA,MAAM,MAAA,GAAS,YAAY,MAAM;AAC/B,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,GAAA,CAAI,aAAa,CAAA;AACjB,IAAA,OAAA,CAAQ,MAAA,EAAO;AACf,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,CAAC,OAAA,EAAS,GAAG,CAAC,CAAA;AAKjB,EAAA,MAAM,cAAA,GAAiB,YAAY,YAAyC;AAC1E,IAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,IAAA,GAAA,CAAI,oBAAoB,CAAA;AACxB,IAAA,IAAI;AACF,MAAA,MAAM,gBAAA,GAAmB,MAAM,OAAA,CAAQ,cAAA,EAAe;AACtD,MAAA,IAAI,gBAAA,EAAkB;AACpB,QAAA,UAAA,CAAW,gBAA+B,CAAA;AAC1C,QAAA,kBAAA,CAAmB,IAAI,CAAA;AACvB,QAAA,OAAO,gBAAA;AAAA,MACT;AACA,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,GAAA,CAAI,8BAA8B,GAAG,CAAA;AACrC,MAAA,QAAA,CAAS,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,2BAA2B,CAAC,CAAA;AAC5E,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,GAAG,CAAC,CAAA;AAGjB,EAAA,MAAM,OAAA,GAAU,OAAA,EAAS,IAAA,EAAM,OAAA,IAAW,IAAA;AAC1C,EAAA,MAAM,aAAA,GAAgB,OAAA,EAAS,IAAA,EAAM,UAAA,IAAc,IAAA;AAGnD,EAAA,MAAM,KAAA,GAAgC;AAAA;AAAA,IAEpC,OAAA;AAAA,IACA,eAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA;AAAA,IAGA,OAAA;AAAA,IACA,aAAA;AAAA;AAAA,IAGA,aAAA;AAAA,IACA,MAAA;AAAA,IACA,cAAA;AAAA;AAAA,IAGA;AAAA,GACF;AAEA,EAAA,uBACE,GAAA,CAAC,iBAAA,CAAkB,QAAA,EAAlB,EAA2B,OACzB,QAAA,EACH,CAAA;AAEJ;AAiBO,SAAS,aAAA,GAAwC;AACtD,EAAA,MAAM,OAAA,GAAU,WAAW,iBAAiB,CAAA;AAC5C,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,MAAM,yDAAyD,CAAA;AAAA,EAC3E;AACA,EAAA,OAAO,OAAA;AACT;AAKO,SAAS,YAAA,GAAqB;AACnC,EAAA,iBAAA,GAAoB,IAAA;AACpB,EAAA,iBAAA,GAAoB,KAAA;AACtB;;;AC9RA,IAAM,WAAA,GAAc,gBAAA;AAEpB,SAAS,mBAAmB,UAAA,EAA4B;AACtD,EAAA,OAAO,uBAAuB,UAAU,CAAA,CAAA;AAC1C;AAWA,SAAS,kBAAkB,EAAA,EAAqC;AAC9D,EAAA,OAAO,GAAG,QAAA,EAAS;AACrB;AAQA,SAAS,oBAAoB,IAAA,EAA4B;AAIvD,EAAA,IAAI;AAEF,IAAA,OAAO,IAAA,CAAK,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACzB,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,KAAA,CAAM,4CAA4C,GAAG,CAAA;AAE7D,IAAA,OAAO,aAAa,EAAE,KAAA,EAAO,gCAAA,EAAkC,IAAA,EAAK,CAAA;AAAA,EACtE;AACF;AAQA,SAAS,gBAAmD,IAAA,EAAiB;AAC3E,EAAA,IAAI;AAEF,IAAA,OAAO,IAAA,CAAK,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACzB,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,CAAQ,KAAA,CAAM,wCAAwC,GAAG,CAAA;AACzD,IAAA,QAAQ,MAAM;AAAA,IAAC,CAAA;AAAA,EACjB;AACF;AAKA,SAAS,oBAAA,CACP,OACA,SAAA,EAC4B;AAC5B,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAC;AAEpB,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,IAC1B,GAAG,IAAA;AAAA,IACH,YAAA,EAAc,SAAA,GAAY,IAAA,CAAK,IAAI,CAAA,GAC/B,kBAAkB,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC,CAAA,GACtC;AAAA,GACN,CAAE,CAAA;AACJ;AAKA,SAAS,eAAe,KAAA,EAA6D;AACnF,EAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AAEnB,EAAA,MAAM,aAA8B,EAAC;AAErC,EAAA,IAAI,MAAM,UAAA,EAAY;AACpB,IAAA,UAAA,CAAW,cAAA,GAAiB,iBAAA,CAAkB,KAAA,CAAM,UAAU,CAAA;AAAA,EAChE;AACA,EAAA,IAAI,MAAM,aAAA,EAAe;AACvB,IAAA,UAAA,CAAW,iBAAA,GAAoB,iBAAA,CAAkB,KAAA,CAAM,aAAa,CAAA;AAAA,EACtE;AACA,EAAA,IAAI,MAAM,aAAA,EAAe;AACvB,IAAA,UAAA,CAAW,iBAAA,GAAoB,iBAAA,CAAkB,KAAA,CAAM,aAAa,CAAA;AAAA,EACtE;AACA,EAAA,IAAI,MAAM,OAAA,EAAS;AACjB,IAAA,UAAA,CAAW,WAAA,GAAc,iBAAA,CAAkB,KAAA,CAAM,OAAO,CAAA;AAAA,EAC1D;AAEA,EAAA,OAAO,OAAO,IAAA,CAAK,UAAU,CAAA,CAAE,MAAA,GAAS,IAAI,UAAA,GAAa,MAAA;AAC3D;AAOO,SAAS,cAAc,MAAA,EAAsC;AAElE,EAAA,MAAM,YAA0C,EAAC;AAEjD,EAAA,IAAI,OAAO,KAAA,EAAO;AAChB,IAAA,KAAA,MAAW,IAAA,IAAQ,OAAO,KAAA,EAAO;AAC/B,MAAA,IAAI,KAAK,YAAA,EAAc;AACrB,QAAA,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI,mBAAA,CAAoB,KAAK,YAAY,CAAA;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AAGA,EAAA,IAAI,KAAA;AAEJ,EAAA,IAAI,OAAO,SAAA,EAAW;AACpB,IAAA,KAAA,GAAQ,EAAC;AACT,IAAA,IAAI,MAAA,CAAO,UAAU,cAAA,EAAgB;AACnC,MAAA,KAAA,CAAM,UAAA,GAAa,eAAA,CAAgB,MAAA,CAAO,SAAA,CAAU,cAAc,CAAA;AAAA,IACpE;AACA,IAAA,IAAI,MAAA,CAAO,UAAU,iBAAA,EAAmB;AACtC,MAAA,KAAA,CAAM,aAAA,GAAgB,eAAA,CAAgB,MAAA,CAAO,SAAA,CAAU,iBAAiB,CAAA;AAAA,IAC1E;AACA,IAAA,IAAI,MAAA,CAAO,UAAU,iBAAA,EAAmB;AACtC,MAAA,KAAA,CAAM,aAAA,GAAgB,eAAA,CAAgB,MAAA,CAAO,SAAA,CAAU,iBAAiB,CAAA;AAAA,IAC1E;AACA,IAAA,IAAI,MAAA,CAAO,UAAU,WAAA,EAAa;AAChC,MAAA,KAAA,CAAM,OAAA,GAAU,eAAA,CAAgB,MAAA,CAAO,SAAA,CAAU,WAAW,CAAA;AAAA,IAC9D;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,GAAG,MAAA;AAAA,IACH,WAAW,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,CAAE,MAAA,GAAS,IAAI,SAAA,GAAY,MAAA;AAAA,IAC3D;AAAA,GACF;AACF;AASA,IAAM,iBAAN,MAAqB;AAAA,EAArB,WAAA,GAAA;AACE,IAAA,IAAA,CAAQ,SAAA,uBAAwD,GAAA,EAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAK5D,aAAa,UAAA,EAA+C;AAClE,IAAA,IAAI,CAAC,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,UAAU,CAAA,EAAG;AACnC,MAAA,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,UAAA,kBAAY,IAAI,KAAK,CAAA;AAAA,IAC1C;AACA,IAAA,OAAO,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,UAAU,CAAA;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAA,GAAwD;AAC9D,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAC;AAC3C,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,YAAA,CAAa,OAAA,CAAQ,WAAW,CAAA;AAC/C,MAAA,OAAO,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,MAAM,IAAI,EAAC;AAAA,IACxC,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,EAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqB,OAAA,EAAgD;AAC3E,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,YAAA,CAAa,OAAA,CAAQ,WAAA,EAAa,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,UAAA,EAAkC;AACzD,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAC;AAC3C,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,YAAA,CAAa,OAAA,CAAQ,kBAAA,CAAmB,UAAU,CAAC,CAAA;AAClE,MAAA,OAAO,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,MAAM,IAAI,EAAC;AAAA,IACxC,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,EAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAA,CAAiB,OAAqB,UAAA,EAA0B;AACtE,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,YAAA,CAAa,QAAQ,kBAAA,CAAmB,UAAU,GAAG,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAA,CAAgB,aAAqB,SAAA,EAA2B;AAC9D,IAAA,MAAM,SAAA,GAAY,KAAK,oBAAA,EAAqB;AAC5C,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,gBAAA,CAAiB,UAAU,CAAA;AAErD,IAAA,OAAO,SAAA,CAAU,GAAA,CAAI,CAAC,MAAA,MAAY;AAAA,MAChC,GAAG,MAAA;AAAA;AAAA,MAEH,OAAA,EAAS,YAAA,CAAa,MAAA,CAAO,IAAI,CAAA,IAAK;AAAA,KACxC,CAAE,CAAA;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QAAA,CAAS,MAAA,EAAsB,OAAA,GAAU,IAAA,EAAM,aAAqB,SAAA,EAAiB;AAEnF,IAAA,MAAM,SAAA,GAAY,KAAK,oBAAA,EAAqB;AAC5C,IAAA,MAAM,QAAA,GAAW,UAAU,SAAA,CAAU,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,OAAO,IAAI,CAAA;AAElE,IAAA,MAAM,YAAA,GAA8C;AAAA,MAClD,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,aAAa,MAAA,CAAO,WAAA;AAAA,MACpB,KAAA,EAAO,oBAAA,CAAqB,MAAA,CAAO,KAAA,EAAO,OAAO,SAAS,CAAA;AAAA,MAC1D,SAAA,EAAW,cAAA,CAAe,MAAA,CAAO,KAAK,CAAA;AAAA,MACtC,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA;AAAY,KACtC;AAEA,IAAA,IAAI,YAAY,CAAA,EAAG;AACjB,MAAA,SAAA,CAAU,QAAQ,CAAA,GAAI,YAAA;AAAA,IACxB,CAAA,MAAO;AACL,MAAA,SAAA,CAAU,KAAK,YAAY,CAAA;AAAA,IAC7B;AAEA,IAAA,IAAA,CAAK,qBAAqB,SAAS,CAAA;AAGnC,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,gBAAA,CAAiB,UAAU,CAAA;AACrD,IAAA,YAAA,CAAa,MAAA,CAAO,IAAI,CAAA,GAAI,OAAA;AAC5B,IAAA,IAAA,CAAK,gBAAA,CAAiB,cAAc,UAAU,CAAA;AAE9C,IAAA,IAAA,CAAK,gBAAgB,UAAU,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,CAAW,UAAA,EAAoB,UAAA,GAAqB,SAAA,EAAiB;AAEnE,IAAA,MAAM,SAAA,GAAY,KAAK,oBAAA,EAAqB,CAAE,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,UAAU,CAAA;AACjF,IAAA,IAAA,CAAK,qBAAqB,SAAS,CAAA;AAGnC,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,gBAAA,CAAiB,UAAU,CAAA;AACrD,IAAA,OAAO,aAAa,UAAU,CAAA;AAC9B,IAAA,IAAA,CAAK,gBAAA,CAAiB,cAAc,UAAU,CAAA;AAE9C,IAAA,IAAA,CAAK,gBAAgB,UAAU,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,CAAW,UAAA,EAAoB,OAAA,EAAkB,UAAA,GAAqB,SAAA,EAAiB;AACrF,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,gBAAA,CAAiB,UAAU,CAAA;AACrD,IAAA,YAAA,CAAa,UAAU,CAAA,GAAI,OAAA;AAC3B,IAAA,IAAA,CAAK,gBAAA,CAAiB,cAAc,UAAU,CAAA;AAC9C,IAAA,IAAA,CAAK,gBAAgB,UAAU,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,UAAA,EAA6B;AACxC,IAAA,OAAO,IAAA,CAAK,sBAAqB,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,UAAU,CAAA;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,CAAU,UAAA,EAAoB,UAAA,GAAqB,SAAA,EAAqC;AACtF,IAAA,OAAO,IAAA,CAAK,gBAAgB,UAAU,CAAA,CAAE,KAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,UAAU,CAAA;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAA,CAAkB,aAAqB,SAAA,EAA6B;AAClE,IAAA,OAAO,IAAA,CAAK,eAAA,CAAgB,UAAU,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,CAAA,CAAE,GAAA,CAAI,aAAa,CAAA;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,CAAS,QAAA,EAAgC,UAAA,GAAqB,SAAA,EAAuB;AACnF,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,YAAA,CAAa,UAAU,CAAA;AAC9C,IAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,IAAA,OAAO,MAAM,SAAA,CAAU,MAAA,CAAO,QAAQ,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAA,CAAgB,aAAqB,SAAA,EAAiB;AAC5D,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,eAAA,CAAgB,UAAU,CAAA;AAC/C,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,YAAA,CAAa,UAAU,CAAA;AAC9C,IAAA,SAAA,CAAU,OAAA,CAAQ,CAAC,EAAA,KAAO,EAAA,CAAG,OAAO,CAAC,CAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,CAAM,aAAqB,SAAA,EAAiB;AAC1C,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,YAAA,CAAa,UAAA,CAAW,kBAAA,CAAmB,UAAU,CAAC,CAAA;AACtD,IAAA,IAAA,CAAK,gBAAgB,UAAU,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAAiB;AACf,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,YAAA,CAAa,WAAW,WAAW,CAAA;AAAA,EAErC;AACF,CAAA;AAGO,IAAM,cAAA,GAAiB,IAAI,cAAA,EAAe;;;ACjWjD,SAAS,cAAc,UAAA,EAA4B;AACjD,EAAA,OAAO,kBAAkB,UAAU,CAAA,CAAA;AACrC;AAwCO,SAAS,UAAA,CAAW,aAAqB,SAAA,EAA6B;AAC3E,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIC,QAAAA,CAAyB,EAAE,CAAA;AAGzD,EAAAC,UAAU,MAAM;AAEd,IAAA,UAAA,CAAW,cAAA,CAAe,eAAA,CAAgB,UAAU,CAAC,CAAA;AAGrD,IAAA,MAAM,WAAA,GAAc,cAAA,CAAe,QAAA,CAAS,UAAA,EAAY,UAAU,CAAA;AAGlE,IAAA,MAAM,UAAA,GAAa,cAAc,UAAU,CAAA;AAC3C,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAAoB;AACzC,MAAA,IAAI,CAAA,CAAE,QAAQ,UAAA,EAAY;AACxB,QAAA,UAAA,CAAW,cAAA,CAAe,eAAA,CAAgB,UAAU,CAAC,CAAA;AAAA,MACvD;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAEhD,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,EAAY;AACZ,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,aAAa,CAAA;AAAA,IACrD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAA,MAAM,cAAA,GAAiBC,WAAAA,CAAY,CAAC,MAAA,KAAyB;AAC3D,IAAA,cAAA,CAAe,QAAA,CAAS,MAAA,EAAQ,IAAA,EAAM,UAAU,CAAA;AAAA,EAClD,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAA,MAAM,gBAAA,GAAmBA,WAAAA,CAAY,CAAC,IAAA,KAAiB;AACrD,IAAA,cAAA,CAAe,UAAA,CAAW,MAAM,UAAU,CAAA;AAAA,EAC5C,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAA,MAAM,YAAA,GAAeA,WAAAA,CAAY,CAAC,IAAA,KAAiB;AACjD,IAAA,cAAA,CAAe,UAAA,CAAW,IAAA,EAAM,IAAA,EAAM,UAAU,CAAA;AAAA,EAClD,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAA,MAAM,aAAA,GAAgBA,WAAAA,CAAY,CAAC,IAAA,KAAiB;AAClD,IAAA,cAAA,CAAe,UAAA,CAAW,IAAA,EAAM,KAAA,EAAO,UAAU,CAAA;AAAA,EACnD,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAA,MAAM,YAAA,GAAeA,WAAAA;AAAA,IACnB,CAAC,IAAA,KAAiB,OAAA,CAAQ,KAAK,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,IAAI,CAAA;AAAA,IACnD,CAAC,OAAO;AAAA,GACV;AAGA,EAAA,MAAM,SAAA,GAAYA,WAAAA;AAAA,IAChB,CAAC,SAAiB,OAAA,CAAQ,IAAA,CAAK,OAAK,CAAA,CAAE,IAAA,KAAS,IAAA,IAAQ,CAAA,CAAE,OAAO,CAAA;AAAA,IAChE,CAAC,OAAO;AAAA,GACV;AAGA,EAAA,MAAM,cAAA,GAAiB,QAAQ,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,OAAO,CAAA,CAAE,IAAI,aAAa,CAAA;AAEvE,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AACF;ACtGA,IAAM,aAAA,GAAgBC,cAA8C,MAAS,CAAA;AAK7E,IAAM,sBAAA,GAAyB,wBAAA;AAM/B,IAAI,eAAA,GAAkB,CAAA;AAKtB,IAAM,oBAAA,uBAA2B,GAAA,EAAY;AAkBtC,SAAS,cAAA,CAAe;AAAA,EAC7B,QAAA;AAAA,EACA,YAAA,GAAe,sBAAA;AAAA,EACf,KAAA,GAAQ,KAAA;AAAA,EACR,UAAA,EAAY;AACd,CAAA,EAAwB;AAEtB,EAAA,MAAM,CAAC,kBAAkB,CAAA,GAAIH,QAAAA,CAAS,MAAM;AAC1C,IAAA,IAAI,kBAAA,EAAoB;AACtB,MAAA,OAAO,kBAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAAA,GAAS,CAAA,SAAA,EAAY,EAAE,eAAe,CAAA,CAAA;AAC5C,IAAA,oBAAA,CAAqB,IAAI,MAAM,CAAA;AAC/B,IAAA,OAAO,MAAA;AAAA,EACT,CAAC,CAAA;AAGD,EAAA,MAAM,iBAAiB,CAAC,kBAAA;AAGxB,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,kBAAkB,oBAAA,CAAqB,IAAA,GAAO,KAAK,OAAA,CAAQ,GAAA,CAAI,aAAa,YAAA,EAAc;AAC5F,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,CAAA;AAAA,4CAAA;AAAA,OAGF;AAAA,IACF;AAGA,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,oBAAA,CAAqB,OAAO,kBAAkB,CAAA;AAAA,MAChD;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,cAAA,EAAgB,kBAAkB,CAAC,CAAA;AAGvC,EAAA,MAAM,EAAE,OAAA,EAAS,eAAA,EAAgB,GAAI,aAAA,EAAc;AAGnD,EAAA,MAAM,EAAE,cAAA,EAAe,GAAI,UAAA,CAAW,kBAAkB,CAAA;AAGxD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAID,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,SAAuB,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIA,QAAAA,CAAkB,EAAE,CAAA;AAGhD,EAAA,MAAM,oBAAA,GAAuBI,MAAAA,iBAAoB,IAAI,GAAA,EAAK,CAAA;AAG1D,EAAA,MAAM,YAAA,GAAe,mBAAmB,kBAAkB,CAAA,CAAA;AAG1D,EAAA,MAAM,eAAe,MAAM;AACzB,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,EAAE,eAAe,EAAA,EAAI,YAAA,EAAc,EAAA,EAAI,gBAAA,EAAkB,KAAA,EAAM;AACzG,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,YAAA,CAAa,OAAA,CAAQ,YAAY,CAAA;AAChD,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,OAAO,IAAA,CAAK,MAAM,MAAM,CAAA;AAAA,MAC1B;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,OAAO,EAAE,aAAA,EAAe,EAAA,EAAI,YAAA,EAAc,EAAA,EAAI,kBAAkB,KAAA,EAAM;AAAA,EACxE,CAAA;AAEA,EAAA,MAAM,kBAAkB,YAAA,EAAa;AAGrC,EAAA,MAAM,CAAC,aAAA,EAAe,qBAAqB,CAAA,GAAIJ,QAAAA,CAAiB,gBAAgB,aAAa,CAAA;AAC7F,EAAA,MAAM,CAAC,YAAA,EAAc,oBAAoB,CAAA,GAAIA,QAAAA,CAAiB,gBAAgB,YAAY,CAAA;AAC1F,EAAA,MAAM,CAAC,gBAAA,EAAkB,wBAAwB,CAAA,GAAIA,QAAAA,CAAkB,gBAAgB,gBAAgB,CAAA;AAGvG,EAAA,MAAM,YAAA,GAAeE,WAAAA,CAAY,CAAC,QAAA,KAAyF;AACzH,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,IAAI;AACF,MAAA,YAAA,CAAa,OAAA,CAAQ,YAAA,EAAc,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,IAC7D,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,gBAAA,GAAmBA,WAAAA,CAAY,CAACG,MAAAA,KAAkB;AACtD,IAAA,qBAAA,CAAsBA,MAAK,CAAA;AAC3B,IAAA,YAAA,CAAa,EAAE,aAAA,EAAeA,MAAAA,EAAO,YAAA,EAAc,kBAAkB,CAAA;AAAA,EACvE,CAAA,EAAG,CAAC,YAAA,EAAc,gBAAA,EAAkB,YAAY,CAAC,CAAA;AAEjD,EAAA,MAAM,eAAA,GAAkBH,WAAAA,CAAY,CAACG,MAAAA,KAAkB;AACrD,IAAA,oBAAA,CAAqBA,MAAK,CAAA;AAC1B,IAAA,YAAA,CAAa,EAAE,aAAA,EAAe,YAAA,EAAcA,MAAAA,EAAO,kBAAkB,CAAA;AAAA,EACvE,CAAA,EAAG,CAAC,aAAA,EAAe,gBAAA,EAAkB,YAAY,CAAC,CAAA;AAElD,EAAA,MAAM,mBAAA,GAAsBH,WAAAA,CAAY,CAACG,MAAAA,KAAmB;AAC1D,IAAA,wBAAA,CAAyBA,MAAK,CAAA;AAC9B,IAAA,YAAA,CAAa,EAAE,aAAA,EAAe,YAAA,EAAc,gBAAA,EAAkBA,QAAO,CAAA;AAAA,EACvE,CAAA,EAAG,CAAC,aAAA,EAAe,YAAA,EAAc,YAAY,CAAC,CAAA;AAG9C,EAAA,MAAM,GAAA,GAAMH,WAAAA;AAAA,IACV,CAAC,YAAoB,IAAA,KAAoB;AACvC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,SAAA,EAAY,OAAO,CAAA,CAAA,EAAI,GAAG,IAAI,CAAA;AAAA,MAC5C;AAAA,IACF,CAAA;AAAA,IACA,CAAC,KAAK;AAAA,GACR;AAMA,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAM;AAC3B,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,eAAA,EAAiB;AAChC,MAAA,GAAA,CAAI,qCAAqC,CAAA;AACzC,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,GAAA,CAAI,8CAA8C,CAAA;AAElD,IAAA,IAAI;AACF,MAAA,MAAM,YAAA,GAAe,IAAI,qBAAA,CAAsB;AAAA,QAC7C,GAAA,EAAK,OAAA;AAAA;AAAA,QACL,YAAA;AAAA,QACA;AAAA,OACD,CAAA;AAGD,MAAA,YAAA,CAAa,EAAA,CAAG,YAAA,EAAc,CAAC,KAAA,KAAmB;AAChD,QAAA,GAAA,CAAI,eAAe,KAAK,CAAA;AAAA,MAC1B,CAAC,CAAA;AAED,MAAA,YAAA,CAAa,EAAA,CAAG,UAAA,EAAY,CAAC,KAAA,KAAmB;AAC9C,QAAA,GAAA,CAAI,aAAa,KAAK,CAAA;AAAA,MACxB,CAAC,CAAA;AAED,MAAA,YAAA,CAAa,EAAA,CAAG,YAAA,EAAc,CAAC,KAAA,KAAmB;AAChD,QAAA,GAAA,CAAI,eAAe,KAAK,CAAA;AAAA,MAC1B,CAAC,CAAA;AAED,MAAA,OAAO,YAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,GAAA,CAAI,4BAA4B,GAAG,CAAA;AACnC,MAAA,QAAA,CAAS,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,gCAAgC,CAAC,CAAA;AACjF,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,GAAG,CAAC,OAAA,EAAS,iBAAiB,YAAA,EAAc,KAAA,EAAO,GAAG,CAAC,CAAA;AAGvD,EAAA,MAAM,UAAU,MAAA,KAAW,IAAA;AAK3B,EAAAD,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,IAAA,MAAM,kBAAkB,YAAY;AAElC,MAAA,MAAM,YAAA,GAAe,IAAI,GAAA,CAAI,cAAA,CAAe,IAAI,CAAA,CAAA,KAAK,CAAA,CAAE,IAAI,CAAC,CAAA;AAG5D,MAAA,KAAA,MAAW,IAAA,IAAQ,qBAAqB,OAAA,EAAS;AAC/C,QAAA,IAAI,CAAC,YAAA,CAAa,GAAA,CAAI,IAAI,CAAA,EAAG;AAC3B,UAAA,GAAA,CAAI,yBAAyB,IAAI,CAAA;AACjC,UAAA,IAAI;AACF,YAAA,MAAM,MAAA,CAAO,MAAM,IAAI,CAAA;AACvB,YAAA,oBAAA,CAAqB,OAAA,CAAQ,OAAO,IAAI,CAAA;AACxC,YAAA,GAAA,CAAI,wBAAwB,IAAI,CAAA;AAAA,UAClC,SAAS,GAAA,EAAK;AACZ,YAAA,GAAA,CAAI,8BAAA,EAAgC,MAAM,GAAG,CAAA;AAAA,UAC/C;AAAA,QACF;AAAA,MACF;AAGA,MAAA,KAAA,MAAW,UAAU,cAAA,EAAgB;AACnC,QAAA,IAAI,CAAC,oBAAA,CAAqB,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,IAAI,CAAA,EAAG;AAClD,UAAA,GAAA,CAAI,qBAAA,EAAuB,OAAO,IAAI,CAAA;AACtC,UAAA,IAAI;AAEF,YAAA,IAAI,MAAA,CAAO,SAAA,IAAa,MAAA,CAAO,KAAA,EAAO;AAEpC,cAAA,MAAM,OAAO,GAAA,CAAI;AAAA,gBACf,MAAM,MAAA,CAAO,IAAA;AAAA,gBACb,SAAS,MAAA,CAAO,OAAA;AAAA,gBAChB,OAAO,MAAA,CAAO,KAAA;AAAA,gBACd,WAAW,MAAA,CAAO,SAAA;AAAA,gBAClB,OAAO,MAAA,CAAO;AAAA,eACqB,CAAA;AACrC,cAAA,oBAAA,CAAqB,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,IAAI,CAAA;AAC5C,cAAA,GAAA,CAAI,oBAAA,EAAsB,OAAO,IAAI,CAAA;AAAA,YACvC,CAAA,MAAO;AACL,cAAA,GAAA,CAAI,uDAAA,EAAyD,OAAO,IAAI,CAAA;AAAA,YAC1E;AAAA,UACF,SAAS,GAAA,EAAK;AACZ,YAAA,GAAA,CAAI,4BAAA,EAA8B,MAAA,CAAO,IAAA,EAAM,GAAG,CAAA;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,eAAA,EAAgB;AAAA,EAClB,CAAA,EAAG,CAAC,MAAA,EAAQ,cAAA,EAAgB,GAAG,CAAC,CAAA;AAKhC,EAAA,MAAM,UAAA,GAAaC,YAAY,YAA8B;AAC3D,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,GAAA,CAAI,uCAAuC,CAAA;AAC3C,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,GAAA,CAAI,gBAAgB,CAAA;AACpB,IAAA,YAAA,CAAa,IAAI,CAAA;AAEjB,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,SAAA,EAAU;AACzC,MAAA,SAAA,CAAU,SAAoB,CAAA;AAC9B,MAAA,GAAA,CAAI,gBAAA,EAAkB,UAAU,MAAM,CAAA;AACtC,MAAA,OAAO,SAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,GAAA,CAAI,0BAA0B,GAAG,CAAA;AACjC,MAAA,QAAA,CAAS,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,uBAAuB,CAAC,CAAA;AACxE,MAAA,OAAO,EAAC;AAAA,IACV,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,GAAG,CAAC,CAAA;AAKhB,EAAAD,UAAU,MAAM;AACd,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,UAAA,EAAW;AAAA,IACb;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,UAAU,CAAC,CAAA;AAKvB,EAAA,MAAM,IAAA,GAAOC,WAAAA;AAAA,IACX,OAAO,OAAA,KAAgD;AACrD,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,MACvE;AAEA,MAAA,GAAA,CAAI,eAAA,EAAiB,OAAA,CAAQ,QAAA,CAAS,MAAA,EAAQ,UAAU,CAAA;AACxD,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,QAAA,CAAS,IAAI,CAAA;AAEb,MAAA,IAAI;AAEF,QAAA,MAAM,qBAAA,GAAwB,QAAQ,YAAA,IAAgB,YAAA;AACtD,QAAA,MAAM,qBAAoC,EAAC;AAE3C,QAAA,IAAI,qBAAA,EAAuB;AACzB,UAAA,kBAAA,CAAmB,KAAK,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,uBAAuB,CAAA;AAAA,QAC5E;AACA,QAAA,kBAAA,CAAmB,IAAA,CAAK,GAAG,OAAA,CAAQ,QAAQ,CAAA;AAG3C,QAAA,MAAM,UAAA,GAAsC;AAAA,UAC1C,QAAA,EAAU,kBAAA;AAAA,UACV,aAAA,EAAe;AAAA,SACjB;AAEA,QAAA,IAAI,OAAA,CAAQ,SAAS,aAAA,EAAe;AAClC,UAAA,UAAA,CAAW,KAAA,GAAQ,QAAQ,KAAA,IAAS,aAAA;AAAA,QACtC;AACA,QAAA,IAAI,OAAA,CAAQ,wBAAwB,gBAAA,EAAkB;AACpD,UAAA,UAAA,CAAW,oBAAA,GAAuB,IAAA;AAAA,QACpC;AACA,QAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,UAAA,UAAA,CAAW,cAAc,OAAA,CAAQ,WAAA;AAAA,QACnC;AAGA,QAAA,MAAM,QAAA,GAAW,MAAO,MAAA,CAAoF,IAAA,CAAK,UAAU,CAAA;AAC3H,QAAA,GAAA,CAAI,wBAAwB,CAAA;AAC5B,QAAA,OAAO,QAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,GAAA,CAAI,eAAe,GAAG,CAAA;AACtB,QAAA,MAAMI,SAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,qBAAqB,CAAA;AAC1E,QAAA,QAAA,CAASA,MAAK,CAAA;AACd,QAAA,MAAMA,MAAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,aAAA,EAAe,YAAA,EAAc,kBAAkB,GAAG;AAAA,GAC7D;AAKA,EAAA,MAAM,cAAA,GAAiBJ,WAAAA;AAAA,IACrB,CAAC,OAAA,KAAuD;AACtD,MAAA,IAAI,CAAC,MAAA,EAAQ;AAEX,QAAA,OAAO;AAAA,UACL,CAAC,MAAA,CAAO,aAAa,GAAG,mBAAmB;AACzC,YAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,OAAO,EAAE,OAAA,EAAS,uDAAsD,EAAE;AAAA,UACnG;AAAA,SACF;AAAA,MACF;AAEA,MAAA,GAAA,CAAI,sBAAA,EAAwB,OAAA,CAAQ,QAAA,CAAS,MAAA,EAAQ,UAAU,CAAA;AAC/D,MAAA,QAAA,CAAS,IAAI,CAAA;AAGb,MAAA,MAAM,qBAAA,GAAwB,QAAQ,YAAA,IAAgB,YAAA;AACtD,MAAA,MAAM,qBAAoC,EAAC;AAE3C,MAAA,IAAI,qBAAA,EAAuB;AACzB,QAAA,kBAAA,CAAmB,KAAK,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,uBAAuB,CAAA;AAAA,MAC5E;AACA,MAAA,kBAAA,CAAmB,IAAA,CAAK,GAAG,OAAA,CAAQ,QAAQ,CAAA;AAG3C,MAAA,MAAM,UAAA,GAAsC;AAAA,QAC1C,QAAA,EAAU,kBAAA;AAAA,QACV,aAAA,EAAe,QAAQ,aAAA,IAAiB;AAAA,OAC1C;AAEA,MAAA,IAAI,OAAA,CAAQ,SAAS,aAAA,EAAe;AAClC,QAAA,UAAA,CAAW,KAAA,GAAQ,QAAQ,KAAA,IAAS,aAAA;AAAA,MACtC;AACA,MAAA,IAAI,OAAA,CAAQ,wBAAwB,gBAAA,EAAkB;AACpD,QAAA,UAAA,CAAW,oBAAA,GAAuB,IAAA;AAAA,MACpC;AACA,MAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,QAAA,UAAA,CAAW,cAAc,OAAA,CAAQ,WAAA;AAAA,MACnC;AAGA,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,UAAgE,CAAA;AAGjG,MAAA,OAAO;AAAA,QACL,CAAC,MAAA,CAAO,aAAa,GAAG,mBAAmB;AACzC,UAAA,IAAI;AACF,YAAA,WAAA,MAAiB,SAAS,MAAA,EAAQ;AAEhC,cAAA,MAAM,UAAA,GAAa,KAAA;AAEnB,cAAA,IAAI,UAAA,CAAW,SAAS,MAAA,EAAQ;AAC9B,gBAAA,MAAM,YAAY,UAAA,CAAW,KAAA;AAC7B,gBAAA,GAAA,CAAI,oBAAA,EAAsB,SAAA,EAAW,SAAA,CAAU,CAAA,EAAG,EAAE,CAAC,CAAA;AACrD,gBAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAU;AAAA,cACzC,CAAA,MAAA,IAAW,UAAA,CAAW,IAAA,KAAS,WAAA,EAAa;AAC1C,gBAAA,GAAA,CAAI,mBAAA,EAAqB,WAAW,KAAK,CAAA;AACzC,gBAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,WAAW,KAAA,EAAM;AAAA,cACrD,CAAA,MAAA,IAAW,UAAA,CAAW,IAAA,KAAS,aAAA,EAAe;AAC5C,gBAAA,GAAA,CAAI,oBAAoB,CAAA;AACxB,gBAAA,MAAM,EAAE,IAAA,EAAM,aAAA,EAAe,KAAA,EAAO,WAAW,KAAA,EAAM;AAAA,cACvD,CAAA,MAAA,IAAW,UAAA,CAAW,IAAA,KAAS,OAAA,EAAS;AACtC,gBAAA,MAAM,aAAa,UAAA,CAAW,KAAA;AAC9B,gBAAA,GAAA,CAAI,iBAAiB,UAAU,CAAA;AAC/B,gBAAA,QAAA,CAAS,IAAI,KAAA,CAAM,UAAA,EAAY,OAAA,IAAW,cAAc,CAAC,CAAA;AACzD,gBAAA,MAAM,EAAE,MAAM,OAAA,EAAS,KAAA,EAAO,EAAE,OAAA,EAAS,UAAA,EAAY,OAAA,IAAW,cAAA,EAAe,EAAE;AAAA,cACnF,CAAA,MAAO;AAEL,gBAAA,MAAM,KAAA;AAAA,cACR;AAAA,YACF;AAAA,UACF,SAAS,GAAA,EAAK;AACZ,YAAA,GAAA,CAAI,iBAAiB,GAAG,CAAA;AACxB,YAAA,MAAMI,SAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,eAAe,CAAA;AACpE,YAAA,QAAA,CAASA,MAAK,CAAA;AACd,YAAA,MAAM,EAAE,MAAM,OAAA,EAAS,KAAA,EAAO,EAAE,OAAA,EAASA,MAAAA,CAAM,SAAQ,EAAE;AAAA,UAC3D;AAAA,QACF;AAAA,OACF;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,aAAA,EAAe,YAAA,EAAc,kBAAkB,GAAG;AAAA,GAC7D;AAKA,EAAA,MAAM,UAAA,GAAaJ,WAAAA;AAAA,IACjB,OAAO,IAAA,KAAoC;AACzC,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,MACvE;AAEA,MAAA,GAAA,CAAI,iBAAA,EAAmB,KAAK,IAAI,CAAA;AAChC,MAAA,YAAA,CAAa,IAAI,CAAA;AAEjB,MAAA,IAAI;AACF,QAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,UAAA,CAAW,IAAI,CAAA;AAC/C,QAAA,GAAA,CAAI,kBAAkB,UAAU,CAAA;AAChC,QAAA,OAAO,UAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,GAAA,CAAI,iBAAiB,GAAG,CAAA;AACxB,QAAA,MAAMI,SAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,oBAAoB,CAAA;AACzE,QAAA,QAAA,CAASA,MAAK,CAAA;AACd,QAAA,MAAMA,MAAAA;AAAA,MACR,CAAA,SAAE;AACA,QAAA,YAAA,CAAa,KAAK,CAAA;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,GACd;AAGA,EAAA,MAAM,KAAA,GAA4B;AAAA;AAAA,IAEhC,UAAA,EAAY,kBAAA;AAAA;AAAA,IAGZ,OAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA;AAAA;AAAA,IAGA,MAAA;AAAA;AAAA,IAGA,IAAA;AAAA,IACA,UAAA,EAAY,cAAA;AAAA;AAAA,IAGZ,UAAA;AAAA;AAAA,IAGA,UAAA;AAAA;AAAA,IAGA,aAAA;AAAA,IACA,gBAAA;AAAA,IACA,YAAA;AAAA,IACA,eAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,uBACEC,GAAAA,CAAC,aAAA,CAAc,QAAA,EAAd,EAAuB,OACrB,QAAA,EACH,CAAA;AAEJ;AAmBO,SAAS,SAAA,GAAgC;AAC9C,EAAA,MAAM,OAAA,GAAUC,WAAW,aAAa,CAAA;AACxC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,MAAM,oFAAoF,CAAA;AAAA,EACtG;AACA,EAAA,OAAO,OAAA;AACT","file":"index.js","sourcesContent":["'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useRef,\n} from 'react';\nimport { EmblemAuthSDK } from 'emblem-auth-sdk';\nimport type {\n AuthSession,\n VaultInfo,\n EmblemAuthContextValue,\n EmblemAuthProviderProps,\n} from '../types';\n\n/**\n * Global SDK instance to prevent multiple initializations\n * This is important for React strict mode and hot reloading\n */\nlet globalSDKInstance: EmblemAuthSDK | null = null;\nlet isSDKInitializing = false;\n\n/**\n * Auth context - undefined when not within provider\n */\nconst EmblemAuthContext = createContext<EmblemAuthContextValue | undefined>(undefined);\n\n/**\n * EmblemAuthProvider - Provides authentication state and actions to the app\n *\n * This is a first-class citizen - it has no dependency on Hustle SDK.\n * The Hustle SDK depends on this provider for authentication.\n *\n * @example\n * ```tsx\n * <EmblemAuthProvider\n * appId=\"your-app-id\"\n * apiUrl=\"https://dev-api.emblemvault.ai\"\n * modalUrl=\"https://dev-auth.emblemvault.ai/connect\"\n * >\n * <App />\n * </EmblemAuthProvider>\n * ```\n */\nexport function EmblemAuthProvider({\n children,\n appId,\n apiUrl,\n modalUrl,\n debug = false,\n}: EmblemAuthProviderProps) {\n // State\n const [session, setSession] = useState<AuthSession | null>(null);\n const [isAuthenticated, setIsAuthenticated] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [vaultInfo, setVaultInfo] = useState<VaultInfo | null>(null);\n const [authSDK, setAuthSDK] = useState<EmblemAuthSDK | null>(globalSDKInstance);\n\n // Track if we've initialized\n const initialized = useRef(false);\n\n // Debug logger\n const log = useCallback(\n (message: string, ...args: unknown[]) => {\n if (debug) {\n console.log(`[EmblemAuth] ${message}`, ...args);\n }\n },\n [debug]\n );\n\n /**\n * Fetch vault info after authentication\n */\n const fetchVaultInfo = useCallback(\n async (sdk: EmblemAuthSDK) => {\n try {\n const info = await sdk.getVaultInfo();\n if (info) {\n setVaultInfo(info);\n log('Vault info loaded:', info);\n }\n } catch (err) {\n log('Failed to fetch vault info:', err);\n }\n },\n [log]\n );\n\n /**\n * Handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n (newSession: AuthSession, sdk: EmblemAuthSDK) => {\n log('Auth success - session:', newSession);\n setSession(newSession);\n setIsAuthenticated(true);\n setIsLoading(false);\n setError(null);\n fetchVaultInfo(sdk);\n },\n [log, fetchVaultInfo]\n );\n\n /**\n * Handle authentication error\n */\n const handleAuthError = useCallback(\n (err: Error) => {\n log('Auth error:', err);\n setError(err);\n setIsLoading(false);\n setIsAuthenticated(false);\n setSession(null);\n },\n [log]\n );\n\n /**\n * Handle session expiration\n */\n const handleSessionExpired = useCallback(() => {\n log('Session expired');\n setSession(null);\n setIsAuthenticated(false);\n setVaultInfo(null);\n }, [log]);\n\n /**\n * Initialize the SDK\n */\n useEffect(() => {\n // Prevent double initialization\n if (initialized.current || globalSDKInstance || isSDKInitializing) {\n if (globalSDKInstance && !authSDK) {\n setAuthSDK(globalSDKInstance);\n // Check for existing session\n const existingSession = globalSDKInstance.getSession();\n if (existingSession) {\n handleAuthSuccess(existingSession, globalSDKInstance);\n }\n }\n return;\n }\n\n initialized.current = true;\n isSDKInitializing = true;\n log('Initializing SDK with appId:', appId);\n\n // Create SDK instance\n const sdk = new EmblemAuthSDK({\n appId,\n apiUrl,\n modalUrl,\n onSuccess: (newSession) => {\n handleAuthSuccess(newSession as AuthSession, sdk);\n },\n onError: (err) => {\n handleAuthError(err);\n },\n });\n\n // Store globally and locally\n globalSDKInstance = sdk;\n isSDKInitializing = false;\n setAuthSDK(sdk);\n\n // Check for existing session\n const existingSession = sdk.getSession();\n if (existingSession) {\n log('Found existing session');\n handleAuthSuccess(existingSession as AuthSession, sdk);\n }\n\n // Subscribe to session events\n const handleSessionUpdate = (updatedSession: AuthSession | null) => {\n if (updatedSession) {\n setSession(updatedSession);\n setIsAuthenticated(true);\n } else {\n handleSessionExpired();\n }\n };\n\n sdk.on('session', handleSessionUpdate);\n sdk.on('sessionExpired', handleSessionExpired);\n\n // Cleanup\n return () => {\n sdk.off('session', handleSessionUpdate);\n sdk.off('sessionExpired', handleSessionExpired);\n };\n }, [appId, apiUrl, modalUrl, log, handleAuthSuccess, handleAuthError, handleSessionExpired, authSDK]);\n\n /**\n * Open the auth modal\n */\n const openAuthModal = useCallback(async () => {\n if (!authSDK) {\n setError(new Error('Auth SDK not initialized'));\n return;\n }\n\n log('Opening auth modal');\n setIsLoading(true);\n setError(null);\n\n try {\n await authSDK.openAuthModal();\n // Success is handled by onSuccess callback\n } catch (err) {\n setIsLoading(false);\n setError(err instanceof Error ? err : new Error('Failed to open auth modal'));\n }\n }, [authSDK, log]);\n\n /**\n * Logout and clear session\n */\n const logout = useCallback(() => {\n if (!authSDK) return;\n\n log('Logging out');\n authSDK.logout();\n setSession(null);\n setIsAuthenticated(false);\n setVaultInfo(null);\n setError(null);\n }, [authSDK, log]);\n\n /**\n * Refresh the current session\n */\n const refreshSession = useCallback(async (): Promise<AuthSession | null> => {\n if (!authSDK) return null;\n\n log('Refreshing session');\n try {\n const refreshedSession = await authSDK.refreshSession();\n if (refreshedSession) {\n setSession(refreshedSession as AuthSession);\n setIsAuthenticated(true);\n return refreshedSession as AuthSession;\n }\n return null;\n } catch (err) {\n log('Failed to refresh session:', err);\n setError(err instanceof Error ? err : new Error('Failed to refresh session'));\n return null;\n }\n }, [authSDK, log]);\n\n // Derived values\n const vaultId = session?.user?.vaultId ?? null;\n const walletAddress = session?.user?.evmAddress ?? null;\n\n // Context value\n const value: EmblemAuthContextValue = {\n // State\n session,\n isAuthenticated,\n isLoading,\n error,\n vaultInfo,\n\n // Derived\n vaultId,\n walletAddress,\n\n // Actions\n openAuthModal,\n logout,\n refreshSession,\n\n // For Hustle integration\n authSDK,\n };\n\n return (\n <EmblemAuthContext.Provider value={value}>\n {children}\n </EmblemAuthContext.Provider>\n );\n}\n\n/**\n * Hook to access auth context\n * Must be used within EmblemAuthProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { isAuthenticated, openAuthModal, logout } = useEmblemAuth();\n *\n * return isAuthenticated\n * ? <button onClick={logout}>Logout</button>\n * : <button onClick={openAuthModal}>Connect</button>;\n * }\n * ```\n */\nexport function useEmblemAuth(): EmblemAuthContextValue {\n const context = useContext(EmblemAuthContext);\n if (context === undefined) {\n throw new Error('useEmblemAuth must be used within an EmblemAuthProvider');\n }\n return context;\n}\n\n/**\n * Reset the global SDK instance (useful for testing)\n */\nexport function resetAuthSDK(): void {\n globalSDKInstance = null;\n isSDKInitializing = false;\n}\n","/**\n * Plugin Registry\n *\n * Manages plugin storage and state in localStorage.\n *\n * Storage model:\n * - Installed plugins are GLOBAL (hustle-plugins) - install once, available everywhere\n * - Enabled/disabled state is INSTANCE-SCOPED (hustle-plugin-state-{instanceId})\n *\n * Executor functions are serialized as strings (executorCode) and\n * reconstituted at runtime via new Function().\n *\n * SECURITY TODO: Add signature verification before executing stored code.\n * Plugins should be signed by trusted publishers and verified before\n * any eval/Function execution occurs.\n */\n\nimport type {\n StoredPlugin,\n HustlePlugin,\n HydratedPlugin,\n ToolExecutor,\n PluginHooks,\n SerializedToolDefinition,\n SerializedHooks,\n} from '../types';\n\n/**\n * Storage keys:\n * - PLUGINS_KEY: Global list of installed plugins (not instance-scoped)\n * - getEnabledStateKey: Per-instance enabled/disabled states\n */\nconst PLUGINS_KEY = 'hustle-plugins';\n\nfunction getEnabledStateKey(instanceId: string): string {\n return `hustle-plugin-state-${instanceId}`;\n}\n\ntype PluginChangeCallback = (plugins: StoredPlugin[]) => void;\n\n/** Stored enabled state per instance */\ntype EnabledState = Record<string, boolean>;\n\n/**\n * Serialize a function to a string for storage\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction serializeFunction(fn: (...args: any[]) => any): string {\n return fn.toString();\n}\n\n/**\n * Deserialize a function string back to executable function\n *\n * FIXME: Add signature verification before execution\n * This is a security-sensitive operation that executes stored code.\n */\nfunction deserializeExecutor(code: string): ToolExecutor {\n // Extract function body - handles arrow functions and regular functions\n // The stored code is the full function: \"(args) => { ... }\" or \"async (args) => { ... }\"\n // We wrap it in parentheses and eval to get the function reference\n try {\n // eslint-disable-next-line no-eval\n return eval(`(${code})`) as ToolExecutor;\n } catch (err) {\n console.error('[Hustle] Failed to deserialize executor:', err);\n // Return a no-op executor that reports the error\n return async () => ({ error: 'Failed to deserialize executor', code });\n }\n}\n\n/**\n * Deserialize a hook function string\n *\n * FIXME: Add signature verification before execution\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction deserializeHook<T extends (...args: any[]) => any>(code: string): T {\n try {\n // eslint-disable-next-line no-eval\n return eval(`(${code})`) as T;\n } catch (err) {\n console.error('[Hustle] Failed to deserialize hook:', err);\n return (() => {}) as T;\n }\n}\n\n/**\n * Serialize a plugin's executors to executorCode strings\n */\nfunction serializePluginTools(\n tools: HustlePlugin['tools'],\n executors: HustlePlugin['executors']\n): SerializedToolDefinition[] {\n if (!tools) return [];\n\n return tools.map((tool) => ({\n ...tool,\n executorCode: executors?.[tool.name]\n ? serializeFunction(executors[tool.name])\n : undefined,\n }));\n}\n\n/**\n * Serialize plugin hooks to code strings\n */\nfunction serializeHooks(hooks: PluginHooks | undefined): SerializedHooks | undefined {\n if (!hooks) return undefined;\n\n const serialized: SerializedHooks = {};\n\n if (hooks.onRegister) {\n serialized.onRegisterCode = serializeFunction(hooks.onRegister);\n }\n if (hooks.beforeRequest) {\n serialized.beforeRequestCode = serializeFunction(hooks.beforeRequest);\n }\n if (hooks.afterResponse) {\n serialized.afterResponseCode = serializeFunction(hooks.afterResponse);\n }\n if (hooks.onError) {\n serialized.onErrorCode = serializeFunction(hooks.onError);\n }\n\n return Object.keys(serialized).length > 0 ? serialized : undefined;\n}\n\n/**\n * Hydrate a stored plugin - reconstitute executors from executorCode\n *\n * FIXME: Add signature verification before execution\n */\nexport function hydratePlugin(stored: StoredPlugin): HydratedPlugin {\n // Reconstitute executors from executorCode strings\n const executors: Record<string, ToolExecutor> = {};\n\n if (stored.tools) {\n for (const tool of stored.tools) {\n if (tool.executorCode) {\n executors[tool.name] = deserializeExecutor(tool.executorCode);\n }\n }\n }\n\n // Reconstitute hooks from hooksCode strings\n let hooks: PluginHooks | undefined;\n\n if (stored.hooksCode) {\n hooks = {};\n if (stored.hooksCode.onRegisterCode) {\n hooks.onRegister = deserializeHook(stored.hooksCode.onRegisterCode);\n }\n if (stored.hooksCode.beforeRequestCode) {\n hooks.beforeRequest = deserializeHook(stored.hooksCode.beforeRequestCode);\n }\n if (stored.hooksCode.afterResponseCode) {\n hooks.afterResponse = deserializeHook(stored.hooksCode.afterResponseCode);\n }\n if (stored.hooksCode.onErrorCode) {\n hooks.onError = deserializeHook(stored.hooksCode.onErrorCode);\n }\n }\n\n return {\n ...stored,\n executors: Object.keys(executors).length > 0 ? executors : undefined,\n hooks,\n };\n}\n\n/**\n * Plugin Registry class\n *\n * Manages plugin persistence with:\n * - Global plugin installations (with serialized executorCode)\n * - Instance-scoped enabled/disabled state\n */\nclass PluginRegistry {\n private listeners: Map<string, Set<PluginChangeCallback>> = new Map();\n\n /**\n * Get listeners for a specific instance\n */\n private getListeners(instanceId: string): Set<PluginChangeCallback> {\n if (!this.listeners.has(instanceId)) {\n this.listeners.set(instanceId, new Set());\n }\n return this.listeners.get(instanceId)!;\n }\n\n /**\n * Load installed plugins (global)\n */\n private loadInstalledPlugins(): Omit<StoredPlugin, 'enabled'>[] {\n if (typeof window === 'undefined') return [];\n try {\n const stored = localStorage.getItem(PLUGINS_KEY);\n return stored ? JSON.parse(stored) : [];\n } catch {\n return [];\n }\n }\n\n /**\n * Save installed plugins (global)\n * Serializes executors as executorCode strings\n */\n private saveInstalledPlugins(plugins: Omit<StoredPlugin, 'enabled'>[]): void {\n if (typeof window === 'undefined') return;\n localStorage.setItem(PLUGINS_KEY, JSON.stringify(plugins));\n }\n\n /**\n * Load enabled state for an instance\n */\n private loadEnabledState(instanceId: string): EnabledState {\n if (typeof window === 'undefined') return {};\n try {\n const stored = localStorage.getItem(getEnabledStateKey(instanceId));\n return stored ? JSON.parse(stored) : {};\n } catch {\n return {};\n }\n }\n\n /**\n * Save enabled state for an instance\n */\n private saveEnabledState(state: EnabledState, instanceId: string): void {\n if (typeof window === 'undefined') return;\n localStorage.setItem(getEnabledStateKey(instanceId), JSON.stringify(state));\n }\n\n /**\n * Load plugins with instance-specific enabled state\n * Combines global plugin list with per-instance enabled state\n */\n loadFromStorage(instanceId: string = 'default'): StoredPlugin[] {\n const installed = this.loadInstalledPlugins();\n const enabledState = this.loadEnabledState(instanceId);\n\n return installed.map((plugin) => ({\n ...plugin,\n // Default to enabled if no state exists for this instance\n enabled: enabledState[plugin.name] ?? true,\n }));\n }\n\n /**\n * Register a new plugin (global - available to all instances)\n * Serializes executors as executorCode for persistence\n *\n * @param plugin The plugin to install\n * @param enabled Initial enabled state for this instance (default: true)\n * @param instanceId Instance to set initial enabled state for\n */\n register(plugin: HustlePlugin, enabled = true, instanceId: string = 'default'): void {\n // Add to global installed list with serialized executors\n const installed = this.loadInstalledPlugins();\n const existing = installed.findIndex((p) => p.name === plugin.name);\n\n const storedPlugin: Omit<StoredPlugin, 'enabled'> = {\n name: plugin.name,\n version: plugin.version,\n description: plugin.description,\n tools: serializePluginTools(plugin.tools, plugin.executors),\n hooksCode: serializeHooks(plugin.hooks),\n installedAt: new Date().toISOString(),\n };\n\n if (existing >= 0) {\n installed[existing] = storedPlugin;\n } else {\n installed.push(storedPlugin);\n }\n\n this.saveInstalledPlugins(installed);\n\n // Set initial enabled state for this instance\n const enabledState = this.loadEnabledState(instanceId);\n enabledState[plugin.name] = enabled;\n this.saveEnabledState(enabledState, instanceId);\n\n this.notifyListeners(instanceId);\n }\n\n /**\n * Unregister a plugin (global - removes from all instances)\n */\n unregister(pluginName: string, instanceId: string = 'default'): void {\n // Remove from global list\n const installed = this.loadInstalledPlugins().filter((p) => p.name !== pluginName);\n this.saveInstalledPlugins(installed);\n\n // Clean up enabled state for this instance\n const enabledState = this.loadEnabledState(instanceId);\n delete enabledState[pluginName];\n this.saveEnabledState(enabledState, instanceId);\n\n this.notifyListeners(instanceId);\n }\n\n /**\n * Enable or disable a plugin (instance-scoped)\n */\n setEnabled(pluginName: string, enabled: boolean, instanceId: string = 'default'): void {\n const enabledState = this.loadEnabledState(instanceId);\n enabledState[pluginName] = enabled;\n this.saveEnabledState(enabledState, instanceId);\n this.notifyListeners(instanceId);\n }\n\n /**\n * Check if a plugin is installed (global)\n */\n isRegistered(pluginName: string): boolean {\n return this.loadInstalledPlugins().some((p) => p.name === pluginName);\n }\n\n /**\n * Get a specific plugin with instance-specific enabled state\n */\n getPlugin(pluginName: string, instanceId: string = 'default'): StoredPlugin | undefined {\n return this.loadFromStorage(instanceId).find((p) => p.name === pluginName);\n }\n\n /**\n * Get all enabled plugins for an instance (hydrated with executors)\n */\n getEnabledPlugins(instanceId: string = 'default'): HydratedPlugin[] {\n return this.loadFromStorage(instanceId).filter((p) => p.enabled).map(hydratePlugin);\n }\n\n /**\n * Subscribe to plugin changes for a specific instance\n */\n onChange(callback: PluginChangeCallback, instanceId: string = 'default'): () => void {\n const listeners = this.getListeners(instanceId);\n listeners.add(callback);\n return () => listeners.delete(callback);\n }\n\n /**\n * Notify all listeners for a specific instance\n */\n private notifyListeners(instanceId: string = 'default'): void {\n const plugins = this.loadFromStorage(instanceId);\n const listeners = this.getListeners(instanceId);\n listeners.forEach((cb) => cb(plugins));\n }\n\n /**\n * Clear enabled state for an instance (plugins remain installed globally)\n */\n clear(instanceId: string = 'default'): void {\n if (typeof window === 'undefined') return;\n localStorage.removeItem(getEnabledStateKey(instanceId));\n this.notifyListeners(instanceId);\n }\n\n /**\n * Clear all installed plugins globally\n */\n clearAll(): void {\n if (typeof window === 'undefined') return;\n localStorage.removeItem(PLUGINS_KEY);\n // Note: This doesn't clear instance-specific enabled states\n }\n}\n\n// Singleton instance\nexport const pluginRegistry = new PluginRegistry();\n\nexport default pluginRegistry;\n","'use client';\n\n/**\n * usePlugins Hook\n *\n * Manages plugin state with localStorage persistence and cross-tab sync.\n * Supports instance-scoped storage for multiple HustleProviders.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { pluginRegistry, hydratePlugin } from '../utils/pluginRegistry';\nimport type { StoredPlugin, HustlePlugin, HydratedPlugin } from '../types';\n\n// Re-export hydratePlugin for convenience\nexport { hydratePlugin };\n\n/**\n * Get the storage key for a given instance\n */\nfunction getStorageKey(instanceId: string): string {\n return `hustle-plugins-${instanceId}`;\n}\n\n/**\n * Return type for usePlugins hook\n */\nexport interface UsePluginsReturn {\n /** All registered plugins (with enabled state) */\n plugins: StoredPlugin[];\n /** Only enabled plugins (hydrated with executors) */\n enabledPlugins: HydratedPlugin[];\n /** Register a new plugin */\n registerPlugin: (plugin: HustlePlugin) => void;\n /** Unregister a plugin by name */\n unregisterPlugin: (name: string) => void;\n /** Enable a plugin */\n enablePlugin: (name: string) => void;\n /** Disable a plugin */\n disablePlugin: (name: string) => void;\n /** Check if a plugin is registered */\n isRegistered: (name: string) => boolean;\n /** Check if a plugin is enabled */\n isEnabled: (name: string) => boolean;\n}\n\n/**\n * Hook for managing plugins\n *\n * @param instanceId - Optional instance ID for scoping plugin storage (defaults to 'default')\n *\n * @example\n * ```tsx\n * const { plugins, registerPlugin, enabledPlugins } = usePlugins();\n *\n * // Install a plugin\n * registerPlugin(myPlugin);\n *\n * // Check enabled plugins\n * console.log('Active tools:', enabledPlugins.flatMap(p => p.tools));\n * ```\n */\nexport function usePlugins(instanceId: string = 'default'): UsePluginsReturn {\n const [plugins, setPlugins] = useState<StoredPlugin[]>([]);\n\n // Load initial plugins and subscribe to changes\n useEffect(() => {\n // Load initial state\n setPlugins(pluginRegistry.loadFromStorage(instanceId));\n\n // Subscribe to registry changes for this instance\n const unsubscribe = pluginRegistry.onChange(setPlugins, instanceId);\n\n // Listen to storage events for cross-tab sync\n const storageKey = getStorageKey(instanceId);\n const handleStorage = (e: StorageEvent) => {\n if (e.key === storageKey) {\n setPlugins(pluginRegistry.loadFromStorage(instanceId));\n }\n };\n window.addEventListener('storage', handleStorage);\n\n return () => {\n unsubscribe();\n window.removeEventListener('storage', handleStorage);\n };\n }, [instanceId]);\n\n // Register a new plugin\n const registerPlugin = useCallback((plugin: HustlePlugin) => {\n pluginRegistry.register(plugin, true, instanceId);\n }, [instanceId]);\n\n // Unregister a plugin\n const unregisterPlugin = useCallback((name: string) => {\n pluginRegistry.unregister(name, instanceId);\n }, [instanceId]);\n\n // Enable a plugin\n const enablePlugin = useCallback((name: string) => {\n pluginRegistry.setEnabled(name, true, instanceId);\n }, [instanceId]);\n\n // Disable a plugin\n const disablePlugin = useCallback((name: string) => {\n pluginRegistry.setEnabled(name, false, instanceId);\n }, [instanceId]);\n\n // Check if plugin is registered\n const isRegistered = useCallback(\n (name: string) => plugins.some(p => p.name === name),\n [plugins]\n );\n\n // Check if plugin is enabled\n const isEnabled = useCallback(\n (name: string) => plugins.some(p => p.name === name && p.enabled),\n [plugins]\n );\n\n // Get enabled plugins with hydrated executors\n const enabledPlugins = plugins.filter(p => p.enabled).map(hydratePlugin);\n\n return {\n plugins,\n enabledPlugins,\n registerPlugin,\n unregisterPlugin,\n enablePlugin,\n disablePlugin,\n isRegistered,\n isEnabled,\n };\n}\n\nexport default usePlugins;\n","'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useMemo,\n useRef,\n} from 'react';\nimport { HustleIncognitoClient } from 'hustle-incognito';\nimport { useEmblemAuth } from './EmblemAuthProvider';\nimport { usePlugins } from '../hooks/usePlugins';\nimport type {\n Model,\n ChatOptions,\n StreamOptions,\n StreamChunk,\n ChatResponse,\n Attachment,\n HustleContextValue,\n HustleProviderProps,\n ChatMessage,\n HydratedPlugin,\n} from '../types';\n\n/**\n * Hustle context - undefined when not within provider\n */\nconst HustleContext = createContext<HustleContextValue | undefined>(undefined);\n\n/**\n * Default Hustle API URL\n */\nconst DEFAULT_HUSTLE_API_URL = 'https://agenthustle.ai';\n\n/**\n * Module-level counter for auto-generating instance IDs.\n * Resets on page load, but render order is deterministic.\n */\nlet instanceCounter = 0;\n\n/**\n * Track mounted instances without explicit IDs for dev warnings\n */\nconst mountedAutoInstances = new Set<string>();\n\n/**\n * HustleProvider - Provides Hustle SDK functionality to the app\n *\n * IMPORTANT: This provider depends on EmblemAuthProvider and must be nested within it.\n * It uses the modern pattern of passing the auth SDK instance to HustleIncognitoClient,\n * NOT the deprecated api-key pattern.\n *\n * @example\n * ```tsx\n * <EmblemAuthProvider appId=\"your-app-id\">\n * <HustleProvider hustleApiUrl=\"https://dev.agenthustle.ai\">\n * <App />\n * </HustleProvider>\n * </EmblemAuthProvider>\n * ```\n */\nexport function HustleProvider({\n children,\n hustleApiUrl = DEFAULT_HUSTLE_API_URL,\n debug = false,\n instanceId: explicitInstanceId,\n}: HustleProviderProps) {\n // Generate stable instance ID - explicit or auto-generated based on mount order\n const [resolvedInstanceId] = useState(() => {\n if (explicitInstanceId) {\n return explicitInstanceId;\n }\n // Auto-generate based on mount order\n const autoId = `instance-${++instanceCounter}`;\n mountedAutoInstances.add(autoId);\n return autoId;\n });\n\n // Track if this is an auto-generated instance for cleanup\n const isAutoInstance = !explicitInstanceId;\n\n // Dev warning for multiple auto-generated instances\n useEffect(() => {\n if (isAutoInstance && mountedAutoInstances.size > 1 && process.env.NODE_ENV !== 'production') {\n console.warn(\n `[Hustle] Multiple HustleProviders detected without explicit instanceId. ` +\n `For stable settings persistence, consider adding instanceId prop:\\n` +\n ` <HustleProvider instanceId=\"my-chat-name\">`\n );\n }\n\n // Cleanup on unmount\n return () => {\n if (isAutoInstance) {\n mountedAutoInstances.delete(resolvedInstanceId);\n }\n };\n }, [isAutoInstance, resolvedInstanceId]);\n\n // Get auth context - this provider REQUIRES EmblemAuthProvider\n const { authSDK, isAuthenticated } = useEmblemAuth();\n\n // Get plugins with instance scoping\n const { enabledPlugins } = usePlugins(resolvedInstanceId);\n\n // State\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [models, setModels] = useState<Model[]>([]);\n\n // Track registered plugins to avoid re-registering\n const registeredPluginsRef = useRef<Set<string>>(new Set());\n\n // Settings storage key - scoped to instance\n const SETTINGS_KEY = `hustle-settings-${resolvedInstanceId}`;\n\n // Load initial settings from localStorage\n const loadSettings = () => {\n if (typeof window === 'undefined') return { selectedModel: '', systemPrompt: '', skipServerPrompt: false };\n try {\n const stored = localStorage.getItem(SETTINGS_KEY);\n if (stored) {\n return JSON.parse(stored);\n }\n } catch {\n // Ignore parse errors\n }\n return { selectedModel: '', systemPrompt: '', skipServerPrompt: false };\n };\n\n const initialSettings = loadSettings();\n\n // Settings state (initialized from localStorage)\n const [selectedModel, setSelectedModelState] = useState<string>(initialSettings.selectedModel);\n const [systemPrompt, setSystemPromptState] = useState<string>(initialSettings.systemPrompt);\n const [skipServerPrompt, setSkipServerPromptState] = useState<boolean>(initialSettings.skipServerPrompt);\n\n // Persist settings to localStorage\n const saveSettings = useCallback((settings: { selectedModel: string; systemPrompt: string; skipServerPrompt: boolean }) => {\n if (typeof window === 'undefined') return;\n try {\n localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));\n } catch {\n // Ignore storage errors\n }\n }, []);\n\n // Wrapped setters that also persist\n const setSelectedModel = useCallback((value: string) => {\n setSelectedModelState(value);\n saveSettings({ selectedModel: value, systemPrompt, skipServerPrompt });\n }, [systemPrompt, skipServerPrompt, saveSettings]);\n\n const setSystemPrompt = useCallback((value: string) => {\n setSystemPromptState(value);\n saveSettings({ selectedModel, systemPrompt: value, skipServerPrompt });\n }, [selectedModel, skipServerPrompt, saveSettings]);\n\n const setSkipServerPrompt = useCallback((value: boolean) => {\n setSkipServerPromptState(value);\n saveSettings({ selectedModel, systemPrompt, skipServerPrompt: value });\n }, [selectedModel, systemPrompt, saveSettings]);\n\n // Debug logger\n const log = useCallback(\n (message: string, ...args: unknown[]) => {\n if (debug) {\n console.log(`[Hustle] ${message}`, ...args);\n }\n },\n [debug]\n );\n\n /**\n * Create the Hustle client with the auth SDK\n * This is the CORRECT pattern - using sdk: authSDK, NOT apiKey\n */\n const client = useMemo(() => {\n if (!authSDK || !isAuthenticated) {\n log('Client not created - auth not ready');\n return null;\n }\n\n log('Creating HustleIncognitoClient with auth SDK');\n\n try {\n const hustleClient = new HustleIncognitoClient({\n sdk: authSDK, // CORRECT: Pass auth SDK instance, NOT apiKey\n hustleApiUrl,\n debug,\n });\n\n // Subscribe to events (use any for SDK event types)\n hustleClient.on('tool_start', (event: unknown) => {\n log('Tool start:', event);\n });\n\n hustleClient.on('tool_end', (event: unknown) => {\n log('Tool end:', event);\n });\n\n hustleClient.on('stream_end', (event: unknown) => {\n log('Stream end:', event);\n });\n\n return hustleClient;\n } catch (err) {\n log('Failed to create client:', err);\n setError(err instanceof Error ? err : new Error('Failed to create Hustle client'));\n return null;\n }\n }, [authSDK, isAuthenticated, hustleApiUrl, debug, log]);\n\n // Is ready when client exists\n const isReady = client !== null;\n\n /**\n * Register enabled plugins with the client\n */\n useEffect(() => {\n if (!client) return;\n\n const registerPlugins = async () => {\n // Get the set of enabled plugin names\n const enabledNames = new Set(enabledPlugins.map(p => p.name));\n\n // Unregister plugins that were disabled\n for (const name of registeredPluginsRef.current) {\n if (!enabledNames.has(name)) {\n log('Unregistering plugin:', name);\n try {\n await client.unuse(name);\n registeredPluginsRef.current.delete(name);\n log('Plugin unregistered:', name);\n } catch (err) {\n log('Failed to unregister plugin:', name, err);\n }\n }\n }\n\n // Register new plugins\n for (const plugin of enabledPlugins) {\n if (!registeredPluginsRef.current.has(plugin.name)) {\n log('Registering plugin:', plugin.name);\n try {\n // The SDK's use() method registers the plugin\n if (plugin.executors || plugin.hooks) {\n // Cast to SDK's expected type (our types are compatible but TS is strict)\n await client.use({\n name: plugin.name,\n version: plugin.version,\n tools: plugin.tools,\n executors: plugin.executors,\n hooks: plugin.hooks,\n } as Parameters<typeof client.use>[0]);\n registeredPluginsRef.current.add(plugin.name);\n log('Plugin registered:', plugin.name);\n } else {\n log('Plugin has no executors/hooks, skipping registration:', plugin.name);\n }\n } catch (err) {\n log('Failed to register plugin:', plugin.name, err);\n }\n }\n }\n };\n\n registerPlugins();\n }, [client, enabledPlugins, log]);\n\n /**\n * Load available models\n */\n const loadModels = useCallback(async (): Promise<Model[]> => {\n if (!client) {\n log('Cannot load models - client not ready');\n return [];\n }\n\n log('Loading models');\n setIsLoading(true);\n\n try {\n const modelList = await client.getModels();\n setModels(modelList as Model[]);\n log('Loaded models:', modelList.length);\n return modelList as Model[];\n } catch (err) {\n log('Failed to load models:', err);\n setError(err instanceof Error ? err : new Error('Failed to load models'));\n return [];\n } finally {\n setIsLoading(false);\n }\n }, [client, log]);\n\n /**\n * Load models when client becomes ready\n */\n useEffect(() => {\n if (client) {\n loadModels();\n }\n }, [client, loadModels]);\n\n /**\n * Send a chat message (non-streaming)\n */\n const chat = useCallback(\n async (options: ChatOptions): Promise<ChatResponse> => {\n if (!client) {\n throw new Error('Hustle client not ready. Please authenticate first.');\n }\n\n log('Chat request:', options.messages.length, 'messages');\n setIsLoading(true);\n setError(null);\n\n try {\n // Build the messages array, prepending system prompt if provided\n const effectiveSystemPrompt = options.systemPrompt || systemPrompt;\n const messagesWithSystem: ChatMessage[] = [];\n\n if (effectiveSystemPrompt) {\n messagesWithSystem.push({ role: 'system', content: effectiveSystemPrompt });\n }\n messagesWithSystem.push(...options.messages);\n\n // Build the options object for the SDK\n const sdkOptions: Record<string, unknown> = {\n messages: messagesWithSystem,\n processChunks: true,\n };\n\n if (options.model || selectedModel) {\n sdkOptions.model = options.model || selectedModel;\n }\n if (options.overrideSystemPrompt ?? skipServerPrompt) {\n sdkOptions.overrideSystemPrompt = true;\n }\n if (options.attachments) {\n sdkOptions.attachments = options.attachments;\n }\n\n // Call the SDK - it accepts an options object\n const response = await (client as unknown as { chat: (opts: Record<string, unknown>) => Promise<unknown> }).chat(sdkOptions);\n log('Chat response received');\n return response as ChatResponse;\n } catch (err) {\n log('Chat error:', err);\n const error = err instanceof Error ? err : new Error('Chat request failed');\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client, selectedModel, systemPrompt, skipServerPrompt, log]\n );\n\n /**\n * Send a chat message with streaming response\n */\n const chatStreamImpl = useCallback(\n (options: StreamOptions): AsyncIterable<StreamChunk> => {\n if (!client) {\n // Return an async iterable that yields an error\n return {\n [Symbol.asyncIterator]: async function* () {\n yield { type: 'error', value: { message: 'Hustle client not ready. Please authenticate first.' } } as StreamChunk;\n },\n };\n }\n\n log('Chat stream request:', options.messages.length, 'messages');\n setError(null);\n\n // Build the messages array, prepending system prompt if provided\n const effectiveSystemPrompt = options.systemPrompt || systemPrompt;\n const messagesWithSystem: ChatMessage[] = [];\n\n if (effectiveSystemPrompt) {\n messagesWithSystem.push({ role: 'system', content: effectiveSystemPrompt });\n }\n messagesWithSystem.push(...options.messages);\n\n // Build the options object for the SDK\n const sdkOptions: Record<string, unknown> = {\n messages: messagesWithSystem,\n processChunks: options.processChunks ?? true,\n };\n\n if (options.model || selectedModel) {\n sdkOptions.model = options.model || selectedModel;\n }\n if (options.overrideSystemPrompt ?? skipServerPrompt) {\n sdkOptions.overrideSystemPrompt = true;\n }\n if (options.attachments) {\n sdkOptions.attachments = options.attachments;\n }\n\n // Get the stream from the client (cast through unknown for SDK compatibility)\n const stream = client.chatStream(sdkOptions as unknown as Parameters<typeof client.chatStream>[0]);\n\n // Wrap to add logging and type conversion\n return {\n [Symbol.asyncIterator]: async function* () {\n try {\n for await (const chunk of stream) {\n // Type guard for chunk with type property\n const typedChunk = chunk as { type?: string; value?: unknown };\n\n if (typedChunk.type === 'text') {\n const textValue = typedChunk.value as string;\n log('Stream text chunk:', textValue?.substring(0, 50));\n yield { type: 'text', value: textValue } as StreamChunk;\n } else if (typedChunk.type === 'tool_call') {\n log('Stream tool call:', typedChunk.value);\n yield { type: 'tool_call', value: typedChunk.value } as StreamChunk;\n } else if (typedChunk.type === 'tool_result') {\n log('Stream tool result');\n yield { type: 'tool_result', value: typedChunk.value } as StreamChunk;\n } else if (typedChunk.type === 'error') {\n const errorValue = typedChunk.value as { message?: string };\n log('Stream error:', errorValue);\n setError(new Error(errorValue?.message || 'Stream error'));\n yield { type: 'error', value: { message: errorValue?.message || 'Stream error' } } as StreamChunk;\n } else {\n // Pass through unknown chunk types\n yield chunk as StreamChunk;\n }\n }\n } catch (err) {\n log('Stream error:', err);\n const error = err instanceof Error ? err : new Error('Stream failed');\n setError(error);\n yield { type: 'error', value: { message: error.message } } as StreamChunk;\n }\n },\n };\n },\n [client, selectedModel, systemPrompt, skipServerPrompt, log]\n );\n\n /**\n * Upload a file\n */\n const uploadFile = useCallback(\n async (file: File): Promise<Attachment> => {\n if (!client) {\n throw new Error('Hustle client not ready. Please authenticate first.');\n }\n\n log('Uploading file:', file.name);\n setIsLoading(true);\n\n try {\n const attachment = await client.uploadFile(file);\n log('File uploaded:', attachment);\n return attachment as Attachment;\n } catch (err) {\n log('Upload error:', err);\n const error = err instanceof Error ? err : new Error('File upload failed');\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [client, log]\n );\n\n // Context value\n const value: HustleContextValue = {\n // Instance ID for scoped storage\n instanceId: resolvedInstanceId,\n\n // State\n isReady,\n isLoading,\n error,\n models,\n\n // Client (for advanced use)\n client,\n\n // Chat methods\n chat,\n chatStream: chatStreamImpl,\n\n // File upload\n uploadFile,\n\n // Data fetching\n loadModels,\n\n // Settings\n selectedModel,\n setSelectedModel,\n systemPrompt,\n setSystemPrompt,\n skipServerPrompt,\n setSkipServerPrompt,\n };\n\n return (\n <HustleContext.Provider value={value}>\n {children}\n </HustleContext.Provider>\n );\n}\n\n/**\n * Hook to access Hustle context\n * Must be used within HustleProvider (which must be within EmblemAuthProvider)\n *\n * @example\n * ```tsx\n * function ChatComponent() {\n * const { isReady, chat, chatStream } = useHustle();\n *\n * if (!isReady) {\n * return <div>Please connect to start chatting</div>;\n * }\n *\n * // Use chat or chatStream...\n * }\n * ```\n */\nexport function useHustle(): HustleContextValue {\n const context = useContext(HustleContext);\n if (context === undefined) {\n throw new Error('useHustle must be used within a HustleProvider (which requires EmblemAuthProvider)');\n }\n return context;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-hustle-demo",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "React hooks and components for Emblem Auth + Hustle SDK integration",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./components": {
|
|
16
|
+
"types": "./dist/components/index.d.ts",
|
|
17
|
+
"import": "./dist/components/index.js",
|
|
18
|
+
"require": "./dist/components/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./hooks": {
|
|
21
|
+
"types": "./dist/hooks/index.d.ts",
|
|
22
|
+
"import": "./dist/hooks/index.js",
|
|
23
|
+
"require": "./dist/hooks/index.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./providers": {
|
|
26
|
+
"types": "./dist/providers/index.d.ts",
|
|
27
|
+
"import": "./dist/providers/index.js",
|
|
28
|
+
"require": "./dist/providers/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"src"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"start": "node server.js",
|
|
37
|
+
"dev": "next dev",
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"build:types": "tsc --emitDeclarationOnly",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:watch": "vitest",
|
|
43
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
44
|
+
"clean": "rm -rf dist"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"emblem-auth-sdk": "^1.0.0-alpha.6",
|
|
48
|
+
"highlight.js": "^11.11.1",
|
|
49
|
+
"hustle-incognito": "^0.2.11",
|
|
50
|
+
"marked": "^17.0.1"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
54
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@testing-library/dom": "^10.4.1",
|
|
58
|
+
"@testing-library/react": "^16.3.1",
|
|
59
|
+
"@types/node": "^20.10.0",
|
|
60
|
+
"@types/react": "^18.2.0",
|
|
61
|
+
"@types/react-dom": "^18.2.0",
|
|
62
|
+
"happy-dom": "^20.0.11",
|
|
63
|
+
"react": "^18.2.0",
|
|
64
|
+
"react-dom": "^18.2.0",
|
|
65
|
+
"tsup": "^8.0.0",
|
|
66
|
+
"typescript": "^5.3.0",
|
|
67
|
+
"vitest": "^4.0.16"
|
|
68
|
+
},
|
|
69
|
+
"keywords": [
|
|
70
|
+
"emblem",
|
|
71
|
+
"auth",
|
|
72
|
+
"hustle",
|
|
73
|
+
"sdk",
|
|
74
|
+
"react",
|
|
75
|
+
"hooks",
|
|
76
|
+
"web3",
|
|
77
|
+
"wallet"
|
|
78
|
+
],
|
|
79
|
+
"license": "MIT"
|
|
80
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState, useCallback } from 'react';
|
|
4
|
+
import { useEmblemAuth } from '../providers/EmblemAuthProvider';
|
|
5
|
+
import { tokens, presets, animations } from '../styles';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for AuthStatus component
|
|
9
|
+
*/
|
|
10
|
+
export interface AuthStatusProps {
|
|
11
|
+
/** Additional CSS classes */
|
|
12
|
+
className?: string;
|
|
13
|
+
/** Additional inline styles */
|
|
14
|
+
style?: React.CSSProperties;
|
|
15
|
+
/** Show expandable vault details */
|
|
16
|
+
showVaultInfo?: boolean;
|
|
17
|
+
/** Show logout button */
|
|
18
|
+
showLogout?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Truncate address for display
|
|
23
|
+
*/
|
|
24
|
+
function truncateAddress(address: string): string {
|
|
25
|
+
if (!address || address.length < 10) return address || '';
|
|
26
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Copy text to clipboard
|
|
31
|
+
*/
|
|
32
|
+
async function copyToClipboard(text: string): Promise<boolean> {
|
|
33
|
+
try {
|
|
34
|
+
await navigator.clipboard.writeText(text);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Styles using design tokens
|
|
42
|
+
const s = {
|
|
43
|
+
container: {
|
|
44
|
+
position: 'relative' as const,
|
|
45
|
+
display: 'inline-flex',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
gap: tokens.spacing.sm,
|
|
48
|
+
fontFamily: tokens.typography.fontFamily,
|
|
49
|
+
},
|
|
50
|
+
disconnected: {
|
|
51
|
+
display: 'inline-flex',
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
gap: tokens.spacing.sm,
|
|
54
|
+
color: tokens.colors.textSecondary,
|
|
55
|
+
fontSize: tokens.typography.fontSizeMd,
|
|
56
|
+
},
|
|
57
|
+
dot: {
|
|
58
|
+
display: 'inline-block',
|
|
59
|
+
width: '8px',
|
|
60
|
+
height: '8px',
|
|
61
|
+
borderRadius: tokens.radius.full,
|
|
62
|
+
backgroundColor: tokens.colors.textTertiary,
|
|
63
|
+
},
|
|
64
|
+
dotConnected: {
|
|
65
|
+
backgroundColor: tokens.colors.accentSuccess,
|
|
66
|
+
},
|
|
67
|
+
spinner: {
|
|
68
|
+
display: 'inline-block',
|
|
69
|
+
width: '12px',
|
|
70
|
+
height: '12px',
|
|
71
|
+
border: `2px solid ${tokens.colors.textSecondary}`,
|
|
72
|
+
borderTopColor: 'transparent',
|
|
73
|
+
borderRadius: tokens.radius.full,
|
|
74
|
+
animation: 'hustle-spin 0.8s linear infinite',
|
|
75
|
+
},
|
|
76
|
+
logoutBtn: {
|
|
77
|
+
...presets.buttonIcon,
|
|
78
|
+
border: `1px solid ${tokens.colors.borderSecondary}`,
|
|
79
|
+
borderRadius: tokens.radius.lg,
|
|
80
|
+
transition: `all ${tokens.transitions.normal}`,
|
|
81
|
+
} as React.CSSProperties,
|
|
82
|
+
logoutBtnHover: {
|
|
83
|
+
borderColor: tokens.colors.accentError,
|
|
84
|
+
color: tokens.colors.accentError,
|
|
85
|
+
},
|
|
86
|
+
vaultInfoWrapper: {
|
|
87
|
+
position: 'relative' as const,
|
|
88
|
+
},
|
|
89
|
+
vaultInfo: {
|
|
90
|
+
position: 'absolute' as const,
|
|
91
|
+
top: '100%',
|
|
92
|
+
right: 0,
|
|
93
|
+
marginTop: tokens.spacing.sm,
|
|
94
|
+
background: tokens.colors.bgSecondary,
|
|
95
|
+
border: `1px solid ${tokens.colors.borderPrimary}`,
|
|
96
|
+
borderRadius: tokens.radius.xl,
|
|
97
|
+
padding: tokens.spacing.lg,
|
|
98
|
+
minWidth: '380px',
|
|
99
|
+
zIndex: tokens.zIndex.dropdown,
|
|
100
|
+
boxShadow: tokens.shadows.lg,
|
|
101
|
+
},
|
|
102
|
+
vaultInfoHeader: {
|
|
103
|
+
fontSize: tokens.typography.fontSizeXs,
|
|
104
|
+
fontWeight: tokens.typography.fontWeightSemibold,
|
|
105
|
+
color: tokens.colors.textSecondary,
|
|
106
|
+
letterSpacing: '0.5px',
|
|
107
|
+
marginBottom: tokens.spacing.lg,
|
|
108
|
+
textTransform: 'uppercase' as const,
|
|
109
|
+
},
|
|
110
|
+
vaultInfoRow: {
|
|
111
|
+
marginBottom: tokens.spacing.md,
|
|
112
|
+
},
|
|
113
|
+
vaultLabel: {
|
|
114
|
+
display: 'block',
|
|
115
|
+
fontSize: '12px',
|
|
116
|
+
color: tokens.colors.textTertiary,
|
|
117
|
+
marginBottom: tokens.spacing.xs,
|
|
118
|
+
},
|
|
119
|
+
vaultValueRow: {
|
|
120
|
+
display: 'flex',
|
|
121
|
+
alignItems: 'center',
|
|
122
|
+
justifyContent: 'space-between',
|
|
123
|
+
gap: tokens.spacing.sm,
|
|
124
|
+
},
|
|
125
|
+
vaultValue: {
|
|
126
|
+
fontSize: tokens.typography.fontSizeMd,
|
|
127
|
+
color: tokens.colors.textPrimary,
|
|
128
|
+
fontWeight: tokens.typography.fontWeightMedium,
|
|
129
|
+
flex: 1,
|
|
130
|
+
},
|
|
131
|
+
vaultValueMono: {
|
|
132
|
+
...presets.mono,
|
|
133
|
+
wordBreak: 'break-all' as const,
|
|
134
|
+
},
|
|
135
|
+
copyBtn: {
|
|
136
|
+
background: 'transparent',
|
|
137
|
+
border: `1px solid ${tokens.colors.borderSecondary}`,
|
|
138
|
+
color: tokens.colors.textSecondary,
|
|
139
|
+
padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,
|
|
140
|
+
borderRadius: tokens.radius.sm,
|
|
141
|
+
cursor: 'pointer',
|
|
142
|
+
fontSize: tokens.typography.fontSizeXs,
|
|
143
|
+
transition: `all ${tokens.transitions.normal}`,
|
|
144
|
+
whiteSpace: 'nowrap' as const,
|
|
145
|
+
},
|
|
146
|
+
copyBtnHover: {
|
|
147
|
+
background: tokens.colors.bgHover,
|
|
148
|
+
borderColor: tokens.colors.accentPrimary,
|
|
149
|
+
color: tokens.colors.accentPrimary,
|
|
150
|
+
},
|
|
151
|
+
copyBtnCopied: {
|
|
152
|
+
background: tokens.colors.accentSuccess,
|
|
153
|
+
borderColor: tokens.colors.accentSuccess,
|
|
154
|
+
color: tokens.colors.textInverse,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* AuthStatus - Displays current authentication status and vault info
|
|
160
|
+
*/
|
|
161
|
+
export function AuthStatus({
|
|
162
|
+
className = '',
|
|
163
|
+
style,
|
|
164
|
+
showVaultInfo = false,
|
|
165
|
+
showLogout = false,
|
|
166
|
+
}: AuthStatusProps) {
|
|
167
|
+
const {
|
|
168
|
+
isAuthenticated,
|
|
169
|
+
isLoading,
|
|
170
|
+
walletAddress,
|
|
171
|
+
vaultId,
|
|
172
|
+
vaultInfo,
|
|
173
|
+
logout,
|
|
174
|
+
} = useEmblemAuth();
|
|
175
|
+
|
|
176
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
177
|
+
const [logoutHovered, setLogoutHovered] = useState(false);
|
|
178
|
+
const [copiedField, setCopiedField] = useState<string | null>(null);
|
|
179
|
+
const [copyHovered, setCopyHovered] = useState<string | null>(null);
|
|
180
|
+
|
|
181
|
+
const handleCopy = useCallback(async (field: string, value: string) => {
|
|
182
|
+
const success = await copyToClipboard(value);
|
|
183
|
+
if (success) {
|
|
184
|
+
setCopiedField(field);
|
|
185
|
+
setTimeout(() => setCopiedField(null), 1500);
|
|
186
|
+
}
|
|
187
|
+
}, []);
|
|
188
|
+
|
|
189
|
+
// Not authenticated
|
|
190
|
+
if (!isAuthenticated) {
|
|
191
|
+
if (isLoading) {
|
|
192
|
+
return (
|
|
193
|
+
<>
|
|
194
|
+
<style>{animations}</style>
|
|
195
|
+
<div className={className} style={{ ...s.disconnected, ...style }}>
|
|
196
|
+
<span style={s.spinner} />
|
|
197
|
+
<span>Connecting...</span>
|
|
198
|
+
</div>
|
|
199
|
+
</>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<div className={className} style={{ ...s.disconnected, ...style }}>
|
|
205
|
+
<span style={s.dot} />
|
|
206
|
+
<span>Not connected</span>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Authenticated
|
|
212
|
+
return (
|
|
213
|
+
<>
|
|
214
|
+
<style>{animations}</style>
|
|
215
|
+
<div className={className} style={{ ...s.container, ...style }}>
|
|
216
|
+
{/* Vault info wrapper for hover effect */}
|
|
217
|
+
<div
|
|
218
|
+
style={s.vaultInfoWrapper}
|
|
219
|
+
onMouseEnter={() => showVaultInfo && setIsHovered(true)}
|
|
220
|
+
onMouseLeave={() => showVaultInfo && setIsHovered(false)}
|
|
221
|
+
>
|
|
222
|
+
{/* Connected indicator - just a dot */}
|
|
223
|
+
<span style={{ ...s.dot, ...s.dotConnected }} title="Connected" />
|
|
224
|
+
|
|
225
|
+
{/* Vault info dropdown on hover */}
|
|
226
|
+
{showVaultInfo && isHovered && (
|
|
227
|
+
<div style={s.vaultInfo}>
|
|
228
|
+
<div style={s.vaultInfoHeader}>Vault Information</div>
|
|
229
|
+
|
|
230
|
+
{/* Vault ID */}
|
|
231
|
+
<div style={s.vaultInfoRow}>
|
|
232
|
+
<span style={s.vaultLabel}>Vault ID</span>
|
|
233
|
+
<div style={s.vaultValueRow}>
|
|
234
|
+
<span style={s.vaultValue}>#{vaultId}</span>
|
|
235
|
+
<CopyButton
|
|
236
|
+
field="vaultId"
|
|
237
|
+
value={vaultId || ''}
|
|
238
|
+
copiedField={copiedField}
|
|
239
|
+
copyHovered={copyHovered}
|
|
240
|
+
setCopyHovered={setCopyHovered}
|
|
241
|
+
onCopy={handleCopy}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
{/* Connected Wallet */}
|
|
247
|
+
<div style={s.vaultInfoRow}>
|
|
248
|
+
<span style={s.vaultLabel}>Connected Wallet</span>
|
|
249
|
+
<div style={s.vaultValueRow}>
|
|
250
|
+
<span style={{ ...s.vaultValue, ...s.vaultValueMono }}>{walletAddress}</span>
|
|
251
|
+
<CopyButton
|
|
252
|
+
field="wallet"
|
|
253
|
+
value={walletAddress || ''}
|
|
254
|
+
copiedField={copiedField}
|
|
255
|
+
copyHovered={copyHovered}
|
|
256
|
+
setCopyHovered={setCopyHovered}
|
|
257
|
+
onCopy={handleCopy}
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
{/* EVM Address */}
|
|
263
|
+
{vaultInfo?.evmAddress && (
|
|
264
|
+
<div style={s.vaultInfoRow}>
|
|
265
|
+
<span style={s.vaultLabel}>Vault EVM Address</span>
|
|
266
|
+
<div style={s.vaultValueRow}>
|
|
267
|
+
<span style={{ ...s.vaultValue, ...s.vaultValueMono }}>{vaultInfo.evmAddress}</span>
|
|
268
|
+
<CopyButton
|
|
269
|
+
field="evmAddress"
|
|
270
|
+
value={vaultInfo.evmAddress}
|
|
271
|
+
copiedField={copiedField}
|
|
272
|
+
copyHovered={copyHovered}
|
|
273
|
+
setCopyHovered={setCopyHovered}
|
|
274
|
+
onCopy={handleCopy}
|
|
275
|
+
/>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
{/* Solana Address */}
|
|
281
|
+
{vaultInfo?.solanaAddress && (
|
|
282
|
+
<div style={s.vaultInfoRow}>
|
|
283
|
+
<span style={s.vaultLabel}>Vault Solana Address</span>
|
|
284
|
+
<div style={s.vaultValueRow}>
|
|
285
|
+
<span style={{ ...s.vaultValue, ...s.vaultValueMono }}>{vaultInfo.solanaAddress}</span>
|
|
286
|
+
<CopyButton
|
|
287
|
+
field="solAddress"
|
|
288
|
+
value={vaultInfo.solanaAddress}
|
|
289
|
+
copiedField={copiedField}
|
|
290
|
+
copyHovered={copyHovered}
|
|
291
|
+
setCopyHovered={setCopyHovered}
|
|
292
|
+
onCopy={handleCopy}
|
|
293
|
+
/>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
)}
|
|
297
|
+
</div>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
{/* Logout button */}
|
|
302
|
+
{showLogout && (
|
|
303
|
+
<button
|
|
304
|
+
type="button"
|
|
305
|
+
onClick={logout}
|
|
306
|
+
style={{
|
|
307
|
+
...s.logoutBtn,
|
|
308
|
+
...(logoutHovered ? s.logoutBtnHover : {}),
|
|
309
|
+
}}
|
|
310
|
+
onMouseEnter={() => setLogoutHovered(true)}
|
|
311
|
+
onMouseLeave={() => setLogoutHovered(false)}
|
|
312
|
+
title="Disconnect"
|
|
313
|
+
>
|
|
314
|
+
⏻
|
|
315
|
+
</button>
|
|
316
|
+
)}
|
|
317
|
+
</div>
|
|
318
|
+
</>
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Copy button helper
|
|
323
|
+
interface CopyButtonProps {
|
|
324
|
+
field: string;
|
|
325
|
+
value: string;
|
|
326
|
+
copiedField: string | null;
|
|
327
|
+
copyHovered: string | null;
|
|
328
|
+
setCopyHovered: (field: string | null) => void;
|
|
329
|
+
onCopy: (field: string, value: string) => void;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function CopyButton({ field, value, copiedField, copyHovered, setCopyHovered, onCopy }: CopyButtonProps) {
|
|
333
|
+
const isCopied = copiedField === field;
|
|
334
|
+
const isHovered = copyHovered === field;
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<button
|
|
338
|
+
type="button"
|
|
339
|
+
onClick={() => onCopy(field, value)}
|
|
340
|
+
style={{
|
|
341
|
+
...s.copyBtn,
|
|
342
|
+
...(isCopied ? s.copyBtnCopied : isHovered ? s.copyBtnHover : {}),
|
|
343
|
+
}}
|
|
344
|
+
onMouseEnter={() => setCopyHovered(field)}
|
|
345
|
+
onMouseLeave={() => setCopyHovered(null)}
|
|
346
|
+
>
|
|
347
|
+
{isCopied ? 'Copied!' : 'Copy'}
|
|
348
|
+
</button>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export default AuthStatus;
|