feat: Add browser-style body editor tabs with multi-payload support#5656
feat: Add browser-style body editor tabs with multi-payload support#5656divsriv111 wants to merge 10 commits intousebruno:mainfrom
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR enhances Bruno's request body editor by adding browser-style tabs that allow users to manage multiple request body payloads. Users can create, rename, and close tabs with different body types, and only the active tab's payload is used during request execution.
- Adds tabbed interface for request bodies with create/rename/close functionality
- Extends collection schema to support multiple body entries with backward compatibility
- Implements tab content synchronization with request execution logic
Reviewed Changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/bruno-schema/src/collections/index.js | Adds bodyTabSchema and bodyTabs field to support multiple body tabs in collection schema |
| packages/bruno-lang/v2/src/jsonToBru.js | Adds serialization support for bodyTabs to .bru file format |
| packages/bruno-lang/v2/src/bruToJson.js | Adds parsing support for body:tab blocks from .bru files |
| packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js | Adds updateRequestBodyTabs Redux action to manage tab state |
| packages/bruno-app/src/components/RequestPane/RequestBody/index.js | Replaces single body editor with tabbed interface and tab management logic |
| packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.js | New component providing the tabbed UI with rename/close functionality |
| packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/StyledWrapper.js | Styles for the tabbed interface with scrolling and hover states |
| packages/bruno-app/src/components/RequestPane/RequestBody/integration.spec.js | Integration tests for tab state management and persistence |
| packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js | Unit tests for RequestBody component with tabbed functionality |
| packages/bruno-app/src/components/RequestPane/RequestBody/bodyTabs.spec.js | Unit tests for bodyTabs Redux functionality |
| packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.spec.js | Unit tests for BodyTabs UI component |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
|
Thanks for the PR @divsriv111 This definitely makes sense. |
|
hey @helloanoop Just pushed a rebase with master just in case you want to review with the latest changes. |
6456518 to
ddf6428
Compare
WalkthroughAdds a multi-tab request body editor: UI components and styling for tabbed bodies, local tab state with persistence and Redux sync, BRU grammar/serialization for Changes
Sequence DiagramsequenceDiagram
participant User as User
participant UI as BodyTabs UI
participant RB as RequestBody (Local)
participant Redux as Redux Store
participant Editor as CodeEditor
User->>UI: Click tab / Add / Rename / Close / Right-click
UI->>RB: onTabChange / onAddTab / onTabRename / onTabClose / onReorderTab
RB->>RB: update local bodyTabs & active tab content
RB->>Redux: dispatch updateRequestBodyTabs / updateRequestBodyMode / updateRequestBody
Redux->>Redux: persist draft.request.body and bodyTabs
RB->>Editor: render active tab content & chosen mode
Editor->>User: show editor with active tab content
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/bruno-app/src/components/RequestPane/RequestBody/index.js (1)
325-332:⚠️ Potential issue | 🟡 MinorPotential null dereference on
focusedTab.If
tabsis empty oractiveTabUiddoesn't match any tab,focusedTabwill beundefined, causingfocusedTab.uidto throw.🛡️ Add guard
const onScroll = (editor) => { + if (!focusedTab) return; dispatch( updateRequestBodyScrollPosition({ uid: focusedTab.uid, scrollY: editor.doc.scrollTop }) ); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 325 - 332, In onScroll, focusedTab may be undefined causing focusedTab.uid to throw; update the onScroll handler (and any callers) to guard against a missing focusedTab by returning early or using optional chaining. Specifically, in the onScroll function that dispatches updateRequestBodyScrollPosition, check that focusedTab exists (e.g., if (!focusedTab) return) before accessing focusedTab.uid (or use focusedTab?.uid and handle null case), so dispatch is only called with a valid uid.
🧹 Nitpick comments (4)
packages/bruno-app/src/components/RequestPane/RequestBody/index.js (3)
60-64: Double initialization ofinitializeTabContent()is inefficient.
initializeTabContent()is called twice during initial render—once forbodyTabsstate and once foractiveBodyTab. Consider computing once and reusing:♻️ Suggested refactor
- const [bodyTabs, setBodyTabs] = useState(() => initializeTabContent()); - const [activeBodyTab, setActiveBodyTab] = useState(() => { - const tabs = initializeTabContent(); - return tabs[0]?.id || 1; - }); + const [bodyTabs, setBodyTabs] = useState(initializeTabContent); + const [activeBodyTab, setActiveBodyTab] = useState(() => { + return bodyTabs[0]?.id || 1; + });Or compute both in a single initializer and split:
const [{ bodyTabs, activeBodyTab }, setTabState] = useState(() => { const tabs = initializeTabContent(); return { bodyTabs: tabs, activeBodyTab: tabs[0]?.id || 1 }; });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 60 - 64, initializeTabContent() is being called twice during initial render for bodyTabs and activeBodyTab; compute it once and reuse the result to avoid duplicate work by creating a single initializer that calls initializeTabContent() and sets both bodyTabs and activeBodyTab (or by switching to one combined state object). Specifically, replace the two useState initializers with a single initializer that invokes initializeTabContent() once, then calls setBodyTabs/setActiveBodyTab (or returns an object with bodyTabs and activeBodyTab) using the tabs[0]?.id || 1 value so initializeTabContent is not invoked twice.
112-132: Complex useEffect with potential circular dependency.This effect has
bodyTabsin its dependency array and callssetBodyTabs/saveBodyTabsToReduxwithin. While the guard at line 122 prevents infinite loops in most cases, this pattern is fragile and hard to reason about. IfbodyModeandactiveTab.bodyTypeoscillate, re-renders could cascade.Consider restructuring so that tab
bodyTypeupdates flow unidirectionally—either from Redux to local state or vice versa—rather than bidirectionally via effects.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 112 - 132, The useEffect that syncs bodyMode into bodyTabs creates a fragile bidirectional update between local state and Redux: when bodyMode changes it maps over bodyTabs and calls setBodyTabs/saveBodyTabsToRedux, yet bodyTabs is also a dependency which can cause cascades. Replace this effect with a unidirectional update: move the body-type update logic into the action that sets bodyMode (or into the handler that triggers bodyMode changes) so that when bodyMode is changed you compute updatedTabs and call saveBodyTabsToRedux/setBodyTabs once (remove bodyTabs from the effect dependency and eliminate setBodyTabs/saveBodyTabsToRedux from any effect); locate symbols useEffect, bodyMode, RAW_BODY_MODES, getActiveTab, activeBodyTab, modeSyncSuppressedRef, setBodyTabs, and saveBodyTabsToRedux to apply this change.
315-323:syncActiveTabBeforeActiondispatches multiple Redux actions synchronously.This works but results in multiple state updates. If performance becomes a concern, consider batching these dispatches or combining into a single action.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 315 - 323, syncActiveTabBeforeAction currently triggers multiple synchronous dispatches causing multiple state updates when called from onRun and onSave; replace this by batching those updates or combining into one thunk so only a single state update occurs. Fix by creating a new thunk action (e.g., runRequestThunk and saveRequestThunk) that performs the syncActiveTabBeforeAction logic and then dispatches sendRequest(item, collection.uid) or saveRequest(item.uid, collection.uid) inside the same thunk, or wrap the existing dispatch calls with react-redux's batch() so onRun and onSave each call dispatch(runRequestThunk(...)) / dispatch(saveRequestThunk(...)) (or dispatch(batch(() => { ... }))) to ensure a single batched update; update references in onRun and onSave to dispatch the new thunk/batched call.packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js (1)
305-326: Request action tests could verify dispatch behavior.These tests confirm the button is clickable and the textarea value is updated, but don't verify that
sendRequestorsaveRequestactions were dispatched. Consider spying on the store or mocking the action creators to assert they're called with correct arguments.As per coding guidelines: "Tests should validate real expected output and observable behaviour."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js` around lines 305 - 326, The tests in RequestBody spec assert UI clicks and textarea value but don't verify that the request actions are dispatched; update the two tests in the "Request Actions with Tabs" suite to spy/mock the Redux dispatch or the action creators (sendRequest and saveRequest) and assert they are called with the expected payload when Run is clicked. Locate the RequestBody component tests that query 'run-button' and 'code-editor-textarea' and replace or augment those assertions by injecting a mocked store or jest.spyOn the action creators (sendRequest, saveRequest) and expect those spies to have been called with the request body content (e.g., '{"ready":"to run"}') and correct item/collection identifiers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 231-241: When recreating the last tab in the block that builds
newBlankTab, validate bodyMode against RAW_BODY_MODES (same approach as
handleAddTab) instead of using bodyMode || 'json'; compute a validated bodyType
(e.g., RAW_BODY_MODES.includes(bodyMode) ? bodyMode : 'json') and set
newBlankTab.bodyType to that value, then continue to call setBodyTabs,
setActiveBodyTab, and saveBodyTabsToRedux with the newBlankTab.
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js`:
- Around line 72-76: The mock state used in the test is missing the tabs.tabs
array which the component reads via useSelector (state.tabs.tabs) and then calls
find; update the test's mock state object in RequestBody/index.spec.js to
include a tabs array (e.g., a tab object whose uid matches 'item-1') so that the
component's find call operates on an array not undefined; locate the mock where
tabs: { activeTabUid: 'item-1' } is defined and add a tabs: [...] entry
containing the matching tab item.
---
Outside diff comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 325-332: In onScroll, focusedTab may be undefined causing
focusedTab.uid to throw; update the onScroll handler (and any callers) to guard
against a missing focusedTab by returning early or using optional chaining.
Specifically, in the onScroll function that dispatches
updateRequestBodyScrollPosition, check that focusedTab exists (e.g., if
(!focusedTab) return) before accessing focusedTab.uid (or use focusedTab?.uid
and handle null case), so dispatch is only called with a valid uid.
---
Nitpick comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 60-64: initializeTabContent() is being called twice during initial
render for bodyTabs and activeBodyTab; compute it once and reuse the result to
avoid duplicate work by creating a single initializer that calls
initializeTabContent() and sets both bodyTabs and activeBodyTab (or by switching
to one combined state object). Specifically, replace the two useState
initializers with a single initializer that invokes initializeTabContent() once,
then calls setBodyTabs/setActiveBodyTab (or returns an object with bodyTabs and
activeBodyTab) using the tabs[0]?.id || 1 value so initializeTabContent is not
invoked twice.
- Around line 112-132: The useEffect that syncs bodyMode into bodyTabs creates a
fragile bidirectional update between local state and Redux: when bodyMode
changes it maps over bodyTabs and calls setBodyTabs/saveBodyTabsToRedux, yet
bodyTabs is also a dependency which can cause cascades. Replace this effect with
a unidirectional update: move the body-type update logic into the action that
sets bodyMode (or into the handler that triggers bodyMode changes) so that when
bodyMode is changed you compute updatedTabs and call
saveBodyTabsToRedux/setBodyTabs once (remove bodyTabs from the effect dependency
and eliminate setBodyTabs/saveBodyTabsToRedux from any effect); locate symbols
useEffect, bodyMode, RAW_BODY_MODES, getActiveTab, activeBodyTab,
modeSyncSuppressedRef, setBodyTabs, and saveBodyTabsToRedux to apply this
change.
- Around line 315-323: syncActiveTabBeforeAction currently triggers multiple
synchronous dispatches causing multiple state updates when called from onRun and
onSave; replace this by batching those updates or combining into one thunk so
only a single state update occurs. Fix by creating a new thunk action (e.g.,
runRequestThunk and saveRequestThunk) that performs the
syncActiveTabBeforeAction logic and then dispatches sendRequest(item,
collection.uid) or saveRequest(item.uid, collection.uid) inside the same thunk,
or wrap the existing dispatch calls with react-redux's batch() so onRun and
onSave each call dispatch(runRequestThunk(...)) /
dispatch(saveRequestThunk(...)) (or dispatch(batch(() => { ... }))) to ensure a
single batched update; update references in onRun and onSave to dispatch the new
thunk/batched call.
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js`:
- Around line 305-326: The tests in RequestBody spec assert UI clicks and
textarea value but don't verify that the request actions are dispatched; update
the two tests in the "Request Actions with Tabs" suite to spy/mock the Redux
dispatch or the action creators (sendRequest and saveRequest) and assert they
are called with the expected payload when Run is clicked. Locate the RequestBody
component tests that query 'run-button' and 'code-editor-textarea' and replace
or augment those assertions by injecting a mocked store or jest.spyOn the action
creators (sendRequest, saveRequest) and expect those spies to have been called
with the request body content (e.g., '{"ready":"to run"}') and correct
item/collection identifiers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: caf1a7f0-8f1d-42a0-840d-d327f99a1f53
📒 Files selected for processing (11)
packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/StyledWrapper.jspackages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.jspackages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.spec.jspackages/bruno-app/src/components/RequestPane/RequestBody/bodyTabs.spec.jspackages/bruno-app/src/components/RequestPane/RequestBody/index.jspackages/bruno-app/src/components/RequestPane/RequestBody/index.spec.jspackages/bruno-app/src/components/RequestPane/RequestBody/integration.spec.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-lang/v2/src/bruToJson.jspackages/bruno-lang/v2/src/jsonToBru.jspackages/bruno-schema/src/collections/index.js
👮 Files not reviewed due to content moderation or server errors (8)
- packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/StyledWrapper.js
- packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.js
- packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.spec.js
- packages/bruno-lang/v2/src/bruToJson.js
- packages/bruno-schema/src/collections/index.js
- packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
- packages/bruno-lang/v2/src/jsonToBru.js
- packages/bruno-app/src/components/RequestPane/RequestBody/bodyTabs.spec.js
There was a problem hiding this comment.
♻️ Duplicate comments (2)
packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js (1)
85-89:⚠️ Potential issue | 🟡 MinorMock state missing
tabs.tabsarray.The component accesses
state.tabs.tabsviauseSelector(line 36 in index.js), but the mock only providesactiveTabUid. While lodash'sfindhandlesundefinedgracefully, the mock should match actual Redux state structure for test fidelity.🐛 Add tabs array to mock
tabs: { + tabs: [{ uid: 'item-1', requestBodyScrollPosition: 0 }], activeTabUid: 'item-1' },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js` around lines 85 - 89, The mock state for the test is missing the tabs.tabs array expected by the component; update the test's mock state where tabs is defined (currently tabs: { activeTabUid: 'item-1' }) to include a tabs array (e.g., tabs: { activeTabUid: 'item-1', tabs: [ { uid: 'item-1', ... } ] }) so useSelector in the component (which reads state.tabs.tabs) gets the same shape as real Redux state and lodash.find receives an array instead of undefined; ensure the inserted tab object has a uid matching activeTabUid to keep the test semantics.packages/bruno-app/src/components/RequestPane/RequestBody/index.js (1)
220-230:⚠️ Potential issue | 🟡 MinorInconsistent
bodyTypefallback when closing last tab.At line 225,
bodyType: bodyMode || 'json'doesn't validate againstRAW_BODY_MODES, unlikehandleAddTab(line 185) which does. IfbodyModeis'formUrlEncoded', the new blank tab will have an invalidbodyType.🐛 Proposed fix
if (bodyTabs.length === 1) { + const fallbackMode = bodyMode && RAW_BODY_MODES.includes(bodyMode) ? bodyMode : 'json'; const newBlankTab = { id: Math.max(0, ...bodyTabs.map((tab) => tab.id)) + 1, name: getNextTabName([]), bodyContent: '', - bodyType: bodyMode || 'json' + bodyType: fallbackMode };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 220 - 230, When closing the last body tab you set bodyType using "bodyMode || 'json'" which doesn't validate allowed modes; update the creation of newBlankTab in the block that handles bodyTabs.length === 1 to validate bodyMode against RAW_BODY_MODES (same logic as handleAddTab) and fall back to 'json' if bodyMode is not included in RAW_BODY_MODES; modify the assignment for bodyType in the newBlankTab object so the value is either a valid RAW_BODY_MODES entry or 'json' before calling setBodyTabs, setActiveBodyTab, and saveBodyTabsToRedux.
🧹 Nitpick comments (5)
packages/bruno-app/src/components/RequestPane/RequestBody/index.js (2)
68-70: Consider using lazy initializer for useState instead of useMemo.The
useMemowith empty deps computesinitialTabsonce, then it's used to initializeuseState. The idiomatic React pattern is a lazy initializer:♻️ Optional: Use lazy initializer
- const initialTabs = useMemo(() => initializeTabContent(), []); - const [bodyTabs, setBodyTabs] = useState(initialTabs); - const [activeBodyTab, setActiveBodyTab] = useState(initialTabs[0]?.id || 1); + const [bodyTabs, setBodyTabs] = useState(() => initializeTabContent()); + const [activeBodyTab, setActiveBodyTab] = useState(() => { + const tabs = initializeTabContent(); + return tabs[0]?.id || 1; + });This avoids the exhaustive-deps lint warning and is a more standard pattern for initial state that depends on props.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 68 - 70, The code currently computes initialTabs via useMemo and then uses it to initialize state; instead replace that with lazy initializers on the useState calls so the initializer function initializeTabContent is invoked only once at mount: call useState(() => initializeTabContent()) for bodyTabs (update references to bodyTabs and setBodyTabs) and initialize activeBodyTab with a lazy initializer that derives the id from the same initializeTabContent() result (e.g. useState(() => initializeTabContent()[0]?.id || 1)) and remove the separate useMemo and initialTabs variable.
126-165: Complex sync logic would benefit from clarifying comments.The
useEffecthandles several scenarios: external body mode changes, tab switches, and content synchronization. The ref-based tracking (lastChangeSourceRef,lastSyncedTabIdRef, etc.) prevents feedback loops but makes the flow hard to follow.Consider adding brief inline comments explaining when each branch triggers:
// Branch 1: External bodyMode change → update active tab's bodyType if (bodyModeIsRaw && activeTabMode && activeTab.bodyType !== bodyMode && lastChangeSourceRef.current !== 'tab-switch') { ... } // Branch 2: Tab switch → sync bodyMode to match active tab's bodyType if (activeTabMode && bodyModeIsRaw && bodyMode !== activeTabMode) { ... }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 126 - 165, The useEffect sync logic (useEffect, getActiveTab, lastChangeSourceRef, lastSyncedTabIdRef, lastSyncedContentRef) is hard to follow—add concise inline comments above each major branch to explain intent: before the first if that updates bodyTabs/saveBodyTabsToRedux explain "Branch 1: external bodyMode change → update active tab's bodyType (avoid when tab-switch initiated)"; before the next if that dispatches updateRequestBodyMode explain "Branch 2: tab switch → sync global bodyMode to active tab's bodyType (mark lastChangeSourceRef to prevent feedback)"; and before the final block that dispatches updateRequestBody explain "Branch 3: content sync → push active tab bodyContent to store only when content or tab id changed (use lastSyncedTabIdRef/lastSyncedContentRef)". Keep comments short and place them directly above the corresponding if/blocks (referencing activeBodyTab, bodyMode, bodyTabs, dispatch, updateRequestBodyMode, updateRequestBody) so future readers can quickly map behavior to the ref guards.packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js (2)
200-244: Good rename test coverage; consider adding Escape key cancel test.The rename tests cover double-click entry, Enter submission, and duplicate name handling. For completeness, consider testing:
- Escape key cancels rename (restores original name)
- Empty/whitespace-only rename is rejected
♻️ Optional: Add Escape cancel test
it('should cancel rename on Escape key', async () => { renderWithProviders(<RequestBody item={mockItem} collection={mockCollection} />); const tabElement = screen.getByText('Body 1'); fireEvent.doubleClick(tabElement); const input = screen.getByDisplayValue('Body 1'); fireEvent.change(input, { target: { value: 'Changed Name' } }); fireEvent.keyDown(input, { key: 'Escape' }); await waitFor(() => { expect(screen.getByText('Body 1')).toBeInTheDocument(); expect(screen.queryByText('Changed Name')).not.toBeInTheDocument(); }); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js` around lines 200 - 244, Add a new unit test in RequestBody's index.spec.js to verify that pressing Escape cancels an in-progress rename: render the RequestBody (use renderWithProviders with mockItem), locate the tab by text (screen.getByText('Body 1')), trigger edit with fireEvent.doubleClick, change the input value (screen.getByDisplayValue('Body 1') → fireEvent.change to 'Changed Name'), simulate pressing Escape with fireEvent.keyDown(input, { key: 'Escape' }), and assert via waitFor that the original name ('Body 1') is still in the document and the changed name is not (screen.queryByText('Changed Name') should be null). Include this as a new it('should cancel rename on Escape key', ...) test near the existing Tab Renaming tests.
273-283: Test clicks a hidden close button.When there's only one tab, the close button has
display: none(line 197 in BodyTabs). This test still passes becausefireEventignores visibility, but it tests an unreachable UI path.If the intent is to test closing the last tab, use the context menu path instead, or update BodyTabs to allow this via button.
♻️ Alternative: Test via context menu or remove
Option 1: Remove this test since users can't click a hidden button.
Option 2: Trigger close via context menu (requires updating the mock or exposing context menu interaction).
Option 3: If closing last tab via button should be allowed, update BodyTabs to not hide the button.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js` around lines 273 - 283, The test in RequestBody/index.spec.js simulates clicking the hidden "Close tab" button (getByTitle('Close tab')) which is not visible when there's only one tab because BodyTabs hides it; update the test to exercise a reachable user path: either remove the test, or replace the fireEvent.click on the hidden button with a context-menu close action by opening the tab context menu and triggering the "Close tab" command (mock the context menu handler used by BodyTabs or call the same close handler exposed by RequestBody/BodyTabs), ensuring you assert that "Body 1" is recreated and code-editor textarea is empty; reference the RequestBody component and BodyTabs' context-menu/close handler to locate and wire the correct interaction.packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.js (1)
145-159: Context menu "Close" allows closing the last tab while the button is hidden.The close button is hidden when
tabs.length === 1(line 197), but the context menu "Close" item has nodisabledcondition. Users can still close the last tab via right-click.If this is intentional, no change needed. If you want consistent behavior, disable "Close" when it's the only tab:
♻️ Optional: Disable close in context menu for last tab
{ id: 'close', label: 'Close', + disabled: tabs.length <= 1, onClick: () => { if (onTabClose && contextMenuTabId != null) onTabClose(contextMenuTabId); } },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.js` around lines 145 - 159, The context-menu "Close" item currently allows closing the last tab because it lacks a disabled check; update the menu item with id 'close' (the entry that calls onTabClose with contextMenuTabId) to be disabled when tabs.length <= 1 (same rule used for the 'close-others' item), or alternatively add a guard before calling onTabClose to skip/return when tabs.length <= 1; reference the 'onTabClose' callback, 'contextMenuTabId', and 'tabs.length' when implementing the check so the behavior matches the hidden close button.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 220-230: When closing the last body tab you set bodyType using
"bodyMode || 'json'" which doesn't validate allowed modes; update the creation
of newBlankTab in the block that handles bodyTabs.length === 1 to validate
bodyMode against RAW_BODY_MODES (same logic as handleAddTab) and fall back to
'json' if bodyMode is not included in RAW_BODY_MODES; modify the assignment for
bodyType in the newBlankTab object so the value is either a valid RAW_BODY_MODES
entry or 'json' before calling setBodyTabs, setActiveBodyTab, and
saveBodyTabsToRedux.
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js`:
- Around line 85-89: The mock state for the test is missing the tabs.tabs array
expected by the component; update the test's mock state where tabs is defined
(currently tabs: { activeTabUid: 'item-1' }) to include a tabs array (e.g.,
tabs: { activeTabUid: 'item-1', tabs: [ { uid: 'item-1', ... } ] }) so
useSelector in the component (which reads state.tabs.tabs) gets the same shape
as real Redux state and lodash.find receives an array instead of undefined;
ensure the inserted tab object has a uid matching activeTabUid to keep the test
semantics.
---
Nitpick comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.js`:
- Around line 145-159: The context-menu "Close" item currently allows closing
the last tab because it lacks a disabled check; update the menu item with id
'close' (the entry that calls onTabClose with contextMenuTabId) to be disabled
when tabs.length <= 1 (same rule used for the 'close-others' item), or
alternatively add a guard before calling onTabClose to skip/return when
tabs.length <= 1; reference the 'onTabClose' callback, 'contextMenuTabId', and
'tabs.length' when implementing the check so the behavior matches the hidden
close button.
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 68-70: The code currently computes initialTabs via useMemo and
then uses it to initialize state; instead replace that with lazy initializers on
the useState calls so the initializer function initializeTabContent is invoked
only once at mount: call useState(() => initializeTabContent()) for bodyTabs
(update references to bodyTabs and setBodyTabs) and initialize activeBodyTab
with a lazy initializer that derives the id from the same initializeTabContent()
result (e.g. useState(() => initializeTabContent()[0]?.id || 1)) and remove the
separate useMemo and initialTabs variable.
- Around line 126-165: The useEffect sync logic (useEffect, getActiveTab,
lastChangeSourceRef, lastSyncedTabIdRef, lastSyncedContentRef) is hard to
follow—add concise inline comments above each major branch to explain intent:
before the first if that updates bodyTabs/saveBodyTabsToRedux explain "Branch 1:
external bodyMode change → update active tab's bodyType (avoid when tab-switch
initiated)"; before the next if that dispatches updateRequestBodyMode explain
"Branch 2: tab switch → sync global bodyMode to active tab's bodyType (mark
lastChangeSourceRef to prevent feedback)"; and before the final block that
dispatches updateRequestBody explain "Branch 3: content sync → push active tab
bodyContent to store only when content or tab id changed (use
lastSyncedTabIdRef/lastSyncedContentRef)". Keep comments short and place them
directly above the corresponding if/blocks (referencing activeBodyTab, bodyMode,
bodyTabs, dispatch, updateRequestBodyMode, updateRequestBody) so future readers
can quickly map behavior to the ref guards.
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js`:
- Around line 200-244: Add a new unit test in RequestBody's index.spec.js to
verify that pressing Escape cancels an in-progress rename: render the
RequestBody (use renderWithProviders with mockItem), locate the tab by text
(screen.getByText('Body 1')), trigger edit with fireEvent.doubleClick, change
the input value (screen.getByDisplayValue('Body 1') → fireEvent.change to
'Changed Name'), simulate pressing Escape with fireEvent.keyDown(input, { key:
'Escape' }), and assert via waitFor that the original name ('Body 1') is still
in the document and the changed name is not (screen.queryByText('Changed Name')
should be null). Include this as a new it('should cancel rename on Escape key',
...) test near the existing Tab Renaming tests.
- Around line 273-283: The test in RequestBody/index.spec.js simulates clicking
the hidden "Close tab" button (getByTitle('Close tab')) which is not visible
when there's only one tab because BodyTabs hides it; update the test to exercise
a reachable user path: either remove the test, or replace the fireEvent.click on
the hidden button with a context-menu close action by opening the tab context
menu and triggering the "Close tab" command (mock the context menu handler used
by BodyTabs or call the same close handler exposed by RequestBody/BodyTabs),
ensuring you assert that "Body 1" is recreated and code-editor textarea is
empty; reference the RequestBody component and BodyTabs' context-menu/close
handler to locate and wire the correct interaction.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 85197787-dac0-42de-8045-e5a414fd282a
📒 Files selected for processing (5)
packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/StyledWrapper.jspackages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.jspackages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.spec.jspackages/bruno-app/src/components/RequestPane/RequestBody/index.jspackages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js
✅ Files skipped from review due to trivial changes (2)
- packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/StyledWrapper.js
- packages/bruno-app/src/components/RequestPane/RequestBody/BodyTabs/index.spec.js
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js (1)
319-344: Assertions don't verify the claimed Redux sync behavior.Both tests claim to validate Redux state updates but only assert on UI element presence. The first test checks
stateis defined (always true), and the second only verifies the textarea value — neither confirms actual Redux state changes.Consider verifying the Redux state slice or dispatched actions if testing Redux sync is the intent.
♻️ Suggested stronger assertions
it('should handle run action with tabbed content and update Redux state', async () => { const store = createMockStore(); renderWithProviders(<RequestBody item={mockItem} collection={mockCollection} />, store); const runButton = screen.getByTestId('run-button'); fireEvent.click(runButton); - expect(runButton).toBeInTheDocument(); - const state = store.getState(); - expect(state).toBeDefined(); + // Verify button click occurred (sanity check) + expect(runButton).toBeInTheDocument(); + // Optionally verify Redux state if sync behavior is critical + const state = store.getState(); + const collection = state.collections.collections.find((c) => c.uid === 'collection-1'); + // Add assertion on expected draft/body state if this test is meant to verify Redux sync }); it('should sync active tab content to Redux before running request', async () => { const store = createMockStore(); renderWithProviders(<RequestBody item={mockItem} collection={mockCollection} />, store); const textarea = screen.getByTestId('code-editor-textarea'); fireEvent.change(textarea, { target: { value: '{"ready": "to run"}' } }); const runButton = screen.getByTestId('run-button'); fireEvent.click(runButton); - expect(textarea).toHaveValue('{"ready": "to run"}'); + expect(textarea).toHaveValue('{"ready": "to run"}'); + // Consider verifying the Redux draft body matches the edited content });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js` around lines 319 - 344, The tests for RequestBody (renderWithProviders + createMockStore) claim to verify Redux syncing but never assert on the store; update these specs to inspect the mock store instead of only UI elements: after clicking the element with test id 'run-button' (and after changing 'code-editor-textarea'), call the mock store's API (e.g., store.getActions() or store.getState()) to assert that the expected action was dispatched or that the specific Redux slice (e.g., the request/activeTab or request.body field) was updated to '{"ready": "to run"}'; reference RequestBody, run-button, code-editor-textarea, createMockStore and store.getState/getActions to locate where to add those stronger assertions.packages/bruno-app/src/components/RequestPane/RequestBody/index.js (1)
126-165: Consider extracting this sync logic to a custom hook.This
useEffectmanages three concerns: syncing tab bodyType to Redux mode, syncing Redux mode to tabs, and syncing content. While the ref-based guards prevent loops correctly, the cognitive load is high.If this grows further, extracting to a
useTabReduxSynchook would improve testability and readability. Not urgent given it works, but worth noting for maintainability.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 126 - 165, The large useEffect in RequestBody handles three separate sync responsibilities and should be extracted into a custom hook (e.g., useTabReduxSync) to improve readability and testability: create useTabReduxSync that encapsulates the logic currently inside the useEffect (including calls to getActiveTab, checks against RAW_BODY_MODES, updates via setBodyTabs/saveBodyTabsToRedux, and dispatching updateRequestBodyMode and updateRequestBody) and accepts needed inputs (bodyTabs, activeBodyTab, bodyMode, item.uid, collection.uid, dispatch, setBodyTabs, saveBodyTabsToRedux and the refs lastChangeSourceRef/lastSyncedTabIdRef/lastSyncedContentRef) then replace the useEffect with a single call to useTabReduxSync in RequestBody; keep all guard semantics (lastChangeSourceRef checks, lastSynced* refs) and preserve the exact update behavior for bodyType and bodyContent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 263-280: The duplicate tab naming in handleDuplicateTab creates
identical names like "X (copy)" on multiple duplicates; update
handleDuplicateTab to generate a unique name before inserting by deriving
baseName from tabToDuplicate.name and scanning bodyTabs for existing names that
match the baseName + " (copy)" or " (copy N)" pattern, compute the next
available integer suffix (use " (copy)" for the first duplicate and " (copy 2)",
" (copy 3)", etc. thereafter), set newTab.name to that unique value, then
proceed to setBodyTabs(newTabs), setActiveBodyTab(newTabId), and
saveBodyTabsToRedux(newTabs).
---
Nitpick comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 126-165: The large useEffect in RequestBody handles three separate
sync responsibilities and should be extracted into a custom hook (e.g.,
useTabReduxSync) to improve readability and testability: create useTabReduxSync
that encapsulates the logic currently inside the useEffect (including calls to
getActiveTab, checks against RAW_BODY_MODES, updates via
setBodyTabs/saveBodyTabsToRedux, and dispatching updateRequestBodyMode and
updateRequestBody) and accepts needed inputs (bodyTabs, activeBodyTab, bodyMode,
item.uid, collection.uid, dispatch, setBodyTabs, saveBodyTabsToRedux and the
refs lastChangeSourceRef/lastSyncedTabIdRef/lastSyncedContentRef) then replace
the useEffect with a single call to useTabReduxSync in RequestBody; keep all
guard semantics (lastChangeSourceRef checks, lastSynced* refs) and preserve the
exact update behavior for bodyType and bodyContent.
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js`:
- Around line 319-344: The tests for RequestBody (renderWithProviders +
createMockStore) claim to verify Redux syncing but never assert on the store;
update these specs to inspect the mock store instead of only UI elements: after
clicking the element with test id 'run-button' (and after changing
'code-editor-textarea'), call the mock store's API (e.g., store.getActions() or
store.getState()) to assert that the expected action was dispatched or that the
specific Redux slice (e.g., the request/activeTab or request.body field) was
updated to '{"ready": "to run"}'; reference RequestBody, run-button,
code-editor-textarea, createMockStore and store.getState/getActions to locate
where to add those stronger assertions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7bd07aff-0a79-4107-803c-178d870cef75
📒 Files selected for processing (2)
packages/bruno-app/src/components/RequestPane/RequestBody/index.jspackages/bruno-app/src/components/RequestPane/RequestBody/index.spec.js
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
packages/bruno-app/src/components/RequestPane/RequestBody/index.js (3)
126-165: Complex sync logic works but could benefit from inline comments.The useEffect handles multiple sync scenarios (external bodyMode change vs tab-switch, content persistence) using refs as guards. The logic is correct, but the interactions between
lastChangeSourceRef,lastSyncedTabIdRef, andlastSyncedContentRefare non-obvious. A brief comment block explaining the state machine would help future maintainers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 126 - 165, Add a short explanatory comment block inside the useEffect above the sync logic describing the state machine and the purpose of the refs: explain that lastChangeSourceRef distinguishes programmatic tab-switch updates from user-driven bodyMode changes, lastSyncedTabIdRef and lastSyncedContentRef prevent duplicate dispatches of updateRequestBody, and how bodyTabs/activeBodyTab/bodyMode/RAW_BODY_MODES interact to decide when to call setBodyTabs/saveBodyTabsToRedux or dispatch updateRequestBodyMode/updateRequestBody; reference the key symbols useEffect, lastChangeSourceRef, lastSyncedTabIdRef, lastSyncedContentRef, bodyTabs, activeBodyTab, bodyMode, RAW_BODY_MODES, setBodyTabs, saveBodyTabsToRedux, updateRequestBodyMode, updateRequestBody so future maintainers can quickly understand each branch's intent.
117-120: Stale closure:bodyModenot in useMemo dependencies.
saveBodyTabsToReduxusesbodyModeat line 104 for its fallback logic. SincebodyModeisn't in the dependency array, whenbodyModechanges (whileitem.uid/collection.uidstay the same), the debounced function will capture stalebodyModevalues.♻️ Proposed fix
const debouncedSaveBodyTabs = useMemo( () => debounce((tabs) => saveBodyTabsToRedux(tabs), 300), - [item.uid, collection.uid] + [item.uid, collection.uid, bodyMode] );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 117 - 120, The debouncedSaveBodyTabs closure captures a stale bodyMode because bodyMode is not listed in the useMemo deps; update the useMemo that creates debouncedSaveBodyTabs to include bodyMode (i.e., [item.uid, collection.uid, bodyMode]) so debouncedSaveBodyTabs recreates when bodyMode changes, ensuring saveBodyTabsToRedux (which reads bodyMode) uses the current value; alternatively, ensure saveBodyTabsToRedux is a stable callback that reads bodyMode from up-to-date state, but the simplest fix is adding bodyMode to the dependency array of the useMemo that defines debouncedSaveBodyTabs.
316-350: Consider canceling pending debounced saves before sync.
syncActiveTabBeforeActioncallssaveBodyTabsToReduxdirectly, but ifdebouncedSaveBodyTabshas a pending flush from recent edits, you could get out-of-order dispatches. Flush or cancel the debounce first.🛡️ Optional safeguard
const syncActiveTabBeforeAction = () => { + debouncedSaveBodyTabs.cancel(); const currentActiveTab = bodyTabs.find((tab) => tab.id === activeBodyTab);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around lines 316 - 350, syncActiveTabBeforeAction can trigger saveBodyTabsToRedux while a pending debounced save (debouncedSaveBodyTabs) is queued, causing out-of-order dispatches; before calling saveBodyTabsToRedux, ensure any pending debounce is flushed or canceled. Modify syncActiveTabBeforeAction to call debouncedSaveBodyTabs.flush() (or debouncedSaveBodyTabs.cancel() then call saveBodyTabsToRedux immediately) prior to saveBodyTabsToRedux, keeping the existing updates to updateRequestBodyMode/updateRequestBody and lastSyncedTabIdRef/lastSyncedContentRef intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 386-388: The JSX in RequestBody currently uses the Tailwind color
class text-gray-500; replace that with a themed styled-class: add a
.placeholder-message rule in StyledWrapper.js that sets color: ${(props) =>
props.theme.colors.text.muted} and then update the JSX div in RequestBody (the
placeholder message element) to remove text-gray-500 and use className
"placeholder-message" alongside existing layout classes (e.g., keep "text-center
py-8"). Ensure the styled wrapper is applied to the component so the theme prop
is available.
---
Nitpick comments:
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js`:
- Around line 126-165: Add a short explanatory comment block inside the
useEffect above the sync logic describing the state machine and the purpose of
the refs: explain that lastChangeSourceRef distinguishes programmatic tab-switch
updates from user-driven bodyMode changes, lastSyncedTabIdRef and
lastSyncedContentRef prevent duplicate dispatches of updateRequestBody, and how
bodyTabs/activeBodyTab/bodyMode/RAW_BODY_MODES interact to decide when to call
setBodyTabs/saveBodyTabsToRedux or dispatch
updateRequestBodyMode/updateRequestBody; reference the key symbols useEffect,
lastChangeSourceRef, lastSyncedTabIdRef, lastSyncedContentRef, bodyTabs,
activeBodyTab, bodyMode, RAW_BODY_MODES, setBodyTabs, saveBodyTabsToRedux,
updateRequestBodyMode, updateRequestBody so future maintainers can quickly
understand each branch's intent.
- Around line 117-120: The debouncedSaveBodyTabs closure captures a stale
bodyMode because bodyMode is not listed in the useMemo deps; update the useMemo
that creates debouncedSaveBodyTabs to include bodyMode (i.e., [item.uid,
collection.uid, bodyMode]) so debouncedSaveBodyTabs recreates when bodyMode
changes, ensuring saveBodyTabsToRedux (which reads bodyMode) uses the current
value; alternatively, ensure saveBodyTabsToRedux is a stable callback that reads
bodyMode from up-to-date state, but the simplest fix is adding bodyMode to the
dependency array of the useMemo that defines debouncedSaveBodyTabs.
- Around line 316-350: syncActiveTabBeforeAction can trigger saveBodyTabsToRedux
while a pending debounced save (debouncedSaveBodyTabs) is queued, causing
out-of-order dispatches; before calling saveBodyTabsToRedux, ensure any pending
debounce is flushed or canceled. Modify syncActiveTabBeforeAction to call
debouncedSaveBodyTabs.flush() (or debouncedSaveBodyTabs.cancel() then call
saveBodyTabsToRedux immediately) prior to saveBodyTabsToRedux, keeping the
existing updates to updateRequestBodyMode/updateRequestBody and
lastSyncedTabIdRef/lastSyncedContentRef intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a41d3cb7-2b9c-4539-be08-6c5eb10246d4
📒 Files selected for processing (1)
packages/bruno-app/src/components/RequestPane/RequestBody/index.js
| <div className="text-center py-8 text-gray-500"> | ||
| Select a body type from the dropdown above to start adding content. | ||
| </div> |
There was a problem hiding this comment.
Tailwind color class violates coding guidelines.
text-gray-500 defines color via Tailwind, but guidelines specify Tailwind should only be used for layout and colors should come from styled components' theme prop.
🎨 Proposed fix using styled component
Add a styled element in StyledWrapper.js:
.placeholder-message {
color: ${(props) => props.theme.colors.text.muted};
}Then update the JSX:
- <div className="text-center py-8 text-gray-500">
+ <div className="text-center py-8 placeholder-message">
Select a body type from the dropdown above to start adding content.
</div>As per coding guidelines: "Tailwind classes are used specifically for layout-based styles" and "Tailwind classes shouldn't define colors."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bruno-app/src/components/RequestPane/RequestBody/index.js` around
lines 386 - 388, The JSX in RequestBody currently uses the Tailwind color class
text-gray-500; replace that with a themed styled-class: add a
.placeholder-message rule in StyledWrapper.js that sets color: ${(props) =>
props.theme.colors.text.muted} and then update the JSX div in RequestBody (the
placeholder message element) to remove text-gray-500 and use className
"placeholder-message" alongside existing layout classes (e.g., keep "text-center
py-8"). Ensure the styled wrapper is applied to the component so the theme prop
is available.
|
Hi @helloanoop, @lohit-bruno , @naman-bruno , @bijin-bruno – I have just rebased this PR with the latest As noted in the issue discussion [#5657], there is significant demand for this feature, and it addresses a frequent pain point for developers and QA. Because this area of the project is evolving quickly, I am prioritizing keeping the PR up to date; however, I would appreciate a review at your earliest convenience to prevent further merge conflicts. Thanks for your time! |
Description
Request body editor: browser-style tabs for multiple payloads
This PR enhances Bruno's request body editor by adding a browser-style tab system for all supported body types (JSON, text, XML, etc.). Users can create, rename, switch, and close multiple body tabs; only the active tab is used when sending a request.
Key changes
Body 1,Body 2, …) and closed.Body 1.Schema / compatibility
Body 1so nothing breaks.Tests / verification
Notes for reviewers
Body 1is created automatically.Body 1.Demo
Bruno.tabbed.body.demo.mp4
Resolves #5657
Contribution Checklist:
Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.
Publishing to New Package Managers
Please see here for more information.
Summary by CodeRabbit
New Features
Schema / Format
Tests