@red-hat-developer-hub/backstage-plugin-lightspeed 0.5.0 → 0.5.2
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.
- package/CHANGELOG.md +27 -0
- package/README.md +15 -0
- package/config.d.ts +21 -0
- package/dist/api/LightspeedApiClient.esm.js +3 -2
- package/dist/api/LightspeedApiClient.esm.js.map +1 -1
- package/dist/api/api.esm.js.map +1 -1
- package/dist/components/Attachment.esm.js +98 -0
- package/dist/components/Attachment.esm.js.map +1 -0
- package/dist/components/AttachmentContext.esm.js +105 -0
- package/dist/components/AttachmentContext.esm.js.map +1 -0
- package/dist/components/FilePreview.esm.js +59 -0
- package/dist/components/FilePreview.esm.js.map +1 -0
- package/dist/components/LightSpeedChat.esm.js +40 -26
- package/dist/components/LightSpeedChat.esm.js.map +1 -1
- package/dist/components/LightspeedChatBox.esm.js +56 -5
- package/dist/components/LightspeedChatBox.esm.js.map +1 -1
- package/dist/components/LightspeedPage.esm.js +3 -2
- package/dist/components/LightspeedPage.esm.js.map +1 -1
- package/dist/const.esm.js +58 -1
- package/dist/const.esm.js.map +1 -1
- package/dist/hooks/useAutoScroll.esm.js +78 -0
- package/dist/hooks/useAutoScroll.esm.js.map +1 -0
- package/dist/hooks/useBufferedMessages.esm.js +27 -0
- package/dist/hooks/useBufferedMessages.esm.js.map +1 -0
- package/dist/hooks/useConversationMessages.esm.js +34 -4
- package/dist/hooks/useConversationMessages.esm.js.map +1 -1
- package/dist/hooks/useCreateCoversationMessage.esm.js +4 -2
- package/dist/hooks/useCreateCoversationMessage.esm.js.map +1 -1
- package/dist/hooks/useWelcomePrompts.esm.js +20 -0
- package/dist/hooks/useWelcomePrompts.esm.js.map +1 -0
- package/dist/types.esm.js +9 -0
- package/dist/types.esm.js.map +1 -0
- package/dist/utils/attachment-utils.esm.js +23 -0
- package/dist/utils/attachment-utils.esm.js.map +1 -0
- package/dist/utils/lightspeed-chatbox-utils.esm.js +9 -4
- package/dist/utils/lightspeed-chatbox-utils.esm.js.map +1 -1
- package/dist/utils/prompt-utils.esm.js +17 -0
- package/dist/utils/prompt-utils.esm.js.map +1 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
## @red-hat-developer-hub/backstage-plugin-lightspeed
|
|
2
2
|
|
|
3
|
+
## 0.5.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fe413e3: Functional disclaimer with topic restriction
|
|
8
|
+
- a9e5f32: Updated dependency `@openapitools/openapi-generator-cli` to `2.20.0`.
|
|
9
|
+
Updated dependency `prettier` to `3.5.3`.
|
|
10
|
+
Updated dependency `@redhat-developer/red-hat-developer-hub-theme` to `0.5.1`.
|
|
11
|
+
Updated dependency `@red-hat-developer-hub/backstage-plugin-theme` to `^0.6.0`.
|
|
12
|
+
Updated dependency `@janus-idp/cli` to `3.5.0`.
|
|
13
|
+
- 3a76aaf: Updated dependency `@patternfly/react-core` to `6.3.0-prerelease.4`.
|
|
14
|
+
- 002f7c9: Updated dependency `@testing-library/user-event` to `14.6.1`.
|
|
15
|
+
- ab6a532: Add configurable sample prompts
|
|
16
|
+
- dc4ff7c: Add file attachment support
|
|
17
|
+
Update functional and legal disclaimers
|
|
18
|
+
Add citation links in the bot response
|
|
19
|
+
Update initial user prompts
|
|
20
|
+
- db3257b: Upgrade PF chatbot
|
|
21
|
+
- Updated dependencies [a9e5f32]
|
|
22
|
+
- @red-hat-developer-hub/backstage-plugin-lightspeed-common@0.3.1
|
|
23
|
+
|
|
24
|
+
## 0.5.1
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- e6c1643: Pause autoscroll when user scrolls up during streaming chat responses
|
|
29
|
+
|
|
3
30
|
## 0.5.0
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -122,6 +122,21 @@ lightspeed:
|
|
|
122
122
|
- id: <server_id>
|
|
123
123
|
url: <server_URL>
|
|
124
124
|
token: <api_key>
|
|
125
|
+
questionValidation: true # Optional - To disable question (prompt) validation set it to false.
|
|
126
|
+
prompts: # optional
|
|
127
|
+
- title: <prompt_title>
|
|
128
|
+
- message: <prompt_message>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`questionValidation` is default to be enabled with topic restriction on RHDH related topics.
|
|
132
|
+
If you want to disable the validation, set the value to be `false`.
|
|
133
|
+
|
|
134
|
+
Example configuration to disable `questionValidation`:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
lightspeed:
|
|
138
|
+
questionValidation: false
|
|
139
|
+
servers: ... ...
|
|
125
140
|
```
|
|
126
141
|
|
|
127
142
|
---
|
package/config.d.ts
CHANGED
|
@@ -38,5 +38,26 @@ export interface Config {
|
|
|
38
38
|
*/
|
|
39
39
|
url: string;
|
|
40
40
|
}>;
|
|
41
|
+
/**
|
|
42
|
+
* query restriction validation (Enabled by default).
|
|
43
|
+
* @visibility frontend
|
|
44
|
+
*/
|
|
45
|
+
questionValidation?: boolean;
|
|
46
|
+
prompts?: Array</**
|
|
47
|
+
* @visibility frontend
|
|
48
|
+
*/
|
|
49
|
+
{
|
|
50
|
+
/**
|
|
51
|
+
* The title of the prompt.
|
|
52
|
+
* Displayed as the heading of the prompt.
|
|
53
|
+
* @visibility frontend
|
|
54
|
+
*/
|
|
55
|
+
title: string;
|
|
56
|
+
/**
|
|
57
|
+
* The main question or message shown in the prompt.
|
|
58
|
+
* @visibility frontend
|
|
59
|
+
*/
|
|
60
|
+
message: string;
|
|
61
|
+
}>;
|
|
41
62
|
};
|
|
42
63
|
}
|
|
@@ -13,7 +13,7 @@ class LightspeedApiClient {
|
|
|
13
13
|
getServerUrl() {
|
|
14
14
|
return `${this.configApi.getConfigArray("lightspeed.servers")[0].getOptionalString("url")}`;
|
|
15
15
|
}
|
|
16
|
-
async createMessage(prompt, selectedModel, conversation_id) {
|
|
16
|
+
async createMessage(prompt, selectedModel, conversation_id, attachments) {
|
|
17
17
|
const baseUrl = await this.getBaseUrl();
|
|
18
18
|
const response = await this.fetchApi.fetch(`${baseUrl}/v1/query`, {
|
|
19
19
|
method: "POST",
|
|
@@ -25,7 +25,8 @@ class LightspeedApiClient {
|
|
|
25
25
|
model: selectedModel,
|
|
26
26
|
provider: this.configApi.getConfigArray("lightspeed.servers")[0].getOptionalString("id"),
|
|
27
27
|
// Currently supports a single llm server
|
|
28
|
-
query: prompt
|
|
28
|
+
query: prompt,
|
|
29
|
+
attachments
|
|
29
30
|
})
|
|
30
31
|
});
|
|
31
32
|
if (!response.body) {
|
|
@@ -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 { TEMP_CONVERSATION_ID } from '../const';\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 getServerUrl() {\n // Currently supports a single llm server\n return `${this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('url')}`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: string,\n conversation_id: string,\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: this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('id'), // Currently supports a single llm server\n query: prompt,\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 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 return response?.data ? response.data : [];\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}/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}/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}/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"],"names":[],"mappings":";;
|
|
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 { TEMP_CONVERSATION_ID } from '../const';\nimport { Attachment } 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 getServerUrl() {\n // Currently supports a single llm server\n return `${this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('url')}`;\n }\n\n async createMessage(\n prompt: string,\n selectedModel: 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: this.configApi\n .getConfigArray('lightspeed.servers')[0]\n .getOptionalString('id'), // Currently supports a single llm server\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 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 return response?.data ? response.data : [];\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}/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}/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}/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"],"names":[],"mappings":"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"}
|
package/dist/api/api.esm.js.map
CHANGED
|
@@ -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 OpenAI from 'openai';\n\nimport { BaseMessage, ConversationList } from '../types';\n\nexport type LightspeedAPI = {\n getAllModels: () => Promise<OpenAI.Models.Model[]>;\n getConversationMessages: (conversation_id: string) => Promise<BaseMessage[]>;\n createMessage: (\n prompt: string,\n selectedModel: string,\n conversation_id: string,\n ) => Promise<ReadableStreamDefaultReader>;\n deleteConversation: (\n conversation_id: string,\n ) => Promise<{ success: boolean }>;\n getConversations: () => Promise<ConversationList>;\n};\n\nexport const lightspeedApiRef = createApiRef<LightspeedAPI>({\n id: 'plugin.lightspeed.service',\n});\n"],"names":[],"mappings":";;
|
|
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 OpenAI from 'openai';\n\nimport { Attachment, BaseMessage, ConversationList } from '../types';\n\nexport type LightspeedAPI = {\n getAllModels: () => Promise<OpenAI.Models.Model[]>;\n getConversationMessages: (conversation_id: string) => Promise<BaseMessage[]>;\n createMessage: (\n prompt: string,\n selectedModel: string,\n conversation_id: string,\n attachments: Attachment[],\n ) => Promise<ReadableStreamDefaultReader>;\n deleteConversation: (\n conversation_id: string,\n ) => Promise<{ success: boolean }>;\n getConversations: () => Promise<ConversationList>;\n};\n\nexport const lightspeedApiRef = createApiRef<LightspeedAPI>({\n id: 'plugin.lightspeed.service',\n});\n"],"names":[],"mappings":";;AAqCO,MAAM,mBAAmB,YAA4B,CAAA;AAAA,EAC1D,EAAI,EAAA;AACN,CAAC;;;;"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { makeStyles } from '@material-ui/core';
|
|
3
|
+
import { PreviewAttachment, ChatbotDisplayMode, AttachmentEdit } from '@patternfly/chatbot';
|
|
4
|
+
import { useFileAttachmentContext } from './AttachmentContext.esm.js';
|
|
5
|
+
|
|
6
|
+
const useStyles = makeStyles(() => ({
|
|
7
|
+
modalFooter: {
|
|
8
|
+
"&>button": {
|
|
9
|
+
width: "12% !important"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}));
|
|
13
|
+
const Attachment = () => {
|
|
14
|
+
const {
|
|
15
|
+
currentFileContent,
|
|
16
|
+
setFileContents,
|
|
17
|
+
modalState,
|
|
18
|
+
setCurrentFileContent
|
|
19
|
+
} = useFileAttachmentContext();
|
|
20
|
+
const classes = useStyles();
|
|
21
|
+
if (!currentFileContent) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const {
|
|
25
|
+
previewModalKey,
|
|
26
|
+
isPreviewModalOpen,
|
|
27
|
+
isEditModalOpen,
|
|
28
|
+
setPreviewModalKey,
|
|
29
|
+
setIsEditModalOpen,
|
|
30
|
+
setIsPreviewModalOpen
|
|
31
|
+
} = modalState;
|
|
32
|
+
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
|
|
33
|
+
PreviewAttachment,
|
|
34
|
+
{
|
|
35
|
+
key: previewModalKey,
|
|
36
|
+
code: currentFileContent?.content,
|
|
37
|
+
fileName: currentFileContent?.name,
|
|
38
|
+
isModalOpen: isPreviewModalOpen,
|
|
39
|
+
secondaryActionButtonText: "Close",
|
|
40
|
+
modalFooterClassName: classes.modalFooter,
|
|
41
|
+
onEdit: () => {
|
|
42
|
+
setIsPreviewModalOpen(false);
|
|
43
|
+
setIsEditModalOpen(true);
|
|
44
|
+
},
|
|
45
|
+
onDismiss: () => {
|
|
46
|
+
setCurrentFileContent(undefined);
|
|
47
|
+
setIsPreviewModalOpen(false);
|
|
48
|
+
},
|
|
49
|
+
handleModalToggle: () => setIsPreviewModalOpen(false),
|
|
50
|
+
displayMode: ChatbotDisplayMode.fullscreen
|
|
51
|
+
}
|
|
52
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
53
|
+
AttachmentEdit,
|
|
54
|
+
{
|
|
55
|
+
key: currentFileContent?.content,
|
|
56
|
+
code: currentFileContent?.content,
|
|
57
|
+
fileName: currentFileContent?.name,
|
|
58
|
+
isModalOpen: isEditModalOpen,
|
|
59
|
+
modalFooterClassName: classes.modalFooter,
|
|
60
|
+
onSave: (_, content) => {
|
|
61
|
+
setCurrentFileContent({
|
|
62
|
+
...currentFileContent,
|
|
63
|
+
content
|
|
64
|
+
});
|
|
65
|
+
setFileContents((prev) => {
|
|
66
|
+
const existingIndex = prev.findIndex(
|
|
67
|
+
(f) => f.name === currentFileContent?.name
|
|
68
|
+
);
|
|
69
|
+
if (existingIndex !== -1) {
|
|
70
|
+
const updated = [...prev];
|
|
71
|
+
updated[existingIndex] = {
|
|
72
|
+
...updated[existingIndex],
|
|
73
|
+
content
|
|
74
|
+
};
|
|
75
|
+
return updated;
|
|
76
|
+
}
|
|
77
|
+
return [
|
|
78
|
+
...prev,
|
|
79
|
+
{
|
|
80
|
+
name: currentFileContent?.name,
|
|
81
|
+
type: currentFileContent?.type,
|
|
82
|
+
content
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
});
|
|
86
|
+
setPreviewModalKey((prev) => prev + 1);
|
|
87
|
+
setIsEditModalOpen(false);
|
|
88
|
+
setIsPreviewModalOpen(true);
|
|
89
|
+
},
|
|
90
|
+
onCancel: () => setCurrentFileContent(undefined),
|
|
91
|
+
handleModalToggle: () => setIsEditModalOpen(false),
|
|
92
|
+
displayMode: ChatbotDisplayMode.fullscreen
|
|
93
|
+
}
|
|
94
|
+
));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export { Attachment as default };
|
|
98
|
+
//# sourceMappingURL=Attachment.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Attachment.esm.js","sources":["../../src/components/Attachment.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 React from 'react';\n\nimport { makeStyles } from '@material-ui/core';\nimport {\n AttachmentEdit,\n ChatbotDisplayMode,\n PreviewAttachment,\n} from '@patternfly/chatbot';\n\nimport { useFileAttachmentContext } from './AttachmentContext';\n\nconst useStyles = makeStyles(() => ({\n modalFooter: {\n '&>button': {\n width: '12% !important',\n },\n },\n}));\nconst Attachment = () => {\n const {\n currentFileContent,\n setFileContents,\n modalState,\n setCurrentFileContent,\n } = useFileAttachmentContext();\n const classes = useStyles();\n\n if (!currentFileContent) {\n return null;\n }\n const {\n previewModalKey,\n isPreviewModalOpen,\n isEditModalOpen,\n setPreviewModalKey,\n setIsEditModalOpen,\n setIsPreviewModalOpen,\n } = modalState;\n\n return (\n <>\n <PreviewAttachment\n key={previewModalKey}\n code={currentFileContent?.content}\n fileName={currentFileContent?.name}\n isModalOpen={isPreviewModalOpen}\n secondaryActionButtonText=\"Close\"\n modalFooterClassName={classes.modalFooter}\n onEdit={() => {\n setIsPreviewModalOpen(false);\n setIsEditModalOpen(true);\n }}\n onDismiss={() => {\n setCurrentFileContent(undefined);\n setIsPreviewModalOpen(false);\n }}\n handleModalToggle={() => setIsPreviewModalOpen(false)}\n displayMode={ChatbotDisplayMode.fullscreen}\n />\n <AttachmentEdit\n key={currentFileContent?.content}\n code={currentFileContent?.content}\n fileName={currentFileContent?.name}\n isModalOpen={isEditModalOpen}\n modalFooterClassName={classes.modalFooter}\n onSave={(_, content) => {\n setCurrentFileContent({\n ...currentFileContent,\n content,\n });\n setFileContents(prev => {\n const existingIndex = prev.findIndex(\n f => f.name === currentFileContent?.name,\n );\n\n if (existingIndex !== -1) {\n // Update the existing file's content\n const updated = [...prev];\n updated[existingIndex] = {\n ...updated[existingIndex],\n content: content as string,\n };\n return updated;\n }\n\n // File doesn't exist, add a new one\n return [\n ...prev,\n {\n name: currentFileContent?.name,\n type: currentFileContent?.type,\n content: content as string,\n },\n ];\n });\n\n setPreviewModalKey(prev => prev + 1);\n setIsEditModalOpen(false);\n setIsPreviewModalOpen(true);\n }}\n onCancel={() => setCurrentFileContent(undefined)}\n handleModalToggle={() => setIsEditModalOpen(false)}\n displayMode={ChatbotDisplayMode.fullscreen}\n />\n </>\n );\n};\n\nexport default Attachment;\n"],"names":["React"],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { isSupportedFileType, readFileAsText } from '../utils/attachment-utils.esm.js';
|
|
3
|
+
|
|
4
|
+
const FileAttachmentContext = React__default.createContext(null);
|
|
5
|
+
const FileAttachmentContextProvider = ({ children }) => {
|
|
6
|
+
const [currentFileContent, setCurrentFileContent] = React__default.useState();
|
|
7
|
+
const [isLoadingFile, setIsLoadingFile] = React__default.useState({});
|
|
8
|
+
const [previewModalKey, setPreviewModalKey] = React__default.useState(0);
|
|
9
|
+
const [showAlert, setShowAlert] = React__default.useState(false);
|
|
10
|
+
const [uploadError, setUploadError] = React__default.useState({
|
|
11
|
+
message: null
|
|
12
|
+
});
|
|
13
|
+
const [fileContents, setFileContents] = React__default.useState([]);
|
|
14
|
+
const [isPreviewModalOpen, setIsPreviewModalOpen] = React__default.useState(false);
|
|
15
|
+
const [isEditModalOpen, setIsEditModalOpen] = React__default.useState(false);
|
|
16
|
+
const handleFileUpload = (fileArr) => {
|
|
17
|
+
const existingFile = fileContents.find(
|
|
18
|
+
(file) => file.name === fileArr[0].name
|
|
19
|
+
);
|
|
20
|
+
if (existingFile) {
|
|
21
|
+
setShowAlert(true);
|
|
22
|
+
setUploadError({ type: "info", message: "File already exists." });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
setIsLoadingFile({ [fileArr[0].name]: true });
|
|
26
|
+
if (fileArr.length > 1) {
|
|
27
|
+
setShowAlert(true);
|
|
28
|
+
setUploadError({
|
|
29
|
+
message: "Uploaded more than one file."
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!isSupportedFileType(fileArr[0])) {
|
|
34
|
+
setShowAlert(true);
|
|
35
|
+
setUploadError({
|
|
36
|
+
message: "Unsupported file type. Supported types are: .txt, .yaml, .json."
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (fileArr[0].size > 25e6) {
|
|
41
|
+
setShowAlert(true);
|
|
42
|
+
setUploadError({
|
|
43
|
+
message: "Your file size is too large. Please ensure that your file is less than 25 MB."
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
readFileAsText(fileArr[0]).then((data) => {
|
|
48
|
+
setFileContents((prev) => [
|
|
49
|
+
...prev,
|
|
50
|
+
{
|
|
51
|
+
name: fileArr[0].name,
|
|
52
|
+
type: fileArr[0].type,
|
|
53
|
+
content: data
|
|
54
|
+
}
|
|
55
|
+
]);
|
|
56
|
+
setShowAlert(false);
|
|
57
|
+
setUploadError({ message: null });
|
|
58
|
+
setIsLoadingFile({ [fileArr[0].name]: false });
|
|
59
|
+
}).catch((error) => {
|
|
60
|
+
setUploadError({
|
|
61
|
+
message: `Failed to read file: ${error.message}`
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const modalState = {
|
|
66
|
+
isPreviewModalOpen,
|
|
67
|
+
setIsPreviewModalOpen,
|
|
68
|
+
isEditModalOpen,
|
|
69
|
+
setIsEditModalOpen,
|
|
70
|
+
previewModalKey,
|
|
71
|
+
setPreviewModalKey
|
|
72
|
+
};
|
|
73
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
74
|
+
FileAttachmentContext.Provider,
|
|
75
|
+
{
|
|
76
|
+
value: {
|
|
77
|
+
modalState,
|
|
78
|
+
fileContents,
|
|
79
|
+
setFileContents,
|
|
80
|
+
handleFileUpload,
|
|
81
|
+
isLoadingFile,
|
|
82
|
+
showAlert,
|
|
83
|
+
uploadError,
|
|
84
|
+
setUploadError,
|
|
85
|
+
currentFileContent,
|
|
86
|
+
setCurrentFileContent
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
children
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
const useFileAttachmentContext = () => {
|
|
93
|
+
const context = React__default.useContext(
|
|
94
|
+
FileAttachmentContext
|
|
95
|
+
);
|
|
96
|
+
if (context === null) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"useFileAttachmentContext must be within a FileAttachmentContextProvider"
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return context;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export { FileAttachmentContext, FileAttachmentContextProvider as default, useFileAttachmentContext };
|
|
105
|
+
//# sourceMappingURL=AttachmentContext.esm.js.map
|
|
@@ -0,0 +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 */\nimport React from 'react';\n\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: React.Dispatch<React.SetStateAction<FileContent[]>>;\n setUploadError: React.Dispatch<React.SetStateAction<UploadError>>;\n currentFileContent?: FileContent;\n setCurrentFileContent: React.Dispatch<\n React.SetStateAction<FileContent | undefined>\n >;\n modalState: {\n previewModalKey: number;\n setPreviewModalKey: React.Dispatch<React.SetStateAction<number>>;\n isPreviewModalOpen: boolean;\n setIsPreviewModalOpen: React.Dispatch<React.SetStateAction<boolean>>;\n isEditModalOpen: boolean;\n setIsEditModalOpen: React.Dispatch<React.SetStateAction<boolean>>;\n };\n}\n\nexport const FileAttachmentContext =\n React.createContext<FileAttachmentContextType | null>(null);\n\nconst FileAttachmentContextProvider: React.FC<{\n children: React.ReactNode;\n}> = ({ children }) => {\n const [currentFileContent, setCurrentFileContent] = React.useState<\n FileContent | undefined\n >();\n const [isLoadingFile, setIsLoadingFile] = React.useState<\n Record<string, boolean>\n >({});\n const [previewModalKey, setPreviewModalKey] = React.useState<number>(0);\n const [showAlert, setShowAlert] = React.useState<boolean>(false);\n const [uploadError, setUploadError] = React.useState<UploadError>({\n message: null,\n });\n const [fileContents, setFileContents] = React.useState<FileContent[]>([]);\n const [isPreviewModalOpen, setIsPreviewModalOpen] =\n React.useState<boolean>(false);\n const [isEditModalOpen, setIsEditModalOpen] = React.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({ type: 'info', message: 'File already exists.' });\n return;\n }\n\n setIsLoadingFile({ [fileArr[0].name]: true });\n\n if (fileArr.length > 1) {\n setShowAlert(true);\n setUploadError({\n message: 'Uploaded more than one file.',\n });\n return;\n }\n if (!isSupportedFileType(fileArr[0])) {\n setShowAlert(true);\n setUploadError({\n message:\n 'Unsupported file type. Supported types are: .txt, .yaml, .json.',\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:\n 'Your file size is too large. Please ensure that your file is less than 25 MB.',\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: `Failed to read file: ${error.message}`,\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 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 context = React.useContext<FileAttachmentContextType | null>(\n FileAttachmentContext,\n );\n\n if (context === null) {\n throw new Error(\n 'useFileAttachmentContext must be within a FileAttachmentContextProvider',\n );\n }\n\n return context;\n};\n"],"names":["React"],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { Divider } from '@material-ui/core';
|
|
3
|
+
import { FileDetailsLabel, ChatbotAlert } from '@patternfly/chatbot';
|
|
4
|
+
import { useFileAttachmentContext } from './AttachmentContext.esm.js';
|
|
5
|
+
|
|
6
|
+
const FilePreview = () => {
|
|
7
|
+
const {
|
|
8
|
+
showAlert,
|
|
9
|
+
fileContents,
|
|
10
|
+
isLoadingFile,
|
|
11
|
+
uploadError,
|
|
12
|
+
modalState,
|
|
13
|
+
setFileContents,
|
|
14
|
+
setUploadError,
|
|
15
|
+
setCurrentFileContent
|
|
16
|
+
} = useFileAttachmentContext();
|
|
17
|
+
const { setIsEditModalOpen, setIsPreviewModalOpen, setPreviewModalKey } = modalState;
|
|
18
|
+
const onAttachmentClick = (_, name) => {
|
|
19
|
+
const file = fileContents?.find((f) => f.name === name);
|
|
20
|
+
setCurrentFileContent({
|
|
21
|
+
name,
|
|
22
|
+
type: file?.type ?? "text/plain",
|
|
23
|
+
content: file?.content ?? ""
|
|
24
|
+
});
|
|
25
|
+
setPreviewModalKey((prev) => prev + 1);
|
|
26
|
+
setIsEditModalOpen(false);
|
|
27
|
+
setIsPreviewModalOpen(true);
|
|
28
|
+
};
|
|
29
|
+
const removeFile = (indexToRemove) => {
|
|
30
|
+
setFileContents(
|
|
31
|
+
(prevContents) => prevContents.filter((_, index) => index !== indexToRemove)
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, fileContents.length > 0 && /* @__PURE__ */ React__default.createElement(Divider, null), fileContents && /* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", gap: "10px" } }, fileContents.map((file, index) => /* @__PURE__ */ React__default.createElement(
|
|
35
|
+
FileDetailsLabel,
|
|
36
|
+
{
|
|
37
|
+
key: index,
|
|
38
|
+
fileName: file.name,
|
|
39
|
+
isLoading: isLoadingFile[file.name],
|
|
40
|
+
onClick: onAttachmentClick,
|
|
41
|
+
onClose: () => {
|
|
42
|
+
removeFile(index);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
))), showAlert && uploadError.message && /* @__PURE__ */ React__default.createElement(
|
|
46
|
+
ChatbotAlert,
|
|
47
|
+
{
|
|
48
|
+
component: "h4",
|
|
49
|
+
title: "File upload failed",
|
|
50
|
+
variant: uploadError.type ?? "danger",
|
|
51
|
+
isInline: true,
|
|
52
|
+
onClose: () => setUploadError({ message: null })
|
|
53
|
+
},
|
|
54
|
+
uploadError.message
|
|
55
|
+
));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { FilePreview as default };
|
|
59
|
+
//# sourceMappingURL=FilePreview.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilePreview.esm.js","sources":["../../src/components/FilePreview.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 React from 'react';\n\nimport { Divider } from '@material-ui/core';\nimport { ChatbotAlert, FileDetailsLabel } from '@patternfly/chatbot';\n\nimport { useFileAttachmentContext } from './AttachmentContext';\n\nconst FilePreview = () => {\n const {\n showAlert,\n fileContents,\n isLoadingFile,\n uploadError,\n modalState,\n setFileContents,\n setUploadError,\n setCurrentFileContent,\n } = useFileAttachmentContext();\n\n const { setIsEditModalOpen, setIsPreviewModalOpen, setPreviewModalKey } =\n modalState;\n\n const onAttachmentClick = (_: React.MouseEvent, name: string) => {\n const file = fileContents?.find(f => f.name === name);\n setCurrentFileContent({\n name,\n type: file?.type ?? 'text/plain',\n content: file?.content ?? '',\n });\n setPreviewModalKey(prev => prev + 1); // update key to re-render content\n setIsEditModalOpen(false);\n setIsPreviewModalOpen(true);\n };\n\n const removeFile = (indexToRemove: number) => {\n setFileContents(prevContents =>\n prevContents.filter((_, index) => index !== indexToRemove),\n );\n };\n\n return (\n <>\n {fileContents.length > 0 && <Divider />}\n {fileContents && (\n <div style={{ display: 'flex', gap: '10px' }}>\n {fileContents.map((file, index) => (\n <FileDetailsLabel\n key={index}\n fileName={file.name}\n isLoading={isLoadingFile[file.name]}\n onClick={onAttachmentClick}\n onClose={() => {\n removeFile(index);\n }}\n />\n ))}\n </div>\n )}\n {showAlert && uploadError.message && (\n <ChatbotAlert\n component=\"h4\"\n title=\"File upload failed\"\n variant={uploadError.type ?? 'danger'}\n isInline\n onClose={() => setUploadError({ message: null })}\n >\n {uploadError.message}\n </ChatbotAlert>\n )}\n </>\n );\n};\n\nexport default FilePreview;\n"],"names":["React"],"mappings":";;;;;AAsBA,MAAM,cAAc,MAAM;AACxB,EAAM,MAAA;AAAA,IACJ,SAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA,eAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,MACE,wBAAyB,EAAA;AAE7B,EAAA,MAAM,EAAE,kBAAA,EAAoB,qBAAuB,EAAA,kBAAA,EACjD,GAAA,UAAA;AAEF,EAAM,MAAA,iBAAA,GAAoB,CAAC,CAAA,EAAqB,IAAiB,KAAA;AAC/D,IAAA,MAAM,OAAO,YAAc,EAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,CAAE,SAAS,IAAI,CAAA;AACpD,IAAsB,qBAAA,CAAA;AAAA,MACpB,IAAA;AAAA,MACA,IAAA,EAAM,MAAM,IAAQ,IAAA,YAAA;AAAA,MACpB,OAAA,EAAS,MAAM,OAAW,IAAA;AAAA,KAC3B,CAAA;AACD,IAAmB,kBAAA,CAAA,CAAA,IAAA,KAAQ,OAAO,CAAC,CAAA;AACnC,IAAA,kBAAA,CAAmB,KAAK,CAAA;AACxB,IAAA,qBAAA,CAAsB,IAAI,CAAA;AAAA,GAC5B;AAEA,EAAM,MAAA,UAAA,GAAa,CAAC,aAA0B,KAAA;AAC5C,IAAA,eAAA;AAAA,MAAgB,kBACd,YAAa,CAAA,MAAA,CAAO,CAAC,CAAG,EAAA,KAAA,KAAU,UAAU,aAAa;AAAA,KAC3D;AAAA,GACF;AAEA,EACE,uBAAAA,cAAA,CAAA,aAAA,CAAAA,cAAA,CAAA,QAAA,EAAA,IAAA,EACG,aAAa,MAAS,GAAA,CAAA,iDAAM,OAAQ,EAAA,IAAA,CAAA,EACpC,gCACEA,cAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAI,OAAO,EAAE,OAAA,EAAS,QAAQ,GAAK,EAAA,MAAA,MACjC,YAAa,CAAA,GAAA,CAAI,CAAC,IAAA,EAAM,KACvB,qBAAAA,cAAA,CAAA,aAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACC,GAAK,EAAA,KAAA;AAAA,MACL,UAAU,IAAK,CAAA,IAAA;AAAA,MACf,SAAA,EAAW,aAAc,CAAA,IAAA,CAAK,IAAI,CAAA;AAAA,MAClC,OAAS,EAAA,iBAAA;AAAA,MACT,SAAS,MAAM;AACb,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA;AAClB;AAAA,GAEH,CACH,CAED,EAAA,SAAA,IAAa,YAAY,OACxB,oBAAAA,cAAA,CAAA,aAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,SAAU,EAAA,IAAA;AAAA,MACV,KAAM,EAAA,oBAAA;AAAA,MACN,OAAA,EAAS,YAAY,IAAQ,IAAA,QAAA;AAAA,MAC7B,QAAQ,EAAA,IAAA;AAAA,MACR,SAAS,MAAM,cAAA,CAAe,EAAE,OAAA,EAAS,MAAM;AAAA,KAAA;AAAA,IAE9C,WAAY,CAAA;AAAA,GAGnB,CAAA;AAEJ;;;;"}
|
|
@@ -17,8 +17,13 @@ import { useLastOpenedConversation } from '../hooks/useLastOpenedConversation.es
|
|
|
17
17
|
import { useLightspeedDeletePermission } from '../hooks/useLightspeedDeletePermission.esm.js';
|
|
18
18
|
import '@backstage/plugin-permission-react';
|
|
19
19
|
import '@red-hat-developer-hub/backstage-plugin-lightspeed-common';
|
|
20
|
+
import { useWelcomePrompts } from '../hooks/useWelcomePrompts.esm.js';
|
|
21
|
+
import { getAttachments } from '../utils/attachment-utils.esm.js';
|
|
20
22
|
import { getCategorizeMessages, getFootnoteProps } from '../utils/lightspeed-chatbox-utils.esm.js';
|
|
23
|
+
import Attachment from './Attachment.esm.js';
|
|
24
|
+
import { useFileAttachmentContext } from './AttachmentContext.esm.js';
|
|
21
25
|
import { DeleteModal } from './DeleteModal.esm.js';
|
|
26
|
+
import FilePreview from './FilePreview.esm.js';
|
|
22
27
|
import { LightspeedChatBox } from './LightspeedChatBox.esm.js';
|
|
23
28
|
import { LightspeedChatBoxHeader } from './LightspeedChatBoxHeader.esm.js';
|
|
24
29
|
|
|
@@ -48,6 +53,11 @@ const useStyles = makeStyles((theme) => ({
|
|
|
48
53
|
width: "95% !important",
|
|
49
54
|
maxWidth: "unset !important"
|
|
50
55
|
}
|
|
56
|
+
},
|
|
57
|
+
footerPopover: {
|
|
58
|
+
"& img": {
|
|
59
|
+
maxWidth: "100%"
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
}));
|
|
53
63
|
const LightspeedChat = ({
|
|
@@ -71,6 +81,7 @@ const LightspeedChat = ({
|
|
|
71
81
|
const [targetConversationId, setTargetConversationId] = React__default.useState("");
|
|
72
82
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = React__default.useState(false);
|
|
73
83
|
const { isReady, lastOpenedId, setLastOpenedId, clearLastOpenedId } = useLastOpenedConversation(user);
|
|
84
|
+
const { fileContents, setFileContents, handleFileUpload } = useFileAttachmentContext();
|
|
74
85
|
React__default.useEffect(() => {
|
|
75
86
|
if (isReady && lastOpenedId !== null) {
|
|
76
87
|
setConversationId(lastOpenedId);
|
|
@@ -80,6 +91,7 @@ const LightspeedChat = ({
|
|
|
80
91
|
const { data: conversations = [] } = useConversations();
|
|
81
92
|
const { mutateAsync: deleteConversation } = useDeleteConversation();
|
|
82
93
|
const { allowed: hasDeleteAccess } = useLightspeedDeletePermission();
|
|
94
|
+
const samplePrompts = useWelcomePrompts();
|
|
83
95
|
React__default.useEffect(() => {
|
|
84
96
|
if (user && lastOpenedId === null && isReady) {
|
|
85
97
|
setConversationId(TEMP_CONVERSATION_ID);
|
|
@@ -100,6 +112,9 @@ const LightspeedChat = ({
|
|
|
100
112
|
queryClient.invalidateQueries({
|
|
101
113
|
queryKey: ["conversations"]
|
|
102
114
|
});
|
|
115
|
+
queryClient.invalidateQueries({
|
|
116
|
+
queryKey: ["conversationMessages", conversationId]
|
|
117
|
+
});
|
|
103
118
|
setNewChatCreated(false);
|
|
104
119
|
};
|
|
105
120
|
const { conversationMessages, handleInputPrompt, scrollToBottomRef } = useConversationMessages(
|
|
@@ -111,11 +126,6 @@ const LightspeedChat = ({
|
|
|
111
126
|
onStart
|
|
112
127
|
);
|
|
113
128
|
const [messages, setMessages] = React__default.useState(conversationMessages);
|
|
114
|
-
React__default.useEffect(() => {
|
|
115
|
-
setTimeout(() => {
|
|
116
|
-
scrollToBottomRef.current?.scrollIntoView({ behavior: "auto" });
|
|
117
|
-
}, 10);
|
|
118
|
-
}, [messages, scrollToBottomRef.current]);
|
|
119
129
|
const sendMessage = (message) => {
|
|
120
130
|
if (conversationId !== TEMP_CONVERSATION_ID) {
|
|
121
131
|
setNewChatCreated(false);
|
|
@@ -123,16 +133,19 @@ const LightspeedChat = ({
|
|
|
123
133
|
setAnnouncement(
|
|
124
134
|
`Message from User: ${prompt}. Message from Bot is loading.`
|
|
125
135
|
);
|
|
126
|
-
handleInputPrompt(message.toString());
|
|
136
|
+
handleInputPrompt(message.toString(), getAttachments(fileContents));
|
|
127
137
|
setIsSendButtonDisabled(true);
|
|
138
|
+
setFileContents([]);
|
|
128
139
|
};
|
|
129
140
|
const onNewChat = React__default.useCallback(() => {
|
|
130
141
|
(async () => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
142
|
+
if (conversationId !== TEMP_CONVERSATION_ID) {
|
|
143
|
+
setMessages([]);
|
|
144
|
+
setConversationId(TEMP_CONVERSATION_ID);
|
|
145
|
+
setNewChatCreated(true);
|
|
146
|
+
}
|
|
134
147
|
})();
|
|
135
|
-
}, [setConversationId, setMessages]);
|
|
148
|
+
}, [conversationId, setConversationId, setMessages]);
|
|
136
149
|
const openDeleteModal = (conversation_id) => {
|
|
137
150
|
setTargetConversationId(conversation_id);
|
|
138
151
|
setIsDeleteModalOpen(true);
|
|
@@ -208,30 +221,30 @@ const LightspeedChat = ({
|
|
|
208
221
|
}
|
|
209
222
|
return c_id;
|
|
210
223
|
});
|
|
224
|
+
scrollToBottomRef.current?.scrollToBottom();
|
|
211
225
|
},
|
|
212
|
-
[setConversationId]
|
|
226
|
+
[setConversationId, scrollToBottomRef]
|
|
213
227
|
);
|
|
214
228
|
const conversationFound = !!conversations.find(
|
|
215
229
|
(c) => c.conversation_id === conversationId
|
|
216
230
|
);
|
|
217
|
-
const welcomePrompts = newChatCreated && conversationMessages.length === 0 || !conversationFound && conversationMessages.length === 0 ?
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
title: "Topic 2",
|
|
225
|
-
message: "Helpful prompt for Topic 2",
|
|
226
|
-
onClick: () => sendMessage("Helpful prompt for Topic 2")
|
|
231
|
+
const welcomePrompts = newChatCreated && conversationMessages.length === 0 || !conversationFound && conversationMessages.length === 0 ? samplePrompts?.map((prompt2) => ({
|
|
232
|
+
title: prompt2.title,
|
|
233
|
+
message: prompt2.message,
|
|
234
|
+
onClick: () => {
|
|
235
|
+
sendMessage(prompt2.message);
|
|
227
236
|
}
|
|
228
|
-
|
|
237
|
+
})) : [];
|
|
229
238
|
const handleFilter = React__default.useCallback((value) => {
|
|
230
239
|
setFilterValue(value);
|
|
231
240
|
}, []);
|
|
232
241
|
const onDrawerToggle = React__default.useCallback(() => {
|
|
233
242
|
setIsDrawerOpen((isOpen) => !isOpen);
|
|
234
243
|
}, []);
|
|
244
|
+
const handleAttach = (data, event) => {
|
|
245
|
+
event.preventDefault();
|
|
246
|
+
handleFileUpload(data);
|
|
247
|
+
};
|
|
235
248
|
if (error) {
|
|
236
249
|
return /* @__PURE__ */ React__default.createElement(Box, { padding: 1 }, /* @__PURE__ */ React__default.createElement(ErrorPanel, { error }));
|
|
237
250
|
}
|
|
@@ -287,18 +300,19 @@ const LightspeedChat = ({
|
|
|
287
300
|
ref: scrollToBottomRef,
|
|
288
301
|
welcomePrompts
|
|
289
302
|
}
|
|
290
|
-
)), /* @__PURE__ */ React__default.createElement(ChatbotFooter, { className: classes.footer }, /* @__PURE__ */ React__default.createElement(
|
|
303
|
+
)), /* @__PURE__ */ React__default.createElement(ChatbotFooter, { className: classes.footer }, /* @__PURE__ */ React__default.createElement(FilePreview, null), /* @__PURE__ */ React__default.createElement(
|
|
291
304
|
MessageBar,
|
|
292
305
|
{
|
|
293
306
|
onSendMessage: sendMessage,
|
|
294
307
|
isSendButtonDisabled,
|
|
295
|
-
hasAttachButton:
|
|
308
|
+
hasAttachButton: true,
|
|
309
|
+
handleAttach,
|
|
296
310
|
hasMicrophoneButton: true
|
|
297
311
|
}
|
|
298
|
-
), /* @__PURE__ */ React__default.createElement(ChatbotFootnote, { ...getFootnoteProps() })))
|
|
312
|
+
), /* @__PURE__ */ React__default.createElement(ChatbotFootnote, { ...getFootnoteProps(classes.footerPopover) })))
|
|
299
313
|
}
|
|
300
314
|
)
|
|
301
|
-
));
|
|
315
|
+
), /* @__PURE__ */ React__default.createElement(Attachment, null));
|
|
302
316
|
};
|
|
303
317
|
|
|
304
318
|
export { LightspeedChat };
|