From 2079c7a452b70cdba687a8798947c947e4b0ab4e Mon Sep 17 00:00:00 2001 From: naman-bruno Date: Wed, 8 Jul 2026 14:56:44 +0530 Subject: [PATCH] feat(ai): implement AI chat sidebar popout and sidebar resizing (#8509) --- .../components/AiChatSidebar/Popout/index.js | 23 ++++ .../AiChatSidebar/PopoutWindow/index.js | 128 ++++++++++++++++++ .../components/AiChatSidebar/StyledWrapper.js | 39 +++++- .../src/components/AiChatSidebar/index.js | 113 ++++++++++++++-- .../src/components/RequestTabPanel/index.js | 21 +++ packages/bruno-app/src/pages/Bruno/index.js | 7 +- .../src/providers/ReduxStore/slices/chat.js | 11 ++ packages/bruno-electron/src/index.js | 45 +++++- 8 files changed, 375 insertions(+), 12 deletions(-) create mode 100644 packages/bruno-app/src/components/AiChatSidebar/Popout/index.js create mode 100644 packages/bruno-app/src/components/AiChatSidebar/PopoutWindow/index.js diff --git a/packages/bruno-app/src/components/AiChatSidebar/Popout/index.js b/packages/bruno-app/src/components/AiChatSidebar/Popout/index.js new file mode 100644 index 000000000..6b05bcb46 --- /dev/null +++ b/packages/bruno-app/src/components/AiChatSidebar/Popout/index.js @@ -0,0 +1,23 @@ +import React, { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { closeAiSidebar, dockAiChat } from 'providers/ReduxStore/slices/chat'; +import PopoutWindow from '../PopoutWindow'; +import AiChatSidebar from '../index'; + +const AiChatPopout = ({ collection }) => { + const dispatch = useDispatch(); + + const handleClose = useCallback(({ blocked } = {}) => { + // Closing the OS window closes the assistant (like undocked devtools). + // If window.open was blocked, fall back to the docked sidebar instead. + dispatch(blocked ? dockAiChat() : closeAiSidebar()); + }, [dispatch]); + + return ( + + + + ); +}; + +export default AiChatPopout; diff --git a/packages/bruno-app/src/components/AiChatSidebar/PopoutWindow/index.js b/packages/bruno-app/src/components/AiChatSidebar/PopoutWindow/index.js new file mode 100644 index 000000000..2bf59511a --- /dev/null +++ b/packages/bruno-app/src/components/AiChatSidebar/PopoutWindow/index.js @@ -0,0 +1,128 @@ +import { useState, useEffect, useRef } from 'react'; +import { createPortal } from 'react-dom'; +import { StyleSheetManager } from 'styled-components'; + +// Must match the frameName prefix allow-listed in bruno-electron's +// setWindowOpenHandler, any other window.open is denied there. Each open +// gets a unique suffix so a remount (e.g. StrictMode's dev double-effect) +// never receives a handle to the previous, already-closing named window. +export const AI_POPOUT_WINDOW_NAME_PREFIX = 'bruno-ai-assistant'; +let popoutSeq = 0; + +// Copy every stylesheet already loaded in the main window into the popout +// document. We read cssRules instead of cloning the