@red-hat-developer-hub/backstage-plugin-lightspeed 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/alpha.d.ts +32 -0
  3. package/dist/api/NotebooksApiClient.esm.js +88 -17
  4. package/dist/api/NotebooksApiClient.esm.js.map +1 -1
  5. package/dist/api/notebooksApi.esm.js.map +1 -1
  6. package/dist/components/LightSpeedChat.esm.js +76 -17
  7. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  8. package/dist/components/LightspeedChatContainer.esm.js +17 -2
  9. package/dist/components/LightspeedChatContainer.esm.js.map +1 -1
  10. package/dist/components/LightspeedChatModelsState.esm.js +198 -0
  11. package/dist/components/LightspeedChatModelsState.esm.js.map +1 -0
  12. package/dist/components/LightspeedDrawerProvider.esm.js +2 -1
  13. package/dist/components/LightspeedDrawerProvider.esm.js.map +1 -1
  14. package/dist/components/Router.esm.js +2 -1
  15. package/dist/components/Router.esm.js.map +1 -1
  16. package/dist/components/notebooks/AddDocumentModal.esm.js +170 -0
  17. package/dist/components/notebooks/AddDocumentModal.esm.js.map +1 -0
  18. package/dist/components/notebooks/DocumentSidebar.esm.js +154 -0
  19. package/dist/components/notebooks/DocumentSidebar.esm.js.map +1 -0
  20. package/dist/components/notebooks/FileTypeIcon.esm.js +59 -0
  21. package/dist/components/notebooks/FileTypeIcon.esm.js.map +1 -0
  22. package/dist/components/notebooks/NotebookCard.esm.js +86 -75
  23. package/dist/components/notebooks/NotebookCard.esm.js.map +1 -1
  24. package/dist/components/notebooks/NotebookView.esm.js +376 -0
  25. package/dist/components/notebooks/NotebookView.esm.js.map +1 -0
  26. package/dist/components/notebooks/NotebooksTab.esm.js +15 -5
  27. package/dist/components/notebooks/NotebooksTab.esm.js.map +1 -1
  28. package/dist/components/notebooks/OverwriteConfirmModal.esm.js +141 -0
  29. package/dist/components/notebooks/OverwriteConfirmModal.esm.js.map +1 -0
  30. package/dist/components/notebooks/SidebarCollapseIcon.esm.js +47 -0
  31. package/dist/components/notebooks/SidebarCollapseIcon.esm.js.map +1 -0
  32. package/dist/components/notebooks/UploadResourceScreen.esm.js +58 -0
  33. package/dist/components/notebooks/UploadResourceScreen.esm.js.map +1 -0
  34. package/dist/const.esm.js +26 -1
  35. package/dist/const.esm.js.map +1 -1
  36. package/dist/hooks/notebooks/useCreateNotebook.esm.js +19 -0
  37. package/dist/hooks/notebooks/useCreateNotebook.esm.js.map +1 -0
  38. package/dist/hooks/notebooks/useDocumentStatusPolling.esm.js +39 -0
  39. package/dist/hooks/notebooks/useDocumentStatusPolling.esm.js.map +1 -0
  40. package/dist/hooks/notebooks/useNotebookDocuments.esm.js +18 -0
  41. package/dist/hooks/notebooks/useNotebookDocuments.esm.js.map +1 -0
  42. package/dist/hooks/notebooks/useUploadDocument.esm.js +28 -0
  43. package/dist/hooks/notebooks/useUploadDocument.esm.js.map +1 -0
  44. package/dist/hooks/useLightspeedProviderState.esm.js +13 -6
  45. package/dist/hooks/useLightspeedProviderState.esm.js.map +1 -1
  46. package/dist/index.d.ts +1 -0
  47. package/dist/plugin.esm.js +3 -2
  48. package/dist/plugin.esm.js.map +1 -1
  49. package/dist/routes.esm.js +6 -1
  50. package/dist/routes.esm.js.map +1 -1
  51. package/dist/translations/de.esm.js +35 -0
  52. package/dist/translations/de.esm.js.map +1 -1
  53. package/dist/translations/es.esm.js +35 -0
  54. package/dist/translations/es.esm.js.map +1 -1
  55. package/dist/translations/fr.esm.js +35 -0
  56. package/dist/translations/fr.esm.js.map +1 -1
  57. package/dist/translations/it.esm.js +35 -0
  58. package/dist/translations/it.esm.js.map +1 -1
  59. package/dist/translations/ja.esm.js +35 -0
  60. package/dist/translations/ja.esm.js.map +1 -1
  61. package/dist/translations/ref.esm.js +36 -0
  62. package/dist/translations/ref.esm.js.map +1 -1
  63. package/dist/types.esm.js.map +1 -1
  64. package/dist/utils/notebook-upload-utils.esm.js +48 -0
  65. package/dist/utils/notebook-upload-utils.esm.js.map +1 -0
  66. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## @red-hat-developer-hub/backstage-plugin-lightspeed
2
2
 
3
+ ## 2.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 0c0e14e: Add an empty state for unconfigured LLM.
8
+ - @red-hat-developer-hub/backstage-plugin-lightspeed-common@2.2.1
9
+
10
+ ## 2.2.0
11
+
12
+ ### Minor Changes
13
+
14
+ - 82a9ee8: Add notebook creation and document upload flow with file type validation, overwrite confirmation, collapsible document sidebar, file type icons, i18n support, and unit tests.
15
+
16
+ ### Patch Changes
17
+
18
+ - @red-hat-developer-hub/backstage-plugin-lightspeed-common@2.2.0
19
+
3
20
  ## 2.1.0
4
21
 
5
22
  ### Minor Changes
package/dist/alpha.d.ts CHANGED
@@ -59,6 +59,30 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
59
59
  readonly "notebooks.updated.yesterday": string;
60
60
  readonly "notebooks.updated.days": string;
61
61
  readonly "notebooks.updated.on": string;
62
+ readonly "notebook.view.title": string;
63
+ readonly "notebook.view.close": string;
64
+ readonly "notebook.view.documents.count": string;
65
+ readonly "notebook.view.documents.add": string;
66
+ readonly "notebook.view.upload.heading": string;
67
+ readonly "notebook.view.upload.action": string;
68
+ readonly "notebook.view.input.placeholder": string;
69
+ readonly "notebook.view.sidebar.collapse": string;
70
+ readonly "notebook.view.sidebar.expand": string;
71
+ readonly "notebook.view.sidebar.resize": string;
72
+ readonly "notebook.view.documents.uploading": string;
73
+ readonly "notebook.upload.success": string;
74
+ readonly "notebook.upload.failed": string;
75
+ readonly "notebook.upload.modal.title": string;
76
+ readonly "notebook.upload.modal.dragDropTitle": string;
77
+ readonly "notebook.upload.modal.browseButton": string;
78
+ readonly "notebook.upload.modal.separator": string;
79
+ readonly "notebook.upload.modal.infoText": string;
80
+ readonly "notebook.upload.error.unsupportedType": string;
81
+ readonly "notebook.upload.error.fileTooLarge": string;
82
+ readonly "notebook.upload.error.tooManyFiles": string;
83
+ readonly "notebook.overwrite.modal.title": string;
84
+ readonly "notebook.overwrite.modal.description": string;
85
+ readonly "notebook.overwrite.modal.action": string;
62
86
  readonly "conversation.delete.confirm.title": string;
63
87
  readonly "conversation.delete.confirm.message": string;
64
88
  readonly "conversation.delete.confirm.action": string;
@@ -70,10 +94,18 @@ declare const lightspeedTranslationRef: _backstage_frontend_plugin_api.Translati
70
94
  readonly "permission.subject.plugin": string;
71
95
  readonly "permission.subject.notebooks": string;
72
96
  readonly "permission.notebooks.goBack": string;
97
+ readonly "lcore.notConfigured.title": string;
98
+ readonly "lcore.notConfigured.description": string;
99
+ readonly "lcore.notConfigured.developerLightspeedDocs": string;
100
+ readonly "lcore.notConfigured.backendDocs": string;
101
+ readonly "lcore.loadError.title": string;
102
+ readonly "lcore.loadError.description": string;
73
103
  readonly "footer.accuracy.label": string;
74
104
  readonly "common.cancel": string;
75
105
  readonly "common.close": string;
76
106
  readonly "common.readMore": string;
107
+ readonly "common.retry": string;
108
+ readonly "common.loading": string;
77
109
  readonly "common.noSearchResults": string;
78
110
  readonly "menu.newConversation": string;
79
111
  readonly "chatbox.header.title": string;
@@ -8,6 +8,25 @@ class NotebooksApiClient {
8
8
  async getBaseUrl() {
9
9
  return `${this.configApi.getString("backend.baseUrl")}/api/lightspeed/ai-notebooks`;
10
10
  }
11
+ async handleResponseError(response) {
12
+ let errorMessage = `failed to fetch data, status ${response.status}: ${response.statusText}`;
13
+ try {
14
+ const errorText = await response.text();
15
+ if (errorText) {
16
+ try {
17
+ const errorBody = JSON.parse(errorText);
18
+ if (errorBody?.error) {
19
+ errorMessage = errorBody.error;
20
+ }
21
+ } catch {
22
+ errorMessage = errorText;
23
+ }
24
+ }
25
+ } catch (e) {
26
+ console.warn(e);
27
+ }
28
+ return errorMessage;
29
+ }
11
30
  async fetchJson(url, init) {
12
31
  const response = await this.fetchApi.fetch(url, {
13
32
  headers: {
@@ -16,23 +35,7 @@ class NotebooksApiClient {
16
35
  ...init
17
36
  });
18
37
  if (!response.ok) {
19
- let errorMessage = `failed to fetch data, status ${response.status}: ${response.statusText}`;
20
- try {
21
- const errorText = await response.text();
22
- if (errorText) {
23
- try {
24
- const errorBody = JSON.parse(errorText);
25
- if (errorBody?.error) {
26
- errorMessage = errorBody.error;
27
- }
28
- } catch {
29
- errorMessage = errorText;
30
- }
31
- }
32
- } catch (e) {
33
- console.warn(e);
34
- }
35
- throw new Error(errorMessage);
38
+ throw new Error(await this.handleResponseError(response));
36
39
  }
37
40
  const text = await response.text();
38
41
  if (!text) {
@@ -40,6 +43,34 @@ class NotebooksApiClient {
40
43
  }
41
44
  return JSON.parse(text);
42
45
  }
46
+ async fetchFormData(url, formData, method = "PUT") {
47
+ const response = await this.fetchApi.fetch(url, {
48
+ method,
49
+ body: formData
50
+ });
51
+ if (!response.ok && response.status !== 202) {
52
+ throw new Error(await this.handleResponseError(response));
53
+ }
54
+ const text = await response.text();
55
+ if (!text) {
56
+ return {};
57
+ }
58
+ return JSON.parse(text);
59
+ }
60
+ async createSession(name, description) {
61
+ const baseUrl = await this.getBaseUrl();
62
+ const response = await this.fetchJson(
63
+ `${baseUrl}/v1/sessions`,
64
+ {
65
+ method: "POST",
66
+ body: JSON.stringify({ name, description })
67
+ }
68
+ );
69
+ if (!response.session) {
70
+ throw new Error(response.error ?? "Failed to create session");
71
+ }
72
+ return response.session;
73
+ }
43
74
  async listSessions() {
44
75
  const baseUrl = await this.getBaseUrl();
45
76
  const response = await this.fetchJson(
@@ -66,6 +97,46 @@ class NotebooksApiClient {
66
97
  }
67
98
  );
68
99
  }
100
+ async uploadDocument(sessionId, file, fileType, title, newTitle) {
101
+ const baseUrl = await this.getBaseUrl();
102
+ const formData = new FormData();
103
+ formData.append("file", file);
104
+ formData.append("fileType", fileType);
105
+ formData.append("title", title);
106
+ if (newTitle) {
107
+ formData.append("newTitle", newTitle);
108
+ }
109
+ return this.fetchFormData(
110
+ `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`,
111
+ formData
112
+ );
113
+ }
114
+ async listDocuments(sessionId) {
115
+ const baseUrl = await this.getBaseUrl();
116
+ const response = await this.fetchJson(
117
+ `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`
118
+ );
119
+ const docs = response?.documents ?? [];
120
+ return docs.map((doc) => ({
121
+ ...doc,
122
+ title: doc.title ?? doc.document_id
123
+ }));
124
+ }
125
+ async deleteDocument(sessionId, documentId) {
126
+ const baseUrl = await this.getBaseUrl();
127
+ await this.fetchJson(
128
+ `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}`,
129
+ {
130
+ method: "DELETE"
131
+ }
132
+ );
133
+ }
134
+ async getDocumentStatus(sessionId, documentId) {
135
+ const baseUrl = await this.getBaseUrl();
136
+ return this.fetchJson(
137
+ `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}/status`
138
+ );
139
+ }
69
140
  }
70
141
 
71
142
  export { NotebooksApiClient };
@@ -1 +1 @@
1
- {"version":3,"file":"NotebooksApiClient.esm.js","sources":["../../src/api/NotebooksApiClient.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 { NotebookSession } from '../types';\nimport { NotebooksAPI } from './notebooksApi';\n\n/**\n * @public\n * AI Notebooks API client options\n */\nexport type NotebooksOptions = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\n/**\n * @public\n * AI Notebooks API client implementation\n */\nexport class NotebooksApiClient implements NotebooksAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: NotebooksOptions) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed/ai-notebooks`;\n }\n\n private async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n ...init,\n });\n\n if (!response.ok) {\n let errorMessage = `failed to fetch data, status ${response.status}: ${response.statusText}`;\n try {\n const errorText = await response.text();\n if (errorText) {\n try {\n const errorBody = JSON.parse(errorText);\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch {\n errorMessage = errorText;\n }\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n throw new Error(errorMessage);\n }\n\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n return JSON.parse(text) as T;\n }\n\n async listSessions() {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<{ sessions?: NotebookSession[] }>(\n `${baseUrl}/v1/sessions`,\n );\n return response?.sessions ?? [];\n }\n\n async renameSession(sessionId: string, name: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'PUT',\n body: JSON.stringify({ name }),\n },\n );\n }\n\n async deleteSession(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'DELETE',\n },\n );\n }\n}\n"],"names":[],"mappings":"AAkCO,MAAM,kBAA2C,CAAA;AAAA,EACrC,SAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAA2B,EAAA;AACrC,IAAA,IAAA,CAAK,YAAY,OAAQ,CAAA,SAAA;AACzB,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA;AAAA;AAC1B,EAEA,MAAM,UAAa,GAAA;AACjB,IAAA,OAAO,CAAG,EAAA,IAAA,CAAK,SAAU,CAAA,SAAA,CAAU,iBAAiB,CAAC,CAAA,4BAAA,CAAA;AAAA;AACvD,EAEA,MAAc,SAAa,CAAA,GAAA,EAAa,IAAgC,EAAA;AACtE,IAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,QAAA,CAAS,MAAM,GAAK,EAAA;AAAA,MAC9C,OAAS,EAAA;AAAA,QACP,cAAgB,EAAA;AAAA,OAClB;AAAA,MACA,GAAG;AAAA,KACJ,CAAA;AAED,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAA,IAAI,eAAe,CAAgC,6BAAA,EAAA,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA,CAAA;AAC1F,MAAI,IAAA;AACF,QAAM,MAAA,SAAA,GAAY,MAAM,QAAA,CAAS,IAAK,EAAA;AACtC,QAAA,IAAI,SAAW,EAAA;AACb,UAAI,IAAA;AACF,YAAM,MAAA,SAAA,GAAY,IAAK,CAAA,KAAA,CAAM,SAAS,CAAA;AACtC,YAAA,IAAI,WAAW,KAAO,EAAA;AACpB,cAAA,YAAA,GAAe,SAAU,CAAA,KAAA;AAAA;AAC3B,WACM,CAAA,MAAA;AACN,YAAe,YAAA,GAAA,SAAA;AAAA;AACjB;AACF,eACO,CAAG,EAAA;AAEV,QAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAEhB,MAAM,MAAA,IAAI,MAAM,YAAY,CAAA;AAAA;AAG9B,IAAM,MAAA,IAAA,GAAO,MAAM,QAAA,CAAS,IAAK,EAAA;AACjC,IAAA,IAAI,CAAC,IAAM,EAAA;AACT,MAAA,OAAO,EAAC;AAAA;AAEV,IAAO,OAAA,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA;AACxB,EAEA,MAAM,YAAe,GAAA;AACnB,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AACtC,IAAM,MAAA,QAAA,GAAW,MAAM,IAAK,CAAA,SAAA;AAAA,MAC1B,GAAG,OAAO,CAAA,YAAA;AAAA,KACZ;AACA,IAAO,OAAA,QAAA,EAAU,YAAY,EAAC;AAAA;AAChC,EAEA,MAAM,aAAc,CAAA,SAAA,EAAmB,IAAc,EAAA;AACnD,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AACtC,IAAA,MAAM,IAAK,CAAA,SAAA;AAAA,MACT,CAAG,EAAA,OAAO,CAAgB,aAAA,EAAA,kBAAA,CAAmB,SAAS,CAAC,CAAA,CAAA;AAAA,MACvD;AAAA,QACE,MAAQ,EAAA,KAAA;AAAA,QACR,IAAM,EAAA,IAAA,CAAK,SAAU,CAAA,EAAE,MAAM;AAAA;AAC/B,KACF;AAAA;AACF,EAEA,MAAM,cAAc,SAAmB,EAAA;AACrC,IAAM,MAAA,OAAA,GAAU,MAAM,IAAA,CAAK,UAAW,EAAA;AACtC,IAAA,MAAM,IAAK,CAAA,SAAA;AAAA,MACT,CAAG,EAAA,OAAO,CAAgB,aAAA,EAAA,kBAAA,CAAmB,SAAS,CAAC,CAAA,CAAA;AAAA,MACvD;AAAA,QACE,MAAQ,EAAA;AAAA;AACV,KACF;AAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"NotebooksApiClient.esm.js","sources":["../../src/api/NotebooksApiClient.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 DocumentListResponse,\n DocumentStatus,\n NotebookSession,\n SessionResponse,\n UploadDocumentResponse,\n} from '../types';\nimport { NotebooksAPI } from './notebooksApi';\n\n/**\n * @public\n * AI Notebooks API client options\n */\nexport type NotebooksOptions = {\n configApi: ConfigApi;\n fetchApi: FetchApi;\n};\n\n/**\n * @public\n * AI Notebooks API client implementation\n */\nexport class NotebooksApiClient implements NotebooksAPI {\n private readonly configApi: ConfigApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: NotebooksOptions) {\n this.configApi = options.configApi;\n this.fetchApi = options.fetchApi;\n }\n\n async getBaseUrl() {\n return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed/ai-notebooks`;\n }\n\n private async handleResponseError(response: Response): Promise<string> {\n let errorMessage = `failed to fetch data, status ${response.status}: ${response.statusText}`;\n try {\n const errorText = await response.text();\n if (errorText) {\n try {\n const errorBody = JSON.parse(errorText);\n if (errorBody?.error) {\n errorMessage = errorBody.error;\n }\n } catch {\n errorMessage = errorText;\n }\n }\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(e);\n }\n return errorMessage;\n }\n\n private async fetchJson<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await this.fetchApi.fetch(url, {\n headers: {\n 'Content-Type': 'application/json',\n },\n ...init,\n });\n\n if (!response.ok) {\n throw new Error(await this.handleResponseError(response));\n }\n\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n return JSON.parse(text) as T;\n }\n\n private async fetchFormData<T>(\n url: string,\n formData: FormData,\n method: string = 'PUT',\n ): Promise<T> {\n const response = await this.fetchApi.fetch(url, {\n method,\n body: formData,\n });\n\n if (!response.ok && response.status !== 202) {\n throw new Error(await this.handleResponseError(response));\n }\n\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n return JSON.parse(text) as T;\n }\n\n async createSession(name: string, description?: string) {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<SessionResponse>(\n `${baseUrl}/v1/sessions`,\n {\n method: 'POST',\n body: JSON.stringify({ name, description }),\n },\n );\n if (!response.session) {\n throw new Error(response.error ?? 'Failed to create session');\n }\n return response.session;\n }\n\n async listSessions() {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<{ sessions?: NotebookSession[] }>(\n `${baseUrl}/v1/sessions`,\n );\n return response?.sessions ?? [];\n }\n\n async renameSession(sessionId: string, name: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'PUT',\n body: JSON.stringify({ name }),\n },\n );\n }\n\n async deleteSession(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}`,\n {\n method: 'DELETE',\n },\n );\n }\n\n async uploadDocument(\n sessionId: string,\n file: File,\n fileType: string,\n title: string,\n newTitle?: string,\n ) {\n const baseUrl = await this.getBaseUrl();\n const formData = new FormData();\n formData.append('file', file);\n formData.append('fileType', fileType);\n formData.append('title', title);\n if (newTitle) {\n formData.append('newTitle', newTitle);\n }\n return this.fetchFormData<UploadDocumentResponse>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`,\n formData,\n );\n }\n\n async listDocuments(sessionId: string) {\n const baseUrl = await this.getBaseUrl();\n const response = await this.fetchJson<DocumentListResponse>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents`,\n );\n const docs = response?.documents ?? [];\n return docs.map(doc => ({\n ...doc,\n title: (doc as any).title ?? doc.document_id,\n }));\n }\n\n async deleteDocument(sessionId: string, documentId: string) {\n const baseUrl = await this.getBaseUrl();\n await this.fetchJson(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}`,\n {\n method: 'DELETE',\n },\n );\n }\n\n async getDocumentStatus(sessionId: string, documentId: string) {\n const baseUrl = await this.getBaseUrl();\n return this.fetchJson<DocumentStatus>(\n `${baseUrl}/v1/sessions/${encodeURIComponent(sessionId)}/documents/${encodeURIComponent(documentId)}/status`,\n );\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"notebooksApi.esm.js","sources":["../../src/api/notebooksApi.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, type ApiRef } from '@backstage/core-plugin-api';\n\nimport { NotebookSession } from '../types';\n\n/**\n * @public\n * AI Notebooks API\n */\nexport type NotebooksAPI = {\n listSessions: () => Promise<NotebookSession[]>;\n renameSession: (sessionId: string, name: string) => Promise<void>;\n deleteSession: (sessionId: string) => Promise<void>;\n};\n\n/**\n * @public\n * AI Notebooks API interface\n */\nexport const notebooksApiRef: ApiRef<NotebooksAPI> = createApiRef<NotebooksAPI>(\n {\n id: 'plugin.lightspeed.notebooks.service',\n },\n);\n"],"names":[],"mappings":";;AAkCO,MAAM,eAAwC,GAAA,YAAA;AAAA,EACnD;AAAA,IACE,EAAI,EAAA;AAAA;AAER;;;;"}
1
+ {"version":3,"file":"notebooksApi.esm.js","sources":["../../src/api/notebooksApi.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, type ApiRef } from '@backstage/core-plugin-api';\n\nimport {\n DocumentStatus,\n NotebookSession,\n SessionDocument,\n UploadDocumentResponse,\n} from '../types';\n\n/**\n * @public\n * AI Notebooks API\n */\nexport type NotebooksAPI = {\n createSession: (\n name: string,\n description?: string,\n ) => Promise<NotebookSession>;\n listSessions: () => Promise<NotebookSession[]>;\n renameSession: (sessionId: string, name: string) => Promise<void>;\n deleteSession: (sessionId: string) => Promise<void>;\n uploadDocument: (\n sessionId: string,\n file: File,\n fileType: string,\n title: string,\n newTitle?: string,\n ) => Promise<UploadDocumentResponse>;\n listDocuments: (sessionId: string) => Promise<SessionDocument[]>;\n deleteDocument: (sessionId: string, documentId: string) => Promise<void>;\n getDocumentStatus: (\n sessionId: string,\n documentId: string,\n ) => Promise<DocumentStatus>;\n};\n\n/**\n * @public\n * AI Notebooks API interface\n */\nexport const notebooksApiRef: ApiRef<NotebooksAPI> = createApiRef<NotebooksAPI>(\n {\n id: 'plugin.lightspeed.notebooks.service',\n },\n);\n"],"names":[],"mappings":";;AAwDO,MAAM,eAAwC,GAAA,YAAA;AAAA,EACnD;AAAA,IACE,EAAI,EAAA;AAAA;AAER;;;;"}
@@ -1,12 +1,13 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect } from 'react';
2
+ import { useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect } from 'react';
3
+ import { useNavigate, useMatch } from 'react-router-dom';
3
4
  import { makeStyles, Button } from '@material-ui/core';
4
5
  import { ChatbotDisplayMode, ChatbotContent, ChatbotFooter, MessageBar, ChatbotFootnote, Settings, Chatbot, ChatbotHeader, ChatbotHeaderMain, ChatbotHeaderMenu, ChatbotHeaderTitle, FileDropZone, ChatbotAlert } from '@patternfly/chatbot';
5
6
  import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
6
7
  import { DropdownItem, Tooltip, MenuToggle, Select, SelectList, SelectOption, AlertGroup, Alert, AlertVariant, AlertActionCloseButton, Title, Tabs, Tab } from '@patternfly/react-core';
7
8
  import { SortAmountDownAltIcon, SortAmountDownIcon, PlusIcon, SearchIcon } from '@patternfly/react-icons';
8
9
  import { useQueryClient } from '@tanstack/react-query';
9
- import { TEMP_CONVERSATION_ID, supportedFileTypes } from '../const.esm.js';
10
+ import { UNTITLED_NOTEBOOK_NAME, TEMP_CONVERSATION_ID, supportedFileTypes } from '../const.esm.js';
10
11
  import '@backstage/core-plugin-api';
11
12
  import '../api/api.esm.js';
12
13
  import { useBackstageUserIdentity } from '../hooks/useBackstageUserIdentity.esm.js';
@@ -24,6 +25,8 @@ import { usePinnedChatsSettings } from '../hooks/usePinnedChatsSettings.esm.js';
24
25
  import { useSortSettings } from '../hooks/useSortSettings.esm.js';
25
26
  import { useStopConversation } from '../hooks/useStopConversation.esm.js';
26
27
  import { useTranslation } from '../hooks/useTranslation.esm.js';
28
+ import { useCreateNotebook } from '../hooks/notebooks/useCreateNotebook.esm.js';
29
+ import { useNotebookDocuments } from '../hooks/notebooks/useNotebookDocuments.esm.js';
27
30
  import { useLightspeedDrawerContext } from '../hooks/useLightspeedDrawerContext.esm.js';
28
31
  import { useLightspeedUpdatePermission } from '../hooks/useLightspeedUpdatePermission.esm.js';
29
32
  import { useWelcomePrompts } from '../hooks/useWelcomePrompts.esm.js';
@@ -38,6 +41,7 @@ import { LightspeedChatBoxHeader } from './LightspeedChatBoxHeader.esm.js';
38
41
  import { McpServersSettings } from './McpServersSettings.esm.js';
39
42
  import { DeleteNotebookModal } from './notebooks/DeleteNotebookModal.esm.js';
40
43
  import { NotebooksTab } from './notebooks/NotebooksTab.esm.js';
44
+ import { NotebookView } from './notebooks/NotebookView.esm.js';
41
45
  import { RenameNotebookModal } from './notebooks/RenameNotebookModal.esm.js';
42
46
  import PermissionRequiredState from './PermissionRequiredState.esm.js';
43
47
  import { RenameConversationModal } from './RenameConversationModal.esm.js';
@@ -58,6 +62,13 @@ const useStyles = makeStyles((theme) => ({
58
62
  errorContainer: {
59
63
  padding: theme.spacing(3)
60
64
  },
65
+ drawerFileDropZone: {
66
+ gap: 0,
67
+ rowGap: 0,
68
+ columnGap: 0,
69
+ "--pf-v6-c-multiple-file-upload--Gap": "0",
70
+ "--pf-v5-c-multiple-file-upload--Gap": "0"
71
+ },
61
72
  headerMenu: {
62
73
  // align hamburger icon with title
63
74
  "& .pf-v6-c-button": {
@@ -162,7 +173,13 @@ const useStyles = makeStyles((theme) => ({
162
173
  notebookCard: {
163
174
  borderRadius: theme.spacing(1.5),
164
175
  display: "flex",
165
- flexDirection: "column"
176
+ flexDirection: "column",
177
+ "&:hover": {
178
+ borderColor: "var(--pf-t--global--border--color--hover)",
179
+ borderWidth: "1px",
180
+ borderStyle: "solid",
181
+ cursor: "pointer"
182
+ }
166
183
  },
167
184
  notebookCardHeader: {
168
185
  padding: theme.spacing(2),
@@ -362,10 +379,14 @@ const LightspeedChat = ({
362
379
  const isMobile = useIsMobile();
363
380
  const classes = useStyles();
364
381
  const { t } = useTranslation();
382
+ const navigate = useNavigate();
383
+ const notebooksRouteMatch = useMatch("/lightspeed/notebooks");
365
384
  const user = useBackstageUserIdentity();
366
385
  const [filterValue, setFilterValue] = useState("");
367
386
  const [announcement, setAnnouncement] = useState("");
368
- const [activeTab, setActiveTab] = useState(0);
387
+ const [activeTab, setActiveTab] = useState(
388
+ notebooksRouteMatch ? 1 : 0
389
+ );
369
390
  const { allowed: hasNotebooksAccess, loading: notebooksPermissionLoading } = useLightspeedNotebooksPermission();
370
391
  const notebooksPermissionResolved = !notebooksPermissionLoading && hasNotebooksAccess;
371
392
  const { data: notebooks = [], refetch: refetchNotebooks } = useNotebookSessions(activeTab === 1 && notebooksPermissionResolved);
@@ -375,9 +396,16 @@ const LightspeedChat = ({
375
396
  );
376
397
  const [renameNotebookId, setRenameNotebookId] = useState(null);
377
398
  const [deleteNotebookId, setDeleteNotebookId] = useState(null);
399
+ const [activeNotebook, setActiveNotebook] = useState(
400
+ null
401
+ );
378
402
  const [notebookAlerts, setNotebookAlerts] = useState(
379
403
  []
380
404
  );
405
+ const createNotebookMutation = useCreateNotebook();
406
+ const { data: notebookDocuments = [] } = useNotebookDocuments(
407
+ activeNotebook?.session_id
408
+ );
381
409
  const [conversationId, setConversationId] = useState("");
382
410
  const [requestId, setRequestId] = useState("");
383
411
  const [newChatCreated, setNewChatCreated] = useState(false);
@@ -409,10 +437,36 @@ const LightspeedChat = ({
409
437
  const handleNotebookTabSelect = (_event, tabIndex) => {
410
438
  const nextTab = Number(tabIndex);
411
439
  setActiveTab(nextTab);
412
- if (nextTab === 1 && notebooksPermissionResolved) {
413
- refetchNotebooks();
440
+ if (nextTab === 1) {
441
+ navigate("/lightspeed/notebooks");
442
+ if (notebooksPermissionResolved) {
443
+ refetchNotebooks();
444
+ }
445
+ } else {
446
+ navigate(
447
+ routeConversationId ? `/lightspeed/conversation/${routeConversationId}` : "/lightspeed"
448
+ );
414
449
  }
415
450
  };
451
+ const handleCreateNotebook = useCallback(() => {
452
+ createNotebookMutation.mutate(
453
+ { name: UNTITLED_NOTEBOOK_NAME },
454
+ {
455
+ onSuccess: (session) => {
456
+ setActiveNotebook(session);
457
+ }
458
+ }
459
+ );
460
+ }, [createNotebookMutation]);
461
+ const handleCloseNotebook = useCallback(() => {
462
+ setActiveNotebook(null);
463
+ refetchNotebooks();
464
+ }, [refetchNotebooks]);
465
+ const handleRemoveNotebookAlert = (key) => {
466
+ setNotebookAlerts(
467
+ (prevAlerts) => prevAlerts.filter((alert) => alert.key !== key)
468
+ );
469
+ };
416
470
  const handleNotebookDeleted = () => {
417
471
  const key = Date.now();
418
472
  setNotebookAlerts((prevAlerts) => [
@@ -420,11 +474,6 @@ const LightspeedChat = ({
420
474
  ...prevAlerts
421
475
  ]);
422
476
  };
423
- const handleRemoveNotebookAlert = (key) => {
424
- setNotebookAlerts(
425
- (prevAlerts) => prevAlerts.filter((alert) => alert.key !== key)
426
- );
427
- };
428
477
  useEffect(() => {
429
478
  if (!isMobile && isFullscreenMode) {
430
479
  setIsChatHistoryDrawerOpen(true);
@@ -967,9 +1016,6 @@ const LightspeedChat = ({
967
1016
  classes.sortDropdown
968
1017
  ]
969
1018
  );
970
- const getDocumentsCount = (documentIds) => {
971
- return Array.isArray(documentIds) ? documentIds.length : 0;
972
- };
973
1019
  const handleAttach = (data, event) => {
974
1020
  if ("preventDefault" in event && typeof event.preventDefault === "function") {
975
1021
  event.preventDefault();
@@ -1128,6 +1174,8 @@ const LightspeedChat = ({
1128
1174
  variant: AlertVariant[variant ?? "success"],
1129
1175
  title,
1130
1176
  className: classes.toastAlert,
1177
+ timeout: 8e3,
1178
+ onTimeout: () => handleRemoveNotebookAlert(key),
1131
1179
  actionClose: /* @__PURE__ */ jsx(
1132
1180
  AlertActionCloseButton,
1133
1181
  {
@@ -1276,6 +1324,7 @@ const LightspeedChat = ({
1276
1324
  drawerContent: /* @__PURE__ */ jsxs(
1277
1325
  FileDropZone,
1278
1326
  {
1327
+ className: classes.drawerFileDropZone,
1279
1328
  onFileDrop: (e, data) => handleAttach(data, e),
1280
1329
  displayMode: ChatbotDisplayMode.embedded,
1281
1330
  infoText: t("chatbox.fileUpload.infoText"),
@@ -1299,7 +1348,16 @@ const LightspeedChat = ({
1299
1348
  )
1300
1349
  }
1301
1350
  ),
1302
- showNotebooksPanel && !notebooksPermissionLoading && hasNotebooksAccess && /* @__PURE__ */ jsx(
1351
+ showNotebooksPanel && !notebooksPermissionLoading && hasNotebooksAccess && activeNotebook && /* @__PURE__ */ jsx(
1352
+ NotebookView,
1353
+ {
1354
+ sessionId: activeNotebook.session_id,
1355
+ notebookName: activeNotebook.name,
1356
+ documents: notebookDocuments,
1357
+ onClose: handleCloseNotebook
1358
+ }
1359
+ ),
1360
+ showNotebooksPanel && !notebooksPermissionLoading && hasNotebooksAccess && !activeNotebook && /* @__PURE__ */ jsx(
1303
1361
  NotebooksTab,
1304
1362
  {
1305
1363
  notebooks,
@@ -1307,10 +1365,11 @@ const LightspeedChat = ({
1307
1365
  classes,
1308
1366
  openNotebookMenuId,
1309
1367
  setOpenNotebookMenuId,
1368
+ onSelectNotebook: setActiveNotebook,
1310
1369
  onRename: setRenameNotebookId,
1311
1370
  onDelete: setDeleteNotebookId,
1312
- t,
1313
- getDocumentsCount
1371
+ onCreateNotebook: handleCreateNotebook,
1372
+ t
1314
1373
  }
1315
1374
  ),
1316
1375
  showNotebooksPanel && !notebooksPermissionLoading && !hasNotebooksAccess && /* @__PURE__ */ jsx(