guideai-app 0.3.4 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GuideAI.js +1 -1
- package/GuideAI.js.map +1 -1
- package/README.md +129 -0
- package/dist/GuideAI.d.ts +1 -2
- package/dist/GuideAI.js +1 -1
- package/dist/GuideAI.js.map +1 -1
- package/dist/components/Onboarding.d.ts +2 -2
- package/dist/components/TranscriptBox.d.ts +6 -3
- package/dist/components/WelcomeBubble.d.ts +2 -2
- package/dist/metric/event-listner.d.ts +12 -3
- package/dist/metric/metadata-tracker.d.ts +7 -0
- package/dist/styles/GuideAI.styles.d.ts +1 -1
- package/dist/types/GuideAI.types.d.ts +9 -5
- package/dist/utils/api.d.ts +13 -2
- package/dist/utils/constants.d.ts +2 -1
- package/dist/utils/highlightAndClick.d.ts +3 -0
- package/dist/utils/hoverAndClick.d.ts +4 -0
- package/package.json +2 -1
- package/structure.md +1 -1
- package/webpack.config.js +4 -15
- package/workflow-trigger-usage.md +398 -0
- package/dist/utils/highlight.d.ts +0 -2
package/dist/GuideAI.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GuideAI.js","mappings":"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","sources":["webpack://GuideAI/webpack/universalModuleDefinition","webpack://GuideAI/./src/styles/GuideAI.styles.ts","webpack://GuideAI/./src/utils/messageStorage.ts","webpack://GuideAI/./src/utils/constants.ts","webpack://GuideAI/external umd {\"commonjs\":\"react\",\"commonjs2\":\"react\",\"amd\":\"React\",\"root\":\"React\"}","webpack://GuideAI/./src/utils/api.ts","webpack://GuideAI/./src/GuideAI.tsx","webpack://GuideAI/./src/utils/ui.ts","webpack://GuideAI/./src/components/TranscriptBox.tsx","webpack://GuideAI/./src/components/Onboarding.tsx","webpack://GuideAI/./src/components/WelcomeBubble.tsx","webpack://GuideAI/./src/metric/event-listner.tsx","webpack://GuideAI/./src/utils/highlight.ts","webpack://GuideAI/./src/metric/metadata-tracker.tsx","webpack://GuideAI/./src/metric/index.tsx","webpack://GuideAI/webpack/bootstrap","webpack://GuideAI/webpack/startup"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"React\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"GuideAI\"] = factory(require(\"react\"));\n\telse\n\t\troot[\"GuideAI\"] = factory(root[\"React\"]);\n})(this, (__WEBPACK_EXTERNAL_MODULE__156__) => {\nreturn ","export const guideAIStyles = `\n.guideai-main-ui {\n position: relative;\n}\n\n.guideai-main-controls {\n display: flex;\n align-items: center;\n gap: 12px;\n position: relative;\n}\n\n.guideai-welcome-bubble {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n background: #0066ff;\n color: white;\n padding: 10px 16px;\n border-radius: 20px;\n font-size: 14px;\n white-space: normal;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n animation: bubble-pulse 2s infinite;\n max-width: 280px;\n min-width: 200px;\n text-align: center;\n line-height: 1.3;\n}\n\n.guideai-welcome-bubble.above {\n bottom: calc(100% + 10px);\n}\n\n.guideai-welcome-bubble.below {\n top: calc(100% + 10px);\n}\n\n.guideai-welcome-bubble.above::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-top: 8px solid #0066ff;\n}\n\n.guideai-welcome-bubble.below::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid #0066ff;\n}\n\n@keyframes bubble-pulse {\n 0% { transform: translateX(-50%) scale(1); }\n 50% { transform: translateX(-50%) scale(1.05); }\n 100% { transform: translateX(-50%) scale(1); }\n}\n\n.guideai-icon-wrapper {\n width: 50px;\n height: 50px;\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: rgba(255, 255, 255, 0.9);\n border: 2px solid transparent;\n border-radius: 50%;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.guideai-icon-wrapper:not(.initializing):hover {\n transform: scale(1.1);\n}\n\n.guideai-icon-wrapper.initializing {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(128, 128, 128, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: guideai-spin 1.5s linear infinite;\n opacity: 0.7;\n cursor: default;\n}\n\n.guideai-icon-wrapper.recording {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: guideai-pulse 1s infinite alternate;\n}\n\n.guideai-icon-wrapper.processing {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(255, 165, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: guideai-spin 1s linear infinite;\n}\n\n.guideai-icon-wrapper.playing {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(0, 128, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n@keyframes guideai-pulse {\n 0% { transform: scale(1); }\n 100% { transform: scale(1.05); }\n}\n\n@keyframes guideai-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n@keyframes click-ripple-animation {\n 0% { transform: translate(-50%, -50%) scale(0); opacity: 1; }\n 50% { transform: translate(-50%, -50%) scale(1.2); opacity: 0.6; }\n 100% { transform: translate(-50%, -50%) scale(2.5); opacity: 0; }\n}\n\n@keyframes click-dot-animation {\n 0% { transform: translate(-50%, -50%) scale(0); opacity: 1; }\n 60% { transform: translate(-50%, -50%) scale(1); opacity: 1; }\n 100% { transform: translate(-50%, -50%) scale(1); opacity: 0; }\n}\n\n@keyframes cursor-jiggle {\n 0% { transform: translate(-50%, 0) scale(1); }\n 25% { transform: translate(-50%, -5px) scale(1.1); }\n 50% { transform: translate(-50%, 0) scale(1); }\n 75% { transform: translate(-50%, 5px) scale(1.1); }\n 100% { transform: translate(-50%, 0) scale(1); filter: drop-shadow(0 0 8px #0066ff); }\n}\n\n.guideai-icon {\n width: 60%;\n height: 60%;\n min-width: 16px;\n min-height: 16px;\n max-width: 40px;\n max-height: 40px;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.guideai-icon.initializing {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"%23808080\" d=\"M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z\"/></svg>');\n}\n\n.guideai-icon.microphone {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"%230000FF\" d=\"M176 352c53.02 0 96-42.98 96-96V96c0-53.02-42.98-96-96-96S80 42.98 80 96v160c0 53.02 42.98 96 96 96zm160-160h-16c-8.84 0-16 7.16-16 16v48c0 74.8-64.49 134.82-140.79 127.38C96.71 376.89 48 317.11 48 250.3V208c0-8.84-7.16-16-16-16H16c-8.84 0-16 7.16-16 16v40.16c0 89.64 63.97 169.55 152 181.69V464H96c-8.84 0-16 7.16-16 16v16c0 8.84 7.16 16 16 16h160c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16h-56v-33.77C285.71 418.47 352 344.9 352 256v-48c0-8.84-7.16-16-16-16z\"/></svg>');\n}\n\n.guideai-icon.recording {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><circle cx=\"256\" cy=\"256\" r=\"128\" fill=\"%23FF0000\"/><circle cx=\"256\" cy=\"256\" r=\"200\" stroke=\"%23FF0000\" stroke-width=\"20\" fill=\"none\"/></svg>');\n}\n\n.guideai-icon.processing {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"%23FFA500\" d=\"M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z\"/></svg>');\n}\n\n.guideai-icon.playing {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"%23008000\" d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"/></svg>');\n}\n\n.guideai-icon.text-mode {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"%230066ff\" d=\"M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4l4 4 4-4h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM7 10v2h2v-2H7zm6 2h-2v-2h2v2zm4 0h-2v-2h2v2z\"/></svg>');\n}\n\n.guideai-icon.voice-mode {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"%23008000\" d=\"M176 352c53.02 0 96-42.98 96-96V96c0-53.02-42.98-96-96-96S80 42.98 80 96v160c0 53.02 42.98 96 96 96zm160-160h-16c-8.84 0-16 7.16-16 16v48c0 74.8-64.49 134.82-140.79 127.38C96.71 376.89 48 317.11 48 250.3V208c0-8.84-7.16-16-16-16H16c-8.84 0-16 7.16-16 16v40.16c0 89.64 63.97 169.55 152 181.69V464H96c-8.84 0-16 7.16-16 16v16c0 8.84 7.16 16 16 16h160c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16h-56v-33.77C285.71 418.47 352 344.9 352 256v-48c0-8.84-7.16-16-16-16z\"/></svg>');\n}\n\n\n/* Onboarding styles */\n.guideai-onboarding {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n background: white;\n border-radius: 12px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n width: 300px;\n max-width: 90vw;\n z-index: 1002;\n animation: onboarding-fade-in 0.3s ease-out;\n}\n\n.guideai-onboarding.above {\n bottom: calc(100% + 15px);\n}\n\n.guideai-onboarding.below {\n top: calc(100% + 15px);\n}\n\n.guideai-onboarding.above::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-top: 8px solid white;\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));\n}\n\n.guideai-onboarding.below::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid white;\n filter: drop-shadow(0 -2px 4px rgba(0, 0, 0, 0.1));\n}\n\n@keyframes onboarding-fade-in {\n from { opacity: 0; transform: translateX(-50%) translateY(-10px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n\n.guideai-onboarding-content {\n padding: 16px;\n position: relative;\n}\n\n.guideai-onboarding-close {\n position: absolute;\n top: 10px;\n right: 10px;\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n color: #999;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.guideai-onboarding-close:hover {\n background: #f5f5f5;\n color: #333;\n}\n\n.guideai-onboarding-step {\n text-align: center;\n margin-bottom: 12px;\n}\n\n.guideai-onboarding-icon {\n font-size: 32px;\n margin-bottom: 8px;\n display: inline-block;\n}\n\n.guideai-onboarding-step h3 {\n margin: 0 0 8px;\n font-size: 16px;\n color: #333;\n font-weight: 600;\n}\n\n.guideai-onboarding-step p {\n margin: 0;\n font-size: 13px;\n color: #666;\n line-height: 1.4;\n}\n\n.guideai-onboarding-dots {\n display: flex;\n justify-content: center;\n margin: 12px 0;\n}\n\n.guideai-onboarding-dots .dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #ddd;\n margin: 0 4px;\n transition: all 0.3s ease;\n}\n\n.guideai-onboarding-dots .dot.active {\n background: #0066ff;\n transform: scale(1.2);\n}\n\n.guideai-onboarding-next {\n display: block;\n width: 100%;\n padding: 10px;\n background: #0066ff;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.guideai-onboarding-next:hover {\n background: #0055cc;\n}\n\n/* Transcript Box Styles */\n.guideai-transcript-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: transparent;\n display: flex;\n justify-content: flex-end;\n align-items: flex-end;\n padding-bottom: 40px;\n padding-right: 40px;\n z-index: 10000;\n animation: transcript-fade-in 0.3s ease-out;\n pointer-events: none;\n}\n\n.guideai-transcript-box {\n background: rgba(40, 44, 52, 0.85);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 16px;\n box-shadow: \n 0 8px 32px rgba(0, 0, 0, 0.3),\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n inset 0 -1px 0 rgba(0, 0, 0, 0.1);\n width: 280px;\n max-height: 280px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: transcript-slide-up 0.4s ease-out;\n pointer-events: auto;\n margin-right: 60px;\n position: relative;\n}\n\n.guideai-transcript-box::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);\n border-radius: 16px 16px 0 0;\n}\n\n\n\n.guideai-transcript-messages {\n flex: 1;\n overflow-y: auto;\n padding: 8px 12px;\n max-height: 200px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar {\n width: 6px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar-track {\n background: rgba(0, 0, 0, 0.05);\n border-radius: 3px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.3);\n}\n\n.guideai-transcript-empty {\n text-align: center;\n padding: 20px 12px;\n color: rgba(255, 255, 255, 0.6);\n}\n\n.guideai-transcript-empty-icon {\n font-size: 24px;\n margin-bottom: 8px;\n opacity: 0.5;\n}\n\n.guideai-transcript-empty p {\n margin: 0;\n font-size: 11px;\n color: rgba(255, 255, 255, 0.6);\n}\n\n.guideai-transcript-message {\n margin-bottom: 8px;\n animation: message-fade-in 0.3s ease-out;\n}\n\n.guideai-transcript-message.human {\n text-align: right;\n}\n\n.guideai-transcript-message.guideai {\n text-align: left;\n}\n\n.guideai-transcript-message-content {\n display: inline-block;\n max-width: 85%;\n padding: 6px 10px;\n border-radius: 12px;\n position: relative;\n}\n\n.guideai-transcript-message.human .guideai-transcript-message-content {\n background: rgba(255, 255, 255, 0.15);\n color: rgba(255, 255, 255, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-bottom-right-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n\n.guideai-transcript-message.guideai .guideai-transcript-message-content {\n background: rgba(0, 0, 0, 0.1);\n color: rgba(255, 255, 255, 0.9);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-bottom-left-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n\n.guideai-transcript-message-sender {\n font-size: 9px;\n font-weight: 600;\n margin-bottom: 2px;\n opacity: 0.8;\n}\n\n.guideai-transcript-message-text {\n font-size: 11px;\n line-height: 1.3;\n word-wrap: break-word;\n}\n\n.guideai-transcript-message-time {\n font-size: 8px;\n opacity: 0.6;\n margin-top: 2px;\n}\n\n\n\n@keyframes transcript-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes transcript-slide-up {\n from { \n opacity: 0; \n transform: translateX(40px) scale(0.95); \n }\n to { \n opacity: 1; \n transform: translateX(0) scale(1); \n }\n}\n\n@keyframes message-fade-in {\n from { \n opacity: 0; \n transform: translateY(10px); \n }\n to { \n opacity: 1; \n transform: translateY(0); \n }\n}\n\n/* Transcript Toggle Button Styles - positioned at top of transcript area */\n.guideai-transcript-toggle-button {\n position: fixed;\n bottom: 60px;\n right: 40px;\n width: 36px;\n height: 36px;\n background: rgba(40, 44, 52, 0.9);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);\n transition: all 0.2s ease;\n z-index: 10001;\n font-size: 14px;\n padding: 0;\n color: rgba(255, 255, 255, 0.9);\n animation: toggle-fade-in 0.3s ease-out;\n}\n\n.guideai-transcript-toggle-button:hover {\n background: rgba(40, 44, 52, 1);\n transform: scale(1.1);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);\n}\n\n.guideai-transcript-toggle-button:active {\n transform: scale(0.95);\n}\n\n.guideai-transcript-toggle-icon {\n display: inline-block;\n transition: all 0.2s ease;\n filter: none;\n}\n\n/* Position transcript toggle button based on transcript position */\n.guideai-transcript-overlay {\n pointer-events: none;\n}\n\n.guideai-transcript-toggle-button {\n pointer-events: auto;\n}\n\n/* Input Options Container - Two Stage Layout */\n.guideai-input-options {\n display: flex;\n align-items: center;\n gap: 12px;\n position: relative;\n animation: options-slide-in 0.4s ease-out;\n}\n\n@keyframes options-slide-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n/* Individual Input Option Button */\n.guideai-input-option {\n width: 50px;\n height: 50px;\n background: rgba(255, 255, 255, 0.9);\n border: 2px solid rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n transition: all 0.3s ease;\n font-size: 18px;\n padding: 0;\n position: relative;\n}\n\n.guideai-input-option:hover:not(:disabled) {\n background: rgba(255, 255, 255, 1);\n transform: scale(1.1);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);\n}\n\n.guideai-input-option:active:not(:disabled) {\n transform: scale(0.95);\n}\n\n.guideai-input-option:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n\n/* Voice Option Specific Styling */\n.guideai-input-option.voice-option {\n background: linear-gradient(135deg, rgba(76, 175, 80, 0.1), rgba(139, 195, 74, 0.1));\n border-color: rgba(76, 175, 80, 0.3);\n}\n\n.guideai-input-option.voice-option:hover:not(:disabled) {\n background: linear-gradient(135deg, rgba(76, 175, 80, 0.2), rgba(139, 195, 74, 0.2));\n border-color: rgba(76, 175, 80, 0.5);\n}\n\n/* Text Option Specific Styling */\n.guideai-input-option.text-option {\n background: linear-gradient(135deg, rgba(33, 150, 243, 0.1), rgba(63, 81, 181, 0.1));\n border-color: rgba(33, 150, 243, 0.3);\n}\n\n.guideai-input-option.text-option:hover:not(:disabled) {\n background: linear-gradient(135deg, rgba(33, 150, 243, 0.2), rgba(63, 81, 181, 0.2));\n border-color: rgba(33, 150, 243, 0.5);\n}\n\n.guideai-input-option-icon {\n display: inline-block;\n transition: all 0.2s ease;\n filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));\n}\n\n/* Text Input integrated into Transcript Box */\n.guideai-transcript-text-input {\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n padding: 12px;\n display: flex;\n gap: 8px;\n align-items: flex-end;\n background: rgba(0, 0, 0, 0.02);\n border-radius: 0 0 16px 16px;\n animation: text-input-fade-in 0.3s ease-out;\n}\n\n.guideai-transcript-input-field {\n flex: 1;\n background: rgba(255, 255, 255, 0.15);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n padding: 8px 12px;\n color: rgba(255, 255, 255, 0.95);\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n resize: none;\n outline: none;\n transition: all 0.2s ease;\n min-height: 32px;\n}\n\n.guideai-transcript-input-field::placeholder {\n color: rgba(255, 255, 255, 0.6);\n}\n\n.guideai-transcript-input-field:focus {\n border-color: rgba(255, 255, 255, 0.4);\n background: rgba(255, 255, 255, 0.2);\n box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);\n}\n\n.guideai-transcript-send-button {\n background: rgba(255, 255, 255, 0.15);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 6px;\n padding: 6px 10px;\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 36px;\n height: 32px;\n}\n\n.guideai-transcript-send-button:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.25);\n border-color: rgba(255, 255, 255, 0.3);\n transform: translateY(-1px);\n}\n\n.guideai-transcript-send-button:active:not(:disabled) {\n transform: translateY(0);\n}\n\n.guideai-transcript-send-button:disabled {\n background: rgba(255, 255, 255, 0.05);\n border-color: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.4);\n cursor: not-allowed;\n transform: none;\n}\n\n.guideai-transcript-send-icon {\n font-size: 11px;\n}\n\n/* Animation for text input appearance */\n@keyframes text-input-fade-in {\n from { \n opacity: 0; \n transform: translateY(10px); \n }\n to { \n opacity: 1; \n transform: translateY(0); \n }\n}\n\n@keyframes toggle-fade-in {\n from { \n opacity: 0; \n transform: scale(0.8); \n }\n to { \n opacity: 1; \n transform: scale(1); \n }\n}\n\n\n`; ","// Storage data structures for conversation persistence\nexport interface StoredMessage {\n content: string;\n sender: 'GUIDEAI' | 'HUMAN';\n timestamp: number;\n}\n\nexport interface StoredConversation {\n conversationId: string;\n messages: StoredMessage[];\n timestamp: number;\n organizationKey: string;\n}\n\nexport const CONVERSATION_STORAGE_KEY = 'guideai_conversation';\nexport const CONVERSATION_EXPIRY_MINUTES = 30;\nexport const MAX_STORED_MESSAGES = 100; // Increased to keep more conversation history\n\nexport const isLocalStorageAvailable = (): boolean => {\n try {\n const testKey = '__storage_test__';\n localStorage.setItem(testKey, testKey);\n localStorage.removeItem(testKey);\n return true;\n } catch (e) {\n return false;\n }\n};\n\nconst isValidConversation = (data: any): data is StoredConversation => {\n return (\n data &&\n typeof data === 'object' &&\n typeof data.conversationId === 'string' &&\n Array.isArray(data.messages) &&\n typeof data.timestamp === 'number' &&\n typeof data.organizationKey === 'string' &&\n data.messages.every((msg: any) => (\n typeof msg === 'object' &&\n typeof msg.content === 'string' &&\n (msg.sender === 'HUMAN' || msg.sender === 'GUIDEAI') &&\n typeof msg.timestamp === 'number'\n ))\n );\n};\n\nexport const isConversationExpired = (conversation: StoredConversation): boolean => {\n const expiryTime = CONVERSATION_EXPIRY_MINUTES * 60 * 1000; // convert to milliseconds\n const now = Date.now();\n return (now - conversation.timestamp) > expiryTime;\n};\n\nconst handleStorageError = (error: unknown, operation: string): void => {\n console.error(`Storage error during ${operation}:`, error);\n \n // Check if it's a quota exceeded error\n if (error instanceof DOMException && \n (error.name === 'QuotaExceededError' || \n error.name === 'NS_ERROR_DOM_QUOTA_REACHED')) {\n \n // Try to free up space by removing oldest messages instead of clearing everything\n try {\n const conversation = loadConversationFromStorage();\n if (conversation && conversation.messages.length > 5) {\n // Remove oldest 5 messages\n conversation.messages = conversation.messages.slice(5);\n saveConversationToStorage(conversation);\n console.warn('Storage quota exceeded. Removed oldest 5 messages to free space.');\n } else if (conversation) {\n // If less than 5 messages, clear everything\n clearConversationStorage();\n console.warn('Storage quota exceeded. Cleared conversation data.');\n }\n } catch (cleanupError) {\n console.error('Failed to clean up storage:', cleanupError);\n }\n }\n};\n\nexport const saveConversationToStorage = (conversation: StoredConversation): void => {\n if (!isLocalStorageAvailable()) {\n console.warn('localStorage is not available. Conversation persistence disabled.');\n return;\n }\n \n try {\n // Limit to last MAX_STORED_MESSAGES\n if (conversation.messages.length > MAX_STORED_MESSAGES) {\n conversation.messages = conversation.messages.slice(-MAX_STORED_MESSAGES);\n }\n \n // Update timestamp\n conversation.timestamp = Date.now();\n \n localStorage.setItem(CONVERSATION_STORAGE_KEY, JSON.stringify(conversation));\n } catch (error) {\n handleStorageError(error, 'save');\n }\n};\n\nexport const loadConversationFromStorage = (): StoredConversation | null => {\n if (!isLocalStorageAvailable()) {\n console.warn('localStorage is not available. Conversation persistence disabled.');\n return null;\n }\n \n try {\n const storedData = localStorage.getItem(CONVERSATION_STORAGE_KEY);\n if (!storedData) return null;\n \n const parsedData = JSON.parse(storedData);\n \n // Validate the data structure\n if (!isValidConversation(parsedData)) {\n console.warn('Corrupted conversation data found. Clearing storage.');\n clearConversationStorage();\n return null;\n }\n \n return parsedData;\n } catch (error) {\n handleStorageError(error, 'load');\n // Clear corrupted data on any parsing errors\n clearConversationStorage();\n return null;\n }\n};\n\nexport const clearConversationStorage = (): void => {\n if (!isLocalStorageAvailable()) {\n return;\n }\n \n try {\n localStorage.removeItem(CONVERSATION_STORAGE_KEY);\n } catch (error) {\n handleStorageError(error, 'clear');\n }\n};\n\nexport const addMessageToStorage = (message: StoredMessage, conversationId: string, organizationKey: string): void => {\n if (!isLocalStorageAvailable()) {\n console.warn('localStorage is not available. Message persistence disabled.');\n return;\n }\n \n try {\n let conversation = loadConversationFromStorage();\n \n if (!conversation) {\n conversation = {\n conversationId,\n messages: [],\n timestamp: Date.now(),\n organizationKey\n };\n }\n \n // Check for duplicate messages to prevent adding the same message twice\n const isDuplicate = conversation.messages.some(existingMsg => \n existingMsg.content === message.content && \n existingMsg.sender === message.sender &&\n Math.abs(existingMsg.timestamp - message.timestamp) < 5000 // Within 5 seconds\n );\n \n if (!isDuplicate) {\n conversation.messages.push(message);\n conversation.conversationId = conversationId;\n conversation.organizationKey = organizationKey;\n \n saveConversationToStorage(conversation);\n } else {\n console.log('Duplicate message detected, skipping:', message.content.substring(0, 50));\n }\n } catch (error) {\n console.error('Error adding message to storage:', error);\n }\n};\n\nexport const checkForStoredConversation = (currentOrganizationKey: string): {\n shouldRestore: boolean;\n conversationId: string | null;\n messages: StoredMessage[] | null;\n} => {\n const storedConversation = loadConversationFromStorage();\n \n if (!storedConversation) {\n return { shouldRestore: false, conversationId: null, messages: null };\n }\n \n // Check if conversation is expired - if so, clear it automatically\n if (isConversationExpired(storedConversation)) {\n clearConversationStorage();\n return { shouldRestore: false, conversationId: null, messages: null };\n }\n \n // Check if organization key matches\n if (storedConversation.organizationKey !== currentOrganizationKey) {\n return { shouldRestore: false, conversationId: null, messages: null };\n }\n \n return {\n shouldRestore: true,\n conversationId: storedConversation.conversationId,\n messages: storedConversation.messages\n };\n};\n\nexport const formatConversationContext = (messages: StoredMessage[], maxMessages: number = 10): string => {\n // Take only the last N messages to avoid context length issues\n const recentMessages = messages.slice(-maxMessages);\n \n if (recentMessages.length === 0) {\n return '';\n }\n \n // Filter out very short or empty messages\n const meaningfulMessages = recentMessages.filter(msg => \n msg.content && msg.content.trim().length > 3\n );\n \n if (meaningfulMessages.length === 0) {\n return '';\n }\n \n // Format messages into a readable context string\n const formattedMessages = meaningfulMessages.map(msg => {\n const sender = msg.sender === 'HUMAN' ? 'User' : 'Assistant';\n return `${sender}: ${msg.content.trim()}`;\n }).join('\\n');\n \n return formattedMessages;\n}; ","// Default prompt for Guide AI\nexport const DEFAULT_PROMPT = `you are Guide AI.\n Your role is to answer any question directly and succinctly that a user has. NEVER DIRECTLY MENTION THE SCREENSHOT, but use its information as much as possible to target your responses.\n If nothing is asked, then your goal is to generally assist them.\n IMPORTANT: NEVER answer in more than 10 words. Always be concise and limit answers to 1 sentence maximum. Be simple and as short as possible.\n Your job is to help them get it done through asking more and more targeted specific questions.`;\n\n// WebRTC message types to ignore in logging\nexport const IGNORE_MESSAGE_TYPES = [\n \"delta\", \n \"rate_limits\", \n \"input_audio_buffer\",\n \"response.audio.done\", \n \"response.output_item\", \n \"response.content_part\", \n \"output_audio_buffer\",\n \"response.created\", \n \"session.created\",\n \"conversation.item.truncated\"\n];\n\n// API endpoints\nexport const GUIDE_AI_API_BASE = 'https://www.getguide.ai/api';\nexport const OPENAI_REALTIME_BASE = 'https://api.openai.com/v1/realtime';\nexport const OPENAI_REALTIME_MODEL = \"gpt-4o-realtime-preview-2024-12-17\";\n\n// Gemini API - Note: This should be moved to environment variables in production\nexport const GEMINI_API_KEY = 'AIzaSyBiFyzjYVupLyk8BdmfWzBL1GbzX8OUdPc';\nexport const GEMINI_API_ENDPOINT = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent'; ","module.exports = __WEBPACK_EXTERNAL_MODULE__156__;","import { StoredMessage, addMessageToStorage } from './messageStorage';\nimport { GUIDE_AI_API_BASE } from './constants';\nimport { UserMetadata, MetadataUpdate } from '../types/metadata.types';\n\n// Type for the conversation creation response (now includes token and prompt)\ninterface ConversationData {\n id: string;\n ephemeralToken: string;\n prompt: string;\n}\n\n// Create a new conversation (now also returns ephemeral token + prompt)\nexport const createNewConversation = async (\n organizationKey: string,\n onError: (error: Error, context: string) => void\n): Promise<ConversationData | null> => {\n try {\n const now = new Date();\n const response = await fetch(`${GUIDE_AI_API_BASE}/initialize-session`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n organizationKey,\n userId: 'anonymous',\n date: now.toISOString().split('T')[0],\n time: now.toTimeString().split(' ')[0],\n messages: []\n })\n });\n \n if (!response.ok) {\n const errorText = await response.text().catch(() => 'No error details available');\n console.error(`Conversation creation failed with status ${response.status}:`, errorText);\n throw new Error(`Failed to create conversation: ${response.status} - ${errorText}`);\n }\n \n const conversationData = await response.json();\n console.log('Conversation created:', conversationData.id);\n return conversationData;\n } catch (error) {\n console.error('Error creating conversation:', error);\n onError(error as Error, 'Creating conversation');\n return null;\n }\n};\n\n// Log a message to the conversation via API and local storage\nexport const logMessage = async (\n content: string, \n sender: 'GUIDEAI' | 'HUMAN',\n conversationId: string | null,\n organizationKey: string,\n onError: (error: Error, context: string) => void\n): Promise<void> => {\n if (!conversationId) return;\n \n try {\n const response = await fetch(`${GUIDE_AI_API_BASE}/conversations/${conversationId}/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ content, sender })\n });\n \n if (!response.ok) {\n throw new Error(`Failed to log message: ${response.status} ${response.statusText}`);\n }\n \n const message: StoredMessage = {\n content,\n sender,\n timestamp: Date.now()\n };\n \n addMessageToStorage(message, conversationId, organizationKey);\n } catch (error) {\n console.error('Error logging message:', error);\n onError(error as Error, 'Logging message');\n }\n};\n\n// Send metadata updates in batch\nexport const sendMetadataUpdates = async (\n updates: MetadataUpdate[],\n organizationKey: string,\n onError: (error: Error, context: string) => void\n): Promise<boolean> => {\n if (updates.length === 0) return true;\n\n try {\n const response = await fetch(`${GUIDE_AI_API_BASE}/metadata-updates`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n organizationKey,\n updates,\n batchTimestamp: Date.now(),\n updateCount: updates.length\n })\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send metadata updates: ${response.status} ${response.statusText}`);\n }\n\n console.log(`Sent ${updates.length} metadata updates successfully`);\n return true;\n } catch (error) {\n console.error('Error sending metadata updates:', error);\n onError(error as Error, 'Sending metadata updates');\n return false;\n }\n}; ","// Import React normally, but use a helper function to handle possible duplicates\nimport React from 'react';\nimport { \n StoredMessage, \n clearConversationStorage,\n checkForStoredConversation, \n formatConversationContext \n} from './utils/messageStorage';\n\n// Import our refactored modules\nimport { GuideAIProps, RecordingStatus, UseStateHook, UseEffectHook, UseRefHook, UseCallbackHook, PopupPosition } from './types/GuideAI.types';\nimport { DEFAULT_PROMPT, IGNORE_MESSAGE_TYPES, OPENAI_REALTIME_BASE, OPENAI_REALTIME_MODEL, GEMINI_API_KEY, GEMINI_API_ENDPOINT } from './utils/constants';\nimport { injectStyles, calculateOptimalPosition } from './utils/ui';\nimport { highlightElement } from './utils/highlight';\nimport { createNewConversation, logMessage } from './utils/api';\nimport WelcomeBubble from './components/WelcomeBubble';\nimport Onboarding from './components/Onboarding';\nimport TranscriptBox from './components/TranscriptBox';\nimport { guideAIStyles } from './styles/GuideAI.styles';\nimport { EventTracker, UserMetadataTracker, UserMetadata } from './metric';\nimport { sendMetadataUpdates } from './utils/api';\n\n// Get React hooks safely, avoiding duplicate React issues - moved outside component\nconst getReactHooks = () => {\n // Try to use the React instance from window if available (for react-scripts)\n if (typeof window !== 'undefined' && (window as any).React) {\n return {\n useState: (window as any).React.useState as UseStateHook,\n useEffect: (window as any).React.useEffect as UseEffectHook,\n useRef: (window as any).React.useRef as UseRefHook,\n useMemo: (window as any).React.useMemo,\n useCallback: (window as any).React.useCallback as UseCallbackHook\n };\n }\n // Otherwise use the imported React\n return {\n useState: React.useState,\n useEffect: React.useEffect,\n useRef: React.useRef,\n useMemo: React.useMemo,\n useCallback: React.useCallback as UseCallbackHook\n };\n};\n\n// Cache the hooks to ensure consistent instances across renders\nconst cachedHooks = getReactHooks();\n\nconst GuideAIComponent = (props: GuideAIProps) => {\n const {\n organizationKey,\n position,\n onError = console.error,\n metadata: metadataOptions,\n transcript: transcriptOptions,\n input: inputOptions\n } = props;\n\n // Use cached hooks to ensure consistent instances\n const hooks = cachedHooks;\n\n const [status, setStatus] = hooks.useState<RecordingStatus>('idle');\n const [isClient, setIsClient] = hooks.useState(false);\n\n const [isHighlighting, setIsHighlighting] = hooks.useState(false);\n const [hasInteracted, setHasInteracted] = hooks.useState(false);\n const [prompt, setPrompt] = hooks.useState<string | null>(null);\n const [isSessionReady, setIsSessionReady] = hooks.useState(false);\n const [isConnecting, setIsConnecting] = hooks.useState(false);\n const [showOnboarding, setShowOnboarding] = hooks.useState(false);\n const [popupPosition, setPopupPosition] = hooks.useState<PopupPosition>('above');\n const conversationIdRef = hooks.useRef<string | null>(null);\n\n const componentRef = hooks.useRef<HTMLDivElement>(null);\n const peerConnectionRef = hooks.useRef<RTCPeerConnection | null>(null);\n const dataChannelRef = hooks.useRef<RTCDataChannel | null>(null);\n const hasCreatedConversationRef = hooks.useRef(false);\n const mediaStreamRef = hooks.useRef<MediaStream | null>(null);\n const audioElementRef = hooks.useRef<HTMLAudioElement | null>(null);\n const [ephemeralToken, setEphemeralToken] = hooks.useState<string | null>(null);\n const hasInitializedRef = hooks.useRef(false);\n const isUnmountingRef = hooks.useRef(false);\n const [isConversationActive, setIsConversationActive] = hooks.useState(false);\n const [restoredMessages, setRestoredMessages] = hooks.useState<StoredMessage[]>([]);\n const [showTranscript, setShowTranscript] = hooks.useState(\n transcriptOptions?.enabled !== false // Default to true unless explicitly disabled\n );\n const [allMessages, setAllMessages] = hooks.useState<StoredMessage[]>([]);\n const [inputMode, setInputMode] = hooks.useState<'voice' | 'text'>(\n inputOptions?.defaultMode || 'voice'\n );\n const [textInput, setTextInput] = hooks.useState('');\n const [showTextInput, setShowTextInput] = hooks.useState(false);\n\n const hasAttemptedAutoStartRef = hooks.useRef(false);\n const initializationAttemptedRef = hooks.useRef(false);\n\n\n\n // ===== METADATA AND EVENT TRACKING =====\n \n // Create event tracker - memoized to prevent recreation\n const eventTracker = hooks.useMemo(() => {\n const customerId = metadataOptions?.initialUserData?.userId || 'anonymous';\n const organizationId = metadataOptions?.initialUserData?.organizationKey || 'default';\n const customerType = metadataOptions?.initialUserData?.userType || 'user';\n \n return new EventTracker({ customerId, organizationId, customerType, organizationKey });\n }, [organizationKey]); // Only depend on organizationKey\n\n // Create metadata tracker - memoized to prevent recreation\n const metadataTracker = hooks.useMemo(() => \n new UserMetadataTracker(organizationKey, metadataOptions?.config || {}), \n [organizationKey] // Remove onError and metadataOptions?.config from dependencies\n );\n \n // ===== API & EXTERNAL CALLS =====\n \n // Call Gemini Flash API to validate and fix JSON\n const geminiFlash = async (prompt: string): Promise<string> => {\n const response = await fetch(`${GEMINI_API_ENDPOINT}?key=${GEMINI_API_KEY}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: [\n {\n parts: [\n {\n text: prompt\n }\n ]\n }\n ]\n })\n });\n \n const responseJson = await response.json();\n const content = responseJson.candidates[0].content;\n const responseText = content.parts[0].text;\n return responseText;\n };\n\n // ===== COMPONENT WRAPPER FUNCTIONS =====\n \n // Wrapper functions to maintain original signatures\n const handleCreateNewConversation = hooks.useCallback(async () => {\n if (hasCreatedConversationRef.current) return null;\n hasCreatedConversationRef.current = true;\n \n const conversationData = await createNewConversation(organizationKey, onError);\n if (conversationData) {\n conversationIdRef.current = conversationData.id;\n setEphemeralToken(conversationData.ephemeralToken);\n setPrompt(conversationData.prompt);\n return conversationData;\n }\n return null;\n }, [organizationKey]); // Removed onError from dependencies to prevent re-creation\n\n const handleLogMessage = hooks.useCallback(async (content: string, sender: 'GUIDEAI' | 'HUMAN') => {\n await logMessage(content, sender, conversationIdRef.current, organizationKey, onError);\n \n // Check if this exact message already exists to prevent duplicates\n setAllMessages(prev => {\n const lastMessage = prev[prev.length - 1];\n if (lastMessage && \n lastMessage.content === content && \n lastMessage.sender === sender) {\n // Message already exists, don't add duplicate\n return prev;\n }\n \n // Add message to local state for transcript\n const newMessage: StoredMessage = {\n content,\n sender,\n timestamp: Date.now()\n };\n return [...prev, newMessage];\n });\n }, [organizationKey]); // Removed onError from dependencies to prevent re-creation\n\n const handleHighlightElement = hooks.useCallback(async (selector: string | string[]) => {\n return highlightElement(selector, isHighlighting, setIsHighlighting, handleLogMessage);\n }, [isHighlighting, handleLogMessage]);\n\n // ===== UI UTILITIES & POSITION =====\n \n // Calculate optimal position for popups based on component location\n const getOptimalPosition = hooks.useCallback((elementHeight: number = 240, spacing: number = 15): PopupPosition => {\n if (!componentRef.current) return 'above';\n \n const rect = componentRef.current.getBoundingClientRect();\n return calculateOptimalPosition(rect, elementHeight, spacing);\n }, []);\n\n // ===== WEBRTC MANAGEMENT =====\n \n const sendMessage = (message: any) => {\n if (dataChannelRef.current?.readyState === 'open') {\n // console.log('sending message', message);\n dataChannelRef.current.send(JSON.stringify(message));\n } else {\n console.error('Data channel not open, cannot send message');\n setStatus('idle');\n }\n };\n \n const cleanupWebRTC = hooks.useCallback(() => {\n if (mediaStreamRef.current) {\n // Stop tracks completely instead of just disabling them\n mediaStreamRef.current.getAudioTracks().forEach(track => {\n track.stop();\n });\n mediaStreamRef.current = null;\n }\n \n if (dataChannelRef.current) {\n dataChannelRef.current.close();\n dataChannelRef.current = null;\n }\n \n if (peerConnectionRef.current) {\n peerConnectionRef.current.close();\n peerConnectionRef.current = null;\n }\n \n if (audioElementRef.current) {\n audioElementRef.current.srcObject = null;\n }\n \n // Reset session ready state\n setIsSessionReady(false);\n }, []);\n\n const initializeWebRTC = async (audioStream: MediaStream | null): Promise<boolean> => {\n try {\n const pc = new RTCPeerConnection();\n peerConnectionRef.current = pc;\n \n // Only add audio tracks if audioStream is provided (for voice mode)\n if (audioStream) {\n audioStream.getAudioTracks().forEach(track => {\n pc.addTrack(track, audioStream);\n });\n }\n \n // Only create audio element if we have an audio stream (voice mode)\n if (audioStream && !audioElementRef.current) {\n const audioEl = document.createElement('audio');\n audioEl.autoplay = true;\n document.body.appendChild(audioEl);\n audioElementRef.current = audioEl;\n }\n \n pc.ontrack = (e) => {\n if (audioElementRef.current && e.track.kind === 'audio') {\n const stream = new MediaStream([e.track]);\n audioElementRef.current.srcObject = stream;\n }\n };\n \n const dc = pc.createDataChannel('oai-events');\n dataChannelRef.current = dc;\n \n dc.onopen = () => { \n // Configure session based on whether we have audio stream (voice vs text mode)\n const sessionConfig: any = {\n tools: [{\n type: \"function\",\n name: \"highlight\",\n description: \"Highlight specific elements on the page to guide the user's attention\",\n parameters: {\n type: \"object\",\n properties: {\n selector: {\n oneOf: [\n {\n type: \"string\",\n description: \"CSS selector or XPath for the element to highlight (e.g., '#search-bar')\"\n },\n {\n type: \"array\",\n items: {\n type: \"string\"\n },\n description: \"List of CSS selectors or XPaths to highlight in sequence (e.g., ['#first-button', '#second-button'])\"\n }\n ],\n description: \"CSS selector(s) or XPath(s) for the element(s) to highlight. Can be a single selector string or an array of selectors to process sequentially.\"\n }\n },\n required: [\"selector\"]\n }\n }],\n tool_choice: \"auto\"\n };\n\n // Only add audio-specific config if we have an audio stream\n if (audioStream) {\n sessionConfig.turn_detection = {\n type: \"semantic_vad\",\n create_response: true,\n interrupt_response: false\n };\n sessionConfig.input_audio_transcription = {\n model: \"gpt-4o-mini-transcribe\",\n language: \"en\",\n };\n }\n\n sendMessage({\n type: \"session.update\",\n session: sessionConfig\n });\n };\n \n dc.onmessage = (e) => {\n try {\n const message = JSON.parse(e.data);\n\n switch (message.type) { \n case 'session.updated':\n // console.log('session.updated', message);\n setIsSessionReady(true);\n setIsConnecting(false);\n \n // If we have restored messages, send conversation context to AI now that session is ready\n if (restoredMessages.length > 0) {\n const conversationContext = formatConversationContext(restoredMessages, 5); // Reduce to 5 messages\n \n // Send as system context instead of user input to avoid confusion\n sendMessage({\n type: \"conversation.item.create\",\n item: {\n type: \"message\",\n role: \"system\",\n content: [{\n type: \"input_text\",\n text: `This is context from a previous conversation session. The user is continuing an existing conversation. Previous context: ${conversationContext}`\n }]\n }\n });\n \n console.log('Sent conversation context with', restoredMessages.length, 'restored messages');\n }\n break;\n\n case 'session.transcript_disabled':\n if (message.text && message.text.trim()) {\n console.log('Session transcript:', message.text);\n handleLogMessage(message.text, 'HUMAN');\n }\n break;\n \n case 'conversation.item.input_audio_transcription.completed':\n if (message.transcript && message.transcript.trim()) {\n console.log('User transcript:', message.transcript);\n // Only log if it's different from the last message to avoid duplicates\n if (allMessages.length === 0 || \n allMessages[allMessages.length - 1].content !== message.transcript) {\n handleLogMessage(message.transcript, 'HUMAN');\n }\n }\n break;\n\n case 'response.audio_transcript.done':\n if (message.transcript && message.transcript.trim()) {\n console.log('Assistant message:', message.transcript);\n // Only log if it's different from the last assistant message to avoid duplicates\n const lastMessage = allMessages[allMessages.length - 1];\n if (!lastMessage || \n lastMessage.sender !== 'GUIDEAI' ||\n lastMessage.content !== message.transcript) {\n handleLogMessage(message.transcript, 'GUIDEAI');\n }\n }\n break;\n \n case \"conversation.item.created\":\n // console.log('conversation.item.created', message);\n break;\n\n case \"input_audio_buffer.speech_started\":\n // console.log('User speaking started');\n setStatus('recording');\n break;\n\n case \"input_audio_buffer.speech_stopped\":\n // console.log('User speaking stopped');\n setStatus('processing');\n break;\n\n case 'response.done':\n // console.log('response.done', message);\n for (const out of message.response.output) {\n switch (out.type) {\n case \"message\":\n // console.log('Assistant message:', out.text);\n break;\n \n case \"function_call\":\n // console.log('function_call', out);\n if (out.name === 'highlight' && out.arguments) {\n // console.log('Tool call:', out.arguments);\n try {\n let argsToUse = out.arguments;\n // Check if the JSON string is missing a closing quote\n // try {\n // console.log(\"argsToUse\", argsToUse);\n // const parsedArgs = JSON.parse(argsToUse);\n // console.log(\"parsedArgs\", parsedArgs);\n // highlightElement(parsedArgs.selector);\n // } catch (error) {\n // console.log(\"errored, argsToUse\", argsToUse);\n const prompt = `\n Validate the following JSON string and return ONLY a correct JSON object:\n Note: The selector(s) should be either a css selector or an xpath, so modify those if invalid.\n Also, [url=''] is a valid css selector, so do not modify it if it's present.\n Do not add any other text or newlines to the response.\n It should be of the following format:\n { \"selector\": \"string\" }\n or\n { \"selector\": [\"string\", ...] }\n DO NOT add any other text or newlines to the response, it should begin and end with curly braces.\n DO NOT add '''json to the response.\n The JSON string is:\n ${argsToUse}\n `\n geminiFlash(prompt).then(response => {\n response = response.replace(\"```json\", \"\").replace(\"```\", \"\").trim();\n // console.log(\"response\", response);\n const parsedArgs = JSON.parse(response);\n handleHighlightElement(parsedArgs.selector);\n }).catch(err => {\n console.error(\"Error calling Gemini:\", err);\n });\n // }\n\n\n } catch (error) {\n console.error('Error parsing function arguments:', error);\n console.log(out.arguments);\n handleLogMessage('Error parsing function arguments: ' + out.arguments, 'GUIDEAI');\n }\n }\n \n sendMessage({\n type: \"conversation.item.create\",\n item: {\n type: \"function_call_output\",\n call_id: out.call_id,\n output: JSON.stringify(true)\n }\n });\n\n sendMessage({\n type: 'response.create',\n });\n break;\n }\n }\n \n break;\n\n case \"response.function_call_arguments.delta\":\n // console.log('response.function_call_arguments.delta', message);\n break;\n\n case \"response.function_call_arguments.done\":\n // console.log('response.function_call_arguments.done', message);\n break;\n\n case \"output_audio_buffer.started\":\n setStatus('playing');\n break;\n\n case \"output_audio_buffer.stopped\":\n // console.log('output_audio_buffer.stopped');\n setStatus('recording');\n \n break;\n\n case 'error':\n if (message.error.code === 'session_expired') {\n console.error('Session expired:', message);\n // Batch state updates to prevent mounting/unmounting loops\n setTimeout(() => {\n setStatus('idle');\n setIsConnecting(false);\n setIsConversationActive(false);\n setEphemeralToken(null); // Clear expired token so reconnection gets fresh one\n hasCreatedConversationRef.current = false; // Allow creating new conversation\n }, 0);\n break;\n }\n console.error('OpenAI API error:', message);\n onError(new Error(message.error.message || 'Unknown API error'), 'OpenAI API error');\n // Batch state updates to prevent mounting/unmounting loops\n setTimeout(() => {\n setStatus('idle');\n setIsConnecting(false);\n setIsConversationActive(false);\n setEphemeralToken(null); // Clear token in case of auth-related errors\n hasCreatedConversationRef.current = false; // Allow creating new conversation\n }, 0);\n break;\n \n default:\n if (!IGNORE_MESSAGE_TYPES.some(fragment => message.type.includes(fragment))) {\n console.log('Unhandled:', message.type, message);\n }\n }\n } catch (error) {\n console.error('Error handling WebRTC message:', error);\n // Batch state updates to prevent mounting/unmounting loops\n setTimeout(() => {\n setStatus('idle');\n setIsConversationActive(false);\n }, 0);\n }\n };\n \n // Start the session using SDP\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n \n if (!pc.localDescription || !pc.localDescription.sdp) {\n throw new Error('Failed to create local SDP offer');\n }\n \n const baseUrl = OPENAI_REALTIME_BASE;\n const model = OPENAI_REALTIME_MODEL;\n \n \n const sdpResponse = await fetch(`${baseUrl}?model=${model}`, {\n method: 'POST',\n body: pc.localDescription.sdp,\n headers: {\n Authorization: `Bearer ${ephemeralToken}`,\n 'Content-Type': 'application/sdp'\n },\n });\n \n if (!sdpResponse.ok) {\n const errorText = await sdpResponse.text().catch(() => 'No error details available');\n console.error('WebRTC connection failed with status:', sdpResponse.status);\n console.error('Error details:', errorText);\n \n // Clear token on authentication failures\n if (sdpResponse.status === 401 || sdpResponse.status === 403) {\n setEphemeralToken(null);\n }\n \n throw new Error(`Failed to connect to OpenAI WebRTC: ${sdpResponse.status} - ${errorText}`);\n }\n \n const answerSdp = await sdpResponse.text();\n \n const answer = {\n type: 'answer',\n sdp: answerSdp,\n };\n \n await pc.setRemoteDescription(answer as RTCSessionDescriptionInit);\n \n return true;\n } catch (error) {\n console.error('Error initializing WebRTC:', error);\n onError(error as Error, 'WebRTC initialization failed');\n setIsConnecting(false);\n return false;\n }\n };\n\n // ===== SESSION MANAGEMENT =====\n \n const startConversationSession = hooks.useCallback(async () => {\n try {\n setStatus('processing');\n \n if (peerConnectionRef.current && dataChannelRef.current?.readyState === 'open' && mediaStreamRef.current) {\n mediaStreamRef.current.getAudioTracks().forEach(track => {\n track.enabled = true;\n });\n setStatus('recording');\n return true;\n } else {\n cleanupWebRTC();\n \n try {\n setIsConnecting(true);\n \n // Ensure we have a valid token (get fresh one if needed)\n if (!ephemeralToken) {\n console.log('No token available, getting fresh token...');\n const conversationData = await handleCreateNewConversation();\n if (!conversationData) {\n console.error('Failed to get fresh token');\n setIsConnecting(false);\n setStatus('idle');\n return false;\n }\n }\n \n let audioStream: MediaStream | null = null;\n \n // Always attempt microphone access for voice functionality\n if (true) {\n try {\n audioStream = await navigator.mediaDevices.getUserMedia({ \n audio: {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n channelCount: 1,\n sampleRate: 48000,\n }\n });\n mediaStreamRef.current = audioStream;\n console.log('Microphone access granted - audio tracks:', audioStream.getAudioTracks().length);\n } catch (micError) {\n console.error('Microphone access failed:', micError);\n console.log('Voice functionality will not be available - continuing with text-only mode');\n // Continue without microphone for text-only mode\n }\n }\n \n const initialized = await initializeWebRTC(audioStream);\n \n if (!initialized) {\n // Clean up if initialization fails\n if (audioStream) {\n audioStream.getAudioTracks().forEach(track => {\n track.stop();\n });\n mediaStreamRef.current = null;\n }\n setStatus('idle');\n setIsConnecting(false);\n return false;\n }\n \n // Set appropriate status based on whether we have audio\n if (audioStream) {\n setStatus('recording');\n } else {\n setStatus('idle'); // Text-only mode, no recording status needed\n }\n return true;\n } catch (error) {\n console.error('Error during conversation setup:', error);\n onError(error as Error, 'Microphone or WebRTC setup failed');\n setStatus('idle');\n setIsConnecting(false);\n return false;\n }\n }\n } catch (error) {\n setStatus('idle');\n setIsConnecting(false);\n onError(error as Error, 'Starting conversation failed');\n return false;\n }\n }, [ephemeralToken]); // Remove onError from dependencies to prevent re-creation\n\n const endConversationSession = hooks.useCallback(() => {\n if (!isClient) return;\n \n if (mediaStreamRef.current) {\n mediaStreamRef.current.getAudioTracks().forEach(track => {\n track.stop();\n });\n }\n \n cleanupWebRTC();\n setStatus('idle');\n \n // Clear localStorage when conversation is explicitly ended\n clearConversationStorage();\n \n // Reset restored messages state\n setRestoredMessages([]);\n \n // Reset auto-start flag to allow future auto-starts\n hasAttemptedAutoStartRef.current = false;\n }, [isClient]);\n\n // ===== UI EVENT HANDLERS =====\n \n const handleToggleConversation = async () => {\n if (!isClient) return;\n \n if (!hasInteracted) {\n setHasInteracted(true);\n localStorage.setItem('guideAI_hasInteracted', 'true');\n \n // Show onboarding on first interaction\n setShowOnboarding(true);\n return;\n }\n \n if (!isConversationActive) {\n const success = await startConversationSession();\n if (success) {\n setIsConversationActive(true);\n // Always show transcript when conversation starts\n if (transcriptOptions?.enabled !== false) {\n setShowTranscript(true);\n }\n // Show text input option by default (unless explicitly disabled)\n if (inputOptions?.enableTextInput !== false) {\n setShowTextInput(true);\n }\n }\n } else {\n endConversationSession();\n setIsConversationActive(false);\n // Hide transcript and text input when conversation ends\n if (transcriptOptions?.enabled !== false) {\n setShowTranscript(false);\n }\n setShowTextInput(false);\n }\n };\n\n const handleToggleTranscript = () => {\n setShowTranscript(prev => !prev);\n };\n\n const handleToggleInputMode = () => {\n setInputMode(prev => {\n const newMode = prev === 'voice' ? 'text' : 'voice';\n // Update text input visibility based on new mode\n if (newMode === 'text' && isConversationActive) {\n setShowTextInput(true);\n } else {\n setShowTextInput(false);\n }\n return newMode;\n });\n };\n\n\n\n const handleTextSubmit = async () => {\n if (!textInput.trim() || !isConversationActive) return;\n\n // Log the user's text message to console (matching voice input behavior)\n console.log('User transcript:', textInput.trim());\n \n // Log the user's text message\n handleLogMessage(textInput.trim(), 'HUMAN');\n\n // Send text message to AI via WebRTC data channel\n if (dataChannelRef.current?.readyState === 'open') {\n const message = {\n type: \"conversation.item.create\",\n item: {\n type: \"message\",\n role: \"user\",\n content: [{\n type: \"input_text\",\n text: textInput.trim()\n }]\n }\n };\n \n dataChannelRef.current.send(JSON.stringify(message));\n\n // Trigger response generation\n const responseMessage = {\n type: \"response.create\"\n };\n dataChannelRef.current.send(JSON.stringify(responseMessage));\n }\n\n // Clear the input\n setTextInput('');\n };\n\n const handleTextKeyPress = (event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleTextSubmit();\n }\n };\n\n const handleOnboardingComplete = async () => {\n setShowOnboarding(false);\n \n // Start the conversation session when onboarding is completed\n const success = await startConversationSession();\n if (success) {\n setIsConversationActive(true);\n // Only auto-show transcript if enabled in options\n if (transcriptOptions?.enabled !== false) {\n setShowTranscript(true);\n }\n }\n };\n\n const handleOnboardingClose = () => {\n setShowOnboarding(false);\n };\n\n const handleCloseTranscript = () => {\n setShowTranscript(false);\n };\n\n // ===== REACT EFFECTS & LIFECYCLE ===== \n \n // CSS styles injection effect\n hooks.useEffect(() => {\n console.log('GuideAI: Injecting styles...', { guideAIStyles: !!guideAIStyles });\n injectStyles(guideAIStyles, 'guide-ai-styles');\n \n // Debug: Check if styles were actually injected\n setTimeout(() => {\n const styleElement = document.getElementById('guide-ai-styles');\n console.log('GuideAI: Styles injection check:', { \n styleElement: !!styleElement, \n styleContent: styleElement?.textContent?.substring(0, 100) \n });\n }, 100);\n }, [guideAIStyles]);\n \n // Metadata initialization effect - EventTracker now auto-initializes\n hooks.useEffect(() => {\n console.log('GuideAI: Metadata initialization effect triggered', {\n metadataTracker: !!metadataTracker,\n metadataOptions: !!metadataOptions,\n initialUserData: metadataOptions?.initialUserData\n });\n \n // Initialize UserMetadataTracker\n try {\n metadataTracker.init();\n console.log('GuideAI: UserMetadataTracker.init() called successfully');\n } catch (error) {\n console.error('GuideAI: Failed to initialize UserMetadataTracker:', error);\n }\n \n if (metadataOptions?.initialUserData) {\n try {\n metadataTracker.updateUserInfo(metadataOptions.initialUserData);\n console.log('GuideAI: Initial user data updated successfully');\n } catch (error) {\n console.error('GuideAI: Failed to update initial user data:', error);\n }\n }\n }, [metadataTracker, metadataOptions?.initialUserData]);\n \n // Component initialization effect\n hooks.useEffect(() => {\n console.log('GuideAI: Component mounting - initialization started', { \n hasInitialized: hasInitializedRef.current,\n initializationAttempted: initializationAttemptedRef.current,\n isClient,\n organizationKey,\n timestamp: Date.now()\n });\n \n // Prevent multiple initializations\n if (hasInitializedRef.current || isUnmountingRef.current || initializationAttemptedRef.current) {\n console.log('GuideAI: Already initialized, unmounting, or initialization already attempted, skipping');\n return;\n }\n \n // Additional safeguard: check if we're in a browser environment\n if (typeof window === 'undefined') {\n console.log('GuideAI: Not in browser environment, skipping initialization');\n return;\n }\n \n try {\n // Mark that we've attempted initialization\n initializationAttemptedRef.current = true;\n \n // Set initialization flag immediately to prevent race conditions\n hasInitializedRef.current = true;\n \n // Set client flag\n setIsClient(true);\n \n // Check for stored conversation before initializing\n const { shouldRestore, conversationId, messages } = checkForStoredConversation(organizationKey);\n \n if (shouldRestore && conversationId && messages) {\n // Store the restored messages to use as context\n setRestoredMessages(messages);\n console.log('GuideAI: Restored conversation with', messages.length, 'messages');\n } else {\n // No valid conversation to restore\n setRestoredMessages([]);\n console.log('GuideAI: No conversation to restore');\n }\n \n // Create new conversation (gets conversation + token + prompt)\n handleCreateNewConversation().then(conversationData => {\n if (!isUnmountingRef.current) {\n console.log('GuideAI: Initialization completed successfully', { conversationData: !!conversationData });\n }\n }).catch(error => {\n if (!isUnmountingRef.current) {\n console.error('GuideAI: Failed to create conversation:', error);\n // Reset initialization flag on error to allow retry\n hasInitializedRef.current = false;\n initializationAttemptedRef.current = false;\n }\n });\n \n const hasInteractedBefore = localStorage.getItem('guideAI_hasInteracted');\n if (!hasInteractedBefore) {\n setHasInteracted(false);\n } else {\n setHasInteracted(true);\n }\n \n console.log('GuideAI: Initialization setup completed');\n } catch (error) {\n console.error('GuideAI: Error during initialization:', error);\n // Reset initialization flag on error\n hasInitializedRef.current = false;\n initializationAttemptedRef.current = false;\n }\n }, [organizationKey]); // Removed handleCreateNewConversation from dependencies\n \n // Component cleanup effect\n hooks.useEffect(() => {\n return () => {\n console.log('GuideAI: Component unmounting - cleanup triggered', { \n hasInitialized: hasInitializedRef.current,\n isUnmounting: isUnmountingRef.current,\n timestamp: Date.now()\n });\n \n // Set unmounting flag to prevent any new initialization\n isUnmountingRef.current = true;\n \n // Clean up WebRTC connections\n cleanupWebRTC();\n \n // Clean up audio element\n if (audioElementRef.current) {\n try {\n audioElementRef.current.srcObject = null;\n audioElementRef.current.remove();\n } catch (error) {\n console.warn('Error cleaning up audio element:', error);\n }\n audioElementRef.current = null;\n }\n \n // Stop event tracking on cleanup\n if (eventTracker) {\n try {\n eventTracker.stopTracking();\n } catch (error) {\n console.warn('Error stopping event tracker:', error);\n }\n }\n \n // Stop metadata tracking and sync any pending updates\n if (metadataTracker) {\n try {\n metadataTracker.destroy();\n } catch (error) {\n console.warn('Error destroying metadata tracker:', error);\n }\n }\n \n // Reset all flags on cleanup\n hasInitializedRef.current = false;\n isUnmountingRef.current = false;\n hasAttemptedAutoStartRef.current = false;\n hasCreatedConversationRef.current = false;\n initializationAttemptedRef.current = false;\n \n console.log('GuideAI: Cleanup completed');\n };\n }, []);\n\n // Metadata sync effect - IMPROVED WITH LONGER INTERVALS\n hooks.useEffect(() => {\n if (!metadataTracker) return;\n \n // Use improved default interval (5 minutes) but allow overrides\n const syncInterval = metadataOptions?.config?.syncInterval || 300000; // 5 minutes instead of 2 minutes\n \n const syncTimer = setInterval(async () => {\n if (metadataTracker) {\n const pendingUpdates = metadataTracker.syncNow();\n if (pendingUpdates.length > 0) {\n try {\n await sendMetadataUpdates(pendingUpdates, organizationKey, onError);\n \n // Notify parent component of metadata updates if callback provided\n if (metadataOptions?.onMetadataUpdate) {\n metadataOptions.onMetadataUpdate(metadataTracker.getMetadata());\n }\n } catch (error) {\n console.error('Failed to sync metadata updates:', error);\n // Note: Failed updates are retained in tracker for retry\n }\n }\n }\n }, syncInterval);\n\n return () => {\n clearInterval(syncTimer);\n };\n }, [organizationKey]); // Only depend on organizationKey\n\n // Auto-restart effect - TEMPORARILY DISABLED FOR TESTING\n hooks.useEffect(() => {\n // TESTING: Disable auto-restart to see if this fixes mounting/unmounting loop\n if (ephemeralToken && restoredMessages.length > 0 && !hasAttemptedAutoStartRef.current && !isConversationActive) {\n console.log('Auto-restarting conversation with', restoredMessages.length, 'restored messages');\n hasAttemptedAutoStartRef.current = true;\n \n startConversationSession().then(success => {\n if (success) {\n setIsConversationActive(true);\n setHasInteracted(true);\n localStorage.setItem('guideAI_hasInteracted', 'true');\n }\n });\n }\n }, [ephemeralToken, restoredMessages, isConversationActive]); // Add isConversationActive to prevent loops\n\n // Load restored messages into allMessages state - FIXED\n hooks.useEffect(() => {\n if (restoredMessages.length > 0 && allMessages.length === 0) {\n setAllMessages(restoredMessages);\n }\n }, [restoredMessages]); // Remove allMessages.length to prevent loops\n\n // Update popup position when needed - STABILIZED\n hooks.useEffect(() => {\n if (showOnboarding || (!hasInteracted && ephemeralToken)) {\n // Use onboarding dimensions if onboarding is shown, otherwise welcome bubble dimensions\n const height = showOnboarding ? 240 : 50;\n const spacing = showOnboarding ? 20 : 15;\n setPopupPosition(getOptimalPosition(height, spacing));\n }\n }, [showOnboarding, hasInteracted, ephemeralToken]); // Remove getOptimalPosition from dependencies\n\n // ===== TRANSCRIPT BOX PROPS MEMOIZATION =====\n \n // Memoize TranscriptBox props to prevent unnecessary re-renders\n const transcriptBoxProps = hooks.useMemo(() => ({\n messages: allMessages,\n isVisible: showTranscript,\n onClose: handleCloseTranscript,\n showToggleButton: transcriptOptions?.showToggleButton !== false && isConversationActive,\n onToggle: handleToggleTranscript,\n showTextInput: isConversationActive && inputOptions?.enableTextInput !== false,\n textInput: textInput,\n onTextInputChange: setTextInput,\n onTextSubmit: handleTextSubmit,\n onTextKeyPress: handleTextKeyPress,\n textPlaceholder: inputOptions?.placeholder || \"Type your message...\"\n }), [\n allMessages.length, // Only depend on length, not the full array\n showTranscript,\n isConversationActive,\n textInput,\n transcriptOptions?.showToggleButton,\n inputOptions?.enableTextInput,\n inputOptions?.placeholder\n ]);\n\n\n\n // ===== METADATA HELPER FUNCTIONS =====\n \n // Expose metadata tracking methods globally on window for external access - STABILIZED\n hooks.useEffect(() => {\n if (typeof window !== 'undefined') {\n // Create global GuideAI metadata API\n (window as any).GuideAI = {\n ...(window as any).GuideAI,\n metadata: {\n // Track user login\n trackLogin: (additionalInfo?: Partial<UserMetadata>) => {\n metadataTracker?.trackLogin(additionalInfo);\n },\n \n // Update user information\n updateUserInfo: (userInfo: Partial<UserMetadata>) => {\n metadataTracker?.updateUserInfo(userInfo);\n },\n \n // Track custom events\n trackCustomEvent: (eventType: string, customData: Record<string, any>) => {\n metadataTracker?.trackCustomEvent(eventType, customData);\n },\n \n // Get current metadata\n getMetadata: () => {\n return metadataTracker?.getMetadata() || null;\n },\n \n // Force sync metadata now\n syncMetadata: async () => {\n if (metadataTracker) {\n const updates = metadataTracker.syncNow();\n if (updates.length > 0) {\n return await sendMetadataUpdates(updates, organizationKey, onError);\n }\n }\n return true;\n },\n \n // Reset session visit tracking (call this when user logs in)\n resetSessionVisitTracking: () => {\n metadataTracker?.resetSessionVisitTracking();\n },\n \n // Manually track a visit (useful for login events)\n trackVisitManually: () => {\n metadataTracker?.trackVisitManually();\n }\n },\n transcript: {\n // Toggle transcript visibility\n toggle: () => {\n setShowTranscript(prev => !prev);\n },\n \n // Show transcript\n show: () => {\n setShowTranscript(true);\n },\n \n // Hide transcript\n hide: () => {\n setShowTranscript(false);\n },\n \n // Get current transcript visibility\n isVisible: () => {\n return showTranscript;\n }\n },\n input: {\n // Toggle input mode between voice and text\n toggleMode: () => {\n handleToggleInputMode();\n },\n \n // Set input mode\n setMode: (mode: 'voice' | 'text') => {\n setInputMode(mode);\n if (mode === 'text' && inputOptions?.enableTextInput !== false && isConversationActive) {\n setShowTextInput(true);\n } else {\n setShowTextInput(false);\n }\n },\n \n // Get current input mode\n getMode: () => {\n return inputMode;\n },\n \n // Send text message programmatically\n sendText: (text: string) => {\n if (text.trim() && isConversationActive) {\n setTextInput(text);\n handleTextSubmit();\n }\n }\n }\n };\n }\n }, [organizationKey]); // Only depend on organizationKey\n\n if (!isClient) {\n return null;\n }\n\n // Determine if we should use fixed positioning (when position props are provided)\n const shouldUseFixedPosition = position && Object.keys(position).length > 0;\n \n // Base styles for the component\n const baseStyles: React.CSSProperties = {\n position: shouldUseFixedPosition ? 'fixed' : 'relative',\n zIndex: shouldUseFixedPosition ? 1000 : 'auto',\n ...(shouldUseFixedPosition ? position : {}),\n };\n\n return (\n <>\n <div\n ref={componentRef}\n style={baseStyles}\n >\n <div className=\"guideai-main-ui\">\n {!hasInteracted && ephemeralToken && (\n <WelcomeBubble position={popupPosition} />\n )}\n \n <Onboarding \n position={popupPosition}\n isVisible={showOnboarding}\n onComplete={handleOnboardingComplete}\n onClose={handleOnboardingClose}\n />\n \n <div className=\"guideai-main-controls\">\n <div \n className={`guideai-icon-wrapper ${isConnecting ? 'initializing' : status}`}\n onClick={isConnecting ? undefined : handleToggleConversation}\n style={{\n ...(isConnecting ? { cursor: 'default' } : {})\n }}\n title={isConnecting ? 'Initializing...' : \n status === 'idle' ? 'Click to start conversation' : \n status === 'recording' ? 'Click to end conversation' : \n status === 'processing' ? 'Processing your message...' : \n 'AI is speaking, click to end conversation'}\n >\n {isConnecting && <i className=\"guideai-icon initializing\" />}\n {!isConnecting && status === 'idle' && <i className=\"guideai-icon microphone\" />}\n {!isConnecting && status === 'recording' && <i className=\"guideai-icon recording\" />}\n {!isConnecting && status === 'processing' && <i className=\"guideai-icon processing\" />}\n {!isConnecting && status === 'playing' && <i className=\"guideai-icon playing\" />}\n </div>\n \n </div>\n \n </div>\n </div>\n \n {/* Transcript Box */}\n <TranscriptBox {...transcriptBoxProps} />\n </>\n );\n};\n\nconst GuideAI = GuideAIComponent;\n\nexport default GuideAI;","import { PopupPosition } from '../types/GuideAI.types';\n\n// Function to inject CSS styles into the document head\nexport const injectStyles = (css: string, id: string) => {\n if (typeof document === 'undefined') return; // SSR safety\n \n // Check if styles are already injected\n if (document.getElementById(id)) return;\n \n const styleElement = document.createElement('style');\n styleElement.id = id;\n styleElement.textContent = css;\n document.head.appendChild(styleElement);\n};\n\n// Calculate optimal position for popups based on component location\nexport const calculateOptimalPosition = (\n componentRect: DOMRect, \n elementHeight: number = 240, \n spacing: number = 15\n): PopupPosition => {\n const viewportHeight = window.innerHeight;\n \n const spaceAbove = componentRect.top;\n const spaceBelow = viewportHeight - componentRect.bottom;\n \n const canFitBelow = spaceBelow >= (elementHeight + spacing);\n const canFitAbove = spaceAbove >= (elementHeight + spacing);\n \n if (canFitBelow) {\n return 'below';\n } else if (canFitAbove) {\n return 'above';\n } else {\n return spaceBelow > spaceAbove ? 'below' : 'above';\n }\n}; ","import React from 'react';\nimport { StoredMessage } from '../utils/messageStorage';\n\ninterface TranscriptBoxProps {\n messages: StoredMessage[];\n isVisible: boolean;\n onClose: () => void;\n showToggleButton?: boolean;\n onToggle?: () => void;\n showTextInput?: boolean;\n textInput?: string;\n onTextInputChange?: (value: string) => void;\n onTextSubmit?: () => void;\n onTextKeyPress?: (event: React.KeyboardEvent) => void;\n textPlaceholder?: string;\n}\n\nconst TranscriptBoxComponent: React.FC<TranscriptBoxProps> = ({ \n messages, \n isVisible, \n onClose, \n showToggleButton = true, \n onToggle,\n showTextInput = false,\n textInput = '',\n onTextInputChange,\n onTextSubmit,\n onTextKeyPress,\n textPlaceholder = \"Type your message...\"\n}) => {\n const formatTime = (timestamp: number) => {\n return new Date(timestamp).toLocaleTimeString([], { \n hour: '2-digit', \n minute: '2-digit' \n });\n };\n\n const scrollToBottom = (element: HTMLDivElement) => {\n element.scrollTop = element.scrollHeight;\n };\n\n React.useEffect(() => {\n const transcriptContainer = document.getElementById('guideai-transcript-container');\n if (transcriptContainer) {\n scrollToBottom(transcriptContainer as HTMLDivElement);\n }\n }, [messages.length]); // Only depend on length, not the full messages array\n\n // Show toggle button if there are messages and toggle is enabled, even when transcript is hidden\n const shouldShowToggleButton = showToggleButton && messages.length > 0;\n \n // Show transcript if visible OR if text input should be shown\n const shouldShowTranscript = isVisible || showTextInput;\n \n // Don't render anything if no messages, no toggle button, and no text input\n if (messages.length === 0 && !shouldShowToggleButton && !showTextInput) return null;\n\n return (\n <div className=\"guideai-transcript-overlay\">\n {/* Toggle Button - shows even when transcript is hidden */}\n {shouldShowToggleButton && (\n <button\n className=\"guideai-transcript-toggle-button\"\n onClick={onToggle}\n title={isVisible ? 'Hide Transcript' : 'Show Transcript'}\n >\n <span className=\"guideai-transcript-toggle-icon\">\n {isVisible ? '📄' : '📋'}\n </span>\n </button>\n )}\n \n {/* Transcript Box - show when visible or when text input is needed */}\n {shouldShowTranscript && (\n <div className=\"guideai-transcript-box\">\n {/* Messages Container - only show when transcript is visible */}\n {isVisible && (\n <div \n id=\"guideai-transcript-container\"\n className=\"guideai-transcript-messages\"\n >\n {messages.length === 0 ? (\n <div className=\"guideai-transcript-empty\">\n <div className=\"guideai-transcript-empty-icon\">🎤</div>\n <p>Start a conversation to see the transcript here</p>\n </div>\n ) : (\n messages.map((message, index) => (\n <div \n key={`${message.timestamp}-${index}`}\n className={`guideai-transcript-message ${message.sender.toLowerCase()}`}\n >\n <div className=\"guideai-transcript-message-content\">\n <div className=\"guideai-transcript-message-sender\">\n {message.sender === 'HUMAN' ? 'You' : 'GuideAI'}\n </div>\n <div className=\"guideai-transcript-message-text\">\n {message.content}\n </div>\n <div className=\"guideai-transcript-message-time\">\n {formatTime(message.timestamp)}\n </div>\n </div>\n </div>\n ))\n )}\n </div>\n )}\n \n {/* Text Input - show when in text mode */}\n {showTextInput && (\n <div className=\"guideai-transcript-text-input\">\n <textarea\n className=\"guideai-transcript-input-field\"\n value={textInput}\n onChange={(e) => onTextInputChange?.(e.target.value)}\n onKeyPress={onTextKeyPress}\n placeholder={textPlaceholder}\n rows={2}\n />\n <button\n className=\"guideai-transcript-send-button\"\n onClick={onTextSubmit}\n disabled={!textInput.trim()}\n title=\"Send Message\"\n >\n <span className=\"guideai-transcript-send-icon\">📤</span>\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n\nconst TranscriptBox = React.memo(TranscriptBoxComponent, (prevProps, nextProps) => {\n // Only re-render if essential props change\n return (\n prevProps.messages.length === nextProps.messages.length &&\n prevProps.isVisible === nextProps.isVisible &&\n prevProps.showToggleButton === nextProps.showToggleButton &&\n prevProps.showTextInput === nextProps.showTextInput &&\n prevProps.textInput === nextProps.textInput &&\n prevProps.textPlaceholder === nextProps.textPlaceholder\n );\n});\n\nTranscriptBox.displayName = 'TranscriptBox';\n\nexport default TranscriptBox; ","import React, { useState } from 'react';\nimport { PopupPosition } from '../types/GuideAI.types';\n\ninterface OnboardingProps {\n position: PopupPosition;\n isVisible: boolean;\n onComplete: () => void; // Called when user completes onboarding and wants to start conversation\n onClose: () => void; // Called when user closes onboarding without completing\n}\n\nconst Onboarding: React.FC<OnboardingProps> = ({ position, isVisible, onComplete, onClose }) => {\n const [step, setStep] = useState(1);\n\n const handleNext = () => {\n if (step < 3) {\n setStep(step + 1);\n } else {\n // Reset step and complete onboarding\n setStep(1);\n onComplete();\n }\n };\n\n const handleClose = () => {\n // Reset step when closing\n setStep(1);\n onClose();\n };\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <div className={`guideai-onboarding ${position}`}>\n <div className=\"guideai-onboarding-content\">\n <button className=\"guideai-onboarding-close\" onClick={handleClose}>×</button>\n \n {step === 1 && (\n <div className=\"guideai-onboarding-step\">\n <div className=\"guideai-onboarding-icon volume-icon\">🔊</div>\n <h3>Turn on your volume</h3>\n <p>Make sure your device's volume is turned on so you can hear Guide AI's responses.</p>\n </div>\n )}\n \n {step === 2 && (\n <div className=\"guideai-onboarding-step\">\n <div className=\"guideai-onboarding-icon mic-icon\">🎤</div>\n <h3>Allow microphone access</h3>\n <p>When prompted, click \"Allow\" to let Guide AI access your microphone.</p>\n </div>\n )}\n \n {step === 3 && (\n <div className=\"guideai-onboarding-step\">\n <div className=\"guideai-onboarding-icon ready-icon\">✅</div>\n <h3>You're all set!</h3>\n <p>Click the microphone icon to start asking questions.</p>\n </div>\n )}\n \n <div className=\"guideai-onboarding-dots\">\n <span className={`dot ${step === 1 ? 'active' : ''}`}></span>\n <span className={`dot ${step === 2 ? 'active' : ''}`}></span>\n <span className={`dot ${step === 3 ? 'active' : ''}`}></span>\n </div>\n \n <button className=\"guideai-onboarding-next\" onClick={handleNext}>\n {step < 3 ? 'Next' : 'Get Started'}\n </button>\n </div>\n </div>\n );\n};\n\nexport default Onboarding; ","import React from 'react';\nimport { PopupPosition } from '../types/GuideAI.types';\n\ninterface WelcomeBubbleProps {\n position: PopupPosition;\n}\n\nconst WelcomeBubble: React.FC<WelcomeBubbleProps> = ({ position }) => {\n return (\n <div className={`guideai-welcome-bubble ${position}`}>\n Stuck? Click here and ask me a question\n </div>\n );\n};\n\nexport default WelcomeBubble; ","// Event tracking system for GuideAI\n// Tracks click, focus, change, and submit events on all relevant elements\n\ninterface EventData {\n type: 'click' | 'focus' | 'change' | 'submit' | 'route_change';\n element?: Element;\n tagName?: string;\n className?: string;\n id?: string;\n textContent?: string;\n value?: string;\n formData?: FormData;\n timestamp: number;\n url: string;\n previousUrl?: string;\n event?: Event;\n // Customer metadata\n customerId?: string;\n customerType?: string;\n customerSegment?: string;\n // Session metadata\n sessionId?: string;\n deviceType?: 'desktop' | 'mobile' | 'tablet';\n userAgent?: string;\n screenResolution?: string;\n // Application context\n currentPage?: string;\n userRole?: string;\n organizationId?: string;\n // Geographic and timezone\n timezone?: string;\n locale?: string;\n}\n\nclass EventTracker {\n private isTracking: boolean = false;\n private eventData: EventData[] = [];\n private batchSize: number = 25; // Increased from 10 to 25 events\n private batchTimeout: number = 15000; // Increased from 5s to 15s\n private batchTimer: NodeJS.Timeout | null = null;\n private pendingEvents: EventData[] = [];\n private currentUrl: string = '';\n private organizationKey?: string;\n private lastEventTime: number = 0;\n private eventThrottleInterval: number = 500; // Minimum 500ms between similar events\n private lastEventsByType: Map<string, number> = new Map();\n private duplicateEventBuffer: Map<string, EventData> = new Map();\n private maxDuplicateBuffer: number = 100;\n private isInitialized: boolean = false;\n\n // Customer metadata storage\n private customerMetadata: {\n customerId?: string;\n customerType?: string;\n customerSegment?: string;\n userRole?: string;\n organizationId?: string;\n } = {};\n\n constructor(options?: { \n customerId?: string; \n customerType?: string; \n organizationId?: string; \n organizationKey?: string;\n batchSize?: number;\n batchTimeout?: number;\n eventThrottleInterval?: number;\n }) {\n if (options?.customerId) {\n this.customerMetadata.customerId = String(options.customerId);\n }\n if (options?.customerType) {\n this.customerMetadata.customerType = options.customerType;\n }\n if (options?.organizationId) {\n this.customerMetadata.organizationId = String(options.organizationId);\n }\n if (options?.organizationKey) {\n this.organizationKey = options.organizationKey;\n }\n \n // Allow customization of batching parameters\n if (options?.batchSize && options.batchSize > 0) {\n this.batchSize = options.batchSize;\n }\n if (options?.batchTimeout && options.batchTimeout > 0) {\n this.batchTimeout = options.batchTimeout;\n }\n if (options?.eventThrottleInterval && options.eventThrottleInterval >= 0) {\n this.eventThrottleInterval = options.eventThrottleInterval;\n }\n \n // Auto-initialize like UserMetadataTracker\n this.init();\n }\n\n // Auto-initialization method\n private init(): void {\n if (this.isInitialized) return;\n \n // Load existing events from localStorage\n this.loadEventsFromStorage();\n \n // Start tracking when DOM is ready\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => this.startTracking());\n } else {\n this.startTracking();\n }\n }\n \n this.isInitialized = true;\n if (process.env.NODE_ENV === 'development') {\n console.log('EventTracker: Auto-initialized and ready');\n }\n }\n\n // Load events from localStorage\n private loadEventsFromStorage(): void {\n if (typeof window === 'undefined') return;\n \n try {\n const storageKey = `guideai_events_${this.organizationKey || 'default'}`;\n const storedEvents = localStorage.getItem(storageKey);\n \n if (storedEvents) {\n const parsedEvents = JSON.parse(storedEvents);\n this.eventData = Array.isArray(parsedEvents) ? parsedEvents : [];\n if (process.env.NODE_ENV === 'development') {\n console.log('EventTracker: Loaded', this.eventData.length, 'events from localStorage');\n }\n }\n } catch (error) {\n console.warn('EventTracker: Failed to load events from localStorage', error);\n }\n }\n\n // Save events to localStorage\n private saveEventsToStorage(): void {\n if (typeof window === 'undefined') return;\n \n try {\n const storageKey = `guideai_events_${this.organizationKey || 'default'}`;\n \n // Sanitize event data before saving to localStorage to prevent circular reference errors\n const sanitizedEventData = this.eventData.map(event => this.sanitizeEventData(event));\n \n if (process.env.NODE_ENV === 'development') {\n console.log('EventTracker: saveEventsToStorage called with', this.eventData.length, 'events');\n }\n localStorage.setItem(storageKey, JSON.stringify(sanitizedEventData));\n if (process.env.NODE_ENV === 'development') {\n console.log('EventTracker: Successfully saved', sanitizedEventData.length, 'events to localStorage');\n }\n } catch (error) {\n console.warn('EventTracker: Failed to save events to localStorage', error);\n }\n }\n\n\n // Method to set customer metadata\n public setCustomerMetadata(metadata: Partial<typeof this.customerMetadata>): void {\n // All remaining metadata is safe to collect\n this.customerMetadata = { ...this.customerMetadata, ...metadata };\n }\n\n // Method to get customer metadata\n public getCustomerMetadata(): typeof this.customerMetadata {\n return { ...this.customerMetadata };\n }\n\n // Method to clear customer metadata\n public clearCustomerMetadata(): void {\n this.customerMetadata = {};\n }\n\n public initialize(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n // Start tracking when DOM is ready\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => this.startTracking());\n } else {\n this.startTracking();\n }\n }\n\n // Helper method to enrich event data with customer metadata\n private enrichEventData(baseEventData: Omit<EventData, 'customerId' | 'customerType' | 'customerSegment' | 'sessionId' | 'deviceType' | 'userAgent' | 'screenResolution' | 'currentPage' | 'userRole' | 'organizationId' | 'timezone' | 'locale'>): EventData {\n // Sanitize the base event data to remove circular references\n const sanitizedBaseData = this.sanitizeEventData(baseEventData);\n \n const enrichedData = {\n ...sanitizedBaseData,\n // Customer metadata\n customerId: this.customerMetadata.customerId,\n customerType: this.customerMetadata.customerType,\n customerSegment: this.customerMetadata.customerSegment,\n organizationKey: this.organizationKey,\n // Session metadata\n sessionId: this.getSessionId(),\n deviceType: this.getDeviceType(),\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n screenResolution: typeof screen !== 'undefined' ? `${screen.width}x${screen.height}` : undefined,\n // Application context\n currentPage: window.location.pathname,\n userRole: this.customerMetadata.userRole,\n organizationId: this.customerMetadata.organizationId,\n // Geographic and timezone\n timezone: typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined'\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : undefined,\n locale: navigator.language,\n };\n\n return enrichedData;\n }\n\n // Helper method to sanitize event data and remove circular references\n private sanitizeEventData(eventData: any): any {\n const sanitized: any = {};\n \n // Copy safe primitive values\n Object.keys(eventData).forEach(key => {\n const value = eventData[key];\n \n switch (key) {\n case 'element':\n // Extract only safe element properties\n if (value && typeof value === 'object') {\n sanitized[key] = {\n tagName: value.tagName,\n id: value.id,\n className: value.className,\n textContent: value.textContent?.substring(0, 100)\n };\n }\n break;\n \n case 'event':\n // Remove event object entirely - it contains circular references\n break;\n \n case 'formData':\n // Convert FormData to plain object\n if (value instanceof FormData) {\n const formDataObj: Record<string, any> = {};\n value.forEach((val, key) => {\n formDataObj[key] = val;\n });\n sanitized[key] = formDataObj;\n } else {\n sanitized[key] = value;\n }\n break;\n \n default:\n // Copy primitive values and safe objects\n if (value !== null && \n (typeof value === 'string' || \n typeof value === 'number' || \n typeof value === 'boolean' ||\n Array.isArray(value))) {\n sanitized[key] = value;\n }\n break;\n }\n });\n \n return sanitized;\n }\n\n\n\n // Helper method to get or create session ID\n private getSessionId(): string {\n let sessionId = sessionStorage.getItem('eventTracker_sessionId');\n if (!sessionId) {\n sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n sessionStorage.setItem('eventTracker_sessionId', sessionId);\n }\n return sessionId;\n }\n\n // Helper method to detect device type\n private getDeviceType(): 'desktop' | 'mobile' | 'tablet' {\n if (typeof navigator === 'undefined') {\n return 'desktop';\n }\n const userAgent = navigator.userAgent.toLowerCase();\n if (/tablet|ipad|playbook|silk/i.test(userAgent)) {\n return 'tablet';\n }\n if (/mobile|android|iphone|ipod|blackberry|opera mini|iemobile/i.test(userAgent)) {\n return 'mobile';\n }\n return 'desktop';\n }\n\n public startTracking(): void {\n if (this.isTracking) return;\n\n this.isTracking = true;\n if (process.env.NODE_ENV === 'development') {\n console.log('EventTracker: Started tracking events');\n }\n\n // Track click events on all elements\n this.trackClickEvents();\n\n // Track focus events on focusable elements\n this.trackFocusEvents();\n\n // Track change events on form elements\n this.trackChangeEvents();\n\n // Track submit events on forms\n this.trackSubmitEvents();\n\n // Track route changes\n this.trackRouteChanges();\n }\n\n public stopTracking(): void {\n if (!this.isTracking) return;\n\n this.isTracking = false;\n if (process.env.NODE_ENV === 'development') {\n console.log('EventTracker: Stopped tracking events');\n }\n\n // Emit any pending events before stopping\n this.emitBatch();\n\n // Remove all event listeners\n document.removeEventListener('click', this.handleClick, true);\n document.removeEventListener('focus', this.handleFocus, true);\n document.removeEventListener('change', this.handleChange, true);\n document.removeEventListener('submit', this.handleSubmit, true);\n window.removeEventListener('popstate', this.handlePopstate);\n window.removeEventListener('hashchange', this.handleHashChange);\n }\n\n private trackClickEvents(): void {\n document.addEventListener('click', this.handleClick, true);\n }\n\n private trackFocusEvents(): void {\n document.addEventListener('focus', this.handleFocus, true);\n }\n\n private trackChangeEvents(): void {\n document.addEventListener('change', this.handleChange, true);\n }\n\n private trackSubmitEvents(): void {\n document.addEventListener('submit', this.handleSubmit, true);\n }\n\n private trackRouteChanges(): void {\n // Track browser back/forward navigation\n window.addEventListener('popstate', this.handlePopstate);\n\n // Track hash changes\n window.addEventListener('hashchange', this.handleHashChange);\n\n // Track History API changes (pushState, replaceState)\n this.interceptHistoryAPI();\n }\n\n private handleClick = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n \n // Filter out clicks on non-interactive elements unless they have special attributes\n if (!this.isSignificantClickTarget(target)) {\n return;\n }\n \n const baseEventData = {\n type: 'click' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n value: this.getSafeValue(target),\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n };\n\n private handleFocus = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n\n // Only track focus on significant focusable elements\n if (this.isFocusable(target) && this.isSignificantFocusTarget(target)) {\n const baseEventData = {\n type: 'focus' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n value: this.getSafeValue(target),\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n }\n };\n\n private handleChange = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n\n // Only track change on form elements\n if (this.isFormElement(target)) {\n const baseEventData = {\n type: 'change' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n value: this.getSafeValue(target),\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n }\n };\n\n private handleSubmit = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n\n // Only track submit on form elements\n if (target.tagName === 'FORM') {\n const form = target as HTMLFormElement;\n const formData = new FormData(form);\n\n const baseEventData = {\n type: 'submit' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n formData: formData,\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n }\n };\n\n private handlePopstate = (event: PopStateEvent): void => {\n if (!this.isTracking) return;\n\n const newUrl = window.location.href;\n const previousUrl = this.getPreviousUrl();\n \n // Only track if URL actually changed meaningfully\n if (this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl,\n event: event\n };\n\n this.logEvent(baseEventData);\n this.currentUrl = newUrl; // Update current URL\n }\n };\n\n private handleHashChange = (event: HashChangeEvent): void => {\n if (!this.isTracking) return;\n\n const newUrl = window.location.href;\n const previousUrl = event.oldURL;\n \n // Only track if hash change is significant\n if (this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl,\n event: event\n };\n\n this.logEvent(baseEventData);\n this.currentUrl = newUrl; // Update current URL\n }\n };\n\n private interceptHistoryAPI(): void {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n // Store the current URL before interception\n this.currentUrl = window.location.href;\n\n // Intercept pushState\n history.pushState = (...args) => {\n const previousUrl = this.currentUrl;\n const result = originalPushState.apply(history, args);\n const newUrl = window.location.href;\n this.currentUrl = newUrl;\n\n if (this.isTracking && this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl\n };\n this.logEvent(baseEventData);\n }\n\n return result;\n };\n\n // Intercept replaceState\n history.replaceState = (...args) => {\n const previousUrl = this.currentUrl;\n const result = originalReplaceState.apply(history, args);\n const newUrl = window.location.href;\n this.currentUrl = newUrl;\n\n if (this.isTracking && this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl\n };\n this.logEvent(baseEventData);\n }\n\n return result;\n };\n }\n\n private getPreviousUrl(): string {\n return this.currentUrl || window.location.href;\n }\n\n private getSafeValue(element: Element): string | undefined {\n const tagName = element.tagName.toLowerCase();\n const inputElement = element as HTMLInputElement;\n const selectElement = element as HTMLSelectElement;\n\n // Only collect values for elements that are typically useful for targeting\n switch (tagName) {\n case 'button':\n return inputElement.value || inputElement.textContent?.trim();\n\n case 'select':\n return `${selectElement.name || ''}|${selectElement.type || ''}|${selectElement.selectedIndex || 0}`;\n\n case 'textarea':\n return inputElement.name || inputElement.type || '';\n\n case 'input':\n const inputType = inputElement.type;\n // Only collect value for button-like input types\n if (inputType === 'submit' || inputType === 'button' || inputType === 'reset') {\n return `${inputElement.name || ''}|${inputType}|${inputElement.value || ''}`;\n }\n // For other input types, only collect name and type, not the actual value\n return `${inputElement.name || ''}|${inputType}`;\n\n default:\n return undefined;\n }\n }\n\n private isFocusable(element: Element): boolean {\n const focusableSelectors = [\n 'input:not([type=\"hidden\"])',\n 'select',\n 'textarea',\n 'button',\n 'a[href]',\n 'area[href]',\n 'iframe',\n 'object',\n 'embed',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]'\n ];\n\n return focusableSelectors.some(selector => element.matches(selector));\n }\n\n private isFormElement(element: Element): boolean {\n const formElementTags = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'];\n return formElementTags.includes(element.tagName);\n }\n\n // Helper method to generate a unique key for event deduplication\n private generateEventKey(eventData: any): string {\n return `${eventData.type}_${eventData.tagName}_${eventData.id || ''}_${eventData.className || ''}_${eventData.url}`;\n }\n\n // Helper method to check if event should be throttled\n private shouldThrottleEvent(eventData: any, currentTime: number): boolean {\n const lastEventTime = this.lastEventsByType.get(eventData.type) || 0;\n return (currentTime - lastEventTime) < this.eventThrottleInterval;\n }\n\n // Helper method to check for duplicate events\n private isDuplicateEvent(eventKey: string, eventData: any): boolean {\n const existingEvent = this.duplicateEventBuffer.get(eventKey);\n if (!existingEvent) return false;\n \n // Consider events duplicate if they're within 2 seconds and have same content\n const timeDiff = eventData.timestamp - existingEvent.timestamp;\n return timeDiff < 2000 && existingEvent.textContent === eventData.textContent;\n }\n\n // Helper method to determine if click target is significant\n private isSignificantClickTarget(element: Element): boolean {\n const significantTags = ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'];\n const interactiveRoles = ['button', 'link', 'menuitem', 'tab', 'option'];\n \n // Always track clicks on interactive elements\n if (significantTags.includes(element.tagName)) {\n return true;\n }\n \n // Track elements with interactive roles\n const role = element.getAttribute('role');\n if (role && interactiveRoles.includes(role.toLowerCase())) {\n return true;\n }\n \n // Track elements with click handlers (has onclick or data-* attributes)\n if (element.hasAttribute('onclick') || \n Array.from(element.attributes).some(attr => attr.name.startsWith('data-'))) {\n return true;\n }\n \n // Track elements with cursor pointer style\n const style = window.getComputedStyle(element);\n if (style.cursor === 'pointer') {\n return true;\n }\n \n return false;\n }\n\n // Helper method to determine if focus target is significant\n private isSignificantFocusTarget(element: Element): boolean {\n // Only track focus on form elements and elements with tabindex\n const formElements = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'];\n if (formElements.includes(element.tagName)) {\n return true;\n }\n \n // Track elements with positive tabindex\n const tabindex = element.getAttribute('tabindex');\n if (tabindex && parseInt(tabindex) >= 0) {\n return true;\n }\n \n return false;\n }\n\n // Helper method to determine if route change is significant\n private isSignificantRouteChange(newUrl: string, previousUrl: string): boolean {\n if (!newUrl || !previousUrl || newUrl === previousUrl) {\n return false;\n }\n \n try {\n const newUrlObj = new URL(newUrl);\n const prevUrlObj = new URL(previousUrl);\n \n // Ignore query parameter only changes\n if (newUrlObj.pathname === prevUrlObj.pathname && \n newUrlObj.hash === prevUrlObj.hash) {\n return false;\n }\n \n // Ignore small hash changes (like anchor scrolling)\n if (newUrlObj.pathname === prevUrlObj.pathname && \n Math.abs(newUrlObj.hash.length - prevUrlObj.hash.length) < 5) {\n return false;\n }\n \n return true;\n } catch (error) {\n // If URL parsing fails, consider it significant to be safe\n return true;\n }\n }\n\n private logEvent(baseEventData: Omit<EventData, 'customerType' | 'customerSegment' | 'sessionId' | 'deviceType' | 'currentPage' | 'userRole' | 'locale'>): void {\n const now = Date.now();\n const eventKey = this.generateEventKey(baseEventData);\n \n // Throttle similar events\n if (this.shouldThrottleEvent(baseEventData, now)) {\n return;\n }\n \n // Check for duplicate events\n if (this.isDuplicateEvent(eventKey, baseEventData)) {\n return;\n }\n \n // Enrich the event data with customer metadata\n const enrichedEventData = this.enrichEventData(baseEventData);\n\n // Store event data for historical access (with size limit)\n this.eventData.push(enrichedEventData);\n if (this.eventData.length > 1000) { // Limit stored events\n this.eventData = this.eventData.slice(-500); // Keep last 500\n }\n\n // Add to pending batch\n this.pendingEvents.push(enrichedEventData);\n \n // Update throttle tracking\n this.lastEventsByType.set(baseEventData.type, now);\n this.duplicateEventBuffer.set(eventKey, enrichedEventData);\n \n // Clean duplicate buffer if it gets too large\n if (this.duplicateEventBuffer.size > this.maxDuplicateBuffer) {\n const oldestKeys = Array.from(this.duplicateEventBuffer.keys()).slice(0, 50);\n oldestKeys.forEach(key => this.duplicateEventBuffer.delete(key));\n }\n\n // Save to localStorage immediately for persistence\n this.saveEventsToStorage();\n\n // Check if we should emit based on batch size\n if (this.pendingEvents.length >= this.batchSize) {\n this.emitBatch();\n } else {\n // Start or reset the timeout for time-based emission\n this.startBatchTimer();\n }\n }\n\n private startBatchTimer(): void {\n // Clear existing timer\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n }\n\n // Start new timer\n this.batchTimer = setTimeout(() => {\n this.emitBatch();\n }, this.batchTimeout);\n }\n\n private emitBatch(): void {\n if (this.pendingEvents.length === 0) return;\n\n // Create batch payload\n const batchPayload = {\n events: [...this.pendingEvents],\n batchSize: this.pendingEvents.length,\n timestamp: Date.now(),\n };\n\n // Console log the batch\n if (process.env.NODE_ENV === 'development') {\n console.log('EventTracker Batch:', batchPayload);\n }\n\n // Save events to localStorage\n this.saveEventsToStorage();\n\n // Emit custom event for metadata tracking integration\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent('guideai:event_batch', {\n detail: batchPayload\n }));\n }\n\n // Clear pending events\n this.pendingEvents = [];\n\n // Clear timer\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = null;\n }\n }\n\n public getEventData(): EventData[] {\n return [...this.eventData];\n }\n\n public clearEventData(): void {\n this.eventData = [];\n }\n\n public getEventDataByType(type: EventData['type']): EventData[] {\n return this.eventData.filter(event => event.type === type);\n }\n\n public getEventDataByElement(tagName: string): EventData[] {\n return this.eventData.filter(event => event.tagName === tagName.toUpperCase());\n }\n\n public getEventDataByUrl(url: string): EventData[] {\n return this.eventData.filter(event => event.url.includes(url));\n }\n\n public emitPendingEvents(): void {\n this.emitBatch();\n }\n\n public setBatchConfig(batchSize: number, batchTimeout: number): void {\n this.batchSize = batchSize;\n this.batchTimeout = batchTimeout;\n }\n\n public getPendingEventsCount(): number {\n return this.pendingEvents.length;\n }\n\n // Customer analytics methods\n public getCustomerAnalytics(): {\n totalEvents: number;\n eventsByCustomer: Record<string, number>;\n eventsByType: Record<string, number>;\n eventsByPage: Record<string, number>;\n eventsByDevice: Record<string, number>;\n sessionDuration: number;\n lastActivity: number;\n } {\n const now = Date.now();\n const sessionStart = this.getSessionStartTime();\n const sessionDuration = now - sessionStart;\n\n const analytics = {\n totalEvents: this.eventData.length,\n eventsByCustomer: {} as Record<string, number>,\n eventsByType: {} as Record<string, number>,\n eventsByPage: {} as Record<string, number>,\n eventsByDevice: {} as Record<string, number>,\n sessionDuration,\n lastActivity: this.eventData.length > 0 ? this.eventData[this.eventData.length - 1].timestamp : now\n };\n\n // Group events by customer\n this.eventData.forEach(event => {\n if (event.customerId) {\n analytics.eventsByCustomer[event.customerId] = (analytics.eventsByCustomer[event.customerId] || 0) + 1;\n }\n\n // Group events by type\n analytics.eventsByType[event.type] = (analytics.eventsByType[event.type] || 0) + 1;\n\n // Group events by page\n if (event.currentPage) {\n analytics.eventsByPage[event.currentPage] = (analytics.eventsByPage[event.currentPage] || 0) + 1;\n }\n\n // Group events by device\n if (event.deviceType) {\n analytics.eventsByDevice[event.deviceType] = (analytics.eventsByDevice[event.deviceType] || 0) + 1;\n }\n });\n\n return analytics;\n }\n\n // Get session start time\n private getSessionStartTime(): number {\n const sessionStart = sessionStorage.getItem('eventTracker_sessionStart');\n if (!sessionStart) {\n const startTime = Date.now();\n sessionStorage.setItem('eventTracker_sessionStart', startTime.toString());\n return startTime;\n }\n return parseInt(sessionStart, 10);\n }\n\n // Method to identify customer from DOM or context\n public identifyCustomerFromContext(): void {\n // Try to find customer information from the current page context\n // This could be from meta tags, data attributes, or other DOM elements\n\n\n\n // Check for user role in meta tags\n const userRoleMeta = document.querySelector('meta[name=\"user-role\"]');\n if (userRoleMeta) {\n this.setCustomerMetadata({ userRole: userRoleMeta.getAttribute('content') || undefined });\n }\n\n\n\n // Try to infer customer type from URL or page context\n const pathname = window.location.pathname;\n if (pathname.includes('/admin/')) {\n this.setCustomerMetadata({ customerType: 'admin' });\n } else if (pathname.includes('/agent/')) {\n this.setCustomerMetadata({ customerType: 'agent' });\n } else if (pathname.includes('/business/')) {\n this.setCustomerMetadata({ customerType: 'business' });\n } else if (pathname.includes('/individual/')) {\n this.setCustomerMetadata({ customerType: 'individual' });\n }\n }\n\n // Method to set customer metadata from external source (e.g., authentication system)\n public setCustomerFromAuth(authData: {\n id?: string;\n role?: string;\n customerType?: 'individual' | 'business' | 'agent' | 'admin';\n customerSegment?: string;\n }): void {\n this.setCustomerMetadata({\n userRole: authData.role,\n customerType: authData.customerType,\n customerSegment: authData.customerSegment\n });\n }\n\n\n}\n\nexport default EventTracker;\nexport type { EventData };\n\n// Export customer metadata types for external use\nexport interface CustomerMetadata {\n customerId?: string;\n customerType?: 'individual' | 'business' | 'agent' | 'admin';\n customerSegment?: string;\n userRole?: string;\n}\n\nexport interface CustomerAnalytics {\n totalEvents: number;\n eventsByCustomer: Record<string, number>;\n eventsByType: Record<string, number>;\n eventsByPage: Record<string, number>;\n eventsByDevice: Record<string, number>;\n sessionDuration: number;\n lastActivity: number;\n}\n","// Create click effects and dispatch click event on an element\nexport const clickElement = async (element: Element, rect: DOMRect): Promise<void> => {\n return new Promise<void>((resolve) => {\n setTimeout(() => {\n const clickEffectContainer = document.createElement('div');\n clickEffectContainer.style.cssText = `\n position: fixed;\n left: ${rect.left + rect.width / 2}px;\n top: ${rect.top + rect.height / 2}px;\n width: 60px;\n height: 60px;\n z-index: 1000;\n pointer-events: none;\n transform: translate(-50%, -50%);\n `;\n document.body.appendChild(clickEffectContainer);\n \n for (let j = 0; j < 3; j++) {\n const clickRipple = document.createElement('div');\n clickRipple.style.cssText = `\n position: absolute;\n left: 50%;\n top: 50%;\n width: 40px;\n height: 40px;\n border: 2px solid rgba(0, 102, 255, ${0.7 - (j * 0.2)});\n border-radius: 50%;\n transform: translate(-50%, -50%) scale(0);\n opacity: 1;\n animation: click-ripple-animation 0.8s ease-out ${j * 0.15}s forwards;\n box-shadow: 0 0 8px rgba(0, 102, 255, 0.4);\n `;\n clickEffectContainer.appendChild(clickRipple);\n }\n \n const clickDot = document.createElement('div');\n clickDot.style.cssText = `\n position: absolute;\n left: 50%;\n top: 50%;\n width: 12px;\n height: 12px;\n background: rgba(0, 102, 255, 0.9);\n border-radius: 50%;\n transform: translate(-50%, -50%) scale(0);\n animation: click-dot-animation 0.4s ease-out forwards;\n box-shadow: 0 0 5px rgba(0, 102, 255, 0.8);\n `;\n clickEffectContainer.appendChild(clickDot);\n \n const clickEvent = new MouseEvent('click', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n\n // Check for clickable subelements (links, buttons, inputs)\n const clickableElements = element.querySelectorAll('a, button, input[type=\"button\"], input[type=\"submit\"]');\n const clickableElement = clickableElements.length > 0 ? clickableElements[0] as HTMLElement : element as HTMLElement;\n console.log('Clicking element:', clickableElement);\n clickableElement.dispatchEvent(clickEvent);\n\n const originalBoxShadow = clickableElement.style.boxShadow;\n const originalTransition = clickableElement.style.transition;\n const originalZIndex = clickableElement.style.zIndex;\n\n clickableElement.style.transition = 'all 0.3s ease-out';\n clickableElement.style.boxShadow = '0 0 0 2px rgba(0, 102, 255, 0.5), 0 0 10px rgba(0, 102, 255, 0.3)';\n clickableElement.style.zIndex = '999';\n\n setTimeout(() => {\n clickableElement.style.boxShadow = originalBoxShadow;\n clickableElement.style.transition = originalTransition;\n clickableElement.style.zIndex = originalZIndex;\n\n clickEffectContainer.remove(); \n resolve();\n }, 1000);\n\n }, 1500);\n });\n};\n\n// Highlight elements on the page with animated cursor and click effects\nexport const highlightElement = async (\n selector: string | string[],\n isHighlighting: boolean,\n setIsHighlighting: (highlighting: boolean) => void,\n logMessage: (content: string, sender: 'GUIDEAI' | 'HUMAN') => void\n): Promise<boolean> => {\n if (isHighlighting) return false;\n \n const selectors = Array.isArray(selector) ? selector : [selector];\n if (selectors.length === 0) return false;\n \n console.log('Moving cursor to elements:', selectors);\n \n let cursorElement: HTMLElement | null = null;\n try {\n setIsHighlighting(true);\n \n for (let i = 0; i < selectors.length; i++) {\n const currentSelector = selectors[i];\n \n if (i > 0) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n \n let element;\n if (currentSelector.startsWith('//')) {\n const node = document.evaluate(currentSelector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\n if (!(node instanceof Element)) {\n console.log('XPath returned a non-Element node:', currentSelector);\n console.log(node);\n logMessage('XPath returned a non-Element node: ' + currentSelector, 'GUIDEAI');\n break;\n }\n element = node as Element;\n } else {\n element = document.querySelector(currentSelector);\n }\n \n if (!element) {\n console.log('Element not found:', currentSelector);\n logMessage('Element not found: ' + currentSelector, 'GUIDEAI');\n break;\n }\n \n const rect = element.getBoundingClientRect();\n \n // Create cursor if it's the first element, or reuse existing cursor\n if (!cursorElement) {\n cursorElement = document.createElement('div');\n cursorElement.id = 'guide-ai-cursor';\n \n const blueCursorSvg = `\n <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path fill=\"#0066ff\" d=\"M7,2l12,11.2l-5.8,0.5l3.3,7.3l-2.2,1l-3.2-7.4L7,18.5V2\" />\n </svg>\n `;\n \n cursorElement.innerHTML = blueCursorSvg;\n cursorElement.style.cssText = `\n position: fixed;\n width: 64px;\n height: 64px;\n pointer-events: none;\n z-index: 9999;\n transition: all 0.8s ease-in-out;\n transform: translate(-50%, 0);\n filter: drop-shadow(0 0 4px rgba(0, 102, 255, 0.8));\n `;\n \n document.body.appendChild(cursorElement);\n \n // First element starts from center of viewport\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n cursorElement.style.left = `${viewportWidth / 2}px`;\n cursorElement.style.top = `${viewportHeight / 2}px`;\n }\n \n // Force reflow to ensure transition works\n cursorElement!.offsetHeight;\n \n await new Promise<void>(resolve => {\n setTimeout(() => {\n cursorElement!.style.left = `${rect.left + rect.width / 2}px`;\n cursorElement!.style.top = `${rect.top + rect.height / 2 - 10}px`;\n \n setTimeout(() => {\n cursorElement!.style.animation = 'cursor-jiggle 0.5s ease-in-out infinite';\n resolve();\n }, 800);\n }, 100);\n });\n \n await clickElement(element, rect);\n }\n\n setTimeout(() => {\n cursorElement?.remove();\n setIsHighlighting(false);\n }, 1000);\n \n return true;\n } catch (error) {\n console.error('Error moving cursor:', error);\n cursorElement?.remove();\n setIsHighlighting(false);\n return false;\n }\n}; ","// User metadata tracking system for GuideAI\n// Tracks user visits, logins, and other metadata for Overproof integration\n\nimport { \n UserMetadata, \n MetadataConfig, \n MetadataStorageData, \n MetadataEvent, \n MetadataEventType,\n MetadataUpdate \n} from '../types/metadata.types';\nimport { sendMetadataUpdates } from '../utils/api';\n\nconst METADATA_STORAGE_KEY = 'guideai_user_metadata';\nconst METADATA_VERSION = '1.0.0';\nconst SESSION_VISIT_KEY = 'guideai_session_visit';\n\nclass UserMetadataTracker {\n private config: Required<MetadataConfig>;\n private metadata: UserMetadata;\n private syncTimer: NodeJS.Timeout | null = null;\n private pendingUpdates: MetadataUpdate[] = [];\n private isInitialized: boolean = false;\n private onError?: (error: Error, context: string) => void;\n private lastSyncTime: number = 0;\n private minSyncInterval: number = 60000; // Minimum 1 minute between syncs\n private lastMetadataHash: string = '';\n private pendingUpdateTypes: Set<string> = new Set();\n\n constructor(organizationKey: string, config: MetadataConfig = {}, onError?: (error: Error, context: string) => void) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Constructor called', { organizationKey, config });\n }\n \n this.config = {\n trackVisits: true,\n trackLogins: true,\n syncInterval: 300000, // Increased to 5 minutes instead of 30 seconds\n storage: 'localStorage',\n customFields: [],\n collectBrowserInfo: true,\n collectUserAgent: true,\n ...config\n };\n\n this.metadata = {\n organizationKey,\n visitCount: 0,\n loginCount: 0\n };\n \n this.onError = onError;\n\n // Don't call init() immediately - let the component control initialization\n // this.init();\n \n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Constructor completed', { metadata: this.metadata });\n }\n }\n\n public init(): void {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: init() method called', { isInitialized: this.isInitialized });\n }\n \n if (this.isInitialized) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Already initialized, skipping');\n }\n return;\n }\n\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Starting initialization...');\n }\n\n // Load existing metadata from storage\n this.loadMetadata();\n\n // Collect browser info if enabled\n if (this.config.collectBrowserInfo) {\n this.collectBrowserInfo();\n }\n\n // Track initial visit if enabled and not already tracked this session\n if (this.config.trackVisits) {\n this.trackVisitOncePerSession();\n }\n\n // Start sync timer\n this.startSyncTimer();\n\n // Listen for EventTracker events to detect user interactions\n this.setupEventTrackerIntegration();\n\n this.isInitialized = true;\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Initialized successfully', this.metadata);\n }\n }\n\n public updateUserInfo(userInfo: Partial<UserMetadata>): void {\n const timestamp = Date.now();\n \n // Check if the update is actually different\n if (this.isDataDuplicate('user_info', userInfo)) {\n return;\n }\n \n // Update metadata\n this.metadata = {\n ...this.metadata,\n ...userInfo,\n lastVisit: timestamp\n };\n\n // Add to pending updates with deduplication\n this.addPendingUpdate({\n type: 'user_info',\n timestamp,\n data: userInfo\n });\n\n // Save to storage\n this.saveMetadata();\n\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: User info updated', userInfo);\n }\n }\n\n public trackLogin(additionalInfo?: Partial<UserMetadata>): void {\n if (!this.config.trackLogins) return;\n\n const timestamp = Date.now();\n \n // Check if this is a duplicate login event (within 30 seconds)\n const lastLoginTime = this.metadata.lastLogin || 0;\n if (timestamp - lastLoginTime < 30000) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Ignoring duplicate login event');\n }\n return;\n }\n \n this.metadata = {\n ...this.metadata,\n ...additionalInfo,\n loginCount: (this.metadata.loginCount || 0) + 1,\n lastLogin: timestamp,\n lastVisit: timestamp\n };\n\n this.addPendingUpdate({\n type: 'login',\n timestamp,\n data: {\n loginCount: this.metadata.loginCount,\n lastLogin: timestamp,\n ...additionalInfo\n }\n });\n\n this.saveMetadata();\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Login tracked', this.metadata.loginCount);\n }\n }\n\n private trackVisitOncePerSession(): void {\n if (!this.config.trackVisits) return;\n\n // Check if we've already tracked a visit this session\n const sessionKey = `${SESSION_VISIT_KEY}_${this.metadata.organizationKey}`;\n const hasTrackedThisSession = typeof window !== 'undefined' && sessionStorage.getItem(sessionKey);\n \n // Check if EventTracker has created a new session\n const eventTrackerSessionId = typeof window !== 'undefined' && sessionStorage.getItem('eventTracker_sessionId');\n const lastTrackedSessionId = typeof window !== 'undefined' && sessionStorage.getItem(`${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`);\n \n // If EventTracker has a new session, reset our visit tracking\n if (eventTrackerSessionId && eventTrackerSessionId !== lastTrackedSessionId) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: New EventTracker session detected, resetting visit tracking');\n }\n if (typeof window !== 'undefined') {\n sessionStorage.removeItem(sessionKey);\n sessionStorage.setItem(`${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`, eventTrackerSessionId);\n }\n }\n \n if (hasTrackedThisSession) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Visit already tracked this session, skipping');\n }\n return;\n }\n\n const timestamp = Date.now();\n const isFirstVisit = !this.metadata.firstVisit;\n\n this.metadata = {\n ...this.metadata,\n firstVisit: this.metadata.firstVisit || timestamp,\n lastVisit: timestamp,\n visitCount: (this.metadata.visitCount || 0) + 1\n };\n\n this.addPendingUpdate({\n type: 'visit',\n timestamp,\n data: {\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit,\n visitCount: this.metadata.visitCount\n }\n });\n\n // Mark that we've tracked a visit this session\n if (typeof window !== 'undefined') {\n sessionStorage.setItem(sessionKey, timestamp.toString());\n // Store the current EventTracker session ID\n const eventTrackerSessionId = sessionStorage.getItem('eventTracker_sessionId');\n if (eventTrackerSessionId) {\n sessionStorage.setItem(`${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`, eventTrackerSessionId);\n }\n }\n\n this.saveMetadata();\n \n if (process.env.NODE_ENV === 'development') {\n console.log(`UserMetadataTracker: ${isFirstVisit ? 'First' : 'Return'} visit tracked`, {\n visitCount: this.metadata.visitCount,\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit\n });\n }\n }\n\n // Legacy method - now calls the session-based version\n public trackVisit(): void {\n this.trackVisitOncePerSession();\n }\n\n // Method to reset session visit tracking (call this when user logs in)\n public resetSessionVisitTracking(): void {\n const sessionKey = `${SESSION_VISIT_KEY}_${this.metadata.organizationKey}`;\n const sessionIdKey = `${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`;\n if (typeof window !== 'undefined') {\n sessionStorage.removeItem(sessionKey);\n sessionStorage.removeItem(sessionIdKey);\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Session visit tracking reset');\n }\n }\n }\n\n // Method to manually track a visit (useful for login events)\n public trackVisitManually(): void {\n if (!this.config.trackVisits) return;\n\n const timestamp = Date.now();\n const isFirstVisit = !this.metadata.firstVisit;\n\n this.metadata = {\n ...this.metadata,\n firstVisit: this.metadata.firstVisit || timestamp,\n lastVisit: timestamp,\n visitCount: (this.metadata.visitCount || 0) + 1\n };\n\n this.addPendingUpdate({\n type: 'visit',\n timestamp,\n data: {\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit,\n visitCount: this.metadata.visitCount\n }\n });\n\n // Mark that we've tracked a visit this session\n const sessionKey = `${SESSION_VISIT_KEY}_${this.metadata.organizationKey}`;\n const sessionIdKey = `${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`;\n if (typeof window !== 'undefined') {\n sessionStorage.setItem(sessionKey, timestamp.toString());\n // Store the current EventTracker session ID\n const eventTrackerSessionId = sessionStorage.getItem('eventTracker_sessionId');\n if (eventTrackerSessionId) {\n sessionStorage.setItem(sessionIdKey, eventTrackerSessionId);\n }\n }\n\n this.saveMetadata();\n \n if (process.env.NODE_ENV === 'development') {\n console.log(`UserMetadataTracker: Manual visit tracked`, {\n visitCount: this.metadata.visitCount,\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit\n });\n }\n }\n\n public trackCustomEvent(eventType: string, customData: Record<string, any>): void {\n const timestamp = Date.now();\n\n // Update custom fields if they're in the config\n const customFields = { ...this.metadata.customFields };\n for (const [key, value] of Object.entries(customData)) {\n if (this.config.customFields.includes(key)) {\n customFields[key] = value;\n }\n }\n\n this.metadata = {\n ...this.metadata,\n customFields,\n lastVisit: timestamp\n };\n\n this.addPendingUpdate({\n type: 'custom',\n timestamp,\n data: { customFields }\n });\n\n this.saveMetadata();\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Custom event tracked', eventType, customData);\n }\n }\n\n public getMetadata(): UserMetadata {\n return { ...this.metadata };\n }\n\n public getPendingUpdates(): MetadataUpdate[] {\n return [...this.pendingUpdates];\n }\n\n public clearPendingUpdates(): void {\n this.pendingUpdates = [];\n this.pendingUpdateTypes.clear();\n }\n\n public syncNow(): MetadataUpdate[] {\n const updates = [...this.pendingUpdates];\n this.clearPendingUpdates();\n return updates;\n }\n\n private loadMetadata(): void {\n try {\n const storageData = this.getFromStorage();\n if (storageData && storageData.metadata) {\n // Merge stored metadata with current metadata\n this.metadata = {\n ...this.metadata,\n ...storageData.metadata\n };\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Loaded metadata from storage', this.metadata);\n }\n }\n } catch (error) {\n console.warn('UserMetadataTracker: Failed to load metadata from storage', error);\n // Clear corrupted data\n this.clearStorage();\n }\n }\n\n private saveMetadata(): void {\n try {\n const storageData: MetadataStorageData = {\n metadata: this.metadata,\n lastUpdated: Date.now(),\n version: METADATA_VERSION\n };\n\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: saveMetadata called with', this.metadata);\n }\n \n this.setToStorage(storageData);\n } catch (error) {\n console.warn('UserMetadataTracker: Failed to save metadata to storage', error);\n }\n }\n\n private collectBrowserInfo(): void {\n if (typeof window === 'undefined') return;\n\n const userAgent = navigator.userAgent;\n const browserInfo = this.parseBrowserInfo(userAgent);\n\n this.metadata = {\n ...this.metadata,\n userAgent: this.config.collectUserAgent ? userAgent : undefined,\n browserInfo\n };\n \n // Save the updated metadata to localStorage\n this.saveMetadata();\n }\n\n private parseBrowserInfo(userAgent: string): UserMetadata['browserInfo'] {\n // Simple browser detection - could be enhanced with a proper library\n let name = 'Unknown';\n let version = 'Unknown';\n let platform = 'Unknown';\n\n // Detect browser\n if (userAgent.includes('Chrome') && !userAgent.includes('Edg')) {\n name = 'Chrome';\n const match = userAgent.match(/Chrome\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n } else if (userAgent.includes('Firefox')) {\n name = 'Firefox';\n const match = userAgent.match(/Firefox\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n } else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {\n name = 'Safari';\n const match = userAgent.match(/Version\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n } else if (userAgent.includes('Edg')) {\n name = 'Edge';\n const match = userAgent.match(/Edg\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n }\n\n // Detect platform\n if (userAgent.includes('Win')) platform = 'Windows';\n else if (userAgent.includes('Mac')) platform = 'macOS';\n else if (userAgent.includes('Linux')) platform = 'Linux';\n else if (userAgent.includes('Android')) platform = 'Android';\n else if (userAgent.includes('iOS')) platform = 'iOS';\n\n return { name, version, platform };\n }\n\n private startSyncTimer(): void {\n if (this.syncTimer) return;\n\n this.syncTimer = setInterval(() => {\n if (this.pendingUpdates.length > 0 && this.shouldSync()) {\n this.emitPendingUpdates();\n }\n }, this.config.syncInterval);\n }\n\n // Helper method to check if sync should occur (respects minimum interval)\n private shouldSync(): boolean {\n const now = Date.now();\n return (now - this.lastSyncTime) >= this.minSyncInterval;\n }\n\n // Helper method to add pending updates with deduplication\n private addPendingUpdate(update: MetadataUpdate): void {\n // Check if we already have a pending update of this type\n if (this.pendingUpdateTypes.has(update.type)) {\n // Replace the existing update of this type with the latest one\n this.pendingUpdates = this.pendingUpdates.filter(u => u.type !== update.type);\n }\n \n this.pendingUpdates.push(update);\n this.pendingUpdateTypes.add(update.type);\n }\n\n // Helper method to check if data is duplicate\n private isDataDuplicate(type: string, data: any): boolean {\n const currentHash = this.generateDataHash(data);\n const existingUpdate = this.pendingUpdates.find(u => u.type === type);\n \n if (existingUpdate) {\n const existingHash = this.generateDataHash(existingUpdate.data);\n return currentHash === existingHash;\n }\n \n return false;\n }\n\n // Helper method to generate a simple hash for data comparison\n private generateDataHash(data: any): string {\n return JSON.stringify(data, Object.keys(data).sort());\n }\n\n private setupEventTrackerIntegration(): void {\n if (typeof window === 'undefined') return;\n\n let lastActivityUpdate = 0;\n const activityUpdateThrottle = 60000; // Only update activity once per minute\n\n // Listen for EventTracker batches to detect user interactions\n const handleEventBatch = (event: Event) => {\n const customEvent = event as CustomEvent;\n const batchData = customEvent.detail;\n const now = Date.now();\n \n if (batchData && batchData.events) {\n // Look for login-related events (form submissions, clicks on login buttons, etc.)\n const hasLoginEvent = batchData.events.some((evt: any) => \n evt.type === 'submit' || \n (evt.type === 'click' && (\n evt.textContent?.toLowerCase().includes('login') ||\n evt.textContent?.toLowerCase().includes('sign in') ||\n evt.className?.toLowerCase().includes('login') ||\n evt.id?.toLowerCase().includes('login')\n ))\n );\n\n if (hasLoginEvent) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Detected potential login event from EventTracker');\n }\n // Note: We don't automatically track login here as it requires explicit confirmation\n // This is just for detecting potential login scenarios\n }\n\n // Track general user activity with throttling\n if (now - lastActivityUpdate > activityUpdateThrottle) {\n this.metadata = {\n ...this.metadata,\n lastVisit: now\n };\n lastActivityUpdate = now;\n }\n }\n };\n\n window.addEventListener('guideai:event_batch', handleEventBatch);\n \n // Store the listener for cleanup\n (this as any)._eventTrackerListener = handleEventBatch;\n }\n\n private stopSyncTimer(): void {\n if (this.syncTimer) {\n clearInterval(this.syncTimer);\n this.syncTimer = null;\n }\n }\n\n private async emitPendingUpdates(): Promise<void> {\n if (this.pendingUpdates.length === 0) return;\n\n const updates = [...this.pendingUpdates];\n this.lastSyncTime = Date.now();\n \n // Emit the updates (integrate with existing event system)\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Emitting metadata updates', {\n updateCount: updates.length,\n updates,\n currentMetadata: this.metadata\n });\n }\n\n // Send to backend API\n try {\n if (this.onError) {\n await sendMetadataUpdates(updates, this.metadata.organizationKey, this.onError);\n }\n \n // Clear pending updates after successful emission\n this.clearPendingUpdates();\n } catch (error) {\n console.warn('UserMetadataTracker: Failed to send metadata updates, will retry later', error);\n // Don't clear pending updates so they can be retried\n }\n }\n\n private getFromStorage(): MetadataStorageData | null {\n try {\n const key = `${METADATA_STORAGE_KEY}_${this.metadata.organizationKey}`;\n let data: string | null = null;\n\n switch (this.config.storage) {\n case 'localStorage':\n data = typeof window !== 'undefined' ? localStorage.getItem(key) : null;\n break;\n case 'sessionStorage':\n data = typeof window !== 'undefined' ? sessionStorage.getItem(key) : null;\n break;\n case 'memory':\n // For memory storage, we don't persist across sessions\n return null;\n }\n\n return data ? JSON.parse(data) : null;\n } catch (error) {\n console.warn('UserMetadataTracker: Error reading from storage', error);\n return null;\n }\n }\n\n private setToStorage(data: MetadataStorageData): void {\n try {\n const key = `${METADATA_STORAGE_KEY}_${this.metadata.organizationKey}`;\n const serialized = JSON.stringify(data);\n\n switch (this.config.storage) {\n case 'localStorage':\n if (typeof window !== 'undefined') {\n localStorage.setItem(key, serialized);\n }\n break;\n case 'sessionStorage':\n if (typeof window !== 'undefined') {\n sessionStorage.setItem(key, serialized);\n }\n break;\n case 'memory':\n // For memory storage, we don't persist\n break;\n }\n } catch (error) {\n console.warn('UserMetadataTracker: Error writing to storage', error);\n }\n }\n\n private clearStorage(): void {\n try {\n const key = `${METADATA_STORAGE_KEY}_${this.metadata.organizationKey}`;\n\n switch (this.config.storage) {\n case 'localStorage':\n if (typeof window !== 'undefined') {\n localStorage.removeItem(key);\n }\n break;\n case 'sessionStorage':\n if (typeof window !== 'undefined') {\n sessionStorage.removeItem(key);\n }\n break;\n case 'memory':\n // Nothing to clear for memory storage\n break;\n }\n } catch (error) {\n console.warn('UserMetadataTracker: Error clearing storage', error);\n }\n }\n\n public destroy(): void {\n // Emit any pending updates before destroying\n this.emitPendingUpdates();\n \n // Stop sync timer\n this.stopSyncTimer();\n \n // Remove event listener\n if (typeof window !== 'undefined' && (this as any)._eventTrackerListener) {\n window.removeEventListener('guideai:event_batch', (this as any)._eventTrackerListener);\n }\n \n // Clear state\n this.isInitialized = false;\n this.pendingUpdates = [];\n \n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Destroyed');\n }\n }\n}\n\nexport default UserMetadataTracker;\nexport type { UserMetadata, MetadataConfig, MetadataUpdate };\n","// Metric exports\nexport { default as EventTracker } from './event-listner';\nexport type { EventData } from './event-listner';\nexport { default as UserMetadataTracker } from './metadata-tracker';\nexport type { UserMetadata, MetadataConfig, MetadataUpdate } from './metadata-tracker';","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(258);\n"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE__156__","guideAIStyles","CONVERSATION_STORAGE_KEY","CONVERSATION_EXPIRY_MINUTES","MAX_STORED_MESSAGES","isLocalStorageAvailable","testKey","localStorage","setItem","removeItem","e","isConversationExpired","conversation","expiryTime","Date","now","timestamp","handleStorageError","error","operation","console","DOMException","name","loadConversationFromStorage","messages","length","slice","saveConversationToStorage","warn","clearConversationStorage","cleanupError","JSON","stringify","storedData","getItem","parsedData","parse","data","conversationId","Array","isArray","organizationKey","every","msg","content","sender","addMessageToStorage","message","some","existingMsg","Math","abs","log","substring","push","checkForStoredConversation","currentOrganizationKey","storedConversation","shouldRestore","formatConversationContext","maxMessages","recentMessages","meaningfulMessages","filter","trim","map","join","DEFAULT_PROMPT","IGNORE_MESSAGE_TYPES","GUIDE_AI_API_BASE","OPENAI_REALTIME_BASE","OPENAI_REALTIME_MODEL","GEMINI_API_KEY","GEMINI_API_ENDPOINT","createNewConversation","onError","fetch","method","headers","body","userId","date","toISOString","split","time","toTimeString","response","ok","text","catch","errorText","status","Error","json","conversationData","id","logMessage","statusText","sendMetadataUpdates","updates","batchTimestamp","updateCount","cachedHooks","window","React","useState","useEffect","useRef","useMemo","useCallback","props","position","metadataOptions","transcriptOptions","inputOptions","hooks","setStatus","isClient","setIsClient","isHighlighting","setIsHighlighting","hasInteracted","setHasInteracted","setPrompt","setIsSessionReady","isConnecting","setIsConnecting","showOnboarding","setShowOnboarding","popupPosition","setPopupPosition","conversationIdRef","componentRef","peerConnectionRef","dataChannelRef","hasCreatedConversationRef","mediaStreamRef","audioElementRef","ephemeralToken","setEphemeralToken","hasInitializedRef","isUnmountingRef","isConversationActive","setIsConversationActive","restoredMessages","setRestoredMessages","enabled","showTranscript","setShowTranscript","allMessages","setAllMessages","defaultMode","inputMode","setInputMode","textInput","setTextInput","setShowTextInput","hasAttemptedAutoStartRef","initializationAttemptedRef","eventTracker","customerId","initialUserData","organizationId","customerType","userType","EventTracker","metadataTracker","UserMetadataTracker","config","geminiFlash","prompt","contents","parts","responseJson","candidates","handleCreateNewConversation","current","handleLogMessage","prev","lastMessage","newMessage","handleHighlightElement","selector","highlightElement","getOptimalPosition","elementHeight","spacing","rect","getBoundingClientRect","calculateOptimalPosition","sendMessage","readyState","send","cleanupWebRTC","getAudioTracks","forEach","track","stop","close","srcObject","initializeWebRTC","audioStream","RTCPeerConnection","addTrack","audioEl","document","createElement","autoplay","appendChild","ontrack","kind","stream","MediaStream","dc","createDataChannel","onopen","sessionConfig","tools","type","description","parameters","properties","oneOf","items","required","tool_choice","turn_detection","create_response","interrupt_response","input_audio_transcription","model","language","session","onmessage","conversationContext","item","role","transcript","output","out","arguments","argsToUse","then","replace","parsedArgs","err","call_id","code","setTimeout","fragment","includes","createOffer","offer","setLocalDescription","localDescription","sdp","baseUrl","Authorization","sdpResponse","answerSdp","answer","setRemoteDescription","startConversationSession","navigator","mediaDevices","getUserMedia","audio","echoCancellation","noiseSuppression","autoGainControl","channelCount","sampleRate","endConversationSession","handleToggleTranscript","handleTextSubmit","responseMessage","handleTextKeyPress","event","key","shiftKey","preventDefault","handleCloseTranscript","injectStyles","styleElement","getElementById","styleContent","textContent","init","updateUserInfo","hasInitialized","initializationAttempted","hasInteractedBefore","isUnmounting","remove","stopTracking","destroy","syncInterval","syncTimer","setInterval","pendingUpdates","syncNow","onMetadataUpdate","getMetadata","clearInterval","success","transcriptBoxProps","isVisible","onClose","showToggleButton","onToggle","showTextInput","enableTextInput","onTextInputChange","onTextSubmit","onTextKeyPress","textPlaceholder","placeholder","GuideAI","metadata","trackLogin","additionalInfo","userInfo","trackCustomEvent","eventType","customData","syncMetadata","resetSessionVisitTracking","trackVisitManually","toggle","show","hide","input","toggleMode","newMode","setMode","mode","getMode","sendText","shouldUseFixedPosition","Object","keys","baseStyles","zIndex","ref","style","className","onComplete","onClick","undefined","cursor","title","css","head","componentRect","viewportHeight","innerHeight","spaceAbove","top","spaceBelow","bottom","TranscriptBox","memo","element","transcriptContainer","scrollTop","scrollHeight","shouldShowToggleButton","shouldShowTranscript","index","toLowerCase","toLocaleTimeString","hour","minute","value","onChange","target","onKeyPress","rows","disabled","prevProps","nextProps","displayName","step","setStep","options","isTracking","eventData","batchSize","batchTimeout","batchTimer","pendingEvents","currentUrl","lastEventTime","eventThrottleInterval","lastEventsByType","Map","duplicateEventBuffer","maxDuplicateBuffer","isInitialized","customerMetadata","handleClick","isSignificantClickTarget","baseEventData","tagName","getSafeValue","url","location","href","logEvent","handleFocus","isFocusable","isSignificantFocusTarget","handleChange","isFormElement","handleSubmit","formData","FormData","handlePopstate","newUrl","previousUrl","getPreviousUrl","isSignificantRouteChange","handleHashChange","oldURL","String","loadEventsFromStorage","addEventListener","startTracking","storageKey","storedEvents","parsedEvents","saveEventsToStorage","sanitizedEventData","sanitizeEventData","setCustomerMetadata","getCustomerMetadata","clearCustomerMetadata","initialize","enrichEventData","sanitizedBaseData","customerSegment","sessionId","getSessionId","deviceType","getDeviceType","userAgent","screenResolution","screen","width","height","currentPage","pathname","userRole","timezone","Intl","DateTimeFormat","resolvedOptions","timeZone","locale","sanitized","val","sessionStorage","random","toString","substr","test","trackClickEvents","trackFocusEvents","trackChangeEvents","trackSubmitEvents","trackRouteChanges","emitBatch","removeEventListener","interceptHistoryAPI","originalPushState","history","pushState","originalReplaceState","replaceState","result","apply","args","inputElement","selectElement","selectedIndex","inputType","matches","generateEventKey","shouldThrottleEvent","currentTime","get","isDuplicateEvent","eventKey","existingEvent","getAttribute","hasAttribute","from","attributes","attr","startsWith","getComputedStyle","tabindex","parseInt","newUrlObj","URL","prevUrlObj","hash","enrichedEventData","set","size","delete","startBatchTimer","clearTimeout","batchPayload","events","dispatchEvent","CustomEvent","detail","getEventData","clearEventData","getEventDataByType","getEventDataByElement","toUpperCase","getEventDataByUrl","emitPendingEvents","setBatchConfig","getPendingEventsCount","getCustomerAnalytics","sessionDuration","getSessionStartTime","analytics","totalEvents","eventsByCustomer","eventsByType","eventsByPage","eventsByDevice","lastActivity","sessionStart","startTime","identifyCustomerFromContext","userRoleMeta","querySelector","setCustomerFromAuth","authData","clickElement","Promise","resolve","clickEffectContainer","cssText","left","j","clickRipple","clickDot","clickEvent","MouseEvent","view","bubbles","cancelable","clickableElements","querySelectorAll","clickableElement","originalBoxShadow","boxShadow","originalTransition","transition","originalZIndex","selectors","cursorElement","i","currentSelector","node","evaluate","XPathResult","FIRST_ORDERED_NODE_TYPE","singleNodeValue","Element","innerHTML","viewportWidth","innerWidth","offsetHeight","animation","METADATA_STORAGE_KEY","SESSION_VISIT_KEY","lastSyncTime","minSyncInterval","lastMetadataHash","pendingUpdateTypes","Set","trackVisits","trackLogins","storage","customFields","collectBrowserInfo","collectUserAgent","visitCount","loginCount","loadMetadata","trackVisitOncePerSession","startSyncTimer","setupEventTrackerIntegration","isDataDuplicate","lastVisit","addPendingUpdate","saveMetadata","lastLogin","sessionKey","hasTrackedThisSession","eventTrackerSessionId","lastTrackedSessionId","firstVisit","trackVisit","sessionIdKey","entries","getPendingUpdates","clearPendingUpdates","clear","storageData","getFromStorage","clearStorage","lastUpdated","version","setToStorage","browserInfo","parseBrowserInfo","platform","match","shouldSync","emitPendingUpdates","update","has","u","add","currentHash","generateDataHash","existingUpdate","find","sort","lastActivityUpdate","handleEventBatch","batchData","evt","_eventTrackerListener","stopSyncTimer","serialized","default","__webpack_module_cache__","__webpack_exports__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","call"],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"GuideAI.js","mappings":"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","sources":["webpack://GuideAI/webpack/universalModuleDefinition","webpack://GuideAI/./src/styles/GuideAI.styles.ts","webpack://GuideAI/./src/utils/hoverAndClick.ts","webpack://GuideAI/./src/utils/messageStorage.ts","webpack://GuideAI/./src/utils/constants.ts","webpack://GuideAI/./src/utils/api.ts","webpack://GuideAI/./src/GuideAI.tsx","webpack://GuideAI/./src/utils/workflow.ts","webpack://GuideAI/./src/utils/ui.ts","webpack://GuideAI/./src/utils/highlightAndClick.ts","webpack://GuideAI/./src/components/TranscriptBox.tsx","webpack://GuideAI/./src/utils/logger.ts","webpack://GuideAI/./src/components/Onboarding.tsx","webpack://GuideAI/./src/components/WelcomeBubble.tsx","webpack://GuideAI/./src/metric/event-listner.tsx","webpack://GuideAI/./src/metric/metadata-tracker.tsx","webpack://GuideAI/./src/metric/index.tsx","webpack://GuideAI/webpack/bootstrap","webpack://GuideAI/webpack/startup"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"GuideAI\"] = factory();\n\telse\n\t\troot[\"GuideAI\"] = factory();\n})(this, () => {\nreturn ","export const guideAIStyles = `\n.guideai-main-ui {\n position: relative;\n}\n\n.guideai-main-controls {\n display: flex;\n align-items: center;\n gap: 12px;\n position: relative;\n}\n\n.guideai-welcome-bubble {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n background: #0066ff;\n color: white;\n padding: 10px 16px;\n border-radius: 20px;\n font-size: 14px;\n white-space: normal;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n animation: bubble-pulse 2s infinite;\n max-width: 280px;\n min-width: 200px;\n text-align: center;\n line-height: 1.3;\n}\n\n.guideai-welcome-bubble.above {\n bottom: calc(100% + 10px);\n}\n\n.guideai-welcome-bubble.below {\n top: calc(100% + 10px);\n}\n\n.guideai-welcome-bubble.above::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-top: 8px solid #0066ff;\n}\n\n.guideai-welcome-bubble.below::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid #0066ff;\n}\n\n@keyframes bubble-pulse {\n 0% { transform: translateX(-50%) scale(1); }\n 50% { transform: translateX(-50%) scale(1.05); }\n 100% { transform: translateX(-50%) scale(1); }\n}\n\n.guideai-icon-wrapper {\n width: 50px;\n height: 50px;\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: rgba(255, 255, 255, 0.9);\n border: 2px solid transparent;\n border-radius: 50%;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.guideai-icon-wrapper:not(.initializing):hover {\n transform: scale(1.1);\n}\n\n.guideai-icon-wrapper.initializing {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(128, 128, 128, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: guideai-spin 1.5s linear infinite;\n opacity: 0.7;\n cursor: default;\n}\n\n.guideai-icon-wrapper.recording {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: guideai-pulse 1s infinite alternate;\n}\n\n.guideai-icon-wrapper.processing {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(255, 165, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: guideai-spin 1s linear infinite;\n}\n\n.guideai-icon-wrapper.playing {\n background-color: rgba(255, 255, 255, 0.9);\n box-shadow: 0 0 0 2px rgba(0, 128, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);\n}\n\n@keyframes guideai-pulse {\n 0% { transform: scale(1); }\n 100% { transform: scale(1.05); }\n}\n\n@keyframes guideai-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n@keyframes click-ripple-animation {\n 0% { transform: translate(-50%, -50%) scale(0); opacity: 1; }\n 50% { transform: translate(-50%, -50%) scale(1.2); opacity: 0.6; }\n 100% { transform: translate(-50%, -50%) scale(2.5); opacity: 0; }\n}\n\n@keyframes click-dot-animation {\n 0% { transform: translate(-50%, -50%) scale(0); opacity: 1; }\n 60% { transform: translate(-50%, -50%) scale(1); opacity: 1; }\n 100% { transform: translate(-50%, -50%) scale(1); opacity: 0; }\n}\n\n@keyframes cursor-jiggle {\n 0% { transform: translate(-50%, 0) scale(1); }\n 25% { transform: translate(-50%, -5px) scale(1.1); }\n 50% { transform: translate(-50%, 0) scale(1); }\n 75% { transform: translate(-50%, 5px) scale(1.1); }\n 100% { transform: translate(-50%, 0) scale(1); filter: drop-shadow(0 0 8px #0066ff); }\n}\n\n.guideai-icon {\n width: 60%;\n height: 60%;\n min-width: 16px;\n min-height: 16px;\n max-width: 40px;\n max-height: 40px;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center;\n}\n\n.guideai-icon.initializing {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"%23808080\" d=\"M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z\"/></svg>');\n}\n\n.guideai-icon.microphone {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"%230000FF\" d=\"M176 352c53.02 0 96-42.98 96-96V96c0-53.02-42.98-96-96-96S80 42.98 80 96v160c0 53.02 42.98 96 96 96zm160-160h-16c-8.84 0-16 7.16-16 16v48c0 74.8-64.49 134.82-140.79 127.38C96.71 376.89 48 317.11 48 250.3V208c0-8.84-7.16-16-16-16H16c-8.84 0-16 7.16-16 16v40.16c0 89.64 63.97 169.55 152 181.69V464H96c-8.84 0-16 7.16-16 16v16c0 8.84 7.16 16 16 16h160c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16h-56v-33.77C285.71 418.47 352 344.9 352 256v-48c0-8.84-7.16-16-16-16z\"/></svg>');\n}\n\n.guideai-icon.recording {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><circle cx=\"256\" cy=\"256\" r=\"128\" fill=\"%23FF0000\"/><circle cx=\"256\" cy=\"256\" r=\"200\" stroke=\"%23FF0000\" stroke-width=\"20\" fill=\"none\"/></svg>');\n}\n\n.guideai-icon.processing {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"%23FFA500\" d=\"M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z\"/></svg>');\n}\n\n.guideai-icon.playing {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"%23008000\" d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"/></svg>');\n}\n\n.guideai-icon.text-mode {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"%230066ff\" d=\"M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4l4 4 4-4h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM7 10v2h2v-2H7zm6 2h-2v-2h2v2zm4 0h-2v-2h2v2z\"/></svg>');\n}\n\n.guideai-icon.voice-mode {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 352 512\"><path fill=\"%23008000\" d=\"M176 352c53.02 0 96-42.98 96-96V96c0-53.02-42.98-96-96-96S80 42.98 80 96v160c0 53.02 42.98 96 96 96zm160-160h-16c-8.84 0-16 7.16-16 16v48c0 74.8-64.49 134.82-140.79 127.38C96.71 376.89 48 317.11 48 250.3V208c0-8.84-7.16-16-16-16H16c-8.84 0-16 7.16-16 16v40.16c0 89.64 63.97 169.55 152 181.69V464H96c-8.84 0-16 7.16-16 16v16c0 8.84 7.16 16 16 16h160c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16h-56v-33.77C285.71 418.47 352 344.9 352 256v-48c0-8.84-7.16-16-16-16z\"/></svg>');\n}\n\n\n/* Onboarding styles */\n.guideai-onboarding {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n background: white;\n border-radius: 12px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n width: 300px;\n max-width: 90vw;\n z-index: 1002;\n animation: onboarding-fade-in 0.3s ease-out;\n}\n\n.guideai-onboarding.above {\n bottom: calc(100% + 15px);\n}\n\n.guideai-onboarding.below {\n top: calc(100% + 15px);\n}\n\n.guideai-onboarding.above::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-top: 8px solid white;\n filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));\n}\n\n.guideai-onboarding.below::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-left: 8px solid transparent;\n border-right: 8px solid transparent;\n border-bottom: 8px solid white;\n filter: drop-shadow(0 -2px 4px rgba(0, 0, 0, 0.1));\n}\n\n@keyframes onboarding-fade-in {\n from { opacity: 0; transform: translateX(-50%) translateY(-10px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n\n.guideai-onboarding-content {\n padding: 16px;\n position: relative;\n}\n\n.guideai-onboarding-close {\n position: absolute;\n top: 10px;\n right: 10px;\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n color: #999;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n}\n\n.guideai-onboarding-close:hover {\n background: #f5f5f5;\n color: #333;\n}\n\n.guideai-onboarding-step {\n text-align: center;\n margin-bottom: 12px;\n}\n\n.guideai-onboarding-icon {\n font-size: 32px;\n margin-bottom: 8px;\n display: inline-block;\n}\n\n.guideai-onboarding-step h3 {\n margin: 0 0 8px;\n font-size: 16px;\n color: #333;\n font-weight: 600;\n}\n\n.guideai-onboarding-step p {\n margin: 0;\n font-size: 13px;\n color: #666;\n line-height: 1.4;\n}\n\n.guideai-onboarding-dots {\n display: flex;\n justify-content: center;\n margin: 12px 0;\n}\n\n.guideai-onboarding-dots .dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #ddd;\n margin: 0 4px;\n transition: all 0.3s ease;\n}\n\n.guideai-onboarding-dots .dot.active {\n background: #0066ff;\n transform: scale(1.2);\n}\n\n.guideai-onboarding-next {\n display: block;\n width: 100%;\n padding: 10px;\n background: #0066ff;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s ease;\n}\n\n.guideai-onboarding-next:hover {\n background: #0055cc;\n}\n\n/* Transcript Box Styles */\n.guideai-transcript-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: transparent;\n display: flex;\n justify-content: flex-end;\n align-items: flex-end;\n padding-bottom: 40px;\n padding-right: 40px;\n z-index: 10000;\n animation: transcript-fade-in 0.3s ease-out;\n pointer-events: none;\n}\n\n.guideai-transcript-box {\n background: rgba(40, 44, 52, 0.85);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 16px;\n box-shadow: \n 0 8px 32px rgba(0, 0, 0, 0.3),\n inset 0 1px 0 rgba(255, 255, 255, 0.2),\n inset 0 -1px 0 rgba(0, 0, 0, 0.1);\n width: 280px;\n max-height: 280px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n animation: transcript-slide-up 0.4s ease-out;\n pointer-events: auto;\n margin-right: 60px;\n position: relative;\n}\n\n.guideai-transcript-box::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);\n border-radius: 16px 16px 0 0;\n}\n\n\n\n.guideai-transcript-messages {\n flex: 1;\n overflow-y: auto;\n padding: 8px 12px;\n max-height: 200px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar {\n width: 6px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar-track {\n background: rgba(0, 0, 0, 0.05);\n border-radius: 3px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n}\n\n.guideai-transcript-messages::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.3);\n}\n\n.guideai-transcript-empty {\n text-align: center;\n padding: 20px 12px;\n color: rgba(255, 255, 255, 0.6);\n}\n\n.guideai-transcript-empty-icon {\n font-size: 24px;\n margin-bottom: 8px;\n opacity: 0.5;\n}\n\n.guideai-transcript-empty p {\n margin: 0;\n font-size: 11px;\n color: rgba(255, 255, 255, 0.6);\n}\n\n.guideai-transcript-message {\n margin-bottom: 8px;\n animation: message-fade-in 0.3s ease-out;\n}\n\n.guideai-transcript-message.human {\n text-align: right;\n}\n\n.guideai-transcript-message.guideai {\n text-align: left;\n}\n\n.guideai-transcript-message-content {\n display: inline-block;\n max-width: 85%;\n padding: 6px 10px;\n border-radius: 12px;\n position: relative;\n}\n\n.guideai-transcript-message.human .guideai-transcript-message-content {\n background: rgba(255, 255, 255, 0.15);\n color: rgba(255, 255, 255, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-bottom-right-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n\n.guideai-transcript-message.guideai .guideai-transcript-message-content {\n background: rgba(0, 0, 0, 0.1);\n color: rgba(255, 255, 255, 0.9);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-bottom-left-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n\n.guideai-transcript-message-sender {\n font-size: 9px;\n font-weight: 600;\n margin-bottom: 2px;\n opacity: 0.8;\n}\n\n.guideai-transcript-message-text {\n font-size: 11px;\n line-height: 1.3;\n word-wrap: break-word;\n}\n\n.guideai-transcript-message-time {\n font-size: 8px;\n opacity: 0.6;\n margin-top: 2px;\n}\n\n\n\n@keyframes transcript-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes transcript-slide-up {\n from { \n opacity: 0; \n transform: translateX(40px) scale(0.95); \n }\n to { \n opacity: 1; \n transform: translateX(0) scale(1); \n }\n}\n\n@keyframes message-fade-in {\n from { \n opacity: 0; \n transform: translateY(10px); \n }\n to { \n opacity: 1; \n transform: translateY(0); \n }\n}\n\n/* Transcript Toggle Button Styles - positioned at top of transcript area */\n.guideai-transcript-toggle-button {\n position: fixed;\n bottom: 60px;\n right: 40px;\n width: 36px;\n height: 36px;\n background: rgba(40, 44, 52, 0.9);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);\n transition: all 0.2s ease;\n z-index: 10001;\n font-size: 14px;\n padding: 0;\n color: rgba(255, 255, 255, 0.9);\n animation: toggle-fade-in 0.3s ease-out;\n}\n\n.guideai-transcript-toggle-button:hover {\n background: rgba(40, 44, 52, 1);\n transform: scale(1.1);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);\n}\n\n.guideai-transcript-toggle-button:active {\n transform: scale(0.95);\n}\n\n.guideai-transcript-toggle-icon {\n display: inline-block;\n transition: all 0.2s ease;\n filter: none;\n}\n\n/* Position transcript toggle button based on transcript position */\n.guideai-transcript-overlay {\n pointer-events: none;\n}\n\n.guideai-transcript-toggle-button {\n pointer-events: auto;\n}\n\n/* Input Options Container - Two Stage Layout */\n.guideai-input-options {\n display: flex;\n align-items: center;\n gap: 12px;\n position: relative;\n animation: options-slide-in 0.4s ease-out;\n}\n\n@keyframes options-slide-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n/* Individual Input Option Button */\n.guideai-input-option {\n width: 50px;\n height: 50px;\n background: rgba(255, 255, 255, 0.9);\n border: 2px solid rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n transition: all 0.3s ease;\n font-size: 18px;\n padding: 0;\n position: relative;\n}\n\n.guideai-input-option:hover:not(:disabled) {\n background: rgba(255, 255, 255, 1);\n transform: scale(1.1);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);\n}\n\n.guideai-input-option:active:not(:disabled) {\n transform: scale(0.95);\n}\n\n.guideai-input-option:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n\n/* Voice Option Specific Styling */\n.guideai-input-option.voice-option {\n background: linear-gradient(135deg, rgba(76, 175, 80, 0.1), rgba(139, 195, 74, 0.1));\n border-color: rgba(76, 175, 80, 0.3);\n}\n\n.guideai-input-option.voice-option:hover:not(:disabled) {\n background: linear-gradient(135deg, rgba(76, 175, 80, 0.2), rgba(139, 195, 74, 0.2));\n border-color: rgba(76, 175, 80, 0.5);\n}\n\n/* Text Option Specific Styling */\n.guideai-input-option.text-option {\n background: linear-gradient(135deg, rgba(33, 150, 243, 0.1), rgba(63, 81, 181, 0.1));\n border-color: rgba(33, 150, 243, 0.3);\n}\n\n.guideai-input-option.text-option:hover:not(:disabled) {\n background: linear-gradient(135deg, rgba(33, 150, 243, 0.2), rgba(63, 81, 181, 0.2));\n border-color: rgba(33, 150, 243, 0.5);\n}\n\n.guideai-input-option-icon {\n display: inline-block;\n transition: all 0.2s ease;\n filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));\n}\n\n/* Text Input integrated into Transcript Box */\n.guideai-transcript-text-input {\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n padding: 12px;\n display: flex;\n gap: 8px;\n align-items: flex-end;\n background: rgba(0, 0, 0, 0.02);\n border-radius: 0 0 16px 16px;\n animation: text-input-fade-in 0.3s ease-out;\n}\n\n.guideai-transcript-input-field {\n flex: 1;\n background: rgba(255, 255, 255, 0.15);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n padding: 8px 12px;\n color: rgba(255, 255, 255, 0.95);\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n resize: none;\n outline: none;\n transition: all 0.2s ease;\n min-height: 32px;\n}\n\n.guideai-transcript-input-field::placeholder {\n color: rgba(255, 255, 255, 0.6);\n}\n\n.guideai-transcript-input-field:focus {\n border-color: rgba(255, 255, 255, 0.4);\n background: rgba(255, 255, 255, 0.2);\n box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);\n}\n\n.guideai-transcript-send-button {\n background: rgba(255, 255, 255, 0.15);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 6px;\n padding: 6px 10px;\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 36px;\n height: 32px;\n}\n\n.guideai-transcript-send-button:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.25);\n border-color: rgba(255, 255, 255, 0.3);\n transform: translateY(-1px);\n}\n\n.guideai-transcript-send-button:active:not(:disabled) {\n transform: translateY(0);\n}\n\n.guideai-transcript-send-button:disabled {\n background: rgba(255, 255, 255, 0.05);\n border-color: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.4);\n cursor: not-allowed;\n transform: none;\n}\n\n.guideai-transcript-send-icon {\n font-size: 11px;\n}\n\n/* Animation for text input appearance */\n@keyframes text-input-fade-in {\n from { \n opacity: 0; \n transform: translateY(10px); \n }\n to { \n opacity: 1; \n transform: translateY(0); \n }\n}\n\n@keyframes toggle-fade-in {\n from { \n opacity: 0; \n transform: scale(0.8); \n }\n to { \n opacity: 1; \n transform: scale(1); \n }\n}\n\n/* Hover effect animations */\n@keyframes hover-pulse {\n 0%, 100% {\n opacity: 0.6;\n transform: scale(1);\n box-shadow: 0 0 10px rgba(255, 165, 0, 0.6);\n }\n 25% {\n opacity: 0.9;\n transform: scale(1.03);\n box-shadow: 0 0 20px rgba(255, 165, 0, 0.8);\n }\n 50% {\n opacity: 1;\n transform: scale(1.05);\n box-shadow: 0 0 25px rgba(255, 165, 0, 1);\n }\n 75% {\n opacity: 0.9;\n transform: scale(1.03);\n box-shadow: 0 0 20px rgba(255, 165, 0, 0.8);\n }\n}\n\n@keyframes hover-indicator-fade {\n from { \n opacity: 0; \n transform: translateX(-50%) translateY(-10px); \n }\n to { \n opacity: 1; \n transform: translateX(-50%) translateY(0); \n }\n}\n\n@keyframes cursor-hover-float {\n 0%, 100% {\n transform: translate(-50%, 0) translateY(0px);\n }\n 25% {\n transform: translate(-50%, 0) translateY(-3px);\n }\n 50% {\n transform: translate(-50%, 0) translateY(-6px);\n }\n 75% {\n transform: translate(-50%, 0) translateY(-3px);\n }\n}\n\n\n`; ","// Create hover effects and dispatch hover event on an element\nexport const createHoverEffect = async (element: Element, rect: DOMRect): Promise<void> => {\n return new Promise<void>((resolve) => {\n setTimeout(() => {\n // Get fresh rect in case element has expanded since initial measurement\n const currentRect = element.getBoundingClientRect();\n const hoverEffectContainer = document.createElement('div');\n hoverEffectContainer.style.cssText = `\n position: fixed;\n left: ${currentRect.left}px;\n top: ${currentRect.top}px;\n width: ${currentRect.width}px;\n height: ${currentRect.height}px;\n z-index: 999;\n pointer-events: none;\n border: 2px solid rgba(255, 165, 0, 0.8);\n border-radius: 4px;\n background: rgba(255, 165, 0, 0.1);\n box-shadow: 0 0 10px rgba(255, 165, 0, 0.6);\n animation: hover-pulse 0.8s ease-in-out infinite;\n `;\n document.body.appendChild(hoverEffectContainer);\n\n // Create floating hover indicator\n const hoverIndicator = document.createElement('div');\n hoverIndicator.style.cssText = `\n position: fixed;\n left: ${currentRect.left + currentRect.width / 2}px;\n top: ${currentRect.top - 30}px;\n background: rgba(255, 165, 0, 0.9);\n color: white;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n font-family: Arial, sans-serif;\n font-weight: bold;\n z-index: 1000;\n pointer-events: none;\n transform: translateX(-50%);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: hover-indicator-fade 0.5s ease-in forwards;\n `;\n hoverIndicator.textContent = 'HOVER';\n document.body.appendChild(hoverIndicator);\n\n const mouseEnterEvent = new MouseEvent('mouseenter', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n const mouseOverEvent = new MouseEvent('mouseover', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n\n console.log('Hovering over element:', element);\n element.dispatchEvent(mouseEnterEvent);\n element.dispatchEvent(mouseOverEvent);\n\n // Temporarily apply a subtle highlight to the element itself\n const originalBoxShadow = (element as HTMLElement).style.boxShadow;\n const originalTransition = (element as HTMLElement).style.transition;\n const originalBackground = (element as HTMLElement).style.background;\n const originalZIndex = (element as HTMLElement).style.zIndex;\n\n (element as HTMLElement).style.transition = 'all 0.3s ease-out';\n (element as HTMLElement).style.boxShadow = '0 0 0 2px rgba(255, 165, 0, 0.6), 0 0 15px rgba(255, 165, 0, 0.4)';\n (element as HTMLElement).style.background = (element as HTMLElement).style.background + ', rgba(255, 165, 0, 0.05)';\n (element as HTMLElement).style.zIndex = '998'; // Ensure it's above other content but below cursor\n\n setTimeout(() => {\n (element as HTMLElement).style.boxShadow = originalBoxShadow;\n (element as HTMLElement).style.transition = originalTransition;\n (element as HTMLElement).style.background = originalBackground;\n (element as HTMLElement).style.zIndex = originalZIndex;\n\n hoverEffectContainer.remove();\n hoverIndicator.remove();\n resolve();\n }, 3000); // Keep hover effect for 3 seconds\n }, 250); // Small delay before applying hover effects\n });\n};\n\n// Create hover effects with position tracking for dynamic elements\nexport const createHoverEffectWithTracking = async (element: Element, initialRect: DOMRect, cursorElement: HTMLElement | null): Promise<void> => {\n return new Promise<void>((resolve) => {\n setTimeout(() => {\n // Get fresh rect in case element has expanded since initial measurement\n let currentRect = element.getBoundingClientRect();\n const hoverEffectContainer = document.createElement('div');\n hoverEffectContainer.style.cssText = `\n position: fixed;\n left: ${currentRect.left}px;\n top: ${currentRect.top}px;\n width: ${currentRect.width}px;\n height: ${currentRect.height}px;\n z-index: 999;\n pointer-events: none;\n border: 2px solid rgba(255, 165, 0, 0.8);\n border-radius: 4px;\n background: rgba(255, 165, 0, 0.1);\n box-shadow: 0 0 10px rgba(255, 165, 0, 0.6);\n animation: hover-pulse 0.8s ease-in-out infinite;\n transition: all 0.1s ease-out;\n `;\n document.body.appendChild(hoverEffectContainer);\n\n // Create floating hover indicator\n const hoverIndicator = document.createElement('div');\n hoverIndicator.style.cssText = `\n position: fixed;\n left: ${currentRect.left + currentRect.width / 2}px;\n top: ${currentRect.top - 30}px;\n background: rgba(255, 165, 0, 0.9);\n color: white;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n font-family: Arial, sans-serif;\n font-weight: bold;\n z-index: 1000;\n pointer-events: none;\n transform: translateX(-50%);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: hover-indicator-fade 0.5s ease-in forwards;\n transition: all 0.1s ease-out;\n `;\n hoverIndicator.textContent = 'HOVER';\n document.body.appendChild(hoverIndicator);\n\n const mouseEnterEvent = new MouseEvent('mouseenter', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n const mouseOverEvent = new MouseEvent('mouseover', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n\n console.log('Hovering over element with position tracking:', element);\n element.dispatchEvent(mouseEnterEvent);\n element.dispatchEvent(mouseOverEvent);\n\n // Temporarily apply a subtle highlight to the element itself\n const originalBoxShadow = (element as HTMLElement).style.boxShadow;\n const originalTransition = (element as HTMLElement).style.transition;\n const originalBackground = (element as HTMLElement).style.background;\n const originalZIndex = (element as HTMLElement).style.zIndex;\n\n (element as HTMLElement).style.transition = 'all 0.3s ease-out';\n (element as HTMLElement).style.boxShadow = '0 0 0 2px rgba(255, 165, 0, 0.6), 0 0 15px rgba(255, 165, 0, 0.4)';\n (element as HTMLElement).style.background = (element as HTMLElement).style.background + ', rgba(255, 165, 0, 0.05)';\n (element as HTMLElement).style.zIndex = '998'; // Ensure it's above other content but below cursor\n\n // Position tracking interval\n const trackingInterval = setInterval(() => {\n const newRect = element.getBoundingClientRect();\n \n // Check if position or size changed significantly (more than 2px to avoid tiny fluctuations)\n if (Math.abs(newRect.left - currentRect.left) > 2 || \n Math.abs(newRect.top - currentRect.top) > 2 ||\n Math.abs(newRect.width - currentRect.width) > 2 ||\n Math.abs(newRect.height - currentRect.height) > 2) {\n \n console.log('Element position changed, updating cursor and hover effects');\n currentRect = newRect;\n \n // Update hover effect container position\n hoverEffectContainer.style.left = `${currentRect.left}px`;\n hoverEffectContainer.style.top = `${currentRect.top}px`;\n hoverEffectContainer.style.width = `${currentRect.width}px`;\n hoverEffectContainer.style.height = `${currentRect.height}px`;\n \n // Update hover indicator position\n hoverIndicator.style.left = `${currentRect.left + currentRect.width / 2}px`;\n hoverIndicator.style.top = `${currentRect.top - 30}px`;\n \n // Update cursor position if it exists\n if (cursorElement) {\n cursorElement.style.left = `${currentRect.left + currentRect.width / 2}px`;\n cursorElement.style.top = `${currentRect.top + currentRect.height / 2 - 10}px`;\n }\n }\n }, 50); // Check every 50ms for position changes\n\n setTimeout(() => {\n clearInterval(trackingInterval);\n \n (element as HTMLElement).style.boxShadow = originalBoxShadow;\n (element as HTMLElement).style.transition = originalTransition;\n (element as HTMLElement).style.background = originalBackground;\n (element as HTMLElement).style.zIndex = originalZIndex;\n\n hoverEffectContainer.remove();\n hoverIndicator.remove();\n resolve();\n }, 3000); // Keep hover effect for 3 seconds\n }, 250); // Small delay before applying hover effects\n });\n};\n\n// Create click effects and dispatch click event on an element\nexport const clickElement = async (element: Element, rect: DOMRect): Promise<void> => {\n return new Promise<void>((resolve) => {\n setTimeout(() => {\n const clickEffectContainer = document.createElement('div');\n clickEffectContainer.style.cssText = `\n position: fixed;\n left: ${rect.left + rect.width / 2}px;\n top: ${rect.top + rect.height / 2}px;\n width: 60px;\n height: 60px;\n z-index: 1000;\n pointer-events: none;\n transform: translate(-50%, -50%);\n `;\n document.body.appendChild(clickEffectContainer);\n \n for (let j = 0; j < 3; j++) {\n const clickRipple = document.createElement('div');\n clickRipple.style.cssText = `\n position: absolute;\n left: 50%;\n top: 50%;\n width: 40px;\n height: 40px;\n border: 2px solid rgba(0, 102, 255, ${0.7 - (j * 0.2)});\n border-radius: 50%;\n transform: translate(-50%, -50%) scale(0);\n opacity: 1;\n animation: click-ripple-animation 0.8s ease-out ${j * 0.15}s forwards;\n box-shadow: 0 0 8px rgba(0, 102, 255, 0.4);\n `;\n clickEffectContainer.appendChild(clickRipple);\n }\n \n const clickDot = document.createElement('div');\n clickDot.style.cssText = `\n position: absolute;\n left: 50%;\n top: 50%;\n width: 12px;\n height: 12px;\n background: rgba(0, 102, 255, 0.9);\n border-radius: 50%;\n transform: translate(-50%, -50%) scale(0);\n animation: click-dot-animation 0.4s ease-out forwards;\n box-shadow: 0 0 5px rgba(0, 102, 255, 0.8);\n `;\n clickEffectContainer.appendChild(clickDot);\n \n const clickEvent = new MouseEvent('click', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n\n // Check for clickable subelements (links, buttons, inputs)\n const clickableElements = element.querySelectorAll('a, button, input[type=\"button\"], input[type=\"submit\"]');\n const clickableElement = clickableElements.length > 0 ? clickableElements[0] as HTMLElement : element as HTMLElement;\n console.log('Clicking element:', clickableElement);\n clickableElement.dispatchEvent(clickEvent);\n\n const originalBoxShadow = clickableElement.style.boxShadow;\n const originalTransition = clickableElement.style.transition;\n const originalZIndex = clickableElement.style.zIndex;\n\n clickableElement.style.transition = 'all 0.3s ease-out';\n clickableElement.style.boxShadow = '0 0 0 2px rgba(0, 102, 255, 0.5), 0 0 10px rgba(0, 102, 255, 0.3)';\n clickableElement.style.zIndex = '999';\n\n setTimeout(() => {\n clickableElement.style.boxShadow = originalBoxShadow;\n clickableElement.style.transition = originalTransition;\n clickableElement.style.zIndex = originalZIndex;\n\n clickEffectContainer.remove(); \n resolve();\n }, 1000);\n\n }, 1500);\n });\n};\n\n// Parse custom selector format: \"text:cssSelector:textContent\"\nconst parseCustomSelector = (selector: string): { type: 'text' | 'css' | 'xpath', cssSelector?: string, textContent?: string, originalSelector?: string } => {\n // Check if it's a text selector format: \"text:cssSelector:textContent\"\n if (selector.startsWith('text:')) {\n const parts = selector.split(':');\n if (parts.length >= 3) {\n const cssSelector = parts[1];\n const textContent = parts.slice(2).join(':'); // Rejoin in case text contains colons\n return { type: 'text', cssSelector, textContent };\n }\n }\n \n // Check if it's XPath\n if (selector.startsWith('//')) {\n return { type: 'xpath', originalSelector: selector };\n }\n \n // Default to CSS selector\n return { type: 'css', originalSelector: selector };\n};\n\n// Find element using custom selector logic\nconst findElementBySelector = (selector: string): Element | null => {\n const parsed = parseCustomSelector(selector);\n \n switch (parsed.type) {\n case 'text':\n if (!parsed.cssSelector || !parsed.textContent) return null;\n \n // Find all elements matching the CSS selector\n const elements = document.querySelectorAll(parsed.cssSelector);\n \n // Filter by text content\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n const textContent = element.textContent?.trim();\n if (textContent === parsed.textContent.trim()) {\n return element;\n }\n }\n \n // If exact match not found, try finding parent elements that contain the text\n // This handles cases where the text is in a child element\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n const parent = element.closest('button, [role=\"button\"], a, [onclick]') || element.parentElement;\n if (parent && parent.textContent?.trim().includes(parsed.textContent.trim())) {\n return parent;\n }\n }\n \n return null;\n \n case 'xpath':\n if (!parsed.originalSelector) return null;\n const node = document.evaluate(parsed.originalSelector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\n return node instanceof Element ? node : null;\n \n case 'css':\n default:\n if (!parsed.originalSelector) return null;\n return document.querySelector(parsed.originalSelector);\n }\n};\n\n// Hover then click on elements on the page with animated cursor\nexport const hoverThenClick = async (\n selector: string | string[],\n isHighlighting: boolean,\n setIsHighlighting: (highlighting: boolean) => void,\n logMessage: (content: string, sender: 'GUIDEAI' | 'HUMAN') => void\n): Promise<boolean> => {\n if (isHighlighting) return false;\n \n const selectors = Array.isArray(selector) ? selector : [selector];\n if (selectors.length === 0) return false;\n \n console.log('Moving cursor to hover over elements:', selectors);\n \n let cursorElement: HTMLElement | null = null;\n try {\n setIsHighlighting(true);\n \n for (let i = 0; i < selectors.length; i++) {\n const currentSelector = selectors[i];\n \n if (i > 0) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n \n // Use enhanced element finding logic\n const element = findElementBySelector(currentSelector);\n \n if (!element) {\n console.log('Element not found:', currentSelector);\n logMessage('Element not found: ' + currentSelector, 'GUIDEAI');\n continue; // Continue to next selector instead of breaking\n }\n \n // Scroll element into view if it's not visible\n element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' });\n \n // Wait a moment for scrolling to complete\n await new Promise(resolve => setTimeout(resolve, 300));\n \n // Get rect after scrolling\n let rect = element.getBoundingClientRect();\n \n // For collapsed elements, we might need to trigger hover first to get accurate dimensions\n const isLikelyCollapsed = rect.width < 50 || rect.height < 20;\n \n if (isLikelyCollapsed) {\n // Dispatch initial hover events to potentially expand the element\n const mouseEnterEvent = new MouseEvent('mouseenter', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n element.dispatchEvent(mouseEnterEvent);\n \n // Wait for potential expansion animation\n await new Promise(resolve => setTimeout(resolve, 200));\n \n // Get updated rect after potential expansion\n rect = element.getBoundingClientRect();\n }\n \n // Create cursor if it's the first element, or reuse existing cursor\n if (!cursorElement) {\n cursorElement = document.createElement('div');\n cursorElement.id = 'guide-ai-cursor';\n \n const blueCursorSvg = `\n <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path fill=\"#0066ff\" d=\"M7,2l12,11.2l-5.8,0.5l3.3,7.3l-2.2,1l-3.2-7.4L7,18.5V2\" />\n </svg>\n `;\n \n cursorElement.innerHTML = blueCursorSvg;\n cursorElement.style.cssText = `\n position: fixed;\n width: 64px;\n height: 64px;\n pointer-events: none;\n z-index: 9999;\n transition: all 0.8s ease-in-out;\n transform: translate(-50%, 0);\n filter: drop-shadow(0 0 4px rgba(0, 102, 255, 0.8));\n `;\n \n document.body.appendChild(cursorElement);\n \n // First element starts from center of viewport\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n cursorElement.style.left = `${viewportWidth / 2}px`;\n cursorElement.style.top = `${viewportHeight / 2}px`;\n }\n \n // Force reflow to ensure transition works\n cursorElement!.offsetHeight;\n \n await new Promise<void>(resolve => {\n setTimeout(() => {\n cursorElement!.style.left = `${rect.left + rect.width / 2}px`;\n cursorElement!.style.top = `${rect.top + rect.height / 2 - 10}px`;\n \n setTimeout(() => {\n cursorElement!.style.animation = 'cursor-hover-float 2s ease-in-out infinite';\n resolve();\n }, 800);\n }, 100);\n });\n \n // Click the element\n await clickElement(element, rect);\n }\n\n setTimeout(() => {\n cursorElement?.remove();\n setIsHighlighting(false);\n }, 1000);\n \n return true;\n } catch (error) {\n console.error('Error hovering over elements:', error);\n cursorElement?.remove();\n setIsHighlighting(false);\n return false;\n }\n}; ","// Storage data structures for conversation persistence\nexport interface StoredMessage {\n content: string;\n sender: 'GUIDEAI' | 'HUMAN';\n timestamp: number;\n}\n\nexport interface StoredConversation {\n conversationId: string;\n messages: StoredMessage[];\n timestamp: number;\n organizationKey: string;\n}\n\nexport const CONVERSATION_STORAGE_KEY = 'guideai_conversation';\nexport const CONVERSATION_EXPIRY_MINUTES = 30;\nexport const MAX_STORED_MESSAGES = 100; // Increased to keep more conversation history\n\nexport const isLocalStorageAvailable = (): boolean => {\n try {\n const testKey = '__storage_test__';\n localStorage.setItem(testKey, testKey);\n localStorage.removeItem(testKey);\n return true;\n } catch (e) {\n return false;\n }\n};\n\nconst isValidConversation = (data: any): data is StoredConversation => {\n return (\n data &&\n typeof data === 'object' &&\n typeof data.conversationId === 'string' &&\n Array.isArray(data.messages) &&\n typeof data.timestamp === 'number' &&\n typeof data.organizationKey === 'string' &&\n data.messages.every((msg: any) => (\n typeof msg === 'object' &&\n typeof msg.content === 'string' &&\n (msg.sender === 'HUMAN' || msg.sender === 'GUIDEAI') &&\n typeof msg.timestamp === 'number'\n ))\n );\n};\n\nexport const isConversationExpired = (conversation: StoredConversation): boolean => {\n const expiryTime = CONVERSATION_EXPIRY_MINUTES * 60 * 1000; // convert to milliseconds\n const now = Date.now();\n return (now - conversation.timestamp) > expiryTime;\n};\n\nconst handleStorageError = (error: unknown, operation: string): void => {\n console.error(`Storage error during ${operation}:`, error);\n \n // Check if it's a quota exceeded error\n if (error instanceof DOMException && \n (error.name === 'QuotaExceededError' || \n error.name === 'NS_ERROR_DOM_QUOTA_REACHED')) {\n \n // Try to free up space by removing oldest messages instead of clearing everything\n try {\n const conversation = loadConversationFromStorage();\n if (conversation && conversation.messages.length > 5) {\n // Remove oldest 5 messages\n conversation.messages = conversation.messages.slice(5);\n saveConversationToStorage(conversation);\n console.warn('Storage quota exceeded. Removed oldest 5 messages to free space.');\n } else if (conversation) {\n // If less than 5 messages, clear everything\n clearConversationStorage();\n console.warn('Storage quota exceeded. Cleared conversation data.');\n }\n } catch (cleanupError) {\n console.error('Failed to clean up storage:', cleanupError);\n }\n }\n};\n\nexport const saveConversationToStorage = (conversation: StoredConversation): void => {\n if (!isLocalStorageAvailable()) {\n console.warn('localStorage is not available. Conversation persistence disabled.');\n return;\n }\n \n try {\n // Limit to last MAX_STORED_MESSAGES\n if (conversation.messages.length > MAX_STORED_MESSAGES) {\n conversation.messages = conversation.messages.slice(-MAX_STORED_MESSAGES);\n }\n \n // Update timestamp\n conversation.timestamp = Date.now();\n \n localStorage.setItem(CONVERSATION_STORAGE_KEY, JSON.stringify(conversation));\n } catch (error) {\n handleStorageError(error, 'save');\n }\n};\n\nexport const loadConversationFromStorage = (): StoredConversation | null => {\n if (!isLocalStorageAvailable()) {\n console.warn('localStorage is not available. Conversation persistence disabled.');\n return null;\n }\n \n try {\n const storedData = localStorage.getItem(CONVERSATION_STORAGE_KEY);\n if (!storedData) return null;\n \n const parsedData = JSON.parse(storedData);\n \n // Validate the data structure\n if (!isValidConversation(parsedData)) {\n console.warn('Corrupted conversation data found. Clearing storage.');\n clearConversationStorage();\n return null;\n }\n \n return parsedData;\n } catch (error) {\n handleStorageError(error, 'load');\n // Clear corrupted data on any parsing errors\n clearConversationStorage();\n return null;\n }\n};\n\nexport const clearConversationStorage = (): void => {\n if (!isLocalStorageAvailable()) {\n return;\n }\n \n try {\n localStorage.removeItem(CONVERSATION_STORAGE_KEY);\n } catch (error) {\n handleStorageError(error, 'clear');\n }\n};\n\nexport const addMessageToStorage = (message: StoredMessage, conversationId: string, organizationKey: string): void => {\n if (!isLocalStorageAvailable()) {\n console.warn('localStorage is not available. Message persistence disabled.');\n return;\n }\n \n try {\n let conversation = loadConversationFromStorage();\n \n if (!conversation) {\n conversation = {\n conversationId,\n messages: [],\n timestamp: Date.now(),\n organizationKey\n };\n }\n \n // Check for duplicate messages to prevent adding the same message twice\n const isDuplicate = conversation.messages.some(existingMsg => \n existingMsg.content === message.content && \n existingMsg.sender === message.sender &&\n Math.abs(existingMsg.timestamp - message.timestamp) < 5000 // Within 5 seconds\n );\n \n if (!isDuplicate) {\n conversation.messages.push(message);\n conversation.conversationId = conversationId;\n conversation.organizationKey = organizationKey;\n \n saveConversationToStorage(conversation);\n } else {\n console.log('Duplicate message detected, skipping:', message.content.substring(0, 50));\n }\n } catch (error) {\n console.error('Error adding message to storage:', error);\n }\n};\n\nexport const checkForStoredConversation = (currentOrganizationKey: string): {\n shouldRestore: boolean;\n conversationId: string | null;\n messages: StoredMessage[] | null;\n} => {\n const storedConversation = loadConversationFromStorage();\n \n if (!storedConversation) {\n return { shouldRestore: false, conversationId: null, messages: null };\n }\n \n // Check if conversation is expired - if so, clear it automatically\n if (isConversationExpired(storedConversation)) {\n clearConversationStorage();\n return { shouldRestore: false, conversationId: null, messages: null };\n }\n \n // Check if organization key matches\n if (storedConversation.organizationKey !== currentOrganizationKey) {\n return { shouldRestore: false, conversationId: null, messages: null };\n }\n \n return {\n shouldRestore: true,\n conversationId: storedConversation.conversationId,\n messages: storedConversation.messages\n };\n};\n\nexport const formatConversationContext = (messages: StoredMessage[], maxMessages: number = 10): string => {\n // Take only the last N messages to avoid context length issues\n const recentMessages = messages.slice(-maxMessages);\n \n if (recentMessages.length === 0) {\n return '';\n }\n \n // Filter out very short or empty messages\n const meaningfulMessages = recentMessages.filter(msg => \n msg.content && msg.content.trim().length > 3\n );\n \n if (meaningfulMessages.length === 0) {\n return '';\n }\n \n // Format messages into a readable context string\n const formattedMessages = meaningfulMessages.map(msg => {\n const sender = msg.sender === 'HUMAN' ? 'User' : 'Assistant';\n return `${sender}: ${msg.content.trim()}`;\n }).join('\\n');\n \n return formattedMessages;\n}; ","// Default prompt for Guide AI\nexport const DEFAULT_PROMPT = `you are Guide AI.\n Your role is to answer any question directly and succinctly that a user has. NEVER DIRECTLY MENTION THE SCREENSHOT, but use its information as much as possible to target your responses.\n If nothing is asked, then your goal is to generally assist them.\n IMPORTANT: NEVER answer in more than 10 words. Always be concise and limit answers to 1 sentence maximum. Be simple and as short as possible.\n Your job is to help them get it done through asking more and more targeted specific questions.`;\n\n// WebRTC message types to ignore in logging\nexport const IGNORE_MESSAGE_TYPES = [\n \"delta\", \n \"rate_limits\", \n \"input_audio_buffer\",\n \"response.audio.done\", \n \"response.output_item\", \n \"response.content_part\", \n \"output_audio_buffer\",\n \"response.created\", \n \"session.created\",\n \"conversation.item.truncated\"\n];\n\n// API endpoints - HYBRID APPROACH\nexport const GUIDE_AI_API_BASE = 'https://www.getguide.ai/api'; // Production for conversations\nexport const METADATA_API_BASE = 'http://localhost:3000/api'; // Local stub for metadata/events\nexport const OPENAI_REALTIME_BASE = 'https://api.openai.com/v1/realtime';\nexport const OPENAI_REALTIME_MODEL = \"gpt-realtime-2025-08-28\";\n\n// Gemini API - Note: This should be moved to environment variables in production\nexport const GEMINI_API_KEY = 'AIzaSyBiFyzjYVupLyk8BdmfWzBL1GbzX8OUdPc';\nexport const GEMINI_API_ENDPOINT = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent'; ","import { StoredMessage, addMessageToStorage } from './messageStorage';\nimport { GUIDE_AI_API_BASE } from './constants';\nimport { UserMetadata, MetadataUpdate } from '../types/metadata.types';\nimport Logger from './logger';\n\n// Type for workflow data from initialize-session\ninterface Workflow {\n id: string;\n name: string;\n triggers: string[];\n organizationKey: string;\n prompt: string;\n archived: boolean;\n createdAt: string;\n updatedAt: string;\n}\n\n// Type for the conversation creation response (now includes token, prompt, and workflows)\ninterface ConversationData {\n id: string;\n ephemeralToken: string;\n prompt: string;\n workflows: Workflow[];\n}\n\n// Create a new conversation (now also returns ephemeral token + prompt + workflows)\nexport const createNewConversation = async (\n organizationKey: string,\n onError: (error: Error, context: string) => void,\n workflowKey?: string\n): Promise<ConversationData | null> => {\n try {\n const now = new Date();\n const requestBody: any = {\n organizationKey,\n userId: 'anonymous',\n date: now.toISOString().split('T')[0],\n time: now.toTimeString().split(' ')[0],\n messages: []\n };\n \n // Add workflowKey if provided\n if (workflowKey) {\n requestBody.workflowKey = workflowKey;\n }\n \n Logger.apiCall('POST', '/initialize-session', requestBody);\n \n const response = await fetch(`${GUIDE_AI_API_BASE}/initialize-session`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n const errorText = await response.text().catch(() => 'No error details available');\n Logger.apiResponse('POST', '/initialize-session', false, { status: response.status, error: errorText });\n throw new Error(`Failed to create conversation: ${response.status} - ${errorText}`);\n }\n \n const conversationData = await response.json();\n Logger.apiResponse('POST', '/initialize-session', true, { conversationId: conversationData.id });\n return conversationData;\n } catch (error) {\n Logger.error('API', 'Error creating conversation', error);\n onError(error as Error, 'Creating conversation');\n return null;\n }\n};\n\n// Export the Workflow type for use in other modules\nexport type { Workflow };\n\n// Log a message to the conversation via API and local storage\nexport const logMessage = async (\n content: string, \n sender: 'GUIDEAI' | 'HUMAN',\n conversationId: string | null,\n organizationKey: string,\n onError: (error: Error, context: string) => void\n): Promise<void> => {\n if (!conversationId) return;\n \n try {\n const requestData = { content, sender };\n const endpoint = `/conversations/${conversationId}/messages`;\n \n Logger.apiCall('POST', endpoint, requestData);\n \n const response = await fetch(`${GUIDE_AI_API_BASE}${endpoint}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(requestData)\n });\n \n if (!response.ok) {\n Logger.apiResponse('POST', endpoint, false, { status: response.status });\n throw new Error(`Failed to log message: ${response.status} ${response.statusText}`);\n }\n \n Logger.apiResponse('POST', endpoint, true, { sender, contentLength: content.length });\n \n const message: StoredMessage = {\n content,\n sender,\n timestamp: Date.now()\n };\n \n addMessageToStorage(message, conversationId, organizationKey);\n } catch (error) {\n Logger.error('API', 'Error logging message', error);\n onError(error as Error, 'Logging message');\n }\n};\n\n// Send metadata updates in batch\nexport const sendMetadataUpdates = async (\n updates: MetadataUpdate[],\n organizationKey: string,\n onError: (error: Error, context: string) => void\n): Promise<boolean> => {\n if (updates.length === 0) return true;\n\n try {\n const requestData = {\n organizationKey,\n updates,\n batchTimestamp: Date.now(),\n updateCount: updates.length\n };\n \n Logger.apiCall('POST', '/metadata-updates', requestData);\n \n const response = await fetch(`${GUIDE_AI_API_BASE}/metadata-updates`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestData)\n });\n\n if (!response.ok) {\n Logger.apiResponse('POST', '/metadata-updates', false, { status: response.status });\n throw new Error(`Failed to send metadata updates: ${response.status} ${response.statusText}`);\n }\n\n Logger.apiResponse('POST', '/metadata-updates', true, { updateCount: updates.length });\n return true;\n } catch (error) {\n Logger.error('API', 'Error sending metadata updates', error);\n onError(error as Error, 'Sending metadata updates');\n return false;\n }\n}; ","import {\n StoredMessage,\n clearConversationStorage,\n checkForStoredConversation,\n formatConversationContext\n} from './utils/messageStorage';\n\n// Import our refactored modules\nimport { GuideAIProps, RecordingStatus, UseStateHook, UseEffectHook, UseRefHook, UseCallbackHook, PopupPosition } from './types/GuideAI.types';\nimport { DEFAULT_PROMPT, IGNORE_MESSAGE_TYPES, OPENAI_REALTIME_BASE, OPENAI_REALTIME_MODEL, GEMINI_API_KEY, GEMINI_API_ENDPOINT } from './utils/constants';\nimport { injectStyles, calculateOptimalPosition } from './utils/ui';\nimport { hoverThenClick } from './utils/hoverAndClick';\nimport { highlightThenClick } from './utils/highlightAndClick';\nimport { createNewConversation, logMessage, Workflow } from './utils/api';\nimport { detectTriggerWords, detectWorkflowTriggers, logWorkflowEvent, WorkflowLogEvent } from './utils/workflow';\nimport WelcomeBubble from './components/WelcomeBubble';\nimport Onboarding from './components/Onboarding';\nimport TranscriptBox from './components/TranscriptBox';\nimport { guideAIStyles } from './styles/GuideAI.styles';\nimport { EventTracker, UserMetadataTracker, UserMetadata } from './metric';\nimport { sendMetadataUpdates } from './utils/api';\n\ntype GuideAIScreenshotDetail = {\n dataUrl: string;\n width?: number;\n height?: number;\n hash?: string;\n reason: 'preview' | 'final' | 'change';\n timestamp?: number;\n};\n\n// Get React hooks safely, using the React instance passed as props\nconst getReactHooks = (React: typeof import('react')) => {\n // Use the React instance passed from the Chrome extension\n if (React && React.useState && React.useEffect && React.useRef) {\n console.log('GuideAI: Using React instance from props, version:', React.version);\n return {\n useState: React.useState,\n useEffect: React.useEffect,\n useRef: React.useRef,\n useMemo: React.useMemo,\n useCallback: React.useCallback as UseCallbackHook\n };\n }\n console.error('GuideAI: React hooks not available. React object:', React);\n console.error('GuideAI: React.useState:', React?.useState);\n console.error('GuideAI: React.useEffect:', React?.useEffect);\n console.error('GuideAI: React.useRef:', React?.useRef);\n throw new Error('GuideAI: React hooks not available. Make sure React is properly passed as props.');\n};\n\nconst GuideAIComponent = (props: GuideAIProps) => {\n const {\n organizationKey,\n workflowKey,\n position,\n onError = console.error,\n metadata: metadataOptions,\n transcript: transcriptOptions,\n input: inputOptions,\n React,\n ReactDOM\n } = props;\n\n // Get React hooks safely inside component with error handling\n let hooks;\n try {\n hooks = getReactHooks(React);\n } catch (error) {\n console.error('GuideAI: Failed to get React hooks:', error);\n // Return a fallback component that shows an error\n return React.createElement('div', {\n style: {\n position: 'fixed',\n bottom: '20px',\n left: '50%',\n transform: 'translateX(-50%)',\n background: '#f44336',\n color: 'white',\n padding: '12px 16px',\n borderRadius: '4px',\n fontFamily: 'Arial, sans-serif',\n fontSize: 14,\n zIndex: 1000\n }\n }, 'GuideAI: React initialization failed. Please refresh the page.');\n }\n\n const [status, setStatus] = hooks.useState<RecordingStatus>('idle');\n const [isClient, setIsClient] = hooks.useState(false);\n\n const [isHighlighting, setIsHighlighting] = hooks.useState(false);\n const [isHovering, setIsHovering] = hooks.useState(false);\n const [hasInteracted, setHasInteracted] = hooks.useState(false);\n const [prompt, setPrompt] = hooks.useState<string | null>(null);\n const [workflows, setWorkflows] = hooks.useState<Workflow[]>([]);\n const [isSessionReady, setIsSessionReady] = hooks.useState(false);\n const [isConnecting, setIsConnecting] = hooks.useState(false);\n const [showOnboarding, setShowOnboarding] = hooks.useState(false);\n const [popupPosition, setPopupPosition] = hooks.useState<PopupPosition>('above');\n const conversationIdRef = hooks.useRef<string | null>(null);\n\n const componentRef = hooks.useRef<HTMLDivElement>(null);\n const peerConnectionRef = hooks.useRef<RTCPeerConnection | null>(null);\n const dataChannelRef = hooks.useRef<RTCDataChannel | null>(null);\n const hasCreatedConversationRef = hooks.useRef(false);\n const mediaStreamRef = hooks.useRef<MediaStream | null>(null);\n const audioElementRef = hooks.useRef<HTMLAudioElement | null>(null);\n const [ephemeralToken, setEphemeralToken] = hooks.useState<string | null>(null);\n const hasInitializedRef = hooks.useRef(false);\n const isUnmountingRef = hooks.useRef(false);\n const [isConversationActive, setIsConversationActive] = hooks.useState(false);\n const [restoredMessages, setRestoredMessages] = hooks.useState<StoredMessage[]>([]);\n const [showTranscript, setShowTranscript] = hooks.useState(\n transcriptOptions?.enabled !== false // Default to true unless explicitly disabled\n );\n const [allMessages, setAllMessages] = hooks.useState<StoredMessage[]>([]);\n const [inputMode, setInputMode] = hooks.useState<'voice' | 'text'>(\n inputOptions?.defaultMode || 'voice'\n );\n const [textInput, setTextInput] = hooks.useState('');\n const [showTextInput, setShowTextInput] = hooks.useState(false);\n\n const latestScreenshotRef = hooks.useRef<GuideAIScreenshotDetail | null>(null);\n const lastSentImageHashRef = hooks.useRef<string | null>(null);\n const lastImageSendAtRef = hooks.useRef<number>(0);\n const isImageSendInFlightRef = hooks.useRef<boolean>(false);\n const pendingImageDetailRef = hooks.useRef<GuideAIScreenshotDetail | null>(null);\n\n const hasAttemptedAutoStartRef = hooks.useRef(false);\n const initializationAttemptedRef = hooks.useRef(false);\n\n\n\n // ===== METADATA AND EVENT TRACKING =====\n\n // Create event tracker - memoized to prevent recreation\n const eventTracker = hooks.useMemo(() => {\n const customerId = metadataOptions?.initialUserData?.userId || 'anonymous';\n const organizationId = metadataOptions?.initialUserData?.organizationKey || 'default';\n const customerType = metadataOptions?.initialUserData?.userType || 'user';\n\n return new EventTracker({ customerId, organizationId, customerType, organizationKey });\n }, [organizationKey]); // Only depend on organizationKey\n\n // Create metadata tracker - memoized to prevent recreation\n const metadataTracker = hooks.useMemo(() => \n new UserMetadataTracker(organizationKey, metadataOptions?.config || {}, onError), \n [organizationKey, onError] // Include onError callback\n );\n\n // ===== API & EXTERNAL CALLS =====\n\n // Call Gemini Flash API to validate and fix JSON\n const geminiFlash = async (prompt: string): Promise<string> => {\n const response = await fetch(`${GEMINI_API_ENDPOINT}?key=${GEMINI_API_KEY}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: [\n {\n parts: [\n {\n text: prompt\n }\n ]\n }\n ]\n })\n });\n\n const responseJson = await response.json();\n const content = responseJson.candidates[0].content;\n const responseText = content.parts[0].text;\n return responseText;\n };\n\n // ===== COMPONENT WRAPPER FUNCTIONS =====\n\n // Wrapper functions to maintain original signatures\n const handleCreateNewConversation = hooks.useCallback(async () => {\n if (hasCreatedConversationRef.current) return null;\n hasCreatedConversationRef.current = true;\n\n const conversationData = await createNewConversation(organizationKey, onError, workflowKey);\n if (conversationData) {\n conversationIdRef.current = conversationData.id;\n setEphemeralToken(conversationData.ephemeralToken);\n setPrompt(conversationData.prompt);\n setWorkflows(conversationData.workflows || []);\n return conversationData;\n }\n return null;\n }, [organizationKey]); // Removed onError from dependencies to prevent re-creation\n\n const handleLogMessage = hooks.useCallback(async (content: string, sender: 'GUIDEAI' | 'HUMAN') => {\n await logMessage(content, sender, conversationIdRef.current, organizationKey, onError);\n\n // Check if this exact message already exists to prevent duplicates\n setAllMessages(prev => {\n const lastMessage = prev[prev.length - 1];\n if (lastMessage &&\n lastMessage.content === content &&\n lastMessage.sender === sender) {\n // Message already exists, don't add duplicate\n return prev;\n }\n\n // Add message to local state for transcript\n const newMessage: StoredMessage = {\n content,\n sender,\n timestamp: Date.now()\n };\n return [...prev, newMessage];\n });\n }, [organizationKey]); // Removed onError from dependencies to prevent re-creation\n\n const handleHoverThenClick = hooks.useCallback(async (selector: string | string[]) => {\n return hoverThenClick(selector, isHighlighting, setIsHighlighting, handleLogMessage);\n }, [isHighlighting, setIsHighlighting, handleLogMessage]);\n\n const handleHighlightThenClick = hooks.useCallback(async (selector: string | string[], hoverTime?: number) => {\n return highlightThenClick(selector, isHovering, setIsHovering, handleLogMessage, hoverTime);\n }, [isHovering, setIsHovering, handleLogMessage]);\n\n // ===== UI UTILITIES & POSITION =====\n\n // Calculate optimal position for popups based on component location\n const getOptimalPosition = hooks.useCallback((elementHeight: number = 240, spacing: number = 15): PopupPosition => {\n if (!componentRef.current) return 'above';\n\n const rect = componentRef.current.getBoundingClientRect();\n return calculateOptimalPosition(rect, elementHeight, spacing);\n }, []);\n\n // ===== WEBRTC MANAGEMENT =====\n\n const sendMessage = (message: any) => {\n if (dataChannelRef.current?.readyState === 'open') {\n // console.log('sending message', message);\n dataChannelRef.current.send(JSON.stringify(message));\n } else {\n console.error('Data channel not open, cannot send message');\n setStatus('idle');\n }\n };\n\n const canSendImageNow = (urgent: boolean) => {\n if (dataChannelRef.current?.readyState !== 'open') return false;\n if (urgent) return true;\n const since = Date.now() - lastImageSendAtRef.current;\n return since >= 1200;\n };\n\n const createImageContentPart = (detail: GuideAIScreenshotDetail) => ({\n type: 'input_image',\n image_url: detail.dataUrl\n });\n\n const createUserImageItem = (detail: GuideAIScreenshotDetail) => ({\n type: 'conversation.item.create',\n item: {\n type: 'message',\n role: 'user',\n content: [createImageContentPart(detail)]\n }\n });\n\n const sendImageItem = (detail: GuideAIScreenshotDetail) => {\n if (!detail?.dataUrl) return;\n if (lastSentImageHashRef.current && detail.hash && lastSentImageHashRef.current === detail.hash) return;\n const imageMessage = createUserImageItem(detail);\n console.log('GuideAI: Sending image message to OpenAI API:', JSON.stringify(imageMessage, null, 2));\n sendMessage(imageMessage);\n lastSentImageHashRef.current = detail.hash || null;\n lastImageSendAtRef.current = Date.now();\n\n // Notify the extension that this screenshot was sent to LLM\n if (typeof window !== 'undefined' && (window as any).GuideAI?.visualContext?.markScreenshotSent) {\n console.log('GuideAI: Calling markScreenshotSent with detail:', detail);\n (window as any).GuideAI.visualContext.markScreenshotSent(detail);\n } else {\n console.log('GuideAI: markScreenshotSent not available on window.GuideAI.visualContext');\n }\n };\n\n const maybeSendImage = (detail: GuideAIScreenshotDetail, urgent: boolean) => {\n console.log('GuideAI: maybeSendImage called', { urgent, canSend: canSendImageNow(urgent), inFlight: isImageSendInFlightRef.current });\n if (!canSendImageNow(urgent)) {\n console.log('GuideAI: Cannot send image now, queuing');\n pendingImageDetailRef.current = detail;\n return;\n }\n if (isImageSendInFlightRef.current) {\n console.log('GuideAI: Image send in flight, queuing');\n pendingImageDetailRef.current = detail;\n return;\n }\n isImageSendInFlightRef.current = true;\n try {\n console.log('GuideAI: Sending image item to LLM');\n sendImageItem(detail);\n } finally {\n isImageSendInFlightRef.current = false;\n if (pendingImageDetailRef.current) {\n const next = pendingImageDetailRef.current;\n pendingImageDetailRef.current = null;\n maybeSendImage(next, false);\n }\n }\n };\n\n const cleanupWebRTC = hooks.useCallback(() => {\n if (mediaStreamRef.current) {\n // Stop tracks completely instead of just disabling them\n mediaStreamRef.current.getAudioTracks().forEach(track => {\n track.stop();\n });\n mediaStreamRef.current = null;\n }\n\n if (dataChannelRef.current) {\n dataChannelRef.current.close();\n dataChannelRef.current = null;\n }\n\n if (peerConnectionRef.current) {\n peerConnectionRef.current.close();\n peerConnectionRef.current = null;\n }\n\n if (audioElementRef.current) {\n audioElementRef.current.srcObject = null;\n }\n\n // Reset session ready state\n setIsSessionReady(false);\n }, []);\n\n const initializeWebRTC = async (audioStream: MediaStream | null): Promise<boolean> => {\n try {\n const pc = new RTCPeerConnection();\n peerConnectionRef.current = pc;\n\n // Only add audio tracks if audioStream is provided (for voice mode)\n if (audioStream) {\n audioStream.getAudioTracks().forEach(track => {\n pc.addTrack(track, audioStream);\n });\n }\n\n // Only create audio element if we have an audio stream (voice mode)\n if (audioStream && !audioElementRef.current) {\n const audioEl = document.createElement('audio');\n audioEl.autoplay = true;\n\n // Detect if running in Chrome extension content script\n const isContentScript = typeof window !== 'undefined' &&\n (window as any).chrome &&\n (window as any).chrome.runtime &&\n (window as any).chrome.runtime.id;\n\n if (isContentScript) {\n // Enhanced audio processing for extension context\n audioEl.crossOrigin = \"anonymous\";\n audioEl.preload = \"auto\";\n\n // Try to use the page's audio context for better integration\n try {\n const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();\n // Create a gain node for better volume control and echo prevention\n const gainNode = audioContext.createGain();\n gainNode.gain.value = 0.8; // Slightly reduce volume to prevent feedback\n\n // Store audio context reference for potential cleanup\n (audioEl as any)._audioContext = audioContext;\n (audioEl as any)._gainNode = gainNode;\n\n console.log('GuideAI: Enhanced audio context created for extension');\n } catch (error) {\n console.warn('GuideAI: Could not create enhanced audio context:', error);\n }\n }\n\n document.body.appendChild(audioEl);\n audioElementRef.current = audioEl;\n }\n\n pc.ontrack = (e) => {\n if (audioElementRef.current && e.track.kind === 'audio') {\n const stream = new MediaStream([e.track]);\n audioElementRef.current.srcObject = stream;\n }\n };\n\n const dc = pc.createDataChannel('oai-events');\n dataChannelRef.current = dc;\n\n dc.onopen = () => {\n // Configure session based on whether we have audio stream (voice vs text mode)\n const sessionConfig: any = {\n tools: [{\n type: \"function\",\n name: \"highlight\",\n description: \"Highlight specific elements on the page to guide the user's attention. Supports CSS selectors, XPath, and custom text-based selectors.\",\n parameters: {\n type: \"object\",\n properties: {\n selector: {\n oneOf: [\n {\n type: \"string\",\n description: \"Selector for the element to highlight. Supports multiple formats: 1) CSS selector (e.g., '#search-bar', '.button') 2) XPath (e.g., '//button[@id=\\\"submit\\\"]') 3) Text-based selector (e.g., 'text:.MuiBox-root:Add Account') which finds elements with specified CSS class containing exact text\"\n },\n {\n type: \"array\",\n items: {\n type: \"string\"\n },\n description: \"List of selectors to highlight in sequence. Each can use any of the supported formats (CSS, XPath, or text-based).\"\n }\n ],\n description: \"Selector(s) for the element(s) to highlight. Supports CSS selectors, XPath expressions, and custom text-based selectors in format 'text:cssSelector:textContent' for precise targeting.\"\n }\n },\n required: [\"selector\"]\n }\n }, {\n type: \"function\",\n name: \"hoverThenHighlight\",\n description: \"Hover over specific elements on the page with extended visual feedback. Used for testing and demonstration purposes.\",\n parameters: {\n type: \"object\",\n properties: {\n selector: {\n oneOf: [\n {\n type: \"string\",\n description: \"Selector for the element to hover over. Supports multiple formats: 1) CSS selector (e.g., '#search-bar', '.button') 2) XPath (e.g., '//button[@id=\\\"submit\\\"]') 3) Text-based selector (e.g., 'text:.MuiBox-root:Add Account') which finds elements with specified CSS class containing exact text\"\n },\n {\n type: \"array\",\n items: {\n type: \"string\"\n },\n description: \"List of selectors to hover over in sequence. Each can use any of the supported formats (CSS, XPath, or text-based).\"\n }\n ],\n description: \"Selector(s) for the element(s) to hover over. Supports CSS selectors, XPath expressions, and custom text-based selectors in format 'text:cssSelector:textContent' for precise targeting.\"\n },\n hoverTime: {\n type: \"integer\",\n description: \"Time to hover over each element in milliseconds before clicking the last element. Default is 3000ms (3 seconds).\",\n minimum: 500,\n maximum: 10000\n }\n },\n required: [\"selector\"]\n }\n }],\n tool_choice: \"auto\"\n };\n\n // Add workflow trigger tools based on available workflows\n if (workflows.length > 0) {\n workflows.forEach(workflow => {\n if (!workflow.archived && workflow.triggers.length > 0) {\n sessionConfig.tools.push({\n type: \"function\",\n name: `trigger_workflow_${workflow.id}`,\n description: `Trigger the \"${workflow.name}\" workflow when specific trigger words are detected`,\n parameters: {\n type: \"object\",\n properties: {\n triggerWords: {\n type: \"array\",\n items: {\n type: \"string\"\n },\n description: `Array of detected trigger words from: ${workflow.triggers.join(', ')}`\n },\n userMessage: {\n type: \"string\",\n description: \"The complete user message that triggered the workflow\"\n }\n },\n required: [\"triggerWords\", \"userMessage\"]\n }\n });\n }\n });\n }\n\n // Only add audio-specific config if we have an audio stream\n if (audioStream) {\n sessionConfig.turn_detection = {\n type: \"semantic_vad\",\n create_response: true,\n interrupt_response: false\n };\n sessionConfig.input_audio_transcription = {\n model: \"gpt-4o-mini-transcribe\",\n language: \"en\",\n };\n }\n\n sendMessage({\n type: \"session.update\",\n session: sessionConfig\n });\n };\n\n dc.onmessage = (e) => {\n try {\n const message = JSON.parse(e.data);\n\n switch (message.type) {\n case 'session.updated':\n // console.log('session.updated', message);\n setIsSessionReady(true);\n setIsConnecting(false);\n\n // Send the workflow-specific prompt as a system message\n if (prompt) {\n let enhancedPrompt = prompt;\n\n // Add workflow trigger instructions if workflows are available\n if (workflows.length > 0) {\n const activeWorkflows = workflows.filter(w => !w.archived && w.triggers.length > 0);\n if (activeWorkflows.length > 0) {\n // Log workflow initialization\n const initEvent: WorkflowLogEvent = {\n type: 'workflow_init',\n timestamp: new Date().toISOString(),\n data: {\n totalWorkflows: workflows.length,\n activeWorkflows: activeWorkflows.length,\n workflows: activeWorkflows.map((w: Workflow) => ({\n id: w.id,\n name: w.name,\n triggers: w.triggers\n }))\n }\n };\n logWorkflowEvent(initEvent);\n\n // Log workflow initialization in transcript\n const workflowInitMessage = `🔧 **Workflows Loaded**: ${activeWorkflows.length} active workflow(s) available\\n\\n${activeWorkflows.map((w: Workflow) => `• **${w.name}**: ${w.triggers.join(', ')}`).join('\\n')}`;\n handleLogMessage(workflowInitMessage, 'GUIDEAI');\n\n enhancedPrompt += `\n\nIMPORTANT WORKFLOW INSTRUCTIONS:\nYou have access to workflow trigger tools. When you detect specific trigger words in the user's message, you should call the appropriate workflow trigger function.\n\nAvailable workflows:`;\n\n activeWorkflows.forEach(workflow => {\n enhancedPrompt += `\n- ${workflow.name}: Call trigger_workflow_${workflow.id} when you detect these trigger words: ${workflow.triggers.join(', ')}`;\n });\n\n enhancedPrompt += `\n\nTo use workflow triggers:\n1. Analyze the user's message for trigger words\n2. If trigger words match any workflow, call the corresponding trigger_workflow_[id] function with:\n - triggerWords: Array of detected trigger words\n - userMessage: The complete user message\n\nAlways call the appropriate workflow trigger function when you detect relevant trigger words in the user's message.`;\n }\n }\n\n sendMessage({\n type: \"conversation.item.create\",\n item: {\n type: \"message\",\n role: \"system\",\n content: [{\n type: \"input_text\",\n text: enhancedPrompt\n }]\n }\n });\n\n console.log('Sent workflow-enhanced prompt as system message');\n }\n\n // If we have restored messages, send conversation context to AI now that session is ready\n if (restoredMessages.length > 0) {\n const conversationContext = formatConversationContext(restoredMessages, 5); // Reduce to 5 messages\n\n // Send as system context instead of user input to avoid confusion\n sendMessage({\n type: \"conversation.item.create\",\n item: {\n type: \"message\",\n role: \"system\",\n content: [{\n type: \"input_text\",\n text: `This is context from a previous conversation session. The user is continuing an existing conversation. Previous context: ${conversationContext}`\n }]\n }\n });\n\n console.log('Sent conversation context with', restoredMessages.length, 'restored messages');\n }\n break;\n\n case 'session.transcript_disabled':\n if (message.text && message.text.trim()) {\n console.log('Session transcript:', message.text);\n handleLogMessage(message.text, 'HUMAN');\n }\n break;\n\n case 'conversation.item.input_audio_transcription.completed':\n if (message.transcript && message.transcript.trim()) {\n console.log('User transcript:', message.transcript);\n\n // Check for trigger words in the user message\n if (workflows.length > 0) {\n const triggeredWorkflows = detectWorkflowTriggers(message.transcript, workflows);\n if (triggeredWorkflows.length > 0) {\n const triggerEvent: WorkflowLogEvent = {\n type: 'trigger_detected',\n timestamp: new Date().toISOString(),\n data: {\n userMessage: message.transcript,\n triggeredWorkflows: triggeredWorkflows.map((w: Workflow) => ({\n id: w.id,\n name: w.name,\n triggers: w.triggers\n }))\n }\n };\n logWorkflowEvent(triggerEvent);\n\n // Log trigger word detection in transcript\n const triggerDetectionMessage = `🎯 **Trigger Words Detected**: ${triggeredWorkflows.map(w => w.name).join(', ')}\\n\\n*Analyzing message for workflow activation...*`;\n handleLogMessage(triggerDetectionMessage, 'GUIDEAI');\n }\n }\n\n // Only log if it's different from the last message to avoid duplicates\n if (allMessages.length === 0 ||\n allMessages[allMessages.length - 1].content !== message.transcript) {\n handleLogMessage(message.transcript, 'HUMAN');\n }\n }\n break;\n\n case 'response.audio_transcript.done':\n if (message.transcript && message.transcript.trim()) {\n console.log('Assistant message:', message.transcript);\n // Only log if it's different from the last assistant message to avoid duplicates\n const lastMessage = allMessages[allMessages.length - 1];\n if (!lastMessage ||\n lastMessage.sender !== 'GUIDEAI' ||\n lastMessage.content !== message.transcript) {\n handleLogMessage(message.transcript, 'GUIDEAI');\n }\n }\n break;\n\n case \"conversation.item.created\":\n // console.log('conversation.item.created', message);\n break;\n\n case \"input_audio_buffer.speech_started\":\n // console.log('User speaking started');\n setStatus('recording');\n break;\n\n case \"input_audio_buffer.speech_stopped\":\n // console.log('User speaking stopped');\n setStatus('processing');\n break;\n\n case 'response.done':\n // console.log('response.done', message);\n for (const out of message.response.output) {\n switch (out.type) {\n case \"message\":\n // console.log('Assistant message:', out.text);\n break;\n\n case \"function_call\":\n // console.log('function_call', out);\n if (out.name === 'highlight' && out.arguments) {\n // console.log('Tool call:', out.arguments);\n try {\n let argsToUse = out.arguments;\n // Check if the JSON string is missing a closing quote\n // try {\n // console.log(\"argsToUse\", argsToUse);\n // const parsedArgs = JSON.parse(argsToUse);\n // console.log(\"parsedArgs\", parsedArgs);\n // selectElementAndClick(parsedArgs.selector);\n // } catch (error) {\n // console.log(\"errored, argsToUse\", argsToUse);\n const prompt = `\n Validate the following JSON string and return ONLY a correct JSON object:\n Note: The selector(s) should be either a css selector or an xpath, so modify those if invalid.\n Also, [url=''] is a valid css selector, so do not modify it if it's present.\n Do not add any other text or newlines to the response.\n It should be of the following format:\n { \"selector\": \"string\" }\n or\n { \"selector\": [\"string\", ...] }\n DO NOT add any other text or newlines to the response, it should begin and end with curly braces.\n DO NOT add '''json to the response.\n The JSON string is:\n ${argsToUse}\n `\n geminiFlash(prompt).then(response => {\n response = response.replace(\"```json\", \"\").replace(\"```\", \"\").trim();\n // console.log(\"response\", response);\n const parsedArgs = JSON.parse(response);\n handleHoverThenClick(parsedArgs.selector);\n }).catch(err => {\n console.error(\"Error calling Gemini:\", err);\n });\n // }\n\n\n } catch (error) {\n console.error('Error parsing function arguments:', error);\n console.log(out.arguments);\n handleLogMessage('Error parsing function arguments: ' + out.arguments, 'GUIDEAI');\n }\n } else if (out.name === 'hoverThenHighlight' && out.arguments) {\n try {\n let argsToUse = out.arguments;\n // Use Gemini to validate/fix JSON format\n const prompt = `\n Validate the following JSON string and return ONLY a correct JSON object:\n Note: The selector(s) should be either a css selector or an xpath, so modify those if invalid.\n Also, [url=''] is a valid css selector, so do not modify it if it's present.\n Do not add any other text or newlines to the response.\n It should be of the following format:\n { \"selector\": \"string\" }\n or\n { \"selector\": [\"string\", ...] }\n or\n { \"selector\": \"string\", \"hoverTime\": number }\n or\n { \"selector\": [\"string\", ...], \"hoverTime\": number }\n The hoverTime parameter is optional and represents time to hover in milliseconds (500-10000).\n If not provided, default to 3000ms.\n DO NOT add any other text or newlines to the response, it should begin and end with curly braces.\n DO NOT add '''json to the response.\n The JSON string is:\n ${argsToUse}\n `;\n geminiFlash(prompt).then(response => {\n response = response.replace(\"```json\", \"\").replace(\"```\", \"\").trim();\n // console.log(\"hoverThenHighlight response\", response);\n const parsedArgs = JSON.parse(response);\n handleHighlightThenClick(parsedArgs.selector, parsedArgs.hoverTime || 3000);\n }).catch(err => {\n console.error(\"Error calling Gemini for hoverThenHighlight:\", err);\n });\n\n } catch (error) {\n console.error('Error parsing hoverThenHighlight function arguments:', error);\n console.log(out.arguments);\n handleLogMessage('Error parsing hoverThenHighlight function arguments: ' + out.arguments, 'GUIDEAI');\n }\n\n } else if (out.name.startsWith('trigger_workflow_') && out.arguments) {\n // Handle workflow trigger\n try {\n const parsedArgs = JSON.parse(out.arguments);\n const { triggerWords, userMessage } = parsedArgs;\n\n // Extract workflow ID from function name\n const workflowId = out.name.replace('trigger_workflow_', '');\n const workflow = workflows.find(w => w.id === workflowId);\n\n if (workflow) {\n // Log the workflow trigger\n const activationEvent: WorkflowLogEvent = {\n type: 'workflow_activated',\n timestamp: new Date().toISOString(),\n data: {\n workflowId,\n workflowName: workflow.name,\n triggerWords,\n userMessage\n }\n };\n logWorkflowEvent(activationEvent);\n\n // Send the workflow prompt as a system message to guide the AI\n sendMessage({\n type: \"conversation.item.create\",\n item: {\n type: \"message\",\n role: \"system\",\n content: [{\n type: \"input_text\",\n text: `WORKFLOW ACTIVATED: ${workflow.name}\\n\\n${workflow.prompt}`\n }]\n }\n });\n\n // Log a message indicating the workflow was triggered\n const workflowActivationMessage = `🔧 **Workflow Activated**: ${workflow.name}\\n\\n*Trigger words detected: ${triggerWords.join(', ')}*\\n\\nI'm now using the ${workflow.name} workflow to better assist you.`;\n handleLogMessage(workflowActivationMessage, 'GUIDEAI');\n } else {\n console.error('Workflow not found:', workflowId);\n handleLogMessage('Error: Workflow not found', 'GUIDEAI');\n }\n } catch (error) {\n console.error('Error parsing workflow trigger arguments:', error);\n handleLogMessage('Error parsing workflow trigger arguments: ' + out.arguments, 'GUIDEAI');\n }\n }\n\n sendMessage({\n type: \"conversation.item.create\",\n item: {\n type: \"function_call_output\",\n call_id: out.call_id,\n output: JSON.stringify(true)\n }\n });\n\n sendMessage({\n type: 'response.create',\n });\n break;\n }\n }\n\n break;\n\n case \"response.function_call_arguments.delta\":\n // console.log('response.function_call_arguments.delta', message);\n break;\n\n case \"response.function_call_arguments.done\":\n // console.log('response.function_call_arguments.done', message);\n break;\n\n case \"output_audio_buffer.started\":\n setStatus('playing');\n break;\n\n case \"output_audio_buffer.stopped\":\n // console.log('output_audio_buffer.stopped');\n setStatus('recording');\n\n break;\n\n case 'error':\n if (message.error.code === 'session_expired') {\n console.error('Session expired:', message);\n // Batch state updates to prevent mounting/unmounting loops\n setTimeout(() => {\n setStatus('idle');\n setIsConnecting(false);\n setIsConversationActive(false);\n setEphemeralToken(null); // Clear expired token so reconnection gets fresh one\n hasCreatedConversationRef.current = false; // Allow creating new conversation\n }, 0);\n break;\n }\n console.error('OpenAI API error:', message);\n console.error('Error details:', JSON.stringify(message.error, null, 2));\n console.error('Error message:', message.error.message);\n console.error('Error code:', message.error.code);\n onError(new Error(message.error.message || 'Unknown API error'), 'OpenAI API error');\n // Batch state updates to prevent mounting/unmounting loops\n setTimeout(() => {\n setStatus('idle');\n setIsConnecting(false);\n setIsConversationActive(false);\n setEphemeralToken(null); // Clear token in case of auth-related errors\n hasCreatedConversationRef.current = false; // Allow creating new conversation\n }, 0);\n break;\n\n default:\n if (!IGNORE_MESSAGE_TYPES.some(fragment => message.type.includes(fragment))) {\n console.log('Unhandled:', message.type, message);\n }\n }\n } catch (error) {\n console.error('Error handling WebRTC message:', error);\n // Batch state updates to prevent mounting/unmounting loops\n setTimeout(() => {\n setStatus('idle');\n setIsConversationActive(false);\n }, 0);\n }\n };\n\n // Start the session using SDP\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n\n if (!pc.localDescription || !pc.localDescription.sdp) {\n throw new Error('Failed to create local SDP offer');\n }\n\n const baseUrl = OPENAI_REALTIME_BASE;\n const model = OPENAI_REALTIME_MODEL;\n\n\n const sdpResponse = await fetch(`${baseUrl}?model=${model}`, {\n method: 'POST',\n body: pc.localDescription.sdp,\n headers: {\n Authorization: `Bearer ${ephemeralToken}`,\n 'Content-Type': 'application/sdp'\n },\n });\n\n if (!sdpResponse.ok) {\n const errorText = await sdpResponse.text().catch(() => 'No error details available');\n console.error('WebRTC connection failed with status:', sdpResponse.status);\n console.error('Error details:', errorText);\n\n // Clear token on authentication failures\n if (sdpResponse.status === 401 || sdpResponse.status === 403) {\n setEphemeralToken(null);\n }\n\n throw new Error(`Failed to connect to OpenAI WebRTC: ${sdpResponse.status} - ${errorText}`);\n }\n\n const answerSdp = await sdpResponse.text();\n\n const answer = {\n type: 'answer',\n sdp: answerSdp,\n };\n\n await pc.setRemoteDescription(answer as RTCSessionDescriptionInit);\n\n return true;\n } catch (error) {\n console.error('Error initializing WebRTC:', error);\n onError(error as Error, 'WebRTC initialization failed');\n setIsConnecting(false);\n return false;\n }\n };\n\n hooks.useEffect(() => {\n const onScreenshot = (ev: Event) => {\n const anyEv = ev as any;\n const detail = anyEv?.detail as GuideAIScreenshotDetail | undefined;\n if (!detail || !detail.dataUrl) return;\n console.log('GuideAI: Received screenshot event', { reason: detail.reason, status, hasDataUrl: !!detail.dataUrl });\n latestScreenshotRef.current = detail;\n const urgent = detail.reason === 'final';\n if (status === 'recording' || status === 'processing') {\n console.log('GuideAI: Sending image to LLM', { reason: detail.reason, urgent });\n maybeSendImage(detail, urgent);\n } else {\n console.log('GuideAI: Not sending image - status is', status);\n }\n };\n window.addEventListener('guideai:screenshot', onScreenshot as EventListener);\n return () => window.removeEventListener('guideai:screenshot', onScreenshot as EventListener);\n }, [status]);\n\n // ===== SESSION MANAGEMENT =====\n\n const startConversationSession = hooks.useCallback(async () => {\n try {\n setStatus('processing');\n\n if (peerConnectionRef.current && dataChannelRef.current?.readyState === 'open' && mediaStreamRef.current) {\n mediaStreamRef.current.getAudioTracks().forEach(track => {\n track.enabled = true;\n });\n setStatus('recording');\n return true;\n } else {\n cleanupWebRTC();\n\n try {\n setIsConnecting(true);\n\n // Ensure we have a valid token (get fresh one if needed)\n if (!ephemeralToken) {\n console.log('No token available, getting fresh token...');\n const conversationData = await handleCreateNewConversation();\n if (!conversationData) {\n console.error('Failed to get fresh token');\n setIsConnecting(false);\n setStatus('idle');\n return false;\n }\n }\n\n let audioStream: MediaStream | null = null;\n\n // Always attempt microphone access for voice functionality\n if (true) {\n try {\n audioStream = await navigator.mediaDevices.getUserMedia({\n audio: {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n channelCount: 1,\n sampleRate: 48000,\n }\n });\n mediaStreamRef.current = audioStream;\n console.log('Microphone access granted - audio tracks:', audioStream.getAudioTracks().length);\n } catch (micError) {\n console.error('Microphone access failed:', micError);\n console.log('Voice functionality will not be available - continuing with text-only mode');\n // Continue without microphone for text-only mode\n }\n }\n\n const initialized = await initializeWebRTC(audioStream);\n\n if (!initialized) {\n // Clean up if initialization fails\n if (audioStream) {\n audioStream.getAudioTracks().forEach(track => {\n track.stop();\n });\n mediaStreamRef.current = null;\n }\n setStatus('idle');\n setIsConnecting(false);\n return false;\n }\n\n // Set appropriate status based on whether we have audio\n if (audioStream) {\n setStatus('recording');\n } else {\n setStatus('idle'); // Text-only mode, no recording status needed\n }\n return true;\n } catch (error) {\n console.error('Error during conversation setup:', error);\n onError(error as Error, 'Microphone or WebRTC setup failed');\n setStatus('idle');\n setIsConnecting(false);\n return false;\n }\n }\n } catch (error) {\n setStatus('idle');\n setIsConnecting(false);\n onError(error as Error, 'Starting conversation failed');\n return false;\n }\n }, [ephemeralToken]); // Remove onError from dependencies to prevent re-creation\n\n const endConversationSession = hooks.useCallback(() => {\n if (!isClient) return;\n\n if (mediaStreamRef.current) {\n mediaStreamRef.current.getAudioTracks().forEach(track => {\n track.stop();\n });\n }\n\n cleanupWebRTC();\n setStatus('idle');\n\n // Clear localStorage when conversation is explicitly ended\n clearConversationStorage();\n\n // Reset restored messages state\n setRestoredMessages([]);\n\n // Reset auto-start flag to allow future auto-starts\n hasAttemptedAutoStartRef.current = false;\n }, [isClient]);\n\n // ===== UI EVENT HANDLERS =====\n\n const handleToggleConversation = async () => {\n if (!isClient) return;\n\n if (!hasInteracted) {\n setHasInteracted(true);\n localStorage.setItem('guideAI_hasInteracted', 'true');\n\n // Show onboarding on first interaction\n setShowOnboarding(true);\n return;\n }\n\n if (!isConversationActive) {\n const success = await startConversationSession();\n if (success) {\n setIsConversationActive(true);\n // Always show transcript when conversation starts\n if (transcriptOptions?.enabled !== false) {\n setShowTranscript(true);\n }\n // Show text input option by default (unless explicitly disabled)\n if (inputOptions?.enableTextInput !== false) {\n setShowTextInput(true);\n }\n }\n } else {\n endConversationSession();\n setIsConversationActive(false);\n // Hide transcript and text input when conversation ends\n if (transcriptOptions?.enabled !== false) {\n setShowTranscript(false);\n }\n setShowTextInput(false);\n }\n };\n\n const handleToggleTranscript = () => {\n setShowTranscript(prev => !prev);\n };\n\n const handleToggleInputMode = () => {\n setInputMode(prev => {\n const newMode = prev === 'voice' ? 'text' : 'voice';\n // Update text input visibility based on new mode\n if (newMode === 'text' && isConversationActive) {\n setShowTextInput(true);\n } else {\n setShowTextInput(false);\n }\n return newMode;\n });\n };\n\n\n\n const handleTextSubmit = async () => {\n if (!textInput.trim() || !isConversationActive) return;\n\n const userMessage = textInput.trim();\n\n // Log the user's text message to console (matching voice input behavior)\n console.log('User transcript:', userMessage);\n\n // Check for trigger words in the user message\n if (workflows.length > 0) {\n const triggeredWorkflows = detectWorkflowTriggers(userMessage, workflows);\n if (triggeredWorkflows.length > 0) {\n const textTriggerEvent: WorkflowLogEvent = {\n type: 'trigger_detected',\n timestamp: new Date().toISOString(),\n data: {\n userMessage,\n triggeredWorkflows: triggeredWorkflows.map((w: Workflow) => ({\n id: w.id,\n name: w.name,\n triggers: w.triggers\n }))\n }\n };\n logWorkflowEvent(textTriggerEvent);\n\n // Log trigger word detection in transcript\n const triggerDetectionMessage = `🎯 **Trigger Words Detected**: ${triggeredWorkflows.map(w => w.name).join(', ')}\\n\\n*Analyzing message for workflow activation...*`;\n handleLogMessage(triggerDetectionMessage, 'GUIDEAI');\n }\n }\n // Log the user's text message\n handleLogMessage(userMessage, 'HUMAN');\n\n // Send text message with potential image to AI via WebRTC data channel\n if (dataChannelRef.current?.readyState === 'open') {\n const parts: any[] = [{ type: 'input_text', text: textInput.trim() }];\n const snap = latestScreenshotRef.current;\n const unsentImage = snap && (!lastSentImageHashRef.current || snap.hash !== lastSentImageHashRef.current);\n if (snap && unsentImage) {\n parts.push(createImageContentPart(snap));\n // Update the image hash to prevent resending\n lastSentImageHashRef.current = snap.hash || null;\n lastImageSendAtRef.current = Date.now();\n }\n const message = { type: 'conversation.item.create', item: { type: 'message', role: 'user', content: parts } };\n\n console.log('GuideAI: Sending message to OpenAI API:', JSON.stringify(message, null, 2));\n dataChannelRef.current.send(JSON.stringify(message));\n\n // Trigger response generation\n const responseMessage = {\n type: \"response.create\"\n };\n dataChannelRef.current.send(JSON.stringify(responseMessage));\n }\n\n // Clear the input\n setTextInput('');\n };\n\n const handleTextKeyPress = (event: React.KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n handleTextSubmit();\n }\n };\n\n const handleOnboardingComplete = async () => {\n setShowOnboarding(false);\n\n // Start the conversation session when onboarding is completed\n const success = await startConversationSession();\n if (success) {\n setIsConversationActive(true);\n // Only auto-show transcript if enabled in options\n if (transcriptOptions?.enabled !== false) {\n setShowTranscript(true);\n }\n }\n };\n\n const handleOnboardingClose = () => {\n setShowOnboarding(false);\n };\n\n const handleCloseTranscript = () => {\n setShowTranscript(false);\n };\n\n // ===== REACT EFFECTS & LIFECYCLE ===== \n\n // CSS styles injection effect\n hooks.useEffect(() => {\n console.log('GuideAI: Injecting styles...', { guideAIStyles: !!guideAIStyles });\n injectStyles(guideAIStyles, 'guide-ai-styles');\n\n // Debug: Check if styles were actually injected\n setTimeout(() => {\n const styleElement = document.getElementById('guide-ai-styles');\n console.log('GuideAI: Styles injection check:', {\n styleElement: !!styleElement,\n styleContent: styleElement?.textContent?.substring(0, 100)\n });\n }, 100);\n }, [guideAIStyles]);\n\n // Metadata initialization effect - EventTracker now auto-initializes\n hooks.useEffect(() => {\n console.log('GuideAI: Metadata initialization effect triggered', {\n metadataTracker: !!metadataTracker,\n metadataOptions: !!metadataOptions,\n initialUserData: metadataOptions?.initialUserData\n });\n\n // Initialize UserMetadataTracker\n try {\n metadataTracker.init();\n console.log('GuideAI: UserMetadataTracker.init() called successfully');\n } catch (error) {\n console.error('GuideAI: Failed to initialize UserMetadataTracker:', error);\n }\n\n if (metadataOptions?.initialUserData) {\n try {\n metadataTracker.updateUserInfo(metadataOptions.initialUserData);\n console.log('GuideAI: Initial user data updated successfully');\n } catch (error) {\n console.error('GuideAI: Failed to update initial user data:', error);\n }\n }\n }, [metadataTracker, metadataOptions?.initialUserData]);\n\n // Component initialization effect\n hooks.useEffect(() => {\n console.log('GuideAI: Component mounting - initialization started', {\n hasInitialized: hasInitializedRef.current,\n initializationAttempted: initializationAttemptedRef.current,\n isClient,\n organizationKey,\n timestamp: Date.now()\n });\n\n // Prevent multiple initializations\n if (hasInitializedRef.current || isUnmountingRef.current || initializationAttemptedRef.current) {\n console.log('GuideAI: Already initialized, unmounting, or initialization already attempted, skipping');\n return;\n }\n\n // Additional safeguard: check if we're in a browser environment\n if (typeof window === 'undefined') {\n console.log('GuideAI: Not in browser environment, skipping initialization');\n return;\n }\n\n try {\n // Mark that we've attempted initialization\n initializationAttemptedRef.current = true;\n\n // Set initialization flag immediately to prevent race conditions\n hasInitializedRef.current = true;\n\n // Set client flag\n setIsClient(true);\n\n // Check for stored conversation before initializing\n const { shouldRestore, conversationId, messages } = checkForStoredConversation(organizationKey);\n\n if (shouldRestore && conversationId && messages) {\n // Store the restored messages to use as context\n setRestoredMessages(messages);\n console.log('GuideAI: Restored conversation with', messages.length, 'messages');\n } else {\n // No valid conversation to restore\n setRestoredMessages([]);\n console.log('GuideAI: No conversation to restore');\n }\n\n // Create new conversation (gets conversation + token + prompt)\n handleCreateNewConversation().then(conversationData => {\n if (!isUnmountingRef.current) {\n console.log('GuideAI: Initialization completed successfully', { conversationData: !!conversationData });\n }\n }).catch(error => {\n if (!isUnmountingRef.current) {\n console.error('GuideAI: Failed to create conversation:', error);\n // Reset initialization flag on error to allow retry\n hasInitializedRef.current = false;\n initializationAttemptedRef.current = false;\n }\n });\n\n const hasInteractedBefore = localStorage.getItem('guideAI_hasInteracted');\n if (!hasInteractedBefore) {\n setHasInteracted(false);\n } else {\n setHasInteracted(true);\n }\n\n console.log('GuideAI: Initialization setup completed');\n } catch (error) {\n console.error('GuideAI: Error during initialization:', error);\n // Reset initialization flag on error\n hasInitializedRef.current = false;\n initializationAttemptedRef.current = false;\n }\n }, [organizationKey]); // Removed handleCreateNewConversation from dependencies\n\n // Component cleanup effect\n hooks.useEffect(() => {\n return () => {\n console.log('GuideAI: Component unmounting - cleanup triggered', {\n hasInitialized: hasInitializedRef.current,\n isUnmounting: isUnmountingRef.current,\n timestamp: Date.now()\n });\n\n // Set unmounting flag to prevent any new initialization\n isUnmountingRef.current = true;\n\n // Clean up WebRTC connections\n cleanupWebRTC();\n\n // Clean up audio element\n if (audioElementRef.current) {\n try {\n // Clean up enhanced audio context if it exists\n if ((audioElementRef.current as any)._audioContext) {\n try {\n (audioElementRef.current as any)._audioContext.close();\n console.log('GuideAI: Enhanced audio context cleaned up');\n } catch (contextError) {\n console.warn('Error cleaning up audio context:', contextError);\n }\n }\n\n audioElementRef.current.srcObject = null;\n audioElementRef.current.remove();\n } catch (error) {\n console.warn('Error cleaning up audio element:', error);\n }\n audioElementRef.current = null;\n }\n\n // Stop event tracking on cleanup\n if (eventTracker) {\n try {\n eventTracker.stopTracking();\n } catch (error) {\n console.warn('Error stopping event tracker:', error);\n }\n }\n\n // Stop metadata tracking and sync any pending updates\n if (metadataTracker) {\n try {\n metadataTracker.destroy();\n } catch (error) {\n console.warn('Error destroying metadata tracker:', error);\n }\n }\n\n // Reset all flags on cleanup\n hasInitializedRef.current = false;\n isUnmountingRef.current = false;\n hasAttemptedAutoStartRef.current = false;\n hasCreatedConversationRef.current = false;\n initializationAttemptedRef.current = false;\n\n console.log('GuideAI: Cleanup completed');\n };\n }, []);\n\n // Metadata sync effect - IMPROVED WITH LONGER INTERVALS\n hooks.useEffect(() => {\n if (!metadataTracker) return;\n\n // Use improved default interval (5 minutes) but allow overrides\n const syncInterval = metadataOptions?.config?.syncInterval || 300000; // 5 minutes instead of 2 minutes\n\n const syncTimer = setInterval(async () => {\n if (metadataTracker) {\n const pendingUpdates = metadataTracker.syncNow();\n if (pendingUpdates.length > 0) {\n try {\n await sendMetadataUpdates(pendingUpdates, organizationKey, onError);\n\n // Notify parent component of metadata updates if callback provided\n if (metadataOptions?.onMetadataUpdate) {\n metadataOptions.onMetadataUpdate(metadataTracker.getMetadata());\n }\n } catch (error) {\n console.error('Failed to sync metadata updates:', error);\n // Note: Failed updates are retained in tracker for retry\n }\n }\n }\n }, syncInterval);\n\n return () => {\n clearInterval(syncTimer);\n };\n }, [organizationKey]); // Only depend on organizationKey\n\n // Auto-restart effect - TEMPORARILY DISABLED FOR TESTING\n hooks.useEffect(() => {\n // TESTING: Disable auto-restart to see if this fixes mounting/unmounting loop\n if (ephemeralToken && restoredMessages.length > 0 && !hasAttemptedAutoStartRef.current && !isConversationActive) {\n console.log('Auto-restarting conversation with', restoredMessages.length, 'restored messages');\n hasAttemptedAutoStartRef.current = true;\n\n startConversationSession().then(success => {\n if (success) {\n setIsConversationActive(true);\n setHasInteracted(true);\n localStorage.setItem('guideAI_hasInteracted', 'true');\n }\n });\n }\n }, [ephemeralToken, restoredMessages, isConversationActive]); // Add isConversationActive to prevent loops\n\n // Load restored messages into allMessages state - FIXED\n hooks.useEffect(() => {\n if (restoredMessages.length > 0 && allMessages.length === 0) {\n setAllMessages(restoredMessages);\n }\n }, [restoredMessages]); // Remove allMessages.length to prevent loops\n\n // Update popup position when needed - STABILIZED\n hooks.useEffect(() => {\n if (showOnboarding || (!hasInteracted && ephemeralToken)) {\n // Use onboarding dimensions if onboarding is shown, otherwise welcome bubble dimensions\n const height = showOnboarding ? 240 : 50;\n const spacing = showOnboarding ? 20 : 15;\n setPopupPosition(getOptimalPosition(height, spacing));\n }\n }, [showOnboarding, hasInteracted, ephemeralToken]); // Remove getOptimalPosition from dependencies\n\n // ===== TRANSCRIPT BOX PROPS MEMOIZATION =====\n\n // Memoize TranscriptBox props to prevent unnecessary re-renders\n const transcriptBoxProps = hooks.useMemo(() => ({\n messages: allMessages,\n isVisible: showTranscript,\n onClose: handleCloseTranscript,\n showToggleButton: transcriptOptions?.showToggleButton !== false && isConversationActive,\n onToggle: handleToggleTranscript,\n showTextInput: isConversationActive && inputOptions?.enableTextInput !== false,\n textInput: textInput,\n onTextInputChange: setTextInput,\n onTextSubmit: handleTextSubmit,\n onTextKeyPress: handleTextKeyPress,\n textPlaceholder: inputOptions?.placeholder || \"Type your message...\"\n }), [\n allMessages.length, // Only depend on length, not the full array\n showTranscript,\n isConversationActive,\n textInput,\n transcriptOptions?.showToggleButton,\n inputOptions?.enableTextInput,\n inputOptions?.placeholder\n ]);\n\n\n\n // ===== METADATA HELPER FUNCTIONS =====\n\n // Expose metadata tracking methods globally on window for external access - STABILIZED\n hooks.useEffect(() => {\n if (typeof window !== 'undefined') {\n // Create global GuideAI metadata API\n (window as any).GuideAI = {\n ...(window as any).GuideAI,\n metadata: {\n // Track user login\n trackLogin: (additionalInfo?: Partial<UserMetadata>) => {\n metadataTracker?.trackLogin(additionalInfo);\n },\n\n // Update user information\n updateUserInfo: (userInfo: Partial<UserMetadata>) => {\n metadataTracker?.updateUserInfo(userInfo);\n },\n\n // Track custom events\n trackCustomEvent: (eventType: string, customData: Record<string, any>) => {\n metadataTracker?.trackCustomEvent(eventType, customData);\n },\n\n // Get current metadata\n getMetadata: () => {\n return metadataTracker?.getMetadata() || null;\n },\n\n // Force sync metadata now\n syncMetadata: async () => {\n if (metadataTracker) {\n const updates = metadataTracker.syncNow();\n if (updates.length > 0) {\n return await sendMetadataUpdates(updates, organizationKey, onError);\n }\n }\n return true;\n },\n\n // Reset session visit tracking (call this when user logs in)\n resetSessionVisitTracking: () => {\n metadataTracker?.resetSessionVisitTracking();\n },\n\n // Manually track a visit (useful for login events)\n trackVisitManually: () => {\n metadataTracker?.trackVisitManually();\n }\n },\n transcript: {\n // Toggle transcript visibility\n toggle: () => {\n setShowTranscript(prev => !prev);\n },\n\n // Show transcript\n show: () => {\n setShowTranscript(true);\n },\n\n // Hide transcript\n hide: () => {\n setShowTranscript(false);\n },\n\n // Get current transcript visibility\n isVisible: () => {\n return showTranscript;\n }\n },\n input: {\n // Toggle input mode between voice and text\n toggleMode: () => {\n handleToggleInputMode();\n },\n\n // Set input mode\n setMode: (mode: 'voice' | 'text') => {\n setInputMode(mode);\n if (mode === 'text' && inputOptions?.enableTextInput !== false && isConversationActive) {\n setShowTextInput(true);\n } else {\n setShowTextInput(false);\n }\n },\n\n // Get current input mode\n getMode: () => {\n return inputMode;\n },\n\n // Send text message programmatically\n sendText: (text: string) => {\n if (text.trim() && isConversationActive) {\n setTextInput(text);\n handleTextSubmit();\n }\n }\n }\n };\n }\n }, [organizationKey]); // Only depend on organizationKey\n\n if (!isClient) {\n return null;\n }\n\n // Determine if we should use fixed positioning (when position props are provided)\n const shouldUseFixedPosition = position && Object.keys(position).length > 0;\n\n // Default positioning for bottom middle of screen\n const defaultPosition = {\n bottom: '20px',\n left: '50%',\n transform: 'translateX(-50%)'\n };\n\n // Base styles for the component\n const baseStyles: React.CSSProperties = {\n position: 'fixed',\n zIndex: 1000,\n ...(shouldUseFixedPosition ? position : defaultPosition),\n };\n\n return (\n <>\n <div\n ref={componentRef}\n style={baseStyles}\n >\n <div className=\"guideai-main-ui\">\n {!hasInteracted && ephemeralToken && (\n <WelcomeBubble position={popupPosition} React={React} />\n )}\n\n <Onboarding\n position={popupPosition}\n isVisible={showOnboarding}\n onComplete={handleOnboardingComplete}\n onClose={handleOnboardingClose}\n React={React}\n />\n\n <div className=\"guideai-main-controls\">\n <div\n className={`guideai-icon-wrapper ${isConnecting ? 'initializing' : status}`}\n onClick={isConnecting ? undefined : handleToggleConversation}\n style={{\n ...(isConnecting ? { cursor: 'default' } : {})\n }}\n title={isConnecting ? 'Initializing...' :\n status === 'idle' ? 'Click to start conversation' :\n status === 'recording' ? 'Click to end conversation' :\n status === 'processing' ? 'Processing your message...' :\n 'AI is speaking, click to end conversation'}\n >\n {isConnecting && <i className=\"guideai-icon initializing\" />}\n {!isConnecting && status === 'idle' && <i className=\"guideai-icon microphone\" />}\n {!isConnecting && status === 'recording' && <i className=\"guideai-icon recording\" />}\n {!isConnecting && status === 'processing' && <i className=\"guideai-icon processing\" />}\n {!isConnecting && status === 'playing' && <i className=\"guideai-icon playing\" />}\n </div>\n\n </div>\n\n </div>\n </div>\n\n {/* Transcript Box */}\n <TranscriptBox {...transcriptBoxProps} React={React} />\n </>\n );\n};\n\nconst GuideAI = GuideAIComponent;\n\nexport default GuideAI;","// Workflow utility functions\nimport { Workflow } from './api';\n\nexport interface WorkflowTrigger {\n triggerWords: string[];\n workflowKey: string;\n description?: string;\n}\n\nexport interface WorkflowLogEvent {\n type: 'workflow_init' | 'trigger_detected' | 'workflow_activated';\n timestamp: string;\n data: any;\n}\n\n/**\n * Format a workflow log message for console output\n */\nexport const formatWorkflowLog = (event: WorkflowLogEvent): string => {\n const timestamp = new Date(event.timestamp).toLocaleTimeString();\n \n switch (event.type) {\n case 'workflow_init':\n return `🔧 [${timestamp}] Workflows initialized: ${event.data.activeWorkflows} active workflow(s)`;\n case 'trigger_detected':\n return `🎯 [${timestamp}] Trigger words detected: ${event.data.triggeredWorkflows.map((w: any) => w.name).join(', ')}`;\n case 'workflow_activated':\n return `🚀 [${timestamp}] Workflow activated: ${event.data.workflowName}`;\n default:\n return `[${timestamp}] ${JSON.stringify(event.data)}`;\n }\n};\n\n/**\n * Log workflow event to console with formatting\n */\nexport const logWorkflowEvent = (event: WorkflowLogEvent): void => {\n console.log(formatWorkflowLog(event));\n};\n\n/**\n * Detect which workflows should be triggered based on a user message\n * @param message The user's message\n * @param workflows Array of available workflows\n * @returns Array of workflows that should be triggered\n */\nexport const detectWorkflowTriggers = (message: string, workflows: Workflow[]): Workflow[] => {\n const normalizedMessage = message.toLowerCase().trim();\n const triggeredWorkflows: Workflow[] = [];\n \n for (const workflow of workflows) {\n if (workflow.archived || workflow.triggers.length === 0) continue;\n \n for (const trigger of workflow.triggers) {\n const normalizedTrigger = trigger.toLowerCase().trim();\n if (normalizedMessage.includes(normalizedTrigger)) {\n triggeredWorkflows.push(workflow);\n break; // Only add each workflow once\n }\n }\n }\n \n return triggeredWorkflows;\n};\n\n/**\n * Get trigger words from a message that match a specific workflow\n * @param message The user's message\n * @param workflow The workflow to check against\n * @returns Array of detected trigger words for this workflow\n */\nexport const getWorkflowTriggerWords = (message: string, workflow: Workflow): string[] => {\n const normalizedMessage = message.toLowerCase().trim();\n const detected: string[] = [];\n \n for (const trigger of workflow.triggers) {\n const normalizedTrigger = trigger.toLowerCase().trim();\n if (normalizedMessage.includes(normalizedTrigger)) {\n detected.push(trigger);\n }\n }\n \n return detected;\n};\n\n/**\n * Check if a message contains any trigger words for any workflow\n * @param message The user's message\n * @param workflows Array of available workflows\n * @returns True if any workflow triggers are found\n */\nexport const hasWorkflowTriggers = (message: string, workflows: Workflow[]): boolean => {\n return detectWorkflowTriggers(message, workflows).length > 0;\n};\n\n/**\n * Get the most relevant workflow for a message\n * @param message The user's message\n * @param workflows Array of available workflows\n * @returns The most relevant workflow or null if none found\n */\nexport const getMostRelevantWorkflow = (message: string, workflows: Workflow[]): Workflow | null => {\n const triggered = detectWorkflowTriggers(message, workflows);\n if (triggered.length === 0) return null;\n \n // Return the workflow with the most specific trigger (longest trigger word)\n return triggered.reduce((mostRelevant, current) => {\n const currentMaxTrigger = Math.max(...current.triggers.map(t => t.length));\n const mostRelevantMaxTrigger = Math.max(...mostRelevant.triggers.map(t => t.length));\n return currentMaxTrigger > mostRelevantMaxTrigger ? current : mostRelevant;\n });\n};\n\n/**\n * Legacy function for backward compatibility\n * @deprecated Use detectWorkflowTriggers instead\n */\nexport const detectTriggerWords = (message: string, triggerWords: string[]): string[] => {\n const normalizedMessage = message.toLowerCase().trim();\n const detected: string[] = [];\n \n for (const trigger of triggerWords) {\n const normalizedTrigger = trigger.toLowerCase().trim();\n if (normalizedMessage.includes(normalizedTrigger)) {\n detected.push(trigger);\n }\n }\n \n return detected;\n};\n\n/**\n * Legacy function for backward compatibility\n * @deprecated Use hasWorkflowTriggers instead\n */\nexport const hasTriggerWords = (message: string, triggerWords: string[]): boolean => {\n return detectTriggerWords(message, triggerWords).length > 0;\n};\n\n/**\n * Legacy function for backward compatibility\n * @deprecated Use getMostRelevantWorkflow instead\n */\nexport const getMostRelevantTrigger = (message: string, triggerWords: string[]): string | null => {\n const detected = detectTriggerWords(message, triggerWords);\n if (detected.length === 0) return null;\n \n // Return the longest trigger word (more specific) or the first one\n return detected.reduce((longest, current) => \n current.length > longest.length ? current : longest\n );\n};\n","import { PopupPosition } from '../types/GuideAI.types';\n\n// Function to inject CSS styles into the document head\nexport const injectStyles = (css: string, id: string) => {\n if (typeof document === 'undefined') return; // SSR safety\n \n // Check if styles are already injected\n if (document.getElementById(id)) return;\n \n const styleElement = document.createElement('style');\n styleElement.id = id;\n styleElement.textContent = css;\n document.head.appendChild(styleElement);\n};\n\n// Calculate optimal position for popups based on component location\nexport const calculateOptimalPosition = (\n componentRect: DOMRect, \n elementHeight: number = 240, \n spacing: number = 15\n): PopupPosition => {\n const viewportHeight = window.innerHeight;\n \n const spaceAbove = componentRect.top;\n const spaceBelow = viewportHeight - componentRect.bottom;\n \n const canFitBelow = spaceBelow >= (elementHeight + spacing);\n const canFitAbove = spaceAbove >= (elementHeight + spacing);\n \n if (canFitBelow) {\n return 'below';\n } else if (canFitAbove) {\n return 'above';\n } else {\n return spaceBelow > spaceAbove ? 'below' : 'above';\n }\n}; ","// Create hover effects and dispatch hover event on an element\nexport const createHoverEffect = async (element: Element, rect: DOMRect): Promise<void> => {\n return new Promise<void>((resolve) => {\n setTimeout(() => {\n // Get fresh rect in case element has expanded since initial measurement\n const currentRect = element.getBoundingClientRect();\n const hoverEffectContainer = document.createElement('div');\n hoverEffectContainer.style.cssText = `\n position: fixed;\n left: ${currentRect.left}px;\n top: ${currentRect.top}px;\n width: ${currentRect.width}px;\n height: ${currentRect.height}px;\n z-index: 999;\n pointer-events: none;\n border: 2px solid rgba(255, 165, 0, 0.8);\n border-radius: 4px;\n background: rgba(255, 165, 0, 0.1);\n box-shadow: 0 0 10px rgba(255, 165, 0, 0.6);\n animation: hover-pulse 0.8s ease-in-out infinite;\n `;\n document.body.appendChild(hoverEffectContainer);\n\n // Create floating hover indicator\n const hoverIndicator = document.createElement('div');\n hoverIndicator.style.cssText = `\n position: fixed;\n left: ${currentRect.left + currentRect.width / 2}px;\n top: ${currentRect.top - 30}px;\n background: rgba(255, 165, 0, 0.9);\n color: white;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n font-family: Arial, sans-serif;\n font-weight: bold;\n z-index: 1000;\n pointer-events: none;\n transform: translateX(-50%);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: hover-indicator-fade 0.5s ease-in forwards;\n `;\n hoverIndicator.textContent = 'HOVER';\n document.body.appendChild(hoverIndicator);\n\n const mouseEnterEvent = new MouseEvent('mouseenter', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n const mouseOverEvent = new MouseEvent('mouseover', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n\n console.log('Hovering over element:', element);\n element.dispatchEvent(mouseEnterEvent);\n element.dispatchEvent(mouseOverEvent);\n\n // Temporarily apply a subtle highlight to the element itself\n const originalBoxShadow = (element as HTMLElement).style.boxShadow;\n const originalTransition = (element as HTMLElement).style.transition;\n const originalBackground = (element as HTMLElement).style.background;\n const originalZIndex = (element as HTMLElement).style.zIndex;\n\n (element as HTMLElement).style.transition = 'all 0.3s ease-out';\n (element as HTMLElement).style.boxShadow = '0 0 0 2px rgba(255, 165, 0, 0.6), 0 0 15px rgba(255, 165, 0, 0.4)';\n (element as HTMLElement).style.background = (element as HTMLElement).style.background + ', rgba(255, 165, 0, 0.05)';\n (element as HTMLElement).style.zIndex = '998'; // Ensure it's above other content but below cursor\n\n setTimeout(() => {\n (element as HTMLElement).style.boxShadow = originalBoxShadow;\n (element as HTMLElement).style.transition = originalTransition;\n (element as HTMLElement).style.background = originalBackground;\n (element as HTMLElement).style.zIndex = originalZIndex;\n\n hoverEffectContainer.remove();\n hoverIndicator.remove();\n resolve();\n }, 1000); // Keep hover effect for 3 seconds\n }, 250); // Small delay before applying hover effects\n });\n};\n\n// Create hover effects with position tracking for dynamic elements\nexport const createHoverEffectWithTracking = async (element: Element, initialRect: DOMRect, cursorElement: HTMLElement | null, duration?: number): Promise<void> => {\n return new Promise<void>((resolve) => {\n setTimeout(() => {\n // Get fresh rect in case element has expanded since initial measurement\n let currentRect = element.getBoundingClientRect();\n const hoverEffectContainer = document.createElement('div');\n hoverEffectContainer.style.cssText = `\n position: fixed;\n left: ${currentRect.left}px;\n top: ${currentRect.top}px;\n width: ${currentRect.width}px;\n height: ${currentRect.height}px;\n z-index: 999;\n pointer-events: none;\n border: 2px solid rgba(255, 165, 0, 0.8);\n border-radius: 4px;\n background: rgba(255, 165, 0, 0.1);\n box-shadow: 0 0 10px rgba(255, 165, 0, 0.6);\n animation: hover-pulse 0.8s ease-in-out infinite;\n transition: all 0.1s ease-out;\n `;\n document.body.appendChild(hoverEffectContainer);\n\n // Create floating hover indicator\n const hoverIndicator = document.createElement('div');\n hoverIndicator.style.cssText = `\n position: fixed;\n left: ${currentRect.left + currentRect.width / 2}px;\n top: ${currentRect.top - 30}px;\n background: rgba(255, 165, 0, 0.9);\n color: white;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n font-family: Arial, sans-serif;\n font-weight: bold;\n z-index: 1000;\n pointer-events: none;\n transform: translateX(-50%);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n animation: hover-indicator-fade 0.5s ease-in forwards;\n transition: all 0.1s ease-out;\n `;\n hoverIndicator.textContent = 'HOVER';\n document.body.appendChild(hoverIndicator);\n\n const mouseEnterEvent = new MouseEvent('mouseenter', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n const mouseOverEvent = new MouseEvent('mouseover', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n\n console.log('Hovering over element with position tracking:', element);\n element.dispatchEvent(mouseEnterEvent);\n element.dispatchEvent(mouseOverEvent);\n\n // Temporarily apply a subtle highlight to the element itself\n const originalBoxShadow = (element as HTMLElement).style.boxShadow;\n const originalTransition = (element as HTMLElement).style.transition;\n const originalBackground = (element as HTMLElement).style.background;\n const originalZIndex = (element as HTMLElement).style.zIndex;\n\n (element as HTMLElement).style.transition = 'all 0.3s ease-out';\n (element as HTMLElement).style.boxShadow = '0 0 0 2px rgba(255, 165, 0, 0.6), 0 0 15px rgba(255, 165, 0, 0.4)';\n (element as HTMLElement).style.background = (element as HTMLElement).style.background + ', rgba(255, 165, 0, 0.05)';\n (element as HTMLElement).style.zIndex = '998'; // Ensure it's above other content but below cursor\n\n // Position tracking interval\n const trackingInterval = setInterval(() => {\n const newRect = element.getBoundingClientRect();\n \n // Check if position or size changed significantly (more than 2px to avoid tiny fluctuations)\n if (Math.abs(newRect.left - currentRect.left) > 2 || \n Math.abs(newRect.top - currentRect.top) > 2 ||\n Math.abs(newRect.width - currentRect.width) > 2 ||\n Math.abs(newRect.height - currentRect.height) > 2) {\n \n console.log('Element position changed, updating cursor and hover effects');\n currentRect = newRect;\n \n // Update hover effect container position\n hoverEffectContainer.style.left = `${currentRect.left}px`;\n hoverEffectContainer.style.top = `${currentRect.top}px`;\n hoverEffectContainer.style.width = `${currentRect.width}px`;\n hoverEffectContainer.style.height = `${currentRect.height}px`;\n \n // Update hover indicator position\n hoverIndicator.style.left = `${currentRect.left + currentRect.width / 2}px`;\n hoverIndicator.style.top = `${currentRect.top - 30}px`;\n \n // Update cursor position if it exists\n if (cursorElement) {\n cursorElement.style.left = `${currentRect.left + currentRect.width / 2}px`;\n cursorElement.style.top = `${currentRect.top + currentRect.height / 2 - 10}px`;\n }\n }\n }, 50); // Check every 50ms for position changes\n\n setTimeout(() => {\n clearInterval(trackingInterval);\n \n (element as HTMLElement).style.boxShadow = originalBoxShadow;\n (element as HTMLElement).style.transition = originalTransition;\n (element as HTMLElement).style.background = originalBackground;\n (element as HTMLElement).style.zIndex = originalZIndex;\n\n hoverEffectContainer.remove();\n hoverIndicator.remove();\n resolve();\n }, duration || 3000); // Keep hover effect for specified duration or 3 seconds\n }, 250); // Small delay before applying hover effects\n });\n};\n\n// Parse custom selector format: \"text:cssSelector:textContent\"\nconst parseCustomSelector = (selector: string): { type: 'text' | 'css' | 'xpath', cssSelector?: string, textContent?: string, originalSelector?: string } => {\n // Check if it's a text selector format: \"text:cssSelector:textContent\"\n if (selector.startsWith('text:')) {\n const parts = selector.split(':');\n if (parts.length >= 3) {\n const cssSelector = parts[1];\n const textContent = parts.slice(2).join(':'); // Rejoin in case text contains colons\n return { type: 'text', cssSelector, textContent };\n }\n }\n \n // Check if it's XPath\n if (selector.startsWith('//')) {\n return { type: 'xpath', originalSelector: selector };\n }\n \n // Default to CSS selector\n return { type: 'css', originalSelector: selector };\n};\n\n// Find element using custom selector logic\nconst findElementBySelector = (selector: string): Element | null => {\n const parsed = parseCustomSelector(selector);\n \n switch (parsed.type) {\n case 'text':\n if (!parsed.cssSelector || !parsed.textContent) return null;\n \n // Find all elements matching the CSS selector\n const elements = document.querySelectorAll(parsed.cssSelector);\n \n // Filter by text content\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n const textContent = element.textContent?.trim();\n if (textContent === parsed.textContent.trim()) {\n return element;\n }\n }\n \n // If exact match not found, try finding parent elements that contain the text\n // This handles cases where the text is in a child element\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n const parent = element.closest('button, [role=\"button\"], a, [onclick]') || element.parentElement;\n if (parent && parent.textContent?.trim().includes(parsed.textContent.trim())) {\n return parent;\n }\n }\n \n return null;\n \n case 'xpath':\n if (!parsed.originalSelector) return null;\n const node = document.evaluate(parsed.originalSelector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\n return node instanceof Element ? node : null;\n \n case 'css':\n default:\n if (!parsed.originalSelector) return null;\n return document.querySelector(parsed.originalSelector);\n }\n};\n\n// Highlight then click elements on the page with animated cursor\nexport const highlightThenClick = async (\n selector: string | string[],\n isHovering: boolean,\n setIsHovering: (hovering: boolean) => void,\n logMessage: (content: string, sender: 'GUIDEAI' | 'HUMAN') => void,\n hoverTime?: number\n): Promise<boolean> => {\n if (isHovering) return false;\n \n const selectors = Array.isArray(selector) ? selector : [selector];\n if (selectors.length === 0) return false;\n \n console.log('Moving cursor to highlight elements, then click last element:', selectors);\n \n let cursorElement: HTMLElement | null = null;\n let lastElement: Element | null = null;\n \n try {\n setIsHovering(true);\n \n // First, highlight all elements\n for (let i = 0; i < selectors.length; i++) {\n const currentSelector = selectors[i];\n \n if (i > 0) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n \n // Use enhanced element finding logic\n const element = findElementBySelector(currentSelector);\n \n if (!element) {\n console.log('Element not found:', currentSelector);\n logMessage('Element not found: ' + currentSelector, 'GUIDEAI');\n continue; // Continue to next selector instead of breaking\n }\n \n // Store the last found element for clicking\n lastElement = element;\n \n // Scroll element into view if it's not visible\n element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' });\n \n // Wait a moment for scrolling to complete\n await new Promise(resolve => setTimeout(resolve, 300));\n \n // Get rect after scrolling\n let rect = element.getBoundingClientRect();\n \n // For collapsed elements, we might need to trigger hover first to get accurate dimensions\n const isLikelyCollapsed = rect.width < 50 || rect.height < 20;\n \n if (isLikelyCollapsed) {\n // Dispatch initial hover events to potentially expand the element\n const mouseEnterEvent = new MouseEvent('mouseenter', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n element.dispatchEvent(mouseEnterEvent);\n \n // Wait for potential expansion animation\n await new Promise(resolve => setTimeout(resolve, 200));\n \n // Get updated rect after potential expansion\n rect = element.getBoundingClientRect();\n }\n \n // Create cursor if it's the first element, or reuse existing cursor\n if (!cursorElement) {\n cursorElement = document.createElement('div');\n cursorElement.id = 'guide-ai-cursor';\n \n const blueCursorSvg = `\n <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path fill=\"#0066ff\" d=\"M7,2l12,11.2l-5.8,0.5l3.3,7.3l-2.2,1l-3.2-7.4L7,18.5V2\" />\n </svg>\n `;\n \n cursorElement.innerHTML = blueCursorSvg;\n cursorElement.style.cssText = `\n position: fixed;\n width: 64px;\n height: 64px;\n pointer-events: none;\n z-index: 9999;\n transition: all 0.8s ease-in-out;\n transform: translate(-50%, 0);\n filter: drop-shadow(0 0 4px rgba(0, 102, 255, 0.8));\n `;\n \n document.body.appendChild(cursorElement);\n \n // First element starts from center of viewport\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n cursorElement.style.left = `${viewportWidth / 2}px`;\n cursorElement.style.top = `${viewportHeight / 2}px`;\n }\n \n // Force reflow to ensure transition works\n cursorElement!.offsetHeight;\n \n await new Promise<void>(resolve => {\n setTimeout(() => {\n cursorElement!.style.left = `${rect.left + rect.width / 2}px`;\n cursorElement!.style.top = `${rect.top + rect.height / 2 - 10}px`;\n \n setTimeout(() => {\n cursorElement!.style.animation = 'cursor-hover-float 2s ease-in-out infinite';\n resolve();\n }, 800);\n }, 100);\n });\n \n // Create hover effect on the element with position tracking (no click yet)\n await createHoverEffectWithTracking(element, rect, cursorElement, hoverTime || 3000);\n }\n \n // After highlighting all elements, click the last element\n if (lastElement) {\n console.log('All elements highlighted. Now clicking the last element:', lastElement);\n \n // Add a small delay before clicking the last element\n // Use shorter delay when no custom hover time is provided\n const clickDelay = hoverTime ?? 1000;\n await new Promise(resolve => setTimeout(resolve, clickDelay));\n \n // Get the final position of the last element\n const finalRect = lastElement.getBoundingClientRect();\n \n // Move cursor to the final element if needed\n if (cursorElement) {\n cursorElement.style.left = `${finalRect.left + finalRect.width / 2}px`;\n cursorElement.style.top = `${finalRect.top + finalRect.height / 2 - 10}px`;\n }\n \n // Create click effect\n const clickEvent = new MouseEvent('click', {\n view: window,\n bubbles: true,\n cancelable: true,\n });\n \n // Check for clickable subelements (links, buttons, inputs)\n const clickableElements = lastElement.querySelectorAll('a, button, input[type=\"button\"], input[type=\"submit\"]');\n const clickableElement = clickableElements.length > 0 ? clickableElements[0] as HTMLElement : lastElement as HTMLElement;\n console.log('Clicking last element:', clickableElement);\n clickableElement.dispatchEvent(clickEvent);\n }\n\n setTimeout(() => {\n cursorElement?.remove();\n setIsHovering(false);\n }, 1000);\n \n return true;\n } catch (error) {\n console.error('Error highlighting elements:', error);\n cursorElement?.remove();\n setIsHovering(false);\n return false;\n }\n};\n","import { StoredMessage } from '../utils/messageStorage';\n\ninterface TranscriptBoxProps {\n messages: StoredMessage[];\n isVisible: boolean;\n onClose: () => void;\n showToggleButton?: boolean;\n onToggle?: () => void;\n showTextInput?: boolean;\n textInput?: string;\n onTextInputChange?: (value: string) => void;\n onTextSubmit?: () => void;\n onTextKeyPress?: (event: any) => void;\n textPlaceholder?: string;\n React: typeof import('react');\n}\n\nconst TranscriptBoxComponent = ({ \n messages, \n isVisible, \n onClose, \n showToggleButton = true, \n onToggle,\n showTextInput = false,\n textInput = '',\n onTextInputChange,\n onTextSubmit,\n onTextKeyPress,\n textPlaceholder = \"Type your message...\",\n React\n}: TranscriptBoxProps) => {\n const formatTime = (timestamp: number) => {\n return new Date(timestamp).toLocaleTimeString([], { \n hour: '2-digit', \n minute: '2-digit' \n });\n };\n\n const scrollToBottom = (element: HTMLDivElement) => {\n element.scrollTop = element.scrollHeight;\n };\n\n React.useEffect(() => {\n const transcriptContainer = document.getElementById('guideai-transcript-container');\n if (transcriptContainer) {\n scrollToBottom(transcriptContainer as HTMLDivElement);\n }\n }, [messages.length]); // Only depend on length, not the full messages array\n\n // Show toggle button if there are messages and toggle is enabled, even when transcript is hidden\n const shouldShowToggleButton = showToggleButton && messages.length > 0;\n \n // Show transcript if visible OR if text input should be shown\n const shouldShowTranscript = isVisible || showTextInput;\n \n // Don't render anything if no messages, no toggle button, and no text input\n if (messages.length === 0 && !shouldShowToggleButton && !showTextInput) return null;\n\n return (\n <div className=\"guideai-transcript-overlay\">\n {/* Toggle Button - shows even when transcript is hidden */}\n {shouldShowToggleButton && (\n <button\n className=\"guideai-transcript-toggle-button\"\n onClick={onToggle}\n title={isVisible ? 'Hide Transcript' : 'Show Transcript'}\n >\n <span className=\"guideai-transcript-toggle-icon\">\n {isVisible ? '📄' : '📋'}\n </span>\n </button>\n )}\n \n {/* Transcript Box - show when visible or when text input is needed */}\n {shouldShowTranscript && (\n <div className=\"guideai-transcript-box\">\n {/* Messages Container - only show when transcript is visible */}\n {isVisible && (\n <div \n id=\"guideai-transcript-container\"\n className=\"guideai-transcript-messages\"\n >\n {messages.length === 0 ? (\n <div className=\"guideai-transcript-empty\">\n <div className=\"guideai-transcript-empty-icon\">🎤</div>\n <p>Start a conversation to see the transcript here</p>\n </div>\n ) : (\n messages.map((message: StoredMessage, index: number) => (\n <div \n key={`${message.timestamp}-${index}`}\n className={`guideai-transcript-message ${message.sender.toLowerCase()}`}\n >\n <div className=\"guideai-transcript-message-content\">\n <div className=\"guideai-transcript-message-sender\">\n {message.sender === 'HUMAN' ? 'You' : 'GuideAI'}\n </div>\n <div className=\"guideai-transcript-message-text\">\n {message.content}\n </div>\n <div className=\"guideai-transcript-message-time\">\n {formatTime(message.timestamp)}\n </div>\n </div>\n </div>\n ))\n )}\n </div>\n )}\n \n {/* Text Input - show when in text mode */}\n {showTextInput && (\n <div className=\"guideai-transcript-text-input\">\n <textarea\n className=\"guideai-transcript-input-field\"\n value={textInput}\n onChange={(e) => onTextInputChange?.(e.target.value)}\n onKeyPress={onTextKeyPress}\n placeholder={textPlaceholder}\n rows={2}\n />\n <button\n className=\"guideai-transcript-send-button\"\n onClick={onTextSubmit}\n disabled={!textInput.trim()}\n title=\"Send Message\"\n >\n <span className=\"guideai-transcript-send-icon\">📤</span>\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n\nconst TranscriptBox = (props: TranscriptBoxProps) => {\n return TranscriptBoxComponent(props);\n};\n\nTranscriptBox.displayName = 'TranscriptBox';\n\nexport default TranscriptBox; ","/**\n * Standardized logging utility for GuideAI package\n * Provides consistent formatting and development gating across all components\n */\n\ntype LogLevel = 'log' | 'warn' | 'error';\ntype Component = 'GuideAI' | 'EventTracker' | 'UserMetadata' | 'API' | 'TranscriptBox' | 'Onboarding';\n\nclass Logger {\n private static formatMessage(component: Component, action: string): string {\n return `[GuideAI:${component}] ${action}`;\n }\n\n /**\n * Standard info logging - only in development\n */\n static log(component: Component, action: string, data?: any): void {\n if (process.env.NODE_ENV === 'development') {\n if (data !== undefined) {\n console.log(this.formatMessage(component, action), data);\n } else {\n console.log(this.formatMessage(component, action));\n }\n }\n }\n\n /**\n * Warning logging - always shown\n */\n static warn(component: Component, action: string, data?: any): void {\n if (data !== undefined) {\n console.warn(this.formatMessage(component, action), data);\n } else {\n console.warn(this.formatMessage(component, action));\n }\n }\n\n /**\n * Error logging - always shown\n */\n static error(component: Component, action: string, error?: any): void {\n if (error !== undefined) {\n console.error(this.formatMessage(component, action), error);\n } else {\n console.error(this.formatMessage(component, action));\n }\n }\n\n /**\n * API call logging with method and endpoint\n */\n static apiCall(method: string, endpoint: string, data?: any): void {\n if (process.env.NODE_ENV === 'development') {\n const action = `${method} ${endpoint}`;\n if (data !== undefined) {\n console.log(this.formatMessage('API', action), data);\n } else {\n console.log(this.formatMessage('API', action));\n }\n }\n }\n\n /**\n * API response logging\n */\n static apiResponse(method: string, endpoint: string, success: boolean, data?: any): void {\n if (process.env.NODE_ENV === 'development') {\n const status = success ? '✅ SUCCESS' : '❌ FAILED';\n const action = `${method} ${endpoint} ${status}`;\n if (data !== undefined) {\n console.log(this.formatMessage('API', action), data);\n } else {\n console.log(this.formatMessage('API', action));\n }\n }\n }\n\n /**\n * Conversation flow tracking\n */\n static conversation(action: string, data?: any): void {\n if (process.env.NODE_ENV === 'development') {\n if (data !== undefined) {\n console.log(this.formatMessage('GuideAI', action), data);\n } else {\n console.log(this.formatMessage('GuideAI', action));\n }\n }\n }\n\n /**\n * Event tracking logging\n */\n static event(action: string, data?: any): void {\n // Temporarily removed development gate for debugging\n if (data !== undefined) {\n console.log(this.formatMessage('EventTracker', action), data);\n } else {\n console.log(this.formatMessage('EventTracker', action));\n }\n }\n\n /**\n * Metadata tracking logging\n */\n static metadata(action: string, data?: any): void {\n // Temporarily removed development gate for debugging\n if (data !== undefined) {\n console.log(this.formatMessage('UserMetadata', action), data);\n } else {\n console.log(this.formatMessage('UserMetadata', action));\n }\n }\n}\n\nexport default Logger;\n","import { PopupPosition } from '../types/GuideAI.types';\n\ninterface OnboardingProps {\n position: PopupPosition;\n isVisible: boolean;\n onComplete: () => void; // Called when user completes onboarding and wants to start conversation\n onClose: () => void; // Called when user closes onboarding without completing\n React: typeof import('react');\n}\n\nconst Onboarding = ({ position, isVisible, onComplete, onClose, React }: OnboardingProps) => {\n const [step, setStep] = React.useState(1);\n\n const handleNext = () => {\n if (step < 3) {\n setStep(step + 1);\n } else {\n // Reset step and complete onboarding\n setStep(1);\n onComplete();\n }\n };\n\n const handleClose = () => {\n // Reset step when closing\n setStep(1);\n onClose();\n };\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <div className={`guideai-onboarding ${position}`}>\n <div className=\"guideai-onboarding-content\">\n <button className=\"guideai-onboarding-close\" onClick={handleClose}>×</button>\n \n {step === 1 && (\n <div className=\"guideai-onboarding-step\">\n <div className=\"guideai-onboarding-icon volume-icon\">🔊</div>\n <h3>Turn on your volume</h3>\n <p>Make sure your device's volume is turned on so you can hear Guide AI's responses.</p>\n </div>\n )}\n \n {step === 2 && (\n <div className=\"guideai-onboarding-step\">\n <div className=\"guideai-onboarding-icon mic-icon\">🎤</div>\n <h3>Allow microphone access</h3>\n <p>When prompted, click \"Allow\" to let Guide AI access your microphone.</p>\n </div>\n )}\n \n {step === 3 && (\n <div className=\"guideai-onboarding-step\">\n <div className=\"guideai-onboarding-icon ready-icon\">✅</div>\n <h3>You're all set!</h3>\n <p>Click the microphone icon to start asking questions.</p>\n </div>\n )}\n \n <div className=\"guideai-onboarding-dots\">\n <span className={`dot ${step === 1 ? 'active' : ''}`}></span>\n <span className={`dot ${step === 2 ? 'active' : ''}`}></span>\n <span className={`dot ${step === 3 ? 'active' : ''}`}></span>\n </div>\n \n <button className=\"guideai-onboarding-next\" onClick={handleNext}>\n {step < 3 ? 'Next' : 'Get Started'}\n </button>\n </div>\n </div>\n );\n};\n\nexport default Onboarding; ","import { PopupPosition } from '../types/GuideAI.types';\n\ninterface WelcomeBubbleProps {\n position: PopupPosition;\n React: typeof import('react');\n}\n\nconst WelcomeBubble = ({ position, React }: WelcomeBubbleProps) => {\n return (\n <div className={`guideai-welcome-bubble ${position}`}>\n Stuck? Click here and ask me a question\n </div>\n );\n};\n\nexport default WelcomeBubble; ","// Event tracking system for GuideAI\n// Tracks click, focus, change, and submit events on all relevant elements\n\nimport Logger from '../utils/logger';\nimport { GUIDE_AI_API_BASE } from '../utils/constants';\n\ninterface EventData {\n type: 'click' | 'focus' | 'change' | 'submit' | 'route_change';\n element?: Element;\n tagName?: string;\n className?: string;\n id?: string;\n textContent?: string;\n value?: string;\n formData?: FormData;\n timestamp: number;\n url: string;\n previousUrl?: string;\n event?: Event;\n // Customer metadata\n customerId?: string;\n customerType?: string;\n customerSegment?: string;\n // Conversation-based session tracking\n conversationStartTime?: number;\n timeSinceConversationStart?: number;\n deviceType?: 'desktop' | 'mobile' | 'tablet';\n userAgent?: string;\n screenResolution?: string;\n // Application context\n currentPage?: string;\n userRole?: string;\n organizationId?: string;\n // Geographic and timezone\n timezone?: string;\n locale?: string;\n}\n\nclass EventTracker {\n private isTracking: boolean = false;\n private eventData: EventData[] = [];\n private batchSize: number = 25; // Increased from 10 to 25 events\n private batchTimeout: number = 15000; // Increased from 5s to 15s\n private batchTimer: NodeJS.Timeout | null = null;\n private pendingEvents: EventData[] = [];\n private currentUrl: string = '';\n private organizationKey?: string;\n private lastEventTime: number = 0;\n private eventThrottleInterval: number = 500; // Minimum 500ms between similar events\n private lastEventsByType: Map<string, number> = new Map();\n private duplicateEventBuffer: Map<string, EventData> = new Map();\n private maxDuplicateBuffer: number = 100;\n private isInitialized: boolean = false;\n\n // Conversation-based timing (replaces session management)\n private conversationStartTime: number | null = null;\n\n // Page unload and inactivity tracking\n private inactivityTimer: NodeJS.Timeout | null = null;\n private inactivityTimeout: number = 60 * 60 * 1000; // 60 minutes\n private lastActivityTime: number = Date.now();\n\n // Customer metadata storage\n private customerMetadata: {\n customerId?: string;\n customerType?: string;\n customerSegment?: string;\n userRole?: string;\n organizationId?: string;\n } = {};\n\n constructor(options?: { \n customerId?: string; \n customerType?: string; \n organizationId?: string; \n organizationKey?: string;\n batchSize?: number;\n batchTimeout?: number;\n eventThrottleInterval?: number;\n }) {\n if (options?.customerId) {\n this.customerMetadata.customerId = String(options.customerId);\n }\n if (options?.customerType) {\n this.customerMetadata.customerType = options.customerType;\n }\n if (options?.organizationId) {\n this.customerMetadata.organizationId = String(options.organizationId);\n }\n if (options?.organizationKey) {\n this.organizationKey = options.organizationKey;\n }\n \n // Allow customization of batching parameters\n if (options?.batchSize && options.batchSize > 0) {\n this.batchSize = options.batchSize;\n }\n if (options?.batchTimeout && options.batchTimeout > 0) {\n this.batchTimeout = options.batchTimeout;\n }\n if (options?.eventThrottleInterval && options.eventThrottleInterval >= 0) {\n this.eventThrottleInterval = options.eventThrottleInterval;\n }\n \n // Auto-initialize like UserMetadataTracker\n this.init();\n }\n\n // Auto-initialization method\n private init(): void {\n if (this.isInitialized) return;\n \n // Load existing events from localStorage\n this.loadEventsFromStorage();\n \n // Start tracking when DOM is ready\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => this.startTracking());\n } else {\n this.startTracking();\n }\n }\n \n // Setup page unload and inactivity tracking\n this.setupPageUnloadTracking();\n this.setupInactivityTracking();\n \n this.isInitialized = true;\n Logger.event('Auto-initialized and ready');\n }\n\n // Load events from localStorage\n private loadEventsFromStorage(): void {\n if (typeof window === 'undefined') return;\n \n try {\n const storageKey = `guideai_events_${this.organizationKey || 'default'}`;\n const storedEvents = localStorage.getItem(storageKey);\n \n if (storedEvents) {\n const parsedEvents = JSON.parse(storedEvents);\n this.eventData = Array.isArray(parsedEvents) ? parsedEvents : [];\n Logger.event('Loaded events from localStorage', { eventCount: this.eventData.length });\n }\n } catch (error) {\n Logger.warn('EventTracker', 'Failed to load events from localStorage', error);\n }\n }\n\n // Save events to localStorage\n private saveEventsToStorage(): void {\n if (typeof window === 'undefined') return;\n \n try {\n const storageKey = `guideai_events_${this.organizationKey || 'default'}`;\n \n // Sanitize event data before saving to localStorage to prevent circular reference errors\n const sanitizedEventData = this.eventData.map(event => this.sanitizeEventData(event));\n \n Logger.event('Saving events to localStorage', { eventCount: this.eventData.length });\n localStorage.setItem(storageKey, JSON.stringify(sanitizedEventData));\n Logger.event('Successfully saved events to localStorage', { savedCount: sanitizedEventData.length });\n } catch (error) {\n Logger.warn('EventTracker', 'Failed to save events to localStorage', error);\n }\n }\n\n\n // Method to set customer metadata\n public setCustomerMetadata(metadata: Partial<typeof this.customerMetadata>): void {\n // All remaining metadata is safe to collect\n this.customerMetadata = { ...this.customerMetadata, ...metadata };\n }\n\n // Method to get customer metadata\n public getCustomerMetadata(): typeof this.customerMetadata {\n return { ...this.customerMetadata };\n }\n\n // Method to clear customer metadata\n public clearCustomerMetadata(): void {\n this.customerMetadata = {};\n }\n\n public initialize(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n // Start tracking when DOM is ready\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => this.startTracking());\n } else {\n this.startTracking();\n }\n }\n\n // Helper method to enrich event data with customer metadata\n private enrichEventData(baseEventData: Omit<EventData, 'customerId' | 'customerType' | 'customerSegment' | 'sessionId' | 'deviceType' | 'userAgent' | 'screenResolution' | 'currentPage' | 'userRole' | 'organizationId' | 'timezone' | 'locale'>): EventData {\n // Sanitize the base event data to remove circular references\n const sanitizedBaseData = this.sanitizeEventData(baseEventData);\n \n const enrichedData = {\n ...sanitizedBaseData,\n // Customer metadata\n customerId: this.customerMetadata.customerId,\n customerType: this.customerMetadata.customerType,\n customerSegment: this.customerMetadata.customerSegment,\n organizationKey: this.organizationKey,\n // Conversation timing (replaces sessionId)\n ...this.getConversationTiming(),\n deviceType: this.getDeviceType(),\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n screenResolution: typeof screen !== 'undefined' ? `${screen.width}x${screen.height}` : undefined,\n // Application context\n currentPage: window.location.pathname,\n userRole: this.customerMetadata.userRole,\n organizationId: this.customerMetadata.organizationId,\n // Geographic and timezone\n timezone: typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined'\n ? Intl.DateTimeFormat().resolvedOptions().timeZone\n : undefined,\n locale: navigator.language,\n };\n\n return enrichedData;\n }\n\n // Helper method to sanitize event data and remove circular references\n private sanitizeEventData(eventData: any): any {\n const sanitized: any = {};\n \n // Copy safe primitive values\n Object.keys(eventData).forEach(key => {\n const value = eventData[key];\n \n switch (key) {\n case 'element':\n // Extract only safe element properties\n if (value && typeof value === 'object') {\n sanitized[key] = {\n tagName: value.tagName,\n id: value.id,\n className: value.className,\n textContent: value.textContent?.substring(0, 100)\n };\n }\n break;\n \n case 'event':\n // Remove event object entirely - it contains circular references\n break;\n \n case 'formData':\n // Convert FormData to plain object\n if (value instanceof FormData) {\n const formDataObj: Record<string, any> = {};\n value.forEach((val, key) => {\n formDataObj[key] = val;\n });\n sanitized[key] = formDataObj;\n } else {\n sanitized[key] = value;\n }\n break;\n \n default:\n // Copy primitive values and safe objects\n if (value !== null && \n (typeof value === 'string' || \n typeof value === 'number' || \n typeof value === 'boolean' ||\n Array.isArray(value))) {\n sanitized[key] = value;\n }\n break;\n }\n });\n \n return sanitized;\n }\n\n\n\n // Set conversation start time (called by GuideAI when conversation starts)\n public setConversationStartTime(startTime: number): void {\n this.conversationStartTime = startTime;\n Logger.event('Conversation start time set', { startTime: new Date(startTime).toISOString() });\n }\n\n // Get conversation timing data\n private getConversationTiming(): { conversationStartTime?: number; timeSinceConversationStart?: number } {\n if (!this.conversationStartTime) {\n return {};\n }\n \n const now = Date.now();\n return {\n conversationStartTime: this.conversationStartTime,\n timeSinceConversationStart: now - this.conversationStartTime\n };\n }\n\n // Helper method to detect device type\n private getDeviceType(): 'desktop' | 'mobile' | 'tablet' {\n if (typeof navigator === 'undefined') {\n return 'desktop';\n }\n const userAgent = navigator.userAgent.toLowerCase();\n if (/tablet|ipad|playbook|silk/i.test(userAgent)) {\n return 'tablet';\n }\n if (/mobile|android|iphone|ipod|blackberry|opera mini|iemobile/i.test(userAgent)) {\n return 'mobile';\n }\n return 'desktop';\n }\n\n public startTracking(): void {\n if (this.isTracking) return;\n\n this.isTracking = true;\n Logger.event('Started tracking events');\n\n // Track click events on all elements\n this.trackClickEvents();\n\n // Track focus events on focusable elements\n this.trackFocusEvents();\n\n // Track change events on form elements\n this.trackChangeEvents();\n\n // Track submit events on forms\n this.trackSubmitEvents();\n\n // Track route changes\n this.trackRouteChanges();\n }\n\n public stopTracking(): void {\n if (!this.isTracking) return;\n\n this.isTracking = false;\n Logger.event('Stopped tracking events');\n\n // Emit any pending events before stopping\n this.emitBatch();\n\n // Remove all event listeners\n document.removeEventListener('click', this.handleClick, true);\n document.removeEventListener('focus', this.handleFocus, true);\n document.removeEventListener('change', this.handleChange, true);\n document.removeEventListener('submit', this.handleSubmit, true);\n window.removeEventListener('popstate', this.handlePopstate);\n window.removeEventListener('hashchange', this.handleHashChange);\n\n // Cleanup page tracking\n this.cleanupPageTracking();\n }\n\n private trackClickEvents(): void {\n document.addEventListener('click', this.handleClick, true);\n }\n\n private trackFocusEvents(): void {\n document.addEventListener('focus', this.handleFocus, true);\n }\n\n private trackChangeEvents(): void {\n document.addEventListener('change', this.handleChange, true);\n }\n\n private trackSubmitEvents(): void {\n document.addEventListener('submit', this.handleSubmit, true);\n }\n\n private trackRouteChanges(): void {\n // Track browser back/forward navigation\n window.addEventListener('popstate', this.handlePopstate);\n\n // Track hash changes\n window.addEventListener('hashchange', this.handleHashChange);\n\n // Track History API changes (pushState, replaceState)\n this.interceptHistoryAPI();\n }\n\n private handleClick = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n \n // Filter out clicks on non-interactive elements unless they have special attributes\n if (!this.isSignificantClickTarget(target)) {\n return;\n }\n \n const baseEventData = {\n type: 'click' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n value: this.getSafeValue(target),\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n };\n\n private handleFocus = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n\n // Only track focus on significant focusable elements\n if (this.isFocusable(target) && this.isSignificantFocusTarget(target)) {\n const baseEventData = {\n type: 'focus' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n value: this.getSafeValue(target),\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n }\n };\n\n private handleChange = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n\n // Only track change on form elements\n if (this.isFormElement(target)) {\n const baseEventData = {\n type: 'change' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n value: this.getSafeValue(target),\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n }\n };\n\n private handleSubmit = (event: Event): void => {\n if (!this.isTracking) return;\n\n const target = event.target as Element;\n\n // Only track submit on form elements\n if (target.tagName === 'FORM') {\n const form = target as HTMLFormElement;\n const formData = new FormData(form);\n\n const baseEventData = {\n type: 'submit' as const,\n element: target,\n tagName: target.tagName,\n className: target.className,\n id: target.id,\n textContent: target.textContent?.substring(0, 100),\n formData: formData,\n timestamp: Date.now(),\n url: window.location.href,\n event: event\n };\n\n this.logEvent(baseEventData);\n }\n };\n\n private handlePopstate = (event: PopStateEvent): void => {\n if (!this.isTracking) return;\n\n const newUrl = window.location.href;\n const previousUrl = this.getPreviousUrl();\n \n // Only track if URL actually changed meaningfully\n if (this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl,\n event: event\n };\n\n this.logEvent(baseEventData);\n this.currentUrl = newUrl; // Update current URL\n }\n };\n\n private handleHashChange = (event: HashChangeEvent): void => {\n if (!this.isTracking) return;\n\n const newUrl = window.location.href;\n const previousUrl = event.oldURL;\n \n // Only track if hash change is significant\n if (this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl,\n event: event\n };\n\n this.logEvent(baseEventData);\n this.currentUrl = newUrl; // Update current URL\n }\n };\n\n private interceptHistoryAPI(): void {\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n // Store the current URL before interception\n this.currentUrl = window.location.href;\n\n // Intercept pushState\n history.pushState = (...args) => {\n const previousUrl = this.currentUrl;\n const result = originalPushState.apply(history, args);\n const newUrl = window.location.href;\n this.currentUrl = newUrl;\n\n if (this.isTracking && this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl\n };\n this.logEvent(baseEventData);\n }\n\n return result;\n };\n\n // Intercept replaceState\n history.replaceState = (...args) => {\n const previousUrl = this.currentUrl;\n const result = originalReplaceState.apply(history, args);\n const newUrl = window.location.href;\n this.currentUrl = newUrl;\n\n if (this.isTracking && this.isSignificantRouteChange(newUrl, previousUrl)) {\n const baseEventData = {\n type: 'route_change' as const,\n timestamp: Date.now(),\n url: newUrl,\n previousUrl: previousUrl\n };\n this.logEvent(baseEventData);\n }\n\n return result;\n };\n }\n\n private getPreviousUrl(): string {\n return this.currentUrl || window.location.href;\n }\n\n private getSafeValue(element: Element): string | undefined {\n const tagName = element.tagName.toLowerCase();\n const inputElement = element as HTMLInputElement;\n const selectElement = element as HTMLSelectElement;\n\n // Only collect values for elements that are typically useful for targeting\n switch (tagName) {\n case 'button':\n return inputElement.value || inputElement.textContent?.trim();\n\n case 'select':\n return `${selectElement.name || ''}|${selectElement.type || ''}|${selectElement.selectedIndex || 0}`;\n\n case 'textarea':\n return inputElement.name || inputElement.type || '';\n\n case 'input':\n const inputType = inputElement.type;\n // Only collect value for button-like input types\n if (inputType === 'submit' || inputType === 'button' || inputType === 'reset') {\n return `${inputElement.name || ''}|${inputType}|${inputElement.value || ''}`;\n }\n // For other input types, only collect name and type, not the actual value\n return `${inputElement.name || ''}|${inputType}`;\n\n default:\n return undefined;\n }\n }\n\n private isFocusable(element: Element): boolean {\n const focusableSelectors = [\n 'input:not([type=\"hidden\"])',\n 'select',\n 'textarea',\n 'button',\n 'a[href]',\n 'area[href]',\n 'iframe',\n 'object',\n 'embed',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]'\n ];\n\n return focusableSelectors.some(selector => element.matches(selector));\n }\n\n private isFormElement(element: Element): boolean {\n const formElementTags = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'];\n return formElementTags.includes(element.tagName);\n }\n\n // Helper method to generate a unique key for event deduplication\n private generateEventKey(eventData: any): string {\n return `${eventData.type}_${eventData.tagName}_${eventData.id || ''}_${eventData.className || ''}_${eventData.url}`;\n }\n\n // Helper method to check if event should be throttled\n private shouldThrottleEvent(eventData: any, currentTime: number): boolean {\n const lastEventTime = this.lastEventsByType.get(eventData.type) || 0;\n return (currentTime - lastEventTime) < this.eventThrottleInterval;\n }\n\n // Helper method to check for duplicate events\n private isDuplicateEvent(eventKey: string, eventData: any): boolean {\n const existingEvent = this.duplicateEventBuffer.get(eventKey);\n if (!existingEvent) return false;\n \n // Consider events duplicate if they're within 2 seconds and have same content\n const timeDiff = eventData.timestamp - existingEvent.timestamp;\n return timeDiff < 2000 && existingEvent.textContent === eventData.textContent;\n }\n\n // Helper method to determine if click target is significant\n private isSignificantClickTarget(element: Element): boolean {\n const significantTags = ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'];\n const interactiveRoles = ['button', 'link', 'menuitem', 'tab', 'option'];\n \n // Always track clicks on interactive elements\n if (significantTags.includes(element.tagName)) {\n return true;\n }\n \n // Track elements with interactive roles\n const role = element.getAttribute('role');\n if (role && interactiveRoles.includes(role.toLowerCase())) {\n return true;\n }\n \n // Track elements with click handlers (has onclick or data-* attributes)\n if (element.hasAttribute('onclick') || \n Array.from(element.attributes).some(attr => attr.name.startsWith('data-'))) {\n return true;\n }\n \n // Track elements with cursor pointer style\n const style = window.getComputedStyle(element);\n if (style.cursor === 'pointer') {\n return true;\n }\n \n return false;\n }\n\n // Helper method to determine if focus target is significant\n private isSignificantFocusTarget(element: Element): boolean {\n // Only track focus on form elements and elements with tabindex\n const formElements = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'];\n if (formElements.includes(element.tagName)) {\n return true;\n }\n \n // Track elements with positive tabindex\n const tabindex = element.getAttribute('tabindex');\n if (tabindex && parseInt(tabindex) >= 0) {\n return true;\n }\n \n return false;\n }\n\n // Helper method to determine if route change is significant\n private isSignificantRouteChange(newUrl: string, previousUrl: string): boolean {\n if (!newUrl || !previousUrl || newUrl === previousUrl) {\n return false;\n }\n \n try {\n const newUrlObj = new URL(newUrl);\n const prevUrlObj = new URL(previousUrl);\n \n // Ignore query parameter only changes\n if (newUrlObj.pathname === prevUrlObj.pathname && \n newUrlObj.hash === prevUrlObj.hash) {\n return false;\n }\n \n // Ignore small hash changes (like anchor scrolling)\n if (newUrlObj.pathname === prevUrlObj.pathname && \n Math.abs(newUrlObj.hash.length - prevUrlObj.hash.length) < 5) {\n return false;\n }\n \n return true;\n } catch (error) {\n // If URL parsing fails, consider it significant to be safe\n return true;\n }\n }\n\n private logEvent(baseEventData: Omit<EventData, 'customerType' | 'customerSegment' | 'sessionId' | 'deviceType' | 'currentPage' | 'userRole' | 'locale'>): void {\n const now = Date.now();\n const eventKey = this.generateEventKey(baseEventData);\n \n // Throttle similar events\n if (this.shouldThrottleEvent(baseEventData, now)) {\n return;\n }\n \n // Check for duplicate events\n if (this.isDuplicateEvent(eventKey, baseEventData)) {\n return;\n }\n \n // Enrich the event data with customer metadata\n const enrichedEventData = this.enrichEventData(baseEventData);\n\n // Store event data for historical access (with size limit)\n this.eventData.push(enrichedEventData);\n if (this.eventData.length > 1000) { // Limit stored events\n this.eventData = this.eventData.slice(-500); // Keep last 500\n }\n\n // Add to pending batch\n this.pendingEvents.push(enrichedEventData);\n \n // Update throttle tracking\n this.lastEventsByType.set(baseEventData.type, now);\n this.duplicateEventBuffer.set(eventKey, enrichedEventData);\n \n // Clean duplicate buffer if it gets too large\n if (this.duplicateEventBuffer.size > this.maxDuplicateBuffer) {\n const oldestKeys = Array.from(this.duplicateEventBuffer.keys()).slice(0, 50);\n oldestKeys.forEach(key => this.duplicateEventBuffer.delete(key));\n }\n\n // Save to localStorage immediately for persistence\n this.saveEventsToStorage();\n\n // Note: API emission now only happens on page unload or 60min inactivity\n // Removed automatic batch emission to reduce API calls\n // Events are stored locally and will be sent when user navigates away or after inactivity\n }\n\n // startBatchTimer method removed - no longer using automatic batch emission\n\n private emitBatch(): void {\n Logger.event('emitBatch called', { \n pendingEventsCount: this.pendingEvents.length,\n pendingEvents: this.pendingEvents.map(e => ({ type: e.type, timestamp: e.timestamp }))\n });\n \n if (this.pendingEvents.length === 0) {\n Logger.event('No pending events to emit');\n return;\n }\n\n // Prevent duplicate API calls - if we're already emitting, skip\n if ((this as any)._isEmitting) {\n Logger.event('Already emitting batch, skipping duplicate call');\n return;\n }\n (this as any)._isEmitting = true;\n\n // Create batch payload\n const events = [...this.pendingEvents];\n const batchPayload = {\n events,\n batchSize: events.length,\n timestamp: Date.now(),\n };\n\n // Log the batch emission\n Logger.event('Emitting batch', { \n eventCount: events.length, \n organizationKey: this.organizationKey,\n batchSize: events.length \n });\n\n // Save events to localStorage\n this.saveEventsToStorage();\n\n // Send to API\n this.sendEventBatch(events);\n\n // Emit custom event for metadata tracking integration\n if (typeof window !== 'undefined') {\n window.dispatchEvent(new CustomEvent('guideai:event_batch', {\n detail: batchPayload\n }));\n }\n\n // Clear pending events\n this.pendingEvents = [];\n\n // Note: batchTimer removed - no longer using automatic batch emission\n }\n\n // Send event batch to API\n private async sendEventBatch(events: EventData[]): Promise<void> {\n if (!this.organizationKey) {\n Logger.warn('EventTracker', 'Cannot send events - no organizationKey set');\n return;\n }\n\n try {\n const requestData = {\n organizationKey: this.organizationKey,\n events,\n batchTimestamp: Date.now(),\n eventCount: events.length\n };\n\n Logger.apiCall('POST', '/events', requestData);\n\n const response = await fetch(`${GUIDE_AI_API_BASE}/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestData)\n });\n\n if (!response.ok) {\n Logger.apiResponse('POST', '/events', false, { status: response.status });\n throw new Error(`Failed to send event batch: ${response.status} ${response.statusText}`);\n }\n\n Logger.apiResponse('POST', '/events', true, { eventCount: events.length });\n } catch (error) {\n Logger.error('EventTracker', 'Failed to send event batch to API', error);\n // Don't throw - we want event tracking to continue even if API fails\n } finally {\n // Reset the emitting flag to allow future API calls\n (this as any)._isEmitting = false;\n }\n }\n\n public getEventData(): EventData[] {\n return [...this.eventData];\n }\n\n public clearEventData(): void {\n this.eventData = [];\n }\n\n public getEventDataByType(type: EventData['type']): EventData[] {\n return this.eventData.filter(event => event.type === type);\n }\n\n public getEventDataByElement(tagName: string): EventData[] {\n return this.eventData.filter(event => event.tagName === tagName.toUpperCase());\n }\n\n public getEventDataByUrl(url: string): EventData[] {\n return this.eventData.filter(event => event.url.includes(url));\n }\n\n public emitPendingEvents(): void {\n this.emitBatch();\n }\n\n public setBatchConfig(batchSize: number, batchTimeout: number): void {\n this.batchSize = batchSize;\n this.batchTimeout = batchTimeout;\n }\n\n public getPendingEventsCount(): number {\n return this.pendingEvents.length;\n }\n\n // Customer analytics methods\n public getCustomerAnalytics(): {\n totalEvents: number;\n eventsByCustomer: Record<string, number>;\n eventsByType: Record<string, number>;\n eventsByPage: Record<string, number>;\n eventsByDevice: Record<string, number>;\n sessionDuration: number;\n lastActivity: number;\n } {\n const now = Date.now();\n const sessionStart = this.getSessionStartTime();\n const sessionDuration = now - sessionStart;\n\n const analytics = {\n totalEvents: this.eventData.length,\n eventsByCustomer: {} as Record<string, number>,\n eventsByType: {} as Record<string, number>,\n eventsByPage: {} as Record<string, number>,\n eventsByDevice: {} as Record<string, number>,\n sessionDuration,\n lastActivity: this.eventData.length > 0 ? this.eventData[this.eventData.length - 1].timestamp : now\n };\n\n // Group events by customer\n this.eventData.forEach(event => {\n if (event.customerId) {\n analytics.eventsByCustomer[event.customerId] = (analytics.eventsByCustomer[event.customerId] || 0) + 1;\n }\n\n // Group events by type\n analytics.eventsByType[event.type] = (analytics.eventsByType[event.type] || 0) + 1;\n\n // Group events by page\n if (event.currentPage) {\n analytics.eventsByPage[event.currentPage] = (analytics.eventsByPage[event.currentPage] || 0) + 1;\n }\n\n // Group events by device\n if (event.deviceType) {\n analytics.eventsByDevice[event.deviceType] = (analytics.eventsByDevice[event.deviceType] || 0) + 1;\n }\n });\n\n return analytics;\n }\n\n // Get session start time\n private getSessionStartTime(): number {\n const sessionStart = sessionStorage.getItem('eventTracker_sessionStart');\n if (!sessionStart) {\n const startTime = Date.now();\n sessionStorage.setItem('eventTracker_sessionStart', startTime.toString());\n return startTime;\n }\n return parseInt(sessionStart, 10);\n }\n\n // Method to identify customer from DOM or context\n public identifyCustomerFromContext(): void {\n // Try to find customer information from the current page context\n // This could be from meta tags, data attributes, or other DOM elements\n\n\n\n // Check for user role in meta tags\n const userRoleMeta = document.querySelector('meta[name=\"user-role\"]');\n if (userRoleMeta) {\n this.setCustomerMetadata({ userRole: userRoleMeta.getAttribute('content') || undefined });\n }\n\n\n\n // Try to infer customer type from URL or page context\n const pathname = window.location.pathname;\n if (pathname.includes('/admin/')) {\n this.setCustomerMetadata({ customerType: 'admin' });\n } else if (pathname.includes('/agent/')) {\n this.setCustomerMetadata({ customerType: 'agent' });\n } else if (pathname.includes('/business/')) {\n this.setCustomerMetadata({ customerType: 'business' });\n } else if (pathname.includes('/individual/')) {\n this.setCustomerMetadata({ customerType: 'individual' });\n }\n }\n\n // Method to set customer metadata from external source (e.g., authentication system)\n public setCustomerFromAuth(authData: {\n id?: string;\n role?: string;\n customerType?: 'individual' | 'business' | 'agent' | 'admin';\n customerSegment?: string;\n }): void {\n this.setCustomerMetadata({\n userRole: authData.role,\n customerType: authData.customerType,\n customerSegment: authData.customerSegment\n });\n }\n\n // Setup page unload tracking\n private setupPageUnloadTracking(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n Logger.event('Page unload detected - emitting pending events');\n // Force emit any pending events before page unload\n this.emitBatch();\n };\n\n window.addEventListener('beforeunload', handleBeforeUnload);\n \n // Store the listener for cleanup\n (this as any)._beforeUnloadListener = handleBeforeUnload;\n }\n\n // Setup inactivity tracking\n private setupInactivityTracking(): void {\n if (typeof window === 'undefined') return;\n\n const resetInactivityTimer = () => {\n this.lastActivityTime = Date.now();\n \n // Clear existing timer\n if (this.inactivityTimer) {\n clearTimeout(this.inactivityTimer);\n }\n \n // Set new timer for 60 minutes\n this.inactivityTimer = setTimeout(() => {\n Logger.event('60 minutes of inactivity detected - emitting pending events');\n this.emitBatch();\n }, this.inactivityTimeout);\n };\n\n // Track various user activities\n const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n \n activityEvents.forEach(eventType => {\n document.addEventListener(eventType, resetInactivityTimer, true);\n });\n\n // Initialize the timer\n resetInactivityTimer();\n \n // Store the listeners for cleanup\n (this as any)._activityListeners = { resetInactivityTimer, activityEvents };\n }\n\n // Cleanup method for page unload and inactivity tracking\n private cleanupPageTracking(): void {\n if (typeof window === 'undefined') return;\n\n // Remove beforeunload listener\n if ((this as any)._beforeUnloadListener) {\n window.removeEventListener('beforeunload', (this as any)._beforeUnloadListener);\n }\n\n // Remove activity listeners\n const listeners = (this as any)._activityListeners;\n if (listeners) {\n listeners.activityEvents.forEach((eventType: string) => {\n document.removeEventListener(eventType, listeners.resetInactivityTimer, true);\n });\n }\n\n // Clear inactivity timer\n if (this.inactivityTimer) {\n clearTimeout(this.inactivityTimer);\n this.inactivityTimer = null;\n }\n }\n\n}\n\nexport default EventTracker;\nexport type { EventData };\n\n// Export customer metadata types for external use\nexport interface CustomerMetadata {\n customerId?: string;\n customerType?: 'individual' | 'business' | 'agent' | 'admin';\n customerSegment?: string;\n userRole?: string;\n}\n\nexport interface CustomerAnalytics {\n totalEvents: number;\n eventsByCustomer: Record<string, number>;\n eventsByType: Record<string, number>;\n eventsByPage: Record<string, number>;\n eventsByDevice: Record<string, number>;\n sessionDuration: number;\n lastActivity: number;\n}\n","// User metadata tracking system for GuideAI\n// \n// ✅ SESSION MANAGEMENT IMPLEMENTATION COMPLETE:\n// - API calls only on page navigation or 60min inactivity\n// - Duplicate call prevention with _isEmitting guards\n// - Old 5-minute sync timer disabled\n// - Page unload and inactivity tracking implemented\n// - Perfect timing control achieved\n//\n// This implementation meets all business requirements for stable\n// session management and controlled API call frequency.\n// Tracks user visits, logins, and other metadata for Overproof integration\n\nimport { \n UserMetadata, \n MetadataConfig, \n MetadataStorageData, \n MetadataEvent, \n MetadataEventType,\n MetadataUpdate \n} from '../types/metadata.types';\nimport { sendMetadataUpdates } from '../utils/api';\nimport Logger from '../utils/logger';\n\nconst METADATA_STORAGE_KEY = 'guideai_user_metadata';\nconst METADATA_VERSION = '1.0.0';\nconst SESSION_VISIT_KEY = 'guideai_session_visit';\n\nclass UserMetadataTracker {\n private config: Required<MetadataConfig>;\n private metadata: UserMetadata;\n private syncTimer: NodeJS.Timeout | null = null;\n private pendingUpdates: MetadataUpdate[] = [];\n private isInitialized: boolean = false;\n private onError?: (error: Error, context: string) => void;\n private lastSyncTime: number = 0;\n private minSyncInterval: number = 60000; // Minimum 1 minute between syncs\n private lastMetadataHash: string = '';\n private pendingUpdateTypes: Set<string> = new Set();\n\n // Page unload and inactivity tracking\n private inactivityTimer: NodeJS.Timeout | null = null;\n private inactivityTimeout: number = 60 * 60 * 1000; // 60 minutes\n private lastActivityTime: number = Date.now();\n\n constructor(organizationKey: string, config: MetadataConfig = {}, onError?: (error: Error, context: string) => void) {\n Logger.metadata('Constructor called', { organizationKey, config });\n \n this.config = {\n trackVisits: true,\n trackLogins: true,\n syncInterval: 300000, // Increased to 5 minutes instead of 30 seconds\n storage: 'localStorage',\n customFields: [],\n collectBrowserInfo: true,\n collectUserAgent: true,\n ...config\n };\n\n this.metadata = {\n organizationKey,\n visitCount: 0,\n loginCount: 0\n };\n \n this.onError = onError;\n\n // Don't call init() immediately - let the component control initialization\n // this.init();\n \n Logger.metadata('Constructor completed', { metadata: this.metadata });\n }\n\n public init(): void {\n Logger.metadata('init() method called', { isInitialized: this.isInitialized });\n \n if (this.isInitialized) {\n Logger.metadata('Already initialized, skipping');\n return;\n }\n\n Logger.metadata('Starting initialization...');\n\n // Load existing metadata from storage\n this.loadMetadata();\n\n // Collect browser info if enabled\n if (this.config.collectBrowserInfo) {\n this.collectBrowserInfo();\n }\n\n // Track initial visit if enabled and not already tracked this session\n if (this.config.trackVisits) {\n this.trackVisitOncePerSession();\n }\n\n // Start sync timer - DISABLED: Using page unload + inactivity instead\n // this.startSyncTimer();\n\n // Listen for EventTracker events to detect user interactions\n this.setupEventTrackerIntegration();\n\n // Setup page unload and inactivity tracking\n this.setupPageUnloadTracking();\n this.setupInactivityTracking();\n\n this.isInitialized = true;\n Logger.metadata('Initialized successfully', this.metadata);\n }\n\n public updateUserInfo(userInfo: Partial<UserMetadata>): void {\n const timestamp = Date.now();\n \n // Check if the update is actually different\n if (this.isDataDuplicate('user_info', userInfo)) {\n return;\n }\n \n // Update metadata\n this.metadata = {\n ...this.metadata,\n ...userInfo,\n lastVisit: timestamp\n };\n\n // Add to pending updates with deduplication\n this.addPendingUpdate({\n type: 'user_info',\n timestamp,\n data: userInfo\n });\n\n // Save to storage\n this.saveMetadata();\n\n Logger.metadata('User info updated', userInfo);\n }\n\n public trackLogin(additionalInfo?: Partial<UserMetadata>): void {\n if (!this.config.trackLogins) return;\n\n const timestamp = Date.now();\n \n // Check if this is a duplicate login event (within 30 seconds)\n const lastLoginTime = this.metadata.lastLogin || 0;\n if (timestamp - lastLoginTime < 30000) {\n Logger.metadata('Ignoring duplicate login event');\n return;\n }\n \n this.metadata = {\n ...this.metadata,\n ...additionalInfo,\n loginCount: (this.metadata.loginCount || 0) + 1,\n lastLogin: timestamp,\n lastVisit: timestamp\n };\n\n this.addPendingUpdate({\n type: 'login',\n timestamp,\n data: {\n loginCount: this.metadata.loginCount,\n lastLogin: timestamp,\n ...additionalInfo\n }\n });\n\n this.saveMetadata();\n Logger.metadata('Login tracked', { loginCount: this.metadata.loginCount });\n }\n\n private trackVisitOncePerSession(): void {\n if (!this.config.trackVisits) return;\n\n // Check if we've already tracked a visit this session\n const sessionKey = `${SESSION_VISIT_KEY}_${this.metadata.organizationKey}`;\n const hasTrackedThisSession = typeof window !== 'undefined' && sessionStorage.getItem(sessionKey);\n \n // Check if EventTracker has created a new session\n const eventTrackerSessionId = typeof window !== 'undefined' && sessionStorage.getItem('eventTracker_sessionId');\n const lastTrackedSessionId = typeof window !== 'undefined' && sessionStorage.getItem(`${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`);\n \n // If EventTracker has a new session, reset our visit tracking\n if (eventTrackerSessionId && eventTrackerSessionId !== lastTrackedSessionId) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: New EventTracker session detected, resetting visit tracking');\n }\n if (typeof window !== 'undefined') {\n sessionStorage.removeItem(sessionKey);\n sessionStorage.setItem(`${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`, eventTrackerSessionId);\n }\n }\n \n if (hasTrackedThisSession) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Visit already tracked this session, skipping');\n }\n return;\n }\n\n const timestamp = Date.now();\n const isFirstVisit = !this.metadata.firstVisit;\n\n this.metadata = {\n ...this.metadata,\n firstVisit: this.metadata.firstVisit || timestamp,\n lastVisit: timestamp,\n visitCount: (this.metadata.visitCount || 0) + 1\n };\n\n this.addPendingUpdate({\n type: 'visit',\n timestamp,\n data: {\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit,\n visitCount: this.metadata.visitCount\n }\n });\n\n // Mark that we've tracked a visit this session\n if (typeof window !== 'undefined') {\n sessionStorage.setItem(sessionKey, timestamp.toString());\n // Store the current EventTracker session ID\n const eventTrackerSessionId = sessionStorage.getItem('eventTracker_sessionId');\n if (eventTrackerSessionId) {\n sessionStorage.setItem(`${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`, eventTrackerSessionId);\n }\n }\n\n this.saveMetadata();\n \n if (process.env.NODE_ENV === 'development') {\n console.log(`UserMetadataTracker: ${isFirstVisit ? 'First' : 'Return'} visit tracked`, {\n visitCount: this.metadata.visitCount,\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit\n });\n }\n }\n\n // Legacy method - now calls the session-based version\n public trackVisit(): void {\n this.trackVisitOncePerSession();\n }\n\n // Method to reset session visit tracking (call this when user logs in)\n public resetSessionVisitTracking(): void {\n const sessionKey = `${SESSION_VISIT_KEY}_${this.metadata.organizationKey}`;\n const sessionIdKey = `${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`;\n if (typeof window !== 'undefined') {\n sessionStorage.removeItem(sessionKey);\n sessionStorage.removeItem(sessionIdKey);\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Session visit tracking reset');\n }\n }\n }\n\n // Method to manually track a visit (useful for login events)\n public trackVisitManually(): void {\n if (!this.config.trackVisits) return;\n\n const timestamp = Date.now();\n const isFirstVisit = !this.metadata.firstVisit;\n\n this.metadata = {\n ...this.metadata,\n firstVisit: this.metadata.firstVisit || timestamp,\n lastVisit: timestamp,\n visitCount: (this.metadata.visitCount || 0) + 1\n };\n\n this.addPendingUpdate({\n type: 'visit',\n timestamp,\n data: {\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit,\n visitCount: this.metadata.visitCount\n }\n });\n\n // Mark that we've tracked a visit this session\n const sessionKey = `${SESSION_VISIT_KEY}_${this.metadata.organizationKey}`;\n const sessionIdKey = `${SESSION_VISIT_KEY}_sessionId_${this.metadata.organizationKey}`;\n if (typeof window !== 'undefined') {\n sessionStorage.setItem(sessionKey, timestamp.toString());\n // Store the current EventTracker session ID\n const eventTrackerSessionId = sessionStorage.getItem('eventTracker_sessionId');\n if (eventTrackerSessionId) {\n sessionStorage.setItem(sessionIdKey, eventTrackerSessionId);\n }\n }\n\n this.saveMetadata();\n \n if (process.env.NODE_ENV === 'development') {\n console.log(`UserMetadataTracker: Manual visit tracked`, {\n visitCount: this.metadata.visitCount,\n firstVisit: this.metadata.firstVisit,\n lastVisit: this.metadata.lastVisit\n });\n }\n }\n\n public trackCustomEvent(eventType: string, customData: Record<string, any>): void {\n const timestamp = Date.now();\n\n // Update custom fields if they're in the config\n const customFields = { ...this.metadata.customFields };\n for (const [key, value] of Object.entries(customData)) {\n if (this.config.customFields.includes(key)) {\n customFields[key] = value;\n }\n }\n\n this.metadata = {\n ...this.metadata,\n customFields,\n lastVisit: timestamp\n };\n\n this.addPendingUpdate({\n type: 'custom',\n timestamp,\n data: { customFields }\n });\n\n this.saveMetadata();\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Custom event tracked', eventType, customData);\n }\n }\n\n public getMetadata(): UserMetadata {\n return { ...this.metadata };\n }\n\n public getPendingUpdates(): MetadataUpdate[] {\n return [...this.pendingUpdates];\n }\n\n public clearPendingUpdates(): void {\n this.pendingUpdates = [];\n this.pendingUpdateTypes.clear();\n }\n\n public syncNow(): MetadataUpdate[] {\n const updates = [...this.pendingUpdates];\n this.clearPendingUpdates();\n return updates;\n }\n\n private loadMetadata(): void {\n try {\n const storageData = this.getFromStorage();\n if (storageData && storageData.metadata) {\n // Merge stored metadata with current metadata\n this.metadata = {\n ...this.metadata,\n ...storageData.metadata\n };\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Loaded metadata from storage', this.metadata);\n }\n }\n } catch (error) {\n console.warn('UserMetadataTracker: Failed to load metadata from storage', error);\n // Clear corrupted data\n this.clearStorage();\n }\n }\n\n private saveMetadata(): void {\n try {\n const storageData: MetadataStorageData = {\n metadata: this.metadata,\n lastUpdated: Date.now(),\n version: METADATA_VERSION\n };\n\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: saveMetadata called with', this.metadata);\n }\n \n this.setToStorage(storageData);\n } catch (error) {\n console.warn('UserMetadataTracker: Failed to save metadata to storage', error);\n }\n }\n\n private collectBrowserInfo(): void {\n if (typeof window === 'undefined') return;\n\n const userAgent = navigator.userAgent;\n const browserInfo = this.parseBrowserInfo(userAgent);\n\n this.metadata = {\n ...this.metadata,\n userAgent: this.config.collectUserAgent ? userAgent : undefined,\n browserInfo\n };\n \n // Save the updated metadata to localStorage\n this.saveMetadata();\n }\n\n private parseBrowserInfo(userAgent: string): UserMetadata['browserInfo'] {\n // Simple browser detection - could be enhanced with a proper library\n let name = 'Unknown';\n let version = 'Unknown';\n let platform = 'Unknown';\n\n // Detect browser\n if (userAgent.includes('Chrome') && !userAgent.includes('Edg')) {\n name = 'Chrome';\n const match = userAgent.match(/Chrome\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n } else if (userAgent.includes('Firefox')) {\n name = 'Firefox';\n const match = userAgent.match(/Firefox\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n } else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {\n name = 'Safari';\n const match = userAgent.match(/Version\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n } else if (userAgent.includes('Edg')) {\n name = 'Edge';\n const match = userAgent.match(/Edg\\/([0-9.]+)/);\n version = match ? match[1] : 'Unknown';\n }\n\n // Detect platform\n if (userAgent.includes('Win')) platform = 'Windows';\n else if (userAgent.includes('Mac')) platform = 'macOS';\n else if (userAgent.includes('Linux')) platform = 'Linux';\n else if (userAgent.includes('Android')) platform = 'Android';\n else if (userAgent.includes('iOS')) platform = 'iOS';\n\n return { name, version, platform };\n }\n\n private startSyncTimer(): void {\n if (this.syncTimer) return;\n\n this.syncTimer = setInterval(() => {\n if (this.pendingUpdates.length > 0 && this.shouldSync()) {\n this.emitPendingUpdates();\n }\n }, this.config.syncInterval);\n }\n\n // Helper method to check if sync should occur (respects minimum interval)\n private shouldSync(): boolean {\n const now = Date.now();\n return (now - this.lastSyncTime) >= this.minSyncInterval;\n }\n\n // Helper method to add pending updates with deduplication\n private addPendingUpdate(update: MetadataUpdate): void {\n // Check if we already have a pending update of this type\n if (this.pendingUpdateTypes.has(update.type)) {\n // Replace the existing update of this type with the latest one\n this.pendingUpdates = this.pendingUpdates.filter(u => u.type !== update.type);\n }\n \n this.pendingUpdates.push(update);\n this.pendingUpdateTypes.add(update.type);\n \n // Note: API sync now happens on page unload or 60min inactivity only\n // Removed immediate API sync to reduce API calls\n }\n\n // Helper method to check if data is duplicate\n private isDataDuplicate(type: string, data: any): boolean {\n const currentHash = this.generateDataHash(data);\n const existingUpdate = this.pendingUpdates.find(u => u.type === type);\n \n if (existingUpdate) {\n const existingHash = this.generateDataHash(existingUpdate.data);\n return currentHash === existingHash;\n }\n \n return false;\n }\n\n // Helper method to generate a simple hash for data comparison\n private generateDataHash(data: any): string {\n return JSON.stringify(data, Object.keys(data).sort());\n }\n\n private setupEventTrackerIntegration(): void {\n if (typeof window === 'undefined') return;\n\n let lastActivityUpdate = 0;\n const activityUpdateThrottle = 60000; // Only update activity once per minute\n\n // Listen for EventTracker batches to detect user interactions\n const handleEventBatch = (event: Event) => {\n const customEvent = event as CustomEvent;\n const batchData = customEvent.detail;\n const now = Date.now();\n \n if (batchData && batchData.events) {\n // Look for login-related events (form submissions, clicks on login buttons, etc.)\n const hasLoginEvent = batchData.events.some((evt: any) => \n evt.type === 'submit' || \n (evt.type === 'click' && (\n evt.textContent?.toLowerCase().includes('login') ||\n evt.textContent?.toLowerCase().includes('sign in') ||\n evt.className?.toLowerCase().includes('login') ||\n evt.id?.toLowerCase().includes('login')\n ))\n );\n\n if (hasLoginEvent) {\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Detected potential login event from EventTracker');\n }\n // Note: We don't automatically track login here as it requires explicit confirmation\n // This is just for detecting potential login scenarios\n }\n\n // Track general user activity with throttling\n if (now - lastActivityUpdate > activityUpdateThrottle) {\n this.metadata = {\n ...this.metadata,\n lastVisit: now\n };\n lastActivityUpdate = now;\n }\n }\n };\n\n window.addEventListener('guideai:event_batch', handleEventBatch);\n \n // Store the listener for cleanup\n (this as any)._eventTrackerListener = handleEventBatch;\n }\n\n private stopSyncTimer(): void {\n if (this.syncTimer) {\n clearInterval(this.syncTimer);\n this.syncTimer = null;\n }\n }\n\n private async emitPendingUpdates(): Promise<void> {\n Logger.metadata('emitPendingUpdates called', { \n pendingUpdatesCount: this.pendingUpdates.length,\n pendingUpdates: this.pendingUpdates \n });\n \n if (this.pendingUpdates.length === 0) {\n Logger.metadata('No pending updates to emit');\n return;\n }\n\n // Prevent duplicate API calls - if we're already emitting, skip\n if ((this as any)._isEmitting) {\n Logger.metadata('Already emitting updates, skipping duplicate call');\n return;\n }\n (this as any)._isEmitting = true;\n\n const updates = [...this.pendingUpdates];\n this.lastSyncTime = Date.now();\n \n // Emit the updates (integrate with existing event system)\n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Emitting metadata updates', {\n updateCount: updates.length,\n updates,\n currentMetadata: this.metadata\n });\n }\n\n // Send to backend API\n try {\n if (this.onError) {\n await sendMetadataUpdates(updates, this.metadata.organizationKey, this.onError);\n }\n \n // Clear pending updates after successful emission\n this.clearPendingUpdates();\n } catch (error) {\n console.warn('UserMetadataTracker: Failed to send metadata updates, will retry later', error);\n // Don't clear pending updates so they can be retried\n }\n }\n\n // Immediate API sync method - for development/low-frequency updates\n private async emitPendingUpdatesImmediate(): Promise<void> {\n if (this.pendingUpdates.length === 0) return;\n \n // Rate limiting: Don't sync more than once per 5 seconds\n const now = Date.now();\n if ((now - this.lastSyncTime) < 5000) {\n console.log('UserMetadataTracker: Rate limiting - skipping immediate sync');\n return;\n }\n\n const updates = [...this.pendingUpdates];\n \n Logger.metadata('Immediate sync - sending metadata updates', {\n updateCount: updates.length,\n updates,\n currentMetadata: this.metadata\n });\n\n // Send to backend API immediately\n try {\n if (this.onError) {\n await sendMetadataUpdates(updates, this.metadata.organizationKey, this.onError);\n }\n \n // Clear pending updates after successful emission\n this.clearPendingUpdates();\n this.lastSyncTime = Date.now();\n \n Logger.metadata(`✅ Immediate sync successful - sent ${updates.length} updates`);\n } catch (error) {\n Logger.warn('UserMetadata', 'Immediate sync failed, keeping updates for timer-based retry', error);\n // Don't clear pending updates so the timer-based sync can retry later\n }\n }\n\n private getFromStorage(): MetadataStorageData | null {\n try {\n const key = `${METADATA_STORAGE_KEY}_${this.metadata.organizationKey}`;\n let data: string | null = null;\n\n switch (this.config.storage) {\n case 'localStorage':\n data = typeof window !== 'undefined' ? localStorage.getItem(key) : null;\n break;\n case 'sessionStorage':\n data = typeof window !== 'undefined' ? sessionStorage.getItem(key) : null;\n break;\n case 'memory':\n // For memory storage, we don't persist across sessions\n return null;\n }\n\n return data ? JSON.parse(data) : null;\n } catch (error) {\n console.warn('UserMetadataTracker: Error reading from storage', error);\n return null;\n }\n }\n\n private setToStorage(data: MetadataStorageData): void {\n try {\n const key = `${METADATA_STORAGE_KEY}_${this.metadata.organizationKey}`;\n const serialized = JSON.stringify(data);\n\n Logger.metadata('Saving to storage', { \n storageType: this.config.storage, \n key, \n dataSize: serialized.length \n });\n\n switch (this.config.storage) {\n case 'localStorage':\n if (typeof window !== 'undefined') {\n localStorage.setItem(key, serialized);\n Logger.metadata('Successfully saved to localStorage', { key });\n }\n break;\n case 'sessionStorage':\n if (typeof window !== 'undefined') {\n sessionStorage.setItem(key, serialized);\n Logger.metadata('Successfully saved to sessionStorage', { key });\n }\n break;\n case 'memory':\n // For memory storage, we don't persist\n Logger.metadata('Memory storage - no persistence', { key });\n break;\n }\n } catch (error) {\n Logger.warn('UserMetadata', 'Error writing to storage', error);\n }\n }\n\n private clearStorage(): void {\n try {\n const key = `${METADATA_STORAGE_KEY}_${this.metadata.organizationKey}`;\n\n switch (this.config.storage) {\n case 'localStorage':\n if (typeof window !== 'undefined') {\n localStorage.removeItem(key);\n }\n break;\n case 'sessionStorage':\n if (typeof window !== 'undefined') {\n sessionStorage.removeItem(key);\n }\n break;\n case 'memory':\n // Nothing to clear for memory storage\n break;\n }\n } catch (error) {\n console.warn('UserMetadataTracker: Error clearing storage', error);\n }\n }\n\n // Setup page unload tracking\n private setupPageUnloadTracking(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n Logger.metadata('Page unload detected - emitting pending metadata updates');\n // Force emit any pending updates before page unload\n this.emitPendingUpdates();\n };\n\n window.addEventListener('beforeunload', handleBeforeUnload);\n \n // Store the listener for cleanup\n (this as any)._beforeUnloadListener = handleBeforeUnload;\n }\n\n // Setup inactivity tracking\n private setupInactivityTracking(): void {\n if (typeof window === 'undefined') return;\n\n const resetInactivityTimer = () => {\n this.lastActivityTime = Date.now();\n \n // Clear existing timer\n if (this.inactivityTimer) {\n clearTimeout(this.inactivityTimer);\n }\n \n // Set new timer for 60 minutes\n this.inactivityTimer = setTimeout(() => {\n Logger.metadata('60 minutes of inactivity detected - emitting pending metadata updates');\n this.emitPendingUpdates();\n }, this.inactivityTimeout);\n };\n\n // Track various user activities\n const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n \n activityEvents.forEach(eventType => {\n document.addEventListener(eventType, resetInactivityTimer, true);\n });\n\n // Initialize the timer\n resetInactivityTimer();\n \n // Store the listeners for cleanup\n (this as any)._activityListeners = { resetInactivityTimer, activityEvents };\n }\n\n // Cleanup method for page unload and inactivity tracking\n private cleanupPageTracking(): void {\n if (typeof window === 'undefined') return;\n\n // Remove beforeunload listener\n if ((this as any)._beforeUnloadListener) {\n window.removeEventListener('beforeunload', (this as any)._beforeUnloadListener);\n }\n\n // Remove activity listeners\n const listeners = (this as any)._activityListeners;\n if (listeners) {\n listeners.activityEvents.forEach((eventType: string) => {\n document.removeEventListener(eventType, listeners.resetInactivityTimer, true);\n });\n }\n\n // Clear inactivity timer\n if (this.inactivityTimer) {\n clearTimeout(this.inactivityTimer);\n this.inactivityTimer = null;\n }\n }\n\n public destroy(): void {\n // Emit any pending updates before destroying\n this.emitPendingUpdates();\n \n // Stop sync timer\n this.stopSyncTimer();\n \n // Remove event listener\n if (typeof window !== 'undefined' && (this as any)._eventTrackerListener) {\n window.removeEventListener('guideai:event_batch', (this as any)._eventTrackerListener);\n }\n \n // Cleanup page tracking\n this.cleanupPageTracking();\n \n // Clear state\n this.isInitialized = false;\n this.pendingUpdates = [];\n \n if (process.env.NODE_ENV === 'development') {\n console.log('UserMetadataTracker: Destroyed');\n }\n }\n}\n\nexport default UserMetadataTracker;\nexport type { UserMetadata, MetadataConfig, MetadataUpdate };\n","// Metric exports\nexport { default as EventTracker } from './event-listner';\nexport type { EventData } from './event-listner';\nexport { default as UserMetadataTracker } from './metadata-tracker';\nexport type { UserMetadata, MetadataConfig, MetadataUpdate } from './metadata-tracker';","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(258);\n"],"names":["root","factory","exports","module","define","amd","this","guideAIStyles","createHoverEffect","element","rect","Promise","resolve","setTimeout","currentRect","getBoundingClientRect","hoverEffectContainer","document","createElement","style","cssText","left","top","width","height","body","appendChild","hoverIndicator","textContent","mouseEnterEvent","MouseEvent","view","window","bubbles","cancelable","mouseOverEvent","console","log","dispatchEvent","originalBoxShadow","boxShadow","originalTransition","transition","originalBackground","background","originalZIndex","zIndex","remove","createHoverEffectWithTracking","initialRect","cursorElement","trackingInterval","setInterval","newRect","Math","abs","clearInterval","clickElement","clickEffectContainer","j","clickRipple","clickDot","clickEvent","clickableElements","querySelectorAll","clickableElement","length","hoverThenClick","selector","isHighlighting","setIsHighlighting","logMessage","selectors","Array","isArray","i","currentSelector","parsed","startsWith","parts","split","type","cssSelector","slice","join","originalSelector","parseCustomSelector","elements","trim","closest","parentElement","includes","node","evaluate","XPathResult","FIRST_ORDERED_NODE_TYPE","singleNodeValue","Element","querySelector","findElementBySelector","scrollIntoView","behavior","block","inline","id","innerHTML","viewportWidth","innerWidth","viewportHeight","innerHeight","offsetHeight","animation","error","CONVERSATION_STORAGE_KEY","CONVERSATION_EXPIRY_MINUTES","MAX_STORED_MESSAGES","isLocalStorageAvailable","testKey","localStorage","setItem","removeItem","e","isConversationExpired","conversation","expiryTime","Date","now","timestamp","handleStorageError","operation","DOMException","name","loadConversationFromStorage","messages","saveConversationToStorage","warn","clearConversationStorage","cleanupError","JSON","stringify","storedData","getItem","parsedData","parse","data","conversationId","organizationKey","every","msg","content","sender","addMessageToStorage","message","some","existingMsg","substring","push","checkForStoredConversation","currentOrganizationKey","storedConversation","shouldRestore","formatConversationContext","maxMessages","recentMessages","meaningfulMessages","filter","map","DEFAULT_PROMPT","IGNORE_MESSAGE_TYPES","GUIDE_AI_API_BASE","METADATA_API_BASE","OPENAI_REALTIME_BASE","OPENAI_REALTIME_MODEL","GEMINI_API_KEY","GEMINI_API_ENDPOINT","createNewConversation","onError","workflowKey","requestBody","userId","date","toISOString","time","toTimeString","apiCall","fetch","method","headers","response","ok","text","catch","errorText","apiResponse","status","Error","json","conversationData","requestData","endpoint","statusText","contentLength","sendMetadataUpdates","updates","batchTimestamp","updateCount","props","hooks","position","metadataOptions","transcriptOptions","inputOptions","React","useState","useEffect","useRef","version","useMemo","useCallback","getReactHooks","bottom","transform","color","padding","borderRadius","fontFamily","fontSize","setStatus","isClient","setIsClient","isHovering","setIsHovering","hasInteracted","setHasInteracted","prompt","setPrompt","workflows","setWorkflows","setIsSessionReady","isConnecting","setIsConnecting","showOnboarding","setShowOnboarding","popupPosition","setPopupPosition","conversationIdRef","componentRef","peerConnectionRef","dataChannelRef","hasCreatedConversationRef","mediaStreamRef","audioElementRef","ephemeralToken","setEphemeralToken","hasInitializedRef","isUnmountingRef","isConversationActive","setIsConversationActive","restoredMessages","setRestoredMessages","enabled","showTranscript","setShowTranscript","allMessages","setAllMessages","defaultMode","inputMode","setInputMode","textInput","setTextInput","setShowTextInput","latestScreenshotRef","lastSentImageHashRef","lastImageSendAtRef","isImageSendInFlightRef","pendingImageDetailRef","hasAttemptedAutoStartRef","initializationAttemptedRef","eventTracker","customerId","initialUserData","organizationId","customerType","userType","EventTracker","metadataTracker","UserMetadataTracker","config","geminiFlash","contents","responseJson","candidates","handleCreateNewConversation","current","handleLogMessage","prev","lastMessage","newMessage","handleHoverThenClick","handleHighlightThenClick","hoverTime","highlightThenClick","getOptimalPosition","elementHeight","spacing","calculateOptimalPosition","sendMessage","readyState","send","canSendImageNow","urgent","createImageContentPart","detail","image_url","dataUrl","maybeSendImage","canSend","inFlight","hash","imageMessage","item","role","createUserImageItem","GuideAI","visualContext","markScreenshotSent","sendImageItem","next","cleanupWebRTC","getAudioTracks","forEach","track","stop","close","srcObject","initializeWebRTC","audioStream","RTCPeerConnection","addTrack","audioEl","autoplay","chrome","runtime","crossOrigin","preload","audioContext","AudioContext","webkitAudioContext","gainNode","createGain","gain","value","_audioContext","_gainNode","ontrack","kind","stream","MediaStream","dc","createDataChannel","onopen","sessionConfig","tools","description","parameters","properties","oneOf","items","required","minimum","maximum","tool_choice","workflow","archived","triggers","triggerWords","userMessage","turn_detection","create_response","interrupt_response","input_audio_transcription","model","language","session","onmessage","activeWorkflows","w","initEvent","totalWorkflows","logWorkflowEvent","workflowInitMessage","conversationContext","transcript","triggeredWorkflows","detectWorkflowTriggers","triggerEvent","triggerDetectionMessage","out","arguments","argsToUse","then","replace","parsedArgs","err","find","activationEvent","workflowId","workflowName","workflowActivationMessage","call_id","output","code","fragment","createOffer","offer","setLocalDescription","localDescription","sdp","baseUrl","Authorization","sdpResponse","answerSdp","answer","setRemoteDescription","onScreenshot","ev","anyEv","reason","hasDataUrl","addEventListener","removeEventListener","startConversationSession","navigator","mediaDevices","getUserMedia","audio","echoCancellation","noiseSuppression","autoGainControl","channelCount","sampleRate","endConversationSession","handleToggleTranscript","handleTextSubmit","textTriggerEvent","snap","unsentImage","responseMessage","handleTextKeyPress","event","key","shiftKey","preventDefault","handleCloseTranscript","injectStyles","styleElement","getElementById","styleContent","init","updateUserInfo","hasInitialized","initializationAttempted","hasInteractedBefore","isUnmounting","contextError","stopTracking","destroy","syncInterval","syncTimer","pendingUpdates","syncNow","onMetadataUpdate","getMetadata","success","transcriptBoxProps","isVisible","onClose","showToggleButton","onToggle","showTextInput","enableTextInput","onTextInputChange","onTextSubmit","onTextKeyPress","textPlaceholder","placeholder","metadata","trackLogin","additionalInfo","userInfo","trackCustomEvent","eventType","customData","syncMetadata","resetSessionVisitTracking","trackVisitManually","toggle","show","hide","input","toggleMode","newMode","setMode","mode","getMode","sendText","shouldUseFixedPosition","Object","keys","baseStyles","ref","className","onComplete","onClick","undefined","cursor","title","formatWorkflowLog","toLocaleTimeString","normalizedMessage","toLowerCase","normalizedTrigger","getWorkflowTriggerWords","detected","trigger","hasWorkflowTriggers","getMostRelevantWorkflow","triggered","reduce","mostRelevant","max","t","detectTriggerWords","hasTriggerWords","getMostRelevantTrigger","longest","css","head","componentRect","spaceAbove","spaceBelow","duration","lastElement","finalRect","TranscriptBox","transcriptContainer","scrollTop","scrollHeight","shouldShowToggleButton","shouldShowTranscript","index","hour","minute","onChange","target","onKeyPress","rows","disabled","TranscriptBoxComponent","displayName","formatMessage","component","action","Logger","step","setStep","options","isTracking","eventData","batchSize","batchTimeout","batchTimer","pendingEvents","currentUrl","lastEventTime","eventThrottleInterval","lastEventsByType","Map","duplicateEventBuffer","maxDuplicateBuffer","isInitialized","conversationStartTime","inactivityTimer","inactivityTimeout","lastActivityTime","customerMetadata","handleClick","isSignificantClickTarget","baseEventData","tagName","getSafeValue","url","location","href","logEvent","handleFocus","isFocusable","isSignificantFocusTarget","handleChange","isFormElement","handleSubmit","formData","FormData","handlePopstate","newUrl","previousUrl","getPreviousUrl","isSignificantRouteChange","handleHashChange","oldURL","String","loadEventsFromStorage","startTracking","setupPageUnloadTracking","setupInactivityTracking","storageKey","storedEvents","parsedEvents","eventCount","saveEventsToStorage","sanitizedEventData","sanitizeEventData","savedCount","setCustomerMetadata","getCustomerMetadata","clearCustomerMetadata","initialize","enrichEventData","sanitizedBaseData","customerSegment","getConversationTiming","deviceType","getDeviceType","userAgent","screenResolution","screen","currentPage","pathname","userRole","timezone","Intl","DateTimeFormat","resolvedOptions","timeZone","locale","sanitized","val","setConversationStartTime","startTime","timeSinceConversationStart","test","trackClickEvents","trackFocusEvents","trackChangeEvents","trackSubmitEvents","trackRouteChanges","emitBatch","cleanupPageTracking","interceptHistoryAPI","originalPushState","history","pushState","originalReplaceState","replaceState","result","apply","args","inputElement","selectElement","selectedIndex","inputType","matches","generateEventKey","shouldThrottleEvent","currentTime","get","isDuplicateEvent","eventKey","existingEvent","getAttribute","hasAttribute","from","attributes","attr","getComputedStyle","tabindex","parseInt","newUrlObj","URL","prevUrlObj","enrichedEventData","set","size","delete","pendingEventsCount","_isEmitting","events","batchPayload","sendEventBatch","CustomEvent","getEventData","clearEventData","getEventDataByType","getEventDataByElement","toUpperCase","getEventDataByUrl","emitPendingEvents","setBatchConfig","getPendingEventsCount","getCustomerAnalytics","sessionDuration","getSessionStartTime","analytics","totalEvents","eventsByCustomer","eventsByType","eventsByPage","eventsByDevice","lastActivity","sessionStart","sessionStorage","toString","identifyCustomerFromContext","userRoleMeta","setCustomerFromAuth","authData","handleBeforeUnload","_beforeUnloadListener","resetInactivityTimer","clearTimeout","activityEvents","_activityListeners","listeners","METADATA_STORAGE_KEY","SESSION_VISIT_KEY","lastSyncTime","minSyncInterval","lastMetadataHash","pendingUpdateTypes","Set","trackVisits","trackLogins","storage","customFields","collectBrowserInfo","collectUserAgent","visitCount","loginCount","loadMetadata","trackVisitOncePerSession","setupEventTrackerIntegration","isDataDuplicate","lastVisit","addPendingUpdate","saveMetadata","lastLogin","sessionKey","hasTrackedThisSession","eventTrackerSessionId","lastTrackedSessionId","firstVisit","trackVisit","sessionIdKey","entries","getPendingUpdates","clearPendingUpdates","clear","storageData","getFromStorage","clearStorage","lastUpdated","setToStorage","browserInfo","parseBrowserInfo","platform","match","startSyncTimer","shouldSync","emitPendingUpdates","update","has","u","add","currentHash","generateDataHash","existingUpdate","sort","lastActivityUpdate","handleEventBatch","batchData","evt","_eventTrackerListener","stopSyncTimer","pendingUpdatesCount","emitPendingUpdatesImmediate","currentMetadata","serialized","storageType","dataSize","default","__webpack_module_cache__","__webpack_exports__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","call"],"sourceRoot":""}
|