@red-hat-developer-hub/backstage-plugin-intelligent-assistant 5.1.0 → 5.3.0

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.
Files changed (87) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +40 -5
  3. package/dist/alpha.d.ts +61 -0
  4. package/dist/api/LightspeedApiClient.esm.js +57 -0
  5. package/dist/api/LightspeedApiClient.esm.js.map +1 -1
  6. package/dist/api/api.esm.js.map +1 -1
  7. package/dist/components/DeleteModal.esm.js +2 -0
  8. package/dist/components/DeleteModal.esm.js.map +1 -1
  9. package/dist/components/DeleteSavedPromptModal.esm.js +75 -0
  10. package/dist/components/DeleteSavedPromptModal.esm.js.map +1 -0
  11. package/dist/components/LightSpeedChat.esm.js +276 -134
  12. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  13. package/dist/components/LightspeedChatBoxHeader.esm.js +57 -2
  14. package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
  15. package/dist/components/LightspeedChatContainer.esm.js +2 -1
  16. package/dist/components/LightspeedChatContainer.esm.js.map +1 -1
  17. package/dist/components/LightspeedDrawerContext.esm.js.map +1 -1
  18. package/dist/components/LightspeedDrawerProvider.esm.js +1 -0
  19. package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -1
  20. package/dist/components/McpServersSettings.esm.js +9 -22
  21. package/dist/components/McpServersSettings.esm.js.map +1 -1
  22. package/dist/components/MessageBarModelSelector.esm.js +104 -12
  23. package/dist/components/MessageBarModelSelector.esm.js.map +1 -1
  24. package/dist/components/SavedPromptCard.esm.js +100 -0
  25. package/dist/components/SavedPromptCard.esm.js.map +1 -0
  26. package/dist/components/SavedPromptKebabMenu.esm.js +87 -0
  27. package/dist/components/SavedPromptKebabMenu.esm.js.map +1 -0
  28. package/dist/components/SavedPromptMenuItems.esm.js +52 -0
  29. package/dist/components/SavedPromptMenuItems.esm.js.map +1 -0
  30. package/dist/components/SavedPromptsEmptyState.esm.js +63 -0
  31. package/dist/components/SavedPromptsEmptyState.esm.js.map +1 -0
  32. package/dist/components/SavedPromptsList.esm.js +63 -0
  33. package/dist/components/SavedPromptsList.esm.js.map +1 -0
  34. package/dist/components/SavedPromptsSettings.esm.js +285 -0
  35. package/dist/components/SavedPromptsSettings.esm.js.map +1 -0
  36. package/dist/components/ScreenContextChip.esm.js +172 -0
  37. package/dist/components/ScreenContextChip.esm.js.map +1 -0
  38. package/dist/components/SettingsPanel.esm.js +135 -0
  39. package/dist/components/SettingsPanel.esm.js.map +1 -0
  40. package/dist/hooks/screenContextLabelSubscription.esm.js +113 -0
  41. package/dist/hooks/screenContextLabelSubscription.esm.js.map +1 -0
  42. package/dist/hooks/useConversationHistoryGroups.esm.js +71 -0
  43. package/dist/hooks/useConversationHistoryGroups.esm.js.map +1 -0
  44. package/dist/hooks/useFeedbackActions.esm.js.map +1 -1
  45. package/dist/hooks/useLightspeedProviderState.esm.js +19 -5
  46. package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -1
  47. package/dist/hooks/useSavedPromptActions.esm.js +64 -0
  48. package/dist/hooks/useSavedPromptActions.esm.js.map +1 -0
  49. package/dist/hooks/useSavedPrompts.esm.js +75 -0
  50. package/dist/hooks/useSavedPrompts.esm.js.map +1 -0
  51. package/dist/hooks/useSavedPromptsSettings.esm.js +65 -0
  52. package/dist/hooks/useSavedPromptsSettings.esm.js.map +1 -0
  53. package/dist/hooks/useScreenContextLabelRevision.esm.js +13 -0
  54. package/dist/hooks/useScreenContextLabelRevision.esm.js.map +1 -0
  55. package/dist/hooks/useScreenContextSettings.esm.js +110 -0
  56. package/dist/hooks/useScreenContextSettings.esm.js.map +1 -0
  57. package/dist/hooks/useSettingsPanelUrlState.esm.js +95 -0
  58. package/dist/hooks/useSettingsPanelUrlState.esm.js.map +1 -0
  59. package/dist/hooks/useWelcomePrompts.esm.js +16 -5
  60. package/dist/hooks/useWelcomePrompts.esm.js.map +1 -1
  61. package/dist/hooks/useWindowLocation.esm.js +28 -0
  62. package/dist/hooks/useWindowLocation.esm.js.map +1 -0
  63. package/dist/translations/de.esm.js +64 -1
  64. package/dist/translations/de.esm.js.map +1 -1
  65. package/dist/translations/es.esm.js +64 -1
  66. package/dist/translations/es.esm.js.map +1 -1
  67. package/dist/translations/fr.esm.js +64 -1
  68. package/dist/translations/fr.esm.js.map +1 -1
  69. package/dist/translations/it.esm.js +64 -1
  70. package/dist/translations/it.esm.js.map +1 -1
  71. package/dist/translations/ja.esm.js +64 -1
  72. package/dist/translations/ja.esm.js.map +1 -1
  73. package/dist/translations/ref.esm.js +64 -2
  74. package/dist/translations/ref.esm.js.map +1 -1
  75. package/dist/utils/buildConversationHistoryGroups.esm.js +241 -0
  76. package/dist/utils/buildConversationHistoryGroups.esm.js.map +1 -0
  77. package/dist/utils/dom-selectors.esm.js +22 -2
  78. package/dist/utils/dom-selectors.esm.js.map +1 -1
  79. package/dist/utils/prompt-utils.esm.js +18 -1
  80. package/dist/utils/prompt-utils.esm.js.map +1 -1
  81. package/dist/utils/screen-capture.esm.js +116 -0
  82. package/dist/utils/screen-capture.esm.js.map +1 -0
  83. package/dist/utils/screen-context-utils.esm.js +343 -0
  84. package/dist/utils/screen-context-utils.esm.js.map +1 -0
  85. package/dist/utils/sensitive-data-redactor.esm.js +39 -1
  86. package/dist/utils/sensitive-data-redactor.esm.js.map +1 -1
  87. package/package.json +5 -15
@@ -8,7 +8,7 @@ import Tabs from '@mui/material/Tabs';
8
8
  import { Chatbot, ChatbotHeader, ChatbotHeaderTitle, ChatbotFooter, MessageBar, ChatbotContent, Settings, ChatbotDisplayMode, ChatbotHeaderMain, ChatbotAlert, FileDropZone } from '@patternfly/chatbot';
9
9
  import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
10
10
  import { SelectList, DropdownItem, Tooltip, MenuToggle, Select, SelectOption, Button, Title, Label } from '@patternfly/react-core';
11
- import { PenIcon, ThumbtackIcon, TrashIcon, SearchIcon, SortAmountDownAltIcon, SortAmountDownIcon, PlusIcon } from '@patternfly/react-icons';
11
+ import { PenIcon, ThumbtackIcon, TrashIcon, SortAmountDownAltIcon, SortAmountDownIcon, PlusIcon, SearchIcon } from '@patternfly/react-icons';
12
12
  import { RhUiAiExperienceIcon } from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-experience-icon';
13
13
  import { useQueryClient } from '@tanstack/react-query';
14
14
  import { LIGHTSPEED_PATH, UNTITLED_NOTEBOOK_NAME, TEMP_CONVERSATION_ID, supportedFileTypes } from '../const.esm.js';
@@ -29,6 +29,13 @@ import { useNotebookConversationIds } from '../hooks/useNotebookConversationIds.
29
29
  import { useNotebookSession } from '../hooks/notebooks/useNotebookSession.esm.js';
30
30
  import { useNotebookSessions } from '../hooks/notebooks/useNotebookSessions.esm.js';
31
31
  import { usePinnedChatsSettings } from '../hooks/usePinnedChatsSettings.esm.js';
32
+ import { useScreenContextSettings } from '../hooks/useScreenContextSettings.esm.js';
33
+ import { buildScreenContextAttachments } from '../utils/screen-context-utils.esm.js';
34
+ import { useSavedPromptsSettings } from '../hooks/useSavedPromptsSettings.esm.js';
35
+ import { useSavedPrompts } from '../hooks/useSavedPrompts.esm.js';
36
+ import { useSavedPromptActions } from '../hooks/useSavedPromptActions.esm.js';
37
+ import { useConversationHistoryGroups } from '../hooks/useConversationHistoryGroups.esm.js';
38
+ import { useSettingsPanelUrlState } from '../hooks/useSettingsPanelUrlState.esm.js';
32
39
  import '../api/notebooksApi.esm.js';
33
40
  import { useRenameNotebookWithAlert } from '../hooks/notebooks/useRenameNotebookWithAlert.esm.js';
34
41
  import { useSortSettings } from '../hooks/useSortSettings.esm.js';
@@ -38,16 +45,16 @@ import { useNotebookDocuments } from '../hooks/notebooks/useNotebookDocuments.es
38
45
  import { useLightspeedDrawerContext } from '../hooks/useLightspeedDrawerContext.esm.js';
39
46
  import { useWelcomePrompts } from '../hooks/useWelcomePrompts.esm.js';
40
47
  import { getAttachments } from '../utils/attachment-utils.esm.js';
41
- import { extractPageContext } from '../utils/dom-extractor.esm.js';
48
+ import { isSavedPromptConversationId, SAVED_PROMPT_CONVERSATION_ID_PREFIX } from '../utils/buildConversationHistoryGroups.esm.js';
42
49
  import { getCategorizeMessages, ChatbotFootnoteWithIcon, getFootnoteProps } from '../utils/lightspeed-chatbox-utils.esm.js';
43
50
  import Attachment from './Attachment.esm.js';
44
51
  import { useFileAttachmentContext } from './AttachmentContext.esm.js';
45
52
  import { CollapsedHistoryStrip } from './CollapsedHistoryStrip.esm.js';
46
53
  import { DeleteModal } from './DeleteModal.esm.js';
54
+ import { DeleteSavedPromptModal } from './DeleteSavedPromptModal.esm.js';
47
55
  import FilePreview from './FilePreview.esm.js';
48
56
  import { LightspeedChatBox } from './LightspeedChatBox.esm.js';
49
57
  import { LightspeedChatBoxHeader } from './LightspeedChatBoxHeader.esm.js';
50
- import { McpServersSettings } from './McpServersSettings.esm.js';
51
58
  import { MessageBarModelSelector } from './MessageBarModelSelector.esm.js';
52
59
  import { DeleteNotebookModal } from './notebooks/DeleteNotebookModal.esm.js';
53
60
  import { NotebookHeaderActions } from './notebooks/NotebookHeaderActions.esm.js';
@@ -56,6 +63,9 @@ import { useNotebookStreamStore } from './notebooks/NotebookStreamProvider.esm.j
56
63
  import { NotebookView } from './notebooks/NotebookView.esm.js';
57
64
  import { SidebarCollapseIcon, SidebarExpandIcon } from './notebooks/SidebarCollapseIcon.esm.js';
58
65
  import { RenameConversationModal } from './RenameConversationModal.esm.js';
66
+ import { SavedPromptMenuItems } from './SavedPromptMenuItems.esm.js';
67
+ import { ScreenContextChip } from './ScreenContextChip.esm.js';
68
+ import { SettingsPanel } from './SettingsPanel.esm.js';
59
69
  import { ToastAlertGroup } from './ToastAlertGroup.esm.js';
60
70
 
61
71
  const COLLAPSE_PANEL_ICON_SVG = `url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16 21V3H14V21H16ZM12 17V7L7 12L12 17Z' fill='black'/%3E%3C/svg%3E") no-repeat center`;
@@ -81,6 +91,24 @@ const StyledChatbot = styled(Chatbot, {
81
91
  height: 0,
82
92
  overflow: "hidden"
83
93
  },
94
+ // Remove once @patternfly/chatbot supports ConversationGroup header actions:
95
+ // https://github.com/patternfly/chatbot/issues/904
96
+ "& .pf-chatbot__history-menu .pf-v6-c-menu__list": {
97
+ paddingInlineStart: 0
98
+ },
99
+ "& .pf-chatbot__menu-show-all-toggle": {
100
+ cursor: "pointer"
101
+ },
102
+ "& .pf-chatbot__menu-item-header--expandable.lightspeed-saved-prompts-group .pf-v6-c-button__text": {
103
+ width: "100%"
104
+ },
105
+ "& .lightspeed-saved-prompts-section-gear": {
106
+ opacity: 0,
107
+ transition: "opacity 0.15s ease-in-out"
108
+ },
109
+ "& .pf-chatbot__menu-item-header--expandable.lightspeed-saved-prompts-group:hover .lightspeed-saved-prompts-section-gear, & .pf-chatbot__menu-item-header--expandable.lightspeed-saved-prompts-group .lightspeed-saved-prompts-section-gear:focus-visible": {
110
+ opacity: 1
111
+ },
84
112
  "& .pf-chatbot-container": {
85
113
  minHeight: 0
86
114
  },
@@ -211,6 +239,14 @@ const StyledMessageBar = styled(MessageBar)(({ theme }) => ({
211
239
  display: "none"
212
240
  }
213
241
  }));
242
+ const MessageBarActionsRow = styled("div")({
243
+ display: "flex",
244
+ flex: "1 1 0",
245
+ minWidth: 0,
246
+ overflow: "hidden",
247
+ alignItems: "center",
248
+ gap: 8
249
+ });
214
250
  const StyledSelectList = styled(SelectList)({
215
251
  padding: 0,
216
252
  margin: 0
@@ -233,6 +269,19 @@ const StyledChatbotContent = styled(ChatbotContent, {
233
269
  overflowX: "hidden",
234
270
  overflowWrap: "break-word",
235
271
  wordBreak: "break-word"
272
+ },
273
+ // Remove once @patternfly/chatbot adds a responsive grid layout for prompt suggestions:
274
+ // https://github.com/patternfly/chatbot/issues/905
275
+ "& .pf-chatbot__prompt-suggestions": {
276
+ display: "grid !important",
277
+ flexDirection: "unset !important",
278
+ alignItems: "stretch",
279
+ gap: "var(--pf-t--global--spacer--lg)",
280
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 12.5rem), 1fr))"
281
+ },
282
+ "& .pf-chatbot__prompt-suggestions > .pf-chatbot__prompt-suggestion": {
283
+ height: "100%",
284
+ minWidth: 0
236
285
  }
237
286
  }));
238
287
  const ContentScroll = styled("div", {
@@ -383,6 +432,15 @@ const FullscreenChatLayout = styled("div")({
383
432
  "--pf-v6-c-menu__item--PaddingInlineStart": "var(--pf-t--global--spacer--md)",
384
433
  "--pf-v6-c-menu__item--PaddingInlineEnd": "var(--pf-t--global--spacer--md)"
385
434
  },
435
+ "& .pf-chatbot__menu-item .pf-v6-c-menu__item-main, & .pf-chatbot__menu-item .pf-v5-c-menu__item-main": {
436
+ minWidth: 0,
437
+ overflow: "hidden"
438
+ },
439
+ "& .pf-chatbot__menu-item-text, & .pf-v6-c-menu__item-text, & .pf-v5-c-menu__item-text": {
440
+ overflow: "hidden",
441
+ textOverflow: "ellipsis",
442
+ whiteSpace: "nowrap"
443
+ },
386
444
  "& .pf-chatbot__menu-item .pf-v6-c-menu-toggle, & .pf-chatbot__menu-item .pf-v5-c-menu-toggle": {
387
445
  opacity: 0,
388
446
  transition: "opacity 0.15s ease-in-out"
@@ -415,6 +473,9 @@ const LightspeedChat = ({
415
473
  const domExtractionMaxChars = configApi.getOptionalNumber(
416
474
  "intelligent-assistant.screen-context.dom-extraction.maxChars"
417
475
  ) ?? 8e3;
476
+ const screenshotsEnabled = configApi.getOptionalBoolean(
477
+ "intelligent-assistant.screen-context.screenshots.enabled"
478
+ ) ?? true;
418
479
  const notebooksRouteMatch = useMatch(`${LIGHTSPEED_PATH}/notebooks`);
419
480
  const notebookViewRouteMatch = useMatch(
420
481
  `${LIGHTSPEED_PATH}/notebooks/:notebookId`
@@ -434,9 +495,23 @@ const LightspeedChat = ({
434
495
  shellViewTab,
435
496
  setShellViewTab,
436
497
  activeNotebookId,
437
- setActiveNotebookId
498
+ setActiveNotebookId,
499
+ settingsTab: persistedSettingsTab,
500
+ setSettingsTab: setPersistedSettingsTab
438
501
  } = useLightspeedDrawerContext();
439
502
  const isFullscreenMode = displayMode === ChatbotDisplayMode.embedded;
503
+ const {
504
+ isOpen: isSettingsOpen,
505
+ activeTab: activeSettingsTab,
506
+ openSettings,
507
+ closeSettings,
508
+ setActiveTab: setSettingsTab
509
+ } = useSettingsPanelUrlState(
510
+ isFullscreenMode,
511
+ persistedSettingsTab,
512
+ setPersistedSettingsTab
513
+ );
514
+ const showCompactSettings = !isFullscreenMode && isSettingsOpen;
440
515
  const location = useLocation();
441
516
  const isNotebooksFullscreenPath = location.pathname === `${LIGHTSPEED_PATH}/notebooks` || location.pathname.startsWith(`${LIGHTSPEED_PATH}/notebooks/`);
442
517
  const user = useBackstageUserIdentity();
@@ -515,7 +590,6 @@ const LightspeedChat = ({
515
590
  const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
516
591
  const [isRenameModalOpen, setIsRenameModalOpen] = useState(false);
517
592
  const [isSortSelectOpen, setIsSortSelectOpen] = useState(false);
518
- const [isMcpSettingsOpen, setIsMcpSettingsOpen] = useState(false);
519
593
  const [chatHeaderBgColor, setChatHeaderBgColor] = useState();
520
594
  const contentScrollRef = useRef(null);
521
595
  const bottomSentinelRef = useRef(null);
@@ -736,12 +810,17 @@ const LightspeedChat = ({
736
810
  if (computedBg) {
737
811
  setChatHeaderBgColor(computedBg);
738
812
  }
739
- }, [displayMode, isMcpSettingsOpen]);
813
+ }, [displayMode, isSettingsOpen]);
740
814
  useEffect(() => {
741
- if (!mcpToolsPermissionResolved && isMcpSettingsOpen) {
742
- setIsMcpSettingsOpen(false);
815
+ if (!mcpToolsPermissionResolved && isSettingsOpen && activeSettingsTab === "mcp-servers") {
816
+ setSettingsTab("saved-prompts");
743
817
  }
744
- }, [mcpToolsPermissionResolved, isMcpSettingsOpen]);
818
+ }, [
819
+ mcpToolsPermissionResolved,
820
+ isSettingsOpen,
821
+ activeSettingsTab,
822
+ setSettingsTab
823
+ ]);
745
824
  const {
746
825
  isPinningChatsEnabled,
747
826
  pinnedChats,
@@ -749,6 +828,23 @@ const LightspeedChat = ({
749
828
  pinChat,
750
829
  unpinChat
751
830
  } = usePinnedChatsSettings(user);
831
+ const { isSavedPromptsEnabled, handleSavedPromptsToggle } = useSavedPromptsSettings(user);
832
+ const {
833
+ isScreenContextSharingEnabled,
834
+ isScreenContextPaused,
835
+ handleScreenContextSharingToggle,
836
+ toggleScreenContextPaused
837
+ } = useScreenContextSettings(user);
838
+ const selectedModelSupportsVision = useMemo(
839
+ () => models.find((m) => m.value === selectedModel)?.supportsVision ?? false,
840
+ [models, selectedModel]
841
+ );
842
+ let screenContextChipState = "recording";
843
+ if (isFullscreenMode) {
844
+ screenContextChipState = "unavailable";
845
+ } else if (isScreenContextPaused) {
846
+ screenContextChipState = "paused";
847
+ }
752
848
  const { selectedSort, handleSortChange } = useSortSettings(user);
753
849
  const {
754
850
  uploadError,
@@ -803,7 +899,18 @@ const LightspeedChat = ({
803
899
  isLoading,
804
900
  isRefetching
805
901
  } = useConversations(chatPermissionResolved);
806
- const samplePrompts = useWelcomePrompts(chatPermissionResolved);
902
+ const {
903
+ savedPrompts,
904
+ config: savedPromptsConfig,
905
+ loading: savedPromptsLoading,
906
+ error: savedPromptsError,
907
+ createPrompt,
908
+ deletePrompt
909
+ } = useSavedPrompts();
910
+ const samplePrompts = useWelcomePrompts(
911
+ chatPermissionResolved,
912
+ isSavedPromptsEnabled ? savedPrompts : void 0
913
+ );
807
914
  useEffect(() => {
808
915
  if (!user || !isReady) return;
809
916
  const onOverlayLikeSurface = isFullscreenMode || !routeConversationId;
@@ -926,31 +1033,54 @@ const LightspeedChat = ({
926
1033
  prompt: message.toString()
927
1034
  })
928
1035
  );
929
- const allAttachments = getAttachments(fileContents);
930
- if (screenContextEnabled && domExtractionEnabled) {
1036
+ const messageText = message.toString();
1037
+ setIsSendButtonDisabled(true);
1038
+ void (async () => {
1039
+ const allAttachments = getAttachments(fileContents);
931
1040
  try {
932
- const domContext = extractPageContext({
933
- maxChars: domExtractionMaxChars
1041
+ const screenAttachments = await buildScreenContextAttachments({
1042
+ adminEnabled: screenContextEnabled,
1043
+ sharingEnabled: isScreenContextSharingEnabled,
1044
+ paused: isScreenContextPaused,
1045
+ isFullscreen: isFullscreenMode,
1046
+ domEnabled: domExtractionEnabled,
1047
+ screenshotsEnabled,
1048
+ supportsVision: selectedModelSupportsVision,
1049
+ domExtractionMaxChars
934
1050
  });
935
- if (domContext) {
936
- allAttachments.push({
937
- attachment_type: "configuration",
938
- content_type: "text/plain",
939
- content: domContext
940
- });
941
- }
1051
+ allAttachments.push(...screenAttachments);
942
1052
  } catch {
943
1053
  }
944
- }
945
- handleInputPrompt(message.toString(), allAttachments);
946
- setIsSendButtonDisabled(true);
947
- setFileContents([]);
948
- setDraftMessage("");
1054
+ handleInputPrompt(messageText, allAttachments);
1055
+ setFileContents([]);
1056
+ setDraftMessage("");
1057
+ })();
949
1058
  };
1059
+ const handleApplySavedPrompt = useCallback(
1060
+ (content) => {
1061
+ setDraftMessage(content);
1062
+ setMessageBarKey((k) => k + 1);
1063
+ },
1064
+ [setDraftMessage]
1065
+ );
1066
+ const {
1067
+ sendDirectly: sendSavedPromptDirectly,
1068
+ requestDelete: requestSavedPromptDelete,
1069
+ promptToDelete,
1070
+ closeDeleteModal: closeSavedPromptDeleteModal,
1071
+ confirmDelete: confirmSavedPromptDelete,
1072
+ isDeleting: isSavedPromptDeleting,
1073
+ deleteError: savedPromptDeleteError,
1074
+ isDeleteModalOpen: isSavedPromptDeleteModalOpen
1075
+ } = useSavedPromptActions({
1076
+ onApplyToInput: handleApplySavedPrompt,
1077
+ onSendDirectly: sendMessage,
1078
+ onDelete: deletePrompt
1079
+ });
950
1080
  const onNewChat = useCallback(() => {
951
1081
  (async () => {
952
1082
  if (!isFullscreenMode) {
953
- setIsMcpSettingsOpen(false);
1083
+ closeSettings();
954
1084
  }
955
1085
  if (conversationId !== TEMP_CONVERSATION_ID) {
956
1086
  setMessages([]);
@@ -974,7 +1104,8 @@ const LightspeedChat = ({
974
1104
  setUploadError,
975
1105
  setDraftMessage,
976
1106
  setCurrentConversationId,
977
- isFullscreenMode
1107
+ isFullscreenMode,
1108
+ closeSettings
978
1109
  ]);
979
1110
  const openDeleteModal = (conversation_id) => {
980
1111
  setTargetConversationId(conversation_id);
@@ -1064,76 +1195,47 @@ const LightspeedChat = ({
1064
1195
  selectedSort
1065
1196
  ]
1066
1197
  );
1067
- const filterConversations = useCallback(
1068
- (targetValue) => {
1069
- const pinnedChatsKey = t("conversation.category.pinnedChats") || "Pinned chats";
1070
- let isNoPinnedChatsSearchResults = false;
1071
- let isNoRecentChatsSearchResults = false;
1072
- const filteredConversations2 = Object.entries(categorizedMessages).reduce(
1073
- (acc, [key, items]) => {
1074
- const filteredItems = items.filter(
1075
- (item) => (item.text ?? "").toLocaleLowerCase("en-US").includes(targetValue.toLocaleLowerCase("en-US"))
1076
- );
1077
- const isPinnedCategory = key === pinnedChatsKey;
1078
- if (isPinnedCategory && isPinningChatsEnabled) {
1079
- if (filteredItems.length > 0) {
1080
- acc[pinnedChatsKey] = filteredItems;
1081
- } else {
1082
- isNoPinnedChatsSearchResults = categorizedMessages[pinnedChatsKey].length > 0;
1083
- acc[pinnedChatsKey] = [
1084
- {
1085
- id: isNoPinnedChatsSearchResults ? "no-pinned-chats-search-results" : "no-pinned-chats",
1086
- text: isNoPinnedChatsSearchResults ? t("common.noSearchResults") : t("chatbox.emptyState.noPinnedChats"),
1087
- noIcon: true,
1088
- additionalProps: {
1089
- isDisabled: true,
1090
- style: {
1091
- fontStyle: "italic",
1092
- opacity: 0.6
1093
- }
1094
- }
1095
- }
1096
- ];
1097
- }
1098
- } else if (!isPinnedCategory) {
1099
- if (filteredItems.length > 0) {
1100
- acc[key] = filteredItems;
1101
- } else {
1102
- isNoRecentChatsSearchResults = categorizedMessages[key].length > 0;
1103
- acc[key] = [
1104
- {
1105
- id: isNoRecentChatsSearchResults ? "no-recent-chats-search-results" : "no-recent-chats",
1106
- text: isNoRecentChatsSearchResults ? t("common.noSearchResults") : t("chatbox.emptyState.noRecentChats"),
1107
- noIcon: true,
1108
- additionalProps: {
1109
- isDisabled: true,
1110
- style: {
1111
- fontStyle: "italic",
1112
- opacity: 0.6
1113
- }
1114
- }
1115
- }
1116
- ];
1117
- }
1118
- }
1119
- return acc;
1120
- },
1121
- {}
1122
- );
1123
- if (isNoPinnedChatsSearchResults && isNoRecentChatsSearchResults) {
1124
- return {};
1198
+ const getSavedPromptMenuItems = useCallback(
1199
+ (prompt) => /* @__PURE__ */ jsx(
1200
+ SavedPromptMenuItems,
1201
+ {
1202
+ prompt,
1203
+ variant: "sidebar",
1204
+ onSendDirectly: sendSavedPromptDirectly,
1205
+ onDelete: requestSavedPromptDelete,
1206
+ isSendDirectlyDisabled: streamingUiMatchesView
1125
1207
  }
1126
- return filteredConversations2;
1127
- },
1128
- [categorizedMessages, isPinningChatsEnabled, t]
1208
+ ),
1209
+ [sendSavedPromptDirectly, requestSavedPromptDelete, streamingUiMatchesView]
1129
1210
  );
1211
+ const { conversationGroups, hasNoSearchResults } = useConversationHistoryGroups({
1212
+ savedPrompts,
1213
+ categorizedMessages,
1214
+ filterValue,
1215
+ isSavedPromptsEnabled,
1216
+ isPinningChatsEnabled,
1217
+ isSettingsOpen,
1218
+ activeSettingsTab,
1219
+ openSettings,
1220
+ getSavedPromptMenuItems
1221
+ });
1130
1222
  useEffect(() => {
1131
1223
  setMessages(conversationMessages);
1132
1224
  }, [conversationMessages]);
1133
1225
  const onSelectActiveItem = useCallback(
1134
1226
  (_, selectedItem) => {
1227
+ if (selectedItem !== void 0 && isSavedPromptConversationId(selectedItem)) {
1228
+ const promptId = String(selectedItem).slice(
1229
+ SAVED_PROMPT_CONVERSATION_ID_PREFIX.length
1230
+ );
1231
+ const prompt = savedPrompts.find(({ id }) => id === promptId);
1232
+ if (prompt) {
1233
+ handleApplySavedPrompt(prompt.content);
1234
+ }
1235
+ return;
1236
+ }
1135
1237
  if (!isFullscreenMode) {
1136
- setIsMcpSettingsOpen(false);
1238
+ closeSettings();
1137
1239
  setIsChatHistoryDrawerOpen(false);
1138
1240
  }
1139
1241
  setNewChatCreated(false);
@@ -1157,7 +1259,9 @@ const LightspeedChat = ({
1157
1259
  setDraftMessage,
1158
1260
  scrollToBottomRef,
1159
1261
  setCurrentConversationId,
1160
- setIsMcpSettingsOpen,
1262
+ closeSettings,
1263
+ savedPrompts,
1264
+ handleApplySavedPrompt,
1161
1265
  isFullscreenMode
1162
1266
  ]
1163
1267
  );
@@ -1165,8 +1269,9 @@ const LightspeedChat = ({
1165
1269
  (c) => c.conversation_id === viewConversationId
1166
1270
  );
1167
1271
  const getMaxPrompts = () => {
1272
+ const count = samplePrompts?.length ?? 0;
1168
1273
  if (isFullscreenMode) {
1169
- return samplePrompts?.length;
1274
+ return count;
1170
1275
  }
1171
1276
  if (displayMode === ChatbotDisplayMode.docked) {
1172
1277
  return 2;
@@ -1307,7 +1412,7 @@ const LightspeedChat = ({
1307
1412
  }, [
1308
1413
  conversationId,
1309
1414
  displayMode,
1310
- isMcpSettingsOpen,
1415
+ isSettingsOpen,
1311
1416
  messages.length,
1312
1417
  welcomePrompts.length
1313
1418
  ]);
@@ -1324,20 +1429,6 @@ const LightspeedChat = ({
1324
1429
  }),
1325
1430
  [filterValue, onClearFilter]
1326
1431
  );
1327
- const filteredConversations = useMemo(
1328
- () => filterConversations(filterValue),
1329
- [filterConversations, filterValue]
1330
- );
1331
- const noResultsState = useMemo(() => {
1332
- if (!filterValue || Object.keys(filteredConversations).length > 0) {
1333
- return void 0;
1334
- }
1335
- return {
1336
- bodyText: t("chatbox.emptyState.noResults.body"),
1337
- titleText: t("chatbox.emptyState.noResults.title"),
1338
- icon: SearchIcon
1339
- };
1340
- }, [filterValue, filteredConversations, t]);
1341
1432
  const onChatHistoryDrawerToggle = useCallback(() => {
1342
1433
  setIsChatHistoryDrawerOpen((isOpen) => !isOpen);
1343
1434
  }, []);
@@ -1501,16 +1592,28 @@ const LightspeedChat = ({
1501
1592
  tooltipContent: t("tooltip.send")
1502
1593
  }
1503
1594
  },
1504
- additionalActions: /* @__PURE__ */ jsx(
1505
- MessageBarModelSelector,
1506
- {
1507
- selectedModel,
1508
- models,
1509
- onSelect: handleSelectedModel,
1510
- disabled: isSendButtonDisabled || messages.length > 0,
1511
- disabledTooltip: t("modelSelector.disabledTooltip")
1512
- }
1513
- ),
1595
+ additionalActions: /* @__PURE__ */ jsxs(MessageBarActionsRow, { children: [
1596
+ /* @__PURE__ */ jsx(
1597
+ MessageBarModelSelector,
1598
+ {
1599
+ selectedModel,
1600
+ models,
1601
+ onSelect: handleSelectedModel,
1602
+ disabled: isSendButtonDisabled || messages.length > 0,
1603
+ disabledTooltip: t("modelSelector.disabledTooltip")
1604
+ }
1605
+ ),
1606
+ screenContextEnabled && isScreenContextSharingEnabled && showChatPanel && /* @__PURE__ */ jsx(
1607
+ ScreenContextChip,
1608
+ {
1609
+ state: screenContextChipState,
1610
+ domEnabled: domExtractionEnabled,
1611
+ screenshotsEnabled,
1612
+ supportsVision: selectedModelSupportsVision,
1613
+ onTogglePaused: toggleScreenContextPaused
1614
+ }
1615
+ )
1616
+ ] }),
1514
1617
  forceMultilineLayout: true,
1515
1618
  allowedFileTypes: supportedFileTypes,
1516
1619
  onAttachRejected,
@@ -1521,21 +1624,35 @@ const LightspeedChat = ({
1521
1624
  /* @__PURE__ */ jsx(ChatbotFootnoteWithIcon, { ...getFootnoteProps(t) })
1522
1625
  ] })
1523
1626
  ] });
1524
- const mcpSettingsPanel = /* @__PURE__ */ jsx(
1525
- McpServersSettings,
1627
+ const settingsPanel = /* @__PURE__ */ jsx(
1628
+ SettingsPanel,
1526
1629
  {
1527
- onClose: () => setIsMcpSettingsOpen(false),
1528
- backgroundColor: chatHeaderBgColor
1630
+ activeTab: activeSettingsTab,
1631
+ onTabChange: setSettingsTab,
1632
+ onClose: closeSettings,
1633
+ backgroundColor: chatHeaderBgColor,
1634
+ isSavedPromptsEnabled,
1635
+ onEnableSavedPrompts: () => handleSavedPromptsToggle(true),
1636
+ onApplySavedPromptToInput: handleApplySavedPrompt,
1637
+ onSendSavedPromptDirectly: sendMessage,
1638
+ isChatStreaming: streamingUiMatchesView,
1639
+ savedPrompts,
1640
+ savedPromptsConfig,
1641
+ savedPromptsLoading,
1642
+ savedPromptsError,
1643
+ onCreateSavedPrompt: createPrompt,
1644
+ onRequestSavedPromptDelete: requestSavedPromptDelete,
1645
+ showMcpSettings: mcpToolsPermissionResolved
1529
1646
  }
1530
1647
  );
1531
1648
  const mainPanelContent = (() => {
1532
- if (!isMcpSettingsOpen || !mcpToolsPermissionResolved) {
1649
+ if (!isSettingsOpen) {
1533
1650
  return /* @__PURE__ */ jsx(Fragment, { children: chatMainContent });
1534
1651
  }
1535
1652
  if (isFullscreenMode) {
1536
1653
  return /* @__PURE__ */ jsxs(McpFullscreenLayout, { children: [
1537
1654
  /* @__PURE__ */ jsx(McpChatPane, { children: chatMainContent }),
1538
- /* @__PURE__ */ jsx(McpSettingsPane, { children: mcpSettingsPanel })
1655
+ /* @__PURE__ */ jsx(McpSettingsPane, { children: settingsPanel })
1539
1656
  ] });
1540
1657
  }
1541
1658
  return /* @__PURE__ */ jsx(
@@ -1543,9 +1660,9 @@ const LightspeedChat = ({
1543
1660
  {
1544
1661
  fields: [
1545
1662
  {
1546
- id: "mcp-servers-settings",
1663
+ id: "settings-panel",
1547
1664
  label: "",
1548
- field: mcpSettingsPanel
1665
+ field: settingsPanel
1549
1666
  }
1550
1667
  ]
1551
1668
  }
@@ -1554,7 +1671,7 @@ const LightspeedChat = ({
1554
1671
  let drawerPanelStyle;
1555
1672
  if (!isFullscreenMode) {
1556
1673
  drawerPanelStyle = { zIndex: 1300 };
1557
- } else if (isMcpSettingsOpen) {
1674
+ } else if (isSettingsOpen) {
1558
1675
  drawerPanelStyle = { width: 320, minWidth: 320, maxWidth: 320 };
1559
1676
  } else {
1560
1677
  drawerPanelStyle = { minWidth: 232, maxWidth: 400 };
@@ -1596,14 +1713,15 @@ const LightspeedChat = ({
1596
1713
  isCompact: !isFullscreenMode
1597
1714
  }
1598
1715
  ),
1599
- /* @__PURE__ */ jsxs(
1716
+ /* @__PURE__ */ jsx(
1600
1717
  StyledChatbot,
1601
1718
  {
1602
1719
  displayMode: ChatbotDisplayMode.embedded,
1603
1720
  isCompact: !isFullscreenMode,
1604
1721
  compactDrawerOpen: !isFullscreenMode && isChatHistoryDrawerOpen,
1605
- mcpDrawerFix: isMcpSettingsOpen && !isChatHistoryDrawerOpen,
1606
- children: [
1722
+ mcpDrawerFix: isSettingsOpen && !isChatHistoryDrawerOpen,
1723
+ "data-screen-capture-exclude": true,
1724
+ children: showCompactSettings ? settingsPanel : /* @__PURE__ */ jsxs(Fragment, { children: [
1607
1725
  /* @__PURE__ */ jsxs(StyledChatbotHeader, { children: [
1608
1726
  /* @__PURE__ */ jsxs(ChatbotHeaderMain, { children: [
1609
1727
  showChatPanel && !isFullscreenMode && /* @__PURE__ */ jsxs(HeaderActions, { children: [
@@ -1690,17 +1808,26 @@ const LightspeedChat = ({
1690
1808
  handleSelectedModel,
1691
1809
  models,
1692
1810
  isPinningChatsEnabled,
1811
+ isSavedPromptsEnabled,
1812
+ onSavedPromptsToggle: handleSavedPromptsToggle,
1693
1813
  hideModelSelector: true,
1694
1814
  showChatTabOptions: !showNotebooksPanel,
1695
1815
  showMcpSettings: mcpToolsPermissionResolved,
1696
1816
  setDisplayMode: setDisplayModeFromHeader,
1697
1817
  displayMode,
1698
1818
  onPinnedChatsToggle: handlePinningChatsToggle,
1699
- onMcpSettingsClick: () => setIsMcpSettingsOpen(true)
1819
+ screenContextAdminEnabled: screenContextEnabled,
1820
+ isScreenContextSharingEnabled,
1821
+ onScreenContextSharingToggle: handleScreenContextSharingToggle,
1822
+ onMcpSettingsClick: () => {
1823
+ openSettings(
1824
+ mcpToolsPermissionResolved ? "mcp-servers" : "saved-prompts"
1825
+ );
1826
+ }
1700
1827
  }
1701
1828
  )
1702
1829
  ] }),
1703
- /* @__PURE__ */ jsx(HeaderDivider, { "data-testid": "lightspeed-header-divider" }),
1830
+ (isFullscreenMode || shouldShowTabs) && /* @__PURE__ */ jsx(HeaderDivider, { "data-testid": "lightspeed-header-divider" }),
1704
1831
  shouldShowTabs && /* @__PURE__ */ jsxs(
1705
1832
  Tabs,
1706
1833
  {
@@ -1817,7 +1944,7 @@ const LightspeedChat = ({
1817
1944
  setIsDrawerOpen: setIsChatHistoryDrawerOpen,
1818
1945
  activeItemId: viewConversationId,
1819
1946
  onSelectActiveItem,
1820
- conversations: filteredConversations,
1947
+ conversations: conversationGroups,
1821
1948
  onNewChat,
1822
1949
  newChatButtonText: t("button.newChat"),
1823
1950
  newChatButtonProps: {
@@ -1829,7 +1956,11 @@ const LightspeedChat = ({
1829
1956
  searchInputAriaLabel: t("aria.search.placeholder"),
1830
1957
  searchInputProps,
1831
1958
  searchActionEnd: sortDropdown,
1832
- noResultsState,
1959
+ noResultsState: filterValue && hasNoSearchResults ? {
1960
+ bodyText: t("chatbox.emptyState.noResults.body"),
1961
+ titleText: t("chatbox.emptyState.noResults.title"),
1962
+ icon: SearchIcon
1963
+ } : void 0,
1833
1964
  drawerContent: /* @__PURE__ */ jsxs(
1834
1965
  ChatFileDropZone,
1835
1966
  {
@@ -1919,7 +2050,18 @@ const LightspeedChat = ({
1919
2050
  }
1920
2051
  )
1921
2052
  ] })
1922
- ]
2053
+ ] })
2054
+ }
2055
+ ),
2056
+ /* @__PURE__ */ jsx(
2057
+ DeleteSavedPromptModal,
2058
+ {
2059
+ isOpen: isSavedPromptDeleteModalOpen,
2060
+ promptName: promptToDelete?.name,
2061
+ isDeleting: isSavedPromptDeleting,
2062
+ error: savedPromptDeleteError,
2063
+ onClose: closeSavedPromptDeleteModal,
2064
+ onConfirm: confirmSavedPromptDelete
1923
2065
  }
1924
2066
  ),
1925
2067
  /* @__PURE__ */ jsx(Attachment, {})