impact-chatbot 2.3.50 → 2.3.52

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
@@ -1299,12 +1299,70 @@ const ThinkingIndicator = (props) => {
1299
1299
  return (jsxRuntime.jsx("div", { children: jsxRuntime.jsx("div", { children: thinkingContentState ? (jsxRuntime.jsx("div", { children: jsxRuntime.jsx(TextRenderer, { text: thinkingContentState, thinking: true }) })) : (jsxRuntime.jsxs("div", { className: classes.thinkingSpinner, children: [jsxRuntime.jsx("span", { className: classes.thinkingDot }), jsxRuntime.jsx("span", { className: classes.thinkingDot }), jsxRuntime.jsx("span", { className: classes.thinkingDot })] })) }) }));
1300
1300
  };
1301
1301
 
1302
+ /**
1303
+ * Formats elapsed seconds into "Xm:Ys" display string.
1304
+ * e.g. 0 -> "0m:00s", 65 -> "1m:05s", 130 -> "2m:10s"
1305
+ */
1306
+ const formatElapsedTime = (totalSeconds) => {
1307
+ const minutes = Math.floor(totalSeconds / 60);
1308
+ const seconds = totalSeconds % 60;
1309
+ return `${minutes}m:${String(seconds).padStart(2, '0')}s`;
1310
+ };
1302
1311
  const ThinkinHeaderInfo = (props) => {
1303
1312
  const thinkingContext = reactRedux.useSelector((state) => {
1304
1313
  return state.smartBotReducer.thinkingContext;
1305
1314
  });
1306
- const { thinkingHeaderMessage } = thinkingContext;
1307
- return (jsxRuntime.jsx("div", { className: "thinkin-header-info", children: thinkingHeaderMessage }));
1315
+ const { streamStartTime, isStreamCompleted, finalElapsedSeconds, thinkingHeaderMessage } = thinkingContext;
1316
+ const [elapsed, setElapsed] = React.useState(0);
1317
+ const intervalRef = React.useRef(null);
1318
+ // Once this instance sees "completed", freeze it permanently so a future
1319
+ // chat's streamStartTime doesn't cause it to start ticking again.
1320
+ const frozenRef = React.useRef(false);
1321
+ const frozenTextRef = React.useRef(null);
1322
+ React.useEffect(() => {
1323
+ // If already frozen, ignore all future Redux changes
1324
+ if (frozenRef.current)
1325
+ return;
1326
+ // Clear any existing interval
1327
+ if (intervalRef.current) {
1328
+ clearInterval(intervalRef.current);
1329
+ intervalRef.current = null;
1330
+ }
1331
+ if (isStreamCompleted && typeof finalElapsedSeconds === 'number') {
1332
+ // Freeze this instance — it will never tick again
1333
+ frozenRef.current = true;
1334
+ frozenTextRef.current = `Completed in ${formatElapsedTime(finalElapsedSeconds)}`;
1335
+ setElapsed(finalElapsedSeconds);
1336
+ }
1337
+ else if (streamStartTime && !isStreamCompleted) {
1338
+ // Live timer — update every second
1339
+ const tick = () => {
1340
+ const now = Date.now();
1341
+ const diffSeconds = Math.floor((now - streamStartTime) / 1000);
1342
+ setElapsed(diffSeconds);
1343
+ };
1344
+ tick(); // immediate first tick
1345
+ intervalRef.current = setInterval(tick, 1000);
1346
+ }
1347
+ return () => {
1348
+ if (intervalRef.current) {
1349
+ clearInterval(intervalRef.current);
1350
+ intervalRef.current = null;
1351
+ }
1352
+ };
1353
+ }, [streamStartTime, isStreamCompleted, finalElapsedSeconds]);
1354
+ // Determine display text
1355
+ let displayText = thinkingHeaderMessage || '';
1356
+ if (frozenRef.current && frozenTextRef.current) {
1357
+ displayText = frozenTextRef.current;
1358
+ }
1359
+ else if (streamStartTime && !isStreamCompleted) {
1360
+ displayText = `Working for ${formatElapsedTime(elapsed)}`;
1361
+ }
1362
+ else if (isStreamCompleted && typeof finalElapsedSeconds === 'number') {
1363
+ displayText = `Completed in ${formatElapsedTime(finalElapsedSeconds)}`;
1364
+ }
1365
+ return (jsxRuntime.jsx("div", { className: "thinkin-header-info", children: displayText }));
1308
1366
  };
1309
1367
 
1310
1368
  // import { exampleGraphData } from "../examples/GraphExample";
@@ -4074,7 +4132,7 @@ const useChatState = () => {
4074
4132
  const [chatId, setChatId] = React.useState("");
4075
4133
  const [isStop, setIsStop] = React.useState(false);
4076
4134
  const [functionsState, setFunctionsState] = React.useState({});
4077
- const [thinkingHeaderMessage, setThinkingHeaderMessage] = React.useState("Planning next moves...");
4135
+ const [thinkingHeaderMessage, setThinkingHeaderMessage] = React.useState("Working… 0m:00s");
4078
4136
  const [legacyAgentScreen, setLegacyAgentScreen] = React.useState(false);
4079
4137
  const [uniqueChatId, setUniqueChatId] = React.useState("");
4080
4138
  const [fieldNumber, setFieldNumber] = React.useState(0);
@@ -4830,7 +4888,7 @@ const ChatPlaceholder = (props) => {
4830
4888
  agentId: card.agentId || card.id
4831
4889
  }));
4832
4890
  const dataToMap = legacyAgentScreen || displayQuestions ? transformedCardList : rectangleData;
4833
- return (jsxRuntime.jsxs("div", { className: classes.placeholderContainer, children: [jsxRuntime.jsx("div", { className: classes.centerIconContainer, children: jsxRuntime.jsx(SvgCenter3D, { className: classes.centerIcon }) }), jsxRuntime.jsxs("div", { className: classes.headingContainer, children: [jsxRuntime.jsx(material.Typography, { variant: "h1", className: classes.heading, children: "Alan's Capabilities" }), jsxRuntime.jsx("span", { className: classes.alphaTag, children: "Alpha" })] }), jsxRuntime.jsx(material.Typography, { variant: "body1", className: classes.headingHelperText, children: "Discover potential issues & opportunities Alan can help you with!" }), jsxRuntime.jsx("div", { className: classes.rectanglesContainer, children: dataToMap.map((item, index) => (jsxRuntime.jsx(Rectangle, { type: item.type, icon: item.icon, title: item.title, description: item.description, onClick: () => handleRectangleClick(item?.agentId, item?.title), hoverable: legacyAgentScreen || displayQuestions }, index))) })] }));
4891
+ return (jsxRuntime.jsxs("div", { className: classes.placeholderContainer, children: [jsxRuntime.jsx("div", { className: classes.centerIconContainer, children: jsxRuntime.jsx(SvgCenter3D, { className: classes.centerIcon }) }), jsxRuntime.jsxs("div", { className: classes.headingContainer, children: [jsxRuntime.jsx(material.Typography, { variant: "h1", className: classes.heading, children: "Alan's Capabilities" }), jsxRuntime.jsx("span", { className: classes.alphaTag, children: "Beta" })] }), jsxRuntime.jsx(material.Typography, { variant: "body1", className: classes.headingHelperText, children: "Discover potential issues & opportunities Alan can help you with!" }), jsxRuntime.jsx("div", { className: classes.rectanglesContainer, children: dataToMap.map((item, index) => (jsxRuntime.jsx(Rectangle, { type: item.type, icon: item.icon, title: item.title, description: item.description, onClick: () => handleRectangleClick(item?.agentId, item?.title), hoverable: legacyAgentScreen || displayQuestions }, index))) })] }));
4834
4892
  };
4835
4893
 
4836
4894
  const dateFormat = "DD-MM-YYYY HH:mm:ss";
@@ -5408,6 +5466,7 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5408
5466
  status: "",
5409
5467
  });
5410
5468
  const chatbotContext = reactRedux.useSelector((state) => state.smartBotReducer.chatbotContext);
5469
+ const thinkingContext = reactRedux.useSelector((state) => state.smartBotReducer.thinkingContext);
5411
5470
  const handleButtonClick = (button) => {
5412
5471
  if (isStepFormSubmit) {
5413
5472
  // Build the user_input from chatbotContext
@@ -5422,7 +5481,6 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5422
5481
  : typeof value === 'number' ? value : String(value);
5423
5482
  }
5424
5483
  }
5425
- console.log("[ButtonContent] step form submit, calling init API with userInput:", userInput);
5426
5484
  dispatch(smartBotActions.setChatbotContext({}));
5427
5485
  // Call the init API via SSE directly
5428
5486
  callInitApiStream(userInput);
@@ -5481,6 +5539,9 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5481
5539
  dispatch(smartBotActions.setStepFormStreamData({ status: "streaming_start", chunks: [], sessionId }));
5482
5540
  // Fire DOM event so SmartBot can show stop icon (Redux gets cleared by TabularContent before parent effects run)
5483
5541
  window.dispatchEvent(new CustomEvent("stepFormStreamStart"));
5542
+ // Capture start time for computing elapsed on completion.
5543
+ // Timer keeps running from the original streamStartTime (set when first API was hit).
5544
+ const stepFormStreamStartTime = Date.now();
5484
5545
  sourceRef.current = AxiosSource(endPoint, {
5485
5546
  method: "POST",
5486
5547
  headers: {
@@ -5495,7 +5556,6 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5495
5556
  sourceRef.current.addEventListener("message", (event) => {
5496
5557
  try {
5497
5558
  const data = JSON.parse(event.data);
5498
- console.log("[ButtonContent] SSE chunk:", data);
5499
5559
  // Always keep stepFormStreamControl.sessionId in sync with the latest chunk's session_id
5500
5560
  if (data?.session_id) {
5501
5561
  stepFormStreamControl.sessionId = data.session_id;
@@ -5536,6 +5596,22 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5536
5596
  stepFormStreamControl.isStreaming = false;
5537
5597
  stepFormStreamControl.abort = null;
5538
5598
  window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
5599
+ // Stop the timer only when status:"completed" is received
5600
+ if (data?.status === "completed") {
5601
+ // Use the original streamStartTime from Redux (set when first API was hit)
5602
+ const originalStartTime = thinkingContext?.streamStartTime || stepFormStreamStartTime;
5603
+ const totalElapsed = Math.floor((Date.now() - originalStartTime) / 1000);
5604
+ const sfMin = Math.floor(totalElapsed / 60);
5605
+ const sfSec = totalElapsed % 60;
5606
+ const sfWorkedLabel = `Completed in ${sfMin}m:${String(sfSec).padStart(2, '0')}s`;
5607
+ dispatch(smartBotActions.setThinkingContext({
5608
+ streamStartTime: originalStartTime,
5609
+ isStreamCompleted: true,
5610
+ finalElapsedSeconds: totalElapsed,
5611
+ thinkingHeaderMessage: sfWorkedLabel,
5612
+ thinkingContent: "",
5613
+ }));
5614
+ }
5539
5615
  // Signal tab switch to agent_response when status is "completed",
5540
5616
  // but only if the response doesn't contain a new step_form that needs user interaction
5541
5617
  const hasStepForm = chunksRef.some((c) => c.status === "step_form");
@@ -6994,24 +7070,6 @@ function clearTabNotification() {
6994
7070
  * This survives the cloneDeep replacement of botData in SmartBot's useEffect.
6995
7071
  */
6996
7072
  const streamStateMap = new Map();
6997
- /**
6998
- * Formats thinking time to display minutes when appropriate
6999
- * @param {number} seconds - Time in seconds
7000
- * @returns {string} Formatted time string
7001
- */
7002
- const formatThinkingTime = (seconds) => {
7003
- if (seconds < 60) {
7004
- return `${seconds} second${seconds !== 1 ? 's' : ''}`;
7005
- }
7006
- const minutes = Math.floor(seconds / 60);
7007
- const remainingSeconds = seconds % 60;
7008
- if (remainingSeconds === 0) {
7009
- return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
7010
- }
7011
- else {
7012
- return `${minutes} min ${remainingSeconds} sec`;
7013
- }
7014
- };
7015
7073
  /**
7016
7074
  * StreamedContent Component
7017
7075
  * Handles real-time streaming of chat messages with a typing-like effect
@@ -7075,7 +7133,8 @@ const StreamedContent = ({ botData }) => {
7075
7133
  React.useRef(""); // Tracks the last received message
7076
7134
  const thinkingStartTimeRef = React.useRef(null);
7077
7135
  const thinkingTimeFinalRef = React.useRef(0);
7078
- const thinkingHeaderMessageRef = React.useRef("Planning next moves.....");
7136
+ const thinkingHeaderMessageRef = React.useRef("Working for 0m:00s");
7137
+ const streamStartTimeRef = React.useRef(null);
7079
7138
  const thinkingDoneRef = React.useRef(false);
7080
7139
  const setThinkingContentRef = React.useRef(setThinkingContent); // Store current setThinkingContent function
7081
7140
  const streamTimeoutRef = React.useRef(null); // Ref for stream timeout
@@ -7141,6 +7200,17 @@ const StreamedContent = ({ botData }) => {
7141
7200
  ? botData?.utilityObject?.method
7142
7201
  : "PUT";
7143
7202
  delete botData.inputBody.chat_input;
7203
+ // Start the timer immediately when the API is called
7204
+ if (!streamStartTimeRef.current) {
7205
+ streamStartTimeRef.current = Date.now();
7206
+ dispatch(smartBotActions.setThinkingContext({
7207
+ streamStartTime: streamStartTimeRef.current,
7208
+ isStreamCompleted: false,
7209
+ finalElapsedSeconds: null,
7210
+ thinkingHeaderMessage: "Working for 0m:00s",
7211
+ thinkingContent: "",
7212
+ }));
7213
+ }
7144
7214
  // Initialize SSE connection with API endpoint
7145
7215
  sourceRef.current = AxiosSource(endPoint, {
7146
7216
  method: method,
@@ -7229,7 +7299,7 @@ const StreamedContent = ({ botData }) => {
7229
7299
  setQuestionsStepsMap(lodash.cloneDeep(initialMap));
7230
7300
  }
7231
7301
  else if (data.status === "thinking") {
7232
- messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = "Planning next moves";
7302
+ messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = (jsxRuntime.jsx(ThinkinHeaderInfo, { thinkingHeaderMessage: "Working for 0m:00s" }));
7233
7303
  // Start thinking if not already started
7234
7304
  if (!isThinking) {
7235
7305
  setIsThinking(true);
@@ -7240,6 +7310,9 @@ const StreamedContent = ({ botData }) => {
7240
7310
  thinkingContentRef.current = thinkingContentRef.current + data.message;
7241
7311
  messageToStoreRef.current.chatData.thinkingResponse.thinkingStream = newValue;
7242
7312
  dispatch(smartBotActions.setThinkingContext({
7313
+ streamStartTime: streamStartTimeRef.current,
7314
+ isStreamCompleted: false,
7315
+ finalElapsedSeconds: null,
7243
7316
  thinkingHeaderMessage: thinkingHeaderMessageRef.current,
7244
7317
  thinkingContent: thinkingContentRef.current,
7245
7318
  }));
@@ -7372,7 +7445,8 @@ const StreamedContent = ({ botData }) => {
7372
7445
  stepFormStreamControl.baseUrl = _burl;
7373
7446
  // If this is the [DONE] chunk, mark streaming as complete
7374
7447
  if (data.message === "[DONE]") {
7375
- setStepsDone(true);
7448
+ // Do NOT set stepsDone here — step_form [DONE] is not status:"completed".
7449
+ // stepsDone should only be true when status:"completed" arrives.
7376
7450
  setIsStreamingDone(true);
7377
7451
  const doneState = streamStateMap.get(streamKey);
7378
7452
  if (doneState)
@@ -7401,10 +7475,13 @@ const StreamedContent = ({ botData }) => {
7401
7475
  // Store thinking time in messageToStoreRef for persistence
7402
7476
  messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
7403
7477
  thinkingTimeFinalRef.current = finalThinkingTime;
7404
- setThinkingHeaderMessage(`Thought for ${formatThinkingTime(finalThinkingTime)}`);
7405
- thinkingHeaderMessageRef.current = `Thought for ${formatThinkingTime(finalThinkingTime)}`;
7478
+ // Keep timer running — do NOT set "Worked for" here.
7479
+ // Timer continues showing "Working for Xm:Ys" until status:"completed" arrives.
7406
7480
  dispatch(smartBotActions.setThinkingContext({
7407
- thinkingHeaderMessage: `Thinking Completed`,
7481
+ streamStartTime: streamStartTimeRef.current,
7482
+ isStreamCompleted: false,
7483
+ finalElapsedSeconds: null,
7484
+ thinkingHeaderMessage: thinkingHeaderMessageRef.current,
7408
7485
  thinkingContent: thinkingContentRef.current,
7409
7486
  }));
7410
7487
  }
@@ -7518,11 +7595,31 @@ const StreamedContent = ({ botData }) => {
7518
7595
  if (currentMode === "agent") {
7519
7596
  setIsStop(false);
7520
7597
  // Use thinkingTime state instead of thinkingTimeFinalRef.current to ensure we have the correct value
7521
- thinkingTime || thinkingTimeFinalRef.current || 1;
7522
- messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = `Thinking Completed`;
7523
- // messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = `Thought for ${formatThinkingTime(
7524
- // finalThinkingTime
7525
- // )}`;
7598
+ const finalThinkingTime = thinkingTime || thinkingTimeFinalRef.current || 1;
7599
+ const completedElapsed = streamStartTimeRef.current
7600
+ ? Math.floor((Date.now() - streamStartTimeRef.current) / 1000)
7601
+ : finalThinkingTime;
7602
+ const compMin = Math.floor(completedElapsed / 60);
7603
+ const compSec = completedElapsed % 60;
7604
+ const workedForLabel = `Completed in ${compMin}m:${String(compSec).padStart(2, '0')}s`;
7605
+ // Only stop the timer if we got status:"completed" (stepsDone=true).
7606
+ // If stream ended with step_form [DONE], keep timer running for the next restream.
7607
+ if (stepsDone) {
7608
+ // Completed — use a static string so this chat's heading won't
7609
+ // re-animate when a future chat dispatches a new streamStartTime.
7610
+ messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = workedForLabel;
7611
+ dispatch(smartBotActions.setThinkingContext({
7612
+ streamStartTime: streamStartTimeRef.current,
7613
+ isStreamCompleted: true,
7614
+ finalElapsedSeconds: completedElapsed,
7615
+ thinkingHeaderMessage: workedForLabel,
7616
+ thinkingContent: thinkingContentRef.current,
7617
+ }));
7618
+ }
7619
+ else {
7620
+ // step_form case: keep heading as the live ThinkinHeaderInfo component so the timer keeps ticking
7621
+ messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = (jsxRuntime.jsx(ThinkinHeaderInfo, { thinkingHeaderMessage: "Working for 0m:00s" }));
7622
+ }
7526
7623
  let actualResponse = lodash.cloneDeep(messageToStoreRef.current.chatData.thinkingResponse.thinkingStream);
7527
7624
  messageToStoreRef.current.chatData.thinkingResponse.thinkingContent = actualResponse;
7528
7625
  parseResponse(messageToStoreRef.current.chatData, "text");
@@ -7763,6 +7860,20 @@ const StreamedContent = ({ botData }) => {
7763
7860
  setShowThoughtDropdown(true);
7764
7861
  messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
7765
7862
  }
7863
+ // Stop the timer on abort
7864
+ const abortElapsed = streamStartTimeRef.current
7865
+ ? Math.floor((Date.now() - streamStartTimeRef.current) / 1000)
7866
+ : 0;
7867
+ const abortMin = Math.floor(abortElapsed / 60);
7868
+ const abortSec = abortElapsed % 60;
7869
+ const abortLabel = `Completed in ${abortMin}m:${String(abortSec).padStart(2, '0')}s`;
7870
+ dispatch(smartBotActions.setThinkingContext({
7871
+ streamStartTime: streamStartTimeRef.current,
7872
+ isStreamCompleted: true,
7873
+ finalElapsedSeconds: abortElapsed,
7874
+ thinkingHeaderMessage: abortLabel,
7875
+ thinkingContent: thinkingContentRef.current,
7876
+ }));
7766
7877
  // If no content yet, show aborted message
7767
7878
  // if (isEmpty(messageToStoreRef.current.chatData?.response)) {
7768
7879
  messageToStoreRef.current.chatData.response =
@@ -11650,7 +11761,7 @@ const useConversationManagement = (chatDataRef, currentMode, activeConversationI
11650
11761
  thinkingStream: item.data,
11651
11762
  thinkingContent: item.data,
11652
11763
  thinkingTime: 1,
11653
- thinkingHeading: "Thinking Completed",
11764
+ thinkingHeading: "Completed in 0m:00s",
11654
11765
  };
11655
11766
  }
11656
11767
  else if (item.chatType === "steps") {
@@ -11845,7 +11956,9 @@ const useConversationManagement = (chatDataRef, currentMode, activeConversationI
11845
11956
  thinkingResponse: msg.thinkingResponse ? {
11846
11957
  thinkingStream: msg.thinkingResponse.thinkingStream,
11847
11958
  thinkingTime: msg.thinkingResponse.thinkingTime,
11848
- thinkingHeading: msg.thinkingResponse.thinkingHeading,
11959
+ thinkingHeading: typeof msg.thinkingResponse.thinkingHeading === 'string'
11960
+ ? msg.thinkingResponse.thinkingHeading
11961
+ : "Completed in 0m:00s",
11849
11962
  } : undefined,
11850
11963
  }
11851
11964
  }))
@@ -12621,7 +12734,10 @@ const SmartBot = (props) => {
12621
12734
  // if(!isEmpty(userInput)) {
12622
12735
  dispatch(smartBotActions.setThinkingContext({
12623
12736
  thinkingContent: "",
12624
- thinkingHeaderMessage: "Planning next moves.....",
12737
+ thinkingHeaderMessage: "Working for 0m:00s",
12738
+ streamStartTime: Date.now(),
12739
+ isStreamCompleted: false,
12740
+ finalElapsedSeconds: null,
12625
12741
  }));
12626
12742
  // }
12627
12743
  prepareDataAndSendToAgent(data, true, {
@@ -12836,7 +12952,10 @@ const SmartBot = (props) => {
12836
12952
  setShowChatPlaceholder(true);
12837
12953
  dispatch(smartBotActions.setThinkingContext({
12838
12954
  thinkingContent: "",
12839
- thinkingHeaderMessage: "Planning next moves.....",
12955
+ thinkingHeaderMessage: "Working for 0m:00s",
12956
+ streamStartTime: null,
12957
+ isStreamCompleted: false,
12958
+ finalElapsedSeconds: null,
12840
12959
  }));
12841
12960
  setInitValue(true);
12842
12961
  setSessionId("");