@red-hat-developer-hub/backstage-plugin-intelligent-assistant 3.2.0 → 4.1.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 (63) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +7 -10
  3. package/config.d.ts +27 -0
  4. package/dist/alpha.d.ts +21 -7
  5. package/dist/api/NotebooksApiClient.esm.js +10 -0
  6. package/dist/api/NotebooksApiClient.esm.js.map +1 -1
  7. package/dist/api/notebooksApi.esm.js.map +1 -1
  8. package/dist/components/LightSpeedChat.esm.js +50 -37
  9. package/dist/components/LightSpeedChat.esm.js.map +1 -1
  10. package/dist/components/LightspeedChatContainer.esm.js +7 -5
  11. package/dist/components/LightspeedChatContainer.esm.js.map +1 -1
  12. package/dist/components/McpServersSettings.esm.js +4 -3
  13. package/dist/components/McpServersSettings.esm.js.map +1 -1
  14. package/dist/components/notebooks/AddDocumentModal.esm.js +181 -66
  15. package/dist/components/notebooks/AddDocumentModal.esm.js.map +1 -1
  16. package/dist/components/notebooks/DocumentSidebar.esm.js +258 -27
  17. package/dist/components/notebooks/DocumentSidebar.esm.js.map +1 -1
  18. package/dist/components/notebooks/FileListItem.esm.js +15 -9
  19. package/dist/components/notebooks/FileListItem.esm.js.map +1 -1
  20. package/dist/components/notebooks/NotebookCard.esm.js +71 -14
  21. package/dist/components/notebooks/NotebookCard.esm.js.map +1 -1
  22. package/dist/components/notebooks/NotebookView.esm.js +202 -42
  23. package/dist/components/notebooks/NotebookView.esm.js.map +1 -1
  24. package/dist/components/notebooks/NotebooksTab.esm.js.map +1 -1
  25. package/dist/components/notebooks/OverwriteConfirmModal.esm.js +122 -42
  26. package/dist/components/notebooks/OverwriteConfirmModal.esm.js.map +1 -1
  27. package/dist/components/notebooks/UploadResourceScreen.esm.js +0 -1
  28. package/dist/components/notebooks/UploadResourceScreen.esm.js.map +1 -1
  29. package/dist/const.esm.js +2 -1
  30. package/dist/const.esm.js.map +1 -1
  31. package/dist/hooks/notebooks/useInlineEdit.esm.js +60 -0
  32. package/dist/hooks/notebooks/useInlineEdit.esm.js.map +1 -0
  33. package/dist/hooks/notebooks/useLightspeedNotebooksPermission.esm.js +4 -3
  34. package/dist/hooks/notebooks/useLightspeedNotebooksPermission.esm.js.map +1 -1
  35. package/dist/hooks/notebooks/useRenameDocument.esm.js +50 -0
  36. package/dist/hooks/notebooks/useRenameDocument.esm.js.map +1 -0
  37. package/dist/hooks/notebooks/useRenameNotebook.esm.js +48 -1
  38. package/dist/hooks/notebooks/useRenameNotebook.esm.js.map +1 -1
  39. package/dist/hooks/notebooks/useRenameNotebookWithAlert.esm.js +34 -0
  40. package/dist/hooks/notebooks/useRenameNotebookWithAlert.esm.js.map +1 -0
  41. package/dist/hooks/useLightspeedDeletePermission.esm.js +2 -2
  42. package/dist/hooks/useLightspeedDeletePermission.esm.js.map +1 -1
  43. package/dist/hooks/useLightspeedUpdatePermission.esm.js +2 -2
  44. package/dist/hooks/useLightspeedUpdatePermission.esm.js.map +1 -1
  45. package/dist/hooks/useLightspeedViewPermission.esm.js +6 -4
  46. package/dist/hooks/useLightspeedViewPermission.esm.js.map +1 -1
  47. package/dist/translations/de.esm.js +31 -17
  48. package/dist/translations/de.esm.js.map +1 -1
  49. package/dist/translations/es.esm.js +31 -17
  50. package/dist/translations/es.esm.js.map +1 -1
  51. package/dist/translations/fr.esm.js +31 -17
  52. package/dist/translations/fr.esm.js.map +1 -1
  53. package/dist/translations/it.esm.js +31 -17
  54. package/dist/translations/it.esm.js.map +1 -1
  55. package/dist/translations/ja.esm.js +31 -17
  56. package/dist/translations/ja.esm.js.map +1 -1
  57. package/dist/translations/ref.esm.js +32 -18
  58. package/dist/translations/ref.esm.js.map +1 -1
  59. package/dist/utils/notebook-upload-runner.esm.js +16 -0
  60. package/dist/utils/notebook-upload-runner.esm.js.map +1 -0
  61. package/package.json +4 -3
  62. package/dist/components/notebooks/RenameNotebookModal.esm.js +0 -187
  63. package/dist/components/notebooks/RenameNotebookModal.esm.js.map +0 -1
@@ -1,10 +1,24 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useCallback } from 'react';
2
3
  import Typography from '@mui/material/Typography';
3
- import { Card, CardHeader, CardTitle, Dropdown, DropdownList, DropdownItem, MenuToggle, CardBody } from '@patternfly/react-core';
4
- import { EllipsisVIcon } from '@patternfly/react-icons';
5
- import { CatalogIcon } from '@patternfly/react-icons/dist/esm/icons';
4
+ import { Card, CardHeader, CardTitle, TextInput, Dropdown, DropdownList, DropdownItem, MenuToggle, CardBody } from '@patternfly/react-core';
5
+ import { CatalogIcon, PenIcon, TrashIcon, EllipsisVIcon } from '@patternfly/react-icons';
6
+ import { useInlineEdit } from '../../hooks/notebooks/useInlineEdit.esm.js';
6
7
  import { formatUpdatedLabel } from '../../utils/notebooks-utils.esm.js';
7
8
 
9
+ const titleInputStyle = {
10
+ "--pf-v6-c-form-control--before--BorderStyle": "none",
11
+ "--pf-v6-c-form-control--after--BorderColor": "var(--pf-t--global--text--color--brand--default)",
12
+ "--pf-v6-c-form-control--FontSize": "1.25rem",
13
+ "--pf-v6-c-form-control--PaddingBlockStart": "2px",
14
+ "--pf-v6-c-form-control--PaddingBlockEnd": "2px",
15
+ "--pf-v6-c-form-control--PaddingInlineStart": "6px",
16
+ "--pf-v6-c-form-control--PaddingInlineEnd": "6px",
17
+ "--pf-v6-c-form-control--OutlineOffset": "0",
18
+ width: "100%",
19
+ minWidth: 0,
20
+ fontWeight: 500
21
+ };
8
22
  const NotebookCard = ({
9
23
  notebook,
10
24
  classes,
@@ -16,8 +30,26 @@ const NotebookCard = ({
16
30
  t
17
31
  }) => {
18
32
  const isMenuOpen = openNotebookMenuId === notebook.session_id;
33
+ const {
34
+ isEditing,
35
+ editValue: editName,
36
+ setEditValue: setEditName,
37
+ inputRef,
38
+ startEditing,
39
+ save: saveRename,
40
+ handleKeyDown
41
+ } = useInlineEdit({
42
+ currentName: notebook.name,
43
+ onSave: (newName) => onRename(notebook.session_id, newName),
44
+ onStart: () => setOpenNotebookMenuId(null)
45
+ });
46
+ const handleCardClick = useCallback(() => {
47
+ if (!isEditing) {
48
+ onClick(notebook);
49
+ }
50
+ }, [isEditing, onClick, notebook]);
19
51
  const handleCardKeyDown = (e) => {
20
- if (e.key === "Enter" || e.key === " ") {
52
+ if (!isEditing && (e.key === "Enter" || e.key === " ")) {
21
53
  e.preventDefault();
22
54
  onClick(notebook);
23
55
  }
@@ -28,8 +60,10 @@ const NotebookCard = ({
28
60
  className: classes.notebookCard,
29
61
  component: "div",
30
62
  tabIndex: 0,
31
- "aria-label": t("notebooks.card.openAria", { name: notebook.name }),
32
- onClick: () => onClick(notebook),
63
+ "aria-label": t("notebooks.card.openAria", {
64
+ name: notebook.name
65
+ }),
66
+ onClick: handleCardClick,
33
67
  onKeyDown: handleCardKeyDown,
34
68
  children: [
35
69
  /* @__PURE__ */ jsx(
@@ -69,10 +103,10 @@ const NotebookCard = ({
69
103
  DropdownItem,
70
104
  {
71
105
  className: classes.notebookDropdownItem,
106
+ icon: /* @__PURE__ */ jsx(PenIcon, {}),
72
107
  onClick: (event) => {
73
108
  event.stopPropagation();
74
- onRename(notebook.session_id);
75
- setOpenNotebookMenuId(null);
109
+ startEditing();
76
110
  },
77
111
  children: t("notebooks.actions.rename")
78
112
  }
@@ -81,6 +115,7 @@ const NotebookCard = ({
81
115
  DropdownItem,
82
116
  {
83
117
  className: classes.notebookDropdownItem,
118
+ icon: /* @__PURE__ */ jsx(TrashIcon, {}),
84
119
  onClick: (event) => {
85
120
  event.stopPropagation();
86
121
  onDelete(notebook.session_id);
@@ -96,17 +131,39 @@ const NotebookCard = ({
96
131
  },
97
132
  children: /* @__PURE__ */ jsxs(CardTitle, { className: classes.notebookTitle, children: [
98
133
  /* @__PURE__ */ jsx(CatalogIcon, {}),
99
- /* @__PURE__ */ jsx(Typography, { component: "span", className: classes.notebookTitleText, children: notebook.name })
134
+ isEditing ? /* @__PURE__ */ jsx(
135
+ TextInput,
136
+ {
137
+ ref: inputRef,
138
+ style: titleInputStyle,
139
+ value: editName,
140
+ onChange: (_event, value) => setEditName(value),
141
+ onBlur: saveRename,
142
+ onKeyDown: handleKeyDown,
143
+ "aria-label": t("notebooks.rename.inline.tooltip"),
144
+ onClick: (e) => e.stopPropagation()
145
+ }
146
+ ) : /* @__PURE__ */ jsx(
147
+ Typography,
148
+ {
149
+ component: "span",
150
+ className: classes.notebookTitleText,
151
+ title: t("notebooks.rename.inline.tooltip"),
152
+ onClick: (e) => {
153
+ e.stopPropagation();
154
+ startEditing();
155
+ },
156
+ children: notebook.name
157
+ }
158
+ )
100
159
  ] })
101
160
  }
102
161
  ),
103
162
  /* @__PURE__ */ jsx("div", { className: classes.notebookCardDivider }),
104
163
  /* @__PURE__ */ jsx(CardBody, { className: classes.notebookCardBody, children: /* @__PURE__ */ jsxs("div", { children: [
105
- /* @__PURE__ */ jsx("div", { className: classes.notebookDocuments, children: /* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
106
- notebook.document_count ?? 0,
107
- " ",
108
- t("notebooks.documents")
109
- ] }) }),
164
+ /* @__PURE__ */ jsx("div", { className: classes.notebookDocuments, children: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: t("notebooks.documents", {
165
+ count: notebook.document_count ?? 0
166
+ }) }) }),
110
167
  /* @__PURE__ */ jsx("div", { className: classes.notebookUpdated, children: /* @__PURE__ */ jsx(Typography, { variant: "caption", children: formatUpdatedLabel(notebook.updated_at, t) }) })
111
168
  ] }) })
112
169
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"NotebookCard.esm.js","sources":["../../../src/components/notebooks/NotebookCard.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 type { TranslationFunction } from '@backstage/core-plugin-api/alpha';\n\nimport Typography from '@mui/material/Typography';\nimport {\n Card,\n CardBody,\n CardHeader,\n CardTitle,\n Dropdown,\n DropdownItem,\n DropdownList,\n MenuToggle,\n} from '@patternfly/react-core';\nimport { EllipsisVIcon } from '@patternfly/react-icons';\nimport { CatalogIcon } from '@patternfly/react-icons/dist/esm/icons';\n\nimport { intelligentAssistantTranslationRef } from '../../translations/ref';\nimport { NotebookSession } from '../../types';\nimport { formatUpdatedLabel } from '../../utils/notebooks-utils';\n\ntype NotebookCardProps = {\n notebook: NotebookSession;\n classes: Record<string, string>;\n openNotebookMenuId: string | null;\n setOpenNotebookMenuId: React.Dispatch<React.SetStateAction<string | null>>;\n onClick: (notebook: NotebookSession) => void;\n onRename: (sessionId: string) => void;\n onDelete: (sessionId: string) => void;\n t: TranslationFunction<typeof intelligentAssistantTranslationRef.T>;\n};\n\nexport const NotebookCard = ({\n notebook,\n classes,\n openNotebookMenuId,\n setOpenNotebookMenuId,\n onClick,\n onRename,\n onDelete,\n t,\n}: NotebookCardProps) => {\n const isMenuOpen = openNotebookMenuId === notebook.session_id;\n\n const handleCardKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick(notebook);\n }\n };\n\n return (\n <Card\n className={classes.notebookCard}\n component=\"div\"\n tabIndex={0}\n aria-label={t('notebooks.card.openAria' as any, { name: notebook.name })}\n onClick={() => onClick(notebook)}\n onKeyDown={handleCardKeyDown}\n >\n <CardHeader\n className={classes.notebookCardHeader}\n actions={{\n actions: (\n <Dropdown\n className={classes.notebookDropdownMenu}\n isOpen={isMenuOpen}\n popperProps={{\n position: 'end',\n preventOverflow: true,\n }}\n onOpenChange={isOpen =>\n setOpenNotebookMenuId(isOpen ? notebook.session_id : null)\n }\n toggle={toggleRef => (\n <MenuToggle\n ref={toggleRef}\n variant=\"plain\"\n className={classes.notebookMenuButton}\n aria-label={t('aria.options.label')}\n isExpanded={isMenuOpen}\n onClick={event => {\n event.stopPropagation();\n setOpenNotebookMenuId(current =>\n current === notebook.session_id\n ? null\n : notebook.session_id,\n );\n }}\n >\n <EllipsisVIcon />\n </MenuToggle>\n )}\n >\n <DropdownList className={classes.notebookDropdownList}>\n <DropdownItem\n className={classes.notebookDropdownItem}\n onClick={event => {\n event.stopPropagation();\n onRename(notebook.session_id);\n setOpenNotebookMenuId(null);\n }}\n >\n {t('notebooks.actions.rename')}\n </DropdownItem>\n <DropdownItem\n className={classes.notebookDropdownItem}\n onClick={event => {\n event.stopPropagation();\n onDelete(notebook.session_id);\n setOpenNotebookMenuId(null);\n }}\n >\n {t('notebooks.actions.delete')}\n </DropdownItem>\n </DropdownList>\n </Dropdown>\n ),\n className: classes.notebookCardHeaderActions,\n }}\n >\n <CardTitle className={classes.notebookTitle}>\n <CatalogIcon />\n <Typography component=\"span\" className={classes.notebookTitleText}>\n {notebook.name}\n </Typography>\n </CardTitle>\n </CardHeader>\n <div className={classes.notebookCardDivider} />\n <CardBody className={classes.notebookCardBody}>\n <div>\n <div className={classes.notebookDocuments}>\n <Typography variant=\"body2\">\n {notebook.document_count ?? 0} {t('notebooks.documents')}\n </Typography>\n </div>\n <div className={classes.notebookUpdated}>\n <Typography variant=\"caption\">\n {formatUpdatedLabel(notebook.updated_at, t)}\n </Typography>\n </div>\n </div>\n </CardBody>\n </Card>\n );\n};\n"],"names":[],"mappings":";;;;;;;AA+CO,MAAM,eAAe,CAAC;AAAA,EAC3B,QAAA;AAAA,EACA,OAAA;AAAA,EACA,kBAAA;AAAA,EACA,qBAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,KAAyB;AACvB,EAAA,MAAM,UAAA,GAAa,uBAAuB,QAAA,CAAS,UAAA;AAEnD,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,KAA2B;AACpD,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACtC,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,IAClB;AAAA,EACF,CAAA;AAEA,EAAA,uBACE,IAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAA,CAAQ,YAAA;AAAA,MACnB,SAAA,EAAU,KAAA;AAAA,MACV,QAAA,EAAU,CAAA;AAAA,MACV,cAAY,CAAA,CAAE,yBAAA,EAAkC,EAAE,IAAA,EAAM,QAAA,CAAS,MAAM,CAAA;AAAA,MACvE,OAAA,EAAS,MAAM,OAAA,CAAQ,QAAQ,CAAA;AAAA,MAC/B,SAAA,EAAW,iBAAA;AAAA,MAEX,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,WAAW,OAAA,CAAQ,kBAAA;AAAA,YACnB,OAAA,EAAS;AAAA,cACP,OAAA,kBACE,GAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBACC,WAAW,OAAA,CAAQ,oBAAA;AAAA,kBACnB,MAAA,EAAQ,UAAA;AAAA,kBACR,WAAA,EAAa;AAAA,oBACX,QAAA,EAAU,KAAA;AAAA,oBACV,eAAA,EAAiB;AAAA,mBACnB;AAAA,kBACA,cAAc,CAAA,MAAA,KACZ,qBAAA,CAAsB,MAAA,GAAS,QAAA,CAAS,aAAa,IAAI,CAAA;AAAA,kBAE3D,QAAQ,CAAA,SAAA,qBACN,GAAA;AAAA,oBAAC,UAAA;AAAA,oBAAA;AAAA,sBACC,GAAA,EAAK,SAAA;AAAA,sBACL,OAAA,EAAQ,OAAA;AAAA,sBACR,WAAW,OAAA,CAAQ,kBAAA;AAAA,sBACnB,YAAA,EAAY,EAAE,oBAAoB,CAAA;AAAA,sBAClC,UAAA,EAAY,UAAA;AAAA,sBACZ,SAAS,CAAA,KAAA,KAAS;AAChB,wBAAA,KAAA,CAAM,eAAA,EAAgB;AACtB,wBAAA,qBAAA;AAAA,0BAAsB,CAAA,OAAA,KACpB,OAAA,KAAY,QAAA,CAAS,UAAA,GACjB,OACA,QAAA,CAAS;AAAA,yBACf;AAAA,sBACF,CAAA;AAAA,sBAEA,8BAAC,aAAA,EAAA,EAAc;AAAA;AAAA,mBACjB;AAAA,kBAGF,QAAA,kBAAA,IAAA,CAAC,YAAA,EAAA,EAAa,SAAA,EAAW,OAAA,CAAQ,oBAAA,EAC/B,QAAA,EAAA;AAAA,oCAAA,GAAA;AAAA,sBAAC,YAAA;AAAA,sBAAA;AAAA,wBACC,WAAW,OAAA,CAAQ,oBAAA;AAAA,wBACnB,SAAS,CAAA,KAAA,KAAS;AAChB,0BAAA,KAAA,CAAM,eAAA,EAAgB;AACtB,0BAAA,QAAA,CAAS,SAAS,UAAU,CAAA;AAC5B,0BAAA,qBAAA,CAAsB,IAAI,CAAA;AAAA,wBAC5B,CAAA;AAAA,wBAEC,YAAE,0BAA0B;AAAA;AAAA,qBAC/B;AAAA,oCACA,GAAA;AAAA,sBAAC,YAAA;AAAA,sBAAA;AAAA,wBACC,WAAW,OAAA,CAAQ,oBAAA;AAAA,wBACnB,SAAS,CAAA,KAAA,KAAS;AAChB,0BAAA,KAAA,CAAM,eAAA,EAAgB;AACtB,0BAAA,QAAA,CAAS,SAAS,UAAU,CAAA;AAC5B,0BAAA,qBAAA,CAAsB,IAAI,CAAA;AAAA,wBAC5B,CAAA;AAAA,wBAEC,YAAE,0BAA0B;AAAA;AAAA;AAC/B,mBAAA,EACF;AAAA;AAAA,eACF;AAAA,cAEF,WAAW,OAAA,CAAQ;AAAA,aACrB;AAAA,YAEA,QAAA,kBAAA,IAAA,CAAC,SAAA,EAAA,EAAU,SAAA,EAAW,OAAA,CAAQ,aAAA,EAC5B,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,WAAA,EAAA,EAAY,CAAA;AAAA,8BACb,GAAA,CAAC,cAAW,SAAA,EAAU,MAAA,EAAO,WAAW,OAAA,CAAQ,iBAAA,EAC7C,mBAAS,IAAA,EACZ;AAAA,aAAA,EACF;AAAA;AAAA,SACF;AAAA,wBACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,mBAAA,EAAqB,CAAA;AAAA,4BAC5C,QAAA,EAAA,EAAS,SAAA,EAAW,OAAA,CAAQ,gBAAA,EAC3B,+BAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,SAAI,SAAA,EAAW,OAAA,CAAQ,mBACtB,QAAA,kBAAA,IAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,OAAA,EACjB,QAAA,EAAA;AAAA,YAAA,QAAA,CAAS,cAAA,IAAkB,CAAA;AAAA,YAAE,GAAA;AAAA,YAAE,EAAE,qBAAqB;AAAA,WAAA,EACzD,CAAA,EACF,CAAA;AAAA,0BACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,iBACtB,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAA,EACjB,QAAA,EAAA,kBAAA,CAAmB,QAAA,CAAS,UAAA,EAAY,CAAC,GAC5C,CAAA,EACF;AAAA,SAAA,EACF,CAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
1
+ {"version":3,"file":"NotebookCard.esm.js","sources":["../../../src/components/notebooks/NotebookCard.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 { useCallback } from 'react';\n\nimport type { TranslationFunction } from '@backstage/core-plugin-api/alpha';\n\nimport Typography from '@mui/material/Typography';\nimport {\n Card,\n CardBody,\n CardHeader,\n CardTitle,\n Dropdown,\n DropdownItem,\n DropdownList,\n MenuToggle,\n TextInput,\n} from '@patternfly/react-core';\nimport {\n CatalogIcon,\n EllipsisVIcon,\n PenIcon,\n TrashIcon,\n} from '@patternfly/react-icons';\n\nimport { useInlineEdit } from '../../hooks/notebooks/useInlineEdit';\nimport { intelligentAssistantTranslationRef } from '../../translations/ref';\nimport { NotebookSession } from '../../types';\nimport { formatUpdatedLabel } from '../../utils/notebooks-utils';\n\ntype NotebookCardProps = {\n notebook: NotebookSession;\n classes: Record<string, string>;\n openNotebookMenuId: string | null;\n setOpenNotebookMenuId: React.Dispatch<React.SetStateAction<string | null>>;\n onClick: (notebook: NotebookSession) => void;\n onRename: (sessionId: string, newName: string) => void;\n onDelete: (sessionId: string) => void;\n t: TranslationFunction<typeof intelligentAssistantTranslationRef.T>;\n};\n\nconst titleInputStyle: React.CSSProperties = {\n '--pf-v6-c-form-control--before--BorderStyle': 'none',\n '--pf-v6-c-form-control--after--BorderColor':\n 'var(--pf-t--global--text--color--brand--default)',\n '--pf-v6-c-form-control--FontSize': '1.25rem',\n '--pf-v6-c-form-control--PaddingBlockStart': '2px',\n '--pf-v6-c-form-control--PaddingBlockEnd': '2px',\n '--pf-v6-c-form-control--PaddingInlineStart': '6px',\n '--pf-v6-c-form-control--PaddingInlineEnd': '6px',\n '--pf-v6-c-form-control--OutlineOffset': '0',\n width: '100%',\n minWidth: 0,\n fontWeight: 500,\n} as React.CSSProperties;\n\nexport const NotebookCard = ({\n notebook,\n classes,\n openNotebookMenuId,\n setOpenNotebookMenuId,\n onClick,\n onRename,\n onDelete,\n t,\n}: NotebookCardProps) => {\n const isMenuOpen = openNotebookMenuId === notebook.session_id;\n\n const {\n isEditing,\n editValue: editName,\n setEditValue: setEditName,\n inputRef,\n startEditing,\n save: saveRename,\n handleKeyDown,\n } = useInlineEdit({\n currentName: notebook.name,\n onSave: newName => onRename(notebook.session_id, newName),\n onStart: () => setOpenNotebookMenuId(null),\n });\n\n const handleCardClick = useCallback(() => {\n if (!isEditing) {\n onClick(notebook);\n }\n }, [isEditing, onClick, notebook]);\n\n const handleCardKeyDown = (e: React.KeyboardEvent) => {\n if (!isEditing && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick(notebook);\n }\n };\n\n return (\n <Card\n className={classes.notebookCard}\n component=\"div\"\n tabIndex={0}\n aria-label={(t as Function)('notebooks.card.openAria', {\n name: notebook.name,\n })}\n onClick={handleCardClick}\n onKeyDown={handleCardKeyDown}\n >\n <CardHeader\n className={classes.notebookCardHeader}\n actions={{\n actions: (\n <Dropdown\n className={classes.notebookDropdownMenu}\n isOpen={isMenuOpen}\n popperProps={{\n position: 'end',\n preventOverflow: true,\n }}\n onOpenChange={isOpen =>\n setOpenNotebookMenuId(isOpen ? notebook.session_id : null)\n }\n toggle={toggleRef => (\n <MenuToggle\n ref={toggleRef}\n variant=\"plain\"\n className={classes.notebookMenuButton}\n aria-label={t('aria.options.label')}\n isExpanded={isMenuOpen}\n onClick={event => {\n event.stopPropagation();\n setOpenNotebookMenuId(current =>\n current === notebook.session_id\n ? null\n : notebook.session_id,\n );\n }}\n >\n <EllipsisVIcon />\n </MenuToggle>\n )}\n >\n <DropdownList className={classes.notebookDropdownList}>\n <DropdownItem\n className={classes.notebookDropdownItem}\n icon={<PenIcon />}\n onClick={event => {\n event.stopPropagation();\n startEditing();\n }}\n >\n {t('notebooks.actions.rename')}\n </DropdownItem>\n <DropdownItem\n className={classes.notebookDropdownItem}\n icon={<TrashIcon />}\n onClick={event => {\n event.stopPropagation();\n onDelete(notebook.session_id);\n setOpenNotebookMenuId(null);\n }}\n >\n {t('notebooks.actions.delete')}\n </DropdownItem>\n </DropdownList>\n </Dropdown>\n ),\n className: classes.notebookCardHeaderActions,\n }}\n >\n <CardTitle className={classes.notebookTitle}>\n <CatalogIcon />\n {isEditing ? (\n <TextInput\n ref={inputRef}\n style={titleInputStyle}\n value={editName}\n onChange={(_event, value) => setEditName(value)}\n onBlur={saveRename}\n onKeyDown={handleKeyDown}\n aria-label={t('notebooks.rename.inline.tooltip')}\n onClick={e => e.stopPropagation()}\n />\n ) : (\n <Typography\n component=\"span\"\n className={classes.notebookTitleText}\n title={t('notebooks.rename.inline.tooltip')}\n onClick={e => {\n e.stopPropagation();\n startEditing();\n }}\n >\n {notebook.name}\n </Typography>\n )}\n </CardTitle>\n </CardHeader>\n <div className={classes.notebookCardDivider} />\n <CardBody className={classes.notebookCardBody}>\n <div>\n <div className={classes.notebookDocuments}>\n <Typography variant=\"body2\">\n {(t as Function)('notebooks.documents', {\n count: notebook.document_count ?? 0,\n })}\n </Typography>\n </div>\n <div className={classes.notebookUpdated}>\n <Typography variant=\"caption\">\n {formatUpdatedLabel(notebook.updated_at, t)}\n </Typography>\n </div>\n </div>\n </CardBody>\n </Card>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,19 +1,24 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { useState, useCallback, useEffect, useRef } from 'react';
2
+ import { useState, useRef, useCallback, useEffect } from 'react';
3
3
  import { useApi, configApiRef } from '@backstage/core-plugin-api';
4
4
  import { makeStyles, Typography } from '@material-ui/core';
5
5
  import { ChatbotFooter, MessageBar, ChatbotContent } from '@patternfly/chatbot';
6
6
  import { DrawerPanelContent, AlertGroup, Alert, AlertVariant, AlertActionCloseButton, Drawer, DrawerContent, DrawerContentBody, Tooltip, Button } from '@patternfly/react-core';
7
- import { TimesIcon } from '@patternfly/react-icons';
7
+ import { TimesIcon, PlusIcon } from '@patternfly/react-icons';
8
8
  import { useQueryClient } from '@tanstack/react-query';
9
9
  import { notebooksApiRef } from '../../api/notebooksApi.esm.js';
10
10
  import { TEMP_CONVERSATION_ID, UNTITLED_NOTEBOOK_NAME, NOTEBOOK_MAX_FILES } from '../../const.esm.js';
11
11
  import { useCreateNotebookMessage } from '../../hooks/notebooks/useCreateNotebookMessage.esm.js';
12
12
  import { useDocumentStatusPolling } from '../../hooks/notebooks/useDocumentStatusPolling.esm.js';
13
+ import { useRenameDocument } from '../../hooks/notebooks/useRenameDocument.esm.js';
14
+ import { useRenameNotebookWithAlert } from '../../hooks/notebooks/useRenameNotebookWithAlert.esm.js';
15
+ import { useUploadDocument } from '../../hooks/notebooks/useUploadDocument.esm.js';
13
16
  import { useConversationMessages } from '../../hooks/useConversationMessages.esm.js';
14
17
  import { useNotebookWelcomePrompts } from '../../hooks/useNotebookWelcomePrompts.esm.js';
18
+ import { useStopConversation } from '../../hooks/useStopConversation.esm.js';
15
19
  import { useTranslation } from '../../hooks/useTranslation.esm.js';
16
20
  import { ChatbotFootnoteWithIcon } from '../../utils/lightspeed-chatbox-utils.esm.js';
21
+ import { runFileUploads } from '../../utils/notebook-upload-runner.esm.js';
17
22
  import { LightspeedChatBox } from '../LightspeedChatBox.esm.js';
18
23
  import { AddDocumentModal } from './AddDocumentModal.esm.js';
19
24
  import { DeleteDocumentModal } from './DeleteDocumentModal.esm.js';
@@ -197,11 +202,33 @@ const useStyles = makeStyles((theme) => ({
197
202
  "& .pf-chatbot__message-bar": {
198
203
  backgroundColor: theme.palette.type === "light" ? theme.palette.grey[100] : "var(--pf-t--global--background--color--secondary--default)"
199
204
  },
200
- "& .pf-chatbot__button--send, & .pf-chatbot__button--microphone": {
205
+ "& .pf-chatbot__button--stop, & .pf-chatbot__button--attach, & .pf-chatbot__button--send, & .pf-chatbot__button--microphone": {
201
206
  "--pf-v6-c-button--BorderRadius": "var(--pf-t--global--border--radius--pill)",
202
207
  borderRadius: "var(--pf-t--global--border--radius--pill) !important"
203
208
  }
204
209
  },
210
+ messageBar: {
211
+ border: "1px solid var(--pf-t--global--border--color--default)",
212
+ borderRadius: 24,
213
+ padding: theme.spacing(0.5),
214
+ "&::after": {
215
+ display: "none"
216
+ }
217
+ },
218
+ addResourceButton: {
219
+ background: "none",
220
+ border: "none",
221
+ cursor: "pointer",
222
+ padding: 4,
223
+ display: "inline-flex",
224
+ alignItems: "center",
225
+ color: "inherit",
226
+ "&:focus-visible": {
227
+ outline: "2px solid var(--pf-t--global--border--color--brand--default)",
228
+ outlineOffset: 2
229
+ },
230
+ marginLeft: theme.spacing(2)
231
+ },
205
232
  chatContent: {
206
233
  minHeight: 0,
207
234
  display: "flex",
@@ -236,6 +263,16 @@ const NotebookView = ({
236
263
  metadata?.conversation_id ?? TEMP_CONVERSATION_ID
237
264
  );
238
265
  const [isSendButtonDisabled, setIsSendButtonDisabled] = useState(false);
266
+ const [requestId, setRequestId] = useState("");
267
+ const { mutate: stopConversation } = useStopConversation();
268
+ const wasStoppedByUserRef = useRef(false);
269
+ const autoDeleteRef = useRef({
270
+ isUntitled: false,
271
+ isEmpty: true,
272
+ noPending: true,
273
+ noUploading: true,
274
+ noChat: true
275
+ });
239
276
  const [announcement, setAnnouncement] = useState(
240
277
  void 0
241
278
  );
@@ -246,10 +283,14 @@ const NotebookView = ({
246
283
  const handleDeleteDocument = useCallback((documentId) => {
247
284
  setDeleteDocumentTarget({ id: documentId, name: documentId });
248
285
  }, []);
286
+ const { mutateAsync: renameDocument } = useRenameDocument();
249
287
  const onComplete = useCallback(
250
288
  (message) => {
251
289
  setIsSendButtonDisabled(false);
252
- setAnnouncement(`Message from Bot: ${message}`);
290
+ if (!wasStoppedByUserRef.current) {
291
+ setAnnouncement(`Message from Bot: ${message}`);
292
+ }
293
+ wasStoppedByUserRef.current = false;
253
294
  queryClient.invalidateQueries({
254
295
  queryKey: ["conversationMessages", conversationId]
255
296
  });
@@ -268,6 +309,9 @@ const NotebookView = ({
268
309
  },
269
310
  [notebookCreateMessage, sessionId]
270
311
  );
312
+ const onRequestIdReady = useCallback((rid) => {
313
+ setRequestId(rid);
314
+ }, []);
271
315
  const { conversationMessages, handleInputPrompt, scrollToBottomRef } = useConversationMessages(
272
316
  conversationId,
273
317
  userName,
@@ -276,7 +320,8 @@ const NotebookView = ({
276
320
  avatar,
277
321
  onComplete,
278
322
  onStart,
279
- createMessageAdapter
323
+ createMessageAdapter,
324
+ onRequestIdReady
280
325
  );
281
326
  const [messages, setMessages] = useState(conversationMessages);
282
327
  useEffect(() => {
@@ -284,6 +329,7 @@ const NotebookView = ({
284
329
  }, [conversationMessages]);
285
330
  const sendMessage = useCallback(
286
331
  (message) => {
332
+ wasStoppedByUserRef.current = false;
287
333
  setAnnouncement(
288
334
  t("conversation.announcement.userMessage", {
289
335
  prompt: message.toString()
@@ -294,11 +340,21 @@ const NotebookView = ({
294
340
  },
295
341
  [handleInputPrompt, t]
296
342
  );
343
+ const handleStopButton = useCallback(() => {
344
+ wasStoppedByUserRef.current = true;
345
+ if (requestId) {
346
+ stopConversation(requestId);
347
+ setRequestId("");
348
+ }
349
+ setIsSendButtonDisabled(false);
350
+ setAnnouncement(t("conversation.announcement.responseStopped"));
351
+ }, [requestId, stopConversation, t]);
297
352
  const notebookPrompts = useNotebookWelcomePrompts();
298
353
  const welcomePrompts = notebookPrompts.map((title) => ({
299
354
  title,
300
355
  onClick: () => sendMessage(title)
301
356
  }));
357
+ const uploadMutation = useUploadDocument();
302
358
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
303
359
  const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
304
360
  const [uploadingFileNames, setUploadingFileNames] = useState([]);
@@ -309,8 +365,56 @@ const NotebookView = ({
309
365
  /* @__PURE__ */ new Set()
310
366
  );
311
367
  const [filesToOverwrite, setFilesToOverwrite] = useState([]);
368
+ const [allFilesForOverwrite, setAllFilesForOverwrite] = useState([]);
312
369
  const [isOverwriteModalOpen, setIsOverwriteModalOpen] = useState(false);
313
370
  const [filesToAddToModal, setFilesToAddToModal] = useState([]);
371
+ autoDeleteRef.current = {
372
+ isUntitled: notebookName === UNTITLED_NOTEBOOK_NAME,
373
+ isEmpty: documents.length === 0 && completedFileNames.size === 0,
374
+ noPending: !pendingUploads.length,
375
+ noUploading: !uploadingFileNames.length,
376
+ noChat: conversationId === TEMP_CONVERSATION_ID
377
+ };
378
+ useEffect(() => {
379
+ return () => {
380
+ const currentNotebook = autoDeleteRef.current;
381
+ if (currentNotebook.isUntitled && currentNotebook.isEmpty && currentNotebook.noPending && currentNotebook.noUploading && currentNotebook.noChat) {
382
+ notebooksApi.deleteSession(sessionId).then(() => {
383
+ queryClient.invalidateQueries({
384
+ queryKey: ["notebooks", "sessions"]
385
+ });
386
+ }).catch(() => {
387
+ });
388
+ } else {
389
+ queryClient.invalidateQueries({
390
+ queryKey: ["notebooks", "sessions"]
391
+ });
392
+ }
393
+ };
394
+ }, [notebooksApi, sessionId, queryClient]);
395
+ const handleRenameDocument = useCallback(
396
+ async (documentId, newTitle) => {
397
+ try {
398
+ await renameDocument({ sessionId, documentId, newTitle });
399
+ } catch {
400
+ setToastAlerts((prev) => [
401
+ {
402
+ key: Date.now() + documentId,
403
+ title: t("notebook.document.rename.error", {
404
+ documentName: documentId
405
+ }),
406
+ variant: "danger"
407
+ },
408
+ ...prev
409
+ ]);
410
+ }
411
+ },
412
+ [renameDocument, sessionId, t]
413
+ );
414
+ const handleRenameNotebook = useRenameNotebookWithAlert({
415
+ setAlerts: setToastAlerts,
416
+ getNotebookName: () => notebookName
417
+ });
314
418
  const confirmDeleteDocument = useCallback(async () => {
315
419
  if (!deleteDocumentTarget) return;
316
420
  const { id: documentId, name: documentName } = deleteDocumentTarget;
@@ -341,6 +445,9 @@ const NotebookView = ({
341
445
  }, [deleteDocumentTarget, notebooksApi, sessionId, queryClient, t]);
342
446
  const handleOpenUploadModal = () => setIsUploadModalOpen(true);
343
447
  const handleCloseUploadModal = () => setIsUploadModalOpen(false);
448
+ const handleCloseNotebook = () => {
449
+ onClose();
450
+ };
344
451
  const handleFilesUploading = (files) => {
345
452
  setUploadingFileNames((prev) => {
346
453
  const newNames = files.map((f) => f.name).filter((name) => !prev.includes(name));
@@ -367,23 +474,37 @@ const NotebookView = ({
367
474
  ...prev
368
475
  ]);
369
476
  };
370
- const handleDuplicatesFound = (files) => {
371
- setFilesToOverwrite(files);
477
+ const handleDuplicatesFound = (duplicateFiles, allFiles) => {
478
+ setFilesToOverwrite(duplicateFiles);
479
+ setAllFilesForOverwrite(allFiles);
480
+ setIsUploadModalOpen(false);
372
481
  setIsOverwriteModalOpen(true);
373
482
  };
374
- const handleOverwriteConfirm = () => {
375
- const files = filesToOverwrite;
483
+ const handleOverwriteConfirm = (filesToUpload) => {
376
484
  setIsOverwriteModalOpen(false);
377
485
  setFilesToOverwrite([]);
378
- if (files.length === 0) return;
379
- setFilesToAddToModal(files);
486
+ setAllFilesForOverwrite([]);
487
+ if (filesToUpload.length === 0) return;
488
+ runFileUploads(uploadMutation, sessionId, filesToUpload, {
489
+ onUploading: handleFilesUploading,
490
+ onStarted: handleUploadStarted,
491
+ onFailed: handleUploadFailed
492
+ });
380
493
  };
381
494
  const handleFilesAddedToModal = () => {
382
495
  setFilesToAddToModal([]);
383
496
  };
497
+ const handleOverwriteBack = () => {
498
+ setIsOverwriteModalOpen(false);
499
+ setFilesToAddToModal(allFilesForOverwrite);
500
+ setFilesToOverwrite([]);
501
+ setAllFilesForOverwrite([]);
502
+ setIsUploadModalOpen(true);
503
+ };
384
504
  const handleOverwriteCancel = () => {
385
505
  setIsOverwriteModalOpen(false);
386
506
  setFilesToOverwrite([]);
507
+ setAllFilesForOverwrite([]);
387
508
  };
388
509
  const pollingResults = useDocumentStatusPolling(sessionId, pendingUploads);
389
510
  useEffect(() => {
@@ -452,7 +573,9 @@ const NotebookView = ({
452
573
  hasUploadsInProgress,
453
574
  onToggleCollapse: () => setSidebarCollapsed((prev) => !prev),
454
575
  onAddDocument: handleOpenUploadModal,
455
- onDeleteDocument: handleDeleteDocument
576
+ onDeleteDocument: handleDeleteDocument,
577
+ onRenameDocument: handleRenameDocument,
578
+ onRenameNotebook: (newName) => handleRenameNotebook(sessionId, newName)
456
579
  }
457
580
  )
458
581
  }
@@ -594,7 +717,7 @@ const NotebookView = ({
594
717
  {
595
718
  variant: "link",
596
719
  className: classes.closeButton,
597
- onClick: onClose,
720
+ onClick: handleCloseNotebook,
598
721
  icon: /* @__PURE__ */ jsx(TimesIcon, {}),
599
722
  iconPosition: "end",
600
723
  children: t("notebook.view.close")
@@ -603,35 +726,70 @@ const NotebookView = ({
603
726
  /* @__PURE__ */ jsx("div", { className: classes.mainContent, children: renderMainContent() }),
604
727
  hasNoDocuments && messages.length === 0 && renderNotebookDisclaimerAlert(),
605
728
  /* @__PURE__ */ jsxs(ChatbotFooter, { className: classes.footer, children: [
606
- hasNoDocuments ? /* @__PURE__ */ jsx(
607
- Tooltip,
608
- {
609
- content: t("notebook.view.input.disabledTooltip"),
610
- position: "top",
611
- children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
612
- MessageBar,
613
- {
614
- hasAttachButton: false,
615
- hasMicrophoneButton: false,
616
- hasStopButton: false,
617
- isSendButtonDisabled: true,
618
- isDisabled: true,
619
- onSendMessage: sendMessage,
620
- placeholder: t("notebook.view.input.placeholder")
729
+ (() => {
730
+ const addResourceAction = /* @__PURE__ */ jsx(
731
+ "button",
732
+ {
733
+ type: "button",
734
+ onClick: () => handleOpenUploadModal(),
735
+ "aria-label": t("notebook.view.documents.add"),
736
+ className: classes.addResourceButton,
737
+ children: /* @__PURE__ */ jsx(PlusIcon, { style: { width: 16, height: 16 } })
738
+ }
739
+ );
740
+ return hasNoDocuments ? /* @__PURE__ */ jsx(
741
+ Tooltip,
742
+ {
743
+ content: t("notebook.view.input.disabledTooltip"),
744
+ position: "top",
745
+ children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
746
+ MessageBar,
747
+ {
748
+ className: classes.messageBar,
749
+ hasAttachButton: false,
750
+ hasMicrophoneButton: false,
751
+ hasStopButton: false,
752
+ isSendButtonDisabled: true,
753
+ isDisabled: true,
754
+ onSendMessage: sendMessage,
755
+ placeholder: t("notebook.view.input.placeholder"),
756
+ forceMultilineLayout: true,
757
+ additionalActions: addResourceAction,
758
+ buttonProps: {
759
+ send: {
760
+ tooltipContent: t("tooltip.send")
761
+ }
762
+ }
763
+ }
764
+ ) })
765
+ }
766
+ ) : /* @__PURE__ */ jsx(
767
+ MessageBar,
768
+ {
769
+ className: classes.messageBar,
770
+ hasAttachButton: false,
771
+ hasMicrophoneButton: true,
772
+ hasStopButton: isSendButtonDisabled,
773
+ handleStopButton: isSendButtonDisabled ? handleStopButton : void 0,
774
+ isSendButtonDisabled,
775
+ onSendMessage: sendMessage,
776
+ placeholder: t("notebook.view.input.placeholder"),
777
+ forceMultilineLayout: true,
778
+ additionalActions: addResourceAction,
779
+ buttonProps: {
780
+ microphone: {
781
+ tooltipContent: {
782
+ active: t("tooltip.microphone.active"),
783
+ inactive: t("tooltip.microphone.inactive")
784
+ }
785
+ },
786
+ send: {
787
+ tooltipContent: t("tooltip.send")
788
+ }
621
789
  }
622
- ) })
623
- }
624
- ) : /* @__PURE__ */ jsx(
625
- MessageBar,
626
- {
627
- hasAttachButton: false,
628
- hasMicrophoneButton: true,
629
- hasStopButton: false,
630
- isSendButtonDisabled,
631
- onSendMessage: sendMessage,
632
- placeholder: t("notebook.view.input.placeholder")
633
- }
634
- ),
790
+ }
791
+ );
792
+ })(),
635
793
  /* @__PURE__ */ jsx(ChatbotFootnoteWithIcon, { label: t("footer.accuracy.label") })
636
794
  ] })
637
795
  ] })
@@ -662,7 +820,9 @@ const NotebookView = ({
662
820
  isOpen: isOverwriteModalOpen,
663
821
  onClose: handleOverwriteCancel,
664
822
  onConfirm: handleOverwriteConfirm,
665
- fileNames: filesToOverwrite.map((f) => f.name)
823
+ onBack: handleOverwriteBack,
824
+ allFiles: allFilesForOverwrite,
825
+ duplicateFileNames: filesToOverwrite.map((f) => f.name)
666
826
  }
667
827
  ),
668
828
  /* @__PURE__ */ jsx(