From a4b5a5116d3f58a76b27774f2a9f36909099f1ea Mon Sep 17 00:00:00 2001 From: Andrii Ieroshenko Date: Wed, 18 Jun 2025 15:51:00 -0700 Subject: [PATCH 1/3] Don't render deleted messages, handle null render promises --- .../src/components/messages/messages.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/jupyter-chat/src/components/messages/messages.tsx b/packages/jupyter-chat/src/components/messages/messages.tsx index 8922fbce..6b3eb621 100644 --- a/packages/jupyter-chat/src/components/messages/messages.tsx +++ b/packages/jupyter-chat/src/components/messages/messages.tsx @@ -115,7 +115,12 @@ export function ChatMessages(props: BaseMessageProps): JSX.Element { const observer = new IntersectionObserver(entries => { // Used on first rendering, to ensure all the message as been rendered once. if (!allRendered) { - Promise.all(renderedPromise.current.map(p => p.promise)).then(() => { + const activePromises = renderedPromise.current + // Filter out nulls signigying deleted messages + .filter(p => p) + .map(p => p.promise); + + Promise.all(activePromises).then(() => { setAllRendered(true); }); } @@ -181,6 +186,11 @@ export function ChatMessages(props: BaseMessageProps): JSX.Element { )} {messages.map((message, i) => { + // Skip rendering deleted messages while preventing sparse array + if (message.deleted) { + listRef.current[i] = null; + return null; + } renderedPromise.current[i] = new PromiseDelegate(); return ( // extra div needed to ensure each bubble is on a new line From f6a850cf73f38874471071f8c4326bf67150ba26 Mon Sep 17 00:00:00 2001 From: Andrii Ieroshenko Date: Wed, 18 Jun 2025 16:40:43 -0700 Subject: [PATCH 2/3] update delete message test --- ui-tests/tests/message-toolbar.spec.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ui-tests/tests/message-toolbar.spec.ts b/ui-tests/tests/message-toolbar.spec.ts index bcc1e7c7..64207e02 100644 --- a/ui-tests/tests/message-toolbar.spec.ts +++ b/ui-tests/tests/message-toolbar.spec.ts @@ -129,20 +129,27 @@ test.describe('#messageToolbar', () => { ).not.toContain('(edited)'); }); - test('should set the message as deleted', async ({ page }) => { + test('should not render deleted message', async ({ page }) => { const chatPanel = await openChat(page, FILENAME); - const message = chatPanel - .locator('.jp-chat-messages-container .jp-chat-message') - .first(); + const messagesContainer = chatPanel.locator('.jp-chat-messages-container'); + + const messageCountBefore = await messagesContainer + .locator('.jp-chat-message') + .count(); + expect(messageCountBefore).toBe(1); + + const message = messagesContainer.locator('.jp-chat-message').first(); const messageContent = message.locator('.jp-chat-rendered-markdown'); // Should display the message toolbar await messageContent.hover({ position: { x: 5, y: 5 } }); await messageContent.locator('.jp-chat-toolbar jp-button').last().click(); - await expect(messageContent).not.toBeVisible(); - expect( - await message.locator('.jp-chat-message-header').textContent() - ).toContain('(message deleted)'); + await expect(message).not.toBeVisible(); + + const messageCountAfter = await messagesContainer + .locator('.jp-chat-message') + .count(); + expect(messageCountAfter).toBe(0); }); }); From e6eac31b6aa4a7da683ade28e7046a26c25841ea Mon Sep 17 00:00:00 2001 From: Andrii Ieroshenko Date: Thu, 19 Jun 2025 09:55:58 -0700 Subject: [PATCH 3/3] fix typo in a comment --- packages/jupyter-chat/src/components/messages/messages.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jupyter-chat/src/components/messages/messages.tsx b/packages/jupyter-chat/src/components/messages/messages.tsx index 6b3eb621..02a21fdc 100644 --- a/packages/jupyter-chat/src/components/messages/messages.tsx +++ b/packages/jupyter-chat/src/components/messages/messages.tsx @@ -116,7 +116,7 @@ export function ChatMessages(props: BaseMessageProps): JSX.Element { // Used on first rendering, to ensure all the message as been rendered once. if (!allRendered) { const activePromises = renderedPromise.current - // Filter out nulls signigying deleted messages + // Filter out nulls signifying deleted messages .filter(p => p) .map(p => p.promise);