impact-chatbot 2.3.82 → 2.3.84

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/dist/index.cjs.js CHANGED
@@ -20,9 +20,9 @@ var rehypeSanitize = require('rehype-sanitize');
20
20
  var remarkBreaks = require('remark-breaks');
21
21
  var remarkGfm = require('remark-gfm');
22
22
  var DOMPurify = require('dompurify');
23
+ var reactRouterDomV5Compat = require('react-router-dom-v5-compat');
23
24
  var filterAction = require('core/actions/filterAction');
24
25
  var smartBotActions = require('core/actions/smartBotActions');
25
- var reactRouterDomV5Compat = require('react-router-dom-v5-compat');
26
26
  var RefreshIcon = require('@mui/icons-material/Refresh');
27
27
  var styled = require('styled-components');
28
28
  var material = require('@mui/material');
@@ -348,9 +348,9 @@ const parseResponse = (data, type, agentId = "", currentMode = "", disableTimeAn
348
348
  userType: "bot",
349
349
  userName: userName,
350
350
  bodyText: data?.response,
351
- headerTitle: data?.response_heading,
351
+ headerTitle: "",
352
352
  thinkingResponse: data?.thinkingResponse,
353
- noShowHeaderTitle: false,
353
+ noShowHeaderTitle: true,
354
354
  bodyType: "text",
355
355
  };
356
356
  case "stream":
@@ -614,7 +614,8 @@ const parseResponse = (data, type, agentId = "", currentMode = "", disableTimeAn
614
614
  timeStamp: timeString,
615
615
  userType: "bot",
616
616
  userName: userName,
617
- headerTitle: data?.response_heading || "",
617
+ headerTitle: "",
618
+ noShowHeaderTitle: true,
618
619
  bodyType: "image",
619
620
  bodyText: data?.image || data,
620
621
  };
@@ -1119,6 +1120,66 @@ const getFormattedApplicationName = (applicationURL) => {
1119
1120
  }
1120
1121
  };
1121
1122
 
1123
+ /**
1124
+ * Resolves a link href to an in-app route (path + search + hash) when the link
1125
+ * points to a screen of this application, otherwise returns null.
1126
+ * Handles both relative links ("/inventory-smart/create-allocation?step=0") and
1127
+ * absolute links sent by the backend
1128
+ * ("https://tapestry.test.impactsmartsuite.com/inventory-smart/create-allocation?step=0").
1129
+ */
1130
+ const resolveInternalPath = (href) => {
1131
+ if (!href || typeof href !== 'string')
1132
+ return null;
1133
+ // Non navigational protocols (mailto:, tel:, javascript:, #anchor) stay as-is
1134
+ if (/^(mailto:|tel:|javascript:)/i.test(href) || href.startsWith('#'))
1135
+ return null;
1136
+ try {
1137
+ const url = new URL(href, window.location.origin);
1138
+ if (url.protocol !== 'http:' && url.protocol !== 'https:')
1139
+ return null;
1140
+ const currentApp = window.location.pathname.split('/')[1];
1141
+ const targetApp = url.pathname.split('/')[1];
1142
+ // Same origin, or a different host of the same app (e.g. backend returns the
1143
+ // deployed host while running locally) - both resolve to a client side route
1144
+ const isInternal = url.origin === window.location.origin ||
1145
+ (!!currentApp && currentApp === targetApp);
1146
+ if (!isInternal)
1147
+ return null;
1148
+ return `${url.pathname}${url.search}${url.hash}`;
1149
+ }
1150
+ catch (e) {
1151
+ return null;
1152
+ }
1153
+ };
1154
+ /**
1155
+ * Minimizes the chat window so the user can see the screen they navigated to.
1156
+ * SmartBot (index.tsx) listens for this event and sets partialClose.
1157
+ */
1158
+ const minimizeChatBot = () => {
1159
+ window.dispatchEvent(new CustomEvent("smartBotMinimize"));
1160
+ };
1161
+ /**
1162
+ * Anchor rendered inside chatbot markdown. Internal links are navigated through
1163
+ * react-router so the chatbot is not remounted by a full page load.
1164
+ */
1165
+ const MarkdownLink = ({ href, children, ...props }) => {
1166
+ const navigate = reactRouterDomV5Compat.useNavigate();
1167
+ const handleClick = (event) => {
1168
+ // Let the browser handle new tab / new window / download intents
1169
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0)
1170
+ return;
1171
+ const internalPath = resolveInternalPath(href);
1172
+ if (!internalPath)
1173
+ return;
1174
+ event.preventDefault();
1175
+ const currentPath = `${window.location.pathname}${window.location.search}`;
1176
+ if (currentPath !== internalPath) {
1177
+ navigate(internalPath);
1178
+ }
1179
+ minimizeChatBot();
1180
+ };
1181
+ return (jsxRuntime.jsx("a", { href: href, target: "_self", onClick: handleClick, ...props, children: children }));
1182
+ };
1122
1183
  /**
1123
1184
  * Checks whether the input string contains meaningful HTML markup
1124
1185
  * (beyond simple inline tags like <b>, <i>, <em>, <strong>, <br>, <a>, <u>
@@ -1159,9 +1220,16 @@ const preprocessMarkdown = (content) => {
1159
1220
  .replace(/(^|\n)([^\n-•]*[a-zA-Z0-9][^\n]*)\n([-•]\s+)/g, '$1$2\n\n$3')
1160
1221
  // Handle list items with - or • appearing directly after text without newline
1161
1222
  // Only treat as a new bullet when preceded by sentence-ending punctuation (to avoid splitting inline hyphens like "KSO- ORLANDO")
1162
- .replace(/([.!?:)\"])\s*([-•])\s+/g, '$1\n\n$2 ')
1163
- // Handle nested list items with proper indentation
1164
- .replace(/(\n\s{2,})([-•]|\d+\.)\s+/g, '\n $2 ')
1223
+ // [^\S\n] (horizontal whitespace only) keeps this from crossing line breaks,
1224
+ // which would dedent already correctly indented nested bullets
1225
+ .replace(/([.!?:)"])[^\S\n]*([-\u2022])[^\S\n]+/g, '$1\n\n$2 ')
1226
+ // Handle nested list items with proper indentation.
1227
+ // Matching only horizontal whitespace after the newline keeps blank lines and
1228
+ // top level numbered items intact (indenting those turns them into plain text).
1229
+ // Normalize to 3 spaces: enough to nest under both "- " and "1. " parents, and
1230
+ // below the 4 space threshold that would turn the line into a code block.
1231
+ // Indents of 4 or more are left untouched (deliberate deeper nesting).
1232
+ .replace(/(\n[^\S\n]{2,3})([-\u2022]|\d+\.)[^\S\n]+/g, '\n $2 ')
1165
1233
  // Ensure double line breaks after list sections
1166
1234
  .replace(/(\n\d+\.\s+.*?)(\n\n###)/g, '$1\n$2')
1167
1235
  .replace(/(\n[-•]\s+.*?)(\n\n###)/g, '$1\n$2')
@@ -1173,8 +1241,8 @@ const preprocessMarkdown = (content) => {
1173
1241
  * Custom components for markdown rendering
1174
1242
  */
1175
1243
  const markdownComponents = {
1176
- // Custom link component that opens in the same tab
1177
- a: ({ href, children, ...props }) => (jsxRuntime.jsx("a", { href: href, target: "_self", ...props, children: children })),
1244
+ // Custom link component that navigates in-app without reloading the page
1245
+ a: MarkdownLink,
1178
1246
  // Custom code component
1179
1247
  code: ({ children, className, ...props }) => (jsxRuntime.jsx("code", { className: `markdown-code ${className || ''}`, ...props, children: children })),
1180
1248
  // Custom pre component for code blocks
@@ -1194,13 +1262,32 @@ const markdownComponents = {
1194
1262
  * Markdown renderer component with sanitization
1195
1263
  */
1196
1264
  const TextRenderer = ({ text, thinking }) => {
1265
+ const navigate = reactRouterDomV5Compat.useNavigate();
1266
+ // Click delegation for anchors inside raw HTML content (dangerouslySetInnerHTML),
1267
+ // so in-app links navigate through react-router instead of reloading the page.
1268
+ const handleHtmlClick = (event) => {
1269
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0)
1270
+ return;
1271
+ const anchor = event.target.closest?.('a');
1272
+ if (!anchor || anchor.target === '_blank')
1273
+ return;
1274
+ const internalPath = resolveInternalPath(anchor.getAttribute('href'));
1275
+ if (!internalPath)
1276
+ return;
1277
+ event.preventDefault();
1278
+ const currentPath = `${window.location.pathname}${window.location.search}`;
1279
+ if (currentPath !== internalPath) {
1280
+ navigate(internalPath);
1281
+ }
1282
+ minimizeChatBot();
1283
+ };
1197
1284
  // If the input contains rich HTML, render it directly with DOMPurify sanitization
1198
1285
  if (containsRichHtml(text)) {
1199
1286
  const sanitizedHtml = DOMPurify.sanitize(text, {
1200
1287
  ADD_TAGS: ['style'],
1201
1288
  ADD_ATTR: ['style', 'class', 'target', 'rel'],
1202
1289
  });
1203
- return (jsxRuntime.jsx("div", { className: `markdown-content md-content ${thinking ? "thinking" : ""}`, dangerouslySetInnerHTML: { __html: sanitizedHtml } }));
1290
+ return (jsxRuntime.jsx("div", { className: `markdown-content md-content ${thinking ? "thinking" : ""}`, onClick: handleHtmlClick, dangerouslySetInnerHTML: { __html: sanitizedHtml } }));
1204
1291
  }
1205
1292
  // Otherwise, use the existing markdown pipeline
1206
1293
  const processedContent = preprocessMarkdown(text);
@@ -5031,7 +5118,7 @@ const ChipsContent = ({ bodyText, props }) => {
5031
5118
  const classes = useStyles$a();
5032
5119
  const globalClasses = globalStyles();
5033
5120
  return (jsxRuntime.jsx("div", { className: `${globalClasses.flexRow} ${globalClasses.flexWrap} ${globalClasses.gap} ${globalClasses.verticalAlignCenter}`, children: bodyText.map((data, index) => {
5034
- const callBack = data.interactable
5121
+ const callBack = data.interactable && props
5035
5122
  ? Object.entries(props).filter((entry) => entry[0] === data.actionName)?.[0]?.[1]
5036
5123
  : null;
5037
5124
  return (jsxRuntime.jsx(material.Typography, { component: "span", variant: "body1", className: `${classes.gptChips} ${data.interactable ? globalClasses.cursorPointer : ""}`, onClick: () => {
@@ -5098,7 +5185,7 @@ const QuestionsContent = ({ bodyText, props }) => {
5098
5185
  };
5099
5186
  };
5100
5187
  return (jsxRuntime.jsx("div", { className: `${globalClasses.flexRow} ${globalClasses.flexColumn} ${classes.gptQuestionContainer}`, children: bodyText.map((data, index) => {
5101
- const callBack = data.interactable
5188
+ const callBack = data.interactable && props
5102
5189
  ? Object.entries(props).filter((entry) => entry[0] === data.actionName)?.[0]?.[1]
5103
5190
  : null;
5104
5191
  return (jsxRuntime.jsxs("div", { className: `${globalClasses.layoutAlignStart} ${classes.gptQuestionBlock}`, style: {
@@ -6849,6 +6936,36 @@ const fetchCrossFilterOptions = async (filterConfig, existingSelections = [], al
6849
6936
  }
6850
6937
  };
6851
6938
 
6939
+ const useStyles$6 = styles.makeStyles(() => ({
6940
+ htmlContentContainer: {
6941
+ width: "100%",
6942
+ "& *": {
6943
+ boxSizing: "border-box",
6944
+ },
6945
+ },
6946
+ }));
6947
+ const HtmlContent = ({ bodyText }) => {
6948
+ const classes = useStyles$6();
6949
+ const containerRef = React.useRef(null);
6950
+ const content = bodyText?.content || "";
6951
+ React.useEffect(() => {
6952
+ if (containerRef.current && content) {
6953
+ const sanitizedHtml = DOMPurify.sanitize(content, {
6954
+ ADD_TAGS: ["style", "details", "summary"],
6955
+ ADD_ATTR: ["open", "target", "rel", "class", "style"],
6956
+ ALLOW_DATA_ATTR: true,
6957
+ FORCE_BODY: true,
6958
+ WHOLE_DOCUMENT: false,
6959
+ });
6960
+ containerRef.current.innerHTML = sanitizedHtml;
6961
+ }
6962
+ }, [content]);
6963
+ if (!content) {
6964
+ return null;
6965
+ }
6966
+ return (jsxRuntime.jsx("div", { ref: containerRef, className: classes.htmlContentContainer }));
6967
+ };
6968
+
6852
6969
  const SliderContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
6853
6970
  const formKey = `${messageIndex}_${bodyText?.paramName}`;
6854
6971
  const { header, headerOrentiation, inputPosition, label, max, min, required, disabled, } = bodyText;
@@ -7394,8 +7511,9 @@ const STEP_FORM_TIMEOUT_KEY = "__stepFormTimedOut";
7394
7511
  * @param {Array} props.formData - Array of raw widget_data items from step_form chunk
7395
7512
  * @param {number} props.messageIndex - Index for form state persistence keys
7396
7513
  */
7397
- const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, showSavedFilters = true, preSelectedFilters = null }) => {
7514
+ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, showSavedFilters = true, preSelectedFilters = null, botProps = null, currentMode = "", agentId = "", sessionId = "", }) => {
7398
7515
  const dispatch = reactRedux.useDispatch();
7516
+ const classes = useStyles$a();
7399
7517
  const savedFilterSets = reactRedux.useSelector((state) => state.smartBotReducer.savedFilterSets);
7400
7518
  const persistedFormValues = reactRedux.useSelector((state) => state.smartBotReducer.persistedFormValues);
7401
7519
  const chatbotContext = reactRedux.useSelector((state) => state.smartBotReducer.chatbotContext);
@@ -7668,7 +7786,11 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7668
7786
  case "text":
7669
7787
  return jsxRuntime.jsx(TextContent, { bodyText: parsedData.bodyText, botData: parsedData }, key);
7670
7788
  case "chips":
7671
- return jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: {} }, key);
7789
+ return parsedData.isMultiSelect ? (jsxRuntime.jsx(SelectableChips, { bodyText: parsedData.bodyText, chipType: "selectable", utilityData: parsedData.utilityData, props: botProps }, key)) : (jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: botProps }, key));
7790
+ case "questions":
7791
+ return jsxRuntime.jsx(QuestionsContent, { bodyText: parsedData.bodyText, props: botProps }, key);
7792
+ case "html":
7793
+ return jsxRuntime.jsx(HtmlContent, { bodyText: parsedData.bodyText }, key);
7672
7794
  case "table":
7673
7795
  return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, key);
7674
7796
  case "graph":
@@ -7699,12 +7821,18 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7699
7821
  const buttonItems = [];
7700
7822
  formData.forEach((item, index) => {
7701
7823
  try {
7702
- const parsedData = parseResponse(item, item.type, "", "", true);
7824
+ const parsedData = parseResponse(item, item.type, agentId, currentMode, true, sessionId);
7703
7825
  if (!parsedData)
7704
7826
  return;
7705
- const rendered = renderItem(parsedData, index);
7706
- if (!rendered)
7827
+ const body = renderItem(parsedData, index);
7828
+ if (!body)
7707
7829
  return;
7830
+ // Skip the block header when it is just a repeat of the label the widget
7831
+ // renders itself (parseResponse derives headerTitle from label for inputs).
7832
+ const showHeaderTitle = !parsedData?.noShowHeaderTitle &&
7833
+ Boolean(parsedData?.headerTitle?.length) &&
7834
+ parsedData.headerTitle !== parsedData?.bodyText?.label;
7835
+ const rendered = showHeaderTitle ? (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("div", { className: `${classes.chatbotText} ${classes.boldText} ${classes.combinedBlockHeaderTitle}`, children: parsedData.headerTitle }), body] }, `step-form-block-${index}`)) : (body);
7708
7836
  if (parsedData.bodyType === "button") {
7709
7837
  buttonItems.push(rendered);
7710
7838
  }
@@ -7727,7 +7855,7 @@ const resetStepFormTimeoutFlag = () => {
7727
7855
  sessionStorage.removeItem(STEP_FORM_TIMEOUT_KEY);
7728
7856
  };
7729
7857
 
7730
- const useStyles$6 = styles.makeStyles(() => ({
7858
+ const useStyles$5 = styles.makeStyles(() => ({
7731
7859
  subStepMarkdown: {
7732
7860
  // Inherit font styles from parent (.progressSubItem)
7733
7861
  fontFamily: 'inherit',
@@ -7801,14 +7929,14 @@ const preprocessSubStepMarkdown = (content) => {
7801
7929
  .trim();
7802
7930
  };
7803
7931
  const SubStepRenderer = ({ text }) => {
7804
- const classes = useStyles$6();
7932
+ const classes = useStyles$5();
7805
7933
  if (!text)
7806
7934
  return null;
7807
7935
  const processed = preprocessSubStepMarkdown(text);
7808
7936
  return (jsxRuntime.jsx("div", { className: classes.subStepMarkdown, children: jsxRuntime.jsx(ReactMarkdown, { remarkPlugins: [remarkGfm, remarkBreaks], children: processed }) }));
7809
7937
  };
7810
7938
 
7811
- const useStyles$5 = makeStyles((theme) => ({
7939
+ const useStyles$4 = makeStyles((theme) => ({
7812
7940
  "@global": {
7813
7941
  "@keyframes progressDotPulse": {
7814
7942
  "0%": {
@@ -8082,7 +8210,7 @@ const getQuestionStatus$1 = (questionSteps) => {
8082
8210
  /**
8083
8211
  * Renders a single progress bar item (main point + sub-items)
8084
8212
  */
8085
- const ProgressBarItem$1 = ({ question, questionSteps, isLast, classes, formData, showSavedFilters = true, preSelectedFilters = null, isFormDisabled, onAllSubItemsAnimated = undefined }) => {
8213
+ const ProgressBarItem$1 = ({ question, questionSteps, isLast, classes, formData, showSavedFilters = true, preSelectedFilters = null, isFormDisabled, onAllSubItemsAnimated = undefined, botProps = null, currentMode = "", agentId = "", sessionId = "" }) => {
8086
8214
  const status = getQuestionStatus$1(questionSteps);
8087
8215
  const animatedCountRef = React.useRef(0);
8088
8216
  const [isExpanded, setIsExpanded] = React.useState(true);
@@ -8116,10 +8244,10 @@ const ProgressBarItem$1 = ({ question, questionSteps, isLast, classes, formData,
8116
8244
  if (animatedCountRef.current >= arr.length && onAllSubItemsAnimated) {
8117
8245
  onAllSubItemsAnimated();
8118
8246
  }
8119
- } }) }, idx))) })), formData && isExpanded && (jsxRuntime.jsx("div", { className: classes.stepFormContainer, children: jsxRuntime.jsx(StepFormContent, { formData: formData, isFormDisabled: isFormDisabled, showSavedFilters: showSavedFilters, preSelectedFilters: preSelectedFilters }) }))] })] }));
8247
+ } }) }, idx))) })), formData && isExpanded && (jsxRuntime.jsx("div", { className: classes.stepFormContainer, children: jsxRuntime.jsx(StepFormContent, { formData: formData, isFormDisabled: isFormDisabled, showSavedFilters: showSavedFilters, preSelectedFilters: preSelectedFilters, botProps: botProps, currentMode: currentMode, agentId: agentId, sessionId: sessionId }) }))] })] }));
8120
8248
  };
8121
- const Steps$1 = ({ steps, setSteps, done, setTabValue, setDone, finalStepDone, setFinalStepDone, stepChange, currentMode, questions = [], questionsStepsMap = {}, stepFormDataMap = {}, isFormDisabled = false, }) => {
8122
- const classes = useStyles$5();
8249
+ const Steps$1 = ({ steps, setSteps, done, setTabValue, setDone, finalStepDone, setFinalStepDone, stepChange, currentMode, questions = [], questionsStepsMap = {}, stepFormDataMap = {}, isFormDisabled = false, botProps = null, agentId = "", sessionId = "", }) => {
8250
+ const classes = useStyles$4();
8123
8251
  React.useState(false);
8124
8252
  const [showThinking, setShowThinking] = React.useState(false);
8125
8253
  // Determine if last question is fully completed (all sub-steps done)
@@ -8201,7 +8329,7 @@ const Steps$1 = ({ steps, setSteps, done, setTabValue, setDone, finalStepDone, s
8201
8329
  const showSavedFilters = formEntry && !Array.isArray(formEntry) ? formEntry.showSavedFilters : true;
8202
8330
  const preSelectedFilters = formEntry && !Array.isArray(formEntry) ? formEntry.preSelectedFilters : null;
8203
8331
  const isLast = index === questions.length - 1;
8204
- return (jsxRuntime.jsx(ProgressBarItem$1, { question: question, questionSteps: questionSteps, isLast: isLast && !showThinking, classes: classes, formData: formData, isFormDisabled: isFormDisabled, showSavedFilters: showSavedFilters, preSelectedFilters: preSelectedFilters, onAllSubItemsAnimated: isLast && lastQuestionCompleted && !done
8332
+ return (jsxRuntime.jsx(ProgressBarItem$1, { question: question, questionSteps: questionSteps, isLast: isLast && !showThinking, classes: classes, formData: formData, isFormDisabled: isFormDisabled, showSavedFilters: showSavedFilters, preSelectedFilters: preSelectedFilters, botProps: botProps, currentMode: currentMode, agentId: agentId, sessionId: sessionId, onAllSubItemsAnimated: isLast && lastQuestionCompleted && !done
8205
8333
  ? () => setShowThinking(true)
8206
8334
  : undefined }, index));
8207
8335
  }), showThinking && !done && (jsxRuntime.jsx("div", { style: {
@@ -8209,36 +8337,6 @@ const Steps$1 = ({ steps, setSteps, done, setTabValue, setDone, finalStepDone, s
8209
8337
  }, children: jsxRuntime.jsx(ProgressBarItem$1, { question: "Thinking", questionSteps: [{ header: "", sub_header: "", step_status: "not-completed" }], isLast: true, classes: classes, formData: null, isFormDisabled: isFormDisabled }) }))] }));
8210
8338
  };
8211
8339
 
8212
- const useStyles$4 = styles.makeStyles(() => ({
8213
- htmlContentContainer: {
8214
- width: "100%",
8215
- "& *": {
8216
- boxSizing: "border-box",
8217
- },
8218
- },
8219
- }));
8220
- const HtmlContent = ({ bodyText }) => {
8221
- const classes = useStyles$4();
8222
- const containerRef = React.useRef(null);
8223
- const content = bodyText?.content || "";
8224
- React.useEffect(() => {
8225
- if (containerRef.current && content) {
8226
- const sanitizedHtml = DOMPurify.sanitize(content, {
8227
- ADD_TAGS: ["style", "details", "summary"],
8228
- ADD_ATTR: ["open", "target", "rel", "class", "style"],
8229
- ALLOW_DATA_ATTR: true,
8230
- FORCE_BODY: true,
8231
- WHOLE_DOCUMENT: false,
8232
- });
8233
- containerRef.current.innerHTML = sanitizedHtml;
8234
- }
8235
- }, [content]);
8236
- if (!content) {
8237
- return null;
8238
- }
8239
- return (jsxRuntime.jsx("div", { ref: containerRef, className: classes.htmlContentContainer }));
8240
- };
8241
-
8242
8340
  const renderWidgetItem = (item, index, isFormDisabled) => {
8243
8341
  try {
8244
8342
  const parsedData = parseResponse(item, item.type, "", "", true);
@@ -8290,7 +8388,7 @@ const AgentResponse$1 = (props) => {
8290
8388
  };
8291
8389
 
8292
8390
  const StepsResponseTab = (props) => {
8293
- const { steps, setSteps, stepsDone, setStepsDone, finalStepDone, setFinalStepDone, content, isStreaming, stepChange, currentMode, questions, questionsStepsMap, stepFormDataMap, isFormDisabled, streamingWidgetData, isRetrying, } = props;
8391
+ const { steps, setSteps, stepsDone, setStepsDone, finalStepDone, setFinalStepDone, content, isStreaming, stepChange, currentMode, questions, questionsStepsMap, stepFormDataMap, isFormDisabled, streamingWidgetData, isRetrying, botProps, agentId, sessionId, } = props;
8294
8392
  const dispatch = reactRedux.useDispatch();
8295
8393
  const thinkingContext = reactRedux.useSelector((state) => state.smartBotReducer.thinkingContext);
8296
8394
  const streamStartTimeRef = React.useRef(thinkingContext?.streamStartTime);
@@ -8354,7 +8452,7 @@ const StepsResponseTab = (props) => {
8354
8452
  icon: jsxRuntime.jsx(PsychologyOutlinedIcon, { fontSize: "large" }),
8355
8453
  },
8356
8454
  ], tabPanels: [
8357
- jsxRuntime.jsx(Steps$1, { steps: steps, setSteps: setSteps, done: stepsDone, setDone: setStepsDone, setTabValue: setTabValue, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, stepChange: stepChange, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: isFormDisabled || isTimedOut }),
8455
+ jsxRuntime.jsx(Steps$1, { steps: steps, setSteps: setSteps, done: stepsDone, setDone: setStepsDone, setTabValue: setTabValue, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, stepChange: stepChange, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: isFormDisabled || isTimedOut, botProps: botProps, agentId: agentId, sessionId: sessionId }),
8358
8456
  jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(AgentResponse$1, { content: content, isStreaming: isStreaming, streamingWidgetData: streamingWidgetData, isFormDisabled: isFormDisabled || isTimedOut }), timeoutMessage && (jsxRuntime.jsx("div", { style: { marginTop: "8px", padding: "0 8px" }, children: jsxRuntime.jsx(TextRenderer, { text: timeoutMessage, thinking: "" }) }))] }),
8359
8457
  ], value: tabValue }) }));
8360
8458
  };
@@ -8536,7 +8634,7 @@ const streamStateMap = new Map();
8536
8634
  * @param {React.RefObject} botData.utilityObject.chatBodyRef - Ref to chat body element
8537
8635
  * @param {Function} botData.utilityObject.setChatDataState - Function to update chat data state
8538
8636
  */
8539
- const StreamedContent = ({ botData }) => {
8637
+ const StreamedContent = ({ botData, botProps }) => {
8540
8638
  const { activeConversationId, currentMode, chatDataRef, chatBodyRef, setChatDataState, chatDataInfoRef, setLoader = (params) => { }, processResponse = (params) => { }, setThinkingContent, thinkingContent, isThinking: isThinkingFromParent, setIsThinking: setIsThinkingFromParent, chatId, setChatId, isStop, setIsStop, functionsRef, functionsState, setFunctionsState, thinkingHeaderMessage, setThinkingHeaderMessage, baseUrl, setNavSessionId } = botData.utilityObject || {};
8541
8639
  const classes = useStyles$7();
8542
8640
  useStyles$a();
@@ -9715,8 +9813,13 @@ const StreamedContent = ({ botData }) => {
9715
9813
  }, 1000);
9716
9814
  }
9717
9815
  }, [isStreamingDone, thinkingTime, activeConversationId]);
9816
+ // Show/hide the stop (cancel execution) icon while the stream is active.
9817
+ // Gate on state only - sourceRef is a ref, so mutating it never re-runs this
9818
+ // effect. Relying on it meant that if the source wasn't assigned yet on the
9819
+ // first run, isStop was set to false and never re-evaluated for the rest of
9820
+ // the stream, leaving the cancel button hidden.
9718
9821
  React.useEffect(() => {
9719
- if (sourceRef.current && isStreaming) {
9822
+ if (isStreaming && !isStreamingDone) {
9720
9823
  setIsStop(true);
9721
9824
  setFunctionsState({
9722
9825
  ...functionsState,
@@ -9727,7 +9830,7 @@ const StreamedContent = ({ botData }) => {
9727
9830
  else {
9728
9831
  setIsStop(false);
9729
9832
  }
9730
- }, [isStreaming]);
9833
+ }, [isStreaming, isStreamingDone]);
9731
9834
  /**
9732
9835
  * Aborts the current streaming connection
9733
9836
  */
@@ -9740,10 +9843,12 @@ const StreamedContent = ({ botData }) => {
9740
9843
  setRetryCountdown(0);
9741
9844
  }
9742
9845
  retryCountRef.current = MAX_RETRY_COUNT; // Exhaust retries so no further auto-retries happen
9743
- if (sourceRef.current && isStreaming) {
9846
+ if (isStreaming) {
9744
9847
  setWasStreamingAborted(true);
9745
9848
  wasStreamingAbortedRef.current = true;
9746
- sourceRef.current.close();
9849
+ if (sourceRef.current) {
9850
+ sourceRef.current.close();
9851
+ }
9747
9852
  setIsStreaming(false);
9748
9853
  setIsStreamingDone(true);
9749
9854
  // Mark as completed+aborted so tab-switch remounts don't restart the stream.
@@ -9903,7 +10008,7 @@ const StreamedContent = ({ botData }) => {
9903
10008
  * @returns {JSX.Element} Rendered content with optional blinking cursor
9904
10009
  */
9905
10010
  const renderContent = () => {
9906
- return (jsxRuntime.jsxs("div", { className: classes.streamContainer, children: [jsxRuntime.jsx(StepsResponseTab, { steps: steps, stepChange: stepChange, setSteps: setSteps, stepsDone: stepsDone, setStepsDone: setStepsDone, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, content: content, isStreaming: isStreaming, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: botData?.isFormDisabled || false, streamingWidgetData: streamingWidgetData, isRetrying: isRetrying }), isRetrying && (jsxRuntime.jsx("div", { className: classes.retryContainer, children: jsxRuntime.jsx(TextRenderer, { text: `Auto re-trying in ${retryCountdown}s`, thinking: "" }) }))] }));
10011
+ return (jsxRuntime.jsxs("div", { className: classes.streamContainer, children: [jsxRuntime.jsx(StepsResponseTab, { steps: steps, stepChange: stepChange, setSteps: setSteps, stepsDone: stepsDone, setStepsDone: setStepsDone, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, content: content, isStreaming: isStreaming, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: botData?.isFormDisabled || false, streamingWidgetData: streamingWidgetData, isRetrying: isRetrying, botProps: botProps, agentId: botData?.inputBody?.agent_id || "", sessionId: messageToStoreRef.current?.sessionId || "" }), isRetrying && (jsxRuntime.jsx("div", { className: classes.retryContainer, children: jsxRuntime.jsx(TextRenderer, { text: `Auto re-trying in ${retryCountdown}s`, thinking: "" }) }))] }));
9907
10012
  };
9908
10013
  if (currentMode === "agent") {
9909
10014
  return renderContent();
@@ -10173,8 +10278,9 @@ const AgentResponse = ({ children }) => {
10173
10278
  // Only the active instance should process stepFormStreamData from Redux.
10174
10279
  let instanceCounter = 0;
10175
10280
  let activeTabularInstanceId = null;
10176
- const TabularContent = ({ steps: initialSteps, currentTabValue, children, questions: initialQuestions = [], questionsStepsMap: initialQuestionsStepsMap = {}, stepFormDataMap: initialStepFormDataMap = {}, isFormDisabled = false, sessionId: propSessionId = "" }) => {
10281
+ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questions: initialQuestions = [], questionsStepsMap: initialQuestionsStepsMap = {}, stepFormDataMap: initialStepFormDataMap = {}, isFormDisabled = false, sessionId: propSessionId = "", botProps = null, currentMode = "", agentId = "" }) => {
10177
10282
  const dispatch = reactRedux.useDispatch();
10283
+ const chatClasses = useStyles$a();
10178
10284
  const stepFormStreamData = reactRedux.useSelector((state) => state.smartBotReducer.stepFormStreamData);
10179
10285
  const thinkingContext = reactRedux.useSelector((state) => state.smartBotReducer.thinkingContext);
10180
10286
  const streamStartTimeRef = React.useRef(thinkingContext?.streamStartTime);
@@ -10258,40 +10364,55 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
10258
10364
  };
10259
10365
  }, []);
10260
10366
  // Render a widget item from SSE data
10367
+ const renderWidgetBody = (parsedData, key) => {
10368
+ switch (parsedData.bodyType) {
10369
+ case "text":
10370
+ return jsxRuntime.jsx(TextContent, { bodyText: parsedData.bodyText, botData: parsedData }, key);
10371
+ case "chips":
10372
+ return parsedData.isMultiSelect ? (jsxRuntime.jsx(SelectableChips, { bodyText: parsedData.bodyText, chipType: "selectable", utilityData: parsedData.utilityData, props: botProps }, key)) : (jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: botProps }, key));
10373
+ case "questions":
10374
+ return jsxRuntime.jsx(QuestionsContent, { bodyText: parsedData.bodyText, props: botProps }, key);
10375
+ case "image":
10376
+ return jsxRuntime.jsx(ImageContent, { bodyText: parsedData.bodyText }, key);
10377
+ case "table":
10378
+ return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, key);
10379
+ case "graph":
10380
+ return jsxRuntime.jsx(GraphContent, { bodyText: parsedData.bodyText }, key);
10381
+ case "radio":
10382
+ return jsxRuntime.jsx(RadioContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10383
+ case "checkbox":
10384
+ return jsxRuntime.jsx(CheckboxContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10385
+ case "select":
10386
+ return jsxRuntime.jsx(SelectContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10387
+ case "slider":
10388
+ return jsxRuntime.jsx(SliderContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10389
+ case "button":
10390
+ return jsxRuntime.jsx(ButtonContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10391
+ case "input":
10392
+ return jsxRuntime.jsx(InputContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10393
+ case "datePicker":
10394
+ return jsxRuntime.jsx(DatePickerContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10395
+ case "dateRangePicker":
10396
+ return jsxRuntime.jsx(DateRangePickerContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10397
+ case "html":
10398
+ return jsxRuntime.jsx(HtmlContent, { bodyText: parsedData.bodyText }, key);
10399
+ default:
10400
+ return null;
10401
+ }
10402
+ };
10261
10403
  const renderWidget = (item, index) => {
10262
10404
  try {
10263
- const parsedData = parseResponse(item, item.type, "", "", true);
10405
+ const parsedData = parseResponse(item, item.type, agentId, currentMode, true, propSessionId);
10264
10406
  if (!parsedData)
10265
10407
  return null;
10266
10408
  const key = `restream-widget-${index}`;
10267
- switch (parsedData.bodyType) {
10268
- case "text":
10269
- return jsxRuntime.jsx(TextContent, { bodyText: parsedData.bodyText, botData: parsedData }, key);
10270
- case "table":
10271
- return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, key);
10272
- case "graph":
10273
- return jsxRuntime.jsx(GraphContent, { bodyText: parsedData.bodyText }, key);
10274
- case "radio":
10275
- return jsxRuntime.jsx(RadioContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10276
- case "checkbox":
10277
- return jsxRuntime.jsx(CheckboxContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10278
- case "select":
10279
- return jsxRuntime.jsx(SelectContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10280
- case "slider":
10281
- return jsxRuntime.jsx(SliderContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10282
- case "button":
10283
- return jsxRuntime.jsx(ButtonContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10284
- case "input":
10285
- return jsxRuntime.jsx(InputContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10286
- case "datePicker":
10287
- return jsxRuntime.jsx(DatePickerContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10288
- case "dateRangePicker":
10289
- return jsxRuntime.jsx(DateRangePickerContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled }, key);
10290
- case "html":
10291
- return jsxRuntime.jsx(HtmlContent, { bodyText: parsedData.bodyText }, key);
10292
- default:
10293
- return null;
10294
- }
10409
+ const body = renderWidgetBody(parsedData, key);
10410
+ if (!body)
10411
+ return null;
10412
+ const showHeaderTitle = !parsedData?.noShowHeaderTitle && Boolean(parsedData?.headerTitle?.length);
10413
+ if (!showHeaderTitle)
10414
+ return body;
10415
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("div", { className: `${chatClasses.chatbotText} ${chatClasses.boldText} ${chatClasses.combinedBlockHeaderTitle}`, children: parsedData.headerTitle }), body] }, `restream-widget-block-${index}`));
10295
10416
  }
10296
10417
  catch (e) {
10297
10418
  console.error("[TabularContent] renderWidget error:", e);
@@ -10661,11 +10782,8 @@ const CombinedContent = ({ botData, props }) => {
10661
10782
  // Get the array of content items from bodyText
10662
10783
  const contentItems = Array.isArray(botData.bodyText) ? botData.bodyText : [];
10663
10784
  // Renders the optional block-level header title above a content item.
10664
- // Text blocks are skipped because their heading is the echoed user query
10665
- // (response_heading), which we don't want to duplicate in the response.
10666
10785
  const renderHeaderTitle = (parsedData) => {
10667
- const showHeaderTitle = parsedData?.bodyType !== "text" &&
10668
- !parsedData?.noShowHeaderTitle &&
10786
+ const showHeaderTitle = !parsedData?.noShowHeaderTitle &&
10669
10787
  Boolean(parsedData?.headerTitle?.length);
10670
10788
  if (!showHeaderTitle)
10671
10789
  return null;
@@ -10735,7 +10853,7 @@ const CombinedContent = ({ botData, props }) => {
10735
10853
  const validContent = renderedContent.filter(content => content !== null);
10736
10854
  const renderCombinedContent = () => (jsxRuntime.jsx("div", { className: "combined-content-container", children: validContent.length > 0 ? (validContent.map((content, index) => (jsxRuntime.jsx("div", { className: "combined-content-item", children: content }, `wrapper-${index}`)))) : (jsxRuntime.jsx("div", { children: "No valid content to display" })) }));
10737
10855
  if (isTabEnabled) {
10738
- return (jsxRuntime.jsx(TabularContent, { steps: botData?.utilityData?.steps || [], currentTabValue: botData?.utilityData?.currentTabValue || "steps", questions: botData?.utilityData?.questions || [], questionsStepsMap: botData?.utilityData?.questionsStepsMap || {}, stepFormDataMap: botData?.utilityData?.stepFormDataMap || {}, isFormDisabled: isFormDisabled, sessionId: botData?.sessionId || "", children: renderCombinedContent() }));
10856
+ return (jsxRuntime.jsx(TabularContent, { steps: botData?.utilityData?.steps || [], currentTabValue: botData?.utilityData?.currentTabValue || "steps", questions: botData?.utilityData?.questions || [], questionsStepsMap: botData?.utilityData?.questionsStepsMap || {}, stepFormDataMap: botData?.utilityData?.stepFormDataMap || {}, isFormDisabled: isFormDisabled, sessionId: botData?.sessionId || "", botProps: props, currentMode: botData?.currentMode || "", agentId: botData?.agentId || "", children: renderCombinedContent() }));
10739
10857
  }
10740
10858
  return renderCombinedContent();
10741
10859
  };
@@ -10774,7 +10892,7 @@ const BotMessage = ({ botData, state, handleLikeDislike, props }) => {
10774
10892
  case "text":
10775
10893
  return jsxRuntime.jsx(TextContent, { bodyText: botData.bodyText, botData: botData });
10776
10894
  case "stream":
10777
- return jsxRuntime.jsx(StreamedContent, { botData: botData });
10895
+ return jsxRuntime.jsx(StreamedContent, { botData: botData, botProps: props });
10778
10896
  case "chips":
10779
10897
  if (botData.isMultiSelect) {
10780
10898
  return (jsxRuntime.jsx(SelectableChips, { bodyText: botData.bodyText, utilityData: botData.utilityData, props: props }));
@@ -15021,13 +15139,19 @@ const SmartBot = (props) => {
15021
15139
  if (newChatId !== undefined)
15022
15140
  setUniqueChatId(newChatId);
15023
15141
  };
15142
+ // Minimize the chat window when the user follows an in-app link from a bot response
15143
+ const handleMinimizeRequest = () => {
15144
+ setPartialClose(true);
15145
+ };
15024
15146
  window.addEventListener("stepFormStreamStart", handleStepFormStreamStart);
15025
15147
  window.addEventListener("stepFormStreamEnd", handleStepFormStreamEnd);
15026
15148
  window.addEventListener("stepFormInitStateUpdate", handleStepFormInitStateUpdate);
15149
+ window.addEventListener("smartBotMinimize", handleMinimizeRequest);
15027
15150
  return () => {
15028
15151
  window.removeEventListener("stepFormStreamStart", handleStepFormStreamStart);
15029
15152
  window.removeEventListener("stepFormStreamEnd", handleStepFormStreamEnd);
15030
15153
  window.removeEventListener("stepFormInitStateUpdate", handleStepFormInitStateUpdate);
15154
+ window.removeEventListener("smartBotMinimize", handleMinimizeRequest);
15031
15155
  };
15032
15156
  }, []);
15033
15157
  const fetchCustomBotConfigurations = async () => {
@@ -15163,6 +15287,13 @@ const SmartBot = (props) => {
15163
15287
  // );
15164
15288
  // return;
15165
15289
  // } else {
15290
+ // The library re-fires the `initialClick` tab's onClick from an
15291
+ // effect whose deps (tabList/menuItems/onChatBotResize) are
15292
+ // recreated on every render. Track the previous tab so we only
15293
+ // reset transient state on a real tab change - otherwise these
15294
+ // spurious re-invocations wipe isStop mid-stream and hide the
15295
+ // cancel execution button.
15296
+ const previousActiveTab = activeTab.current.activeTab;
15166
15297
  setShowSavedFilters(false);
15167
15298
  const agentConversations = chatDataInfoRef?.current[params?.name?.toLowerCase()]?.conversations;
15168
15299
  const firstConversationId = agentConversations ? Object.keys(agentConversations)[0] : undefined;
@@ -15181,7 +15312,9 @@ const SmartBot = (props) => {
15181
15312
  localStorage.setItem("currentModeData", params?.name?.toLowerCase());
15182
15313
  setNewChatScreen(false);
15183
15314
  activeTab.current.activeTab = "agent";
15184
- setIsStop(false);
15315
+ if (previousActiveTab !== "agent") {
15316
+ setIsStop(false);
15317
+ }
15185
15318
  // }
15186
15319
  // setConversation([]);
15187
15320
  // chatDataInfoRef.current[currentMode] = [];
@@ -15200,6 +15333,8 @@ const SmartBot = (props) => {
15200
15333
  // );
15201
15334
  // return;
15202
15335
  // } else {
15336
+ // Only reset transient state on a real tab change.
15337
+ const previousActiveTab = activeTab.current.activeTab;
15203
15338
  setShowSavedFilters(false);
15204
15339
  let currentModeValue = params?.name?.toLowerCase();
15205
15340
  const modeConversations = chatDataInfoRef?.current[currentModeValue]?.conversations;
@@ -15223,7 +15358,9 @@ const SmartBot = (props) => {
15223
15358
  chatDataInfoRef.current = lodash.cloneDeep(parsedData);
15224
15359
  }
15225
15360
  // setNewChatScreen(true);
15226
- setIsStop(false);
15361
+ if (previousActiveTab !== "navigation") {
15362
+ setIsStop(false);
15363
+ }
15227
15364
  setCurrentMode(params?.name?.toLowerCase());
15228
15365
  if (firstConversationId) {
15229
15366
  setActiveConversationId(firstConversationId);
@@ -15292,6 +15429,9 @@ const SmartBot = (props) => {
15292
15429
  // );
15293
15430
  // return;
15294
15431
  // } else {
15432
+ // Only reset transient state on a real tab change - the library
15433
+ // re-fires this initialClick handler on every render.
15434
+ const previousActiveTab = activeTab.current.activeTab;
15295
15435
  setShowSavedFilters(false);
15296
15436
  let currentModeValue = params?.name?.toLowerCase();
15297
15437
  const modeConversations = chatDataInfoRef?.current[currentModeValue]?.conversations;
@@ -15315,7 +15455,9 @@ const SmartBot = (props) => {
15315
15455
  chatDataInfoRef.current = lodash.cloneDeep(parsedData);
15316
15456
  }
15317
15457
  // setNewChatScreen(true);
15318
- setIsStop(false);
15458
+ if (previousActiveTab !== "navigation") {
15459
+ setIsStop(false);
15460
+ }
15319
15461
  setCurrentMode(params?.name?.toLowerCase());
15320
15462
  if (firstConversationId) {
15321
15463
  setActiveConversationId(firstConversationId);
@@ -15346,6 +15488,9 @@ const SmartBot = (props) => {
15346
15488
  // );
15347
15489
  // return;
15348
15490
  // } else {
15491
+ // Only reset transient state on a real tab change - the library
15492
+ // re-fires this initialClick handler on every render.
15493
+ const previousActiveTab = activeTab.current.activeTab;
15349
15494
  setShowSavedFilters(false);
15350
15495
  let currentModeValue = params?.name?.toLowerCase();
15351
15496
  const modeConversations = chatDataInfoRef?.current[currentModeValue]?.conversations;
@@ -15369,7 +15514,9 @@ const SmartBot = (props) => {
15369
15514
  chatDataInfoRef.current = lodash.cloneDeep(parsedData);
15370
15515
  }
15371
15516
  // setNewChatScreen(true);
15372
- setIsStop(false);
15517
+ if (previousActiveTab !== "navigation") {
15518
+ setIsStop(false);
15519
+ }
15373
15520
  setCurrentMode(params?.name?.toLowerCase());
15374
15521
  if (firstConversationId) {
15375
15522
  setActiveConversationId(firstConversationId);