@red-hat-developer-hub/backstage-plugin-lightspeed 1.1.1 → 1.2.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 (73) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +26 -20
  3. package/dist/alpha.d.ts +24 -0
  4. package/dist/api/LightspeedApiClient.esm.js.map +1 -1
  5. package/dist/api/api.esm.js.map +1 -1
  6. package/dist/components/AttachmentContext.esm.js +12 -2
  7. package/dist/components/AttachmentContext.esm.js.map +1 -1
  8. package/dist/components/LightSpeedChat.esm.js +199 -39
  9. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  10. package/dist/components/LightspeedChatBox.esm.js +70 -9
  11. package/dist/components/LightspeedChatBox.esm.js.map +1 -1
  12. package/dist/components/LightspeedChatBoxHeader.esm.js +84 -26
  13. package/dist/components/LightspeedChatBoxHeader.esm.js.map +1 -1
  14. package/dist/components/LightspeedChatContainer.esm.js +118 -0
  15. package/dist/components/LightspeedChatContainer.esm.js.map +1 -0
  16. package/dist/components/LightspeedDrawerContext.esm.js +6 -0
  17. package/dist/components/LightspeedDrawerContext.esm.js.map +1 -0
  18. package/dist/components/LightspeedDrawerProvider.esm.js +175 -0
  19. package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -0
  20. package/dist/components/LightspeedDrawerStateExposer.esm.js +28 -0
  21. package/dist/components/LightspeedDrawerStateExposer.esm.js.map +1 -0
  22. package/dist/components/LightspeedFAB.esm.js +68 -0
  23. package/dist/components/LightspeedFAB.esm.js.map +1 -0
  24. package/dist/components/LightspeedIcon.esm.js +23 -2
  25. package/dist/components/LightspeedIcon.esm.js.map +1 -1
  26. package/dist/components/LightspeedPage.esm.js +5 -110
  27. package/dist/components/LightspeedPage.esm.js.map +1 -1
  28. package/dist/components/Router.esm.js +19 -0
  29. package/dist/components/Router.esm.js.map +1 -0
  30. package/dist/components/ToolCallContent.esm.js +217 -0
  31. package/dist/components/ToolCallContent.esm.js.map +1 -0
  32. package/dist/hooks/useConversationMessages.esm.js +136 -20
  33. package/dist/hooks/useConversationMessages.esm.js.map +1 -1
  34. package/dist/hooks/useLightspeedDrawerContext.esm.js +15 -0
  35. package/dist/hooks/useLightspeedDrawerContext.esm.js.map +1 -0
  36. package/dist/hooks/usePinnedChatsSettings.esm.js +118 -0
  37. package/dist/hooks/usePinnedChatsSettings.esm.js.map +1 -0
  38. package/dist/hooks/useSortSettings.esm.js +60 -0
  39. package/dist/hooks/useSortSettings.esm.js.map +1 -0
  40. package/dist/images/logo-black.svg +22 -0
  41. package/dist/images/rounded-logo.svg +22 -0
  42. package/dist/index.d.ts +52 -2
  43. package/dist/index.esm.js +4 -1
  44. package/dist/index.esm.js.map +1 -1
  45. package/dist/plugin.esm.js +44 -5
  46. package/dist/plugin.esm.js.map +1 -1
  47. package/dist/routes.esm.js +7 -2
  48. package/dist/routes.esm.js.map +1 -1
  49. package/dist/translations/de.esm.js +29 -1
  50. package/dist/translations/de.esm.js.map +1 -1
  51. package/dist/translations/es.esm.js +29 -1
  52. package/dist/translations/es.esm.js.map +1 -1
  53. package/dist/translations/fr.esm.js +120 -115
  54. package/dist/translations/fr.esm.js.map +1 -1
  55. package/dist/translations/index.esm.js +3 -1
  56. package/dist/translations/index.esm.js.map +1 -1
  57. package/dist/translations/it.esm.js +158 -0
  58. package/dist/translations/it.esm.js.map +1 -0
  59. package/dist/translations/ja.esm.js +158 -0
  60. package/dist/translations/ja.esm.js.map +1 -0
  61. package/dist/translations/ref.esm.js +29 -1
  62. package/dist/translations/ref.esm.js.map +1 -1
  63. package/dist/types.esm.js.map +1 -1
  64. package/dist/utils/lightspeed-chatbox-utils.esm.js +24 -5
  65. package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -1
  66. package/dist/utils/reasoningParser.esm.js +46 -0
  67. package/dist/utils/reasoningParser.esm.js.map +1 -0
  68. package/dist/utils/toolCallMapper.esm.js +15 -0
  69. package/dist/utils/toolCallMapper.esm.js.map +1 -0
  70. package/dist/utils/user-utils.esm.js +7 -0
  71. package/dist/utils/user-utils.esm.js.map +1 -0
  72. package/package.json +9 -4
  73. package/app-config.yaml +0 -18
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  ## @red-hat-developer-hub/backstage-plugin-lightspeed
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bb8ef80: Added chatbot display modes (overlay, docked, fullscreen) with the ability to switch between modes via settings dropdown.
8
+ - 275b45e: feat: add conversation sorting with persistence, persisting pinned chats and pinned chats toggle per-user
9
+
10
+ ### Patch Changes
11
+
12
+ - 7f4be01: Fixed file attachments being lost when changing display mode (overlay, docked, window).
13
+ - c5606c3: Refactor ToolCallContent component to use PatternFly components.
14
+ - e16075b: show expandable card for deep thinking responses
15
+ - fa48491: Added tool call support to display AI tool execution results
16
+ - ea9109f: Fixes an issue where the Chat History Drawer was remaining collapsed when loading the Lightspeed page in fullscreen/embedded mode on desktop.
17
+ - 1d656f2: Added a dark logo for light themes
18
+ - @red-hat-developer-hub/backstage-plugin-lightspeed-common@1.2.0
19
+
20
+ ## 1.1.2
21
+
22
+ ### Patch Changes
23
+
24
+ - f74564d: Added 'it' and 'ja' i18n support and updated 'fr' translation strings.
25
+ - @red-hat-developer-hub/backstage-plugin-lightspeed-common@1.1.2
26
+
3
27
  ## 1.1.1
4
28
 
5
29
  ### Patch Changes
package/README.md CHANGED
@@ -86,7 +86,7 @@ Lightspeed is a front-end plugin that enables you to interact with any LLM serve
86
86
 
87
87
  ## Loading as Dynamic Plugin
88
88
 
89
- #### To install Lightspeed plugin into Red Hat Developer Hub or Janus IDP via Helm use this configuration:
89
+ #### To configure Lightspeed plugin into Red Hat Developer Hub use this configuration:
90
90
 
91
91
  - Load the lightspeed plugin from the npm registry
92
92
 
@@ -114,17 +114,21 @@ global:
114
114
  - importName: lightspeedTranslations
115
115
  module: Alpha
116
116
  ref: lightspeedTranslationRef
117
- appIcons:
118
- - name: LightspeedIcon
119
- module: LightspeedPlugin
120
- importName: LightspeedIcon
121
117
  dynamicRoutes:
122
118
  - path: /lightspeed
123
119
  importName: LightspeedPage
124
- module: LightspeedPlugin
125
- menuItem:
126
- icon: LightspeedIcon
127
- text: Lightspeed
120
+ mountPoints:
121
+ - mountPoint: application/listener
122
+ importName: LightspeedFAB
123
+ - mountPoint: application/provider
124
+ importName: LightspeedDrawerProvider
125
+ - mountPoint: application/drawer-state
126
+ importName: LightspeedDrawerStateExposer
127
+ - mountPoint: application/drawer-content
128
+ importName: LightspeedChatContainer
129
+ config:
130
+ id: lightspeed
131
+ priority: 100
128
132
  ```
129
133
 
130
134
  - add the lightspeed configuration in the `app-config.yaml`
@@ -153,8 +157,7 @@ Follow the below steps -
153
157
 
154
158
  - Add the extension point inside the `app-config.yaml` or `app-config.local.yaml` file.
155
159
 
156
- ```
157
-
160
+ ```yaml
158
161
  dynamicPlugins:
159
162
  frontend:
160
163
  red-hat-developer-hub.backstage-plugin-lightspeed:
@@ -162,16 +165,19 @@ dynamicPlugins:
162
165
  - importName: lightspeedTranslations
163
166
  module: Alpha
164
167
  ref: lightspeedTranslationRef
165
- appIcons:
166
- - name: LightspeedIcon
167
- module: LightspeedPlugin
168
- importName: LightspeedIcon
169
168
  dynamicRoutes:
170
169
  - path: /lightspeed
171
170
  importName: LightspeedPage
172
- module: LightspeedPlugin
173
- menuItem:
174
- icon: LightspeedIcon
175
- text: Lightspeed
176
-
171
+ mountPoints:
172
+ - mountPoint: application/listener
173
+ importName: LightspeedFAB
174
+ - mountPoint: application/provider
175
+ importName: LightspeedDrawerProvider
176
+ - mountPoint: application/drawer-state
177
+ importName: LightspeedDrawerStateExposer
178
+ - mountPoint: application/drawer-content
179
+ importName: LightspeedChatContainer
180
+ config:
181
+ id: lightspeed
182
+ priority: 100
177
183
  ```
package/dist/alpha.d.ts CHANGED
@@ -76,6 +76,7 @@ declare const lightspeedTranslationRef: _backstage_core_plugin_api_alpha.Transla
76
76
  readonly "aria.scroll.down": string;
77
77
  readonly "aria.scroll.up": string;
78
78
  readonly "aria.settings.label": string;
79
+ readonly "aria.close": string;
79
80
  readonly "modal.edit": string;
80
81
  readonly "modal.save": string;
81
82
  readonly "modal.close": string;
@@ -97,6 +98,7 @@ declare const lightspeedTranslationRef: _backstage_core_plugin_api_alpha.Transla
97
98
  readonly "tooltip.backToTop": string;
98
99
  readonly "tooltip.backToBottom": string;
99
100
  readonly "tooltip.settings": string;
101
+ readonly "tooltip.close": string;
100
102
  readonly "modal.title.preview": string;
101
103
  readonly "modal.title.edit": string;
102
104
  readonly "icon.lightspeed.alt": string;
@@ -131,6 +133,28 @@ declare const lightspeedTranslationRef: _backstage_core_plugin_api_alpha.Transla
131
133
  readonly "settings.pinned.disable": string;
132
134
  readonly "settings.pinned.enabled.description": string;
133
135
  readonly "settings.pinned.disabled.description": string;
136
+ readonly "toolCall.header": string;
137
+ readonly "toolCall.thinking": string;
138
+ readonly "toolCall.executionTime": string;
139
+ readonly "toolCall.parameters": string;
140
+ readonly "toolCall.response": string;
141
+ readonly "toolCall.showMore": string;
142
+ readonly "toolCall.showLess": string;
143
+ readonly "toolCall.loading": string;
144
+ readonly "toolCall.executing": string;
145
+ readonly "toolCall.copyResponse": string;
146
+ readonly "toolCall.summary": string;
147
+ readonly "toolCall.mcpServer": string;
148
+ readonly "settings.displayMode.label": string;
149
+ readonly "settings.displayMode.overlay": string;
150
+ readonly "settings.displayMode.docked": string;
151
+ readonly "settings.displayMode.fullscreen": string;
152
+ readonly "sort.label": string;
153
+ readonly "sort.newest": string;
154
+ readonly "sort.oldest": string;
155
+ readonly "sort.alphabeticalAsc": string;
156
+ readonly "sort.alphabeticalDesc": string;
157
+ readonly "reasoning.thinking": string;
134
158
  }>;
135
159
 
136
160
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"LightspeedApiClient.esm.js","sources":["../../src/api/LightspeedApiClient.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 { ConfigApi, FetchApi } from '@backstage/core-plugin-api';\n\nimport {\n TEMP_CONVERSATION_ID,\n VALID_TOPIC_RESTRICTION_PROVIDER_IDS,\n} from '../const';\nimport { Attachment, CaptureFeedback } from '../types';\nimport { LightspeedAPI } from './api';\n\nexport type Options = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\nexport class LightspeedApiClient implements LightspeedAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: Options) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/query`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n conversation_id:\n conversation_id === TEMP_CONVERSATION_ID\n ? undefined\n : conversation_id,\n model: selectedModel,\n provider: selectedProvider,\n query: prompt,\n attachments,\n }),\n });\n\n if (!response.body) {\n throw new Error('Readable stream is not supported or there is no body.');\n }\n\n if (!response.ok) {\n const body = await response.body.getReader();\n const reader = body.read();\n const decoder = new TextDecoder('utf-8');\n const text = await reader.then(({ done, value }) => {\n if (done) {\n return '';\n }\n return decoder.decode(value);\n });\n const errorMessage = JSON.parse(text);\n if (errorMessage?.error) {\n throw new Error(`failed to create message: ${errorMessage.error}`);\n }\n }\n return response.body.getReader();\n }\n\n private async fetcher(url: string) {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n if (!response.ok) {\n throw new Error(\n `failed to fetch data, status ${response.status}: ${response.statusText}`,\n );\n }\n return response;\n }\n\n async isTopicRestrictionEnabled() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/shields`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get shields, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n if (!response?.shields || !Array.isArray(response.shields)) {\n return false;\n }\n\n return response.shields.some((shield: any) => {\n const providerId = shield.provider_resource_id;\n\n return VALID_TOPIC_RESTRICTION_PROVIDER_IDS.some(\n validId => providerId.toLowerCase() === validId.toLowerCase(),\n );\n });\n }\n\n async getAllModels() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/models`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get models, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n\n return response?.models ? response?.models : [];\n }\n\n async getConversationMessages(conversation_id: string) {\n if (conversation_id === TEMP_CONVERSATION_ID) {\n return [];\n }\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n );\n if (!result.ok) {\n throw new Error(\n `failed to get conversation messages, status ${result.status}: ${result.statusText}`,\n );\n }\n const response = await result.json();\n return response.chat_history ?? [];\n }\n\n async getConversations() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v2/conversations`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get conversation, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n return response.conversations ?? [];\n }\n\n async deleteConversation(conversation_id: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'DELETE',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to delete conversation, status ${response.status}: ${response.statusText}`,\n );\n }\n return { success: true };\n }\n\n async renameConversation(conversation_id: string, newName: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ topic_summary: newName }),\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to rename conversation, status ${response.status}: ${response.statusText}`,\n );\n }\n return { success: true };\n }\n\n getFeedbackStatus = async () => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v1/feedback/status`,\n {\n method: 'GET',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to GET feedback status, status ${response.status}: ${response.statusText}`,\n );\n }\n const result = await response.json();\n return result?.status?.enabled ?? false;\n };\n\n captureFeedback = async (payload: CaptureFeedback) => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/feedback`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n accept: 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(\n `failed to capture feedback, status ${response.status}: ${response.statusText}`,\n );\n }\n return await response.json();\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LightspeedApiClient.esm.js","sources":["../../src/api/LightspeedApiClient.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 { ConfigApi, FetchApi } from '@backstage/core-plugin-api';\n\nimport {\n TEMP_CONVERSATION_ID,\n VALID_TOPIC_RESTRICTION_PROVIDER_IDS,\n} from '../const';\nimport { Attachment, CaptureFeedback } from '../types';\nimport { LightspeedAPI } from './api';\n\n/**\n * @public\n * Lightspeed API client options\n */\n\nexport type Options = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\n/**\n * @public\n * Lightspeed API client implementation\n */\n\nexport class LightspeedApiClient implements LightspeedAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: Options) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/query`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n conversation_id:\n conversation_id === TEMP_CONVERSATION_ID\n ? undefined\n : conversation_id,\n model: selectedModel,\n provider: selectedProvider,\n query: prompt,\n attachments,\n }),\n });\n\n if (!response.body) {\n throw new Error('Readable stream is not supported or there is no body.');\n }\n\n if (!response.ok) {\n const body = await response.body.getReader();\n const reader = body.read();\n const decoder = new TextDecoder('utf-8');\n const text = await reader.then(({ done, value }) => {\n if (done) {\n return '';\n }\n return decoder.decode(value);\n });\n const errorMessage = JSON.parse(text);\n if (errorMessage?.error) {\n throw new Error(`failed to create message: ${errorMessage.error}`);\n }\n }\n return response.body.getReader();\n }\n\n private async fetcher(url: string) {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n if (!response.ok) {\n throw new Error(\n `failed to fetch data, status ${response.status}: ${response.statusText}`,\n );\n }\n return response;\n }\n\n async isTopicRestrictionEnabled() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/shields`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get shields, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n if (!response?.shields || !Array.isArray(response.shields)) {\n return false;\n }\n\n return response.shields.some((shield: any) => {\n const providerId = shield.provider_resource_id;\n\n return VALID_TOPIC_RESTRICTION_PROVIDER_IDS.some(\n validId => providerId.toLowerCase() === validId.toLowerCase(),\n );\n });\n }\n\n async getAllModels() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v1/models`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get models, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n\n return response?.models ? response?.models : [];\n }\n\n async getConversationMessages(conversation_id: string) {\n if (conversation_id === TEMP_CONVERSATION_ID) {\n return [];\n }\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n );\n if (!result.ok) {\n throw new Error(\n `failed to get conversation messages, status ${result.status}: ${result.statusText}`,\n );\n }\n const response = await result.json();\n return response.chat_history ?? [];\n }\n\n async getConversations() {\n const baseUrl = await this.getBaseUrl();\n const result = await this.fetcher(`${baseUrl}/v2/conversations`);\n\n if (!result.ok) {\n throw new Error(\n `failed to get conversation, status ${result.status}: ${result.statusText}`,\n );\n }\n\n const response = await result.json();\n return response.conversations ?? [];\n }\n\n async deleteConversation(conversation_id: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'DELETE',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to delete conversation, status ${response.status}: ${response.statusText}`,\n );\n }\n return { success: true };\n }\n\n async renameConversation(conversation_id: string, newName: string) {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v2/conversations/${encodeURIComponent(conversation_id)}`,\n {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ topic_summary: newName }),\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to rename conversation, status ${response.status}: ${response.statusText}`,\n );\n }\n return { success: true };\n }\n\n getFeedbackStatus = async () => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(\n `${baseUrl}/v1/feedback/status`,\n {\n method: 'GET',\n headers: {},\n },\n );\n\n if (!response.ok) {\n throw new Error(\n `failed to GET feedback status, status ${response.status}: ${response.statusText}`,\n );\n }\n const result = await response.json();\n return result?.status?.enabled ?? false;\n };\n\n captureFeedback = async (payload: CaptureFeedback) => {\n const baseUrl = await this.getBaseUrl();\n\n const response = await this.fetchApi.fetch(`${baseUrl}/v1/feedback`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n accept: 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(\n `failed to capture feedback, status ${response.status}: ${response.statusText}`,\n );\n }\n return await response.json();\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.esm.js","sources":["../../src/api/api.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 { createApiRef } from '@backstage/core-plugin-api';\n\nimport {\n Attachment,\n BaseMessage,\n CaptureFeedback,\n ConversationList,\n LCSModel,\n} from '../types';\n\nexport type LightspeedAPI = {\n getAllModels: () => Promise<LCSModel[]>;\n getConversationMessages: (conversation_id: string) => Promise<BaseMessage[]>;\n createMessage: (\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) => Promise<ReadableStreamDefaultReader>;\n deleteConversation: (\n conversation_id: string,\n ) => Promise<{ success: boolean }>;\n renameConversation: (\n conversation_id: string,\n newName: string,\n ) => Promise<{ success: boolean }>;\n getConversations: () => Promise<ConversationList>;\n getFeedbackStatus: () => Promise<boolean>;\n captureFeedback: (payload: CaptureFeedback) => Promise<{ response: string }>;\n isTopicRestrictionEnabled: () => Promise<boolean>;\n};\n\nexport const lightspeedApiRef = createApiRef<LightspeedAPI>({\n id: 'plugin.lightspeed.service',\n});\n"],"names":[],"mappings":";;AAiDO,MAAM,mBAAmB,YAA4B,CAAA;AAAA,EAC1D,EAAI,EAAA;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"api.esm.js","sources":["../../src/api/api.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 { createApiRef } from '@backstage/core-plugin-api';\n\nimport {\n Attachment,\n BaseMessage,\n CaptureFeedback,\n ConversationList,\n LCSModel,\n} from '../types';\n\n/**\n * @public\n * Lightspeed API\n */\n\nexport type LightspeedAPI = {\n getAllModels: () => Promise<LCSModel[]>;\n getConversationMessages: (conversation_id: string) => Promise<BaseMessage[]>;\n createMessage: (\n prompt: string,\n selectedModel: string,\n selectedProvider: string,\n conversation_id: string,\n attachments: Attachment[],\n ) => Promise<ReadableStreamDefaultReader>;\n deleteConversation: (\n conversation_id: string,\n ) => Promise<{ success: boolean }>;\n renameConversation: (\n conversation_id: string,\n newName: string,\n ) => Promise<{ success: boolean }>;\n getConversations: () => Promise<ConversationList>;\n getFeedbackStatus: () => Promise<boolean>;\n captureFeedback: (payload: CaptureFeedback) => Promise<{ response: string }>;\n isTopicRestrictionEnabled: () => Promise<boolean>;\n};\n\n/**\n * @public\n * Lightspeed API interface\n */\n\nexport const lightspeedApiRef = createApiRef<LightspeedAPI>({\n id: 'plugin.lightspeed.service',\n});\n"],"names":[],"mappings":";;AA2DO,MAAM,mBAAmB,YAA4B,CAAA;AAAA,EAC1D,EAAI,EAAA;AACN,CAAC;;;;"}
@@ -1,11 +1,13 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { createContext, useState, useContext } from 'react';
2
+ import { createContext, useState, useEffect, useContext } from 'react';
3
+ import { useLightspeedDrawerContext } from '../hooks/useLightspeedDrawerContext.esm.js';
3
4
  import { useTranslation } from '../hooks/useTranslation.esm.js';
4
5
  import { isSupportedFileType, readFileAsText } from '../utils/attachment-utils.esm.js';
5
6
 
6
7
  const FileAttachmentContext = createContext(null);
7
8
  const FileAttachmentContextProvider = ({ children }) => {
8
9
  const { t } = useTranslation();
10
+ const { draftFileContents, setDraftFileContents } = useLightspeedDrawerContext();
9
11
  const [currentFileContent, setCurrentFileContent] = useState();
10
12
  const [isLoadingFile, setIsLoadingFile] = useState(
11
13
  {}
@@ -15,7 +17,15 @@ const FileAttachmentContextProvider = ({ children }) => {
15
17
  const [uploadError, setUploadError] = useState({
16
18
  message: null
17
19
  });
18
- const [fileContents, setFileContents] = useState([]);
20
+ const [fileContents, setFileContentsState] = useState(
21
+ () => draftFileContents
22
+ );
23
+ useEffect(() => {
24
+ setDraftFileContents(fileContents);
25
+ }, [fileContents, setDraftFileContents]);
26
+ const setFileContents = (value) => {
27
+ setFileContentsState(value);
28
+ };
19
29
  const [isPreviewModalOpen, setIsPreviewModalOpen] = useState(false);
20
30
  const [isEditModalOpen, setIsEditModalOpen] = useState(false);
21
31
  const handleFileUpload = (fileArr) => {
@@ -1 +1 @@
1
- {"version":3,"file":"AttachmentContext.esm.js","sources":["../../src/components/AttachmentContext.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 */\n\nimport {\n createContext,\n Dispatch,\n FC,\n ReactNode,\n SetStateAction,\n useContext,\n useState,\n} from 'react';\n\nimport { useTranslation } from '../hooks/useTranslation';\nimport { FileContent } from '../types';\nimport { isSupportedFileType, readFileAsText } from '../utils/attachment-utils';\n\ntype UploadError = { type?: 'info' | 'danger'; message: string | null };\ninterface FileAttachmentContextType {\n showAlert: boolean;\n uploadError: UploadError;\n fileContents: FileContent[];\n isLoadingFile: Record<string, boolean>;\n handleFileUpload: (files: File[]) => void;\n setFileContents: Dispatch<SetStateAction<FileContent[]>>;\n setUploadError: Dispatch<SetStateAction<UploadError>>;\n setShowAlert: Dispatch<SetStateAction<boolean>>;\n currentFileContent?: FileContent;\n setCurrentFileContent: Dispatch<SetStateAction<FileContent | undefined>>;\n modalState: {\n previewModalKey: number;\n setPreviewModalKey: Dispatch<SetStateAction<number>>;\n isPreviewModalOpen: boolean;\n setIsPreviewModalOpen: Dispatch<SetStateAction<boolean>>;\n isEditModalOpen: boolean;\n setIsEditModalOpen: Dispatch<SetStateAction<boolean>>;\n };\n}\n\nexport const FileAttachmentContext =\n createContext<FileAttachmentContextType | null>(null);\n\nconst FileAttachmentContextProvider: FC<{\n children: ReactNode;\n}> = ({ children }) => {\n const { t } = useTranslation();\n const [currentFileContent, setCurrentFileContent] = useState<\n FileContent | undefined\n >();\n const [isLoadingFile, setIsLoadingFile] = useState<Record<string, boolean>>(\n {},\n );\n const [previewModalKey, setPreviewModalKey] = useState<number>(0);\n const [showAlert, setShowAlert] = useState<boolean>(false);\n const [uploadError, setUploadError] = useState<UploadError>({\n message: null,\n });\n const [fileContents, setFileContents] = useState<FileContent[]>([]);\n const [isPreviewModalOpen, setIsPreviewModalOpen] = useState<boolean>(false);\n const [isEditModalOpen, setIsEditModalOpen] = useState<boolean>(false);\n const handleFileUpload = (fileArr: File[]) => {\n const existingFile = fileContents.find(\n file => file.name === fileArr[0].name,\n );\n if (existingFile) {\n setShowAlert(true);\n setUploadError({\n type: 'info',\n message: t('file.upload.error.alreadyExists'),\n });\n return;\n }\n\n setIsLoadingFile({ [fileArr[0].name]: true });\n\n if (fileArr.length > 1) {\n setShowAlert(true);\n setUploadError({\n message: t('file.upload.error.multipleFiles'),\n });\n return;\n }\n if (!isSupportedFileType(fileArr[0])) {\n setShowAlert(true);\n setUploadError({\n message: t('file.upload.error.unsupportedType'),\n });\n return;\n }\n\n // this is 25MB in bytes; size is in bytes\n if (fileArr[0].size > 25000000) {\n setShowAlert(true);\n setUploadError({\n message: t('file.upload.error.fileTooLarge'),\n });\n return;\n }\n\n readFileAsText(fileArr[0])\n .then(data => {\n setFileContents(prev => [\n ...prev,\n {\n name: fileArr[0].name,\n type: fileArr[0].type,\n content: data as string,\n },\n ]);\n setShowAlert(false);\n setUploadError({ message: null });\n setIsLoadingFile({ [fileArr[0].name]: false });\n })\n .catch((error: DOMException) => {\n setUploadError({\n message: t('file.upload.error.readFailed' as any, {\n errorMessage: error.message,\n }),\n });\n });\n };\n\n const modalState = {\n isPreviewModalOpen,\n setIsPreviewModalOpen,\n isEditModalOpen,\n setIsEditModalOpen,\n previewModalKey,\n setPreviewModalKey,\n };\n return (\n <FileAttachmentContext.Provider\n value={{\n modalState,\n fileContents,\n setFileContents,\n handleFileUpload,\n isLoadingFile,\n showAlert,\n setShowAlert,\n uploadError,\n setUploadError,\n currentFileContent,\n setCurrentFileContent,\n }}\n >\n {children}\n </FileAttachmentContext.Provider>\n );\n};\n\nexport default FileAttachmentContextProvider;\n\nexport const useFileAttachmentContext = (): FileAttachmentContextType => {\n const { t } = useTranslation();\n const context = useContext<FileAttachmentContextType | null>(\n FileAttachmentContext,\n );\n\n if (context === null) {\n // The error message is developer-facing and typically not translated\n throw new Error(t('error.context.fileAttachment'));\n }\n\n return context;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AttachmentContext.esm.js","sources":["../../src/components/AttachmentContext.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 */\n\nimport {\n createContext,\n Dispatch,\n FC,\n ReactNode,\n SetStateAction,\n useContext,\n useEffect,\n useState,\n} from 'react';\n\nimport { useLightspeedDrawerContext } from '../hooks/useLightspeedDrawerContext';\nimport { useTranslation } from '../hooks/useTranslation';\nimport { FileContent } from '../types';\nimport { isSupportedFileType, readFileAsText } from '../utils/attachment-utils';\n\ntype UploadError = { type?: 'info' | 'danger'; message: string | null };\ninterface FileAttachmentContextType {\n showAlert: boolean;\n uploadError: UploadError;\n fileContents: FileContent[];\n isLoadingFile: Record<string, boolean>;\n handleFileUpload: (files: File[]) => void;\n setFileContents: Dispatch<SetStateAction<FileContent[]>>;\n setUploadError: Dispatch<SetStateAction<UploadError>>;\n setShowAlert: Dispatch<SetStateAction<boolean>>;\n currentFileContent?: FileContent;\n setCurrentFileContent: Dispatch<SetStateAction<FileContent | undefined>>;\n modalState: {\n previewModalKey: number;\n setPreviewModalKey: Dispatch<SetStateAction<number>>;\n isPreviewModalOpen: boolean;\n setIsPreviewModalOpen: Dispatch<SetStateAction<boolean>>;\n isEditModalOpen: boolean;\n setIsEditModalOpen: Dispatch<SetStateAction<boolean>>;\n };\n}\n\nexport const FileAttachmentContext =\n createContext<FileAttachmentContextType | null>(null);\n\nconst FileAttachmentContextProvider: FC<{\n children: ReactNode;\n}> = ({ children }) => {\n const { t } = useTranslation();\n const { draftFileContents, setDraftFileContents } =\n useLightspeedDrawerContext();\n const [currentFileContent, setCurrentFileContent] = useState<\n FileContent | undefined\n >();\n const [isLoadingFile, setIsLoadingFile] = useState<Record<string, boolean>>(\n {},\n );\n const [previewModalKey, setPreviewModalKey] = useState<number>(0);\n const [showAlert, setShowAlert] = useState<boolean>(false);\n const [uploadError, setUploadError] = useState<UploadError>({\n message: null,\n });\n // Initialize from draftFileContents to preserve files across display mode changes\n const [fileContents, setFileContentsState] = useState<FileContent[]>(\n () => draftFileContents,\n );\n\n // Sync fileContents changes to draftFileContents for persistence across mode changes\n useEffect(() => {\n setDraftFileContents(fileContents);\n }, [fileContents, setDraftFileContents]);\n\n // Wrapper to update local state (which will sync to draft via useEffect)\n const setFileContents: Dispatch<SetStateAction<FileContent[]>> = value => {\n setFileContentsState(value);\n };\n const [isPreviewModalOpen, setIsPreviewModalOpen] = useState<boolean>(false);\n const [isEditModalOpen, setIsEditModalOpen] = useState<boolean>(false);\n const handleFileUpload = (fileArr: File[]) => {\n const existingFile = fileContents.find(\n file => file.name === fileArr[0].name,\n );\n if (existingFile) {\n setShowAlert(true);\n setUploadError({\n type: 'info',\n message: t('file.upload.error.alreadyExists'),\n });\n return;\n }\n\n setIsLoadingFile({ [fileArr[0].name]: true });\n\n if (fileArr.length > 1) {\n setShowAlert(true);\n setUploadError({\n message: t('file.upload.error.multipleFiles'),\n });\n return;\n }\n if (!isSupportedFileType(fileArr[0])) {\n setShowAlert(true);\n setUploadError({\n message: t('file.upload.error.unsupportedType'),\n });\n return;\n }\n\n // this is 25MB in bytes; size is in bytes\n if (fileArr[0].size > 25000000) {\n setShowAlert(true);\n setUploadError({\n message: t('file.upload.error.fileTooLarge'),\n });\n return;\n }\n\n readFileAsText(fileArr[0])\n .then(data => {\n setFileContents(prev => [\n ...prev,\n {\n name: fileArr[0].name,\n type: fileArr[0].type,\n content: data as string,\n },\n ]);\n setShowAlert(false);\n setUploadError({ message: null });\n setIsLoadingFile({ [fileArr[0].name]: false });\n })\n .catch((error: DOMException) => {\n setUploadError({\n message: t('file.upload.error.readFailed' as any, {\n errorMessage: error.message,\n }),\n });\n });\n };\n\n const modalState = {\n isPreviewModalOpen,\n setIsPreviewModalOpen,\n isEditModalOpen,\n setIsEditModalOpen,\n previewModalKey,\n setPreviewModalKey,\n };\n return (\n <FileAttachmentContext.Provider\n value={{\n modalState,\n fileContents,\n setFileContents,\n handleFileUpload,\n isLoadingFile,\n showAlert,\n setShowAlert,\n uploadError,\n setUploadError,\n currentFileContent,\n setCurrentFileContent,\n }}\n >\n {children}\n </FileAttachmentContext.Provider>\n );\n};\n\nexport default FileAttachmentContextProvider;\n\nexport const useFileAttachmentContext = (): FileAttachmentContextType => {\n const { t } = useTranslation();\n const context = useContext<FileAttachmentContextType | null>(\n FileAttachmentContext,\n );\n\n if (context === null) {\n // The error message is developer-facing and typically not translated\n throw new Error(t('error.context.fileAttachment'));\n }\n\n return context;\n};\n"],"names":[],"mappings":"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"}