impact-chatbot 2.3.83 → 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 +99 -7
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +99 -7
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -17,9 +17,9 @@ import rehypeSanitize from 'rehype-sanitize';
|
|
|
17
17
|
import remarkBreaks from 'remark-breaks';
|
|
18
18
|
import remarkGfm from 'remark-gfm';
|
|
19
19
|
import DOMPurify from 'dompurify';
|
|
20
|
+
import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
|
|
20
21
|
import { setSelectedFilters, setFilterConfiguration, getFilterUserConfiguration } from 'core/actions/filterAction';
|
|
21
22
|
import { setChatbotContext, setStepFormStreamData, setMinimizedStreamData, setThinkingContext, setPersistedFormValues, clearPersistedFormValues, setCurrentAgentChatId, setHierarchyKeyValue, setChatbotFilterOptions, setSavedFilterSets } from 'core/actions/smartBotActions';
|
|
22
|
-
import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
|
|
23
23
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
|
24
24
|
import styled from 'styled-components';
|
|
25
25
|
import { CircularProgress, Typography, Grid } from '@mui/material';
|
|
@@ -1098,6 +1098,66 @@ const getFormattedApplicationName = (applicationURL) => {
|
|
|
1098
1098
|
}
|
|
1099
1099
|
};
|
|
1100
1100
|
|
|
1101
|
+
/**
|
|
1102
|
+
* Resolves a link href to an in-app route (path + search + hash) when the link
|
|
1103
|
+
* points to a screen of this application, otherwise returns null.
|
|
1104
|
+
* Handles both relative links ("/inventory-smart/create-allocation?step=0") and
|
|
1105
|
+
* absolute links sent by the backend
|
|
1106
|
+
* ("https://tapestry.test.impactsmartsuite.com/inventory-smart/create-allocation?step=0").
|
|
1107
|
+
*/
|
|
1108
|
+
const resolveInternalPath = (href) => {
|
|
1109
|
+
if (!href || typeof href !== 'string')
|
|
1110
|
+
return null;
|
|
1111
|
+
// Non navigational protocols (mailto:, tel:, javascript:, #anchor) stay as-is
|
|
1112
|
+
if (/^(mailto:|tel:|javascript:)/i.test(href) || href.startsWith('#'))
|
|
1113
|
+
return null;
|
|
1114
|
+
try {
|
|
1115
|
+
const url = new URL(href, window.location.origin);
|
|
1116
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
1117
|
+
return null;
|
|
1118
|
+
const currentApp = window.location.pathname.split('/')[1];
|
|
1119
|
+
const targetApp = url.pathname.split('/')[1];
|
|
1120
|
+
// Same origin, or a different host of the same app (e.g. backend returns the
|
|
1121
|
+
// deployed host while running locally) - both resolve to a client side route
|
|
1122
|
+
const isInternal = url.origin === window.location.origin ||
|
|
1123
|
+
(!!currentApp && currentApp === targetApp);
|
|
1124
|
+
if (!isInternal)
|
|
1125
|
+
return null;
|
|
1126
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
1127
|
+
}
|
|
1128
|
+
catch (e) {
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
/**
|
|
1133
|
+
* Minimizes the chat window so the user can see the screen they navigated to.
|
|
1134
|
+
* SmartBot (index.tsx) listens for this event and sets partialClose.
|
|
1135
|
+
*/
|
|
1136
|
+
const minimizeChatBot = () => {
|
|
1137
|
+
window.dispatchEvent(new CustomEvent("smartBotMinimize"));
|
|
1138
|
+
};
|
|
1139
|
+
/**
|
|
1140
|
+
* Anchor rendered inside chatbot markdown. Internal links are navigated through
|
|
1141
|
+
* react-router so the chatbot is not remounted by a full page load.
|
|
1142
|
+
*/
|
|
1143
|
+
const MarkdownLink = ({ href, children, ...props }) => {
|
|
1144
|
+
const navigate = useNavigate();
|
|
1145
|
+
const handleClick = (event) => {
|
|
1146
|
+
// Let the browser handle new tab / new window / download intents
|
|
1147
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0)
|
|
1148
|
+
return;
|
|
1149
|
+
const internalPath = resolveInternalPath(href);
|
|
1150
|
+
if (!internalPath)
|
|
1151
|
+
return;
|
|
1152
|
+
event.preventDefault();
|
|
1153
|
+
const currentPath = `${window.location.pathname}${window.location.search}`;
|
|
1154
|
+
if (currentPath !== internalPath) {
|
|
1155
|
+
navigate(internalPath);
|
|
1156
|
+
}
|
|
1157
|
+
minimizeChatBot();
|
|
1158
|
+
};
|
|
1159
|
+
return (jsx("a", { href: href, target: "_self", onClick: handleClick, ...props, children: children }));
|
|
1160
|
+
};
|
|
1101
1161
|
/**
|
|
1102
1162
|
* Checks whether the input string contains meaningful HTML markup
|
|
1103
1163
|
* (beyond simple inline tags like <b>, <i>, <em>, <strong>, <br>, <a>, <u>
|
|
@@ -1138,9 +1198,16 @@ const preprocessMarkdown = (content) => {
|
|
|
1138
1198
|
.replace(/(^|\n)([^\n-•]*[a-zA-Z0-9][^\n]*)\n([-•]\s+)/g, '$1$2\n\n$3')
|
|
1139
1199
|
// Handle list items with - or • appearing directly after text without newline
|
|
1140
1200
|
// Only treat as a new bullet when preceded by sentence-ending punctuation (to avoid splitting inline hyphens like "KSO- ORLANDO")
|
|
1141
|
-
|
|
1142
|
-
//
|
|
1143
|
-
.replace(/(\n
|
|
1201
|
+
// [^\S\n] (horizontal whitespace only) keeps this from crossing line breaks,
|
|
1202
|
+
// which would dedent already correctly indented nested bullets
|
|
1203
|
+
.replace(/([.!?:)"])[^\S\n]*([-\u2022])[^\S\n]+/g, '$1\n\n$2 ')
|
|
1204
|
+
// Handle nested list items with proper indentation.
|
|
1205
|
+
// Matching only horizontal whitespace after the newline keeps blank lines and
|
|
1206
|
+
// top level numbered items intact (indenting those turns them into plain text).
|
|
1207
|
+
// Normalize to 3 spaces: enough to nest under both "- " and "1. " parents, and
|
|
1208
|
+
// below the 4 space threshold that would turn the line into a code block.
|
|
1209
|
+
// Indents of 4 or more are left untouched (deliberate deeper nesting).
|
|
1210
|
+
.replace(/(\n[^\S\n]{2,3})([-\u2022]|\d+\.)[^\S\n]+/g, '\n $2 ')
|
|
1144
1211
|
// Ensure double line breaks after list sections
|
|
1145
1212
|
.replace(/(\n\d+\.\s+.*?)(\n\n###)/g, '$1\n$2')
|
|
1146
1213
|
.replace(/(\n[-•]\s+.*?)(\n\n###)/g, '$1\n$2')
|
|
@@ -1152,8 +1219,8 @@ const preprocessMarkdown = (content) => {
|
|
|
1152
1219
|
* Custom components for markdown rendering
|
|
1153
1220
|
*/
|
|
1154
1221
|
const markdownComponents = {
|
|
1155
|
-
// Custom link component that
|
|
1156
|
-
a:
|
|
1222
|
+
// Custom link component that navigates in-app without reloading the page
|
|
1223
|
+
a: MarkdownLink,
|
|
1157
1224
|
// Custom code component
|
|
1158
1225
|
code: ({ children, className, ...props }) => (jsx("code", { className: `markdown-code ${className || ''}`, ...props, children: children })),
|
|
1159
1226
|
// Custom pre component for code blocks
|
|
@@ -1173,13 +1240,32 @@ const markdownComponents = {
|
|
|
1173
1240
|
* Markdown renderer component with sanitization
|
|
1174
1241
|
*/
|
|
1175
1242
|
const TextRenderer = ({ text, thinking }) => {
|
|
1243
|
+
const navigate = useNavigate();
|
|
1244
|
+
// Click delegation for anchors inside raw HTML content (dangerouslySetInnerHTML),
|
|
1245
|
+
// so in-app links navigate through react-router instead of reloading the page.
|
|
1246
|
+
const handleHtmlClick = (event) => {
|
|
1247
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0)
|
|
1248
|
+
return;
|
|
1249
|
+
const anchor = event.target.closest?.('a');
|
|
1250
|
+
if (!anchor || anchor.target === '_blank')
|
|
1251
|
+
return;
|
|
1252
|
+
const internalPath = resolveInternalPath(anchor.getAttribute('href'));
|
|
1253
|
+
if (!internalPath)
|
|
1254
|
+
return;
|
|
1255
|
+
event.preventDefault();
|
|
1256
|
+
const currentPath = `${window.location.pathname}${window.location.search}`;
|
|
1257
|
+
if (currentPath !== internalPath) {
|
|
1258
|
+
navigate(internalPath);
|
|
1259
|
+
}
|
|
1260
|
+
minimizeChatBot();
|
|
1261
|
+
};
|
|
1176
1262
|
// If the input contains rich HTML, render it directly with DOMPurify sanitization
|
|
1177
1263
|
if (containsRichHtml(text)) {
|
|
1178
1264
|
const sanitizedHtml = DOMPurify.sanitize(text, {
|
|
1179
1265
|
ADD_TAGS: ['style'],
|
|
1180
1266
|
ADD_ATTR: ['style', 'class', 'target', 'rel'],
|
|
1181
1267
|
});
|
|
1182
|
-
return (jsx("div", { className: `markdown-content md-content ${thinking ? "thinking" : ""}`, dangerouslySetInnerHTML: { __html: sanitizedHtml } }));
|
|
1268
|
+
return (jsx("div", { className: `markdown-content md-content ${thinking ? "thinking" : ""}`, onClick: handleHtmlClick, dangerouslySetInnerHTML: { __html: sanitizedHtml } }));
|
|
1183
1269
|
}
|
|
1184
1270
|
// Otherwise, use the existing markdown pipeline
|
|
1185
1271
|
const processedContent = preprocessMarkdown(text);
|
|
@@ -15031,13 +15117,19 @@ const SmartBot = (props) => {
|
|
|
15031
15117
|
if (newChatId !== undefined)
|
|
15032
15118
|
setUniqueChatId(newChatId);
|
|
15033
15119
|
};
|
|
15120
|
+
// Minimize the chat window when the user follows an in-app link from a bot response
|
|
15121
|
+
const handleMinimizeRequest = () => {
|
|
15122
|
+
setPartialClose(true);
|
|
15123
|
+
};
|
|
15034
15124
|
window.addEventListener("stepFormStreamStart", handleStepFormStreamStart);
|
|
15035
15125
|
window.addEventListener("stepFormStreamEnd", handleStepFormStreamEnd);
|
|
15036
15126
|
window.addEventListener("stepFormInitStateUpdate", handleStepFormInitStateUpdate);
|
|
15127
|
+
window.addEventListener("smartBotMinimize", handleMinimizeRequest);
|
|
15037
15128
|
return () => {
|
|
15038
15129
|
window.removeEventListener("stepFormStreamStart", handleStepFormStreamStart);
|
|
15039
15130
|
window.removeEventListener("stepFormStreamEnd", handleStepFormStreamEnd);
|
|
15040
15131
|
window.removeEventListener("stepFormInitStateUpdate", handleStepFormInitStateUpdate);
|
|
15132
|
+
window.removeEventListener("smartBotMinimize", handleMinimizeRequest);
|
|
15041
15133
|
};
|
|
15042
15134
|
}, []);
|
|
15043
15135
|
const fetchCustomBotConfigurations = async () => {
|