@red-hat-developer-hub/backstage-plugin-intelligent-assistant 5.2.0 → 5.3.1

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 (106) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +14 -2
  3. package/dist/alpha.d.ts +24 -0
  4. package/dist/components/AttachmentContext.esm.js +1 -1
  5. package/dist/components/ChatLoadingFallback.esm.js +37 -0
  6. package/dist/components/ChatLoadingFallback.esm.js.map +1 -0
  7. package/dist/components/CollapsedHistoryStrip.esm.js +1 -1
  8. package/dist/components/LightSpeedChat.esm.js +76 -29
  9. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  10. package/dist/components/LightspeedChatBox.esm.js +2 -2
  11. package/dist/components/LightspeedChatBoxHeader.esm.js +30 -2
  12. package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
  13. package/dist/components/LightspeedChatContainer.esm.js +20 -4
  14. package/dist/components/LightspeedChatContainer.esm.js.map +1 -1
  15. package/dist/components/LightspeedDrawerContext.esm.js.map +1 -1
  16. package/dist/components/LightspeedDrawerProvider.esm.js +11 -25
  17. package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -1
  18. package/dist/components/LightspeedFABContent.esm.js +14 -2
  19. package/dist/components/LightspeedFABContent.esm.js.map +1 -1
  20. package/dist/components/LightspeedOverlayChat.esm.js +39 -0
  21. package/dist/components/LightspeedOverlayChat.esm.js.map +1 -0
  22. package/dist/components/McpConfigureServerModal.esm.js +2 -2
  23. package/dist/components/MessageBarModelSelector.esm.js +105 -13
  24. package/dist/components/MessageBarModelSelector.esm.js.map +1 -1
  25. package/dist/components/ScreenContextChip.esm.js +172 -0
  26. package/dist/components/ScreenContextChip.esm.js.map +1 -0
  27. package/dist/components/ToastAlertGroup.esm.js +1 -1
  28. package/dist/components/criticalFabBundle.esm.js +3 -0
  29. package/dist/components/criticalFabBundle.esm.js.map +1 -0
  30. package/dist/components/loaders.esm.js +11 -0
  31. package/dist/components/loaders.esm.js.map +1 -0
  32. package/dist/components/notebooks/AddDocumentModal.esm.js +2 -2
  33. package/dist/components/notebooks/DocumentSidebar.esm.js +1 -1
  34. package/dist/components/notebooks/NotebookCard.esm.js +1 -1
  35. package/dist/components/notebooks/NotebookStreamProvider.esm.js +1 -1
  36. package/dist/components/notebooks/NotebookView.esm.js +3 -3
  37. package/dist/components/notebooks/OverwriteConfirmModal.esm.js +1 -1
  38. package/dist/components/notebooks/UploadResourceScreen.esm.js +1 -1
  39. package/dist/components/notebooks/notebookStreamStore.esm.js +1 -1
  40. package/dist/components/notebooks/notebookStreamStore.esm.js.map +1 -1
  41. package/dist/const.esm.js +9 -1
  42. package/dist/const.esm.js.map +1 -1
  43. package/dist/fabModule.esm.js +51 -0
  44. package/dist/fabModule.esm.js.map +1 -0
  45. package/dist/hooks/screenContextLabelSubscription.esm.js +113 -0
  46. package/dist/hooks/screenContextLabelSubscription.esm.js.map +1 -0
  47. package/dist/hooks/useConversationMessages.esm.js +2 -1
  48. package/dist/hooks/useConversationMessages.esm.js.map +1 -1
  49. package/dist/hooks/useDisplayModeSettings.esm.js +1 -1
  50. package/dist/hooks/useDisplayModeSettings.esm.js.map +1 -1
  51. package/dist/hooks/useLightspeedProviderState.esm.js +1 -2
  52. package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -1
  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/useWindowLocation.esm.js +28 -0
  58. package/dist/hooks/useWindowLocation.esm.js.map +1 -0
  59. package/dist/index.d.ts +1 -15
  60. package/dist/index.esm.js +7 -69
  61. package/dist/index.esm.js.map +1 -1
  62. package/dist/intelligent-assistant-fab-module.d.ts +12 -4
  63. package/dist/intelligent-assistant-fab-module.esm.js +1 -1
  64. package/dist/intelligent-assistant-translations-module.d.ts +10 -4
  65. package/dist/intelligent-assistant-translations-module.esm.js +1 -1
  66. package/dist/lazy/LazyLightspeedChatDrawerContent.esm.js +13 -0
  67. package/dist/lazy/LazyLightspeedChatDrawerContent.esm.js.map +1 -0
  68. package/dist/lazy/LazyLightspeedFabRootWrapper.esm.js +17 -0
  69. package/dist/lazy/LazyLightspeedFabRootWrapper.esm.js.map +1 -0
  70. package/dist/legacy.d.ts +14 -16
  71. package/dist/legacy.esm.js +2 -9
  72. package/dist/legacy.esm.js.map +1 -1
  73. package/dist/loadChatPatternflyStyles.esm.js +14 -0
  74. package/dist/loadChatPatternflyStyles.esm.js.map +1 -0
  75. package/dist/muiClassNameConfig.esm.js +6 -0
  76. package/dist/muiClassNameConfig.esm.js.map +1 -0
  77. package/dist/plugin.esm.js +4 -4
  78. package/dist/plugin.esm.js.map +1 -1
  79. package/dist/translations/de.esm.js +24 -0
  80. package/dist/translations/de.esm.js.map +1 -1
  81. package/dist/translations/es.esm.js +24 -0
  82. package/dist/translations/es.esm.js.map +1 -1
  83. package/dist/translations/fr.esm.js +24 -0
  84. package/dist/translations/fr.esm.js.map +1 -1
  85. package/dist/translations/it.esm.js +24 -0
  86. package/dist/translations/it.esm.js.map +1 -1
  87. package/dist/translations/ja.esm.js +24 -0
  88. package/dist/translations/ja.esm.js.map +1 -1
  89. package/dist/translations/ref.esm.js +24 -0
  90. package/dist/translations/ref.esm.js.map +1 -1
  91. package/dist/translationsModule.esm.js +18 -0
  92. package/dist/translationsModule.esm.js.map +1 -0
  93. package/dist/types.esm.js.map +1 -1
  94. package/dist/utils/dom-selectors.esm.js +22 -2
  95. package/dist/utils/dom-selectors.esm.js.map +1 -1
  96. package/dist/utils/lightspeed-chatbox-message-utils.esm.js +145 -0
  97. package/dist/utils/lightspeed-chatbox-message-utils.esm.js.map +1 -0
  98. package/dist/utils/lightspeed-chatbox-utils.esm.js +20 -144
  99. package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -1
  100. package/dist/utils/screen-capture.esm.js +116 -0
  101. package/dist/utils/screen-capture.esm.js.map +1 -0
  102. package/dist/utils/screen-context-utils.esm.js +343 -0
  103. package/dist/utils/screen-context-utils.esm.js.map +1 -0
  104. package/dist/utils/sensitive-data-redactor.esm.js +39 -1
  105. package/dist/utils/sensitive-data-redactor.esm.js.map +1 -1
  106. package/package.json +3 -3
@@ -4,7 +4,26 @@ import { Spinner } from '@patternfly/react-core';
4
4
  import { ThumbtackIcon } from '@patternfly/react-icons';
5
5
  import { RhUiAiInfoIcon } from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-info-icon';
6
6
  import { RagSourceLabel } from '../components/RagSourceLabel.esm.js';
7
+ import { transformDocumentsToSources as transformDocumentsToSources$1 } from './lightspeed-chatbox-message-utils.esm.js';
8
+ export { createBotMessage, createMessage, createUserMessage, getConversationsData, getTimestamp, getTimestampVariablesString, normalizeChatUserInput } from './lightspeed-chatbox-message-utils.esm.js';
7
9
 
10
+ const transformDocumentsToSources = (referenced_documents) => {
11
+ const result = transformDocumentsToSources$1(referenced_documents);
12
+ if (!result) {
13
+ return void 0;
14
+ }
15
+ return {
16
+ sources: result.sources.map((source) => {
17
+ if (!source.ragSource) {
18
+ return source;
19
+ }
20
+ return {
21
+ ...source,
22
+ headerContent: /* @__PURE__ */ jsx(RagSourceLabel, { source: source.ragSource })
23
+ };
24
+ })
25
+ };
26
+ };
8
27
  const getFootnoteProps = (t) => ({
9
28
  label: t?.("footer.accuracy.label") || "Always review AI generated content prior to use."
10
29
  });
@@ -23,149 +42,6 @@ const ChatbotFootnoteWithIcon = ({ label }) => /* @__PURE__ */ jsxs(
23
42
  ]
24
43
  }
25
44
  );
26
- const getTimestampVariablesString = (v) => {
27
- if (v < 10) {
28
- return `0${v}`;
29
- }
30
- return `${v}`;
31
- };
32
- const isBlankLine = (line) => !line.trim();
33
- const isOrderedListLine = (line) => /^\s*\d+\.\s/.test(line);
34
- const isBulletListLine = (line) => /^\s*[-*+]\s/.test(line) || /^\s*\u2022(?:\s+|\S|$)/u.test(line);
35
- const escapeBulletListMarker = (line) => {
36
- if (/^\s*\u2022/u.test(line)) {
37
- return line;
38
- }
39
- return line.replace(/^(\s*)([*+-])(\s)/, "$1\\$2$3");
40
- };
41
- const escapeOrderedListMarker = (line) => line.replace(/^(\s*)(\d+)\.(\s)/, "$1$2\\.$3");
42
- const foldListWithIntro = (s, isListItemLine, escapeListLine) => {
43
- const lines = s.split("\n");
44
- let firstListIdx = -1;
45
- for (let i = 0; i < lines.length; i++) {
46
- const line = lines[i];
47
- if (line !== void 0 && isListItemLine(line)) {
48
- firstListIdx = i;
49
- break;
50
- }
51
- }
52
- if (firstListIdx < 0) {
53
- return s;
54
- }
55
- const intro = lines.slice(0, firstListIdx);
56
- if (intro.some(isBlankLine)) {
57
- return s;
58
- }
59
- const rest = lines.slice(firstListIdx);
60
- const restItems = rest.filter((l) => !isBlankLine(l));
61
- if (restItems.length === 0 || !restItems.every(isListItemLine)) {
62
- return s;
63
- }
64
- const escaped = restItems.map(escapeListLine);
65
- if (intro.length === 0) {
66
- return escaped.join(" \n");
67
- }
68
- return `${intro.join(" \n")}
69
- ${escaped.join(" \n")}`;
70
- };
71
- const foldOrderedListWithIntro = (s) => foldListWithIntro(s, isOrderedListLine, escapeOrderedListMarker);
72
- const foldBulletListWithIntro = (s) => foldListWithIntro(s, isBulletListLine, escapeBulletListMarker);
73
- const normalizeChatUserInput = (input) => {
74
- let s = input.replace(/\r\n/g, "\n").trim();
75
- if (!s) {
76
- return s;
77
- }
78
- s = s.replace(/\n{3,}/g, "\n\n");
79
- s = s.replace(/\n\n(?=\s*\d+\.\s)/gm, "\n");
80
- s = s.replace(/\n\n(?=\s*[-*+]\s)/gm, "\n");
81
- s = s.replace(/\n\n(?=\s*\u2022)/gm, "\n");
82
- s = foldOrderedListWithIntro(s);
83
- s = foldBulletListWithIntro(s);
84
- return s.trim();
85
- };
86
- const getTimestamp = (unix_timestamp) => {
87
- if (typeof unix_timestamp !== "number" || Number.isNaN(unix_timestamp)) {
88
- console.error("Invalid Unix timestamp provided");
89
- return "";
90
- }
91
- const a = new Date(unix_timestamp);
92
- const month = getTimestampVariablesString(a.getMonth() + 1);
93
- const year = a.getFullYear();
94
- const date = getTimestampVariablesString(a.getDate());
95
- const hour = getTimestampVariablesString(a.getHours());
96
- const min = getTimestampVariablesString(a.getMinutes());
97
- const sec = getTimestampVariablesString(a.getSeconds());
98
- const time = `${date}/${month}/${year}, ${hour}:${min}:${sec}`;
99
- return time;
100
- };
101
- const createMessage = ({
102
- role,
103
- name,
104
- avatar,
105
- isLoading = false,
106
- content,
107
- timestamp,
108
- error,
109
- sources,
110
- toolCalls,
111
- defaultUserName = "Guest"
112
- }) => ({
113
- role,
114
- name: name || defaultUserName,
115
- avatar,
116
- isLoading,
117
- content,
118
- timestamp,
119
- error,
120
- sources,
121
- toolCalls
122
- });
123
- const createUserMessage = (props) => createMessage({
124
- ...props,
125
- role: "user",
126
- defaultUserName: props.defaultUserName
127
- });
128
- const createBotMessage = (props) => createMessage({
129
- ...props,
130
- role: "bot"
131
- });
132
- const getConversationsData = (conversation) => {
133
- const [userMessage, botMessage] = conversation.messages || [];
134
- return [
135
- {
136
- ...userMessage,
137
- timestamp: getTimestamp(
138
- conversation.started_at ? new Date(conversation.started_at).getTime() : Date.now()
139
- )
140
- },
141
- {
142
- ...botMessage,
143
- timestamp: getTimestamp(
144
- conversation.completed_at ? new Date(conversation.completed_at).getTime() : Date.now()
145
- ),
146
- referenced_documents: botMessage?.referenced_documents ?? []
147
- }
148
- ];
149
- };
150
- const transformDocumentsToSources = (referenced_documents) => {
151
- if (!referenced_documents || referenced_documents?.length === 0) {
152
- return void 0;
153
- }
154
- return {
155
- sources: referenced_documents.map(
156
- (doc) => ({
157
- body: doc.doc_description,
158
- title: doc.doc_title,
159
- link: doc?.doc_url,
160
- isExternal: !!doc?.doc_url,
161
- ...doc.source ? {
162
- headerContent: /* @__PURE__ */ jsx(RagSourceLabel, { source: doc.source }),
163
- ragSource: doc.source
164
- } : {}
165
- })
166
- )
167
- };
168
- };
169
45
  const sortConversations = (messages, sortOption) => {
170
46
  return [...messages].sort((a, b) => {
171
47
  const aTopicSummary = a.topic_summary || "";
@@ -235,5 +111,5 @@ const getCategorizeMessages = (messages, pinnedChats, addProps, t, sortOption =
235
111
  return filteredCategories;
236
112
  };
237
113
 
238
- export { ChatbotFootnoteWithIcon, createBotMessage, createMessage, createUserMessage, getCategorizeMessages, getConversationsData, getFootnoteProps, getTimestamp, getTimestampVariablesString, normalizeChatUserInput, transformDocumentsToSources };
114
+ export { ChatbotFootnoteWithIcon, getCategorizeMessages, getFootnoteProps, transformDocumentsToSources };
239
115
  //# sourceMappingURL=lightspeed-chatbox-utils.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lightspeed-chatbox-utils.esm.js","sources":["../../src/utils/lightspeed-chatbox-utils.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n ChatbotFootnote,\n Conversation,\n SourcesCardProps,\n} from '@patternfly/chatbot';\nimport { Spinner } from '@patternfly/react-core';\nimport { ThumbtackIcon } from '@patternfly/react-icons';\nimport { RhUiAiInfoIcon } from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-info-icon';\n\nimport { RagSourceLabel } from '../components/RagSourceLabel';\nimport {\n BaseMessage,\n ConversationList,\n ConversationSummary,\n LCSConversation,\n ReferencedDocument,\n ReferencedDocuments,\n ToolCall,\n} from '../types';\n\nexport const getFootnoteProps = (\n t?: (key: string, params?: any) => string,\n) => ({\n label:\n t?.('footer.accuracy.label') ||\n 'Always review AI generated content prior to use.',\n});\n\nexport const ChatbotFootnoteWithIcon = ({ label }: { label: string }) => (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4px',\n }}\n >\n <RhUiAiInfoIcon style={{ width: '14px', height: '14px', flexShrink: 0 }} />\n <ChatbotFootnote label={label} />\n </div>\n);\n\nexport const getTimestampVariablesString = (v: number) => {\n if (v < 10) {\n return `0${v}`;\n }\n return `${v}`;\n};\n\nconst isBlankLine = (line: string) => !line.trim();\n\n/** Ordered list item at line start (CommonMark), ignoring leading spaces. */\nconst isOrderedListLine = (line: string) => /^\\s*\\d+\\.\\s/.test(line);\n\n/** ASCII bullets; also U+2022 (•) — not a CommonMark marker, but users paste it as a list. */\nconst isBulletListLine = (line: string) =>\n /^\\s*[-*+]\\s/.test(line) || /^\\s*\\u2022(?:\\s+|\\S|$)/u.test(line);\n\nconst escapeBulletListMarker = (line: string) => {\n if (/^\\s*\\u2022/u.test(line)) {\n return line;\n }\n return line.replace(/^(\\s*)([*+-])(\\s)/, '$1\\\\$2$3');\n};\n\nconst escapeOrderedListMarker = (line: string) =>\n line.replace(/^(\\s*)(\\d+)\\.(\\s)/, '$1$2\\\\.$3');\n\n/**\n * When intro lines (no blank lines among them) are followed by a contiguous run of\n * list lines, join intro + list into one Markdown paragraph using hard breaks and\n * per-line escaping so the block does not split awkwardly in the chat UI.\n */\nconst foldListWithIntro = (\n s: string,\n isListItemLine: (line: string) => boolean,\n escapeListLine: (line: string) => string,\n): string => {\n const lines = s.split('\\n');\n let firstListIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line !== undefined && isListItemLine(line)) {\n firstListIdx = i;\n break;\n }\n }\n if (firstListIdx < 0) {\n return s;\n }\n\n const intro = lines.slice(0, firstListIdx);\n if (intro.some(isBlankLine)) {\n return s;\n }\n\n const rest = lines.slice(firstListIdx);\n const restItems = rest.filter(l => !isBlankLine(l));\n if (restItems.length === 0 || !restItems.every(isListItemLine)) {\n return s;\n }\n\n const escaped = restItems.map(escapeListLine);\n if (intro.length === 0) {\n return escaped.join(' \\n');\n }\n return `${intro.join(' \\n')} \\n${escaped.join(' \\n')}`;\n};\n\n/**\n * Paragraph + ordered list (even with a single newline) parses as two blocks; merge\n * intro + contiguous numbered lines into one paragraph using hard breaks (two spaces\n * + newline) and escaped `1.` markers so Markdown stays a single block.\n */\nconst foldOrderedListWithIntro = (s: string) =>\n foldListWithIntro(s, isOrderedListLine, escapeOrderedListMarker);\n\n/** Same idea as ordered lists for intro + bullet lines. */\nconst foldBulletListWithIntro = (s: string) =>\n foldListWithIntro(s, isBulletListLine, escapeBulletListMarker);\n\n/**\n * Trims user chat input and reduces blank lines that split Markdown blocks\n * (e.g. paragraph + blank line + ordered list) so PatternFly renders one user bubble.\n */\nexport const normalizeChatUserInput = (input: string): string => {\n let s = input.replace(/\\r\\n/g, '\\n').trim();\n if (!s) {\n return s;\n }\n s = s.replace(/\\n{3,}/g, '\\n\\n');\n s = s.replace(/\\n\\n(?=\\s*\\d+\\.\\s)/gm, '\\n');\n s = s.replace(/\\n\\n(?=\\s*[-*+]\\s)/gm, '\\n');\n s = s.replace(/\\n\\n(?=\\s*\\u2022)/gm, '\\n');\n s = foldOrderedListWithIntro(s);\n s = foldBulletListWithIntro(s);\n return s.trim();\n};\n\nexport const getTimestamp = (unix_timestamp: number) => {\n if (typeof unix_timestamp !== 'number' || Number.isNaN(unix_timestamp)) {\n // eslint-disable-next-line no-console\n console.error('Invalid Unix timestamp provided');\n return '';\n }\n\n const a = new Date(unix_timestamp);\n const month = getTimestampVariablesString(a.getMonth() + 1);\n const year = a.getFullYear();\n const date = getTimestampVariablesString(a.getDate());\n const hour = getTimestampVariablesString(a.getHours());\n const min = getTimestampVariablesString(a.getMinutes());\n const sec = getTimestampVariablesString(a.getSeconds());\n const time = `${date}/${month}/${year}, ${hour}:${min}:${sec}`;\n return time;\n};\n\nexport const splitJsonStrings = (jsonString: string): string[] => {\n const chunks = jsonString.split('}{');\n\n if (chunks.length <= 1) {\n return [jsonString];\n }\n\n return chunks.map((chunk, index, arr) => {\n if (index === 0) {\n return `${chunk}}`;\n } else if (index === arr.length - 1) {\n return `{${chunk}`;\n }\n return `{${chunk}}`;\n });\n};\n\ntype MessageProps = {\n content: string;\n timestamp: string;\n name?: string;\n avatar?: string | any;\n isLoading?: boolean;\n error?: {\n title: string;\n };\n sources?: SourcesCardProps;\n toolCalls?: ToolCall[];\n};\n\nexport const createMessage = ({\n role,\n name,\n avatar,\n isLoading = false,\n content,\n timestamp,\n error,\n sources,\n toolCalls,\n defaultUserName = 'Guest',\n}: MessageProps & { role: 'user' | 'bot'; defaultUserName?: string }) => ({\n role,\n name: name || defaultUserName,\n avatar,\n isLoading,\n content,\n timestamp,\n error,\n sources,\n toolCalls,\n});\n\nexport const createUserMessage = (\n props: MessageProps & { defaultUserName?: string },\n) =>\n createMessage({\n ...props,\n role: 'user',\n defaultUserName: props.defaultUserName,\n });\n\nexport const createBotMessage = (props: MessageProps) =>\n createMessage({\n ...props,\n role: 'bot',\n });\n\nexport const getConversationsData = (\n conversation: LCSConversation,\n): [BaseMessage, BaseMessage] => {\n const [userMessage, botMessage] = conversation.messages || [];\n return [\n {\n ...userMessage,\n timestamp: getTimestamp(\n conversation.started_at\n ? new Date(conversation.started_at).getTime()\n : Date.now(),\n ),\n },\n {\n ...botMessage,\n timestamp: getTimestamp(\n conversation.completed_at\n ? new Date(conversation.completed_at).getTime()\n : Date.now(),\n ),\n referenced_documents: botMessage?.referenced_documents ?? [],\n },\n ];\n};\n\nexport type SourceWithRagId = SourcesCardProps['sources'][number] & {\n /** Full LCORE RAG source id for chip-modal labels. */\n ragSource?: string;\n};\n\nexport const transformDocumentsToSources = (\n referenced_documents: ReferencedDocuments,\n): SourcesCardProps | undefined => {\n if (!referenced_documents || referenced_documents?.length === 0) {\n return undefined;\n }\n return {\n sources: referenced_documents.map(\n (doc: ReferencedDocument): SourceWithRagId => ({\n body: doc.doc_description,\n title: doc.doc_title,\n link: doc?.doc_url,\n isExternal: !!doc?.doc_url,\n ...(doc.source\n ? {\n headerContent: <RagSourceLabel source={doc.source} />,\n ragSource: doc.source,\n }\n : {}),\n }),\n ),\n };\n};\n\nexport type SortOption =\n 'newest' | 'oldest' | 'alphabeticalAsc' | 'alphabeticalDesc';\n\nconst sortConversations = (\n messages: ConversationList,\n sortOption: SortOption,\n): ConversationList => {\n return [...messages].sort((a, b) => {\n const aTopicSummary = a.topic_summary || '';\n const bTopicSummary = b.topic_summary || '';\n switch (sortOption) {\n case 'oldest':\n return a.last_message_timestamp - b.last_message_timestamp;\n case 'alphabeticalAsc':\n return aTopicSummary.localeCompare(bTopicSummary, undefined, {\n sensitivity: 'base',\n });\n case 'alphabeticalDesc':\n return bTopicSummary.localeCompare(aTopicSummary, undefined, {\n sensitivity: 'base',\n });\n case 'newest':\n default:\n return b.last_message_timestamp - a.last_message_timestamp;\n }\n });\n};\n\nexport const getCategorizeMessages = (\n messages: ConversationList,\n pinnedChats: string[],\n addProps: (c: ConversationSummary) => { [k: string]: any },\n t?: (key: string, params?: any) => string,\n sortOption: SortOption = 'newest',\n): { [k: string]: Conversation[] } => {\n const pinnedChatsKey =\n t?.('conversation.category.pinnedChats') || 'Pinned chats';\n const recentKey = t?.('conversation.category.recent') || 'Chats';\n const categorizedMessages: { [k: string]: Conversation[] } = {\n [pinnedChatsKey]: [],\n [recentKey]: [],\n };\n const sortedMessages = sortConversations(messages, sortOption);\n sortedMessages.forEach(c => {\n const message: Conversation = {\n id: c.conversation_id,\n text: c.topic_summary ?? '',\n icon: c.topic_summary ? undefined : <Spinner size=\"sm\" />,\n label: t?.('message.options.label') || 'Options',\n additionalProps: {\n 'aria-label': t?.('aria.options.label') || 'Options',\n },\n ...addProps(c),\n };\n\n if (pinnedChats.includes(c.conversation_id)) {\n categorizedMessages[pinnedChatsKey].push({\n ...message,\n icon: (\n <ThumbtackIcon\n style={{\n width: '1rem',\n height: '1rem',\n display: 'flex',\n alignItems: 'center',\n }}\n />\n ),\n });\n } else {\n categorizedMessages[recentKey].push(message);\n }\n });\n\n const filteredCategories = Object.keys(categorizedMessages).reduce(\n (result, category) => {\n result[category] = categorizedMessages[category];\n return result;\n },\n {} as any,\n );\n\n return filteredCategories;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lightspeed-chatbox-utils.esm.js","sources":["../../src/utils/lightspeed-chatbox-utils.tsx"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n ChatbotFootnote,\n Conversation,\n type SourcesCardProps,\n} from '@patternfly/chatbot';\nimport { Spinner } from '@patternfly/react-core';\nimport { ThumbtackIcon } from '@patternfly/react-icons';\nimport { RhUiAiInfoIcon } from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-info-icon';\n\nimport { RagSourceLabel } from '../components/RagSourceLabel';\nimport {\n ConversationList,\n ConversationSummary,\n ReferencedDocuments,\n} from '../types';\nimport {\n transformDocumentsToSources as transformDocumentsToSourcesBase,\n type SourceWithRagId,\n} from './lightspeed-chatbox-message-utils';\n\nexport {\n createBotMessage,\n createMessage,\n createUserMessage,\n getConversationsData,\n getTimestamp,\n getTimestampVariablesString,\n normalizeChatUserInput,\n} from './lightspeed-chatbox-message-utils';\nexport type { SourceWithRagId } from './lightspeed-chatbox-message-utils';\n\n/**\n * Chat UI wrapper: adds SourcesCard `headerContent` pills from `ragSource`.\n * Pure mapping stays in message-utils so notebookStreamStore stays CSS-clean.\n */\nexport const transformDocumentsToSources = (\n referenced_documents: ReferencedDocuments,\n): SourcesCardProps | undefined => {\n const result = transformDocumentsToSourcesBase(referenced_documents);\n if (!result) {\n return undefined;\n }\n return {\n sources: result.sources.map((source: SourceWithRagId) => {\n if (!source.ragSource) {\n return source;\n }\n return {\n ...source,\n headerContent: <RagSourceLabel source={source.ragSource} />,\n };\n }),\n };\n};\n\nexport const getFootnoteProps = (\n t?: (key: string, params?: any) => string,\n) => ({\n label:\n t?.('footer.accuracy.label') ||\n 'Always review AI generated content prior to use.',\n});\n\nexport const ChatbotFootnoteWithIcon = ({ label }: { label: string }) => (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4px',\n }}\n >\n <RhUiAiInfoIcon style={{ width: '14px', height: '14px', flexShrink: 0 }} />\n <ChatbotFootnote label={label} />\n </div>\n);\n\nexport const splitJsonStrings = (jsonString: string): string[] => {\n const chunks = jsonString.split('}{');\n\n if (chunks.length <= 1) {\n return [jsonString];\n }\n\n return chunks.map((chunk, index, arr) => {\n if (index === 0) {\n return `${chunk}}`;\n } else if (index === arr.length - 1) {\n return `{${chunk}`;\n }\n return `{${chunk}}`;\n });\n};\n\nexport type SortOption =\n 'newest' | 'oldest' | 'alphabeticalAsc' | 'alphabeticalDesc';\n\nconst sortConversations = (\n messages: ConversationList,\n sortOption: SortOption,\n): ConversationList => {\n return [...messages].sort((a, b) => {\n const aTopicSummary = a.topic_summary || '';\n const bTopicSummary = b.topic_summary || '';\n switch (sortOption) {\n case 'oldest':\n return a.last_message_timestamp - b.last_message_timestamp;\n case 'alphabeticalAsc':\n return aTopicSummary.localeCompare(bTopicSummary, undefined, {\n sensitivity: 'base',\n });\n case 'alphabeticalDesc':\n return bTopicSummary.localeCompare(aTopicSummary, undefined, {\n sensitivity: 'base',\n });\n case 'newest':\n default:\n return b.last_message_timestamp - a.last_message_timestamp;\n }\n });\n};\n\nexport const getCategorizeMessages = (\n messages: ConversationList,\n pinnedChats: string[],\n addProps: (c: ConversationSummary) => { [k: string]: any },\n t?: (key: string, params?: any) => string,\n sortOption: SortOption = 'newest',\n): { [k: string]: Conversation[] } => {\n const pinnedChatsKey =\n t?.('conversation.category.pinnedChats') || 'Pinned chats';\n const recentKey = t?.('conversation.category.recent') || 'Chats';\n const categorizedMessages: { [k: string]: Conversation[] } = {\n [pinnedChatsKey]: [],\n [recentKey]: [],\n };\n const sortedMessages = sortConversations(messages, sortOption);\n sortedMessages.forEach(c => {\n const message: Conversation = {\n id: c.conversation_id,\n text: c.topic_summary ?? '',\n icon: c.topic_summary ? undefined : <Spinner size=\"sm\" />,\n label: t?.('message.options.label') || 'Options',\n additionalProps: {\n 'aria-label': t?.('aria.options.label') || 'Options',\n },\n ...addProps(c),\n };\n\n if (pinnedChats.includes(c.conversation_id)) {\n categorizedMessages[pinnedChatsKey].push({\n ...message,\n icon: (\n <ThumbtackIcon\n style={{\n width: '1rem',\n height: '1rem',\n display: 'flex',\n alignItems: 'center',\n }}\n />\n ),\n });\n } else {\n categorizedMessages[recentKey].push(message);\n }\n });\n\n const filteredCategories = Object.keys(categorizedMessages).reduce(\n (result, category) => {\n result[category] = categorizedMessages[category];\n return result;\n },\n {} as any,\n );\n\n return filteredCategories;\n};\n"],"names":["transformDocumentsToSourcesBase"],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ import { sanitizeClonedDom } from './sensitive-data-redactor.esm.js';
2
+
3
+ const DEFAULT_QUALITY = 0.7;
4
+ const DEFAULT_MAX_WIDTH = 1280;
5
+ const DEFAULT_TIMEOUT_MS = 1e3;
6
+ function stripDataUriPrefix(dataUri) {
7
+ const commaIdx = dataUri.indexOf(",");
8
+ return commaIdx !== -1 ? dataUri.slice(commaIdx + 1) : dataUri;
9
+ }
10
+ function waitForIdle() {
11
+ return new Promise((resolve) => {
12
+ if ("requestIdleCallback" in window) {
13
+ window.requestIdleCallback(() => resolve(), { timeout: 100 });
14
+ } else {
15
+ setTimeout(resolve, 0);
16
+ }
17
+ });
18
+ }
19
+ function scaleCanvas(canvas, maxWidth) {
20
+ if (canvas.width <= maxWidth) {
21
+ return canvas;
22
+ }
23
+ const scaleFactor = maxWidth / canvas.width;
24
+ const scaledWidth = maxWidth;
25
+ const scaledHeight = Math.round(canvas.height * scaleFactor);
26
+ const scaledCanvas = document.createElement("canvas");
27
+ scaledCanvas.width = scaledWidth;
28
+ scaledCanvas.height = scaledHeight;
29
+ const ctx = scaledCanvas.getContext("2d");
30
+ if (!ctx) {
31
+ return canvas;
32
+ }
33
+ ctx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);
34
+ return scaledCanvas;
35
+ }
36
+ async function doCapture(options) {
37
+ const targetElement = document.querySelector("#root");
38
+ if (!targetElement) {
39
+ return { success: false, error: "Root element (#root) not found" };
40
+ }
41
+ const { default: html2canvas } = await import('html2canvas-pro');
42
+ const start = window.performance.now();
43
+ const canvas = await html2canvas(targetElement, {
44
+ useCORS: true,
45
+ allowTaint: false,
46
+ logging: false,
47
+ onclone: (clonedDocument) => {
48
+ sanitizeClonedDom(clonedDocument);
49
+ },
50
+ ignoreElements: (element) => {
51
+ if (element.hasAttribute("data-screen-capture-exclude")) {
52
+ return true;
53
+ }
54
+ if (element.classList.contains("pf-chatbot")) {
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+ });
60
+ let scaledCanvas;
61
+ try {
62
+ scaledCanvas = scaleCanvas(canvas, options.maxWidth);
63
+ const dataUri = scaledCanvas.toDataURL("image/jpeg", options.quality);
64
+ const captureTimeMs = Math.round(window.performance.now() - start);
65
+ return {
66
+ success: true,
67
+ base64: stripDataUriPrefix(dataUri),
68
+ contentType: "image/jpeg",
69
+ width: scaledCanvas.width,
70
+ height: scaledCanvas.height,
71
+ captureTimeMs
72
+ };
73
+ } finally {
74
+ canvas.width = 0;
75
+ canvas.height = 0;
76
+ if (scaledCanvas && scaledCanvas !== canvas) {
77
+ scaledCanvas.width = 0;
78
+ scaledCanvas.height = 0;
79
+ }
80
+ }
81
+ }
82
+ async function captureScreenshot(options) {
83
+ if (document.visibilityState === "hidden") {
84
+ return { success: false, error: "Tab not visible" };
85
+ }
86
+ await waitForIdle();
87
+ const resolvedOptions = {
88
+ quality: options?.quality ?? DEFAULT_QUALITY,
89
+ excludeSelector: options?.excludeSelector,
90
+ maxWidth: options?.maxWidth ?? DEFAULT_MAX_WIDTH,
91
+ timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS
92
+ };
93
+ let timeoutId;
94
+ try {
95
+ const result = await Promise.race([
96
+ doCapture(resolvedOptions),
97
+ new Promise((resolve) => {
98
+ timeoutId = setTimeout(
99
+ () => resolve({ success: false, error: "Capture timeout exceeded" }),
100
+ resolvedOptions.timeoutMs
101
+ );
102
+ })
103
+ ]);
104
+ return result;
105
+ } catch (err) {
106
+ return {
107
+ success: false,
108
+ error: err instanceof Error ? err.message : "Unknown capture error"
109
+ };
110
+ } finally {
111
+ clearTimeout(timeoutId);
112
+ }
113
+ }
114
+
115
+ export { captureScreenshot };
116
+ //# sourceMappingURL=screen-capture.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-capture.esm.js","sources":["../../src/utils/screen-capture.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { sanitizeClonedDom } from './sensitive-data-redactor';\n\nconst DEFAULT_QUALITY = 0.7;\nconst DEFAULT_MAX_WIDTH = 1280;\nconst DEFAULT_TIMEOUT_MS = 1000;\nconst DEFAULT_EXCLUDE_SELECTOR = '[data-screen-capture-exclude]';\n\nexport interface CaptureOptions {\n /** Image quality 0-1 (default: 0.7) */\n quality?: number;\n /** CSS selector for elements to exclude (default: '[data-screen-capture-exclude]') */\n excludeSelector?: string;\n /** Max output width in pixels for compression (default: 1280) */\n maxWidth?: number;\n /** Maximum time allowed for capture in ms (default: 1000) */\n timeoutMs?: number;\n}\n\nexport interface CaptureResult {\n success: true;\n /** Base64-encoded image without the data URI prefix */\n base64: string;\n /** MIME type: 'image/jpeg' */\n contentType: string;\n width: number;\n height: number;\n /** Time taken for the capture in milliseconds */\n captureTimeMs: number;\n}\n\nexport interface CaptureError {\n success: false;\n error: string;\n}\n\nexport type CaptureResponse = CaptureResult | CaptureError;\n\nfunction stripDataUriPrefix(dataUri: string): string {\n const commaIdx = dataUri.indexOf(',');\n return commaIdx !== -1 ? dataUri.slice(commaIdx + 1) : dataUri;\n}\n\nfunction waitForIdle(): Promise<void> {\n return new Promise(resolve => {\n if ('requestIdleCallback' in window) {\n (window as Window).requestIdleCallback(() => resolve(), { timeout: 100 });\n } else {\n setTimeout(resolve, 0);\n }\n });\n}\n\nfunction scaleCanvas(\n canvas: HTMLCanvasElement,\n maxWidth: number,\n): HTMLCanvasElement {\n if (canvas.width <= maxWidth) {\n return canvas;\n }\n\n const scaleFactor = maxWidth / canvas.width;\n const scaledWidth = maxWidth;\n const scaledHeight = Math.round(canvas.height * scaleFactor);\n\n const scaledCanvas = document.createElement('canvas');\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n\n const ctx = scaledCanvas.getContext('2d');\n if (!ctx) {\n return canvas;\n }\n ctx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n\n return scaledCanvas;\n}\n\nasync function doCapture(\n options: Required<CaptureOptions>,\n): Promise<CaptureResponse> {\n const targetElement = document.querySelector('#root');\n if (!targetElement) {\n return { success: false, error: 'Root element (#root) not found' };\n }\n\n const { default: html2canvas } = await import('html2canvas-pro');\n\n const start = window.performance.now();\n\n const canvas = await html2canvas(targetElement as HTMLElement, {\n useCORS: true,\n allowTaint: false,\n logging: false,\n onclone: (clonedDocument: Document) => {\n sanitizeClonedDom(clonedDocument);\n },\n ignoreElements: (element: Element) => {\n if (element.hasAttribute('data-screen-capture-exclude')) {\n return true;\n }\n if (element.classList.contains('pf-chatbot')) {\n return true;\n }\n if (options.excludeSelector !== DEFAULT_EXCLUDE_SELECTOR) {\n try {\n if (element.matches(options.excludeSelector)) {\n return true;\n }\n } catch {\n // Invalid selector — skip rather than crash capture\n }\n }\n return false;\n },\n });\n\n let scaledCanvas: HTMLCanvasElement | undefined;\n try {\n scaledCanvas = scaleCanvas(canvas, options.maxWidth);\n const dataUri = scaledCanvas.toDataURL('image/jpeg', options.quality);\n\n const captureTimeMs = Math.round(window.performance.now() - start);\n\n return {\n success: true,\n base64: stripDataUriPrefix(dataUri),\n contentType: 'image/jpeg',\n width: scaledCanvas.width,\n height: scaledCanvas.height,\n captureTimeMs,\n };\n } finally {\n canvas.width = 0;\n canvas.height = 0;\n if (scaledCanvas && scaledCanvas !== canvas) {\n scaledCanvas.width = 0;\n scaledCanvas.height = 0;\n }\n }\n}\n\n/**\n * Captures a screenshot of the current RHDH viewport, excluding the\n * Lightspeed chat panel and redacting sensitive data.\n *\n * Outputs JPEG format.\n * Includes performance guardrails: visibility check, idle deferral, and timeout.\n */\nexport async function captureScreenshot(\n options?: CaptureOptions,\n): Promise<CaptureResponse> {\n if (document.visibilityState === 'hidden') {\n return { success: false, error: 'Tab not visible' };\n }\n\n await waitForIdle();\n\n const resolvedOptions: Required<CaptureOptions> = {\n quality: options?.quality ?? DEFAULT_QUALITY,\n excludeSelector: options?.excludeSelector ?? DEFAULT_EXCLUDE_SELECTOR,\n maxWidth: options?.maxWidth ?? DEFAULT_MAX_WIDTH,\n timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n };\n\n // When the timeout wins the race, doCapture continues running in the background\n // since html2canvas-pro does not support AbortController-based cancellation.\n // The timeout prevents the caller from waiting indefinitely; background work\n // completes harmlessly and its result is discarded.\n let timeoutId: ReturnType<typeof setTimeout>;\n try {\n const result = await Promise.race<CaptureResponse>([\n doCapture(resolvedOptions),\n new Promise<CaptureError>(resolve => {\n timeoutId = setTimeout(\n () => resolve({ success: false, error: 'Capture timeout exceeded' }),\n resolvedOptions.timeoutMs,\n );\n }),\n ]);\n return result;\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Unknown capture error',\n };\n } finally {\n clearTimeout(timeoutId!);\n }\n}\n"],"names":[],"mappings":"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"}