reactbridge-sdk 0.2.16 → 0.2.18

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.js CHANGED
@@ -525,6 +525,7 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
525
525
  let currentResponse = response;
526
526
  let toolCallCount = 0;
527
527
  const maxToolCalls = 50; // Prevent infinite loops
528
+ let previousResultResponse = null;
528
529
  while (currentToolCall && toolCallCount < maxToolCalls) {
529
530
  toolCallCount++;
530
531
  // Execute the current tool
@@ -532,6 +533,14 @@ function useReactBridge({ onIntentDetected, currentContext, onError, onSpeechSta
532
533
  // Step 3: Send tool result back to orchestrator
533
534
  if (lastRequestRef.current && currentResponse.sessionId) {
534
535
  const resultResponse = yield api.sendToolResult(currentResponse.sessionId, toolResult, lastRequestRef.current);
536
+ // Check if result is identical to previous one (prevent infinite loops on duplicate responses)
537
+ const currentResultString = JSON.stringify(resultResponse);
538
+ if (previousResultResponse === currentResultString) {
539
+ console.warn("Identical tool response detected. Exiting loop to prevent infinite loop.");
540
+ currentToolCall = null;
541
+ break;
542
+ }
543
+ previousResultResponse = currentResultString;
535
544
  // Add assistant message with tool result context
536
545
  const finalMessage = {
537
546
  id: `assistant-final-${Date.now()}-${toolCallCount}`,
@@ -881,6 +890,42 @@ toggleButtonClass = defaultToggleButtonClass, toggleButtonTitle = "Open chat ass
881
890
  document.addEventListener("mousedown", handleClickOutside);
882
891
  return () => document.removeEventListener("mousedown", handleClickOutside);
883
892
  }, [isUploadMenuOpen, isOpen, renderMode]);
893
+ // Add scrollbar styling for textareas
894
+ React.useEffect(() => {
895
+ const styleId = "react-bridge-textarea-styles";
896
+ // Check if styles are already injected
897
+ if (!document.getElementById(styleId)) {
898
+ const style = document.createElement("style");
899
+ style.id = styleId;
900
+ style.textContent = `
901
+ .react-bridge-textarea {
902
+ scrollbar-width: thin;
903
+ scrollbar-color: rgba(0, 0, 0, 0.3) transparent;
904
+ }
905
+
906
+ .react-bridge-textarea::-webkit-scrollbar {
907
+ width: 6px;
908
+ }
909
+
910
+ .react-bridge-textarea::-webkit-scrollbar-track {
911
+ background: transparent;
912
+ }
913
+
914
+ .react-bridge-textarea::-webkit-scrollbar-thumb {
915
+ background: rgba(0, 0, 0, 0.3);
916
+ border-radius: 3px;
917
+ }
918
+
919
+ .react-bridge-textarea::-webkit-scrollbar-thumb:hover {
920
+ background: rgba(0, 0, 0, 0.5);
921
+ }
922
+ `;
923
+ document.head.appendChild(style);
924
+ }
925
+ return () => {
926
+ // Optional: cleanup if needed
927
+ };
928
+ }, []);
884
929
  const handleSubmit = (e) => __awaiter(this, void 0, void 0, function* () {
885
930
  e.preventDefault();
886
931
  if ((!inputValue.trim() && !selectedFile) || isLoading)
@@ -1006,7 +1051,7 @@ toggleButtonClass = defaultToggleButtonClass, toggleButtonTitle = "Open chat ass
1006
1051
  gap: theme.spacing.sm,
1007
1052
  alignItems: "flex-end",
1008
1053
  } },
1009
- React.createElement("textarea", { ref: textareaRef, value: inputValue, onChange: handleTextareaChange, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isLoading, style: {
1054
+ React.createElement("textarea", { ref: textareaRef, value: inputValue, onChange: handleTextareaChange, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isLoading, className: "react-bridge-textarea", style: {
1010
1055
  flex: 1,
1011
1056
  padding: theme.spacing.sm,
1012
1057
  fontSize: theme.fontSizes.md,
@@ -1020,6 +1065,7 @@ toggleButtonClass = defaultToggleButtonClass, toggleButtonTitle = "Open chat ass
1020
1065
  minHeight: "40px",
1021
1066
  maxHeight: "120px",
1022
1067
  height: `${textareaHeight}px`,
1068
+ scrollbarWidth: "thin",
1023
1069
  } }),
1024
1070
  React.createElement("button", { type: "button", onClick: () => setIsUploadMenuOpen(!isUploadMenuOpen), disabled: isLoading, title: "Attach file", style: {
1025
1071
  padding: theme.spacing.sm,
@@ -1212,7 +1258,7 @@ toggleButtonClass = defaultToggleButtonClass, toggleButtonTitle = "Open chat ass
1212
1258
  gap: theme.spacing.sm,
1213
1259
  alignItems: "flex-end",
1214
1260
  } },
1215
- React.createElement("textarea", { ref: textareaRef, value: inputValue, onChange: handleTextareaChange, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isLoading, style: {
1261
+ React.createElement("textarea", { ref: textareaRef, value: inputValue, onChange: handleTextareaChange, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isLoading, className: "react-bridge-textarea", style: {
1216
1262
  flex: 1,
1217
1263
  padding: theme.spacing.sm,
1218
1264
  fontSize: theme.fontSizes.md,
@@ -1226,6 +1272,7 @@ toggleButtonClass = defaultToggleButtonClass, toggleButtonTitle = "Open chat ass
1226
1272
  minHeight: "40px",
1227
1273
  maxHeight: "120px",
1228
1274
  height: `${textareaHeight}px`,
1275
+ scrollbarWidth: "thin",
1229
1276
  } }),
1230
1277
  React.createElement("button", { type: "button", onClick: () => setIsUploadMenuOpen(!isUploadMenuOpen), disabled: isLoading, title: "Attach file", style: {
1231
1278
  padding: theme.spacing.sm,
@@ -1359,6 +1406,34 @@ const MIC_ICON_SVG = (React.createElement("svg", { xmlns: "http://www.w3.org/200
1359
1406
  // Plus Icon SVG
1360
1407
  const PLUS_ICON_SVG = (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "currentColor" },
1361
1408
  React.createElement("path", { d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" })));
1409
+ // Expandable Message Component for Search Results
1410
+ const SearchResultItem = ({ content, theme, isExpanded, onToggleExpand, }) => {
1411
+ const characterLimit = 300;
1412
+ const isLongMessage = content.length > characterLimit;
1413
+ const displayText = isExpanded ? content : content.substring(0, characterLimit);
1414
+ return (React.createElement("div", { style: {
1415
+ display: "flex",
1416
+ flexDirection: "column",
1417
+ gap: "8px",
1418
+ } },
1419
+ React.createElement("div", { style: {
1420
+ whiteSpace: "pre-wrap",
1421
+ wordWrap: "break-word",
1422
+ overflowWrap: "break-word",
1423
+ color: theme.colors.text,
1424
+ } }, displayText),
1425
+ isLongMessage && (React.createElement("button", { onClick: onToggleExpand, style: {
1426
+ background: "none",
1427
+ border: "none",
1428
+ color: theme.colors.primary,
1429
+ cursor: "pointer",
1430
+ textDecoration: "underline",
1431
+ padding: "0",
1432
+ textAlign: "left",
1433
+ fontSize: theme.fontSizes.sm,
1434
+ fontWeight: "bold",
1435
+ } }, isExpanded ? "Show less" : "Show more..."))));
1436
+ };
1362
1437
  function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = "Search...", width = "100%", maxResults = 5, theme: themeOverride, onError, onSpeechStart, onSpeechEnd, onTranscript, onAgentResponse, }) {
1363
1438
  const { theme: contextTheme } = useReactBridgeContext();
1364
1439
  const theme = Object.assign(Object.assign({}, contextTheme), themeOverride);
@@ -1376,6 +1451,7 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = "Se
1376
1451
  const [isUploadMenuOpen, setIsUploadMenuOpen] = React.useState(false);
1377
1452
  const [selectedFile, setSelectedFile] = React.useState(null);
1378
1453
  const [filePreview, setFilePreview] = React.useState(null);
1454
+ const [expandedMessages, setExpandedMessages] = React.useState(new Set());
1379
1455
  const containerRef = React.useRef(null);
1380
1456
  const fileInputRef = React.useRef(null);
1381
1457
  // Close dropdown and upload menu when clicking outside
@@ -1458,6 +1534,19 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = "Se
1458
1534
  fileInputRef.current.value = "";
1459
1535
  }
1460
1536
  };
1537
+ // Toggle message expansion
1538
+ const toggleMessageExpansion = React.useCallback((messageId) => {
1539
+ setExpandedMessages((prev) => {
1540
+ const newSet = new Set(prev);
1541
+ if (newSet.has(messageId)) {
1542
+ newSet.delete(messageId);
1543
+ }
1544
+ else {
1545
+ newSet.add(messageId);
1546
+ }
1547
+ return newSet;
1548
+ });
1549
+ }, []);
1461
1550
  const handleSubmit = (e) => __awaiter(this, void 0, void 0, function* () {
1462
1551
  e.preventDefault();
1463
1552
  if ((!inputValue.trim() && !selectedFile) || isLoading)
@@ -1484,7 +1573,8 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = "Se
1484
1573
  // Get only assistant messages (not system messages)
1485
1574
  const displayMessages = messages
1486
1575
  .filter((msg) => msg.role === "assistant")
1487
- .slice(-maxResults);
1576
+ .slice(-maxResults)
1577
+ .reverse();
1488
1578
  return (React.createElement("div", { ref: containerRef, style: {
1489
1579
  position: "relative",
1490
1580
  width,
@@ -1656,12 +1746,12 @@ function ReactBridgeSearch({ onIntentDetected, currentContext, placeholder = "Se
1656
1746
  borderBottom: `1px solid ${theme.colors.border}`,
1657
1747
  fontSize: theme.fontSizes.sm,
1658
1748
  color: theme.colors.text,
1659
- cursor: "pointer",
1660
1749
  }, onMouseEnter: (e) => {
1661
1750
  e.currentTarget.style.backgroundColor = theme.colors.surface;
1662
1751
  }, onMouseLeave: (e) => {
1663
1752
  e.currentTarget.style.backgroundColor = theme.colors.background;
1664
- } }, message.content)))))));
1753
+ } },
1754
+ React.createElement(SearchResultItem, { content: message.content, theme: theme, isExpanded: expandedMessages.has(message.id), onToggleExpand: () => toggleMessageExpansion(message.id) }))))))));
1665
1755
  }
1666
1756
 
1667
1757
  const darkTheme = {