llmasaservice-ui 0.16.2 → 0.16.3

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.css CHANGED
@@ -166,12 +166,15 @@
166
166
  border-radius: var(--border-radius);
167
167
  min-height: var(--input-height);
168
168
  max-height: 120px;
169
- resize: none;
169
+ resize: vertical;
170
170
  overflow-y: auto;
171
171
  background-color: var(--input-background-color);
172
172
  border: 1px solid var(--input-border-color);
173
173
  transition: height 0.1s ease;
174
174
  }
175
+ .llm-panel .chat-input.user-resized {
176
+ max-height: none;
177
+ }
175
178
  .llm-panel .chat-input:focus,
176
179
  .llm-panel input:focus {
177
180
  outline: none;
package/dist/index.js CHANGED
@@ -261,6 +261,7 @@ var ChatPanel = ({
261
261
  const [alwaysApprovedTools, setAlwaysApprovedTools] = (0, import_react3.useState)([]);
262
262
  const [thinkingBlocks, setThinkingBlocks] = (0, import_react3.useState)([]);
263
263
  const [currentThinkingIndex, setCurrentThinkingIndex] = (0, import_react3.useState)(0);
264
+ const [userResizedHeight, setUserResizedHeight] = (0, import_react3.useState)(null);
264
265
  const [pendingButtonAttachments, setPendingButtonAttachments] = (0, import_react3.useState)([]);
265
266
  const actionMatchRegistry = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
266
267
  const deferredActionsRef = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
@@ -515,7 +516,6 @@ var ChatPanel = ({
515
516
  const [toolsLoading, setToolsLoading] = (0, import_react3.useState)(false);
516
517
  const [toolsFetchError, setToolsFetchError] = (0, import_react3.useState)(false);
517
518
  (0, import_react3.useEffect)(() => {
518
- console.log("MCP servers", mcpServers);
519
519
  const fetchAndSetTools = () => __async(void 0, null, function* () {
520
520
  if (!mcpServers || mcpServers.length === 0) {
521
521
  setToolList([]);
@@ -562,7 +562,6 @@ var ChatPanel = ({
562
562
  }));
563
563
  const results = yield Promise.all(fetchPromises);
564
564
  const allTools = results.flat();
565
- console.log("Merged tools from all servers:", allTools);
566
565
  setToolList(allTools);
567
566
  setToolsFetchError(false);
568
567
  } catch (error) {
@@ -591,6 +590,122 @@ var ChatPanel = ({
591
590
  };
592
591
  })
593
592
  });
593
+ const processActionsOnContent = (0, import_react3.useCallback)((content, context) => {
594
+ let workingContent = content;
595
+ const buttonAttachments = [];
596
+ allActions.filter((a) => a.actionType === "tool").forEach((action) => {
597
+ try {
598
+ const regex = new RegExp(action.pattern, "gmi");
599
+ workingContent = workingContent.replace(regex, "");
600
+ } catch (e) {
601
+ console.warn("Invalid tool action regex", action.pattern, e);
602
+ }
603
+ });
604
+ if (context.type === "history") {
605
+ const { cleanedText } = processThinkingTags(workingContent);
606
+ workingContent = cleanedText;
607
+ }
608
+ allActions.filter((a) => a.type !== "response" && a.actionType !== "tool").forEach((action, actionIndex) => {
609
+ try {
610
+ const regex = new RegExp(action.pattern, "gmi");
611
+ const matches = workingContent.match(regex);
612
+ workingContent = workingContent.replace(
613
+ regex,
614
+ (match, ...groups) => {
615
+ var _a2, _b, _c, _d, _e;
616
+ const actualMatch = groups[0] || match;
617
+ const restGroups = groups.slice(1);
618
+ let buttonId;
619
+ if (context.type === "history") {
620
+ const matchIndex = restGroups[restGroups.length - 2];
621
+ buttonId = `button-init-${context.historyIndex}-${actionIndex}-${matchIndex}`;
622
+ } else {
623
+ const offset = groups[groups.length - 2];
624
+ const matchOffset = typeof offset === "number" ? offset : 0;
625
+ const key = `${actionIndex}:${matchOffset}`;
626
+ let existingButtonId = actionMatchRegistry.current.get(key);
627
+ if (!existingButtonId) {
628
+ existingButtonId = `button-stable-${actionSequenceRef.current++}`;
629
+ actionMatchRegistry.current.set(key, existingButtonId);
630
+ }
631
+ buttonId = existingButtonId;
632
+ if (context.isProgressive && finalizedButtonsRef.current.has(buttonId)) {
633
+ return match;
634
+ }
635
+ }
636
+ const substituteTemplate = (template) => {
637
+ let html2 = template.replace(/\$match/gim, actualMatch);
638
+ restGroups.forEach((g, gi) => {
639
+ html2 = html2.replace(new RegExp(`\\$${gi + 1}`, "gmi"), g || "");
640
+ });
641
+ return html2;
642
+ };
643
+ let html = actualMatch;
644
+ if (action.type === "button" || action.type === "callback") {
645
+ if (context.type === "history") {
646
+ html = `<br /><button id="${buttonId}" ${action.style ? 'class="' + action.style + '"' : ""}>${(_a2 = action.markdown) != null ? _a2 : actualMatch}</button>`;
647
+ } else {
648
+ if (context.isProgressive && !context.isIdle) {
649
+ html = `<br /><button id="${buttonId}" data-pending="true" ${action.style ? 'class="' + action.style + '"' : ""}>${substituteTemplate((_b = action.markdown) != null ? _b : actualMatch)}</button>`;
650
+ } else {
651
+ html = `<br /><button id="${buttonId}" ${action.style ? 'class="' + action.style + '"' : ""}>${substituteTemplate((_c = action.markdown) != null ? _c : actualMatch)}</button>`;
652
+ }
653
+ }
654
+ } else if (action.type === "markdown" || action.type === "html") {
655
+ html = context.type === "history" ? (_d = action.markdown) != null ? _d : "" : substituteTemplate((_e = action.markdown) != null ? _e : "");
656
+ }
657
+ if (context.type === "history") {
658
+ html = html.replace(new RegExp("\\$match", "gmi"), actualMatch);
659
+ restGroups.forEach((group, gi) => {
660
+ html = html.replace(
661
+ new RegExp(`\\$${gi + 1}`, "gmi"),
662
+ group || ""
663
+ );
664
+ });
665
+ }
666
+ if (action.type === "button" || action.type === "callback") {
667
+ if (context.type === "history") {
668
+ buttonAttachments.push({
669
+ buttonId,
670
+ action,
671
+ match: actualMatch,
672
+ groups: restGroups
673
+ });
674
+ buttonActionRegistry.current.set(buttonId, {
675
+ action,
676
+ match: actualMatch,
677
+ groups: restGroups
678
+ });
679
+ } else {
680
+ if (!finalizedButtonsRef.current.has(buttonId)) {
681
+ deferredActionsRef.current.set(buttonId, {
682
+ action,
683
+ match: actualMatch,
684
+ groups: restGroups
685
+ });
686
+ if (!context.isProgressive) {
687
+ buttonAttachments.push({
688
+ buttonId,
689
+ action,
690
+ match: actualMatch,
691
+ groups: restGroups
692
+ });
693
+ }
694
+ }
695
+ }
696
+ }
697
+ return html;
698
+ }
699
+ );
700
+ } catch (e) {
701
+ console.warn("Invalid action regex", action.pattern, e);
702
+ }
703
+ });
704
+ return {
705
+ processedContent: workingContent,
706
+ buttonAttachments
707
+ };
708
+ }, [allActions, processThinkingTags, progressiveActions, idle]);
594
709
  (0, import_react3.useEffect)(() => {
595
710
  setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
596
711
  if (customerEmailCaptureMode === "REQUIRED") {
@@ -605,24 +720,12 @@ var ChatPanel = ({
605
720
  if (action.type === "response" && action.pattern) {
606
721
  const regex = new RegExp(action.pattern, "gi");
607
722
  const matches = regex.exec(response);
608
- console.log(
609
- "action match",
610
- matches,
611
- action.pattern,
612
- action.callback
613
- );
614
723
  if (matches && action.callback) {
615
724
  action.callback(matches[0], matches.slice(1));
616
725
  }
617
726
  }
618
727
  });
619
728
  }
620
- if (lastCallId && lastCallId !== "" && idle && Object.keys(history).length > 0) {
621
- console.log("=== HISTORY FOR initialHistory prop ===");
622
- console.log("Copy this object to use as initialHistory:");
623
- console.log(JSON.stringify(history, null, 2));
624
- console.log("=== END HISTORY ===");
625
- }
626
729
  if (responseCompleteCallback) {
627
730
  if (lastCallId && lastCallId !== "" && idle)
628
731
  responseCompleteCallback(lastCallId, lastPrompt != null ? lastPrompt : "", response);
@@ -659,13 +762,11 @@ var ChatPanel = ({
659
762
  link.rel = "stylesheet";
660
763
  link.setAttribute("data-source", "llmasaservice-ui");
661
764
  document.head.appendChild(link);
662
- console.log("Added CSS link", link);
663
765
  } else {
664
766
  const style = document.createElement("style");
665
767
  style.textContent = cssUrl;
666
768
  style.setAttribute("data-source", "llmasaservice-ui");
667
769
  document.head.appendChild(style);
668
- console.log("Added inline CSS");
669
770
  }
670
771
  }
671
772
  return () => {
@@ -814,7 +915,6 @@ var ChatPanel = ({
814
915
  });
815
916
  }
816
917
  } else {
817
- console.warn("Invalid or missing callback in action:", action);
818
918
  return null;
819
919
  }
820
920
  } else {
@@ -830,8 +930,9 @@ var ChatPanel = ({
830
930
  };
831
931
  (0, import_react3.useEffect)(() => {
832
932
  const actionsString = typeof actions === "string" ? actions : JSON.stringify(actions);
933
+ const processedActions = getActionsArraySafely(actionsString);
833
934
  setAllActions([
834
- ...getActionsArraySafely(actionsString),
935
+ ...processedActions,
835
936
  anthropic_toolAction,
836
937
  openAI_toolAction,
837
938
  google_toolAction
@@ -852,63 +953,18 @@ var ChatPanel = ({
852
953
  processedHistoryKeysRef.current.add(prompt);
853
954
  return;
854
955
  }
855
- let workingContent = entry.content;
856
- allActions.filter((a) => a.actionType === "tool").forEach((action) => {
857
- try {
858
- const regex = new RegExp(action.pattern, "gmi");
859
- workingContent = workingContent.replace(regex, "");
860
- } catch (e) {
861
- console.warn("Invalid tool action regex", action.pattern, e);
862
- }
863
- });
864
- const { cleanedText } = processThinkingTags(workingContent);
865
- workingContent = cleanedText;
866
- allActions.filter((a) => a.type !== "response" && a.actionType !== "tool").forEach((action, actionIndex) => {
867
- try {
868
- const regex = new RegExp(action.pattern, "gmi");
869
- workingContent = workingContent.replace(
870
- regex,
871
- (match, ...groups) => {
872
- var _a2, _b;
873
- const matchIndex = groups[groups.length - 2];
874
- const buttonId = `button-init-${historyIndex}-${actionIndex}-${matchIndex}`;
875
- let html = match;
876
- if (action.type === "button" || action.type === "callback") {
877
- html = `<br /><button id="${buttonId}" ${action.style ? 'class="' + action.style + '"' : ""}>${(_a2 = action.markdown) != null ? _a2 : match}</button>`;
878
- } else if (action.type === "markdown" || action.type === "html") {
879
- html = (_b = action.markdown) != null ? _b : "";
880
- }
881
- html = html.replace(new RegExp("\\$match", "gmi"), match);
882
- groups.forEach((group, gi) => {
883
- html = html.replace(
884
- new RegExp(`\\$${gi + 1}`, "gmi"),
885
- group
886
- );
887
- });
888
- if (action.type === "button" || action.type === "callback") {
889
- newButtonAttachments.push({
890
- buttonId,
891
- action,
892
- match,
893
- groups
894
- });
895
- buttonActionRegistry.current.set(buttonId, {
896
- action,
897
- match,
898
- groups
899
- });
900
- }
901
- return html;
902
- }
903
- );
904
- } catch (e) {
905
- console.warn("Invalid action regex", action.pattern, e);
956
+ const { processedContent, buttonAttachments } = processActionsOnContent(
957
+ entry.content,
958
+ {
959
+ type: "history",
960
+ historyIndex
906
961
  }
907
- });
908
- if (workingContent !== entry.content) {
909
- updated[prompt] = __spreadProps(__spreadValues({}, entry), { content: workingContent });
962
+ );
963
+ if (processedContent !== entry.content) {
964
+ updated[prompt] = __spreadProps(__spreadValues({}, entry), { content: processedContent });
910
965
  changed = true;
911
966
  }
967
+ newButtonAttachments.push(...buttonAttachments);
912
968
  processedHistoryKeysRef.current.add(prompt);
913
969
  });
914
970
  if (newButtonAttachments.length > 0) {
@@ -928,7 +984,6 @@ var ChatPanel = ({
928
984
  if (!requests || requests.length === 0)
929
985
  requests = pendingToolRequestsRef.current;
930
986
  if (requests.length === 0) return;
931
- console.log("processGivenToolRequests", requests);
932
987
  setIsLoading(true);
933
988
  const toolsToProcess = [...requests];
934
989
  setPendingToolRequests([]);
@@ -973,7 +1028,6 @@ var ChatPanel = ({
973
1028
  var _a2;
974
1029
  if (!item || !item.req) return null;
975
1030
  const req = item.req;
976
- console.log(`Processing tool ${req.toolName}`);
977
1031
  const mcpTool = toolList.find((tool) => tool.name === req.toolName);
978
1032
  if (!mcpTool) {
979
1033
  console.error(`Tool ${req.toolName} not found in tool list`);
@@ -1120,7 +1174,6 @@ var ChatPanel = ({
1120
1174
  });
1121
1175
  }
1122
1176
  if (toolRequests.length > 0) {
1123
- console.log("toolRequests", toolRequests);
1124
1177
  setPendingToolRequests(toolRequests);
1125
1178
  } else {
1126
1179
  setPendingToolRequests([]);
@@ -1135,66 +1188,16 @@ var ChatPanel = ({
1135
1188
  const { cleanedText, thinkingBlocks: newThinkingBlocks } = processThinkingTags(responseWithoutTools);
1136
1189
  setThinkingBlocks(newThinkingBlocks);
1137
1190
  setCurrentThinkingIndex(Math.max(0, newThinkingBlocks.length - 1));
1138
- let newResponse = cleanedText;
1139
- if (allActions && allActions.length > 0) {
1140
- const nonToolActions = allActions.filter(
1141
- (a) => a.type !== "response" && a.actionType !== "tool"
1142
- );
1143
- nonToolActions.forEach((action, actionIndex) => {
1144
- try {
1145
- const regex = new RegExp(action.pattern, "gmi");
1146
- newResponse = newResponse.replace(
1147
- regex,
1148
- (match, ...groups) => {
1149
- var _a2, _b, _c;
1150
- const offset = groups[groups.length - 2];
1151
- const matchOffset = typeof offset === "number" ? offset : 0;
1152
- const key = `${actionIndex}:${matchOffset}`;
1153
- let buttonId = actionMatchRegistry.current.get(key);
1154
- if (!buttonId) {
1155
- buttonId = `button-stable-${actionSequenceRef.current++}`;
1156
- actionMatchRegistry.current.set(key, buttonId);
1157
- }
1158
- if (progressiveActions && finalizedButtonsRef.current.has(buttonId)) {
1159
- return match;
1160
- }
1161
- const substituteTemplate = (template) => {
1162
- let html = template.replace(/\$match/gim, match);
1163
- groups.forEach((g, gi) => {
1164
- html = html.replace(new RegExp(`\\$${gi + 1}`, "gmi"), g);
1165
- });
1166
- return html;
1167
- };
1168
- let htmlOut = match;
1169
- if (action.type === "button" || action.type === "callback") {
1170
- if (progressiveActions && !idle) {
1171
- htmlOut = `<br /><button id="${buttonId}" data-pending="true" ${action.style ? 'class="' + action.style + '"' : ""}>${substituteTemplate((_a2 = action.markdown) != null ? _a2 : match)}</button>`;
1172
- } else {
1173
- htmlOut = `<br /><button id="${buttonId}" ${action.style ? 'class="' + action.style + '"' : ""}>${substituteTemplate((_b = action.markdown) != null ? _b : match)}</button>`;
1174
- }
1175
- } else if (action.type === "markdown" || action.type === "html") {
1176
- htmlOut = substituteTemplate((_c = action.markdown) != null ? _c : "");
1177
- }
1178
- if ((action.type === "button" || action.type === "callback") && !finalizedButtonsRef.current.has(buttonId)) {
1179
- deferredActionsRef.current.set(buttonId, {
1180
- action,
1181
- match,
1182
- groups
1183
- });
1184
- if (!progressiveActions) {
1185
- setPendingButtonAttachments((prev) => [
1186
- ...prev,
1187
- { buttonId, action, match, groups }
1188
- ]);
1189
- }
1190
- }
1191
- return htmlOut;
1192
- }
1193
- );
1194
- } catch (e) {
1195
- console.warn("Invalid action regex", action.pattern, e);
1196
- }
1197
- });
1191
+ const { processedContent: newResponse, buttonAttachments } = processActionsOnContent(
1192
+ cleanedText,
1193
+ {
1194
+ type: "streaming",
1195
+ isProgressive: progressiveActions,
1196
+ isIdle: idle
1197
+ }
1198
+ );
1199
+ if (!progressiveActions && buttonAttachments.length > 0) {
1200
+ setPendingButtonAttachments((prev) => [...prev, ...buttonAttachments]);
1198
1201
  }
1199
1202
  setHistory((prevHistory) => {
1200
1203
  const existingEntry = prevHistory[lastKey != null ? lastKey : ""] || {
@@ -1423,16 +1426,50 @@ var ChatPanel = ({
1423
1426
  textarea.style.height = "auto";
1424
1427
  const minHeight = 40;
1425
1428
  const maxHeight = 120;
1429
+ const effectiveMinHeight = userResizedHeight ? Math.max(userResizedHeight, minHeight) : minHeight;
1430
+ const effectiveMaxHeight = userResizedHeight ? Number.MAX_SAFE_INTEGER : maxHeight;
1426
1431
  const newHeight = Math.min(
1427
- Math.max(textarea.scrollHeight, minHeight),
1428
- maxHeight
1432
+ Math.max(textarea.scrollHeight, effectiveMinHeight),
1433
+ effectiveMaxHeight
1429
1434
  );
1430
1435
  textarea.style.height = `${newHeight}px`;
1431
1436
  }
1432
- }, []);
1437
+ }, [userResizedHeight]);
1433
1438
  (0, import_react3.useEffect)(() => {
1434
1439
  autoResizeTextarea();
1435
1440
  }, [nextPrompt, autoResizeTextarea]);
1441
+ (0, import_react3.useEffect)(() => {
1442
+ if (!textareaRef.current) return;
1443
+ const textarea = textareaRef.current;
1444
+ let heightBeforeInteraction = textarea.clientHeight;
1445
+ let userIsInteracting = false;
1446
+ const handleMouseDown = () => {
1447
+ heightBeforeInteraction = textarea.clientHeight;
1448
+ userIsInteracting = true;
1449
+ };
1450
+ const handleMouseUp = () => {
1451
+ if (userIsInteracting) {
1452
+ const currentHeight = textarea.clientHeight;
1453
+ if (Math.abs(currentHeight - heightBeforeInteraction) > 5) {
1454
+ setUserResizedHeight(currentHeight);
1455
+ }
1456
+ userIsInteracting = false;
1457
+ }
1458
+ };
1459
+ const handleGlobalMouseUp = () => {
1460
+ if (userIsInteracting) {
1461
+ handleMouseUp();
1462
+ }
1463
+ };
1464
+ textarea.addEventListener("mousedown", handleMouseDown);
1465
+ textarea.addEventListener("mouseup", handleMouseUp);
1466
+ document.addEventListener("mouseup", handleGlobalMouseUp);
1467
+ return () => {
1468
+ textarea.removeEventListener("mousedown", handleMouseDown);
1469
+ textarea.removeEventListener("mouseup", handleMouseUp);
1470
+ document.removeEventListener("mouseup", handleGlobalMouseUp);
1471
+ };
1472
+ }, []);
1436
1473
  (0, import_react3.useEffect)(() => {
1437
1474
  if (initialPrompt && initialPrompt !== "") {
1438
1475
  if (initialPrompt !== lastPrompt) {
@@ -1500,7 +1537,6 @@ var ChatPanel = ({
1500
1537
  }
1501
1538
  }, [history, historyChangedCallback]);
1502
1539
  (0, import_react3.useEffect)(() => {
1503
- console.log("followOnPromptChanged detected");
1504
1540
  if (followOnPrompt && followOnPrompt !== "") {
1505
1541
  continueChat(followOnPrompt);
1506
1542
  }
@@ -1549,7 +1585,6 @@ var ChatPanel = ({
1549
1585
  return res.json();
1550
1586
  })).then((newConvo) => {
1551
1587
  if (newConvo == null ? void 0 : newConvo.id) {
1552
- console.log("new conversation created", newConvo.id);
1553
1588
  setCurrentConversation(newConvo.id);
1554
1589
  return newConvo.id;
1555
1590
  }
@@ -1593,11 +1628,6 @@ var ChatPanel = ({
1593
1628
  if (needsUpdate && (updatedValues.customer_id !== currentCustomer.customer_id || updatedValues.customer_user_email !== currentCustomer.customer_user_email)) {
1594
1629
  ensureConversation().then((convId) => {
1595
1630
  if (convId && convId !== "") {
1596
- console.log(
1597
- "updating conversation with customer id and email",
1598
- convId,
1599
- updatedValues
1600
- );
1601
1631
  fetch(`${publicAPIUrl}/conversations/${convId}`, {
1602
1632
  method: "POST",
1603
1633
  headers: {
@@ -1617,7 +1647,6 @@ var ChatPanel = ({
1617
1647
  }
1618
1648
  }, [currentCustomer, project_id, agent, publicAPIUrl, emailInput]);
1619
1649
  const continueChat = (suggestion) => {
1620
- console.log("continueChat called");
1621
1650
  setThinkingBlocks([]);
1622
1651
  setCurrentThinkingIndex(0);
1623
1652
  if (emailInput && isEmailAddress(emailInput) && !emailInputSet) {
@@ -1631,7 +1660,6 @@ var ChatPanel = ({
1631
1660
  }
1632
1661
  ensureConversation().then((convId) => {
1633
1662
  var _a2, _b, _c;
1634
- console.log("current customer", currentCustomer);
1635
1663
  if (!idle) {
1636
1664
  stop(lastController);
1637
1665
  setHistory((prevHistory) => {
@@ -1700,7 +1728,6 @@ var ChatPanel = ({
1700
1728
  }
1701
1729
  }
1702
1730
  }
1703
- console.log("Sending for conversation", convId);
1704
1731
  const controller = new AbortController();
1705
1732
  send(
1706
1733
  nextPromptToSend,
@@ -2427,7 +2454,7 @@ var ChatPanel = ({
2427
2454
  "textarea",
2428
2455
  {
2429
2456
  ref: textareaRef,
2430
- className: "chat-input",
2457
+ className: `chat-input${userResizedHeight ? " user-resized" : ""}`,
2431
2458
  placeholder,
2432
2459
  value: nextPrompt,
2433
2460
  onChange: (e) => {
@@ -2446,7 +2473,14 @@ var ChatPanel = ({
2446
2473
  }
2447
2474
  }
2448
2475
  },
2449
- disabled: isDisabledDueToNoEmail()
2476
+ onDoubleClick: () => {
2477
+ if (userResizedHeight) {
2478
+ setUserResizedHeight(null);
2479
+ setTimeout(autoResizeTextarea, 0);
2480
+ }
2481
+ },
2482
+ disabled: isDisabledDueToNoEmail(),
2483
+ title: userResizedHeight ? "Double-click to reset to auto-resize" : "Drag bottom-right corner to manually resize"
2450
2484
  }
2451
2485
  ), /* @__PURE__ */ import_react3.default.createElement(
2452
2486
  "button",