@red-hat-developer-hub/backstage-plugin-lightspeed 0.5.7 → 0.6.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 (59) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/components/Attachment.esm.js +71 -61
  3. package/dist/components/Attachment.esm.js.map +1 -1
  4. package/dist/components/AttachmentContext.esm.js +32 -23
  5. package/dist/components/AttachmentContext.esm.js.map +1 -1
  6. package/dist/components/DeleteModal.esm.js +24 -15
  7. package/dist/components/DeleteModal.esm.js.map +1 -1
  8. package/dist/components/FilePreview.esm.js +16 -13
  9. package/dist/components/FilePreview.esm.js.map +1 -1
  10. package/dist/components/LightSpeedChat.esm.js +172 -129
  11. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  12. package/dist/components/LightspeedChatBox.esm.js +43 -34
  13. package/dist/components/LightspeedChatBox.esm.js.map +1 -1
  14. package/dist/components/LightspeedChatBoxHeader.esm.js +15 -12
  15. package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
  16. package/dist/components/LightspeedIcon.esm.js +11 -2
  17. package/dist/components/LightspeedIcon.esm.js.map +1 -1
  18. package/dist/components/LightspeedPage.esm.js +32 -26
  19. package/dist/components/LightspeedPage.esm.js.map +1 -1
  20. package/dist/components/PermissionRequiredIcon.esm.js +10 -2
  21. package/dist/components/PermissionRequiredIcon.esm.js.map +1 -1
  22. package/dist/components/PermissionRequiredState.esm.js +25 -11
  23. package/dist/components/PermissionRequiredState.esm.js.map +1 -1
  24. package/dist/components/Trans.esm.js +33 -0
  25. package/dist/components/Trans.esm.js.map +1 -0
  26. package/dist/const.esm.js +20 -43
  27. package/dist/const.esm.js.map +1 -1
  28. package/dist/hooks/useAutoScroll.esm.js +11 -11
  29. package/dist/hooks/useAutoScroll.esm.js.map +1 -1
  30. package/dist/hooks/useBufferedMessages.esm.js +5 -5
  31. package/dist/hooks/useBufferedMessages.esm.js.map +1 -1
  32. package/dist/hooks/useConversationMessages.esm.js +8 -8
  33. package/dist/hooks/useConversationMessages.esm.js.map +1 -1
  34. package/dist/hooks/useFeedbackActions.esm.js +47 -18
  35. package/dist/hooks/useFeedbackActions.esm.js.map +1 -1
  36. package/dist/hooks/useIsMobile.esm.js +3 -3
  37. package/dist/hooks/useIsMobile.esm.js.map +1 -1
  38. package/dist/hooks/useLastOpenedConversation.esm.js.map +1 -1
  39. package/dist/hooks/useTranslation.esm.js +7 -0
  40. package/dist/hooks/useTranslation.esm.js.map +1 -0
  41. package/dist/hooks/useWelcomePrompts.esm.js +21 -6
  42. package/dist/hooks/useWelcomePrompts.esm.js.map +1 -1
  43. package/dist/index.d.ts +11 -5
  44. package/dist/index.esm.js +1 -0
  45. package/dist/index.esm.js.map +1 -1
  46. package/dist/translations/de.esm.js +128 -0
  47. package/dist/translations/de.esm.js.map +1 -0
  48. package/dist/translations/es.esm.js +128 -0
  49. package/dist/translations/es.esm.js.map +1 -0
  50. package/dist/translations/fr.esm.js +128 -0
  51. package/dist/translations/fr.esm.js.map +1 -0
  52. package/dist/translations/index.esm.js +14 -0
  53. package/dist/translations/index.esm.js.map +1 -0
  54. package/dist/translations/translationRef.esm.js +128 -0
  55. package/dist/translations/translationRef.esm.js.map +1 -0
  56. package/dist/types.esm.js.map +1 -1
  57. package/dist/utils/lightspeed-chatbox-utils.esm.js +24 -21
  58. package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -1
  59. package/package.json +12 -12
package/dist/const.esm.js CHANGED
@@ -1,8 +1,6 @@
1
1
  const TEMP_CONVERSATION_ID = "temp-conversation-id";
2
- const FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;
3
- const FUNCTION_DISCLAIMER = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Developer Lightspeed uses question (prompt) validation to ensure that conversations remain focused on technical topics relevant to Red Hat Developer Hub, such as Backstage, Kubernetes, and OpenShift. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;
4
- const createPrompt = (title, message) => {
5
- return { title, message };
2
+ const createPrompt = (titleKey, messageKey) => {
3
+ return { titleKey, messageKey };
6
4
  };
7
5
  const supportedFileTypes = {
8
6
  "text/plain": [".txt"],
@@ -12,56 +10,35 @@ const supportedFileTypes = {
12
10
  };
13
11
  const DEFAULT_SAMPLE_PROMPTS = [
14
12
  createPrompt(
15
- "Get Help On Code Readability",
16
- "Can you suggest techniques I can use to make my code more readable and maintainable?"
13
+ "prompts.codeReadability.title",
14
+ "prompts.codeReadability.message"
17
15
  ),
16
+ createPrompt("prompts.debugging.title", "prompts.debugging.message"),
18
17
  createPrompt(
19
- "Get Help With Debugging",
20
- "My application is throwing an error when trying to connect to the database. Can you help me identify the issue?"
18
+ "prompts.developmentConcept.title",
19
+ "prompts.developmentConcept.message"
21
20
  ),
22
21
  createPrompt(
23
- "Explain a Development Concept",
24
- "Can you explain how microservices architecture works and its advantages over a monolithic design?"
22
+ "prompts.codeOptimization.title",
23
+ "prompts.codeOptimization.message"
25
24
  ),
25
+ createPrompt("prompts.documentation.title", "prompts.documentation.message"),
26
+ createPrompt("prompts.gitWorkflows.title", "prompts.gitWorkflows.message"),
26
27
  createPrompt(
27
- "Suggest Code Optimizations",
28
- "Can you suggest common ways to optimize code to achieve better performance?"
28
+ "prompts.testingStrategies.title",
29
+ "prompts.testingStrategies.message"
29
30
  ),
30
31
  createPrompt(
31
- "Documentation Summary",
32
- "Can you summarize the documentation for implementing OAuth 2.0 authentication in a web app?"
32
+ "prompts.sortingAlgorithms.title",
33
+ "prompts.sortingAlgorithms.message"
33
34
  ),
34
- createPrompt(
35
- "Workflows With Git",
36
- "I want to make changes to code on another branch without loosing my existing work. What is the procedure to do this using Git?"
37
- ),
38
- createPrompt(
39
- "Suggest Testing Strategies",
40
- "Can you recommend some common testing strategies that will make my application robust and error-free?"
41
- ),
42
- createPrompt(
43
- "Demystify Sorting Algorithms",
44
- "Can you explain the difference between a quicksort and a merge sort algorithm, and when to use each?"
45
- ),
46
- createPrompt(
47
- "Understand Event-Driven Architecture",
48
- "Can you explain what event-driven architecture is and when it\u2019s beneficial to use it in software development?"
49
- )
35
+ createPrompt("prompts.eventDriven.title", "prompts.eventDriven.message")
50
36
  ];
51
37
  const RHDH_SAMPLE_PROMPTS = [
52
- createPrompt(
53
- "Deploy With Tekton",
54
- "Can you help me automate the deployment of my application using Tekton pipelines?"
55
- ),
56
- createPrompt(
57
- "Create An OpenShift Deployment",
58
- "Can you guide me through creating a new deployment in OpenShift for a containerized application?"
59
- ),
60
- createPrompt(
61
- "Getting Started with Red Hat Developer Hub",
62
- "Can you guide me through the first steps to start using Developer Hub as a developer, like exploring the Software Catalog and adding my service?"
63
- )
38
+ createPrompt("prompts.tekton.title", "prompts.tekton.message"),
39
+ createPrompt("prompts.openshift.title", "prompts.openshift.message"),
40
+ createPrompt("prompts.rhdh.title", "prompts.rhdh.message")
64
41
  ];
65
42
 
66
- export { DEFAULT_SAMPLE_PROMPTS, FUNCTION_DISCLAIMER, FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION, RHDH_SAMPLE_PROMPTS, TEMP_CONVERSATION_ID, supportedFileTypes };
43
+ export { DEFAULT_SAMPLE_PROMPTS, RHDH_SAMPLE_PROMPTS, TEMP_CONVERSATION_ID, supportedFileTypes };
67
44
  //# sourceMappingURL=const.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"const.esm.js","sources":["../src/const.ts"],"sourcesContent":["import { SamplePrompts } from './types';\n\n/*\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 */\nexport const TEMP_CONVERSATION_ID = 'temp-conversation-id';\n\nexport const FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;\n\nexport const FUNCTION_DISCLAIMER = `Developer Lightspeed can answer questions on many topics using your configured models. Developer Lightspeed's responses are influenced by the Red Hat Developer Hub documentation but Developer Lightspeed does not have access to your Software Catalog, TechDocs, or Templates etc. Developer Lightspeed uses question (prompt) validation to ensure that conversations remain focused on technical topics relevant to Red Hat Developer Hub, such as Backstage, Kubernetes, and OpenShift. Do not include personal or sensitive information in your input. Interactions with Developer Lightspeed may be reviewed and used to improve products or services.`;\n\nconst createPrompt = (title: string, message: string) => {\n return { title, message };\n};\n\nexport const supportedFileTypes = {\n 'text/plain': ['.txt'],\n 'application/json': ['.json'],\n 'application/yaml': ['.yaml', '.yml'],\n 'application/xml': ['.xml'],\n};\n\nexport const DEFAULT_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt(\n 'Get Help On Code Readability',\n 'Can you suggest techniques I can use to make my code more readable and maintainable?',\n ),\n createPrompt(\n 'Get Help With Debugging',\n 'My application is throwing an error when trying to connect to the database. Can you help me identify the issue?',\n ),\n createPrompt(\n 'Explain a Development Concept',\n 'Can you explain how microservices architecture works and its advantages over a monolithic design?',\n ),\n createPrompt(\n 'Suggest Code Optimizations',\n 'Can you suggest common ways to optimize code to achieve better performance?',\n ),\n createPrompt(\n 'Documentation Summary',\n 'Can you summarize the documentation for implementing OAuth 2.0 authentication in a web app?',\n ),\n createPrompt(\n 'Workflows With Git',\n 'I want to make changes to code on another branch without loosing my existing work. What is the procedure to do this using Git?',\n ),\n createPrompt(\n 'Suggest Testing Strategies',\n 'Can you recommend some common testing strategies that will make my application robust and error-free?',\n ),\n createPrompt(\n 'Demystify Sorting Algorithms',\n 'Can you explain the difference between a quicksort and a merge sort algorithm, and when to use each?',\n ),\n createPrompt(\n 'Understand Event-Driven Architecture',\n 'Can you explain what event-driven architecture is and when it’s beneficial to use it in software development?',\n ),\n];\n\nexport const RHDH_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt(\n 'Deploy With Tekton',\n 'Can you help me automate the deployment of my application using Tekton pipelines?',\n ),\n createPrompt(\n 'Create An OpenShift Deployment',\n 'Can you guide me through creating a new deployment in OpenShift for a containerized application?',\n ),\n createPrompt(\n 'Getting Started with Red Hat Developer Hub',\n 'Can you guide me through the first steps to start using Developer Hub as a developer, like exploring the Software Catalog and adding my service?',\n ),\n];\n"],"names":[],"mappings":"AAiBO,MAAM,oBAAuB,GAAA;AAE7B,MAAM,+CAAkD,GAAA,CAAA,sbAAA;AAExD,MAAM,mBAAsB,GAAA,CAAA,8nBAAA;AAEnC,MAAM,YAAA,GAAe,CAAC,KAAA,EAAe,OAAoB,KAAA;AACvD,EAAO,OAAA,EAAE,OAAO,OAAQ,EAAA;AAC1B,CAAA;AAEO,MAAM,kBAAqB,GAAA;AAAA,EAChC,YAAA,EAAc,CAAC,MAAM,CAAA;AAAA,EACrB,kBAAA,EAAoB,CAAC,OAAO,CAAA;AAAA,EAC5B,kBAAA,EAAoB,CAAC,OAAA,EAAS,MAAM,CAAA;AAAA,EACpC,iBAAA,EAAmB,CAAC,MAAM;AAC5B;AAEO,MAAM,sBAAwC,GAAA;AAAA,EACnD,YAAA;AAAA,IACE,8BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,yBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,+BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,4BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,uBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,oBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,4BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,8BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,sCAAA;AAAA,IACA;AAAA;AAEJ;AAEO,MAAM,mBAAqC,GAAA;AAAA,EAChD,YAAA;AAAA,IACE,oBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,gCAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,4CAAA;AAAA,IACA;AAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"const.esm.js","sources":["../src/const.ts"],"sourcesContent":["import { SamplePrompts } from './types';\n\n/*\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 */\nexport const TEMP_CONVERSATION_ID = 'temp-conversation-id';\n\n// Translation keys for disclaimers\nexport const FUNCTION_DISCLAIMER_WITHOUT_QUESTION_VALIDATION_KEY =\n 'disclaimer.withoutValidation';\nexport const FUNCTION_DISCLAIMER_KEY = 'disclaimer.withValidation';\n\nconst createPrompt = (titleKey: string, messageKey: string) => {\n return { titleKey, messageKey };\n};\n\nexport const supportedFileTypes = {\n 'text/plain': ['.txt'],\n 'application/json': ['.json'],\n 'application/yaml': ['.yaml', '.yml'],\n 'application/xml': ['.xml'],\n};\n\nexport const DEFAULT_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt(\n 'prompts.codeReadability.title',\n 'prompts.codeReadability.message',\n ),\n createPrompt('prompts.debugging.title', 'prompts.debugging.message'),\n createPrompt(\n 'prompts.developmentConcept.title',\n 'prompts.developmentConcept.message',\n ),\n createPrompt(\n 'prompts.codeOptimization.title',\n 'prompts.codeOptimization.message',\n ),\n createPrompt('prompts.documentation.title', 'prompts.documentation.message'),\n createPrompt('prompts.gitWorkflows.title', 'prompts.gitWorkflows.message'),\n createPrompt(\n 'prompts.testingStrategies.title',\n 'prompts.testingStrategies.message',\n ),\n createPrompt(\n 'prompts.sortingAlgorithms.title',\n 'prompts.sortingAlgorithms.message',\n ),\n createPrompt('prompts.eventDriven.title', 'prompts.eventDriven.message'),\n];\n\nexport const RHDH_SAMPLE_PROMPTS: SamplePrompts = [\n createPrompt('prompts.tekton.title', 'prompts.tekton.message'),\n createPrompt('prompts.openshift.title', 'prompts.openshift.message'),\n createPrompt('prompts.rhdh.title', 'prompts.rhdh.message'),\n];\n"],"names":[],"mappings":"AAiBO,MAAM,oBAAuB,GAAA;AAOpC,MAAM,YAAA,GAAe,CAAC,QAAA,EAAkB,UAAuB,KAAA;AAC7D,EAAO,OAAA,EAAE,UAAU,UAAW,EAAA;AAChC,CAAA;AAEO,MAAM,kBAAqB,GAAA;AAAA,EAChC,YAAA,EAAc,CAAC,MAAM,CAAA;AAAA,EACrB,kBAAA,EAAoB,CAAC,OAAO,CAAA;AAAA,EAC5B,kBAAA,EAAoB,CAAC,OAAA,EAAS,MAAM,CAAA;AAAA,EACpC,iBAAA,EAAmB,CAAC,MAAM;AAC5B;AAEO,MAAM,sBAAwC,GAAA;AAAA,EACnD,YAAA;AAAA,IACE,+BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA,CAAa,2BAA2B,2BAA2B,CAAA;AAAA,EACnE,YAAA;AAAA,IACE,kCAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,gCAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA,CAAa,+BAA+B,+BAA+B,CAAA;AAAA,EAC3E,YAAA,CAAa,8BAA8B,8BAA8B,CAAA;AAAA,EACzE,YAAA;AAAA,IACE,iCAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA;AAAA,IACE,iCAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,YAAA,CAAa,6BAA6B,6BAA6B;AACzE;AAEO,MAAM,mBAAqC,GAAA;AAAA,EAChD,YAAA,CAAa,wBAAwB,wBAAwB,CAAA;AAAA,EAC7D,YAAA,CAAa,2BAA2B,2BAA2B,CAAA;AAAA,EACnE,YAAA,CAAa,sBAAsB,sBAAsB;AAC3D;;;;"}
@@ -1,12 +1,12 @@
1
- import React__default from 'react';
1
+ import React from 'react';
2
2
 
3
3
  const useAutoScroll = (containerRef, options = {}) => {
4
4
  const { deltaUp = 10, deltaDown = 60, delay = 200 } = options;
5
- const [autoScroll, setAutoScroll] = React__default.useState(true);
6
- const lastScrollTop = React__default.useRef(0);
7
- const manualScrollInterrupted = React__default.useRef(false);
8
- const debounceTimeout = React__default.useRef(null);
9
- const onScroll = React__default.useCallback(() => {
5
+ const [autoScroll, setAutoScroll] = React.useState(true);
6
+ const lastScrollTop = React.useRef(0);
7
+ const manualScrollInterrupted = React.useRef(false);
8
+ const debounceTimeout = React.useRef(null);
9
+ const onScroll = React.useCallback(() => {
10
10
  const container = containerRef.current;
11
11
  if (!container) return;
12
12
  const currentScrollTop = container.scrollTop;
@@ -27,7 +27,7 @@ const useAutoScroll = (containerRef, options = {}) => {
27
27
  }
28
28
  lastScrollTop.current = currentScrollTop;
29
29
  }, [containerRef, deltaUp, deltaDown, delay]);
30
- React__default.useEffect(() => {
30
+ React.useEffect(() => {
31
31
  const container = containerRef.current;
32
32
  if (!container) {
33
33
  return undefined;
@@ -38,15 +38,15 @@ const useAutoScroll = (containerRef, options = {}) => {
38
38
  if (debounceTimeout.current) clearTimeout(debounceTimeout.current);
39
39
  };
40
40
  }, [onScroll, containerRef]);
41
- const resumeAutoScroll = React__default.useCallback(() => {
41
+ const resumeAutoScroll = React.useCallback(() => {
42
42
  manualScrollInterrupted.current = false;
43
43
  setAutoScroll(true);
44
44
  }, []);
45
- const stopAutoScroll = React__default.useCallback(() => {
45
+ const stopAutoScroll = React.useCallback(() => {
46
46
  manualScrollInterrupted.current = true;
47
47
  setAutoScroll(false);
48
48
  }, []);
49
- const scrollToTop = React__default.useCallback(() => {
49
+ const scrollToTop = React.useCallback(() => {
50
50
  stopAutoScroll();
51
51
  const container = containerRef.current;
52
52
  if (!container) return;
@@ -55,7 +55,7 @@ const useAutoScroll = (containerRef, options = {}) => {
55
55
  behavior: "smooth"
56
56
  });
57
57
  }, [stopAutoScroll, containerRef]);
58
- const scrollToBottom = React__default.useCallback(() => {
58
+ const scrollToBottom = React.useCallback(() => {
59
59
  const container = containerRef.current;
60
60
  if (container) {
61
61
  container.scrollTo({
@@ -1 +1 @@
1
- {"version":3,"file":"useAutoScroll.esm.js","sources":["../../src/hooks/useAutoScroll.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 */\nimport React from 'react';\n\ninterface useAutoScrollOptions {\n deltaUp?: number;\n deltaDown?: number;\n delay?: number;\n}\n\nexport const useAutoScroll = (\n containerRef: React.RefObject<HTMLElement>,\n options: useAutoScrollOptions = {},\n) => {\n const { deltaUp = 10, deltaDown = 60, delay = 200 } = options;\n\n const [autoScroll, setAutoScroll] = React.useState(true);\n const lastScrollTop = React.useRef(0);\n const manualScrollInterrupted = React.useRef(false);\n const debounceTimeout = React.useRef<NodeJS.Timeout | null>(null);\n\n const onScroll = React.useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const currentScrollTop = container.scrollTop;\n const isScrollingDown = currentScrollTop > lastScrollTop.current;\n\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n\n const delta = isScrollingDown ? deltaDown : deltaUp;\n const isAtBottom = distanceFromBottom <= delta;\n\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n\n if (isAtBottom && manualScrollInterrupted.current && isScrollingDown) {\n debounceTimeout.current = setTimeout(() => {\n manualScrollInterrupted.current = false;\n setAutoScroll(true);\n }, delay);\n }\n\n if (!isAtBottom && !manualScrollInterrupted.current) {\n manualScrollInterrupted.current = true;\n setAutoScroll(false);\n }\n\n lastScrollTop.current = currentScrollTop;\n }, [containerRef, deltaUp, deltaDown, delay]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container) {\n return undefined;\n }\n\n container.addEventListener('scroll', onScroll, { passive: true });\n return () => {\n container.removeEventListener('scroll', onScroll);\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n };\n }, [onScroll, containerRef]);\n\n const resumeAutoScroll = React.useCallback(() => {\n manualScrollInterrupted.current = false;\n setAutoScroll(true);\n }, []);\n\n const stopAutoScroll = React.useCallback(() => {\n manualScrollInterrupted.current = true;\n setAutoScroll(false);\n }, []);\n\n const scrollToTop = React.useCallback(() => {\n stopAutoScroll();\n const container = containerRef.current;\n if (!container) return;\n\n container.scrollTo({\n top: 0,\n behavior: 'smooth',\n });\n }, [stopAutoScroll, containerRef]);\n\n const scrollToBottom = React.useCallback(() => {\n const container = containerRef.current;\n if (container) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'smooth',\n });\n }\n resumeAutoScroll();\n }, [resumeAutoScroll, containerRef]);\n\n return {\n autoScroll,\n resumeAutoScroll,\n stopAutoScroll,\n scrollToBottom,\n scrollToTop,\n };\n};\n"],"names":["React"],"mappings":"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"}
1
+ {"version":3,"file":"useAutoScroll.esm.js","sources":["../../src/hooks/useAutoScroll.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 */\nimport React from 'react';\n\ninterface useAutoScrollOptions {\n deltaUp?: number;\n deltaDown?: number;\n delay?: number;\n}\n\nexport const useAutoScroll = (\n containerRef: React.RefObject<HTMLElement>,\n options: useAutoScrollOptions = {},\n) => {\n const { deltaUp = 10, deltaDown = 60, delay = 200 } = options;\n\n const [autoScroll, setAutoScroll] = React.useState(true);\n const lastScrollTop = React.useRef(0);\n const manualScrollInterrupted = React.useRef(false);\n const debounceTimeout = React.useRef<NodeJS.Timeout | null>(null);\n\n const onScroll = React.useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const currentScrollTop = container.scrollTop;\n const isScrollingDown = currentScrollTop > lastScrollTop.current;\n\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n\n const delta = isScrollingDown ? deltaDown : deltaUp;\n const isAtBottom = distanceFromBottom <= delta;\n\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n\n if (isAtBottom && manualScrollInterrupted.current && isScrollingDown) {\n debounceTimeout.current = setTimeout(() => {\n manualScrollInterrupted.current = false;\n setAutoScroll(true);\n }, delay);\n }\n\n if (!isAtBottom && !manualScrollInterrupted.current) {\n manualScrollInterrupted.current = true;\n setAutoScroll(false);\n }\n\n lastScrollTop.current = currentScrollTop;\n }, [containerRef, deltaUp, deltaDown, delay]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container) {\n return undefined;\n }\n\n container.addEventListener('scroll', onScroll, { passive: true });\n return () => {\n container.removeEventListener('scroll', onScroll);\n if (debounceTimeout.current) clearTimeout(debounceTimeout.current);\n };\n }, [onScroll, containerRef]);\n\n const resumeAutoScroll = React.useCallback(() => {\n manualScrollInterrupted.current = false;\n setAutoScroll(true);\n }, []);\n\n const stopAutoScroll = React.useCallback(() => {\n manualScrollInterrupted.current = true;\n setAutoScroll(false);\n }, []);\n\n const scrollToTop = React.useCallback(() => {\n stopAutoScroll();\n const container = containerRef.current;\n if (!container) return;\n\n container.scrollTo({\n top: 0,\n behavior: 'smooth',\n });\n }, [stopAutoScroll, containerRef]);\n\n const scrollToBottom = React.useCallback(() => {\n const container = containerRef.current;\n if (container) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'smooth',\n });\n }\n resumeAutoScroll();\n }, [resumeAutoScroll, containerRef]);\n\n return {\n autoScroll,\n resumeAutoScroll,\n stopAutoScroll,\n scrollToBottom,\n scrollToTop,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import React__default from 'react';
1
+ import React from 'react';
2
2
 
3
3
  const useBufferedMessages = (messages, interval = 30) => {
4
- const [bufferedMessages, setBufferedMessages] = React__default.useState(messages);
5
- const lastUpdateTime = React__default.useRef(0);
6
- const animationFrame = React__default.useRef(null);
7
- React__default.useEffect(() => {
4
+ const [bufferedMessages, setBufferedMessages] = React.useState(messages);
5
+ const lastUpdateTime = React.useRef(0);
6
+ const animationFrame = React.useRef(null);
7
+ React.useEffect(() => {
8
8
  const now = Date.now();
9
9
  const update = () => {
10
10
  setBufferedMessages(messages);
@@ -1 +1 @@
1
- {"version":3,"file":"useBufferedMessages.esm.js","sources":["../../src/hooks/useBufferedMessages.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 */\nimport React from 'react';\n\nexport const useBufferedMessages = <T>(messages: T[], interval = 30): T[] => {\n const [bufferedMessages, setBufferedMessages] = React.useState(messages);\n const lastUpdateTime = React.useRef(0);\n const animationFrame = React.useRef<number | null>(null);\n\n React.useEffect(() => {\n const now = Date.now();\n\n const update = () => {\n setBufferedMessages(messages);\n lastUpdateTime.current = now;\n };\n\n if (now - lastUpdateTime.current > interval) {\n update();\n } else {\n if (animationFrame.current) cancelAnimationFrame(animationFrame.current);\n animationFrame.current = requestAnimationFrame(update);\n }\n\n return () => {\n if (animationFrame.current) cancelAnimationFrame(animationFrame.current);\n };\n }, [messages, interval]);\n\n return bufferedMessages;\n};\n"],"names":["React"],"mappings":";;AAiBO,MAAM,mBAAsB,GAAA,CAAI,QAAe,EAAA,QAAA,GAAW,EAAY,KAAA;AAC3E,EAAA,MAAM,CAAC,gBAAkB,EAAA,mBAAmB,CAAI,GAAAA,cAAA,CAAM,SAAS,QAAQ,CAAA;AACvE,EAAM,MAAA,cAAA,GAAiBA,cAAM,CAAA,MAAA,CAAO,CAAC,CAAA;AACrC,EAAM,MAAA,cAAA,GAAiBA,cAAM,CAAA,MAAA,CAAsB,IAAI,CAAA;AAEvD,EAAAA,cAAA,CAAM,UAAU,MAAM;AACpB,IAAM,MAAA,GAAA,GAAM,KAAK,GAAI,EAAA;AAErB,IAAA,MAAM,SAAS,MAAM;AACnB,MAAA,mBAAA,CAAoB,QAAQ,CAAA;AAC5B,MAAA,cAAA,CAAe,OAAU,GAAA,GAAA;AAAA,KAC3B;AAEA,IAAI,IAAA,GAAA,GAAM,cAAe,CAAA,OAAA,GAAU,QAAU,EAAA;AAC3C,MAAO,MAAA,EAAA;AAAA,KACF,MAAA;AACL,MAAA,IAAI,cAAe,CAAA,OAAA,EAA8B,oBAAA,CAAA,cAAA,CAAe,OAAO,CAAA;AACvE,MAAe,cAAA,CAAA,OAAA,GAAU,sBAAsB,MAAM,CAAA;AAAA;AAGvD,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,cAAe,CAAA,OAAA,EAA8B,oBAAA,CAAA,cAAA,CAAe,OAAO,CAAA;AAAA,KACzE;AAAA,GACC,EAAA,CAAC,QAAU,EAAA,QAAQ,CAAC,CAAA;AAEvB,EAAO,OAAA,gBAAA;AACT;;;;"}
1
+ {"version":3,"file":"useBufferedMessages.esm.js","sources":["../../src/hooks/useBufferedMessages.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 */\nimport React from 'react';\n\nexport const useBufferedMessages = <T>(messages: T[], interval = 30): T[] => {\n const [bufferedMessages, setBufferedMessages] = React.useState(messages);\n const lastUpdateTime = React.useRef(0);\n const animationFrame = React.useRef<number | null>(null);\n\n React.useEffect(() => {\n const now = Date.now();\n\n const update = () => {\n setBufferedMessages(messages);\n lastUpdateTime.current = now;\n };\n\n if (now - lastUpdateTime.current > interval) {\n update();\n } else {\n if (animationFrame.current) cancelAnimationFrame(animationFrame.current);\n animationFrame.current = requestAnimationFrame(update);\n }\n\n return () => {\n if (animationFrame.current) cancelAnimationFrame(animationFrame.current);\n };\n }, [messages, interval]);\n\n return bufferedMessages;\n};\n"],"names":[],"mappings":";;AAiBO,MAAM,mBAAsB,GAAA,CAAI,QAAe,EAAA,QAAA,GAAW,EAAY,KAAA;AAC3E,EAAA,MAAM,CAAC,gBAAkB,EAAA,mBAAmB,CAAI,GAAA,KAAA,CAAM,SAAS,QAAQ,CAAA;AACvE,EAAM,MAAA,cAAA,GAAiB,KAAM,CAAA,MAAA,CAAO,CAAC,CAAA;AACrC,EAAM,MAAA,cAAA,GAAiB,KAAM,CAAA,MAAA,CAAsB,IAAI,CAAA;AAEvD,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAM,MAAA,GAAA,GAAM,KAAK,GAAI,EAAA;AAErB,IAAA,MAAM,SAAS,MAAM;AACnB,MAAA,mBAAA,CAAoB,QAAQ,CAAA;AAC5B,MAAA,cAAA,CAAe,OAAU,GAAA,GAAA;AAAA,KAC3B;AAEA,IAAI,IAAA,GAAA,GAAM,cAAe,CAAA,OAAA,GAAU,QAAU,EAAA;AAC3C,MAAO,MAAA,EAAA;AAAA,KACF,MAAA;AACL,MAAA,IAAI,cAAe,CAAA,OAAA,EAA8B,oBAAA,CAAA,cAAA,CAAe,OAAO,CAAA;AACvE,MAAe,cAAA,CAAA,OAAA,GAAU,sBAAsB,MAAM,CAAA;AAAA;AAGvD,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,cAAe,CAAA,OAAA,EAA8B,oBAAA,CAAA,cAAA,CAAe,OAAO,CAAA;AAAA,KACzE;AAAA,GACC,EAAA,CAAC,QAAU,EAAA,QAAQ,CAAC,CAAA;AAEvB,EAAO,OAAA,gBAAA;AACT;;;;"}
@@ -1,4 +1,4 @@
1
- import React__default from 'react';
1
+ import React from 'react';
2
2
  import { useApi } from '@backstage/core-plugin-api';
3
3
  import { useQuery } from '@tanstack/react-query';
4
4
  import { lightspeedApiRef } from '../api/api.esm.js';
@@ -21,15 +21,15 @@ const useFetchConversationMessages = (currentConversation) => {
21
21
  };
22
22
  const useConversationMessages = (conversationId, userName, selectedModel, avatar = userAvatar, onComplete, onStart) => {
23
23
  const { mutateAsync: createMessage } = useCreateConversationMessage();
24
- const scrollToBottomRef = React__default.useRef(null);
25
- const [currentConversation, setCurrentConversation] = React__default.useState(conversationId);
26
- const [conversations, setConversations] = React__default.useState({
24
+ const scrollToBottomRef = React.useRef(null);
25
+ const [currentConversation, setCurrentConversation] = React.useState(conversationId);
26
+ const [conversations, setConversations] = React.useState({
27
27
  [currentConversation]: []
28
28
  });
29
- const streamingConversations = React__default.useRef({
29
+ const streamingConversations = React.useRef({
30
30
  [currentConversation]: []
31
31
  });
32
- React__default.useEffect(() => {
32
+ React.useEffect(() => {
33
33
  if (currentConversation !== conversationId) {
34
34
  setCurrentConversation(conversationId);
35
35
  setConversations((prev) => {
@@ -42,7 +42,7 @@ const useConversationMessages = (conversationId, userName, selectedModel, avatar
42
42
  }
43
43
  }, [currentConversation, conversationId]);
44
44
  const { data: conversationsData = [], ...queryProps } = useFetchConversationMessages(currentConversation);
45
- React__default.useEffect(() => {
45
+ React.useEffect(() => {
46
46
  if (!Array.isArray(conversationsData) || conversationsData.length === 0 && conversationId !== TEMP_CONVERSATION_ID)
47
47
  return;
48
48
  if (conversations) {
@@ -93,7 +93,7 @@ const useConversationMessages = (conversationId, userName, selectedModel, avatar
93
93
  selectedModel,
94
94
  streamingConversations
95
95
  ]);
96
- const handleInputPrompt = React__default.useCallback(
96
+ const handleInputPrompt = React.useCallback(
97
97
  async (prompt, attachments = []) => {
98
98
  let newConversationId = "";
99
99
  const conversationTuple = [
@@ -1 +1 @@
1
- {"version":3,"file":"useConversationMessages.esm.js","sources":["../../src/hooks/useConversationMessages.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 React from 'react';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { MessageProps } from '@patternfly/chatbot';\nimport { useQuery } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { ScrollContainerHandle } from '../components/LightspeedChatBox';\nimport { TEMP_CONVERSATION_ID } from '../const';\nimport botAvatar from '../images/bot-avatar.svg';\nimport userAvatar from '../images/user-avatar.svg';\nimport { Attachment, ReferencedDocument } from '../types';\nimport {\n createBotMessage,\n createUserMessage,\n getMessageData,\n getTimestamp,\n transformDocumentsToSources,\n} from '../utils/lightspeed-chatbox-utils';\nimport { useCreateConversationMessage } from './useCreateCoversationMessage';\n\n// Fetch all conversation messages\nexport const useFetchConversationMessages = (currentConversation: string) => {\n const lightspeedApi = useApi(lightspeedApiRef);\n return useQuery({\n queryKey: ['conversationMessages', currentConversation],\n queryFn: currentConversation\n ? async () => {\n const response =\n await lightspeedApi.getConversationMessages(currentConversation);\n\n return response;\n }\n : undefined,\n retry: false,\n });\n};\n\ntype Conversations = { [_key: string]: MessageProps[] };\n\n/**\n * Fetches all the messages for given conversation_id\n * @param conversationId\n * @param userName\n * @param selectedModel\n * @param avatar\n *\n */\nexport const useConversationMessages = (\n conversationId: string,\n userName: string | undefined,\n selectedModel: string,\n avatar: string = userAvatar,\n onComplete?: (message: string) => void,\n onStart?: (conversation_id: string) => void,\n) => {\n const { mutateAsync: createMessage } = useCreateConversationMessage();\n const scrollToBottomRef = React.useRef<ScrollContainerHandle>(null);\n\n const [currentConversation, setCurrentConversation] =\n React.useState(conversationId);\n const [conversations, setConversations] = React.useState<Conversations>({\n [currentConversation]: [],\n });\n const streamingConversations = React.useRef<Conversations>({\n [currentConversation]: [],\n });\n\n React.useEffect(() => {\n if (currentConversation !== conversationId) {\n setCurrentConversation(conversationId);\n setConversations(prev => {\n if (prev[conversationId]) return prev;\n\n return {\n ...prev,\n [conversationId]: [],\n };\n });\n }\n }, [currentConversation, conversationId]);\n\n const { data: conversationsData = [], ...queryProps } =\n useFetchConversationMessages(currentConversation);\n\n React.useEffect(() => {\n if (\n !Array.isArray(conversationsData) ||\n (conversationsData.length === 0 &&\n conversationId !== TEMP_CONVERSATION_ID)\n )\n return;\n\n const newConvoIndex: number[] = [];\n\n if (conversations) {\n const _conversations: { [key: string]: any[] } = {\n [currentConversation]: [],\n };\n\n let index = 0;\n for (let i = 0; i < conversationsData.length; i += 2) {\n const userMessage = conversationsData[i];\n const aiMessage = conversationsData[i + 1];\n\n const { content: humanMessage, timestamp: userTimestamp } =\n getMessageData(userMessage);\n const {\n model,\n content: botMessage,\n timestamp: botTimestamp,\n referencedDocuments,\n } = getMessageData(aiMessage);\n\n _conversations[currentConversation].push(\n ...[\n createUserMessage({\n avatar,\n name: userName,\n content: humanMessage,\n timestamp: userTimestamp,\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: false,\n name: model ?? selectedModel,\n content: botMessage,\n timestamp: botTimestamp,\n sources: transformDocumentsToSources(referencedDocuments),\n }),\n ],\n );\n\n newConvoIndex.push(index);\n index++;\n }\n\n if (streamingConversations.current[currentConversation]) {\n _conversations[currentConversation].push(\n ...streamingConversations.current[currentConversation],\n );\n }\n\n setConversations(_conversations);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n conversationsData,\n userName,\n avatar,\n currentConversation,\n selectedModel,\n streamingConversations,\n ]);\n\n const handleInputPrompt = React.useCallback(\n async (prompt: string, attachments: Attachment[] = []) => {\n let newConversationId = '';\n\n const conversationTuple = [\n createUserMessage({\n avatar,\n name: userName,\n content: prompt,\n timestamp: getTimestamp(Date.now()) ?? '',\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: true,\n name: selectedModel,\n content: '',\n timestamp: '',\n }),\n ];\n\n streamingConversations.current = {\n ...streamingConversations.current,\n [currentConversation]: conversationTuple,\n };\n\n setConversations((prevConv: Conversations) => {\n return {\n ...prevConv,\n [currentConversation]: [\n ...(prevConv?.[currentConversation] ?? []),\n ...conversationTuple,\n ],\n };\n });\n\n setTimeout(() => {\n scrollToBottomRef.current?.scrollToBottom();\n }, 0);\n const finalMessages: string[] = [];\n let buffer = '';\n\n try {\n const reader = await createMessage({\n prompt,\n selectedModel,\n currentConversation,\n attachments,\n });\n\n const decoder = new TextDecoder('utf-8');\n const keepGoing = true;\n\n while (keepGoing) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process all complete messages separated by double newlines\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop()!;\n\n for (const part of parts) {\n const lines = part\n .split('\\n')\n .filter(line => line.startsWith('data:'));\n\n const jsonString = lines\n .map(line => line.trim().slice(5).trim())\n .join('');\n try {\n const { event, data } = JSON.parse(jsonString);\n if (event === 'start') {\n if (currentConversation === TEMP_CONVERSATION_ID) {\n // If the conversation is temp, we need to set the new conversation id\n newConversationId = data?.conversation_id;\n }\n }\n\n if (event === 'token') {\n const content = data?.token || '';\n\n finalMessages.push(content);\n\n // Store streaming message\n const [humanMessage, aiMessage] =\n streamingConversations.current[currentConversation];\n streamingConversations.current[currentConversation] = [\n humanMessage,\n { ...aiMessage, content: aiMessage.content + content },\n ];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += content;\n lastMessage.name =\n data?.response_metadata?.model || selectedModel;\n lastMessage.timestamp = getTimestamp(\n data?.response_metadata?.created_at || Date.now(),\n );\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n\n if (event === 'end') {\n const documents = data?.referenced_documents || [];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n if (documents.length) {\n lastMessage.sources = {\n sources: documents.map((doc: ReferencedDocument) => ({\n title: doc.doc_title,\n link: doc.doc_url,\n body: doc.doc_description,\n })),\n };\n }\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn('Error parsing JSON:', error);\n if (typeof onComplete === 'function') {\n onComplete('Invalid JSON received');\n }\n }\n }\n }\n } catch (e) {\n setConversations(prevConversations => {\n const conversation = prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += e;\n lastMessage.error = {\n title: e.message,\n };\n lastMessage.timestamp = getTimestamp(Date.now());\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n finalMessages.push(`${e}`);\n\n return {\n ...prevConversations,\n [newConversationId.length > 0\n ? newConversationId\n : currentConversation]: updatedConversation,\n };\n });\n }\n // reset current streaming\n streamingConversations.current[currentConversation] = [];\n if (typeof onComplete === 'function') {\n onComplete(finalMessages.join(''));\n }\n // Swap temp conversation messages with new conversation\n\n if (currentConversation === TEMP_CONVERSATION_ID && newConversationId) {\n setConversations(prevConversations => {\n return {\n ...prevConversations,\n [newConversationId]: prevConversations[TEMP_CONVERSATION_ID],\n };\n });\n\n onStart?.(newConversationId);\n\n setConversations(prev => {\n const { temp, ...rest } = prev;\n return rest;\n });\n }\n },\n\n [\n avatar,\n userName,\n onComplete,\n onStart,\n selectedModel,\n createMessage,\n currentConversation,\n ],\n );\n\n return {\n conversationMessages: conversations[currentConversation] ?? [],\n handleInputPrompt,\n conversations,\n scrollToBottomRef,\n ...queryProps,\n };\n};\n"],"names":["React"],"mappings":"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"}
1
+ {"version":3,"file":"useConversationMessages.esm.js","sources":["../../src/hooks/useConversationMessages.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 React from 'react';\n\nimport { useApi } from '@backstage/core-plugin-api';\n\nimport { MessageProps } from '@patternfly/chatbot';\nimport { useQuery } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { ScrollContainerHandle } from '../components/LightspeedChatBox';\nimport { TEMP_CONVERSATION_ID } from '../const';\nimport botAvatar from '../images/bot-avatar.svg';\nimport userAvatar from '../images/user-avatar.svg';\nimport { Attachment, ReferencedDocument } from '../types';\nimport {\n createBotMessage,\n createUserMessage,\n getMessageData,\n getTimestamp,\n transformDocumentsToSources,\n} from '../utils/lightspeed-chatbox-utils';\nimport { useCreateConversationMessage } from './useCreateCoversationMessage';\n\n// Fetch all conversation messages\nexport const useFetchConversationMessages = (currentConversation: string) => {\n const lightspeedApi = useApi(lightspeedApiRef);\n return useQuery({\n queryKey: ['conversationMessages', currentConversation],\n queryFn: currentConversation\n ? async () => {\n const response =\n await lightspeedApi.getConversationMessages(currentConversation);\n\n return response;\n }\n : undefined,\n retry: false,\n });\n};\n\ntype Conversations = { [_key: string]: MessageProps[] };\n\n/**\n * Fetches all the messages for given conversation_id\n * @param conversationId\n * @param userName\n * @param selectedModel\n * @param avatar\n *\n */\nexport const useConversationMessages = (\n conversationId: string,\n userName: string | undefined,\n selectedModel: string,\n avatar: string = userAvatar,\n onComplete?: (message: string) => void,\n onStart?: (conversation_id: string) => void,\n) => {\n const { mutateAsync: createMessage } = useCreateConversationMessage();\n const scrollToBottomRef = React.useRef<ScrollContainerHandle>(null);\n\n const [currentConversation, setCurrentConversation] =\n React.useState(conversationId);\n const [conversations, setConversations] = React.useState<Conversations>({\n [currentConversation]: [],\n });\n const streamingConversations = React.useRef<Conversations>({\n [currentConversation]: [],\n });\n\n React.useEffect(() => {\n if (currentConversation !== conversationId) {\n setCurrentConversation(conversationId);\n setConversations(prev => {\n if (prev[conversationId]) return prev;\n\n return {\n ...prev,\n [conversationId]: [],\n };\n });\n }\n }, [currentConversation, conversationId]);\n\n const { data: conversationsData = [], ...queryProps } =\n useFetchConversationMessages(currentConversation);\n\n React.useEffect(() => {\n if (\n !Array.isArray(conversationsData) ||\n (conversationsData.length === 0 &&\n conversationId !== TEMP_CONVERSATION_ID)\n )\n return;\n\n const newConvoIndex: number[] = [];\n\n if (conversations) {\n const _conversations: { [key: string]: any[] } = {\n [currentConversation]: [],\n };\n\n let index = 0;\n for (let i = 0; i < conversationsData.length; i += 2) {\n const userMessage = conversationsData[i];\n const aiMessage = conversationsData[i + 1];\n\n const { content: humanMessage, timestamp: userTimestamp } =\n getMessageData(userMessage);\n const {\n model,\n content: botMessage,\n timestamp: botTimestamp,\n referencedDocuments,\n } = getMessageData(aiMessage);\n\n _conversations[currentConversation].push(\n ...[\n createUserMessage({\n avatar,\n name: userName,\n content: humanMessage,\n timestamp: userTimestamp,\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: false,\n name: model ?? selectedModel,\n content: botMessage,\n timestamp: botTimestamp,\n sources: transformDocumentsToSources(referencedDocuments),\n }),\n ],\n );\n\n newConvoIndex.push(index);\n index++;\n }\n\n if (streamingConversations.current[currentConversation]) {\n _conversations[currentConversation].push(\n ...streamingConversations.current[currentConversation],\n );\n }\n\n setConversations(_conversations);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n conversationsData,\n userName,\n avatar,\n currentConversation,\n selectedModel,\n streamingConversations,\n ]);\n\n const handleInputPrompt = React.useCallback(\n async (prompt: string, attachments: Attachment[] = []) => {\n let newConversationId = '';\n\n const conversationTuple = [\n createUserMessage({\n avatar,\n name: userName,\n content: prompt,\n timestamp: getTimestamp(Date.now()) ?? '',\n }),\n createBotMessage({\n avatar: botAvatar,\n isLoading: true,\n name: selectedModel,\n content: '',\n timestamp: '',\n }),\n ];\n\n streamingConversations.current = {\n ...streamingConversations.current,\n [currentConversation]: conversationTuple,\n };\n\n setConversations((prevConv: Conversations) => {\n return {\n ...prevConv,\n [currentConversation]: [\n ...(prevConv?.[currentConversation] ?? []),\n ...conversationTuple,\n ],\n };\n });\n\n setTimeout(() => {\n scrollToBottomRef.current?.scrollToBottom();\n }, 0);\n const finalMessages: string[] = [];\n let buffer = '';\n\n try {\n const reader = await createMessage({\n prompt,\n selectedModel,\n currentConversation,\n attachments,\n });\n\n const decoder = new TextDecoder('utf-8');\n const keepGoing = true;\n\n while (keepGoing) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process all complete messages separated by double newlines\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop()!;\n\n for (const part of parts) {\n const lines = part\n .split('\\n')\n .filter(line => line.startsWith('data:'));\n\n const jsonString = lines\n .map(line => line.trim().slice(5).trim())\n .join('');\n try {\n const { event, data } = JSON.parse(jsonString);\n if (event === 'start') {\n if (currentConversation === TEMP_CONVERSATION_ID) {\n // If the conversation is temp, we need to set the new conversation id\n newConversationId = data?.conversation_id;\n }\n }\n\n if (event === 'token') {\n const content = data?.token || '';\n\n finalMessages.push(content);\n\n // Store streaming message\n const [humanMessage, aiMessage] =\n streamingConversations.current[currentConversation];\n streamingConversations.current[currentConversation] = [\n humanMessage,\n { ...aiMessage, content: aiMessage.content + content },\n ];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += content;\n lastMessage.name =\n data?.response_metadata?.model || selectedModel;\n lastMessage.timestamp = getTimestamp(\n data?.response_metadata?.created_at || Date.now(),\n );\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n\n if (event === 'end') {\n const documents = data?.referenced_documents || [];\n\n setConversations(prevConversations => {\n const conversation =\n prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n if (documents.length) {\n lastMessage.sources = {\n sources: documents.map((doc: ReferencedDocument) => ({\n title: doc.doc_title,\n link: doc.doc_url,\n body: doc.doc_description,\n })),\n };\n }\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n return {\n ...prevConversations,\n [currentConversation]: updatedConversation,\n };\n });\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn('Error parsing JSON:', error);\n if (typeof onComplete === 'function') {\n onComplete('Invalid JSON received');\n }\n }\n }\n }\n } catch (e) {\n setConversations(prevConversations => {\n const conversation = prevConversations[currentConversation] ?? [];\n\n const lastMessageIndex = conversation.length - 1;\n const lastMessage =\n conversation.length === 0\n ? createBotMessage({\n content: '',\n timestamp: getTimestamp(Date.now()),\n })\n : { ...conversation[lastMessageIndex] };\n\n lastMessage.isLoading = false;\n lastMessage.content += e;\n lastMessage.error = {\n title: e.message,\n };\n lastMessage.timestamp = getTimestamp(Date.now());\n\n const updatedConversation = [\n ...conversation.slice(0, lastMessageIndex),\n lastMessage,\n ];\n\n finalMessages.push(`${e}`);\n\n return {\n ...prevConversations,\n [newConversationId.length > 0\n ? newConversationId\n : currentConversation]: updatedConversation,\n };\n });\n }\n // reset current streaming\n streamingConversations.current[currentConversation] = [];\n if (typeof onComplete === 'function') {\n onComplete(finalMessages.join(''));\n }\n // Swap temp conversation messages with new conversation\n\n if (currentConversation === TEMP_CONVERSATION_ID && newConversationId) {\n setConversations(prevConversations => {\n return {\n ...prevConversations,\n [newConversationId]: prevConversations[TEMP_CONVERSATION_ID],\n };\n });\n\n onStart?.(newConversationId);\n\n setConversations(prev => {\n const { temp, ...rest } = prev;\n return rest;\n });\n }\n },\n\n [\n avatar,\n userName,\n onComplete,\n onStart,\n selectedModel,\n createMessage,\n currentConversation,\n ],\n );\n\n return {\n conversationMessages: conversations[currentConversation] ?? [],\n handleInputPrompt,\n conversations,\n scrollToBottomRef,\n ...queryProps,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -1,20 +1,9 @@
1
- import { useRef, useCallback, useEffect, useMemo } from 'react';
1
+ import { useRef, useMemo, useCallback, useEffect } from 'react';
2
2
  import { useCaptureFeedback } from './useCaptureFeedback.esm.js';
3
3
  import { useFeedbackState } from './useFeedbackState.esm.js';
4
4
  import { useFeedbackStatus } from './useFeedbackStatus.esm.js';
5
+ import { useTranslation } from './useTranslation.esm.js';
5
6
 
6
- const quickResponses = {
7
- positive: [
8
- { id: "1", content: "Helpful information" },
9
- { id: "2", content: "Easy to understand" },
10
- { id: "3", content: "Resolved my issue" }
11
- ],
12
- negative: [
13
- { id: "1", content: `Didn\u2019t answer my question` },
14
- { id: "2", content: "Hard to understand" },
15
- { id: "3", content: "Not Helpful" }
16
- ]
17
- };
18
7
  const useFeedbackActions = (messages, conversationId, isStreaming) => {
19
8
  const copyButtonRef = useRef(null);
20
9
  const speakingButtonRef = useRef(null);
@@ -22,15 +11,43 @@ const useFeedbackActions = (messages, conversationId, isStreaming) => {
22
11
  const { data: feedbackEnabled } = useFeedbackStatus();
23
12
  const { state, ...dispatch } = useFeedbackState();
24
13
  const { mutateAsync: captureFeedback } = useCaptureFeedback();
14
+ const { t } = useTranslation();
15
+ const createQuickResponses = useMemo(
16
+ () => ({
17
+ positive: [
18
+ { id: "1", content: t("feedback.quickResponses.positive.helpful") },
19
+ {
20
+ id: "2",
21
+ content: t("feedback.quickResponses.positive.easyToUnderstand")
22
+ },
23
+ {
24
+ id: "3",
25
+ content: t("feedback.quickResponses.positive.resolvedIssue")
26
+ }
27
+ ],
28
+ negative: [
29
+ { id: "1", content: t("feedback.quickResponses.negative.didntAnswer") },
30
+ {
31
+ id: "2",
32
+ content: t("feedback.quickResponses.negative.hardToUnderstand")
33
+ },
34
+ { id: "3", content: t("feedback.quickResponses.negative.notHelpful") }
35
+ ]
36
+ }),
37
+ [t]
38
+ );
25
39
  const getFeedbackForm = useCallback(
26
40
  (messageId, sentiment) => ({
27
41
  key: `feedback-${sentiment}`,
28
42
  id: `feedback-${sentiment}-${messageId}`,
29
43
  hasTextArea: true,
30
- quickResponses: quickResponses[sentiment],
44
+ title: t("feedback.form.title"),
45
+ textAreaPlaceholder: t("feedback.form.textAreaPlaceholder"),
46
+ submitWord: t("feedback.form.submitWord"),
47
+ quickResponses: createQuickResponses[sentiment],
31
48
  onSubmit: (quickResponse = "0", additionalFeedback) => {
32
49
  const quickIndex = Number(quickResponse) - 1;
33
- const quickText = quickResponse !== "0" ? quickResponses[sentiment]?.[quickIndex]?.content : undefined;
50
+ const quickText = quickResponse !== "0" ? createQuickResponses[sentiment]?.[quickIndex]?.content : undefined;
34
51
  let user_feedback = "";
35
52
  if (quickText && additionalFeedback) {
36
53
  user_feedback = `${quickText}
@@ -69,7 +86,14 @@ Additional feedback: ${additionalFeedback}`;
69
86
  },
70
87
  focusOnLoad: true
71
88
  }),
72
- [captureFeedback, conversationId, dispatch, messages]
89
+ [
90
+ captureFeedback,
91
+ conversationId,
92
+ dispatch,
93
+ messages,
94
+ createQuickResponses,
95
+ t
96
+ ]
73
97
  );
74
98
  const feedbackForms = useCallback(
75
99
  (messageId) => ({
@@ -84,13 +108,15 @@ Additional feedback: ${additionalFeedback}`;
84
108
  if (!isVisible) return undefined;
85
109
  return {
86
110
  id: `feebback-completion-${sentiment}-${messageId}`,
111
+ title: t("feedback.completion.title"),
112
+ body: t("feedback.completion.body"),
87
113
  onClose: () => {
88
114
  dispatch.hideCompletionForm(messageId, sentiment);
89
115
  dispatch.resetButtonState(messageId);
90
116
  }
91
117
  };
92
118
  },
93
- [state, dispatch]
119
+ [state, dispatch, t]
94
120
  );
95
121
  const scrollToFeedbackForm = useCallback(
96
122
  (messageId, sentiment) => {
@@ -181,13 +207,14 @@ Additional feedback: ${additionalFeedback}`;
181
207
  scrollToFeedbackForm(messageId, sentiment);
182
208
  }
183
209
  },
210
+ tooltipContent: selected === "positive" ? t("feedback.tooltips.goodResponse") : t("feedback.tooltips.badResponse"),
184
211
  "aria-expanded": sentiment === selected,
185
212
  "aria-controls": showFeedbackForm ? `feedback-${sentiment}-${messageId}` : `feebback-completion-${sentiment}-${messageId}`
186
213
  });
187
214
  const copyAction = {
188
215
  isClicked: copied,
189
216
  ref: copyButtonRef,
190
- tooltipContent: copied ? "Copied" : null,
217
+ tooltipContent: copied ? t("feedback.tooltips.copied") : t("feedback.tooltips.copy"),
191
218
  onClick: async () => {
192
219
  await window.navigator.clipboard.writeText(
193
220
  message.content
@@ -205,6 +232,7 @@ Additional feedback: ${additionalFeedback}`;
205
232
  isClicked: isSpeaking,
206
233
  ref: speakingButtonRef,
207
234
  id: `listen-${messageId}`,
235
+ tooltipContent: isSpeaking ? t("feedback.tooltips.listening") : t("feedback.tooltips.listen"),
208
236
  onClick: () => {
209
237
  const isCurrentlySpeaking = state[messageId]?.buttonState?.isSpeaking;
210
238
  if (isCurrentlySpeaking) {
@@ -228,6 +256,7 @@ Additional feedback: ${additionalFeedback}`;
228
256
  };
229
257
  }) : messages;
230
258
  }, [
259
+ t,
231
260
  conversationId,
232
261
  messages,
233
262
  state,
@@ -1 +1 @@
1
- {"version":3,"file":"useFeedbackActions.esm.js","sources":["../../src/hooks/useFeedbackActions.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 { useCallback, useEffect, useMemo, useRef } from 'react';\n\nimport { MessageProps } from '@patternfly/chatbot';\nimport QuickResponse from '@patternfly/chatbot/dist/cjs/Message/QuickResponse/QuickResponse';\nimport { UserFeedbackProps } from '@patternfly/chatbot/dist/cjs/Message/UserFeedback/UserFeedback';\nimport { UserFeedbackCompleteProps } from '@patternfly/chatbot/dist/cjs/Message/UserFeedback/UserFeedbackComplete';\n\nimport { useCaptureFeedback } from '../hooks/useCaptureFeedback';\nimport { CaptureFeedback } from '../types';\nimport { Sentiment, useFeedbackState } from './useFeedbackState';\nimport { useFeedbackStatus } from './useFeedbackStatus';\n\nconst quickResponses: { [key in Sentiment]: QuickResponse[] } = {\n positive: [\n { id: '1', content: 'Helpful information' },\n { id: '2', content: 'Easy to understand' },\n { id: '3', content: 'Resolved my issue' },\n ],\n negative: [\n { id: '1', content: `Didn’t answer my question` },\n { id: '2', content: 'Hard to understand' },\n { id: '3', content: 'Not Helpful' },\n ],\n};\n\nexport const useFeedbackActions = <T extends MessageProps>(\n messages: T[],\n conversationId: string,\n isStreaming: boolean,\n): T[] => {\n const copyButtonRef = useRef<HTMLButtonElement>(null);\n const speakingButtonRef = useRef<HTMLButtonElement>(null);\n const currentSpeakingIdRef = useRef<string | null>(null);\n\n const { data: feedbackEnabled } = useFeedbackStatus();\n const { state, ...dispatch } = useFeedbackState();\n const { mutateAsync: captureFeedback } = useCaptureFeedback();\n\n const getFeedbackForm = useCallback(\n (messageId: string, sentiment: 'positive' | 'negative') => ({\n key: `feedback-${sentiment}`,\n id: `feedback-${sentiment}-${messageId}`,\n hasTextArea: true,\n quickResponses: quickResponses[sentiment],\n onSubmit: (\n quickResponse: string | undefined = '0',\n additionalFeedback: string | undefined,\n ) => {\n const quickIndex = Number(quickResponse) - 1;\n const quickText =\n quickResponse !== '0'\n ? quickResponses[sentiment]?.[quickIndex]?.content\n : undefined;\n\n let user_feedback = '';\n\n if (quickText && additionalFeedback) {\n user_feedback = `${quickText}\\nAdditional feedback: ${additionalFeedback}`;\n } else if (quickText) {\n user_feedback = quickText;\n } else if (additionalFeedback) {\n user_feedback = additionalFeedback;\n }\n\n const botAnswerId = Number(messageId.split('-').pop());\n const userQuestionId = botAnswerId - 1;\n\n const payload: CaptureFeedback = {\n conversation_id: conversationId,\n user_feedback,\n sentiment: sentiment === 'positive' ? 1 : -1,\n user_question: messages[userQuestionId].content || '',\n llm_response: messages[botAnswerId].content || '',\n };\n\n captureFeedback(payload).then(() => {\n dispatch.hideFeedbackForm(messageId, sentiment);\n dispatch.showCompletionForm(messageId, sentiment);\n dispatch.updateButtonState(messageId, {\n sentiment: sentiment,\n feedbackSubmitted: true,\n });\n\n setTimeout(() => {\n dispatch.hideCompletionForm(messageId, sentiment);\n }, 1000);\n });\n },\n\n // eslint-disable-next-line no-console\n onClose: () => {\n dispatch.hideFeedbackForm(messageId, sentiment);\n dispatch.resetButtonState(messageId);\n dispatch.updateButtonState(messageId, { sentiment: undefined });\n },\n focusOnLoad: true,\n }),\n [captureFeedback, conversationId, dispatch, messages],\n );\n\n const feedbackForms = useCallback(\n (messageId: string): { [key in Sentiment]: UserFeedbackProps } => ({\n positive: getFeedbackForm(messageId, 'positive'),\n negative: getFeedbackForm(messageId, 'negative'),\n }),\n [getFeedbackForm],\n );\n\n const getUserFeedbackComplete = useCallback(\n (\n messageId: string,\n sentiment: Sentiment,\n ): UserFeedbackCompleteProps | undefined => {\n const isVisible = state[messageId]?.completionForm?.[sentiment];\n if (!isVisible) return undefined;\n\n return {\n id: `feebback-completion-${sentiment}-${messageId}`,\n onClose: () => {\n dispatch.hideCompletionForm(messageId, sentiment);\n dispatch.resetButtonState(messageId);\n },\n };\n },\n [state, dispatch],\n );\n\n const scrollToFeedbackForm = useCallback(\n (messageId: string, sentiment: Sentiment) => {\n setTimeout(() => {\n const el = document.getElementById(\n `feedback-${sentiment}-${messageId}`,\n );\n if (el) {\n el.scrollIntoView({ behavior: 'auto', block: 'start' });\n el.focus();\n }\n }, 0);\n },\n [],\n );\n\n const speakMessage = useCallback(\n (messageId: string, content: string) => {\n if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {\n window.speechSynthesis.cancel();\n\n if (\n currentSpeakingIdRef.current &&\n currentSpeakingIdRef.current !== messageId\n ) {\n dispatch.updateButtonState(currentSpeakingIdRef.current, {\n isSpeaking: false,\n });\n }\n }\n\n // Track the current SpeakingId\n currentSpeakingIdRef.current = messageId;\n\n const utterance = new SpeechSynthesisUtterance(content);\n\n utterance.onstart = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: true });\n };\n\n utterance.onend = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n currentSpeakingIdRef.current = null;\n };\n\n utterance.onerror = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n currentSpeakingIdRef.current = null;\n };\n\n window.speechSynthesis.speak(utterance);\n },\n [dispatch],\n );\n\n const cancelSpeaking = useCallback(\n (messageId?: string) => {\n window.speechSynthesis.cancel();\n\n currentSpeakingIdRef.current = null;\n\n if (messageId) {\n // Cancel just this one message\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n } else {\n // Cancel all messages\n Object.keys(state).forEach(id => {\n if (state[id].buttonState?.isSpeaking) {\n dispatch.updateButtonState(id, { isSpeaking: false });\n }\n });\n }\n setTimeout(() => {\n const el = document.getElementById(`listen-${messageId}`);\n if (el) {\n el.blur();\n }\n }, 10);\n },\n [state, dispatch],\n );\n\n useEffect(() => {\n return () => {\n // Stop any ongoing speech when component unmounts\n cancelSpeaking();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [conversationId]);\n\n return useMemo(() => {\n return feedbackEnabled\n ? messages.map((message, index) => {\n if (\n message.role === 'user' ||\n (isStreaming && messages.length - 1 === index)\n )\n return message;\n\n const messageId = `${conversationId}-${message.role}-${index}`;\n const { sentiment, copied, isSpeaking } =\n state[messageId]?.buttonState || {};\n const showFeedbackForm = state[messageId]?.feedbackForm?.[sentiment];\n const userFeedbackForm =\n showFeedbackForm && feedbackForms(messageId)[sentiment];\n\n const userFeedbackComplete = getUserFeedbackComplete(\n messageId,\n sentiment,\n );\n\n const createSentimentAction = (selected: Sentiment) => ({\n isClicked: sentiment === selected,\n onClick: () => {\n if (sentiment !== selected) {\n dispatch.toggleFeedbackForm(messageId, selected);\n dispatch.updateButtonState(messageId, { sentiment: selected });\n scrollToFeedbackForm(messageId, sentiment);\n }\n },\n 'aria-expanded': sentiment === selected,\n 'aria-controls': showFeedbackForm\n ? `feedback-${sentiment}-${messageId}`\n : `feebback-completion-${sentiment}-${messageId}`,\n });\n\n const copyAction = {\n isClicked: copied,\n ref: copyButtonRef,\n tooltipContent: copied ? 'Copied' : null,\n onClick: async () => {\n await window.navigator.clipboard.writeText(\n message.content as string,\n );\n dispatch.updateButtonState(messageId, {\n copied: true,\n });\n\n setTimeout(() => {\n dispatch.updateButtonState(messageId, { copied: false });\n copyButtonRef?.current?.blur();\n }, 1000);\n },\n };\n\n const listenAction = {\n isClicked: isSpeaking,\n ref: speakingButtonRef,\n id: `listen-${messageId}`,\n onClick: () => {\n const isCurrentlySpeaking =\n state[messageId]?.buttonState?.isSpeaking;\n\n if (isCurrentlySpeaking) {\n // Stop if it's already speaking this message\n cancelSpeaking(messageId);\n } else {\n cancelSpeaking();\n speakMessage(messageId, message.content as string);\n }\n },\n };\n\n return {\n ...message,\n userFeedbackForm,\n userFeedbackComplete,\n actions: {\n positive: createSentimentAction('positive'),\n negative: createSentimentAction('negative'),\n copy: copyAction,\n listen: listenAction,\n },\n };\n })\n : messages;\n }, [\n conversationId,\n messages,\n state,\n dispatch,\n isStreaming,\n speakMessage,\n feedbackForms,\n cancelSpeaking,\n feedbackEnabled,\n getUserFeedbackComplete,\n scrollToFeedbackForm,\n ]);\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFeedbackActions.esm.js","sources":["../../src/hooks/useFeedbackActions.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 { useCallback, useEffect, useMemo, useRef } from 'react';\n\nimport { MessageProps } from '@patternfly/chatbot';\nimport { UserFeedbackProps } from '@patternfly/chatbot/dist/cjs/Message/UserFeedback/UserFeedback';\nimport { UserFeedbackCompleteProps } from '@patternfly/chatbot/dist/cjs/Message/UserFeedback/UserFeedbackComplete';\n\nimport { useCaptureFeedback } from '../hooks/useCaptureFeedback';\nimport { CaptureFeedback } from '../types';\nimport { Sentiment, useFeedbackState } from './useFeedbackState';\nimport { useFeedbackStatus } from './useFeedbackStatus';\nimport { useTranslation } from './useTranslation';\n\nexport const useFeedbackActions = <T extends MessageProps>(\n messages: T[],\n conversationId: string,\n isStreaming: boolean,\n): T[] => {\n const copyButtonRef = useRef<HTMLButtonElement>(null);\n const speakingButtonRef = useRef<HTMLButtonElement>(null);\n const currentSpeakingIdRef = useRef<string | null>(null);\n\n const { data: feedbackEnabled } = useFeedbackStatus();\n const { state, ...dispatch } = useFeedbackState();\n const { mutateAsync: captureFeedback } = useCaptureFeedback();\n const { t } = useTranslation();\n\n const createQuickResponses = useMemo(\n () => ({\n positive: [\n { id: '1', content: t('feedback.quickResponses.positive.helpful') },\n {\n id: '2',\n content: t('feedback.quickResponses.positive.easyToUnderstand'),\n },\n {\n id: '3',\n content: t('feedback.quickResponses.positive.resolvedIssue'),\n },\n ],\n negative: [\n { id: '1', content: t('feedback.quickResponses.negative.didntAnswer') },\n {\n id: '2',\n content: t('feedback.quickResponses.negative.hardToUnderstand'),\n },\n { id: '3', content: t('feedback.quickResponses.negative.notHelpful') },\n ],\n }),\n [t],\n );\n\n const getFeedbackForm = useCallback(\n (messageId: string, sentiment: 'positive' | 'negative') => ({\n key: `feedback-${sentiment}`,\n id: `feedback-${sentiment}-${messageId}`,\n hasTextArea: true,\n title: t('feedback.form.title'),\n textAreaPlaceholder: t('feedback.form.textAreaPlaceholder'),\n submitWord: t('feedback.form.submitWord'),\n quickResponses: createQuickResponses[sentiment],\n onSubmit: (\n quickResponse: string | undefined = '0',\n additionalFeedback: string | undefined,\n ) => {\n const quickIndex = Number(quickResponse) - 1;\n const quickText =\n quickResponse !== '0'\n ? createQuickResponses[sentiment]?.[quickIndex]?.content\n : undefined;\n\n let user_feedback = '';\n\n if (quickText && additionalFeedback) {\n user_feedback = `${quickText}\\nAdditional feedback: ${additionalFeedback}`;\n } else if (quickText) {\n user_feedback = quickText;\n } else if (additionalFeedback) {\n user_feedback = additionalFeedback;\n }\n\n const botAnswerId = Number(messageId.split('-').pop());\n const userQuestionId = botAnswerId - 1;\n\n const payload: CaptureFeedback = {\n conversation_id: conversationId,\n user_feedback,\n sentiment: sentiment === 'positive' ? 1 : -1,\n user_question: messages[userQuestionId].content || '',\n llm_response: messages[botAnswerId].content || '',\n };\n\n captureFeedback(payload).then(() => {\n dispatch.hideFeedbackForm(messageId, sentiment);\n dispatch.showCompletionForm(messageId, sentiment);\n dispatch.updateButtonState(messageId, {\n sentiment: sentiment,\n feedbackSubmitted: true,\n });\n\n setTimeout(() => {\n dispatch.hideCompletionForm(messageId, sentiment);\n }, 1000);\n });\n },\n\n // eslint-disable-next-line no-console\n onClose: () => {\n dispatch.hideFeedbackForm(messageId, sentiment);\n dispatch.resetButtonState(messageId);\n dispatch.updateButtonState(messageId, { sentiment: undefined });\n },\n focusOnLoad: true,\n }),\n [\n captureFeedback,\n conversationId,\n dispatch,\n messages,\n createQuickResponses,\n t,\n ],\n );\n\n const feedbackForms = useCallback(\n (messageId: string): { [key in Sentiment]: UserFeedbackProps } => ({\n positive: getFeedbackForm(messageId, 'positive'),\n negative: getFeedbackForm(messageId, 'negative'),\n }),\n [getFeedbackForm],\n );\n\n const getUserFeedbackComplete = useCallback(\n (\n messageId: string,\n sentiment: Sentiment,\n ): UserFeedbackCompleteProps | undefined => {\n const isVisible = state[messageId]?.completionForm?.[sentiment];\n if (!isVisible) return undefined;\n\n return {\n id: `feebback-completion-${sentiment}-${messageId}`,\n title: t('feedback.completion.title'),\n body: t('feedback.completion.body'),\n onClose: () => {\n dispatch.hideCompletionForm(messageId, sentiment);\n dispatch.resetButtonState(messageId);\n },\n };\n },\n [state, dispatch, t],\n );\n\n const scrollToFeedbackForm = useCallback(\n (messageId: string, sentiment: Sentiment) => {\n setTimeout(() => {\n const el = document.getElementById(\n `feedback-${sentiment}-${messageId}`,\n );\n if (el) {\n el.scrollIntoView({ behavior: 'auto', block: 'start' });\n el.focus();\n }\n }, 0);\n },\n [],\n );\n\n const speakMessage = useCallback(\n (messageId: string, content: string) => {\n if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {\n window.speechSynthesis.cancel();\n\n if (\n currentSpeakingIdRef.current &&\n currentSpeakingIdRef.current !== messageId\n ) {\n dispatch.updateButtonState(currentSpeakingIdRef.current, {\n isSpeaking: false,\n });\n }\n }\n\n // Track the current SpeakingId\n currentSpeakingIdRef.current = messageId;\n\n const utterance = new SpeechSynthesisUtterance(content);\n\n utterance.onstart = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: true });\n };\n\n utterance.onend = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n currentSpeakingIdRef.current = null;\n };\n\n utterance.onerror = () => {\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n currentSpeakingIdRef.current = null;\n };\n\n window.speechSynthesis.speak(utterance);\n },\n [dispatch],\n );\n\n const cancelSpeaking = useCallback(\n (messageId?: string) => {\n window.speechSynthesis.cancel();\n\n currentSpeakingIdRef.current = null;\n\n if (messageId) {\n // Cancel just this one message\n dispatch.updateButtonState(messageId, { isSpeaking: false });\n } else {\n // Cancel all messages\n Object.keys(state).forEach(id => {\n if (state[id].buttonState?.isSpeaking) {\n dispatch.updateButtonState(id, { isSpeaking: false });\n }\n });\n }\n setTimeout(() => {\n const el = document.getElementById(`listen-${messageId}`);\n if (el) {\n el.blur();\n }\n }, 10);\n },\n [state, dispatch],\n );\n\n useEffect(() => {\n return () => {\n // Stop any ongoing speech when component unmounts\n cancelSpeaking();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [conversationId]);\n\n return useMemo(() => {\n return feedbackEnabled\n ? messages.map((message, index) => {\n if (\n message.role === 'user' ||\n (isStreaming && messages.length - 1 === index)\n )\n return message;\n\n const messageId = `${conversationId}-${message.role}-${index}`;\n const { sentiment, copied, isSpeaking } =\n state[messageId]?.buttonState || {};\n const showFeedbackForm = state[messageId]?.feedbackForm?.[sentiment];\n const userFeedbackForm =\n showFeedbackForm && feedbackForms(messageId)[sentiment];\n\n const userFeedbackComplete = getUserFeedbackComplete(\n messageId,\n sentiment,\n );\n\n const createSentimentAction = (selected: Sentiment) => ({\n isClicked: sentiment === selected,\n onClick: () => {\n if (sentiment !== selected) {\n dispatch.toggleFeedbackForm(messageId, selected);\n dispatch.updateButtonState(messageId, { sentiment: selected });\n scrollToFeedbackForm(messageId, sentiment);\n }\n },\n tooltipContent:\n selected === 'positive'\n ? t('feedback.tooltips.goodResponse')\n : t('feedback.tooltips.badResponse'),\n 'aria-expanded': sentiment === selected,\n 'aria-controls': showFeedbackForm\n ? `feedback-${sentiment}-${messageId}`\n : `feebback-completion-${sentiment}-${messageId}`,\n });\n\n const copyAction = {\n isClicked: copied,\n ref: copyButtonRef,\n tooltipContent: copied\n ? t('feedback.tooltips.copied')\n : t('feedback.tooltips.copy'),\n onClick: async () => {\n await window.navigator.clipboard.writeText(\n message.content as string,\n );\n dispatch.updateButtonState(messageId, {\n copied: true,\n });\n\n setTimeout(() => {\n dispatch.updateButtonState(messageId, { copied: false });\n copyButtonRef?.current?.blur();\n }, 1000);\n },\n };\n\n const listenAction = {\n isClicked: isSpeaking,\n ref: speakingButtonRef,\n id: `listen-${messageId}`,\n tooltipContent: isSpeaking\n ? t('feedback.tooltips.listening')\n : t('feedback.tooltips.listen'),\n onClick: () => {\n const isCurrentlySpeaking =\n state[messageId]?.buttonState?.isSpeaking;\n\n if (isCurrentlySpeaking) {\n // Stop if it's already speaking this message\n cancelSpeaking(messageId);\n } else {\n cancelSpeaking();\n speakMessage(messageId, message.content as string);\n }\n },\n };\n\n return {\n ...message,\n userFeedbackForm,\n userFeedbackComplete,\n actions: {\n positive: createSentimentAction('positive'),\n negative: createSentimentAction('negative'),\n copy: copyAction,\n listen: listenAction,\n },\n };\n })\n : messages;\n }, [\n t,\n conversationId,\n messages,\n state,\n dispatch,\n isStreaming,\n speakMessage,\n feedbackForms,\n cancelSpeaking,\n feedbackEnabled,\n getUserFeedbackComplete,\n scrollToFeedbackForm,\n ]);\n};\n"],"names":[],"mappings":"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"}