@red-hat-developer-hub/backstage-plugin-intelligent-assistant 3.0.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 (238) hide show
  1. package/CHANGELOG.md +681 -0
  2. package/README.md +378 -0
  3. package/config.d.ts +70 -0
  4. package/dist/alpha.d.ts +285 -0
  5. package/dist/alpha.esm.js +3 -0
  6. package/dist/alpha.esm.js.map +1 -0
  7. package/dist/api/LightspeedApiClient.esm.js +234 -0
  8. package/dist/api/LightspeedApiClient.esm.js.map +1 -0
  9. package/dist/api/NotebooksApiClient.esm.js +179 -0
  10. package/dist/api/NotebooksApiClient.esm.js.map +1 -0
  11. package/dist/api/api.esm.js +8 -0
  12. package/dist/api/api.esm.js.map +1 -0
  13. package/dist/api/notebooksApi.esm.js +10 -0
  14. package/dist/api/notebooksApi.esm.js.map +1 -0
  15. package/dist/components/Attachment.esm.js +108 -0
  16. package/dist/components/Attachment.esm.js.map +1 -0
  17. package/dist/components/AttachmentContext.esm.js +125 -0
  18. package/dist/components/AttachmentContext.esm.js.map +1 -0
  19. package/dist/components/CollapsedHistoryStrip.esm.js +87 -0
  20. package/dist/components/CollapsedHistoryStrip.esm.js.map +1 -0
  21. package/dist/components/DeleteModal.esm.js +92 -0
  22. package/dist/components/DeleteModal.esm.js.map +1 -0
  23. package/dist/components/FilePreview.esm.js +49 -0
  24. package/dist/components/FilePreview.esm.js.map +1 -0
  25. package/dist/components/LightSpeedChat.esm.js +1898 -0
  26. package/dist/components/LightSpeedChat.esm.js.map +1 -0
  27. package/dist/components/LightspeedChatBox.esm.js +252 -0
  28. package/dist/components/LightspeedChatBox.esm.js.map +1 -0
  29. package/dist/components/LightspeedChatBoxHeader.esm.js +210 -0
  30. package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -0
  31. package/dist/components/LightspeedChatContainer.esm.js +162 -0
  32. package/dist/components/LightspeedChatContainer.esm.js.map +1 -0
  33. package/dist/components/LightspeedChatModelsState.esm.js +198 -0
  34. package/dist/components/LightspeedChatModelsState.esm.js.map +1 -0
  35. package/dist/components/LightspeedDrawerContext.esm.js +15 -0
  36. package/dist/components/LightspeedDrawerContext.esm.js.map +1 -0
  37. package/dist/components/LightspeedDrawerProvider.esm.js +44 -0
  38. package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -0
  39. package/dist/components/LightspeedDrawerStateExposer.esm.js +34 -0
  40. package/dist/components/LightspeedDrawerStateExposer.esm.js.map +1 -0
  41. package/dist/components/LightspeedFAB.esm.js +6 -0
  42. package/dist/components/LightspeedFAB.esm.js.map +1 -0
  43. package/dist/components/LightspeedFABContent.esm.js +65 -0
  44. package/dist/components/LightspeedFABContent.esm.js.map +1 -0
  45. package/dist/components/LightspeedIcon.esm.js +46 -0
  46. package/dist/components/LightspeedIcon.esm.js.map +1 -0
  47. package/dist/components/LightspeedPage.esm.js +31 -0
  48. package/dist/components/LightspeedPage.esm.js.map +1 -0
  49. package/dist/components/McpServersSettings.esm.js +953 -0
  50. package/dist/components/McpServersSettings.esm.js.map +1 -0
  51. package/dist/components/McpSettingsIcon.esm.js +18 -0
  52. package/dist/components/McpSettingsIcon.esm.js.map +1 -0
  53. package/dist/components/MessageBarModelSelector.esm.js +92 -0
  54. package/dist/components/MessageBarModelSelector.esm.js.map +1 -0
  55. package/dist/components/PermissionRequiredIcon.esm.js +18 -0
  56. package/dist/components/PermissionRequiredIcon.esm.js.map +1 -0
  57. package/dist/components/PermissionRequiredState.esm.js +88 -0
  58. package/dist/components/PermissionRequiredState.esm.js.map +1 -0
  59. package/dist/components/RenameConversationModal.esm.js +157 -0
  60. package/dist/components/RenameConversationModal.esm.js.map +1 -0
  61. package/dist/components/Router.esm.js +35 -0
  62. package/dist/components/Router.esm.js.map +1 -0
  63. package/dist/components/SourcesChipModal.esm.js +124 -0
  64. package/dist/components/SourcesChipModal.esm.js.map +1 -0
  65. package/dist/components/ToolCallContent.esm.js +208 -0
  66. package/dist/components/ToolCallContent.esm.js.map +1 -0
  67. package/dist/components/Trans.esm.js +33 -0
  68. package/dist/components/Trans.esm.js.map +1 -0
  69. package/dist/components/notebooks/AddDocumentModal.esm.js +271 -0
  70. package/dist/components/notebooks/AddDocumentModal.esm.js.map +1 -0
  71. package/dist/components/notebooks/DeleteDocumentModal.esm.js +131 -0
  72. package/dist/components/notebooks/DeleteDocumentModal.esm.js.map +1 -0
  73. package/dist/components/notebooks/DeleteNotebookModal.esm.js +143 -0
  74. package/dist/components/notebooks/DeleteNotebookModal.esm.js.map +1 -0
  75. package/dist/components/notebooks/DocumentSidebar.esm.js +235 -0
  76. package/dist/components/notebooks/DocumentSidebar.esm.js.map +1 -0
  77. package/dist/components/notebooks/FileListItem.esm.js +94 -0
  78. package/dist/components/notebooks/FileListItem.esm.js.map +1 -0
  79. package/dist/components/notebooks/FileTypeIcon.esm.js +59 -0
  80. package/dist/components/notebooks/FileTypeIcon.esm.js.map +1 -0
  81. package/dist/components/notebooks/NotebookCard.esm.js +118 -0
  82. package/dist/components/notebooks/NotebookCard.esm.js.map +1 -0
  83. package/dist/components/notebooks/NotebookView.esm.js +681 -0
  84. package/dist/components/notebooks/NotebookView.esm.js.map +1 -0
  85. package/dist/components/notebooks/NotebooksTab.esm.js +71 -0
  86. package/dist/components/notebooks/NotebooksTab.esm.js.map +1 -0
  87. package/dist/components/notebooks/OverwriteConfirmModal.esm.js +142 -0
  88. package/dist/components/notebooks/OverwriteConfirmModal.esm.js.map +1 -0
  89. package/dist/components/notebooks/RenameNotebookModal.esm.js +187 -0
  90. package/dist/components/notebooks/RenameNotebookModal.esm.js.map +1 -0
  91. package/dist/components/notebooks/SidebarCollapseIcon.esm.js +50 -0
  92. package/dist/components/notebooks/SidebarCollapseIcon.esm.js.map +1 -0
  93. package/dist/components/notebooks/UploadResourceScreen.esm.js +79 -0
  94. package/dist/components/notebooks/UploadResourceScreen.esm.js.map +1 -0
  95. package/dist/const.esm.js +74 -0
  96. package/dist/const.esm.js.map +1 -0
  97. package/dist/dev.d.ts +2 -0
  98. package/dist/dev.esm.js +48 -0
  99. package/dist/dev.esm.js.map +1 -0
  100. package/dist/hooks/notebooks/useCreateNotebook.esm.js +19 -0
  101. package/dist/hooks/notebooks/useCreateNotebook.esm.js.map +1 -0
  102. package/dist/hooks/notebooks/useCreateNotebookMessage.esm.js +24 -0
  103. package/dist/hooks/notebooks/useCreateNotebookMessage.esm.js.map +1 -0
  104. package/dist/hooks/notebooks/useDeleteNotebook.esm.js +19 -0
  105. package/dist/hooks/notebooks/useDeleteNotebook.esm.js.map +1 -0
  106. package/dist/hooks/notebooks/useDocumentStatusPolling.esm.js +40 -0
  107. package/dist/hooks/notebooks/useDocumentStatusPolling.esm.js.map +1 -0
  108. package/dist/hooks/notebooks/useLightspeedNotebooksPermission.esm.js +15 -0
  109. package/dist/hooks/notebooks/useLightspeedNotebooksPermission.esm.js.map +1 -0
  110. package/dist/hooks/notebooks/useNotebookDocuments.esm.js +18 -0
  111. package/dist/hooks/notebooks/useNotebookDocuments.esm.js.map +1 -0
  112. package/dist/hooks/notebooks/useNotebookSession.esm.js +18 -0
  113. package/dist/hooks/notebooks/useNotebookSession.esm.js.map +1 -0
  114. package/dist/hooks/notebooks/useNotebookSessions.esm.js +18 -0
  115. package/dist/hooks/notebooks/useNotebookSessions.esm.js.map +1 -0
  116. package/dist/hooks/notebooks/useRenameNotebook.esm.js +19 -0
  117. package/dist/hooks/notebooks/useRenameNotebook.esm.js.map +1 -0
  118. package/dist/hooks/notebooks/useUploadDocument.esm.js +28 -0
  119. package/dist/hooks/notebooks/useUploadDocument.esm.js.map +1 -0
  120. package/dist/hooks/toolCallsCacheStore.esm.js +61 -0
  121. package/dist/hooks/toolCallsCacheStore.esm.js.map +1 -0
  122. package/dist/hooks/useAllModels.esm.js +20 -0
  123. package/dist/hooks/useAllModels.esm.js.map +1 -0
  124. package/dist/hooks/useAutoScroll.esm.js +78 -0
  125. package/dist/hooks/useAutoScroll.esm.js.map +1 -0
  126. package/dist/hooks/useBackstageUserIdentity.esm.js +14 -0
  127. package/dist/hooks/useBackstageUserIdentity.esm.js.map +1 -0
  128. package/dist/hooks/useBufferedMessages.esm.js +27 -0
  129. package/dist/hooks/useBufferedMessages.esm.js.map +1 -0
  130. package/dist/hooks/useCaptureFeedback.esm.js +21 -0
  131. package/dist/hooks/useCaptureFeedback.esm.js.map +1 -0
  132. package/dist/hooks/useConversationMessages.esm.js +544 -0
  133. package/dist/hooks/useConversationMessages.esm.js.map +1 -0
  134. package/dist/hooks/useConversations.esm.js +25 -0
  135. package/dist/hooks/useConversations.esm.js.map +1 -0
  136. package/dist/hooks/useCreateCoversationMessage.esm.js +33 -0
  137. package/dist/hooks/useCreateCoversationMessage.esm.js.map +1 -0
  138. package/dist/hooks/useDeleteConversation.esm.js +39 -0
  139. package/dist/hooks/useDeleteConversation.esm.js.map +1 -0
  140. package/dist/hooks/useDisplayModeSettings.esm.js +57 -0
  141. package/dist/hooks/useDisplayModeSettings.esm.js.map +1 -0
  142. package/dist/hooks/useFeedbackActions.esm.js +275 -0
  143. package/dist/hooks/useFeedbackActions.esm.js.map +1 -0
  144. package/dist/hooks/useFeedbackState.esm.js +93 -0
  145. package/dist/hooks/useFeedbackState.esm.js.map +1 -0
  146. package/dist/hooks/useFeedbackStatus.esm.js +14 -0
  147. package/dist/hooks/useFeedbackStatus.esm.js.map +1 -0
  148. package/dist/hooks/useIsMobile.esm.js +14 -0
  149. package/dist/hooks/useIsMobile.esm.js.map +1 -0
  150. package/dist/hooks/useLastOpenedConversation.esm.js +51 -0
  151. package/dist/hooks/useLastOpenedConversation.esm.js.map +1 -0
  152. package/dist/hooks/useLightspeedDeletePermission.esm.js +12 -0
  153. package/dist/hooks/useLightspeedDeletePermission.esm.js.map +1 -0
  154. package/dist/hooks/useLightspeedDrawerContext.esm.js +15 -0
  155. package/dist/hooks/useLightspeedDrawerContext.esm.js.map +1 -0
  156. package/dist/hooks/useLightspeedProviderState.esm.js +277 -0
  157. package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -0
  158. package/dist/hooks/useLightspeedUpdatePermission.esm.js +12 -0
  159. package/dist/hooks/useLightspeedUpdatePermission.esm.js.map +1 -0
  160. package/dist/hooks/useLightspeedViewPermission.esm.js +18 -0
  161. package/dist/hooks/useLightspeedViewPermission.esm.js.map +1 -0
  162. package/dist/hooks/useNotebookConversationIds.esm.js +19 -0
  163. package/dist/hooks/useNotebookConversationIds.esm.js.map +1 -0
  164. package/dist/hooks/useNotebookWelcomePrompts.esm.js +14 -0
  165. package/dist/hooks/useNotebookWelcomePrompts.esm.js.map +1 -0
  166. package/dist/hooks/usePinnedChatsSettings.esm.js +118 -0
  167. package/dist/hooks/usePinnedChatsSettings.esm.js.map +1 -0
  168. package/dist/hooks/useQuestionValidation.esm.js +16 -0
  169. package/dist/hooks/useQuestionValidation.esm.js.map +1 -0
  170. package/dist/hooks/useRenameConversation.esm.js +44 -0
  171. package/dist/hooks/useRenameConversation.esm.js.map +1 -0
  172. package/dist/hooks/useSortSettings.esm.js +60 -0
  173. package/dist/hooks/useSortSettings.esm.js.map +1 -0
  174. package/dist/hooks/useStopConversation.esm.js +18 -0
  175. package/dist/hooks/useStopConversation.esm.js.map +1 -0
  176. package/dist/hooks/useTranslation.esm.js +7 -0
  177. package/dist/hooks/useTranslation.esm.js.map +1 -0
  178. package/dist/hooks/useWelcomePrompts.esm.js +36 -0
  179. package/dist/hooks/useWelcomePrompts.esm.js.map +1 -0
  180. package/dist/images/bot-avatar-dark.svg +4 -0
  181. package/dist/images/bot-avatar.svg +4 -0
  182. package/dist/images/permission-required.svg +319 -0
  183. package/dist/images/user-avatar.svg +4 -0
  184. package/dist/index.d.ts +113 -0
  185. package/dist/index.esm.js +137 -0
  186. package/dist/index.esm.js.map +1 -0
  187. package/dist/intelligent-assistant-fab-module.d.ts +4 -0
  188. package/dist/intelligent-assistant-fab-module.esm.js +2 -0
  189. package/dist/intelligent-assistant-fab-module.esm.js.map +1 -0
  190. package/dist/intelligent-assistant-translations-module.d.ts +4 -0
  191. package/dist/intelligent-assistant-translations-module.esm.js +2 -0
  192. package/dist/intelligent-assistant-translations-module.esm.js.map +1 -0
  193. package/dist/legacy.d.ts +75 -0
  194. package/dist/legacy.esm.js +13 -0
  195. package/dist/legacy.esm.js.map +1 -0
  196. package/dist/plugin.esm.js +84 -0
  197. package/dist/plugin.esm.js.map +1 -0
  198. package/dist/routes.esm.js +23 -0
  199. package/dist/routes.esm.js.map +1 -0
  200. package/dist/translations/de.esm.js +278 -0
  201. package/dist/translations/de.esm.js.map +1 -0
  202. package/dist/translations/es.esm.js +278 -0
  203. package/dist/translations/es.esm.js.map +1 -0
  204. package/dist/translations/fr.esm.js +278 -0
  205. package/dist/translations/fr.esm.js.map +1 -0
  206. package/dist/translations/index.esm.js +16 -0
  207. package/dist/translations/index.esm.js.map +1 -0
  208. package/dist/translations/it.esm.js +278 -0
  209. package/dist/translations/it.esm.js.map +1 -0
  210. package/dist/translations/ja.esm.js +278 -0
  211. package/dist/translations/ja.esm.js.map +1 -0
  212. package/dist/translations/ref.esm.js +315 -0
  213. package/dist/translations/ref.esm.js.map +1 -0
  214. package/dist/types.esm.js +9 -0
  215. package/dist/types.esm.js.map +1 -0
  216. package/dist/utils/attachment-utils.esm.js +30 -0
  217. package/dist/utils/attachment-utils.esm.js.map +1 -0
  218. package/dist/utils/formatToolResponseForMarkdown.esm.js +98 -0
  219. package/dist/utils/formatToolResponseForMarkdown.esm.js.map +1 -0
  220. package/dist/utils/generateClassName.esm.js +12 -0
  221. package/dist/utils/generateClassName.esm.js.map +1 -0
  222. package/dist/utils/lightspeed-chatbox-utils.esm.js +232 -0
  223. package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -0
  224. package/dist/utils/notebook-upload-utils.esm.js +48 -0
  225. package/dist/utils/notebook-upload-utils.esm.js.map +1 -0
  226. package/dist/utils/notebooks-utils.esm.js +25 -0
  227. package/dist/utils/notebooks-utils.esm.js.map +1 -0
  228. package/dist/utils/prompt-utils.esm.js +17 -0
  229. package/dist/utils/prompt-utils.esm.js.map +1 -0
  230. package/dist/utils/queryClient.esm.js +6 -0
  231. package/dist/utils/queryClient.esm.js.map +1 -0
  232. package/dist/utils/reasoningParser.esm.js +33 -0
  233. package/dist/utils/reasoningParser.esm.js.map +1 -0
  234. package/dist/utils/toolCallMapper.esm.js +15 -0
  235. package/dist/utils/toolCallMapper.esm.js.map +1 -0
  236. package/dist/utils/user-utils.esm.js +7 -0
  237. package/dist/utils/user-utils.esm.js.map +1 -0
  238. package/package.json +182 -0
@@ -0,0 +1,25 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { lightspeedApiRef } from '../api/api.esm.js';
4
+
5
+ const useConversations = () => {
6
+ const lightspeedApi = useApi(lightspeedApiRef);
7
+ return useQuery({
8
+ queryKey: ["conversations"],
9
+ queryFn: async () => {
10
+ const response = await lightspeedApi.getConversations();
11
+ return response;
12
+ },
13
+ refetchInterval: (query) => {
14
+ const data = query.state.data;
15
+ if (!data?.length) return false;
16
+ const hasNullSummary = data.some((c) => !c.topic_summary);
17
+ return hasNullSummary ? 2e3 : false;
18
+ },
19
+ staleTime: 1e3 * 60 * 5
20
+ // 5 minutes
21
+ });
22
+ };
23
+
24
+ export { useConversations };
25
+ //# sourceMappingURL=useConversations.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConversations.esm.js","sources":["../../src/hooks/useConversations.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 { useApi } from '@backstage/core-plugin-api';\n\nimport { useQuery, type UseQueryResult } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { ConversationList } from '../types';\n\n// Fetch all conversations\nexport const useConversations = (): UseQueryResult<ConversationList, Error> => {\n const lightspeedApi = useApi(lightspeedApiRef);\n return useQuery({\n queryKey: ['conversations'],\n queryFn: async () => {\n const response = await lightspeedApi.getConversations();\n return response;\n },\n refetchInterval: query => {\n const data = query.state.data;\n if (!data?.length) return false;\n const hasNullSummary = data.some(c => !c.topic_summary);\n return hasNullSummary ? 2000 : false;\n },\n staleTime: 1000 * 60 * 5, // 5 minutes\n });\n};\n"],"names":[],"mappings":";;;;AAwBO,MAAM,mBAAmB,MAA+C;AAC7E,EAAA,MAAM,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAC7C,EAAA,OAAO,QAAA,CAAS;AAAA,IACd,QAAA,EAAU,CAAC,eAAe,CAAA;AAAA,IAC1B,SAAS,YAAY;AACnB,MAAA,MAAM,QAAA,GAAW,MAAM,aAAA,CAAc,gBAAA,EAAiB;AACtD,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IACA,iBAAiB,CAAA,KAAA,KAAS;AACxB,MAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,IAAA;AACzB,MAAA,IAAI,CAAC,IAAA,EAAM,MAAA,EAAQ,OAAO,KAAA;AAC1B,MAAA,MAAM,iBAAiB,IAAA,CAAK,IAAA,CAAK,CAAA,CAAA,KAAK,CAAC,EAAE,aAAa,CAAA;AACtD,MAAA,OAAO,iBAAiB,GAAA,GAAO,KAAA;AAAA,IACjC,CAAA;AAAA,IACA,SAAA,EAAW,MAAO,EAAA,GAAK;AAAA;AAAA,GACxB,CAAA;AACH;;;;"}
@@ -0,0 +1,33 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import { useMutation } from '@tanstack/react-query';
3
+ import { lightspeedApiRef } from '../api/api.esm.js';
4
+
5
+ const useCreateConversationMessage = () => {
6
+ const lightspeedApi = useApi(lightspeedApiRef);
7
+ return useMutation({
8
+ mutationFn: async ({
9
+ prompt,
10
+ selectedModel,
11
+ selectedProvider,
12
+ currentConversation,
13
+ attachments
14
+ }) => {
15
+ if (!currentConversation) {
16
+ throw new Error("Failed to generate AI response");
17
+ }
18
+ return await lightspeedApi.createMessage(
19
+ `${prompt}`,
20
+ selectedModel,
21
+ selectedProvider,
22
+ currentConversation,
23
+ attachments
24
+ );
25
+ },
26
+ onError: (error) => {
27
+ console.warn(error);
28
+ }
29
+ });
30
+ };
31
+
32
+ export { useCreateConversationMessage };
33
+ //# sourceMappingURL=useCreateCoversationMessage.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCreateCoversationMessage.esm.js","sources":["../../src/hooks/useCreateCoversationMessage.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 { useApi } from '@backstage/core-plugin-api';\n\nimport { useMutation, type UseMutationResult } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { Attachment } from '../types';\n\nexport type CreateMessageVariables = {\n prompt: string;\n selectedModel: string;\n selectedProvider: string;\n currentConversation: string;\n attachments: Attachment[];\n};\n\nexport const useCreateConversationMessage = (): UseMutationResult<\n ReadableStreamDefaultReader<Uint8Array>,\n Error,\n CreateMessageVariables\n> => {\n const lightspeedApi = useApi(lightspeedApiRef);\n\n return useMutation({\n mutationFn: async ({\n prompt,\n selectedModel,\n selectedProvider,\n currentConversation,\n attachments,\n }: CreateMessageVariables) => {\n if (!currentConversation) {\n throw new Error('Failed to generate AI response');\n }\n\n return await lightspeedApi.createMessage(\n `${prompt}`,\n selectedModel,\n selectedProvider,\n currentConversation,\n attachments,\n );\n },\n onError: error => {\n // eslint-disable-next-line\n console.warn(error);\n },\n });\n};\n"],"names":[],"mappings":";;;;AA+BO,MAAM,+BAA+B,MAIvC;AACH,EAAA,MAAM,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAE7C,EAAA,OAAO,WAAA,CAAY;AAAA,IACjB,YAAY,OAAO;AAAA,MACjB,MAAA;AAAA,MACA,aAAA;AAAA,MACA,gBAAA;AAAA,MACA,mBAAA;AAAA,MACA;AAAA,KACF,KAA8B;AAC5B,MAAA,IAAI,CAAC,mBAAA,EAAqB;AACxB,QAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,MAClD;AAEA,MAAA,OAAO,MAAM,aAAA,CAAc,aAAA;AAAA,QACzB,GAAG,MAAM,CAAA,CAAA;AAAA,QACT,aAAA;AAAA,QACA,gBAAA;AAAA,QACA,mBAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,CAAA;AAAA,IACA,SAAS,CAAA,KAAA,KAAS;AAEhB,MAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,IACpB;AAAA,GACD,CAAA;AACH;;;;"}
@@ -0,0 +1,39 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import { useQueryClient, useMutation } from '@tanstack/react-query';
3
+ import { lightspeedApiRef } from '../api/api.esm.js';
4
+ import { clearSharedToolCallsCacheForConversation } from './toolCallsCacheStore.esm.js';
5
+
6
+ const useDeleteConversation = () => {
7
+ const lightspeedApi = useApi(lightspeedApiRef);
8
+ const queryClient = useQueryClient();
9
+ return useMutation({
10
+ mutationFn: async (props) => {
11
+ await lightspeedApi.deleteConversation(props.conversation_id);
12
+ },
13
+ onMutate: async (props) => {
14
+ await queryClient.cancelQueries({ queryKey: ["conversations"] });
15
+ const previousConversations = queryClient.getQueryData(["conversations"]);
16
+ queryClient.setQueryData(
17
+ ["conversations"],
18
+ (old) => old.filter((c) => c.conversation_id !== props.conversation_id)
19
+ );
20
+ return { previousConversations };
21
+ },
22
+ onSuccess: (_, props) => {
23
+ clearSharedToolCallsCacheForConversation(props.conversation_id);
24
+ if (props.invalidateCache) {
25
+ queryClient.invalidateQueries({ queryKey: ["conversations"] });
26
+ }
27
+ },
28
+ onError: (_, __, context) => {
29
+ queryClient.setQueryData(
30
+ ["conversations"],
31
+ context?.previousConversations
32
+ );
33
+ return { success: false };
34
+ }
35
+ });
36
+ };
37
+
38
+ export { useDeleteConversation };
39
+ //# sourceMappingURL=useDeleteConversation.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDeleteConversation.esm.js","sources":["../../src/hooks/useDeleteConversation.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 { useApi } from '@backstage/core-plugin-api';\n\nimport {\n useMutation,\n useQueryClient,\n type UseMutationResult,\n} from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\nimport { ConversationList } from '../types';\nimport { clearSharedToolCallsCacheForConversation } from './toolCallsCacheStore';\n\ntype DeleteVariables = {\n conversation_id: string;\n invalidateCache?: boolean;\n};\n\ntype DeleteContext = {\n previousConversations?: ConversationList;\n};\n\nexport const useDeleteConversation = (): UseMutationResult<\n void,\n Error,\n DeleteVariables,\n DeleteContext\n> => {\n const lightspeedApi = useApi(lightspeedApiRef);\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: async (props: {\n conversation_id: string;\n invalidateCache?: boolean;\n }) => {\n await lightspeedApi.deleteConversation(props.conversation_id);\n },\n onMutate: async props => {\n await queryClient.cancelQueries({ queryKey: ['conversations'] });\n\n const previousConversations: ConversationList | undefined =\n queryClient.getQueryData(['conversations']);\n\n queryClient.setQueryData(['conversations'], (old: ConversationList) =>\n old.filter(c => c.conversation_id !== props.conversation_id),\n );\n\n return { previousConversations };\n },\n onSuccess: (_, props) => {\n clearSharedToolCallsCacheForConversation(props.conversation_id);\n if (props.invalidateCache) {\n queryClient.invalidateQueries({ queryKey: ['conversations'] });\n }\n },\n onError: (_, __, context) => {\n queryClient.setQueryData(\n ['conversations'],\n context?.previousConversations,\n );\n return { success: false };\n },\n });\n};\n"],"names":[],"mappings":";;;;;AAqCO,MAAM,wBAAwB,MAKhC;AACH,EAAA,MAAM,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAC7C,EAAA,MAAM,cAAc,cAAA,EAAe;AAEnC,EAAA,OAAO,WAAA,CAAY;AAAA,IACjB,UAAA,EAAY,OAAO,KAAA,KAGb;AACJ,MAAA,MAAM,aAAA,CAAc,kBAAA,CAAmB,KAAA,CAAM,eAAe,CAAA;AAAA,IAC9D,CAAA;AAAA,IACA,QAAA,EAAU,OAAM,KAAA,KAAS;AACvB,MAAA,MAAM,YAAY,aAAA,CAAc,EAAE,UAAU,CAAC,eAAe,GAAG,CAAA;AAE/D,MAAA,MAAM,qBAAA,GACJ,WAAA,CAAY,YAAA,CAAa,CAAC,eAAe,CAAC,CAAA;AAE5C,MAAA,WAAA,CAAY,YAAA;AAAA,QAAa,CAAC,eAAe,CAAA;AAAA,QAAG,CAAC,QAC3C,GAAA,CAAI,MAAA,CAAO,OAAK,CAAA,CAAE,eAAA,KAAoB,MAAM,eAAe;AAAA,OAC7D;AAEA,MAAA,OAAO,EAAE,qBAAA,EAAsB;AAAA,IACjC,CAAA;AAAA,IACA,SAAA,EAAW,CAAC,CAAA,EAAG,KAAA,KAAU;AACvB,MAAA,wCAAA,CAAyC,MAAM,eAAe,CAAA;AAC9D,MAAA,IAAI,MAAM,eAAA,EAAiB;AACzB,QAAA,WAAA,CAAY,kBAAkB,EAAE,QAAA,EAAU,CAAC,eAAe,GAAG,CAAA;AAAA,MAC/D;AAAA,IACF,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,CAAA,EAAG,EAAA,EAAI,OAAA,KAAY;AAC3B,MAAA,WAAA,CAAY,YAAA;AAAA,QACV,CAAC,eAAe,CAAA;AAAA,QAChB,OAAA,EAAS;AAAA,OACX;AACA,MAAA,OAAO,EAAE,SAAS,KAAA,EAAM;AAAA,IAC1B;AAAA,GACD,CAAA;AACH;;;;"}
@@ -0,0 +1,57 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { useApi, storageApiRef } from '@backstage/core-plugin-api';
3
+ import { ChatbotDisplayMode } from '@patternfly/chatbot';
4
+ import { isGuestUser } from '../utils/user-utils.esm.js';
5
+
6
+ const BUCKET_NAME = "lightspeed";
7
+ const DISPLAY_MODE_KEY = "displayMode";
8
+ const useDisplayModeSettings = (user, defaultMode = ChatbotDisplayMode.default) => {
9
+ const storageApi = useApi(storageApiRef);
10
+ const bucket = storageApi.forBucket(BUCKET_NAME);
11
+ const [displayModeState, setDisplayModeState] = useState(defaultMode);
12
+ const shouldPersist = !isGuestUser(user);
13
+ useEffect(() => {
14
+ if (!user || isGuestUser(user)) {
15
+ setDisplayModeState(defaultMode);
16
+ return void 0;
17
+ }
18
+ try {
19
+ const modeSnapshot = bucket.snapshot(DISPLAY_MODE_KEY);
20
+ setDisplayModeState(modeSnapshot.value ?? defaultMode);
21
+ } catch (error) {
22
+ console.error("Error reading display mode from storage:", error);
23
+ }
24
+ const modeSubscription = bucket.observe$(DISPLAY_MODE_KEY).subscribe({
25
+ next: (snapshot) => {
26
+ setDisplayModeState(snapshot.value ?? defaultMode);
27
+ },
28
+ error: (error) => {
29
+ console.error("Error observing displayMode:", error);
30
+ }
31
+ });
32
+ return () => {
33
+ modeSubscription.unsubscribe();
34
+ };
35
+ }, [bucket, user, defaultMode]);
36
+ const setDisplayMode = useCallback(
37
+ (mode) => {
38
+ if (!user) return;
39
+ setDisplayModeState(mode);
40
+ if (shouldPersist) {
41
+ try {
42
+ bucket.set(DISPLAY_MODE_KEY, mode);
43
+ } catch (error) {
44
+ console.error("Error saving display mode:", error);
45
+ }
46
+ }
47
+ },
48
+ [bucket, user, shouldPersist]
49
+ );
50
+ return {
51
+ displayMode: displayModeState,
52
+ setDisplayMode
53
+ };
54
+ };
55
+
56
+ export { useDisplayModeSettings };
57
+ //# sourceMappingURL=useDisplayModeSettings.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDisplayModeSettings.esm.js","sources":["../../src/hooks/useDisplayModeSettings.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 { useCallback, useEffect, useState } from 'react';\n\nimport { storageApiRef, useApi } from '@backstage/core-plugin-api';\n\nimport { ChatbotDisplayMode } from '@patternfly/chatbot';\n\nimport { isGuestUser } from '../utils/user-utils';\n\nconst BUCKET_NAME = 'lightspeed';\nconst DISPLAY_MODE_KEY = 'displayMode';\n\ntype UseDisplayModeSettingsReturn = {\n displayMode: ChatbotDisplayMode;\n setDisplayMode: (mode: ChatbotDisplayMode) => void;\n};\n\n/**\n * Hook to manage display mode settings with persistence using Backstage StorageApi.\n *\n * @param user - The user entity ref (e.g., \"user:default/john\")\n * @param defaultMode - The default display mode to use if no preference is stored\n * @returns Object containing display mode state and management function\n */\nexport const useDisplayModeSettings = (\n user: string | undefined,\n defaultMode: ChatbotDisplayMode = ChatbotDisplayMode.default,\n): UseDisplayModeSettingsReturn => {\n const storageApi = useApi(storageApiRef);\n const bucket = storageApi.forBucket(BUCKET_NAME);\n\n const [displayModeState, setDisplayModeState] =\n useState<ChatbotDisplayMode>(defaultMode);\n\n const shouldPersist = !isGuestUser(user);\n\n useEffect(() => {\n if (!user || isGuestUser(user)) {\n setDisplayModeState(defaultMode);\n return undefined;\n }\n\n try {\n const modeSnapshot =\n bucket.snapshot<ChatbotDisplayMode>(DISPLAY_MODE_KEY);\n setDisplayModeState(modeSnapshot.value ?? defaultMode);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Error reading display mode from storage:', error);\n }\n\n const modeSubscription = bucket\n .observe$<ChatbotDisplayMode>(DISPLAY_MODE_KEY)\n .subscribe({\n next: snapshot => {\n setDisplayModeState(snapshot.value ?? defaultMode);\n },\n error: error => {\n // eslint-disable-next-line no-console\n console.error('Error observing displayMode:', error);\n },\n });\n\n return () => {\n modeSubscription.unsubscribe();\n };\n }, [bucket, user, defaultMode]);\n\n const setDisplayMode = useCallback(\n (mode: ChatbotDisplayMode) => {\n if (!user) return;\n\n setDisplayModeState(mode);\n\n if (shouldPersist) {\n try {\n bucket.set(DISPLAY_MODE_KEY, mode);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Error saving display mode:', error);\n }\n }\n },\n [bucket, user, shouldPersist],\n );\n\n return {\n displayMode: displayModeState,\n setDisplayMode,\n };\n};\n"],"names":[],"mappings":";;;;;AAuBA,MAAM,WAAA,GAAc,YAAA;AACpB,MAAM,gBAAA,GAAmB,aAAA;AAclB,MAAM,sBAAA,GAAyB,CACpC,IAAA,EACA,WAAA,GAAkC,mBAAmB,OAAA,KACpB;AACjC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,MAAA,GAAS,UAAA,CAAW,SAAA,CAAU,WAAW,CAAA;AAE/C,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAC1C,SAA6B,WAAW,CAAA;AAE1C,EAAA,MAAM,aAAA,GAAgB,CAAC,WAAA,CAAY,IAAI,CAAA;AAEvC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,IAAQ,WAAA,CAAY,IAAI,CAAA,EAAG;AAC9B,MAAA,mBAAA,CAAoB,WAAW,CAAA;AAC/B,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,YAAA,GACJ,MAAA,CAAO,QAAA,CAA6B,gBAAgB,CAAA;AACtD,MAAA,mBAAA,CAAoB,YAAA,CAAa,SAAS,WAAW,CAAA;AAAA,IACvD,SAAS,KAAA,EAAO;AAEd,MAAA,OAAA,CAAQ,KAAA,CAAM,4CAA4C,KAAK,CAAA;AAAA,IACjE;AAEA,IAAA,MAAM,gBAAA,GAAmB,MAAA,CACtB,QAAA,CAA6B,gBAAgB,EAC7C,SAAA,CAAU;AAAA,MACT,MAAM,CAAA,QAAA,KAAY;AAChB,QAAA,mBAAA,CAAoB,QAAA,CAAS,SAAS,WAAW,CAAA;AAAA,MACnD,CAAA;AAAA,MACA,OAAO,CAAA,KAAA,KAAS;AAEd,QAAA,OAAA,CAAQ,KAAA,CAAM,gCAAgC,KAAK,CAAA;AAAA,MACrD;AAAA,KACD,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,gBAAA,CAAiB,WAAA,EAAY;AAAA,IAC/B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,IAAA,EAAM,WAAW,CAAC,CAAA;AAE9B,EAAA,MAAM,cAAA,GAAiB,WAAA;AAAA,IACrB,CAAC,IAAA,KAA6B;AAC5B,MAAA,IAAI,CAAC,IAAA,EAAM;AAEX,MAAA,mBAAA,CAAoB,IAAI,CAAA;AAExB,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,IAAI;AACF,UAAA,MAAA,CAAO,GAAA,CAAI,kBAAkB,IAAI,CAAA;AAAA,QACnC,SAAS,KAAA,EAAO;AAEd,UAAA,OAAA,CAAQ,KAAA,CAAM,8BAA8B,KAAK,CAAA;AAAA,QACnD;AAAA,MACF;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,IAAA,EAAM,aAAa;AAAA,GAC9B;AAEA,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,gBAAA;AAAA,IACb;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,275 @@
1
+ import { useRef, useMemo, useCallback, useEffect } from 'react';
2
+ import { useCaptureFeedback } from './useCaptureFeedback.esm.js';
3
+ import { useFeedbackState } from './useFeedbackState.esm.js';
4
+ import { useFeedbackStatus } from './useFeedbackStatus.esm.js';
5
+ import { useTranslation } from './useTranslation.esm.js';
6
+
7
+ const useFeedbackActions = (messages, conversationId, isStreaming) => {
8
+ const copyButtonRef = useRef(null);
9
+ const speakingButtonRef = useRef(null);
10
+ const currentSpeakingIdRef = useRef(null);
11
+ const { data: feedbackEnabled } = useFeedbackStatus();
12
+ const { state, ...dispatch } = useFeedbackState();
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
+ );
39
+ const getFeedbackForm = useCallback(
40
+ (messageId, sentiment) => ({
41
+ key: `feedback-${sentiment}`,
42
+ id: `feedback-${sentiment}-${messageId}`,
43
+ hasTextArea: true,
44
+ title: t("feedback.form.title"),
45
+ textAreaPlaceholder: t("feedback.form.textAreaPlaceholder"),
46
+ submitWord: t("feedback.form.submitWord"),
47
+ quickResponses: createQuickResponses[sentiment],
48
+ onSubmit: (quickResponse = "0", additionalFeedback) => {
49
+ const quickIndex = Number(quickResponse) - 1;
50
+ const quickText = quickResponse !== "0" ? createQuickResponses[sentiment]?.[quickIndex]?.content : void 0;
51
+ let user_feedback = "";
52
+ if (quickText && additionalFeedback) {
53
+ user_feedback = `${quickText}
54
+ Additional feedback: ${additionalFeedback}`;
55
+ } else if (quickText) {
56
+ user_feedback = quickText;
57
+ } else if (additionalFeedback) {
58
+ user_feedback = additionalFeedback;
59
+ }
60
+ const botAnswerId = Number(messageId.split("-").pop());
61
+ const userQuestionId = botAnswerId - 1;
62
+ const payload = {
63
+ conversation_id: conversationId,
64
+ user_feedback,
65
+ sentiment: sentiment === "positive" ? 1 : -1,
66
+ user_question: messages[userQuestionId].content || "",
67
+ llm_response: messages[botAnswerId].content || ""
68
+ };
69
+ captureFeedback(payload).then(() => {
70
+ dispatch.hideFeedbackForm(messageId, sentiment);
71
+ dispatch.showCompletionForm(messageId, sentiment);
72
+ dispatch.updateButtonState(messageId, {
73
+ sentiment,
74
+ feedbackSubmitted: true
75
+ });
76
+ setTimeout(() => {
77
+ dispatch.hideCompletionForm(messageId, sentiment);
78
+ }, 1e3);
79
+ });
80
+ },
81
+ // eslint-disable-next-line no-console
82
+ onClose: () => {
83
+ dispatch.hideFeedbackForm(messageId, sentiment);
84
+ dispatch.resetButtonState(messageId);
85
+ dispatch.updateButtonState(messageId, { sentiment: void 0 });
86
+ },
87
+ focusOnLoad: true
88
+ }),
89
+ [
90
+ captureFeedback,
91
+ conversationId,
92
+ dispatch,
93
+ messages,
94
+ createQuickResponses,
95
+ t
96
+ ]
97
+ );
98
+ const feedbackForms = useCallback(
99
+ (messageId) => ({
100
+ positive: getFeedbackForm(messageId, "positive"),
101
+ negative: getFeedbackForm(messageId, "negative")
102
+ }),
103
+ [getFeedbackForm]
104
+ );
105
+ const getUserFeedbackComplete = useCallback(
106
+ (messageId, sentiment) => {
107
+ const isVisible = state[messageId]?.completionForm?.[sentiment];
108
+ if (!isVisible) return void 0;
109
+ return {
110
+ id: `feebback-completion-${sentiment}-${messageId}`,
111
+ title: t("feedback.completion.title"),
112
+ body: t("feedback.completion.body"),
113
+ onClose: () => {
114
+ dispatch.hideCompletionForm(messageId, sentiment);
115
+ dispatch.resetButtonState(messageId);
116
+ }
117
+ };
118
+ },
119
+ [state, dispatch, t]
120
+ );
121
+ const scrollToFeedbackForm = useCallback(
122
+ (messageId, sentiment) => {
123
+ setTimeout(() => {
124
+ const el = document.getElementById(
125
+ `feedback-${sentiment}-${messageId}`
126
+ );
127
+ if (el) {
128
+ el.scrollIntoView({ behavior: "auto", block: "start" });
129
+ el.focus();
130
+ }
131
+ }, 0);
132
+ },
133
+ []
134
+ );
135
+ const speakMessage = useCallback(
136
+ (messageId, content) => {
137
+ if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {
138
+ window.speechSynthesis.cancel();
139
+ if (currentSpeakingIdRef.current && currentSpeakingIdRef.current !== messageId) {
140
+ dispatch.updateButtonState(currentSpeakingIdRef.current, {
141
+ isSpeaking: false
142
+ });
143
+ }
144
+ }
145
+ currentSpeakingIdRef.current = messageId;
146
+ const utterance = new SpeechSynthesisUtterance(content);
147
+ utterance.onstart = () => {
148
+ dispatch.updateButtonState(messageId, { isSpeaking: true });
149
+ };
150
+ utterance.onend = () => {
151
+ dispatch.updateButtonState(messageId, { isSpeaking: false });
152
+ currentSpeakingIdRef.current = null;
153
+ };
154
+ utterance.onerror = () => {
155
+ dispatch.updateButtonState(messageId, { isSpeaking: false });
156
+ currentSpeakingIdRef.current = null;
157
+ };
158
+ window.speechSynthesis.speak(utterance);
159
+ },
160
+ [dispatch]
161
+ );
162
+ const cancelSpeaking = useCallback(
163
+ (messageId) => {
164
+ window.speechSynthesis.cancel();
165
+ currentSpeakingIdRef.current = null;
166
+ if (messageId) {
167
+ dispatch.updateButtonState(messageId, { isSpeaking: false });
168
+ } else {
169
+ Object.keys(state).forEach((id) => {
170
+ if (state[id].buttonState?.isSpeaking) {
171
+ dispatch.updateButtonState(id, { isSpeaking: false });
172
+ }
173
+ });
174
+ }
175
+ setTimeout(() => {
176
+ const el = document.getElementById(`listen-${messageId}`);
177
+ if (el) {
178
+ el.blur();
179
+ }
180
+ }, 10);
181
+ },
182
+ [state, dispatch]
183
+ );
184
+ useEffect(() => {
185
+ return () => {
186
+ cancelSpeaking();
187
+ };
188
+ }, [conversationId]);
189
+ return useMemo(() => {
190
+ return feedbackEnabled ? messages.map((message, index) => {
191
+ if (message.role === "user" || isStreaming && messages.length - 1 === index)
192
+ return message;
193
+ const messageId = `${conversationId}-${message.role}-${index}`;
194
+ const { sentiment, copied, isSpeaking } = state[messageId]?.buttonState || {};
195
+ const showFeedbackForm = state[messageId]?.feedbackForm?.[sentiment];
196
+ const userFeedbackForm = showFeedbackForm && feedbackForms(messageId)[sentiment];
197
+ const userFeedbackComplete = getUserFeedbackComplete(
198
+ messageId,
199
+ sentiment
200
+ );
201
+ const createSentimentAction = (selected) => ({
202
+ isClicked: sentiment === selected,
203
+ onClick: () => {
204
+ if (sentiment !== selected) {
205
+ dispatch.toggleFeedbackForm(messageId, selected);
206
+ dispatch.updateButtonState(messageId, { sentiment: selected });
207
+ scrollToFeedbackForm(messageId, sentiment);
208
+ }
209
+ },
210
+ tooltipContent: selected === "positive" ? t("feedback.tooltips.goodResponse") : t("feedback.tooltips.badResponse"),
211
+ "aria-expanded": sentiment === selected,
212
+ "aria-controls": showFeedbackForm ? `feedback-${sentiment}-${messageId}` : `feebback-completion-${sentiment}-${messageId}`
213
+ });
214
+ const copyAction = {
215
+ isClicked: copied,
216
+ ref: copyButtonRef,
217
+ tooltipContent: copied ? t("feedback.tooltips.copied") : t("feedback.tooltips.copy"),
218
+ onClick: async () => {
219
+ await window.navigator.clipboard.writeText(
220
+ message.content
221
+ );
222
+ dispatch.updateButtonState(messageId, {
223
+ copied: true
224
+ });
225
+ setTimeout(() => {
226
+ dispatch.updateButtonState(messageId, { copied: false });
227
+ copyButtonRef?.current?.blur();
228
+ }, 1e3);
229
+ }
230
+ };
231
+ const listenAction = {
232
+ isClicked: isSpeaking,
233
+ ref: speakingButtonRef,
234
+ id: `listen-${messageId}`,
235
+ tooltipContent: isSpeaking ? t("feedback.tooltips.listening") : t("feedback.tooltips.listen"),
236
+ onClick: () => {
237
+ const isCurrentlySpeaking = state[messageId]?.buttonState?.isSpeaking;
238
+ if (isCurrentlySpeaking) {
239
+ cancelSpeaking(messageId);
240
+ } else {
241
+ cancelSpeaking();
242
+ speakMessage(messageId, message.content);
243
+ }
244
+ }
245
+ };
246
+ return {
247
+ ...message,
248
+ userFeedbackForm,
249
+ userFeedbackComplete,
250
+ actions: {
251
+ positive: createSentimentAction("positive"),
252
+ negative: createSentimentAction("negative"),
253
+ copy: copyAction,
254
+ listen: listenAction
255
+ }
256
+ };
257
+ }) : messages;
258
+ }, [
259
+ t,
260
+ conversationId,
261
+ messages,
262
+ state,
263
+ dispatch,
264
+ isStreaming,
265
+ speakMessage,
266
+ feedbackForms,
267
+ cancelSpeaking,
268
+ feedbackEnabled,
269
+ getUserFeedbackComplete,
270
+ scrollToFeedbackForm
271
+ ]);
272
+ };
273
+
274
+ export { useFeedbackActions };
275
+ //# sourceMappingURL=useFeedbackActions.esm.js.map
@@ -0,0 +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 { 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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"}
@@ -0,0 +1,93 @@
1
+ import { useReducer } from 'react';
2
+
3
+ function reducer(state, action) {
4
+ const entry = state[action.messageId] ?? {
5
+ feedbackForm: { positive: false, negative: false },
6
+ completionForm: { positive: false, negative: false },
7
+ buttonState: {}
8
+ };
9
+ switch (action.type) {
10
+ case "SHOW_FEEDBACK_FORM":
11
+ return {
12
+ ...state,
13
+ [action.messageId]: {
14
+ ...entry,
15
+ feedbackForm: { ...entry.feedbackForm, [action.sentiment]: true }
16
+ }
17
+ };
18
+ case "HIDE_FEEDBACK_FORM":
19
+ return {
20
+ ...state,
21
+ [action.messageId]: {
22
+ ...entry,
23
+ feedbackForm: { ...entry.feedbackForm, [action.sentiment]: false }
24
+ }
25
+ };
26
+ case "SHOW_COMPLETION_FORM":
27
+ return {
28
+ ...state,
29
+ [action.messageId]: {
30
+ ...entry,
31
+ completionForm: { ...entry.completionForm, [action.sentiment]: true }
32
+ }
33
+ };
34
+ case "HIDE_COMPLETION_FORM":
35
+ return {
36
+ ...state,
37
+ [action.messageId]: {
38
+ ...entry,
39
+ completionForm: {
40
+ ...entry.completionForm,
41
+ [action.sentiment]: false
42
+ }
43
+ }
44
+ };
45
+ case "UPDATE_BUTTON_STATE":
46
+ return {
47
+ ...state,
48
+ [action.messageId]: {
49
+ ...entry,
50
+ buttonState: { ...entry.buttonState, ...action.payload }
51
+ }
52
+ };
53
+ case "RESET_BUTTON_STATE": {
54
+ const { [action.messageId]: _, ...rest } = state;
55
+ return rest;
56
+ }
57
+ default:
58
+ return state;
59
+ }
60
+ }
61
+ function useFeedbackState() {
62
+ const [state, dispatch] = useReducer(reducer, {});
63
+ const showFeedbackForm = (messageId, sentiment) => dispatch({ type: "SHOW_FEEDBACK_FORM", messageId, sentiment });
64
+ const hideFeedbackForm = (messageId, sentiment) => dispatch({ type: "HIDE_FEEDBACK_FORM", messageId, sentiment });
65
+ const showCompletionForm = (messageId, sentiment) => dispatch({ type: "SHOW_COMPLETION_FORM", messageId, sentiment });
66
+ const hideCompletionForm = (messageId, sentiment) => dispatch({ type: "HIDE_COMPLETION_FORM", messageId, sentiment });
67
+ const updateButtonState = (messageId, payload) => dispatch({ type: "UPDATE_BUTTON_STATE", messageId, payload });
68
+ const resetButtonState = (messageId) => dispatch({ type: "RESET_BUTTON_STATE", messageId });
69
+ const getFeedbackState = (messageId) => state[messageId] ?? {
70
+ feedbackForm: { positive: false, negative: false },
71
+ completionForm: { positive: false, negative: false },
72
+ buttonState: { sentiment: "positive", copied: false }
73
+ };
74
+ const toggleFeedbackForm = (messageId, sentimentToShow) => {
75
+ const opposite = sentimentToShow === "positive" ? "negative" : "positive";
76
+ hideFeedbackForm(messageId, opposite);
77
+ showFeedbackForm(messageId, sentimentToShow);
78
+ };
79
+ return {
80
+ state,
81
+ getFeedbackState,
82
+ showFeedbackForm,
83
+ hideFeedbackForm,
84
+ toggleFeedbackForm,
85
+ showCompletionForm,
86
+ hideCompletionForm,
87
+ updateButtonState,
88
+ resetButtonState
89
+ };
90
+ }
91
+
92
+ export { useFeedbackState };
93
+ //# sourceMappingURL=useFeedbackState.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFeedbackState.esm.js","sources":["../../src/hooks/useFeedbackState.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 { useReducer } from 'react';\n\nexport type Sentiment = 'positive' | 'negative';\n\ntype FeedbackButtonState = {\n sentiment: Sentiment;\n copied: boolean;\n isSpeaking: boolean;\n feedbackSubmitted?: boolean;\n};\n\ntype MessageState = {\n feedbackForm: Record<Sentiment, boolean>;\n completionForm: Record<Sentiment, boolean>;\n buttonState: FeedbackButtonState;\n};\n\ntype State = Record<string, MessageState>;\n\ntype Action =\n | { type: 'SHOW_FEEDBACK_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'HIDE_FEEDBACK_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'SHOW_COMPLETION_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'HIDE_COMPLETION_FORM'; messageId: string; sentiment: Sentiment }\n | { type: 'RESET_BUTTON_STATE'; messageId: string }\n | {\n type: 'UPDATE_BUTTON_STATE';\n messageId: string;\n payload: Partial<FeedbackButtonState>;\n };\n\nfunction reducer(state: State, action: Action): State {\n const entry = state[action.messageId] ?? {\n feedbackForm: { positive: false, negative: false },\n completionForm: { positive: false, negative: false },\n buttonState: {},\n };\n\n switch (action.type) {\n case 'SHOW_FEEDBACK_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n feedbackForm: { ...entry.feedbackForm, [action.sentiment]: true },\n },\n };\n case 'HIDE_FEEDBACK_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n feedbackForm: { ...entry.feedbackForm, [action.sentiment]: false },\n },\n };\n case 'SHOW_COMPLETION_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n completionForm: { ...entry.completionForm, [action.sentiment]: true },\n },\n };\n case 'HIDE_COMPLETION_FORM':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n completionForm: {\n ...entry.completionForm,\n [action.sentiment]: false,\n },\n },\n };\n case 'UPDATE_BUTTON_STATE':\n return {\n ...state,\n [action.messageId]: {\n ...entry,\n buttonState: { ...entry.buttonState, ...action.payload },\n },\n };\n case 'RESET_BUTTON_STATE': {\n const { [action.messageId]: _, ...rest } = state;\n return rest;\n }\n default:\n return state;\n }\n}\n\nexport function useFeedbackState() {\n const [state, dispatch] = useReducer(reducer, {});\n\n const showFeedbackForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'SHOW_FEEDBACK_FORM', messageId, sentiment });\n\n const hideFeedbackForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'HIDE_FEEDBACK_FORM', messageId, sentiment });\n\n const showCompletionForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'SHOW_COMPLETION_FORM', messageId, sentiment });\n\n const hideCompletionForm = (messageId: string, sentiment: Sentiment) =>\n dispatch({ type: 'HIDE_COMPLETION_FORM', messageId, sentiment });\n\n const updateButtonState = (\n messageId: string,\n payload: Partial<FeedbackButtonState>,\n ) => dispatch({ type: 'UPDATE_BUTTON_STATE', messageId, payload });\n\n const resetButtonState = (messageId: string) =>\n dispatch({ type: 'RESET_BUTTON_STATE', messageId });\n\n const getFeedbackState = (messageId: string) =>\n state[messageId] ?? {\n feedbackForm: { positive: false, negative: false },\n completionForm: { positive: false, negative: false },\n buttonState: { sentiment: 'positive', copied: false },\n };\n\n const toggleFeedbackForm = (\n messageId: string,\n sentimentToShow: Sentiment,\n ) => {\n const opposite: Sentiment =\n sentimentToShow === 'positive' ? 'negative' : 'positive';\n\n hideFeedbackForm(messageId, opposite);\n showFeedbackForm(messageId, sentimentToShow);\n };\n\n return {\n state,\n getFeedbackState,\n showFeedbackForm,\n hideFeedbackForm,\n toggleFeedbackForm,\n showCompletionForm,\n hideCompletionForm,\n updateButtonState,\n resetButtonState,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { lightspeedApiRef } from '../api/api.esm.js';
4
+
5
+ const useFeedbackStatus = () => {
6
+ const lightspeedApi = useApi(lightspeedApiRef);
7
+ return useQuery({
8
+ queryKey: ["feedbackStatus"],
9
+ queryFn: async () => await lightspeedApi.getFeedbackStatus()
10
+ });
11
+ };
12
+
13
+ export { useFeedbackStatus };
14
+ //# sourceMappingURL=useFeedbackStatus.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFeedbackStatus.esm.js","sources":["../../src/hooks/useFeedbackStatus.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 { useApi } from '@backstage/core-plugin-api';\n\nimport { useQuery, type UseQueryResult } from '@tanstack/react-query';\n\nimport { lightspeedApiRef } from '../api/api';\n\n// Fetch feedback status\nexport const useFeedbackStatus = (): UseQueryResult<boolean, Error> => {\n const lightspeedApi = useApi(lightspeedApiRef);\n return useQuery({\n queryKey: ['feedbackStatus'],\n queryFn: async () => await lightspeedApi.getFeedbackStatus(),\n });\n};\n"],"names":[],"mappings":";;;;AAsBO,MAAM,oBAAoB,MAAsC;AACrE,EAAA,MAAM,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAC7C,EAAA,OAAO,QAAA,CAAS;AAAA,IACd,QAAA,EAAU,CAAC,gBAAgB,CAAA;AAAA,IAC3B,OAAA,EAAS,YAAY,MAAM,aAAA,CAAc,iBAAA;AAAkB,GAC5D,CAAA;AACH;;;;"}