From e281274400216bfc54ac88440c8812bf0d8a41bc Mon Sep 17 00:00:00 2001 From: Yi Cai Date: Mon, 28 Sep 2026 13:17:20 -0400 Subject: [PATCH 1/2] fix(intelligent-assistant): restore MUI5 IA styles for notebooks and file chips Fixes: https://redhat.atlassian.net/browse/RHDHBUGS-3751 Signed-off-by: Yi Cai Co-authored-by: Cursor --- eslint.frontend-shared.cjs | 11 +- .../.changeset/quiet-scroll-border.md | 5 + .../src/components/FilePreview.tsx | 31 +- .../src/components/LightSpeedChat.tsx | 5 + .../components/notebooks/DocumentSidebar.tsx | 294 ++++++++++-------- .../src/components/notebooks/NotebookView.tsx | 12 + 6 files changed, 204 insertions(+), 154 deletions(-) create mode 100644 workspaces/intelligent-assistant/.changeset/quiet-scroll-border.md diff --git a/eslint.frontend-shared.cjs b/eslint.frontend-shared.cjs index a16752323c3..420a9821247 100644 --- a/eslint.frontend-shared.cjs +++ b/eslint.frontend-shared.cjs @@ -62,10 +62,15 @@ const materialUiMigrationEslintConfig = { * Shared ESLint config for frontend packages across the rhdh-plugins monorepo. * Blocks Material UI v4 (@material-ui/*) imports to prevent reintroduction * after migration to MUI v5 / Backstage UI. + * + * Resolve @backstage/cli from the package/workspace (packageDir), not the + * monorepo root — workspaces pin newer CLI with JSX-transform rules. */ module.exports = function createFrontendEslintConfig(packageDir) { - return require('@backstage/cli/config/eslint-factory')( - packageDir, - materialUiMigrationEslintConfig, + const createPackageConfig = require( + require.resolve('@backstage/cli/config/eslint-factory', { + paths: [packageDir], + }), ); + return createPackageConfig(packageDir, materialUiMigrationEslintConfig); }; diff --git a/workspaces/intelligent-assistant/.changeset/quiet-scroll-border.md b/workspaces/intelligent-assistant/.changeset/quiet-scroll-border.md new file mode 100644 index 00000000000..65d7c84ac50 --- /dev/null +++ b/workspaces/intelligent-assistant/.changeset/quiet-scroll-border.md @@ -0,0 +1,5 @@ +--- +'@red-hat-developer-hub/backstage-plugin-intelligent-assistant': patch +--- + +Fix MUI5 style regressions: notebooks docked scroll, file chip outline border, footer divider, and document row alignment diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/FilePreview.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/FilePreview.tsx index 75dc1b4390c..86b657687b3 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/FilePreview.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/FilePreview.tsx @@ -15,7 +15,6 @@ */ import Box from '@mui/material/Box'; -import Divider from '@mui/material/Divider'; import { FileDetailsLabel } from '@patternfly/chatbot'; import { useFileAttachmentContext } from './AttachmentContext'; @@ -57,22 +56,20 @@ const FilePreview = () => { } return ( - <> - - - {fileContents.map((file, index) => ( - { - removeFile(index); - }} - /> - ))} - - + + {fileContents.map((file, index) => ( + { + removeFile(index); + }} + /> + ))} + ); }; diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightSpeedChat.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightSpeedChat.tsx index 25aa32a4d08..8925e17e9dc 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightSpeedChat.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/LightSpeedChat.tsx @@ -335,6 +335,11 @@ const StyledChatbotFooter = styled(ChatbotFooter)(({ theme }) => ({ backgroundColor: `${floatingBg} !important`, alignItems: 'stretch', }, + // ChatbotFooter always injects an
; MessageBar already has its own edge, + // so the line reads as an extra divider above file chips / the input. + '& > .pf-v6-c-divider': { + display: 'none', + }, '&>.pf-chatbot__footer-container': { width: '100% !important', maxWidth: 'unset !important', diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx index cea34100e3c..36cdc466224 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx @@ -23,6 +23,8 @@ import { Dropdown, DropdownItem, DropdownList, + Flex, + FlexItem, HelperText, HelperTextItem, Icon, @@ -120,26 +122,20 @@ const DocumentsList = styled('div')(({ theme }) => ({ flex: 1, })); -const DocumentItem = styled('div')(({ theme }) => ({ - display: 'flex', - alignItems: 'flex-start', - gap: theme.spacing(1), +// Hover chrome only — alignment comes from PF Flex alignItemsCenter. +const DocumentItem = styled(Flex)(({ theme }) => ({ padding: `${theme.spacing(1)} ${theme.spacing(0.5)}`, - borderRadius: 4, + borderRadius: 'var(--pf-t--global--border--radius--small)', '&:hover': { backgroundColor: 'var(--pf-t--global--background--color--action--plain--hover)', }, - '&:hover .doc-kebab': { - visibility: 'visible', - }, - '&:focus-within .doc-kebab': { + '&:hover .doc-kebab, &:focus-within .doc-kebab': { visibility: 'visible', }, })); const FileName = styled(Typography)({ - flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', @@ -147,7 +143,7 @@ const FileName = styled(Typography)({ fontSize: '0.875rem', lineHeight: '1.25rem', cursor: 'pointer', - borderRadius: 4, + borderRadius: 'var(--pf-t--global--border--radius--small)', padding: '2px 6px', '&:hover': { backgroundColor: @@ -155,12 +151,12 @@ const FileName = styled(Typography)({ }, }); -const RenameContainer = styled('div')({ +const RenameFields = styled('div')({ display: 'flex', alignItems: 'center', - flexWrap: 'wrap', flex: 1, minWidth: 0, + gap: 'var(--pf-t--global--spacer--xs)', }); const RenameInput = styled(TextInput)({ @@ -191,7 +187,8 @@ const RenameExtension = styled(Typography)({ const RenameHelperText = styled('div')({ width: '100%', - paddingTop: 4, + paddingTop: 'var(--pf-t--global--spacer--xs)', + paddingInlineStart: 'calc(28px + var(--pf-t--global--spacer--sm))', '& .pf-v6-c-helper-text__item-text': { color: 'var(--pf-t--global--color--status--danger--default)', }, @@ -199,6 +196,8 @@ const RenameHelperText = styled('div')({ const SpinnerContainer = styled('div')({ flexShrink: 0, + display: 'flex', + alignItems: 'center', }); const KebabToggle = styled(MenuToggle)({ @@ -426,129 +425,156 @@ export const DocumentSidebar = ({ {(documents.length > 0 || activePending.length > 0) && ( - {documents.map(doc => ( - - - {editingDocId === doc.document_id ? ( - (() => { - const validationError = getValidationError( - doc.document_id, - doc.title, - ); - return ( - - setEditName(value)} - onBlur={() => saveRename(doc.document_id, doc.title)} - onKeyDown={event => - handleKeyDown(event, doc.document_id, doc.title) - } - validated={validationError ? 'error' : 'default'} - aria-label={t('notebook.document.rename')} - /> - - {splitFileName(doc.title).extension} - - {validationError && ( - - - - {validationError} - - - - )} - - ); - })() - ) : ( - startEditing(doc.document_id, doc.title)} - > - {doc.title} - - )} - {deletingDocumentIds?.has(doc.document_id) ? ( - - - - ) : ( - - setOpenMenuDocId(isOpen ? doc.document_id : null) - } - toggle={toggleRef => ( - { - event.stopPropagation(); - setOpenMenuDocId(current => - current === doc.document_id ? null : doc.document_id, - ); - }} - aria-label={`${t('aria.options.label')} ${doc.title}`} - > - - - )} + {documents.map(doc => { + const isEditing = editingDocId === doc.document_id; + const validationError = isEditing + ? getValidationError(doc.document_id, doc.title) + : null; + + return ( +
+ - - } - onClick={event => { - event.stopPropagation(); - startEditing(doc.document_id, doc.title); - }} - > - {t('notebook.document.rename')} - - } - onClick={event => { - event.stopPropagation(); - setOpenMenuDocId(null); - onDeleteDocument?.(doc.document_id); - }} - > - {t('notebook.document.delete')} - - - - )} - - ))} + + + + + {isEditing ? ( + + setEditName(value)} + onBlur={() => saveRename(doc.document_id, doc.title)} + onKeyDown={event => + handleKeyDown(event, doc.document_id, doc.title) + } + validated={validationError ? 'error' : 'default'} + aria-label={t('notebook.document.rename')} + /> + + {splitFileName(doc.title).extension} + + + ) : ( + startEditing(doc.document_id, doc.title)} + > + {doc.title} + + )} + + + {deletingDocumentIds?.has(doc.document_id) ? ( + + + + ) : ( + + setOpenMenuDocId(isOpen ? doc.document_id : null) + } + toggle={toggleRef => ( + { + event.stopPropagation(); + setOpenMenuDocId(current => + current === doc.document_id + ? null + : doc.document_id, + ); + }} + aria-label={`${t('aria.options.label')} ${doc.title}`} + > + + + )} + > + + } + onClick={event => { + event.stopPropagation(); + startEditing(doc.document_id, doc.title); + }} + > + {t('notebook.document.rename')} + + } + onClick={event => { + event.stopPropagation(); + setOpenMenuDocId(null); + onDeleteDocument?.(doc.document_id); + }} + > + {t('notebook.document.delete')} + + + + )} + + + {validationError && ( + + + + {validationError} + + + + )} +
+ ); + })} {activePending.map(fileName => ( - - - {fileName} + + + + + + {fileName} + {!completedFileNames?.has(fileName) && ( - - - + + + + + )} ))} diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx index de62f423a6e..c1fd81a375e 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx @@ -192,6 +192,14 @@ const StyledChatbotContent = styled(ChatbotContent)({ display: 'flex', flexDirection: 'column', flex: 1, + // PF sets `overflow: hidden` after `overflow-y: auto` on .pf-chatbot__content, + // which clips the docked landing page with no way to scroll (RHDHBUGS-3751). + // Double class + !important so we beat the PF stylesheet order. + '&&': { + overflowX: 'hidden', + overflowY: 'auto', + }, + WebkitOverflowScrolling: 'touch', '& .pf-chatbot__message-contents': { overflowX: 'hidden', overflowWrap: 'break-word', @@ -205,6 +213,10 @@ const floatingBg = 'var(--pf-t--global--background--color--floating--default)'; const StyledChatbotFooter = styled(ChatbotFooter)(({ theme }) => ({ backgroundColor: `${floatingBg} !important`, alignItems: 'stretch', + // Match Chat: hide ChatbotFooter's injected
(MessageBar supplies the edge). + '& > .pf-v6-c-divider': { + display: 'none', + }, '&>.pf-chatbot__footer-container': { width: '100% !important', maxWidth: 'unset !important', From 9bf6ca88990173ca0b4d46090284bbaaa106976e Mon Sep 17 00:00:00 2001 From: Yi Cai Date: Tue, 29 Sep 2026 16:11:58 -0400 Subject: [PATCH 2/2] fix(intelligent-assistant): scroll notebook welcome and unify rename icons Make notebook details welcome grow with content so overlay/docked can scroll, and use PencilAltIcon for rename actions to match MCP edit. Fixes: https://redhat.atlassian.net/browse/RHDHBUGS-3751 Signed-off-by: Yi Cai Co-authored-by: Cursor Assisted-by: Composer --- .../components/__tests__/LightspeedChat.test.tsx | 2 +- .../src/components/notebooks/DocumentSidebar.tsx | 4 ++-- .../src/components/notebooks/NotebookCard.tsx | 4 ++-- .../src/components/notebooks/NotebookView.tsx | 16 ++++++++++++++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/__tests__/LightspeedChat.test.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/__tests__/LightspeedChat.test.tsx index 3eddafcc45d..411aff0f751 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/__tests__/LightspeedChat.test.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/__tests__/LightspeedChat.test.tsx @@ -1272,7 +1272,7 @@ describe('LightspeedChat', () => { ).toBeInTheDocument(); }); - it('should show PenIcon in new chat button in fullscreen mode', async () => { + it('should show new chat button in fullscreen mode', async () => { mockUseConversations.mockReturnValue({ data: [ { diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx index 36cdc466224..ab8c7fe6271 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/DocumentSidebar.tsx @@ -36,7 +36,7 @@ import { import { AddCircleOIcon, EllipsisVIcon, - PenIcon, + PencilAltIcon, TrashIcon, } from '@patternfly/react-icons'; @@ -517,7 +517,7 @@ export const DocumentSidebar = ({ } + icon={} onClick={event => { event.stopPropagation(); startEditing(doc.document_id, doc.title); diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookCard.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookCard.tsx index b0cada6bb43..ffe05e668f3 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookCard.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookCard.tsx @@ -34,7 +34,7 @@ import { import { CatalogIcon, EllipsisVIcon, - PenIcon, + PencilAltIcon, TrashIcon, } from '@patternfly/react-icons'; @@ -223,7 +223,7 @@ export const NotebookCard = ({ > } + icon={} onClick={event => { event.stopPropagation(); startEditing(); diff --git a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx index c1fd81a375e..c7c66f39881 100644 --- a/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx +++ b/workspaces/intelligent-assistant/plugins/intelligent-assistant/src/components/notebooks/NotebookView.tsx @@ -137,14 +137,25 @@ const MainContent = styled('div')({ minWidth: 0, }); +// Match Chat new-chat welcome: grow with content and let StyledChatbotContent scroll. +// A flex:1 + height:100% MessageBox fills the parent so outer overflowY never engages, +// and justifyContent:flex-end on that inner scroller clips overflow without a usable +// scrollbar (RHDHBUGS-3751 / notebook details welcome in overlay & docked). const WelcomeMessageBox = styled(MessageBox)({ - flex: 1, - minHeight: 0, + flex: 'none', + height: 'auto !important', + overflow: 'visible', maxWidth: 'unset !important', width: '100%', justifyContent: 'flex-end', }); +/** Pushes welcome content to the bottom when the panel is taller than the content. */ +const WelcomeContentSpacer = styled('div')({ + flex: 1, + minHeight: 0, +}); + const NotebookContentArea = styled('div')(({ theme }) => ({ width: '100%', marginBlockStart: theme.spacing(3), @@ -698,6 +709,7 @@ export const NotebookView = ({ } return ( + {renderNotebookDisclaimerAlert()}