simple-support-chat 0.1.1 → 0.3.0
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 +302 -3
- package/dist/client/index.cjs +194 -25
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +91 -6
- package/dist/client/index.d.ts +91 -6
- package/dist/client/index.js +195 -27
- package/dist/client/index.js.map +1 -1
- package/dist/server/index.cjs +2176 -9
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +189 -1
- package/dist/server/index.d.ts +189 -1
- package/dist/server/index.js +2166 -10
- package/dist/server/index.js.map +1 -1
- package/package.json +87 -85
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/context.ts","../../src/client/useChatEngine.ts","../../src/client/useColorScheme.ts","../../src/client/ChatBubble.tsx","../../src/client/SupportChatModal.tsx","../../src/client/useSupportChat.ts"],"names":["useState","useCallback","useEffect","useMemo","useRef","jsxs","Fragment","jsx"],"mappings":";;;;;;;;AAEA,IAAM,WAAA,GAAc,yBAAA;AAGpB,SAAS,iBAAA,GAA4B;AACnC,EAAA,MAAM,KAAA,GACJ,gEAAA;AACF,EAAA,IAAI,EAAA,GAAK,EAAA;AACT,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK;AAC3B,IAAA,EAAA,IAAM,KAAA,CAAM,OAAO,IAAA,CAAK,KAAA,CAAM,KAAK,MAAA,EAAO,GAAI,KAAA,CAAM,MAAM,CAAC,CAAA;AAAA,EAC7D;AACA,EAAA,OAAO,EAAA;AACT;AAGO,SAAS,YAAA,GAAuB;AACrC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,iBAAA,EAAkB;AAE5D,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,YAAA,CAAa,OAAA,CAAQ,WAAW,CAAA;AACjD,IAAA,IAAI,UAAU,OAAO,QAAA;AAErB,IAAA,MAAM,QAAQ,iBAAA,EAAkB;AAChC,IAAA,YAAA,CAAa,OAAA,CAAQ,aAAa,KAAK,CAAA;AACvC,IAAA,OAAO,KAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAO,iBAAA,EAAkB;AAAA,EAC3B;AACF;AAGO,SAAS,uBAAA,GAA4C;AAC1D,EAAA,MAAM,YAAY,YAAA,EAAa;AAE/B,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,EAAA;AAAA,MACT,QAAA,EAAU,EAAA;AAAA,MACV,SAAA,EAAW,EAAA;AAAA,MACX,UAAA,EAAY,EAAA;AAAA,MACZ,QAAA,EAAU,EAAA;AAAA,MACV;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,OAAO,QAAA,CAAS,IAAA;AAAA,IACzB,UAAU,QAAA,CAAS,QAAA;AAAA,IACnB,WAAW,SAAA,CAAU,SAAA;AAAA,IACrB,UAAA,EAAY,GAAG,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAA,EAAI,MAAA,CAAO,OAAO,MAAM,CAAA,CAAA;AAAA,IAC1D,QAAA,EAAU,IAAA,CAAK,cAAA,EAAe,CAAE,iBAAgB,CAAE,QAAA;AAAA,IAClD;AAAA,GACF;AACF;;;ACvBO,SAAS,aAAA,CAAc;AAAA,EAC5B,MAAA;AAAA,EACA;AACF,CAAA,EAAuC;AACrC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,cAAA,CAAwB,EAAE,CAAA;AAC1D,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAS,EAAE,CAAA;AACrC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,KAAK,CAAA;AAE5C,EAAA,MAAM,WAAA,GAAcC,kBAAY,YAAY;AAC1C,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,EAAK;AACxB,IAAA,IAAI,CAAC,QAAQ,OAAA,EAAS;AAEtB,IAAA,MAAM,GAAA,GAAmB;AAAA,MACvB,EAAA,EAAI,CAAA,IAAA,EAAO,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AAAA,MACrB,IAAA;AAAA,MACA,MAAA,EAAQ,MAAA;AAAA,MACR,SAAA,EAAW,KAAK,GAAA;AAAI,KACtB;AAEA,IAAA,WAAA,CAAY,CAAC,IAAA,KAAS,CAAC,GAAG,IAAA,EAAM,GAAG,CAAC,CAAA;AACpC,IAAA,QAAA,CAAS,EAAE,CAAA;AACX,IAAA,UAAA,CAAW,IAAI,CAAA;AAEf,IAAA,IAAI;AACF,MAAA,MAAM,UAAU,uBAAA,EAAwB;AACxC,MAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,MAAA,EAAQ;AAAA,QACnC,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,QAC9C,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,UACnB,OAAA,EAAS,IAAA;AAAA,UACT,MAAM,IAAA,IAAQ,KAAA,CAAA;AAAA,UACd,SAAA,EAAW,IAAA,EAAM,EAAA,IAAM,YAAA,EAAa;AAAA,UACpC;AAAA,SACD;AAAA,OACF,CAAA;AAED,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,WAAA,CAAY,CAAC,IAAA,KAAS;AAAA,UACpB,GAAG,IAAA;AAAA,UACH;AAAA,YACE,EAAA,EAAI,CAAA,IAAA,EAAO,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AAAA,YACrB,IAAA,EAAM,2CAAA;AAAA,YACN,MAAA,EAAQ,QAAA;AAAA,YACR,SAAA,EAAW,KAAK,GAAA;AAAI;AACtB,SACD,CAAA;AAAA,MACH;AAAA,IACF,CAAA,CAAA,MAAQ;AACN,MAAA,WAAA,CAAY,CAAC,IAAA,KAAS;AAAA,QACpB,GAAG,IAAA;AAAA,QACH;AAAA,UACE,EAAA,EAAI,CAAA,IAAA,EAAO,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AAAA,UACrB,IAAA,EAAM,qCAAA;AAAA,UACN,MAAA,EAAQ,QAAA;AAAA,UACR,SAAA,EAAW,KAAK,GAAA;AAAI;AACtB,OACD,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,KAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,IAAI,CAAC,CAAA;AAEjC,EAAA,MAAM,aAAA,GAAgBA,iBAAA;AAAA,IACpB,CAAC,CAAA,KAA2B;AAC1B,MAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAC,EAAE,QAAA,EAAU;AACpC,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,KAAK,WAAA,EAAY;AAAA,MACnB;AAAA,IACF,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,OAAO,EAAE,QAAA,EAAU,KAAA,EAAO,QAAA,EAAU,OAAA,EAAS,aAAa,aAAA,EAAc;AAC1E;ACjGO,SAAS,cAAA,GAA8B;AAC5C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAID,eAAsB,MAAM;AACtD,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,OAAA;AAC1C,IAAA,OAAO,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA,CAAE,UACrD,MAAA,GACA,OAAA;AAAA,EACN,CAAC,CAAA;AAED,EAAAE,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA;AAC3D,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAA2B;AAC1C,MAAA,SAAA,CAAU,CAAA,CAAE,OAAA,GAAU,MAAA,GAAS,OAAO,CAAA;AAAA,IACxC,CAAA;AACA,IAAA,EAAA,CAAG,gBAAA,CAAiB,UAAU,OAAO,CAAA;AACrC,IAAA,OAAO,MAAM,EAAA,CAAG,mBAAA,CAAoB,QAAA,EAAU,OAAO,CAAA;AAAA,EACvD,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,MAAA;AACT;AAgBA,IAAM,WAAA,GAA2B;AAAA,EAC/B,OAAA,EAAS,SAAA;AAAA,EACT,WAAA,EAAa,SAAA;AAAA,EACb,OAAA,EAAS,SAAA;AAAA,EACT,WAAA,EAAa,SAAA;AAAA,EACb,SAAA,EAAW,SAAA;AAAA,EACX,gBAAA,EAAkB,SAAA;AAAA,EAClB,UAAA,EAAY,SAAA;AAAA,EACZ,YAAA,EAAc,SAAA;AAAA,EACd,SAAA,EAAW,SAAA;AAAA,EACX,eAAA,EAAiB;AACnB,CAAA;AAEA,IAAM,UAAA,GAA0B;AAAA,EAC9B,OAAA,EAAS,SAAA;AAAA,EACT,WAAA,EAAa,SAAA;AAAA,EACb,OAAA,EAAS,SAAA;AAAA,EACT,WAAA,EAAa,SAAA;AAAA,EACb,SAAA,EAAW,SAAA;AAAA,EACX,gBAAA,EAAkB,SAAA;AAAA,EAClB,UAAA,EAAY,SAAA;AAAA,EACZ,YAAA,EAAc,SAAA;AAAA,EACd,SAAA,EAAW,SAAA;AAAA,EACX,eAAA,EAAiB;AACnB,CAAA;AAEO,SAAS,eAAe,MAAA,EAAkC;AAC/D,EAAA,OAAO,MAAA,KAAW,SAAS,UAAA,GAAa,WAAA;AAC1C;AC9DA,SAAS,eAAA,GAAwB;AAC/B,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,aAAA,CAAc,+BAA+B,CAAA,EAAG;AAE7D,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,EAAA,KAAA,CAAM,YAAA,CAAa,+BAA+B,EAAE,CAAA;AACpD,EAAA,KAAA,CAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAUpB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,KAAK,CAAA;AACjC;AAaO,SAAS,UAAA,CAAW;AAAA,EACzB,MAAA;AAAA,EACA,QAAA,GAAW,cAAA;AAAA,EACX,KAAA,GAAQ,SAAA;AAAA,EACR,KAAA,GAAQ,SAAA;AAAA,EACR,WAAA,GAAc,mBAAA;AAAA,EACd,IAAA,GAAO,IAAA;AAAA,EACP;AACF,CAAA,EAAoB;AAClB,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIF,eAAS,KAAK,CAAA;AAG1C,EAAA,MAAM,cAAc,cAAA,EAAe;AACnC,EAAA,MAAM,KAAA,GAAQG,cAAQ,MAAM,cAAA,CAAe,WAAW,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAGtE,EAAA,MAAM,EAAE,QAAA,EAAU,KAAA,EAAO,QAAA,EAAU,OAAA,EAAS,WAAA,EAAa,aAAA,EAAc,GACrE,aAAA,CAAc,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAGhC,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIH,cAAAA;AAAA,IAClC;AAAA,GACF;AAEA,EAAA,MAAM,QAAA,GAAWI,aAAuB,IAAI,CAAA;AAC5C,EAAA,MAAM,cAAA,GAAiBA,aAAuB,IAAI,CAAA;AAClD,EAAA,MAAM,QAAA,GAAWA,aAAyB,IAAI,CAAA;AAG9C,EAAAF,gBAAU,MAAM;AACd,IAAA,eAAA,EAAgB;AAAA,EAClB,CAAA,EAAG,EAAE,CAAA;AAGL,EAAAA,gBAAU,MAAM;AACd,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,aAAA,CAAc,MAAM,CAAA;AAAA,IACtB,CAAA,MAAA,IAAW,eAAe,MAAA,EAAQ;AAEhC,MAAA,aAAA,CAAc,SAAS,CAAA;AAAA,IACzB;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,MAAM,kBAAA,GAAqBD,kBAAY,MAAM;AAC3C,IAAA,IAAI,eAAe,SAAA,EAAW;AAC5B,MAAA,aAAA,CAAc,QAAQ,CAAA;AAAA,IACxB;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAAC,gBAAU,MAAM;AACd,IAAA,IAAI,UAAA,KAAe,MAAA,IAAU,QAAA,CAAS,OAAA,EAAS;AAC7C,MAAA,QAAA,CAAS,QAAQ,KAAA,EAAM;AAAA,IACzB;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,EAAAA,gBAAU,MAAM;AACd,IAAA,cAAA,CAAe,OAAA,EAAS,cAAA,CAAe,EAAE,QAAA,EAAU,UAAU,CAAA;AAAA,EAC/D,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAAA,gBAAU,MAAM;AACd,IAAA,IAAI,UAAA,KAAe,MAAA,IAAU,CAAC,QAAA,CAAS,OAAA,EAAS;AAEhD,IAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AAEvB,IAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAAqB;AAC9C,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,QAAA,SAAA,CAAU,KAAK,CAAA;AACf,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,CAAA,CAAE,QAAQ,KAAA,EAAO;AAErB,MAAA,MAAM,YAAY,KAAA,CAAM,gBAAA;AAAA,QACtB;AAAA,OACF;AACA,MAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAE5B,MAAA,MAAM,KAAA,GAAQ,UAAU,CAAC,CAAA;AACzB,MAAA,MAAM,IAAA,GAAO,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA;AAE3C,MAAA,IAAI,EAAE,QAAA,EAAU;AACd,QAAA,IAAI,QAAA,CAAS,kBAAkB,KAAA,EAAO;AACpC,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,IAAA,CAAK,KAAA,EAAM;AAAA,QACb;AAAA,MACF,CAAA,MAAO;AACL,QAAA,IAAI,QAAA,CAAS,kBAAkB,IAAA,EAAM;AACnC,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,KAAA,CAAM,KAAA,EAAM;AAAA,QACd;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,iBAAiB,CAAA;AACtD,IAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,SAAA,EAAW,iBAAiB,CAAA;AAAA,EACxE,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,MAAA,GAASD,iBAAAA,CAAY,MAAM,SAAA,CAAU,CAAC,MAAM,CAAC,CAAC,CAAA,EAAG,EAAE,CAAA;AAGzD,EAAA,MAAM,cAAA,GAAsC;AAAA,IAC1C,QAAA,EAAU,OAAA;AAAA,IACV,MAAA,EAAQ,KAAA;AAAA,IACR,GAAI,QAAA,CAAS,QAAA,CAAS,QAAQ,CAAA,GAAI,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,EAAE,GAAA,EAAK,MAAA,EAAO;AAAA,IACrE,GAAI,QAAA,CAAS,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,KAAA,EAAO,MAAA,EAAO,GAAI,EAAE,IAAA,EAAM,MAAA;AAAO,GACtE;AAIA,EAAA,MAAM,mBAAA,GAA2C;AAAA,IAC/C,QAAA,EAAU,OAAA;AAAA,IACV,MAAA,EAAQ,KAAA;AAAA,IACR,KAAA,EAAO,OAAA;AAAA,IACP,QAAA,EAAU,oBAAA;AAAA,IACV,MAAA,EAAQ,OAAA;AAAA,IACR,SAAA,EAAW,qBAAA;AAAA,IACX,GAAI,QAAA,CAAS,QAAA,CAAS,QAAQ,CAAA,GAAI,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,EAAE,GAAA,EAAK,MAAA,EAAO;AAAA,IACrE,GAAI,QAAA,CAAS,QAAA,CAAS,OAAO,CAAA,GAAI,EAAE,KAAA,EAAO,MAAA,EAAO,GAAI,EAAE,IAAA,EAAM,MAAA;AAAO,GACtE;AAGA,EAAA,MAAM,SAAA,GAAY,UAAA,KAAe,MAAA,IAAU,UAAA,KAAe,SAAA;AAE1D,EAAA,uBACEI,eAAA,CAAAC,mBAAA,EAAA,EAEG,QAAA,EAAA;AAAA,IAAA,SAAA,mCACE,OAAA,EAAA,EAAO,QAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAAA,CAAA,EAaN,CAAA;AAAA,IAIH,IAAA,oBACCC,cAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAS,MAAA;AAAA,QACT,YAAA,EAAY,SAAS,oBAAA,GAAuB,mBAAA;AAAA,QAC5C,eAAA,EAAe,MAAA;AAAA,QACf,eAAA,EAAc,QAAA;AAAA,QACd,KAAA,EAAO;AAAA,UACL,GAAG,cAAA;AAAA,UACH,KAAA,EAAO,MAAA;AAAA,UACP,MAAA,EAAQ,MAAA;AAAA,UACR,YAAA,EAAc,KAAA;AAAA,UACd,eAAA,EAAiB,KAAA;AAAA,UACjB,MAAA,EAAQ,MAAA;AAAA,UACR,MAAA,EAAQ,SAAA;AAAA,UACR,OAAA,EAAS,MAAA;AAAA,UACT,UAAA,EAAY,QAAA;AAAA,UACZ,cAAA,EAAgB,QAAA;AAAA,UAChB,SAAA,EAAW,6BAAA;AAAA,UACX,UAAA,EAAY;AAAA,SACd;AAAA,QACA,YAAA,EAAc,CAAC,CAAA,KAAM;AACnB,UAAA,CAAA,CAAE,aAAA,CAAc,MAAM,SAAA,GAAY,YAAA;AAClC,UAAA,CAAA,CAAE,aAAA,CAAc,MAAM,SAAA,GAAY,4BAAA;AAAA,QACpC,CAAA;AAAA,QACA,YAAA,EAAc,CAAC,CAAA,KAAM;AACnB,UAAA,CAAA,CAAE,aAAA,CAAc,MAAM,SAAA,GAAY,UAAA;AAClC,UAAA,CAAA,CAAE,aAAA,CAAc,MAAM,SAAA,GAAY,6BAAA;AAAA,QACpC,CAAA;AAAA,QAEA,QAAA,kBAAAA,cAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,IAAA;AAAA,YACN,MAAA,EAAO,IAAA;AAAA,YACP,OAAA,EAAQ,WAAA;AAAA,YACR,IAAA,EAAK,MAAA;AAAA,YACL,MAAA,EAAO,OAAA;AAAA,YACP,WAAA,EAAY,GAAA;AAAA,YACZ,aAAA,EAAc,OAAA;AAAA,YACd,cAAA,EAAe,OAAA;AAAA,YACf,aAAA,EAAY,MAAA;AAAA,YAEX,QAAA,EAAA,MAAA,kCACE,MAAA,EAAA,EAAK,CAAA,EAAE,wBAAuB,CAAA,mBAE/BA,cAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,+DAAA,EAAgE;AAAA;AAAA;AAE5E;AAAA,KACF;AAAA,IAID,SAAA,oBACCF,eAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,QAAA;AAAA,QACL,IAAA,EAAK,QAAA;AAAA,QACL,YAAA,EAAW,cAAA;AAAA,QACX,YAAA,EAAW,MAAA;AAAA,QACX,yBAAA,EAAwB,EAAA;AAAA,QACxB,cAAA,EAAgB,kBAAA;AAAA,QAChB,KAAA,EAAO;AAAA,UACL,GAAG,mBAAA;AAAA,UACH,OAAA,EAAS,MAAA;AAAA,UACT,aAAA,EAAe,QAAA;AAAA,UACf,YAAA,EAAc,MAAA;AAAA,UACd,QAAA,EAAU,QAAA;AAAA,UACV,SAAA,EAAW,6BAAA;AAAA,UACX,UAAA,EACE,mEAAA;AAAA,UACF,QAAA,EAAU,MAAA;AAAA,UACV,iBAAiB,KAAA,CAAM,OAAA;AAAA,UACvB,MAAA,EAAQ,CAAA,UAAA,EAAa,KAAA,CAAM,WAAW,CAAA,CAAA;AAAA,UACtC,SAAA,EACE,UAAA,KAAe,MAAA,GACX,qCAAA,GACA;AAAA,SACR;AAAA,QAGA,QAAA,EAAA;AAAA,0BAAAA,eAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAO;AAAA,gBACL,eAAA,EAAiB,KAAA;AAAA,gBACjB,KAAA,EAAO,MAAA;AAAA,gBACP,OAAA,EAAS,MAAA;AAAA,gBACT,OAAA,EAAS,MAAA;AAAA,gBACT,UAAA,EAAY,QAAA;AAAA,gBACZ,cAAA,EAAgB,eAAA;AAAA,gBAChB,UAAA,EAAY;AAAA,eACd;AAAA,cAEA,QAAA,EAAA;AAAA,gCAAAE,cAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,UAAA,EAAY,KAAK,QAAA,EAAU,MAAA,IAAW,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,gCAC3DA,cAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBACC,OAAA,EAAS,MAAM,SAAA,CAAU,KAAK,CAAA;AAAA,oBAC9B,YAAA,EAAW,YAAA;AAAA,oBACX,KAAA,EAAO;AAAA,sBACL,UAAA,EAAY,MAAA;AAAA,sBACZ,MAAA,EAAQ,MAAA;AAAA,sBACR,KAAA,EAAO,MAAA;AAAA,sBACP,MAAA,EAAQ,SAAA;AAAA,sBACR,OAAA,EAAS,KAAA;AAAA,sBACT,UAAA,EAAY,CAAA;AAAA,sBACZ,QAAA,EAAU;AAAA,qBACZ;AAAA,oBACD,QAAA,EAAA;AAAA;AAAA;AAED;AAAA;AAAA,WACF;AAAA,0BAGAF,eAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,KAAA;AAAA,cACL,WAAA,EAAU,QAAA;AAAA,cACV,YAAA,EAAW,eAAA;AAAA,cACX,KAAA,EAAO;AAAA,gBACL,IAAA,EAAM,CAAA;AAAA,gBACN,SAAA,EAAW,MAAA;AAAA,gBACX,OAAA,EAAS,MAAA;AAAA,gBACT,OAAA,EAAS,MAAA;AAAA,gBACT,aAAA,EAAe,QAAA;AAAA,gBACf,GAAA,EAAK;AAAA,eACP;AAAA,cAEC,QAAA,EAAA;AAAA,gBAAA,QAAA,CAAS,WAAW,CAAA,oBACnBE,cAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBACC,KAAA,EAAO;AAAA,sBACL,OAAO,KAAA,CAAM,SAAA;AAAA,sBACb,SAAA,EAAW,QAAA;AAAA,sBACX,SAAA,EAAW;AAAA,qBACb;AAAA,oBACD,QAAA,EAAA;AAAA;AAAA,iBAED;AAAA,gBAED,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,qBACbA,cAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBAEC,KAAA,EAAO;AAAA,sBACL,SAAA,EACE,GAAA,CAAI,MAAA,KAAW,MAAA,GAAS,UAAA,GAAa,YAAA;AAAA,sBACvC,QAAA,EAAU,KAAA;AAAA,sBACV,OAAA,EAAS,WAAA;AAAA,sBACT,YAAA,EACE,GAAA,CAAI,MAAA,KAAW,MAAA,GACX,oBAAA,GACA,oBAAA;AAAA,sBACN,eAAA,EACE,GAAA,CAAI,MAAA,KAAW,MAAA,GAAS,QAAQ,KAAA,CAAM,UAAA;AAAA,sBACxC,KAAA,EAAO,GAAA,CAAI,MAAA,KAAW,MAAA,GAAS,SAAS,KAAA,CAAM,YAAA;AAAA,sBAC9C,SAAA,EAAW;AAAA,qBACb;AAAA,oBAEC,QAAA,EAAA,GAAA,CAAI;AAAA,mBAAA;AAAA,kBAhBA,GAAA,CAAI;AAAA,iBAkBZ,CAAA;AAAA,gCACDA,cAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,cAAA,EAAgB;AAAA;AAAA;AAAA,WAC5B;AAAA,0BAGAF,eAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAO;AAAA,gBACL,SAAA,EAAW,CAAA,UAAA,EAAa,KAAA,CAAM,eAAe,CAAA,CAAA;AAAA,gBAC7C,OAAA,EAAS,MAAA;AAAA,gBACT,OAAA,EAAS,MAAA;AAAA,gBACT,GAAA,EAAK,KAAA;AAAA,gBACL,UAAA,EAAY;AAAA,eACd;AAAA,cAEA,QAAA,EAAA;AAAA,gCAAAE,cAAA;AAAA,kBAAC,OAAA;AAAA,kBAAA;AAAA,oBACC,GAAA,EAAK,QAAA;AAAA,oBACL,IAAA,EAAK,MAAA;AAAA,oBACL,KAAA,EAAO,KAAA;AAAA,oBACP,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,oBACxC,SAAA,EAAW,aAAA;AAAA,oBACX,WAAA;AAAA,oBACA,YAAA,EAAW,mBAAA;AAAA,oBACX,KAAA,EAAO;AAAA,sBACL,IAAA,EAAM,CAAA;AAAA,sBACN,MAAA,EAAQ,CAAA,UAAA,EAAa,KAAA,CAAM,WAAW,CAAA,CAAA;AAAA,sBACtC,YAAA,EAAc,KAAA;AAAA,sBACd,OAAA,EAAS,WAAA;AAAA,sBACT,QAAA,EAAU,MAAA;AAAA,sBACV,OAAA,EAAS,MAAA;AAAA,sBACT,UAAA,EAAY,SAAA;AAAA,sBACZ,iBAAiB,KAAA,CAAM,OAAA;AAAA,sBACvB,OAAO,KAAA,CAAM;AAAA;AACf;AAAA,iBACF;AAAA,gCACAA,cAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBACC,OAAA,EAAS,MAAM,KAAK,WAAA,EAAY;AAAA,oBAChC,QAAA,EAAU,OAAA,IAAW,CAAC,KAAA,CAAM,IAAA,EAAK;AAAA,oBACjC,YAAA,EAAW,cAAA;AAAA,oBACX,KAAA,EAAO;AAAA,sBACL,eAAA,EAAiB,KAAA;AAAA,sBACjB,KAAA,EAAO,MAAA;AAAA,sBACP,MAAA,EAAQ,MAAA;AAAA,sBACR,YAAA,EAAc,KAAA;AAAA,sBACd,OAAA,EAAS,WAAA;AAAA,sBACT,QACE,OAAA,IAAW,CAAC,KAAA,CAAM,IAAA,KAAS,aAAA,GAAgB,SAAA;AAAA,sBAC7C,SAAS,OAAA,IAAW,CAAC,KAAA,CAAM,IAAA,KAAS,GAAA,GAAM,CAAA;AAAA,sBAC1C,UAAA,EAAY,GAAA;AAAA,sBACZ,QAAA,EAAU,MAAA;AAAA,sBACV,UAAA,EAAY,SAAA;AAAA,sBACZ,UAAA,EAAY;AAAA,qBACd;AAAA,oBACD,QAAA,EAAA;AAAA;AAAA;AAED;AAAA;AAAA;AACF;AAAA;AAAA;AACF,GAAA,EAEJ,CAAA;AAEJ;ACrYA,SAAS,oBAAA,GAA6B;AACpC,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,aAAA,CAAc,qCAAqC,CAAA,EAAG;AAEnE,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,EAAA,KAAA,CAAM,YAAA,CAAa,qCAAqC,EAAE,CAAA;AAC1D,EAAA,KAAA,CAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAkBpB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,KAAK,CAAA;AACjC;AAgCO,SAAS,gBAAA,CAAiB;AAAA,EAC/B,MAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA,GAAQ,SAAA;AAAA,EACR,KAAA,GAAQ,YAAA;AAAA,EACR,WAAA,GAAc,mBAAA;AAAA,EACd;AACF,CAAA,EAA0B;AACxB,EAAA,MAAM,EAAE,QAAA,EAAU,KAAA,EAAO,QAAA,EAAU,OAAA,EAAS,WAAA,EAAa,aAAA,EAAc,GACrE,aAAA,CAAc,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAEhC,EAAA,MAAM,cAAc,cAAA,EAAe;AACnC,EAAA,MAAM,KAAA,GAAQJ,cAAQ,MAAM,cAAA,CAAe,WAAW,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEtE,EAAA,MAAM,QAAA,GAAWC,aAAuB,IAAI,CAAA;AAC5C,EAAA,MAAM,QAAA,GAAWA,aAAyB,IAAI,CAAA;AAC9C,EAAA,MAAM,cAAA,GAAiBA,aAAuB,IAAI,CAAA;AAClD,EAAA,MAAM,UAAA,GAAaA,aAAO,KAAK,CAAA;AAC/B,EAAA,MAAM,WAAA,GAAcA,aAAuB,IAAI,CAAA;AAG/C,EAAAF,gBAAU,MAAM;AACd,IAAA,oBAAA,EAAqB;AAAA,EACvB,CAAA,EAAG,EAAE,CAAA;AAGL,EAAAA,gBAAU,MAAM;AACd,IAAA,IAAI,MAAA,IAAU,SAAS,OAAA,EAAS;AAE9B,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,SAAS,OAAA,EAAS,KAAA,IAAS,EAAE,CAAA;AAC5D,MAAA,OAAO,MAAM,aAAa,KAAK,CAAA;AAAA,IACjC;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAAA,gBAAU,MAAM;AACd,IAAA,cAAA,CAAe,OAAA,EAAS,cAAA,CAAe,EAAE,QAAA,EAAU,UAAU,CAAA;AAAA,EAC/D,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAAA,gBAAU,MAAM;AACd,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,QAAA;AACjC,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,QAAA;AAC/B,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,IAAA;AAAA,MACjC,CAAA;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAAA,gBAAU,MAAM;AACd,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,QAAA,CAAS,OAAA,EAAS;AAElC,IAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AAEvB,IAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAAqB;AAC9C,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,QAAA,OAAA,EAAQ;AACR,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,CAAA,CAAE,QAAQ,KAAA,EAAO;AAErB,MAAA,MAAM,YAAY,KAAA,CAAM,gBAAA;AAAA,QACtB;AAAA,OACF;AACA,MAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAE5B,MAAA,MAAM,KAAA,GAAQ,UAAU,CAAC,CAAA;AACzB,MAAA,MAAM,IAAA,GAAO,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA;AAE3C,MAAA,IAAI,EAAE,QAAA,EAAU;AACd,QAAA,IAAI,QAAA,CAAS,kBAAkB,KAAA,EAAO;AACpC,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,IAAA,CAAK,KAAA,EAAM;AAAA,QACb;AAAA,MACF,CAAA,MAAO;AACL,QAAA,IAAI,QAAA,CAAS,kBAAkB,IAAA,EAAM;AACnC,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,KAAA,CAAM,KAAA,EAAM;AAAA,QACd;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,iBAAiB,CAAA;AACtD,IAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,SAAA,EAAW,iBAAiB,CAAA;AAAA,EACxE,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAO,CAAC,CAAA;AAGpB,EAAA,MAAM,WAAA,GAAcD,kBAAY,MAAM;AACpC,IAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AACrB,IAAA,OAAA,EAAQ;AAAA,EACV,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAGZ,EAAA,MAAM,mBAAA,GAAsBA,iBAAAA;AAAA,IAC1B,CAAC,CAAA,KAAwB;AACvB,MAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,aAAA,EAAe;AAChC,QAAA,WAAA,EAAY;AAAA,MACd;AAAA,IACF,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AAEpB,EAAA,uBACEI,eAAAA,CAAAC,mBAAAA,EAAA,EAEE,QAAA,EAAA;AAAA,oBAAAC,eAAC,OAAA,EAAA,EAAO,QAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA,EAeN,CAAA;AAAA,oBAGFA,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,WAAA;AAAA,QACL,kCAAA,EAAiC,EAAA;AAAA,QACjC,OAAA,EAAS,mBAAA;AAAA,QACT,KAAA,EAAO;AAAA,UACL,QAAA,EAAU,OAAA;AAAA,UACV,KAAA,EAAO,CAAA;AAAA,UACP,MAAA,EAAQ,GAAA;AAAA,UACR,eAAA,EAAiB,oBAAA;AAAA,UACjB,OAAA,EAAS,MAAA;AAAA,UACT,UAAA,EAAY,QAAA;AAAA,UACZ,cAAA,EAAgB,QAAA;AAAA,UAChB,UAAA,EACE,mEAAA;AAAA,UACF,QAAA,EAAU,MAAA;AAAA,UACV,SAAA,EAAW;AAAA,SACb;AAAA,QAGA,QAAA,kBAAAF,eAAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,QAAA;AAAA,YACL,IAAA,EAAK,QAAA;AAAA,YACL,YAAA,EAAW,cAAA;AAAA,YACX,YAAA,EAAW,MAAA;AAAA,YACX,yBAAA,EAAwB,EAAA;AAAA,YACxB,KAAA,EAAO;AAAA,cACL,KAAA,EAAO,OAAA;AAAA,cACP,QAAA,EAAU,oBAAA;AAAA,cACV,MAAA,EAAQ,OAAA;AAAA,cACR,SAAA,EAAW,oBAAA;AAAA,cACX,iBAAiB,KAAA,CAAM,OAAA;AAAA,cACvB,YAAA,EAAc,MAAA;AAAA,cACd,QAAA,EAAU,QAAA;AAAA,cACV,OAAA,EAAS,MAAA;AAAA,cACT,aAAA,EAAe,QAAA;AAAA,cACf,SAAA,EAAW,gCAAA;AAAA,cACX,SAAA,EAAW;AAAA,aACb;AAAA,YAGA,QAAA,EAAA;AAAA,8BAAAA,eAAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO;AAAA,oBACL,eAAA,EAAiB,KAAA;AAAA,oBACjB,KAAA,EAAO,MAAA;AAAA,oBACP,OAAA,EAAS,WAAA;AAAA,oBACT,OAAA,EAAS,MAAA;AAAA,oBACT,UAAA,EAAY,QAAA;AAAA,oBACZ,cAAA,EAAgB,eAAA;AAAA,oBAChB,UAAA,EAAY;AAAA,mBACd;AAAA,kBAEA,QAAA,EAAA;AAAA,oCAAAE,cAAAA,CAAC,UAAK,KAAA,EAAO,EAAE,YAAY,GAAA,EAAK,QAAA,EAAU,MAAA,EAAO,EAAI,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,oCAC3DA,cAAAA;AAAA,sBAAC,QAAA;AAAA,sBAAA;AAAA,wBACC,OAAA,EAAS,WAAA;AAAA,wBACT,YAAA,EAAW,YAAA;AAAA,wBACX,KAAA,EAAO;AAAA,0BACL,UAAA,EAAY,MAAA;AAAA,0BACZ,MAAA,EAAQ,MAAA;AAAA,0BACR,KAAA,EAAO,MAAA;AAAA,0BACP,MAAA,EAAQ,SAAA;AAAA,0BACR,OAAA,EAAS,KAAA;AAAA,0BACT,UAAA,EAAY,CAAA;AAAA,0BACZ,QAAA,EAAU;AAAA,yBACZ;AAAA,wBACD,QAAA,EAAA;AAAA;AAAA;AAED;AAAA;AAAA,eACF;AAAA,8BAGAF,eAAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,IAAA,EAAK,KAAA;AAAA,kBACL,WAAA,EAAU,QAAA;AAAA,kBACV,YAAA,EAAW,eAAA;AAAA,kBACX,KAAA,EAAO;AAAA,oBACL,IAAA,EAAM,CAAA;AAAA,oBACN,SAAA,EAAW,MAAA;AAAA,oBACX,OAAA,EAAS,MAAA;AAAA,oBACT,OAAA,EAAS,MAAA;AAAA,oBACT,aAAA,EAAe,QAAA;AAAA,oBACf,GAAA,EAAK;AAAA,mBACP;AAAA,kBAEC,QAAA,EAAA;AAAA,oBAAA,QAAA,CAAS,MAAA,KAAW,qBACnBE,cAAAA;AAAA,sBAAC,KAAA;AAAA,sBAAA;AAAA,wBACC,KAAA,EAAO;AAAA,0BACL,OAAO,KAAA,CAAM,SAAA;AAAA,0BACb,SAAA,EAAW,QAAA;AAAA,0BACX,SAAA,EAAW;AAAA,yBACb;AAAA,wBACD,QAAA,EAAA;AAAA;AAAA,qBAED;AAAA,oBAED,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,qBACbA,cAAAA;AAAA,sBAAC,KAAA;AAAA,sBAAA;AAAA,wBAEC,KAAA,EAAO;AAAA,0BACL,SAAA,EACE,GAAA,CAAI,MAAA,KAAW,MAAA,GAAS,UAAA,GAAa,YAAA;AAAA,0BACvC,QAAA,EAAU,KAAA;AAAA,0BACV,OAAA,EAAS,WAAA;AAAA,0BACT,YAAA,EACE,GAAA,CAAI,MAAA,KAAW,MAAA,GACX,oBAAA,GACA,oBAAA;AAAA,0BACN,eAAA,EACE,GAAA,CAAI,MAAA,KAAW,MAAA,GAAS,QAAQ,KAAA,CAAM,UAAA;AAAA,0BACxC,KAAA,EAAO,GAAA,CAAI,MAAA,KAAW,MAAA,GAAS,SAAS,KAAA,CAAM,YAAA;AAAA,0BAC9C,SAAA,EAAW;AAAA,yBACb;AAAA,wBAEC,QAAA,EAAA,GAAA,CAAI;AAAA,uBAAA;AAAA,sBAhBA,GAAA,CAAI;AAAA,qBAkBZ,CAAA;AAAA,oCACDA,cAAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,cAAA,EAAgB;AAAA;AAAA;AAAA,eAC5B;AAAA,8BAGAF,eAAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO;AAAA,oBACL,SAAA,EAAW,CAAA,UAAA,EAAa,KAAA,CAAM,eAAe,CAAA,CAAA;AAAA,oBAC7C,OAAA,EAAS,WAAA;AAAA,oBACT,OAAA,EAAS,MAAA;AAAA,oBACT,GAAA,EAAK,KAAA;AAAA,oBACL,UAAA,EAAY;AAAA,mBACd;AAAA,kBAEA,QAAA,EAAA;AAAA,oCAAAE,cAAAA;AAAA,sBAAC,OAAA;AAAA,sBAAA;AAAA,wBACC,GAAA,EAAK,QAAA;AAAA,wBACL,IAAA,EAAK,MAAA;AAAA,wBACL,KAAA,EAAO,KAAA;AAAA,wBACP,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,wBACxC,SAAA,EAAW,aAAA;AAAA,wBACX,WAAA;AAAA,wBACA,YAAA,EAAW,mBAAA;AAAA,wBACX,KAAA,EAAO;AAAA,0BACL,IAAA,EAAM,CAAA;AAAA,0BACN,MAAA,EAAQ,CAAA,UAAA,EAAa,KAAA,CAAM,WAAW,CAAA,CAAA;AAAA,0BACtC,YAAA,EAAc,KAAA;AAAA,0BACd,OAAA,EAAS,WAAA;AAAA,0BACT,QAAA,EAAU,MAAA;AAAA,0BACV,OAAA,EAAS,MAAA;AAAA,0BACT,UAAA,EAAY,SAAA;AAAA,0BACZ,iBAAiB,KAAA,CAAM,OAAA;AAAA,0BACvB,OAAO,KAAA,CAAM;AAAA;AACf;AAAA,qBACF;AAAA,oCACAA,cAAAA;AAAA,sBAAC,QAAA;AAAA,sBAAA;AAAA,wBACC,OAAA,EAAS,MAAM,KAAK,WAAA,EAAY;AAAA,wBAChC,QAAA,EAAU,OAAA,IAAW,CAAC,KAAA,CAAM,IAAA,EAAK;AAAA,wBACjC,YAAA,EAAW,cAAA;AAAA,wBACX,KAAA,EAAO;AAAA,0BACL,eAAA,EAAiB,KAAA;AAAA,0BACjB,KAAA,EAAO,MAAA;AAAA,0BACP,MAAA,EAAQ,MAAA;AAAA,0BACR,YAAA,EAAc,KAAA;AAAA,0BACd,OAAA,EAAS,WAAA;AAAA,0BACT,QACE,OAAA,IAAW,CAAC,KAAA,CAAM,IAAA,KAAS,aAAA,GAAgB,SAAA;AAAA,0BAC7C,SAAS,OAAA,IAAW,CAAC,KAAA,CAAM,IAAA,KAAS,GAAA,GAAM,CAAA;AAAA,0BAC1C,UAAA,EAAY,GAAA;AAAA,0BACZ,QAAA,EAAU,MAAA;AAAA,0BACV,UAAA,EAAY,SAAA;AAAA,0BACZ,UAAA,EAAY;AAAA,yBACd;AAAA,wBACD,QAAA,EAAA;AAAA;AAAA;AAED;AAAA;AAAA;AACF;AAAA;AAAA;AACF;AAAA;AACF,GAAA,EACF,CAAA;AAEJ;ACpWO,SAAS,cAAA,GAAmC;AACjD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIP,eAAS,KAAK,CAAA;AAE1C,EAAA,MAAM,OAAOC,iBAAAA,CAAY,MAAM,UAAU,IAAI,CAAA,EAAG,EAAE,CAAA;AAClD,EAAA,MAAM,QAAQA,iBAAAA,CAAY,MAAM,UAAU,KAAK,CAAA,EAAG,EAAE,CAAA;AACpD,EAAA,MAAM,MAAA,GAASA,iBAAAA,CAAY,MAAM,SAAA,CAAU,CAAC,SAAS,CAAC,IAAI,CAAA,EAAG,EAAE,CAAA;AAE/D,EAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAO;AACvC","file":"index.cjs","sourcesContent":["import type { AnonymousContext } from \"./types\";\r\n\r\nconst SESSION_KEY = \"support-chat-session-id\";\r\n\r\n/** Generate a random session ID */\r\nfunction generateSessionId(): string {\r\n const chars =\r\n \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n let id = \"\";\r\n for (let i = 0; i < 16; i++) {\r\n id += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return id;\r\n}\r\n\r\n/** Get or create a persistent session ID from localStorage */\r\nexport function getSessionId(): string {\r\n if (typeof window === \"undefined\") return generateSessionId();\r\n\r\n try {\r\n const existing = localStorage.getItem(SESSION_KEY);\r\n if (existing) return existing;\r\n\r\n const newId = generateSessionId();\r\n localStorage.setItem(SESSION_KEY, newId);\r\n return newId;\r\n } catch {\r\n // localStorage may be blocked (private browsing, etc.)\r\n return generateSessionId();\r\n }\r\n}\r\n\r\n/** Collect anonymous browser context */\r\nexport function collectAnonymousContext(): AnonymousContext {\r\n const sessionId = getSessionId();\r\n\r\n if (typeof window === \"undefined\") {\r\n return {\r\n pageUrl: \"\",\r\n referrer: \"\",\r\n userAgent: \"\",\r\n screenSize: \"\",\r\n timezone: \"\",\r\n sessionId,\r\n };\r\n }\r\n\r\n return {\r\n pageUrl: window.location.href,\r\n referrer: document.referrer,\r\n userAgent: navigator.userAgent,\r\n screenSize: `${window.screen.width}x${window.screen.height}`,\r\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n sessionId,\r\n };\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { ChatMessage, ChatUser } from \"./types\";\r\nimport { collectAnonymousContext, getSessionId } from \"./context\";\r\n\r\n/** Options for initializing the chat engine */\r\nexport interface ChatEngineOptions {\r\n /** URL of the support chat API endpoint */\r\n apiUrl: string;\r\n /** Authenticated user info (optional) */\r\n user?: ChatUser;\r\n}\r\n\r\n/** Return value from useChatEngine */\r\nexport interface ChatEngineState {\r\n /** All chat messages */\r\n messages: ChatMessage[];\r\n /** Current input value */\r\n input: string;\r\n /** Update the input value */\r\n setInput: (value: string) => void;\r\n /** Whether a message is currently being sent */\r\n sending: boolean;\r\n /** Send the current input as a message */\r\n sendMessage: () => Promise<void>;\r\n /** Handle keydown on the input (Enter to send) */\r\n handleKeyDown: (e: React.KeyboardEvent) => void;\r\n}\r\n\r\n/**\r\n * Shared chat engine hook used by both ChatBubble and SupportChatModal.\r\n * Manages message state, input, and API communication.\r\n */\r\nexport function useChatEngine({\r\n apiUrl,\r\n user,\r\n}: ChatEngineOptions): ChatEngineState {\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\r\n const [sending, setSending] = useState(false);\r\n\r\n const sendMessage = useCallback(async () => {\r\n const text = input.trim();\r\n if (!text || sending) return;\r\n\r\n const msg: ChatMessage = {\r\n id: `msg-${Date.now()}`,\r\n text,\r\n sender: \"user\",\r\n timestamp: Date.now(),\r\n };\r\n\r\n setMessages((prev) => [...prev, msg]);\r\n setInput(\"\");\r\n setSending(true);\r\n\r\n try {\r\n const context = collectAnonymousContext();\r\n const response = await fetch(apiUrl, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n message: text,\r\n user: user ?? undefined,\r\n sessionId: user?.id ?? getSessionId(),\r\n context,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Failed to send message. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n }\r\n } catch {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Connection error. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n } finally {\r\n setSending(false);\r\n }\r\n }, [input, sending, apiUrl, user]);\r\n\r\n const handleKeyDown = useCallback(\r\n (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\" && !e.shiftKey) {\r\n e.preventDefault();\r\n void sendMessage();\r\n }\r\n },\r\n [sendMessage],\r\n );\r\n\r\n return { messages, input, setInput, sending, sendMessage, handleKeyDown };\r\n}\r\n","import { useState, useEffect } from \"react\";\r\n\r\nexport type ColorScheme = \"light\" | \"dark\";\r\n\r\n/**\r\n * Detects the user's system color scheme preference via `prefers-color-scheme`.\r\n * Returns \"dark\" or \"light\". Updates reactively if the user changes their system setting.\r\n */\r\nexport function useColorScheme(): ColorScheme {\r\n const [scheme, setScheme] = useState<ColorScheme>(() => {\r\n if (typeof window === \"undefined\") return \"light\";\r\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\r\n ? \"dark\"\r\n : \"light\";\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === \"undefined\") return;\r\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\r\n const handler = (e: MediaQueryListEvent) => {\r\n setScheme(e.matches ? \"dark\" : \"light\");\r\n };\r\n mq.addEventListener(\"change\", handler);\r\n return () => mq.removeEventListener(\"change\", handler);\r\n }, []);\r\n\r\n return scheme;\r\n}\r\n\r\n/** Resolved color tokens for light and dark modes. */\r\nexport interface ThemeTokens {\r\n panelBg: string;\r\n panelBorder: string;\r\n inputBg: string;\r\n inputBorder: string;\r\n inputText: string;\r\n inputPlaceholder: string;\r\n receivedBg: string;\r\n receivedText: string;\r\n emptyText: string;\r\n inputAreaBorder: string;\r\n}\r\n\r\nconst lightTokens: ThemeTokens = {\r\n panelBg: \"#ffffff\",\r\n panelBorder: \"#e5e7eb\",\r\n inputBg: \"#ffffff\",\r\n inputBorder: \"#d1d5db\",\r\n inputText: \"#1f2937\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#f3f4f6\",\r\n receivedText: \"#1f2937\",\r\n emptyText: \"#9ca3af\",\r\n inputAreaBorder: \"#e5e7eb\",\r\n};\r\n\r\nconst darkTokens: ThemeTokens = {\r\n panelBg: \"#1f2937\",\r\n panelBorder: \"#374151\",\r\n inputBg: \"#111827\",\r\n inputBorder: \"#4b5563\",\r\n inputText: \"#f9fafb\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#374151\",\r\n receivedText: \"#f3f4f6\",\r\n emptyText: \"#6b7280\",\r\n inputAreaBorder: \"#374151\",\r\n};\r\n\r\nexport function getThemeTokens(scheme: ColorScheme): ThemeTokens {\r\n return scheme === \"dark\" ? darkTokens : lightTokens;\r\n}\r\n","import { useState, useCallback, useRef, useEffect, useMemo } from \"react\";\r\nimport type { ChatBubbleProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-slide-in {\r\n from { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * ChatBubble -- floating support chat widget.\r\n *\r\n * Renders a circular button that opens an inline chat panel.\r\n * Messages are sent via POST to the configured `apiUrl`.\r\n *\r\n * @example\r\n * ```tsx\r\n * <ChatBubble apiUrl=\"/api/support\" />\r\n * ```\r\n */\r\nexport function ChatBubble({\r\n apiUrl,\r\n position = \"bottom-right\",\r\n color = \"#2563eb\",\r\n title = \"Support\",\r\n placeholder = \"Type a message...\",\r\n show = true,\r\n user,\r\n}: ChatBubbleProps) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n // Detect system color scheme\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n // Use shared chat engine for message logic\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n // Animation state: \"open\" | \"closing\" | \"closed\"\r\n const [panelState, setPanelState] = useState<\"open\" | \"closing\" | \"closed\">(\r\n \"closed\",\r\n );\r\n\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n // Inject keyframe animations on first render\r\n useEffect(() => {\r\n injectKeyframes();\r\n }, []);\r\n\r\n // Sync isOpen -> panelState\r\n useEffect(() => {\r\n if (isOpen) {\r\n setPanelState(\"open\");\r\n } else if (panelState === \"open\") {\r\n // Start closing animation\r\n setPanelState(\"closing\");\r\n }\r\n }, [isOpen]);\r\n\r\n // When closing animation ends, set to fully closed\r\n const handleAnimationEnd = useCallback(() => {\r\n if (panelState === \"closing\") {\r\n setPanelState(\"closed\");\r\n }\r\n }, [panelState]);\r\n\r\n // Focus input when panel opens\r\n useEffect(() => {\r\n if (panelState === \"open\" && inputRef.current) {\r\n inputRef.current.focus();\r\n }\r\n }, [panelState]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Focus trap: keep Tab cycling inside the dialog when open\r\n useEffect(() => {\r\n if (panelState !== \"open\" || !panelRef.current) return;\r\n\r\n const panel = panelRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n setIsOpen(false);\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = panel.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [panelState]);\r\n\r\n const toggle = useCallback(() => setIsOpen((o) => !o), []);\r\n\r\n // Position styles for the bubble button\r\n const positionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n ...(position.includes(\"bottom\") ? { bottom: \"20px\" } : { top: \"20px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Panel position & responsive sizing\r\n // On mobile (<480px viewport), expand to near-full screen\r\n const panelPositionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n width: \"380px\",\r\n maxWidth: \"calc(100vw - 40px)\",\r\n height: \"500px\",\r\n maxHeight: \"calc(100vh - 120px)\",\r\n ...(position.includes(\"bottom\") ? { bottom: \"80px\" } : { top: \"80px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Check if panel is visible (open or animating out)\r\n const showPanel = panelState === \"open\" || panelState === \"closing\";\r\n\r\n return (\r\n <>\r\n {/* Responsive overrides injected as inline <style> */}\r\n {showPanel && (\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-panel] {\r\n width: calc(100vw - 16px) !important;\r\n height: calc(100vh - 100px) !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n left: 8px !important;\r\n right: 8px !important;\r\n bottom: 72px !important;\r\n border-radius: 12px !important;\r\n }\r\n }\r\n `}</style>\r\n )}\r\n\r\n {/* Floating bubble button */}\r\n {show && (\r\n <button\r\n onClick={toggle}\r\n aria-label={isOpen ? \"Close support chat\" : \"Open support chat\"}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"dialog\"\r\n style={{\r\n ...positionStyles,\r\n width: \"56px\",\r\n height: \"56px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: color,\r\n border: \"none\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\r\n transition: \"transform 0.2s ease, box-shadow 0.2s ease\",\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = \"scale(1.1)\";\r\n e.currentTarget.style.boxShadow = \"0 6px 20px rgba(0,0,0,0.2)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = \"scale(1)\";\r\n e.currentTarget.style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n >\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n {isOpen ? (\r\n <path d=\"M18 6L6 18M6 6l12 12\" />\r\n ) : (\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\r\n )}\r\n </svg>\r\n </button>\r\n )}\r\n\r\n {/* Chat panel */}\r\n {showPanel && (\r\n <div\r\n ref={panelRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-panel=\"\"\r\n onAnimationEnd={handleAnimationEnd}\r\n style={{\r\n ...panelPositionStyles,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n borderRadius: \"12px\",\r\n overflow: \"hidden\",\r\n boxShadow: \"0 8px 30px rgba(0,0,0,0.12)\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n backgroundColor: theme.panelBg,\r\n border: `1px solid ${theme.panelBorder}`,\r\n animation:\r\n panelState === \"open\"\r\n ? \"sc-slide-in 0.25s ease-out forwards\"\r\n : \"sc-slide-out 0.2s ease-in forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"16px\" }}>{title}</span>\r\n <button\r\n onClick={() => setIsOpen(false)}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"18px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"40px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"12px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"10px 12px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 16px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useRef, useEffect, useCallback, useMemo } from \"react\";\r\nimport type { SupportChatModalProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for modal keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectModalKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-modal-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-modal-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-modal-backdrop-in {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n }\r\n @keyframes sc-modal-backdrop-out {\r\n from { opacity: 1; }\r\n to { opacity: 0; }\r\n }\r\n @keyframes sc-modal-slide-in {\r\n from { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-modal-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * SupportChatModal -- modal-based support chat.\r\n *\r\n * Renders a centered modal dialog for support chat, designed to be\r\n * triggered from custom UI elements (e.g., \"Contact Us\" links).\r\n *\r\n * Desktop: centered modal, max-width ~500px, with backdrop overlay.\r\n * Mobile: full-screen modal.\r\n *\r\n * Use with the `useSupportChat()` hook:\r\n *\r\n * @example\r\n * ```tsx\r\n * import { SupportChatModal, useSupportChat } from 'support-chat';\r\n *\r\n * function App() {\r\n * const { open, close, isOpen } = useSupportChat();\r\n * return (\r\n * <>\r\n * <button onClick={open}>Contact Us</button>\r\n * <SupportChatModal\r\n * apiUrl=\"/api/support\"\r\n * isOpen={isOpen}\r\n * onClose={close}\r\n * />\r\n * </>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function SupportChatModal({\r\n apiUrl,\r\n isOpen,\r\n onClose,\r\n color = \"#2563eb\",\r\n title = \"Contact Us\",\r\n placeholder = \"Type a message...\",\r\n user,\r\n}: SupportChatModalProps) {\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user });\r\n\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n const modalRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const closingRef = useRef(false);\r\n const backdropRef = useRef<HTMLDivElement>(null);\r\n\r\n // Inject modal keyframes on first render\r\n useEffect(() => {\r\n injectModalKeyframes();\r\n }, []);\r\n\r\n // Focus input when modal opens\r\n useEffect(() => {\r\n if (isOpen && inputRef.current) {\r\n // Small delay to let the modal render before focusing\r\n const timer = setTimeout(() => inputRef.current?.focus(), 50);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [isOpen]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Prevent body scroll when modal is open\r\n useEffect(() => {\r\n if (isOpen) {\r\n const prev = document.body.style.overflow;\r\n document.body.style.overflow = \"hidden\";\r\n return () => {\r\n document.body.style.overflow = prev;\r\n };\r\n }\r\n }, [isOpen]);\r\n\r\n // Focus trap and Escape key handling\r\n useEffect(() => {\r\n if (!isOpen || !modalRef.current) return;\r\n\r\n const modal = modalRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onClose();\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [isOpen, onClose]);\r\n\r\n // Handle close with animation\r\n const handleClose = useCallback(() => {\r\n closingRef.current = true;\r\n onClose();\r\n }, [onClose]);\r\n\r\n // Handle backdrop click\r\n const handleBackdropClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget) {\r\n handleClose();\r\n }\r\n },\r\n [handleClose],\r\n );\r\n\r\n if (!isOpen) return null;\r\n\r\n return (\r\n <>\r\n {/* Responsive style for full-screen on mobile */}\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-modal] {\r\n width: 100vw !important;\r\n height: 100vh !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n border-radius: 0 !important;\r\n margin: 0 !important;\r\n }\r\n [data-support-chat-modal-backdrop] {\r\n align-items: stretch !important;\r\n justify-content: stretch !important;\r\n }\r\n }\r\n `}</style>\r\n\r\n {/* Backdrop overlay */}\r\n <div\r\n ref={backdropRef}\r\n data-support-chat-modal-backdrop=\"\"\r\n onClick={handleBackdropClick}\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n zIndex: 100000,\r\n backgroundColor: \"rgba(0, 0, 0, 0.5)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n animation: \"sc-modal-backdrop-in 0.2s ease-out forwards\",\r\n }}\r\n >\r\n {/* Modal dialog */}\r\n <div\r\n ref={modalRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-modal=\"\"\r\n style={{\r\n width: \"500px\",\r\n maxWidth: \"calc(100vw - 32px)\",\r\n height: \"600px\",\r\n maxHeight: \"calc(100vh - 64px)\",\r\n backgroundColor: theme.panelBg,\r\n borderRadius: \"16px\",\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n boxShadow: \"0 20px 60px rgba(0, 0, 0, 0.2)\",\r\n animation: \"sc-modal-slide-in 0.3s ease-out forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"20px 24px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"18px\" }}>{title}</span>\r\n <button\r\n onClick={handleClose}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"20px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"20px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"60px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"16px 20px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"12px 14px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 20px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { SupportChatState } from \"./types\";\r\n\r\n/**\r\n * Hook that returns controls for opening/closing the support chat.\r\n * Use with `<SupportChatModal />` for modal-triggered chat.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { open, close, toggle, isOpen } = useSupportChat();\r\n * return <button onClick={open}>Contact Us</button>;\r\n * ```\r\n */\r\nexport function useSupportChat(): SupportChatState {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const open = useCallback(() => setIsOpen(true), []);\r\n const close = useCallback(() => setIsOpen(false), []);\r\n const toggle = useCallback(() => setIsOpen((prev) => !prev), []);\r\n\r\n return { open, close, toggle, isOpen };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/context.ts","../../src/client/useChatEngine.ts","../../src/client/useColorScheme.ts","../../src/client/ChatBubble.tsx","../../src/client/SupportChatModal.tsx","../../src/client/useSupportChat.ts","../../src/client/useUnreadCount.ts"],"names":["useState","useRef","useCallback","useEffect","useMemo","jsxs","Fragment","jsx","POLL_INTERVAL"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { AnonymousContext } from \"./types\";\r\n\r\nconst SESSION_KEY = \"support-chat-session-id\";\r\n\r\n/** Generate a random session ID */\r\nfunction generateSessionId(): string {\r\n const chars =\r\n \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\r\n let id = \"\";\r\n for (let i = 0; i < 16; i++) {\r\n id += chars.charAt(Math.floor(Math.random() * chars.length));\r\n }\r\n return id;\r\n}\r\n\r\n/** Get or create a persistent session ID from localStorage */\r\nexport function getSessionId(): string {\r\n if (typeof window === \"undefined\") return generateSessionId();\r\n\r\n try {\r\n const existing = localStorage.getItem(SESSION_KEY);\r\n if (existing) return existing;\r\n\r\n const newId = generateSessionId();\r\n localStorage.setItem(SESSION_KEY, newId);\r\n return newId;\r\n } catch {\r\n // localStorage may be blocked (private browsing, etc.)\r\n return generateSessionId();\r\n }\r\n}\r\n\r\n/** Collect anonymous browser context */\r\nexport function collectAnonymousContext(): AnonymousContext {\r\n const sessionId = getSessionId();\r\n\r\n if (typeof window === \"undefined\") {\r\n return {\r\n pageUrl: \"\",\r\n referrer: \"\",\r\n userAgent: \"\",\r\n screenSize: \"\",\r\n timezone: \"\",\r\n sessionId,\r\n };\r\n }\r\n\r\n return {\r\n pageUrl: window.location.href,\r\n referrer: document.referrer,\r\n userAgent: navigator.userAgent,\r\n screenSize: `${window.screen.width}x${window.screen.height}`,\r\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\n sessionId,\r\n };\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from \"react\";\r\nimport type { ChatMessage, ChatUser } from \"./types\";\r\nimport { collectAnonymousContext, getSessionId } from \"./context\";\r\n\r\n/** Options for initializing the chat engine */\r\nexport interface ChatEngineOptions {\r\n /** URL of the support chat API endpoint */\r\n apiUrl: string;\r\n /** Authenticated user info (optional) */\r\n user?: ChatUser;\r\n /** URL to poll for team replies. When set, polling starts while chat is active. */\r\n repliesUrl?: string;\r\n /** Whether the chat panel is currently visible (controls polling lifecycle). Defaults to true. */\r\n isOpen?: boolean;\r\n}\r\n\r\n/** Return value from useChatEngine */\r\nexport interface ChatEngineState {\r\n /** All chat messages */\r\n messages: ChatMessage[];\r\n /** Current input value */\r\n input: string;\r\n /** Update the input value */\r\n setInput: (value: string) => void;\r\n /** Whether a message is currently being sent */\r\n sending: boolean;\r\n /** Send the current input as a message */\r\n sendMessage: () => Promise<void>;\r\n /** Handle keydown on the input (Enter to send) */\r\n handleKeyDown: (e: React.KeyboardEvent) => void;\r\n}\r\n\r\n/** Shape of the replies endpoint response */\r\ninterface RepliesApiResponse {\r\n replies: Array<{\r\n id: string;\r\n text: string;\r\n sender: string;\r\n timestamp: string;\r\n threadTs: string;\r\n }>;\r\n lastChecked: string;\r\n}\r\n\r\n/** Polling interval in milliseconds */\r\nconst POLL_INTERVAL = 4000;\r\n\r\n/**\r\n * Shared chat engine hook used by both ChatBubble and SupportChatModal.\r\n * Manages message state, input, API communication, and reply polling.\r\n */\r\nexport function useChatEngine({\r\n apiUrl,\r\n user,\r\n repliesUrl,\r\n isOpen = true,\r\n}: ChatEngineOptions): ChatEngineState {\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\r\n const [sending, setSending] = useState(false);\r\n\r\n // Track the latest reply timestamp for incremental polling (since parameter)\r\n const lastReplyTimestampRef = useRef<string | null>(null);\r\n // Track known reply IDs for deduplication\r\n const knownReplyIdsRef = useRef<Set<string>>(new Set());\r\n\r\n const sendMessage = useCallback(async () => {\r\n const text = input.trim();\r\n if (!text || sending) return;\r\n\r\n const msg: ChatMessage = {\r\n id: `msg-${Date.now()}`,\r\n text,\r\n sender: \"user\",\r\n timestamp: Date.now(),\r\n };\r\n\r\n setMessages((prev) => [...prev, msg]);\r\n setInput(\"\");\r\n setSending(true);\r\n\r\n try {\r\n const context = collectAnonymousContext();\r\n const response = await fetch(apiUrl, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify({\r\n message: text,\r\n user: user ?? undefined,\r\n sessionId: user?.id ?? getSessionId(),\r\n context,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Failed to send message. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n }\r\n } catch {\r\n setMessages((prev) => [\r\n ...prev,\r\n {\r\n id: `err-${Date.now()}`,\r\n text: \"Connection error. Please try again.\",\r\n sender: \"system\",\r\n timestamp: Date.now(),\r\n },\r\n ]);\r\n } finally {\r\n setSending(false);\r\n }\r\n }, [input, sending, apiUrl, user]);\r\n\r\n const handleKeyDown = useCallback(\r\n (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\" && !e.shiftKey) {\r\n e.preventDefault();\r\n void sendMessage();\r\n }\r\n },\r\n [sendMessage],\r\n );\r\n\r\n // Reply polling effect\r\n useEffect(() => {\r\n if (!repliesUrl || !isOpen) return;\r\n\r\n const sessionId = user?.id ?? getSessionId();\r\n\r\n const fetchReplies = async () => {\r\n try {\r\n const params = new URLSearchParams({ sessionId });\r\n if (lastReplyTimestampRef.current) {\r\n params.set(\"since\", lastReplyTimestampRef.current);\r\n }\r\n\r\n const response = await fetch(`${repliesUrl}?${params.toString()}`);\r\n if (!response.ok) return;\r\n\r\n const data: RepliesApiResponse = await response.json();\r\n\r\n if (data.replies.length === 0) return;\r\n\r\n // Filter out already-known replies (deduplication)\r\n const newReplies = data.replies.filter(\r\n (r) => !knownReplyIdsRef.current.has(r.id),\r\n );\r\n\r\n if (newReplies.length === 0) return;\r\n\r\n // Track new reply IDs\r\n for (const r of newReplies) {\r\n knownReplyIdsRef.current.add(r.id);\r\n }\r\n\r\n // Update the since cursor to the latest reply timestamp\r\n const latestTimestamp = newReplies.reduce((latest, r) => {\r\n return r.timestamp > latest ? r.timestamp : latest;\r\n }, lastReplyTimestampRef.current ?? \"\");\r\n lastReplyTimestampRef.current = latestTimestamp;\r\n\r\n // Convert server replies to ChatMessage format and append\r\n const replyMessages: ChatMessage[] = newReplies.map((r) => ({\r\n id: r.id,\r\n text: r.text,\r\n sender: \"received\" as const,\r\n timestamp: new Date(r.timestamp).getTime(),\r\n }));\r\n\r\n setMessages((prev) => [...prev, ...replyMessages]);\r\n } catch {\r\n // Silently ignore polling errors — don't spam the user with error messages\r\n }\r\n };\r\n\r\n // Fetch immediately, then set up interval\r\n void fetchReplies();\r\n const intervalId = setInterval(() => void fetchReplies(), POLL_INTERVAL);\r\n\r\n return () => clearInterval(intervalId);\r\n }, [repliesUrl, isOpen, user]);\r\n\r\n return { messages, input, setInput, sending, sendMessage, handleKeyDown };\r\n}\r\n","import { useState, useEffect } from \"react\";\r\n\r\nexport type ColorScheme = \"light\" | \"dark\";\r\n\r\n/**\r\n * Detects the user's system color scheme preference via `prefers-color-scheme`.\r\n * Returns \"dark\" or \"light\". Updates reactively if the user changes their system setting.\r\n */\r\nexport function useColorScheme(): ColorScheme {\r\n const [scheme, setScheme] = useState<ColorScheme>(() => {\r\n if (typeof window === \"undefined\") return \"light\";\r\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches\r\n ? \"dark\"\r\n : \"light\";\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === \"undefined\") return;\r\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\r\n const handler = (e: MediaQueryListEvent) => {\r\n setScheme(e.matches ? \"dark\" : \"light\");\r\n };\r\n mq.addEventListener(\"change\", handler);\r\n return () => mq.removeEventListener(\"change\", handler);\r\n }, []);\r\n\r\n return scheme;\r\n}\r\n\r\n/** Resolved color tokens for light and dark modes. */\r\nexport interface ThemeTokens {\r\n panelBg: string;\r\n panelBorder: string;\r\n inputBg: string;\r\n inputBorder: string;\r\n inputText: string;\r\n inputPlaceholder: string;\r\n receivedBg: string;\r\n receivedText: string;\r\n emptyText: string;\r\n inputAreaBorder: string;\r\n}\r\n\r\nconst lightTokens: ThemeTokens = {\r\n panelBg: \"#ffffff\",\r\n panelBorder: \"#e5e7eb\",\r\n inputBg: \"#ffffff\",\r\n inputBorder: \"#d1d5db\",\r\n inputText: \"#1f2937\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#f3f4f6\",\r\n receivedText: \"#1f2937\",\r\n emptyText: \"#9ca3af\",\r\n inputAreaBorder: \"#e5e7eb\",\r\n};\r\n\r\nconst darkTokens: ThemeTokens = {\r\n panelBg: \"#1f2937\",\r\n panelBorder: \"#374151\",\r\n inputBg: \"#111827\",\r\n inputBorder: \"#4b5563\",\r\n inputText: \"#f9fafb\",\r\n inputPlaceholder: \"#9ca3af\",\r\n receivedBg: \"#374151\",\r\n receivedText: \"#f3f4f6\",\r\n emptyText: \"#6b7280\",\r\n inputAreaBorder: \"#374151\",\r\n};\r\n\r\nexport function getThemeTokens(scheme: ColorScheme): ThemeTokens {\r\n return scheme === \"dark\" ? darkTokens : lightTokens;\r\n}\r\n","import { useState, useCallback, useRef, useEffect, useMemo } from \"react\";\r\nimport type { ChatBubbleProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-slide-in {\r\n from { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(12px) scale(0.96); }\r\n }\r\n @keyframes sc-badge-scale-in {\r\n from { transform: scale(0); }\r\n to { transform: scale(1); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * ChatBubble -- floating support chat widget.\r\n *\r\n * Renders a circular button that opens an inline chat panel.\r\n * Messages are sent via POST to the configured `apiUrl`.\r\n *\r\n * @example\r\n * ```tsx\r\n * <ChatBubble apiUrl=\"/api/support\" />\r\n * ```\r\n */\r\nexport function ChatBubble({\r\n apiUrl,\r\n position = \"bottom-right\",\r\n color = \"#2563eb\",\r\n title = \"Support\",\r\n placeholder = \"Type a message...\",\r\n show = true,\r\n user,\r\n repliesUrl,\r\n isOpen: isOpenProp,\r\n onOpenChange,\r\n badgeColor = \"#eab308\",\r\n unreadCount = 0,\r\n}: ChatBubbleProps) {\r\n // Determine if we're in controlled mode\r\n const isControlled = isOpenProp !== undefined;\r\n\r\n // Internal state for uncontrolled mode\r\n const [isOpenInternal, setIsOpenInternal] = useState(false);\r\n\r\n // Resolved open state: controlled prop takes precedence\r\n const isOpen = isControlled ? isOpenProp : isOpenInternal;\r\n\r\n // Unified state setter that handles both modes\r\n const setIsOpen = useCallback(\r\n (valueOrUpdater: boolean | ((prev: boolean) => boolean)) => {\r\n const newValue =\r\n typeof valueOrUpdater === \"function\"\r\n ? valueOrUpdater(isOpen)\r\n : valueOrUpdater;\r\n\r\n if (isControlled) {\r\n // In controlled mode, only notify the parent -- don't set internal state\r\n onOpenChange?.(newValue);\r\n } else {\r\n // In uncontrolled mode, update internal state and notify if callback provided\r\n setIsOpenInternal(newValue);\r\n onOpenChange?.(newValue);\r\n }\r\n },\r\n [isControlled, isOpen, onOpenChange],\r\n );\r\n\r\n // Detect system color scheme\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n // Use shared chat engine for message logic\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user, repliesUrl, isOpen });\r\n\r\n // Animation state: \"open\" | \"closing\" | \"closed\"\r\n const [panelState, setPanelState] = useState<\"open\" | \"closing\" | \"closed\">(\r\n \"closed\",\r\n );\r\n\r\n const panelRef = useRef<HTMLDivElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n\r\n // Inject keyframe animations on first render\r\n useEffect(() => {\r\n injectKeyframes();\r\n }, []);\r\n\r\n // Sync isOpen -> panelState\r\n useEffect(() => {\r\n if (isOpen) {\r\n setPanelState(\"open\");\r\n } else if (panelState === \"open\") {\r\n // Start closing animation\r\n setPanelState(\"closing\");\r\n }\r\n }, [isOpen]);\r\n\r\n // When closing animation ends, set to fully closed\r\n const handleAnimationEnd = useCallback(() => {\r\n if (panelState === \"closing\") {\r\n setPanelState(\"closed\");\r\n }\r\n }, [panelState]);\r\n\r\n // Focus input when panel opens\r\n useEffect(() => {\r\n if (panelState === \"open\" && inputRef.current) {\r\n inputRef.current.focus();\r\n }\r\n }, [panelState]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Focus trap: keep Tab cycling inside the dialog when open\r\n useEffect(() => {\r\n if (panelState !== \"open\" || !panelRef.current) return;\r\n\r\n const panel = panelRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n setIsOpen(false);\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = panel.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [panelState, setIsOpen]);\r\n\r\n const toggle = useCallback(() => setIsOpen((o) => !o), [setIsOpen]);\r\n\r\n // Position styles for the bubble button\r\n const positionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n ...(position.includes(\"bottom\") ? { bottom: \"20px\" } : { top: \"20px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Panel position & responsive sizing\r\n // On mobile (<480px viewport), expand to near-full screen\r\n const panelPositionStyles: React.CSSProperties = {\r\n position: \"fixed\",\r\n zIndex: 99999,\r\n width: \"380px\",\r\n maxWidth: \"calc(100vw - 40px)\",\r\n height: \"500px\",\r\n maxHeight: \"calc(100vh - 120px)\",\r\n ...(position.includes(\"bottom\") ? { bottom: \"80px\" } : { top: \"80px\" }),\r\n ...(position.includes(\"right\") ? { right: \"20px\" } : { left: \"20px\" }),\r\n };\r\n\r\n // Check if panel is visible (open or animating out)\r\n const showPanel = panelState === \"open\" || panelState === \"closing\";\r\n\r\n return (\r\n <>\r\n {/* Responsive overrides injected as inline <style> */}\r\n {showPanel && (\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-panel] {\r\n width: calc(100vw - 16px) !important;\r\n height: calc(100vh - 100px) !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n left: 8px !important;\r\n right: 8px !important;\r\n bottom: 72px !important;\r\n border-radius: 12px !important;\r\n }\r\n }\r\n `}</style>\r\n )}\r\n\r\n {/* Floating bubble button */}\r\n {show && (\r\n <button\r\n onClick={toggle}\r\n aria-label={isOpen ? \"Close support chat\" : \"Open support chat\"}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"dialog\"\r\n style={{\r\n ...positionStyles,\r\n width: \"56px\",\r\n height: \"56px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: color,\r\n border: \"none\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\r\n transition: \"transform 0.2s ease, box-shadow 0.2s ease\",\r\n overflow: \"visible\",\r\n }}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.transform = \"scale(1.1)\";\r\n e.currentTarget.style.boxShadow = \"0 6px 20px rgba(0,0,0,0.2)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.transform = \"scale(1)\";\r\n e.currentTarget.style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n >\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"white\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n {isOpen ? (\r\n <path d=\"M18 6L6 18M6 6l12 12\" />\r\n ) : (\r\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\r\n )}\r\n </svg>\r\n {/* Unread badge */}\r\n {!isOpen && unreadCount > 0 && (\r\n <span\r\n data-testid=\"unread-badge\"\r\n aria-label={`${unreadCount > 9 ? \"9+\" : unreadCount} unread messages`}\r\n style={{\r\n position: \"absolute\",\r\n top: \"-4px\",\r\n right: \"-4px\",\r\n minWidth: \"20px\",\r\n height: \"20px\",\r\n borderRadius: \"10px\",\r\n backgroundColor: badgeColor,\r\n color: \"#fff\",\r\n fontSize: \"11px\",\r\n fontWeight: 700,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n padding: \"0 5px\",\r\n lineHeight: 1,\r\n boxShadow: \"0 2px 4px rgba(0,0,0,0.2)\",\r\n animation: \"sc-badge-scale-in 0.2s ease-out forwards\",\r\n }}\r\n >\r\n {unreadCount > 9 ? \"9+\" : unreadCount}\r\n </span>\r\n )}\r\n </button>\r\n )}\r\n\r\n {/* Chat panel */}\r\n {showPanel && (\r\n <div\r\n ref={panelRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-panel=\"\"\r\n onAnimationEnd={handleAnimationEnd}\r\n style={{\r\n ...panelPositionStyles,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n borderRadius: \"12px\",\r\n overflow: \"hidden\",\r\n boxShadow: \"0 8px 30px rgba(0,0,0,0.12)\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n backgroundColor: theme.panelBg,\r\n border: `1px solid ${theme.panelBorder}`,\r\n animation:\r\n panelState === \"open\"\r\n ? \"sc-slide-in 0.25s ease-out forwards\"\r\n : \"sc-slide-out 0.2s ease-in forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"16px\" }}>{title}</span>\r\n <button\r\n onClick={() => setIsOpen(false)}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"18px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"16px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"40px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n data-sender={msg.sender}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"12px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"10px 12px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"10px 16px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useRef, useEffect, useCallback, useMemo } from \"react\";\r\nimport type { SupportChatModalProps } from \"./types\";\r\nimport { useChatEngine } from \"./useChatEngine\";\r\nimport { useColorScheme, getThemeTokens } from \"./useColorScheme\";\r\n\r\n/**\r\n * Inject a <style> tag for modal keyframe animations.\r\n * Called once on first mount. Uses a data-attribute to avoid duplicates.\r\n */\r\nfunction injectModalKeyframes(): void {\r\n if (typeof document === \"undefined\") return;\r\n if (document.querySelector(\"[data-support-chat-modal-keyframes]\")) return;\r\n\r\n const style = document.createElement(\"style\");\r\n style.setAttribute(\"data-support-chat-modal-keyframes\", \"\");\r\n style.textContent = `\r\n @keyframes sc-modal-backdrop-in {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n }\r\n @keyframes sc-modal-backdrop-out {\r\n from { opacity: 1; }\r\n to { opacity: 0; }\r\n }\r\n @keyframes sc-modal-slide-in {\r\n from { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n }\r\n @keyframes sc-modal-slide-out {\r\n from { opacity: 1; transform: translateY(0) scale(1); }\r\n to { opacity: 0; transform: translateY(20px) scale(0.96); }\r\n }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * SupportChatModal -- modal-based support chat.\r\n *\r\n * Renders a centered modal dialog for support chat, designed to be\r\n * triggered from custom UI elements (e.g., \"Contact Us\" links).\r\n *\r\n * Desktop: centered modal, max-width ~500px, with backdrop overlay.\r\n * Mobile: full-screen modal.\r\n *\r\n * Use with the `useSupportChat()` hook:\r\n *\r\n * @example\r\n * ```tsx\r\n * import { SupportChatModal, useSupportChat } from 'support-chat';\r\n *\r\n * function App() {\r\n * const { open, close, isOpen } = useSupportChat();\r\n * return (\r\n * <>\r\n * <button onClick={open}>Contact Us</button>\r\n * <SupportChatModal\r\n * apiUrl=\"/api/support\"\r\n * isOpen={isOpen}\r\n * onClose={close}\r\n * />\r\n * </>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function SupportChatModal({\r\n apiUrl,\r\n isOpen,\r\n onClose,\r\n color = \"#2563eb\",\r\n title = \"Contact Us\",\r\n placeholder = \"Type a message...\",\r\n user,\r\n repliesUrl,\r\n}: SupportChatModalProps) {\r\n const { messages, input, setInput, sending, sendMessage, handleKeyDown } =\r\n useChatEngine({ apiUrl, user, repliesUrl, isOpen });\r\n\r\n const colorScheme = useColorScheme();\r\n const theme = useMemo(() => getThemeTokens(colorScheme), [colorScheme]);\r\n\r\n const modalRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const closingRef = useRef(false);\r\n const backdropRef = useRef<HTMLDivElement>(null);\r\n\r\n // Inject modal keyframes on first render\r\n useEffect(() => {\r\n injectModalKeyframes();\r\n }, []);\r\n\r\n // Focus input when modal opens\r\n useEffect(() => {\r\n if (isOpen && inputRef.current) {\r\n // Small delay to let the modal render before focusing\r\n const timer = setTimeout(() => inputRef.current?.focus(), 50);\r\n return () => clearTimeout(timer);\r\n }\r\n }, [isOpen]);\r\n\r\n // Auto-scroll to latest message\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\r\n }, [messages]);\r\n\r\n // Prevent body scroll when modal is open\r\n useEffect(() => {\r\n if (isOpen) {\r\n const prev = document.body.style.overflow;\r\n document.body.style.overflow = \"hidden\";\r\n return () => {\r\n document.body.style.overflow = prev;\r\n };\r\n }\r\n }, [isOpen]);\r\n\r\n // Focus trap and Escape key handling\r\n useEffect(() => {\r\n if (!isOpen || !modalRef.current) return;\r\n\r\n const modal = modalRef.current;\r\n\r\n const handleTrapKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n onClose();\r\n return;\r\n }\r\n\r\n if (e.key !== \"Tab\") return;\r\n\r\n const focusable = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\r\n );\r\n if (focusable.length === 0) return;\r\n\r\n const first = focusable[0]!;\r\n const last = focusable[focusable.length - 1]!;\r\n\r\n if (e.shiftKey) {\r\n if (document.activeElement === first) {\r\n e.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n if (document.activeElement === last) {\r\n e.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener(\"keydown\", handleTrapKeyDown);\r\n return () => document.removeEventListener(\"keydown\", handleTrapKeyDown);\r\n }, [isOpen, onClose]);\r\n\r\n // Handle close with animation\r\n const handleClose = useCallback(() => {\r\n closingRef.current = true;\r\n onClose();\r\n }, [onClose]);\r\n\r\n // Handle backdrop click\r\n const handleBackdropClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget) {\r\n handleClose();\r\n }\r\n },\r\n [handleClose],\r\n );\r\n\r\n if (!isOpen) return null;\r\n\r\n return (\r\n <>\r\n {/* Responsive style for full-screen on mobile */}\r\n <style>{`\r\n @media (max-width: 479px) {\r\n [data-support-chat-modal] {\r\n width: 100vw !important;\r\n height: 100vh !important;\r\n max-width: none !important;\r\n max-height: none !important;\r\n border-radius: 0 !important;\r\n margin: 0 !important;\r\n }\r\n [data-support-chat-modal-backdrop] {\r\n align-items: stretch !important;\r\n justify-content: stretch !important;\r\n }\r\n }\r\n `}</style>\r\n\r\n {/* Backdrop overlay */}\r\n <div\r\n ref={backdropRef}\r\n data-support-chat-modal-backdrop=\"\"\r\n onClick={handleBackdropClick}\r\n style={{\r\n position: \"fixed\",\r\n inset: 0,\r\n zIndex: 100000,\r\n backgroundColor: \"rgba(0, 0, 0, 0.5)\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontFamily:\r\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: \"14px\",\r\n animation: \"sc-modal-backdrop-in 0.2s ease-out forwards\",\r\n }}\r\n >\r\n {/* Modal dialog */}\r\n <div\r\n ref={modalRef}\r\n role=\"dialog\"\r\n aria-label=\"Support chat\"\r\n aria-modal=\"true\"\r\n data-support-chat-modal=\"\"\r\n style={{\r\n width: \"500px\",\r\n maxWidth: \"calc(100vw - 32px)\",\r\n height: \"600px\",\r\n maxHeight: \"calc(100vh - 64px)\",\r\n backgroundColor: theme.panelBg,\r\n borderRadius: \"16px\",\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n boxShadow: \"0 20px 60px rgba(0, 0, 0, 0.2)\",\r\n animation: \"sc-modal-slide-in 0.3s ease-out forwards\",\r\n }}\r\n >\r\n {/* Header */}\r\n <div\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n padding: \"20px 24px\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <span style={{ fontWeight: 600, fontSize: \"18px\" }}>{title}</span>\r\n <button\r\n onClick={handleClose}\r\n aria-label=\"Close chat\"\r\n style={{\r\n background: \"none\",\r\n border: \"none\",\r\n color: \"#fff\",\r\n cursor: \"pointer\",\r\n padding: \"4px\",\r\n lineHeight: 1,\r\n fontSize: \"20px\",\r\n }}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Messages */}\r\n <div\r\n role=\"log\"\r\n aria-live=\"polite\"\r\n aria-label=\"Chat messages\"\r\n style={{\r\n flex: 1,\r\n overflowY: \"auto\",\r\n padding: \"20px\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n }}\r\n >\r\n {messages.length === 0 && (\r\n <div\r\n style={{\r\n color: theme.emptyText,\r\n textAlign: \"center\",\r\n marginTop: \"60px\",\r\n }}\r\n >\r\n Send us a message and we will get back to you!\r\n </div>\r\n )}\r\n {messages.map((msg) => (\r\n <div\r\n key={msg.id}\r\n data-sender={msg.sender}\r\n style={{\r\n alignSelf:\r\n msg.sender === \"user\" ? \"flex-end\" : \"flex-start\",\r\n maxWidth: \"80%\",\r\n padding: \"10px 14px\",\r\n borderRadius:\r\n msg.sender === \"user\"\r\n ? \"16px 16px 4px 16px\"\r\n : \"16px 16px 16px 4px\",\r\n backgroundColor:\r\n msg.sender === \"user\" ? color : theme.receivedBg,\r\n color: msg.sender === \"user\" ? \"#fff\" : theme.receivedText,\r\n wordBreak: \"break-word\",\r\n }}\r\n >\r\n {msg.text}\r\n </div>\r\n ))}\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n {/* Input */}\r\n <div\r\n style={{\r\n borderTop: `1px solid ${theme.inputAreaBorder}`,\r\n padding: \"16px 20px\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={input}\r\n onChange={(e) => setInput(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n aria-label=\"Type your message\"\r\n style={{\r\n flex: 1,\r\n border: `1px solid ${theme.inputBorder}`,\r\n borderRadius: \"8px\",\r\n padding: \"12px 14px\",\r\n fontSize: \"14px\",\r\n outline: \"none\",\r\n fontFamily: \"inherit\",\r\n backgroundColor: theme.inputBg,\r\n color: theme.inputText,\r\n }}\r\n />\r\n <button\r\n onClick={() => void sendMessage()}\r\n disabled={sending || !input.trim()}\r\n aria-label=\"Send message\"\r\n style={{\r\n backgroundColor: color,\r\n color: \"#fff\",\r\n border: \"none\",\r\n borderRadius: \"8px\",\r\n padding: \"12px 20px\",\r\n cursor:\r\n sending || !input.trim() ? \"not-allowed\" : \"pointer\",\r\n opacity: sending || !input.trim() ? 0.5 : 1,\r\n fontWeight: 600,\r\n fontSize: \"14px\",\r\n fontFamily: \"inherit\",\r\n transition: \"opacity 0.2s ease\",\r\n }}\r\n >\r\n Send\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { useState, useCallback } from \"react\";\r\nimport type { SupportChatState } from \"./types\";\r\n\r\n/**\r\n * Hook that returns controls for opening/closing the support chat.\r\n * Use with `<SupportChatModal />` for modal-triggered chat.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { open, close, toggle, isOpen } = useSupportChat();\r\n * return <button onClick={open}>Contact Us</button>;\r\n * ```\r\n */\r\nexport function useSupportChat(): SupportChatState {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const open = useCallback(() => setIsOpen(true), []);\r\n const close = useCallback(() => setIsOpen(false), []);\r\n const toggle = useCallback(() => setIsOpen((prev) => !prev), []);\r\n\r\n return { open, close, toggle, isOpen };\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from \"react\";\r\nimport type { ChatMessage, ChatUser } from \"./types\";\r\nimport { getSessionId } from \"./context\";\r\n\r\n/** Options for the useUnreadCount hook */\r\nexport interface UnreadCountOptions {\r\n /** URL to poll for team replies (e.g., '/api/support/replies') */\r\n repliesUrl: string;\r\n /** Authenticated user info (optional). When provided, user.id is used as sessionId. */\r\n user?: ChatUser;\r\n /** Whether the chat panel is currently open. Polling pauses when true. */\r\n isOpen: boolean;\r\n}\r\n\r\n/** Return value from useUnreadCount */\r\nexport interface UnreadCountState {\r\n /** Number of unread reply messages */\r\n unreadCount: number;\r\n /** Convenience boolean: true when unreadCount > 0 */\r\n hasUnread: boolean;\r\n /** Pre-fetched reply messages ready to display instantly when chat opens */\r\n pendingReplies: ChatMessage[];\r\n /** Reset unreadCount to 0 and clear pendingReplies */\r\n markAsRead: () => void;\r\n}\r\n\r\n/** Shape of the replies endpoint response */\r\ninterface RepliesApiResponse {\r\n replies: Array<{\r\n id: string;\r\n text: string;\r\n sender: string;\r\n timestamp: string;\r\n threadTs: string;\r\n }>;\r\n lastChecked: string;\r\n}\r\n\r\n/** Background polling interval in milliseconds */\r\nconst POLL_INTERVAL = 4000;\r\n\r\n/**\r\n * Hook that tracks unread reply count in the background via polling.\r\n *\r\n * Polls the replies endpoint every 4 seconds when `isOpen` is false.\r\n * Stops background polling when `isOpen` is true (useChatEngine handles it at that point).\r\n * Pre-fetches reply messages so they appear instantly when the chat opens.\r\n *\r\n * Can be used independently of ChatBubble -- any component can call useUnreadCount\r\n * to build custom unread UX.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { unreadCount, hasUnread, pendingReplies, markAsRead } = useUnreadCount({\r\n * repliesUrl: '/api/support/replies',\r\n * user: currentUser,\r\n * isOpen: false,\r\n * });\r\n *\r\n * return (\r\n * <button onClick={openChat}>\r\n * Support {hasUnread && <span className=\"badge\">{unreadCount}</span>}\r\n * </button>\r\n * );\r\n * ```\r\n */\r\nexport function useUnreadCount({\r\n repliesUrl,\r\n user,\r\n isOpen,\r\n}: UnreadCountOptions): UnreadCountState {\r\n const [pendingReplies, setPendingReplies] = useState<ChatMessage[]>([]);\r\n\r\n // Track the latest reply timestamp for incremental polling (since parameter)\r\n const lastReplyTimestampRef = useRef<string | null>(null);\r\n // Track known reply IDs for deduplication\r\n const knownReplyIdsRef = useRef<Set<string>>(new Set());\r\n // Track previous isOpen value to detect transitions\r\n const prevIsOpenRef = useRef<boolean>(isOpen);\r\n\r\n // Auto-reset when isOpen transitions from false to true\r\n useEffect(() => {\r\n if (isOpen && !prevIsOpenRef.current) {\r\n // Transitioning from closed to open: reset unread state\r\n setPendingReplies([]);\r\n knownReplyIdsRef.current.clear();\r\n lastReplyTimestampRef.current = null;\r\n }\r\n prevIsOpenRef.current = isOpen;\r\n }, [isOpen]);\r\n\r\n const markAsRead = useCallback(() => {\r\n setPendingReplies([]);\r\n knownReplyIdsRef.current.clear();\r\n lastReplyTimestampRef.current = null;\r\n }, []);\r\n\r\n // Background polling effect -- only runs when chat is CLOSED\r\n useEffect(() => {\r\n if (!repliesUrl || isOpen) return;\r\n\r\n const sessionId = user?.id ?? getSessionId();\r\n\r\n const fetchReplies = async () => {\r\n try {\r\n const params = new URLSearchParams({ sessionId });\r\n if (lastReplyTimestampRef.current) {\r\n params.set(\"since\", lastReplyTimestampRef.current);\r\n }\r\n\r\n const response = await fetch(`${repliesUrl}?${params.toString()}`);\r\n if (!response.ok) return;\r\n\r\n const data: RepliesApiResponse = await response.json();\r\n\r\n if (data.replies.length === 0) return;\r\n\r\n // Filter out already-known replies (deduplication)\r\n const newReplies = data.replies.filter(\r\n (r) => !knownReplyIdsRef.current.has(r.id),\r\n );\r\n\r\n if (newReplies.length === 0) return;\r\n\r\n // Track new reply IDs\r\n for (const r of newReplies) {\r\n knownReplyIdsRef.current.add(r.id);\r\n }\r\n\r\n // Update the since cursor to the latest reply timestamp\r\n const latestTimestamp = newReplies.reduce((latest, r) => {\r\n return r.timestamp > latest ? r.timestamp : latest;\r\n }, lastReplyTimestampRef.current ?? \"\");\r\n lastReplyTimestampRef.current = latestTimestamp;\r\n\r\n // Convert server replies to ChatMessage format and append\r\n const replyMessages: ChatMessage[] = newReplies.map((r) => ({\r\n id: r.id,\r\n text: r.text,\r\n sender: \"received\" as const,\r\n timestamp: new Date(r.timestamp).getTime(),\r\n }));\r\n\r\n setPendingReplies((prev) => [...prev, ...replyMessages]);\r\n } catch {\r\n // Silently ignore polling errors\r\n }\r\n };\r\n\r\n // Fetch immediately, then set up interval\r\n void fetchReplies();\r\n const intervalId = setInterval(() => void fetchReplies(), POLL_INTERVAL);\r\n\r\n return () => clearInterval(intervalId);\r\n }, [repliesUrl, isOpen, user]);\r\n\r\n const unreadCount = pendingReplies.length;\r\n\r\n return {\r\n unreadCount,\r\n hasUnread: unreadCount > 0,\r\n pendingReplies,\r\n markAsRead,\r\n };\r\n}\r\n"]}
|
package/dist/client/index.d.cts
CHANGED
|
@@ -29,12 +29,44 @@ interface ChatBubbleProps {
|
|
|
29
29
|
show?: boolean;
|
|
30
30
|
/** Authenticated user info (optional) */
|
|
31
31
|
user?: ChatUser;
|
|
32
|
+
/** URL to poll for team replies (e.g., '/api/support/replies'). Enables bidirectional chat. */
|
|
33
|
+
repliesUrl?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Controlled mode: when provided, overrides internal open/close state.
|
|
36
|
+
* The chat panel is open when `isOpen` is true, closed when false.
|
|
37
|
+
* Use together with `onOpenChange` to control the panel externally.
|
|
38
|
+
*
|
|
39
|
+
* When not provided, ChatBubble manages its own open/close state (uncontrolled mode).
|
|
40
|
+
*
|
|
41
|
+
* Note: `show` controls visibility of the floating button, `isOpen` controls the panel state.
|
|
42
|
+
* Both can be used together (e.g., `show={false}` + `isOpen={true}` = no bubble button but panel is open).
|
|
43
|
+
*/
|
|
44
|
+
isOpen?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Callback fired when the user attempts to change the open/close state.
|
|
47
|
+
* Called when the user clicks the bubble button, close button, or presses Escape.
|
|
48
|
+
* In controlled mode, you must update your state in response to this callback.
|
|
49
|
+
* In uncontrolled mode, this is called as a notification after the state changes internally.
|
|
50
|
+
*/
|
|
51
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Color for the unread badge on the floating bubble button.
|
|
54
|
+
* Only visible when there are unread replies and the chat is closed.
|
|
55
|
+
* Default: '#eab308' (yellow).
|
|
56
|
+
*/
|
|
57
|
+
badgeColor?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Number of unread replies to display on the badge.
|
|
60
|
+
* When provided, a circular badge is shown on the floating bubble button.
|
|
61
|
+
* Typically driven by the `useUnreadCount` hook.
|
|
62
|
+
*/
|
|
63
|
+
unreadCount?: number;
|
|
32
64
|
}
|
|
33
65
|
/** A single chat message */
|
|
34
66
|
interface ChatMessage {
|
|
35
67
|
id: string;
|
|
36
68
|
text: string;
|
|
37
|
-
sender: "user" | "system";
|
|
69
|
+
sender: "user" | "system" | "received";
|
|
38
70
|
timestamp: number;
|
|
39
71
|
}
|
|
40
72
|
/** State returned by useSupportChat hook */
|
|
@@ -64,6 +96,8 @@ interface SupportChatModalProps {
|
|
|
64
96
|
placeholder?: string;
|
|
65
97
|
/** Authenticated user info (optional) */
|
|
66
98
|
user?: ChatUser;
|
|
99
|
+
/** URL to poll for team replies (e.g., '/api/support/replies'). Enables bidirectional chat. */
|
|
100
|
+
repliesUrl?: string;
|
|
67
101
|
}
|
|
68
102
|
/** Anonymous context collected automatically from the browser */
|
|
69
103
|
interface AnonymousContext {
|
|
@@ -86,7 +120,7 @@ interface AnonymousContext {
|
|
|
86
120
|
* <ChatBubble apiUrl="/api/support" />
|
|
87
121
|
* ```
|
|
88
122
|
*/
|
|
89
|
-
declare function ChatBubble({ apiUrl, position, color, title, placeholder, show, user, }: ChatBubbleProps): react_jsx_runtime.JSX.Element;
|
|
123
|
+
declare function ChatBubble({ apiUrl, position, color, title, placeholder, show, user, repliesUrl, isOpen: isOpenProp, onOpenChange, badgeColor, unreadCount, }: ChatBubbleProps): react_jsx_runtime.JSX.Element;
|
|
90
124
|
|
|
91
125
|
/**
|
|
92
126
|
* SupportChatModal -- modal-based support chat.
|
|
@@ -118,7 +152,7 @@ declare function ChatBubble({ apiUrl, position, color, title, placeholder, show,
|
|
|
118
152
|
* }
|
|
119
153
|
* ```
|
|
120
154
|
*/
|
|
121
|
-
declare function SupportChatModal({ apiUrl, isOpen, onClose, color, title, placeholder, user, }: SupportChatModalProps): react_jsx_runtime.JSX.Element | null;
|
|
155
|
+
declare function SupportChatModal({ apiUrl, isOpen, onClose, color, title, placeholder, user, repliesUrl, }: SupportChatModalProps): react_jsx_runtime.JSX.Element | null;
|
|
122
156
|
|
|
123
157
|
/**
|
|
124
158
|
* Hook that returns controls for opening/closing the support chat.
|
|
@@ -138,6 +172,10 @@ interface ChatEngineOptions {
|
|
|
138
172
|
apiUrl: string;
|
|
139
173
|
/** Authenticated user info (optional) */
|
|
140
174
|
user?: ChatUser;
|
|
175
|
+
/** URL to poll for team replies. When set, polling starts while chat is active. */
|
|
176
|
+
repliesUrl?: string;
|
|
177
|
+
/** Whether the chat panel is currently visible (controls polling lifecycle). Defaults to true. */
|
|
178
|
+
isOpen?: boolean;
|
|
141
179
|
}
|
|
142
180
|
/** Return value from useChatEngine */
|
|
143
181
|
interface ChatEngineState {
|
|
@@ -156,13 +194,60 @@ interface ChatEngineState {
|
|
|
156
194
|
}
|
|
157
195
|
/**
|
|
158
196
|
* Shared chat engine hook used by both ChatBubble and SupportChatModal.
|
|
159
|
-
* Manages message state, input, and
|
|
197
|
+
* Manages message state, input, API communication, and reply polling.
|
|
160
198
|
*/
|
|
161
|
-
declare function useChatEngine({ apiUrl, user, }: ChatEngineOptions): ChatEngineState;
|
|
199
|
+
declare function useChatEngine({ apiUrl, user, repliesUrl, isOpen, }: ChatEngineOptions): ChatEngineState;
|
|
200
|
+
|
|
201
|
+
/** Options for the useUnreadCount hook */
|
|
202
|
+
interface UnreadCountOptions {
|
|
203
|
+
/** URL to poll for team replies (e.g., '/api/support/replies') */
|
|
204
|
+
repliesUrl: string;
|
|
205
|
+
/** Authenticated user info (optional). When provided, user.id is used as sessionId. */
|
|
206
|
+
user?: ChatUser;
|
|
207
|
+
/** Whether the chat panel is currently open. Polling pauses when true. */
|
|
208
|
+
isOpen: boolean;
|
|
209
|
+
}
|
|
210
|
+
/** Return value from useUnreadCount */
|
|
211
|
+
interface UnreadCountState {
|
|
212
|
+
/** Number of unread reply messages */
|
|
213
|
+
unreadCount: number;
|
|
214
|
+
/** Convenience boolean: true when unreadCount > 0 */
|
|
215
|
+
hasUnread: boolean;
|
|
216
|
+
/** Pre-fetched reply messages ready to display instantly when chat opens */
|
|
217
|
+
pendingReplies: ChatMessage[];
|
|
218
|
+
/** Reset unreadCount to 0 and clear pendingReplies */
|
|
219
|
+
markAsRead: () => void;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Hook that tracks unread reply count in the background via polling.
|
|
223
|
+
*
|
|
224
|
+
* Polls the replies endpoint every 4 seconds when `isOpen` is false.
|
|
225
|
+
* Stops background polling when `isOpen` is true (useChatEngine handles it at that point).
|
|
226
|
+
* Pre-fetches reply messages so they appear instantly when the chat opens.
|
|
227
|
+
*
|
|
228
|
+
* Can be used independently of ChatBubble -- any component can call useUnreadCount
|
|
229
|
+
* to build custom unread UX.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```tsx
|
|
233
|
+
* const { unreadCount, hasUnread, pendingReplies, markAsRead } = useUnreadCount({
|
|
234
|
+
* repliesUrl: '/api/support/replies',
|
|
235
|
+
* user: currentUser,
|
|
236
|
+
* isOpen: false,
|
|
237
|
+
* });
|
|
238
|
+
*
|
|
239
|
+
* return (
|
|
240
|
+
* <button onClick={openChat}>
|
|
241
|
+
* Support {hasUnread && <span className="badge">{unreadCount}</span>}
|
|
242
|
+
* </button>
|
|
243
|
+
* );
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
declare function useUnreadCount({ repliesUrl, user, isOpen, }: UnreadCountOptions): UnreadCountState;
|
|
162
247
|
|
|
163
248
|
/** Get or create a persistent session ID from localStorage */
|
|
164
249
|
declare function getSessionId(): string;
|
|
165
250
|
/** Collect anonymous browser context */
|
|
166
251
|
declare function collectAnonymousContext(): AnonymousContext;
|
|
167
252
|
|
|
168
|
-
export { type AnonymousContext, type BubblePosition, ChatBubble, type ChatBubbleProps, type ChatEngineOptions, type ChatEngineState, type ChatMessage, type ChatUser, SupportChatModal, type SupportChatModalProps, type SupportChatState, collectAnonymousContext, getSessionId, useChatEngine, useSupportChat };
|
|
253
|
+
export { type AnonymousContext, type BubblePosition, ChatBubble, type ChatBubbleProps, type ChatEngineOptions, type ChatEngineState, type ChatMessage, type ChatUser, SupportChatModal, type SupportChatModalProps, type SupportChatState, type UnreadCountOptions, type UnreadCountState, collectAnonymousContext, getSessionId, useChatEngine, useSupportChat, useUnreadCount };
|
package/dist/client/index.d.ts
CHANGED
|
@@ -29,12 +29,44 @@ interface ChatBubbleProps {
|
|
|
29
29
|
show?: boolean;
|
|
30
30
|
/** Authenticated user info (optional) */
|
|
31
31
|
user?: ChatUser;
|
|
32
|
+
/** URL to poll for team replies (e.g., '/api/support/replies'). Enables bidirectional chat. */
|
|
33
|
+
repliesUrl?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Controlled mode: when provided, overrides internal open/close state.
|
|
36
|
+
* The chat panel is open when `isOpen` is true, closed when false.
|
|
37
|
+
* Use together with `onOpenChange` to control the panel externally.
|
|
38
|
+
*
|
|
39
|
+
* When not provided, ChatBubble manages its own open/close state (uncontrolled mode).
|
|
40
|
+
*
|
|
41
|
+
* Note: `show` controls visibility of the floating button, `isOpen` controls the panel state.
|
|
42
|
+
* Both can be used together (e.g., `show={false}` + `isOpen={true}` = no bubble button but panel is open).
|
|
43
|
+
*/
|
|
44
|
+
isOpen?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Callback fired when the user attempts to change the open/close state.
|
|
47
|
+
* Called when the user clicks the bubble button, close button, or presses Escape.
|
|
48
|
+
* In controlled mode, you must update your state in response to this callback.
|
|
49
|
+
* In uncontrolled mode, this is called as a notification after the state changes internally.
|
|
50
|
+
*/
|
|
51
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Color for the unread badge on the floating bubble button.
|
|
54
|
+
* Only visible when there are unread replies and the chat is closed.
|
|
55
|
+
* Default: '#eab308' (yellow).
|
|
56
|
+
*/
|
|
57
|
+
badgeColor?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Number of unread replies to display on the badge.
|
|
60
|
+
* When provided, a circular badge is shown on the floating bubble button.
|
|
61
|
+
* Typically driven by the `useUnreadCount` hook.
|
|
62
|
+
*/
|
|
63
|
+
unreadCount?: number;
|
|
32
64
|
}
|
|
33
65
|
/** A single chat message */
|
|
34
66
|
interface ChatMessage {
|
|
35
67
|
id: string;
|
|
36
68
|
text: string;
|
|
37
|
-
sender: "user" | "system";
|
|
69
|
+
sender: "user" | "system" | "received";
|
|
38
70
|
timestamp: number;
|
|
39
71
|
}
|
|
40
72
|
/** State returned by useSupportChat hook */
|
|
@@ -64,6 +96,8 @@ interface SupportChatModalProps {
|
|
|
64
96
|
placeholder?: string;
|
|
65
97
|
/** Authenticated user info (optional) */
|
|
66
98
|
user?: ChatUser;
|
|
99
|
+
/** URL to poll for team replies (e.g., '/api/support/replies'). Enables bidirectional chat. */
|
|
100
|
+
repliesUrl?: string;
|
|
67
101
|
}
|
|
68
102
|
/** Anonymous context collected automatically from the browser */
|
|
69
103
|
interface AnonymousContext {
|
|
@@ -86,7 +120,7 @@ interface AnonymousContext {
|
|
|
86
120
|
* <ChatBubble apiUrl="/api/support" />
|
|
87
121
|
* ```
|
|
88
122
|
*/
|
|
89
|
-
declare function ChatBubble({ apiUrl, position, color, title, placeholder, show, user, }: ChatBubbleProps): react_jsx_runtime.JSX.Element;
|
|
123
|
+
declare function ChatBubble({ apiUrl, position, color, title, placeholder, show, user, repliesUrl, isOpen: isOpenProp, onOpenChange, badgeColor, unreadCount, }: ChatBubbleProps): react_jsx_runtime.JSX.Element;
|
|
90
124
|
|
|
91
125
|
/**
|
|
92
126
|
* SupportChatModal -- modal-based support chat.
|
|
@@ -118,7 +152,7 @@ declare function ChatBubble({ apiUrl, position, color, title, placeholder, show,
|
|
|
118
152
|
* }
|
|
119
153
|
* ```
|
|
120
154
|
*/
|
|
121
|
-
declare function SupportChatModal({ apiUrl, isOpen, onClose, color, title, placeholder, user, }: SupportChatModalProps): react_jsx_runtime.JSX.Element | null;
|
|
155
|
+
declare function SupportChatModal({ apiUrl, isOpen, onClose, color, title, placeholder, user, repliesUrl, }: SupportChatModalProps): react_jsx_runtime.JSX.Element | null;
|
|
122
156
|
|
|
123
157
|
/**
|
|
124
158
|
* Hook that returns controls for opening/closing the support chat.
|
|
@@ -138,6 +172,10 @@ interface ChatEngineOptions {
|
|
|
138
172
|
apiUrl: string;
|
|
139
173
|
/** Authenticated user info (optional) */
|
|
140
174
|
user?: ChatUser;
|
|
175
|
+
/** URL to poll for team replies. When set, polling starts while chat is active. */
|
|
176
|
+
repliesUrl?: string;
|
|
177
|
+
/** Whether the chat panel is currently visible (controls polling lifecycle). Defaults to true. */
|
|
178
|
+
isOpen?: boolean;
|
|
141
179
|
}
|
|
142
180
|
/** Return value from useChatEngine */
|
|
143
181
|
interface ChatEngineState {
|
|
@@ -156,13 +194,60 @@ interface ChatEngineState {
|
|
|
156
194
|
}
|
|
157
195
|
/**
|
|
158
196
|
* Shared chat engine hook used by both ChatBubble and SupportChatModal.
|
|
159
|
-
* Manages message state, input, and
|
|
197
|
+
* Manages message state, input, API communication, and reply polling.
|
|
160
198
|
*/
|
|
161
|
-
declare function useChatEngine({ apiUrl, user, }: ChatEngineOptions): ChatEngineState;
|
|
199
|
+
declare function useChatEngine({ apiUrl, user, repliesUrl, isOpen, }: ChatEngineOptions): ChatEngineState;
|
|
200
|
+
|
|
201
|
+
/** Options for the useUnreadCount hook */
|
|
202
|
+
interface UnreadCountOptions {
|
|
203
|
+
/** URL to poll for team replies (e.g., '/api/support/replies') */
|
|
204
|
+
repliesUrl: string;
|
|
205
|
+
/** Authenticated user info (optional). When provided, user.id is used as sessionId. */
|
|
206
|
+
user?: ChatUser;
|
|
207
|
+
/** Whether the chat panel is currently open. Polling pauses when true. */
|
|
208
|
+
isOpen: boolean;
|
|
209
|
+
}
|
|
210
|
+
/** Return value from useUnreadCount */
|
|
211
|
+
interface UnreadCountState {
|
|
212
|
+
/** Number of unread reply messages */
|
|
213
|
+
unreadCount: number;
|
|
214
|
+
/** Convenience boolean: true when unreadCount > 0 */
|
|
215
|
+
hasUnread: boolean;
|
|
216
|
+
/** Pre-fetched reply messages ready to display instantly when chat opens */
|
|
217
|
+
pendingReplies: ChatMessage[];
|
|
218
|
+
/** Reset unreadCount to 0 and clear pendingReplies */
|
|
219
|
+
markAsRead: () => void;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Hook that tracks unread reply count in the background via polling.
|
|
223
|
+
*
|
|
224
|
+
* Polls the replies endpoint every 4 seconds when `isOpen` is false.
|
|
225
|
+
* Stops background polling when `isOpen` is true (useChatEngine handles it at that point).
|
|
226
|
+
* Pre-fetches reply messages so they appear instantly when the chat opens.
|
|
227
|
+
*
|
|
228
|
+
* Can be used independently of ChatBubble -- any component can call useUnreadCount
|
|
229
|
+
* to build custom unread UX.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```tsx
|
|
233
|
+
* const { unreadCount, hasUnread, pendingReplies, markAsRead } = useUnreadCount({
|
|
234
|
+
* repliesUrl: '/api/support/replies',
|
|
235
|
+
* user: currentUser,
|
|
236
|
+
* isOpen: false,
|
|
237
|
+
* });
|
|
238
|
+
*
|
|
239
|
+
* return (
|
|
240
|
+
* <button onClick={openChat}>
|
|
241
|
+
* Support {hasUnread && <span className="badge">{unreadCount}</span>}
|
|
242
|
+
* </button>
|
|
243
|
+
* );
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
declare function useUnreadCount({ repliesUrl, user, isOpen, }: UnreadCountOptions): UnreadCountState;
|
|
162
247
|
|
|
163
248
|
/** Get or create a persistent session ID from localStorage */
|
|
164
249
|
declare function getSessionId(): string;
|
|
165
250
|
/** Collect anonymous browser context */
|
|
166
251
|
declare function collectAnonymousContext(): AnonymousContext;
|
|
167
252
|
|
|
168
|
-
export { type AnonymousContext, type BubblePosition, ChatBubble, type ChatBubbleProps, type ChatEngineOptions, type ChatEngineState, type ChatMessage, type ChatUser, SupportChatModal, type SupportChatModalProps, type SupportChatState, collectAnonymousContext, getSessionId, useChatEngine, useSupportChat };
|
|
253
|
+
export { type AnonymousContext, type BubblePosition, ChatBubble, type ChatBubbleProps, type ChatEngineOptions, type ChatEngineState, type ChatMessage, type ChatUser, SupportChatModal, type SupportChatModalProps, type SupportChatState, type UnreadCountOptions, type UnreadCountState, collectAnonymousContext, getSessionId, useChatEngine, useSupportChat, useUnreadCount };
|