feat: add response data type selector in response viewer#6100
feat: add response data type selector in response viewer#6100bijin-bruno merged 26 commits intousebruno:mainfrom
Conversation
b7fba59 to
01624b2
Compare
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js
Outdated
Show resolved
Hide resolved
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js
Outdated
Show resolved
Hide resolved
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
Outdated
Show resolved
Hide resolved
tests/response/response-format-select-and-preview/fixtures/collection/bruno.json
Show resolved
Hide resolved
dc5bcea to
d079708
Compare
WalkthroughAdds response format detection, formatting utilities, selector UI, multiple preview components (HTML/JSON/XML/Text/Video), response action hooks/components (copy/clear/download/layout), QueryResponse wrapper, and Playwright tests/fixtures for format selection and previews. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant ResponsePane
participant Detector as ResponseUtils
participant QueryResponse
participant Selector as TypeSelector
participant Preview as PreviewComponent
User->>ResponsePane: response arrives (dataBuffer, headers)
ResponsePane->>Detector: detectContentTypeFromBuffer / getDefaultResponseFormat
Detector-->>ResponsePane: initialFormat, previewOptions
ResponsePane->>QueryResponse: render(selectedFormat, selectedTab, codeMirrorMode)
QueryResponse->>Selector: render format selector
User->>Selector: choose format
Selector->>QueryResponse: onFormatChange(format)
QueryResponse->>Preview: mount appropriate PreviewComponent
Preview->>Detector: validate/format (isValidHtml / JSON.parse / prettify / formatHexView)
Preview-->>User: display rendered preview or ErrorAlert
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (18)
tests/response/large-response-crash-prevention.spec.ts (1)
14-42: Consider addingtest.stepfor better report readability.Per coding guidelines, wrapping logical sections (create collection, create request, send request, verify warning) in
test.stepwould improve generated report clarity.packages/bruno-app/src/utils/common/index.js (3)
327-424: Consider extracting duplicated large-response handling logic.The same pattern appears for
html,javascript, andtext/rawmodes:if (isVeryLargeResponse) { if (typeof data === 'string') { return data; } if (data === null || data === undefined) { return String(data); } if (typeof data === 'object') { return safeStringifyJSON(data, false); } return String(data); }Extract to a helper function to reduce duplication:
+const handleLargeResponse = (data) => { + if (typeof data === 'string') return data; + if (data === null || data === undefined) return String(data); + if (typeof data === 'object') return safeStringifyJSON(data, false); + return String(data); +}; + // Then use in each mode: if (mode.includes('html')) { - if (isVeryLargeResponse) { - if (typeof data === 'string') { - return data; - } - if (data === null || data === undefined) { - return String(data); - } - if (typeof data === 'object') { - return safeStringifyJSON(data, false); - } - return String(data); - } + if (isVeryLargeResponse) return handleLargeResponse(data);
450-466: Inconsistent function declaration style.These new functions use
functionkeyword while the rest of the file uses arrow function exports (e.g.,export const simpleHash = (str) => {). Consider using arrow functions for consistency.-export function prettifyHtmlString(htmlString) { +export const prettifyHtmlString = (htmlString) => { if (typeof htmlString !== 'string') return htmlString; // ... -}; +};
385-400: Empty string return on error may hide formatting issues.When hex encoding fails, returning
''silently could confuse users. Consider logging a warning or returning a more informative fallback.} catch (stringError) { - return ''; + console.warn('Failed to format hex view:', stringError); + return ''; } } - return ''; + console.warn('Failed to format hex view for non-string data'); + return '';packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
3-23: Consider adding a display name for better debugging.The component uses
memobut lacks adisplayName, which can make React DevTools harder to navigate.Apply this diff:
const TextPreview = memo(({ data }) => { const displayData = useMemo(() => { if (data === null || data === undefined) { return String(data); } if (typeof data === 'object') { try { return JSON.stringify(data); } catch { return String(data); } } return String(data); }, [data]); return ( <div className="p-4 font-mono text-[13px] whitespace-pre-wrap break-words overflow-auto overflow-x-hidden w-full max-w-full h-full"> {displayData} </div> ); }); +TextPreview.displayName = 'TextPreview'; + export default TextPreview;packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js (2)
7-25: Extract helper function outside component for better performance.The
validateJsonDatahelper is recreated on every render. Since it doesn't depend on props or state, extracting it outside the component body will improve performance.Apply this diff:
import React from 'react'; import ReactJson from 'react-json-view'; import ErrorAlert from 'components/ErrorAlert/index'; +// Helper function to validate and parse JSON data +const validateJsonData = (data) => { + // If data is already an object or array, use it directly + if (typeof data === 'object' && data !== null) { + return { data, error: null }; + } + + // If data is a string, try to parse it + if (typeof data === 'string') { + try { + const parsed = JSON.parse(data); + return { data: parsed, error: null }; + } catch (e) { + return { data: null, error: `Invalid JSON format: ${e.message}` }; + } + } + + // For other types, return error + return { data: null, error: 'Invalid input. Expected a JSON object, array, or valid JSON string.' }; +}; + const JsonPreview = ({ data, displayedTheme }) => { - // Helper function to validate and parse JSON data - const validateJsonData = (data) => { - // If data is already an object or array, use it directly - if (typeof data === 'object' && data !== null) { - return { data, error: null }; - } - - // If data is a string, try to parse it - if (typeof data === 'string') { - try { - const parsed = JSON.parse(data); - return { data: parsed, error: null }; - } catch (e) { - return { data: null, error: `Invalid JSON format: ${e.message}` }; - } - } - - // For other types, return error - return { data: null, error: 'Invalid input. Expected a JSON object, array, or valid JSON string.' }; - }; - // Validate and parse JSON data const jsonData = validateJsonData(data);
5-61: Consider adding a display name for better debugging.Adding a
displayNamewill make this component easier to identify in React DevTools.Add after the component definition:
const JsonPreview = ({ data, displayedTheme }) => { // ... component implementation }; +JsonPreview.displayName = 'JsonPreview'; + export default JsonPreview;packages/bruno-app/src/components/ErrorAlert/index.js (2)
13-13: Add error handling for JSON.stringify to prevent crashes.
JSON.stringifywill throw ifmessagecontains circular references. Since this component handles error display, it should be resilient to malformed input.Apply this diff:
- <div className="error-message">{typeof message === 'string' ? message : JSON.stringify(message, null, 2)}</div> + <div className="error-message"> + {typeof message === 'string' ? message : (() => { + try { + return JSON.stringify(message, null, 2); + } catch { + return String(message); + } + })()} + </div>
9-9: Consider making margins configurable via props.Hardcoded margin classes (
mt-4 mb-2) reduce reusability. Consider accepting aclassNameprop to allow customization when needed.Example implementation:
-const ErrorAlert = ({ title, message, onClose }) => { +const ErrorAlert = ({ title, message, onClose, className = 'mt-4 mb-2' }) => { if (!message) return null; return ( - <StyledWrapper className="mt-4 mb-2"> + <StyledWrapper className={className}>packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js (1)
1-3: Consolidate React imports.Minor cleanup: merge the two React import statements.
-import React from 'react'; -import { useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react';packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
13-13: Fragile dark theme detection.Comparing against a hardcoded hex value (
'#1e1e1e') is brittle. If the theme's background color changes, this condition breaks.Consider using a dedicated theme property if available (e.g.,
props.theme.mode === 'dark') or define the background colors in the theme itself.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (2)
27-32: MoveisValidTreeDataoutside the component.This function is recreated on every render. Move it to module scope since it doesn't depend on component state or props.
+const isValidTreeData = (data) => { + if (data === null || data === undefined) return false; + if (typeof data === 'object' && !Array.isArray(data)) return true; + if (Array.isArray(data)) return true; + return false; +}; + export default function XmlPreview({ data, defaultExpanded = true }) { // ... - const isValidTreeData = (data) => { - if (data === null || data === undefined) return false; - if (typeof data === 'object' && !Array.isArray(data)) return true; - if (Array.isArray(data)) return true; - return false; - };
175-177: Remove unused variables.
hasArrayValueandarrayLengthare declared but never used. Array nodes are handled earlier at lines 82-99.- // Determine if this node's value is an array - const hasArrayValue = Array.isArray(node); - const arrayLength = hasArrayValue ? node.length : 0;packages/bruno-app/src/components/ButtonDropdown/index.jsx (2)
20-20: UnuseddropdownTippyRef- remove or utilize.The ref is assigned but never read. If intended for programmatic dropdown control (e.g., closing on selection), implement that; otherwise, remove the dead code.
Also applies to: 42-44
46-48: Consider inlininghandleOptionSelect.This wrapper only calls
onChangewithout additional logic. Per coding guidelines, avoid single-line abstractions that only increase the call stack.- const handleOptionSelect = (optionValue) => { - onChange(optionValue); - }; ... - onClick={() => handleOptionSelect(option.value)} + onClick={() => onChange(option.value)}packages/bruno-app/src/utils/response/index.js (1)
213-218: MP4 detection may miss some valid files.The MP4/MOV
ftypbox can have sizes other than 0x18 or 0x20. Consider checking just for theftypsignature at bytes 4-7 regardless of box size.- if (bytes[0] === 0x00 && bytes[1] === 0x00 && bytes[2] === 0x00 - && (bytes[3] === 0x18 || bytes[3] === 0x20) - && bytes[4] === 0x66 && bytes[5] === 0x74 && bytes[6] === 0x79 && bytes[7] === 0x70) { + // ftyp box - check signature at offset 4 regardless of box size + if (bytes[4] === 0x66 && bytes[5] === 0x74 && bytes[6] === 0x79 && bytes[7] === 0x70) { return 'video/mp4'; }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
1-1:useMemois imported but not used.Remove unused import to keep the codebase clean.
-import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react';packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
117-125: UnusedresponseEncodingin dependency array.
responseEncodingis listed as a dependency but not passed toformatResponse. Either remove from deps or pass it to the function if needed.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ErrorAlert/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/utils/common/codemirror.js(1 hunks)packages/bruno-app/src/utils/common/index.js(4 hunks)packages/bruno-app/src/utils/response/index.js(1 hunks)tests/response/large-response-crash-prevention.spec.ts(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/bruno.json(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru(1 hunks)tests/response/response-format-select-and-preview/init-user-data/collection-security.json(1 hunks)tests/response/response-format-select-and-preview/init-user-data/preferences.json(1 hunks)tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts(1 hunks)tests/utils/page/actions.ts(2 hunks)tests/utils/page/locators.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses. Keep 'em tight
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Avoid single line abstractions where all that's being done is increasing the call stack with one additional function
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.jstests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsxpackages/bruno-app/src/components/ErrorAlert/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.jspackages/bruno-app/src/components/ButtonDropdown/index.jsxtests/utils/page/locators.tstests/response/large-response-crash-prevention.spec.tspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.jspackages/bruno-app/src/utils/response/index.jspackages/bruno-app/src/utils/common/index.jspackages/bruno-app/src/utils/common/codemirror.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.jspackages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.jspackages/bruno-app/src/components/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.jstests/utils/page/actions.ts
tests/**/**.*
⚙️ CodeRabbit configuration file
tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:
Follow best practices for Playwright code and e2e automation
Try to reduce usage of
page.waitForTimeout();in code unless absolutely necessary and the locator cannot be found using existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture Example*: Here's an example of possible fixture and test pair
. ├── fixtures │ └── collection │ ├── base.bru │ ├── bruno.json │ ├── collection.bru │ ├── ws-test-request-with-headers.bru │ ├── ws-test-request-with-subproto.bru │ └── ws-test-request.bru ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection ├── headers.spec.ts ├── persistence.spec.ts ├── variable-interpolation │ ├── fixtures │ │ └── collection │ │ ├── environments │ │ ├── bruno.json │ │ └── ws-interpolation-test.bru │ ├── init-user-data │ └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection └── subproto.spec.ts
Files:
tests/response/response-format-select-and-preview/init-user-data/collection-security.jsontests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tstests/response/response-format-select-and-preview/fixtures/collection/request-html.brutests/utils/page/locators.tstests/response/large-response-crash-prevention.spec.tstests/response/response-format-select-and-preview/fixtures/collection/bruno.jsontests/response/response-format-select-and-preview/init-user-data/preferences.jsontests/response/response-format-select-and-preview/fixtures/collection/request-json.brutests/utils/page/actions.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
Multiple expressions per line in JSX are fine – flexibility is nice
Files:
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsxpackages/bruno-app/src/components/ButtonDropdown/index.jsx
🧬 Code graph analysis (8)
packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js (1)
StyledWrapper(3-59)
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (3)
tests/utils/page/actions.ts (6)
closeAllCollections(205-205)openRequest(214-214)sendRequestAndWaitForResponse(210-210)switchToPreviewTab(212-212)switchToEditorTab(213-213)switchResponseFormat(211-211)tests/utils/page/locators.ts (1)
buildCommonLocators(3-65)packages/bruno-app/src/utils/codemirror/brunoVarInfo.js (1)
editorContainer(298-298)
packages/bruno-app/src/components/ErrorAlert/index.js (1)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/components/ButtonDropdown/index.jsx (3)
packages/bruno-app/src/components/Icons/IconCaretDown/index.js (1)
IconCaretDown(3-16)packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js (1)
StyledWrapper(3-7)packages/bruno-app/src/components/Dropdown/index.js (1)
Dropdown(5-25)
packages/bruno-app/src/utils/response/index.js (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
contentType(56-56)packages/bruno-app/src/utils/common/index.js (3)
contentType(94-94)i(21-21)i(498-498)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
parser(287-287)
packages/bruno-app/src/utils/common/codemirror.js (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
contentType(56-56)packages/bruno-app/src/utils/common/index.js (1)
contentType(94-94)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (3)
packages/bruno-app/src/utils/common/index.js (3)
parsed(320-320)i(21-21)i(498-498)packages/bruno-app/src/components/ErrorAlert/index.js (1)
ErrorAlert(5-23)packages/bruno-app/src/utils/response/index.js (2)
parser(156-156)i(79-79)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
HtmlPreview(5-39)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js (1)
JsonPreview(5-61)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
XmlPreview(5-68)
🔇 Additional comments (30)
packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js (1)
6-6: LGTM!The row height increase to 1.75rem properly accommodates the new response format selector dropdown, maintaining adequate vertical space for the enhanced preview UI.
tests/response/large-response-crash-prevention.spec.ts (1)
41-41: LGTM!Good addition of
exact: true- prevents false matches with other buttons like "View Raw" that might contain "View" in the name.tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru (1)
1-23: LGTM!Fixture correctly defines an HTML echo request for testing the HTML preview functionality. The
timeout: 0(no timeout) is acceptable for test fixtures where external service latency may vary.Note: This test depends on
httpfaker.org- ensure this external service is reliable for CI/CD pipelines.tests/response/response-format-select-and-preview/init-user-data/preferences.json (1)
1-6: LGTM!Properly structured test initialization data with the
{{projectRoot}}placeholder for portable collection path resolution.packages/bruno-app/src/utils/common/index.js (3)
7-8: LGTM on prettier integration.Using
prettier/standalonewithparser-babelis the correct approach for browser/Electron environments where the full prettier package isn't available.
106-117: LGTM on JavaScript content-type detection.The pattern correctly matches common JavaScript MIME types (
application/javascript,text/javascript,application/ecmascript,text/ecmascript).
494-511: LGTM on hex view formatting.Clean implementation with proper offset display, hex bytes, and ASCII representation. The 16-byte width is standard for hex dumps.
tests/response/response-format-select-and-preview/init-user-data/collection-security.json (1)
1-10: LGTM!The test configuration is correctly structured with appropriate sandbox settings for testing response format features.
packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js (1)
1-9: LGTM!Clean styled component wrapper with appropriate SVG color inheritance for the caret icon.
tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru (1)
1-17: LGTM!Well-formed Bruno request fixture using the echo endpoint to test JSON response handling.
packages/bruno-app/src/utils/common/codemirror.js (1)
106-107: LGTM!Correctly adds JavaScript content-type detection following the established pattern. The check for both 'javascript' and 'ecmascript' ensures comprehensive coverage of JS MIME types.
tests/utils/page/locators.ts (1)
54-64: LGTM!Well-structured locator additions that follow the existing patterns and conventions. The use of both test IDs and CSS selectors is appropriate for the different element types.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
5-18: LGTM on the HTML preview logic.The webview injection with base URL and the use of
escapeHtmlfor sanitization is well implemented. The dual validation usingisValidHtmlandisValidHtmlSnippetproperly handles both full HTML documents and snippets.tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (3)
1-16: Well-structured test suite following Playwright best practices.Good use of
test.stepfor report readability, locator variables for maintainability, and multiple assertions per step. The serial execution and cleanup inafterAllis appropriate for this stateful UI flow.
18-88: Comprehensive JSON response format coverage.The test covers all format options (JSON, HTML, XML, JavaScript, Raw, Hex, Base64) with both editor and preview verification. The error state assertions (e.g., "Cannot preview as XML" for JSON data) properly validate fallback behavior.
90-160: Thorough HTML response format test.Validates the webview visibility for HTML preview and error states for incompatible formats (e.g., "Cannot preview as JSON"). The Hex and Base64 content assertions provide good regression coverage. Ensure fixture files exist in the expected location at
tests/response/response-format-select-and-preview/fixturesand verify the test follows all Playwright best practices including proper use oftest.step, locator variables, and absence ofpage.waitForTimeout(),page.pause(), ortest.only.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js (1)
8-15: Solid Blob URL lifecycle management.Proper cleanup via
URL.revokeObjectURLin the useEffect return prevents memory leaks. The dependency array correctly includes bothcontentTypeanddataBuffer.packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
28-41: Clean error styling with theme awareness.The error-title and error-message styles appropriately use theme tokens for colors and font sizing.
tests/utils/page/actions.ts (2)
136-144: Clean implementation of openRequest helper.Properly scopes the request locator within the collection context.
152-163: Well-designed sendRequestAndWaitForResponse with configurable options.The default timeout of 15000ms and options object pattern is clean. Good use of
test.stepfor reporting.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
282-362: XML parsing implementation is solid.The
parseXMLStringfunction handles attributes, text nodes, and repeated elements correctly. Error detection viaparsererrorcheck is appropriate.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx (1)
6-39: Clean component structure with good UX patterns.The ToggleSwitch header with conditional title and the IconEye suffix for preview mode provide clear visual feedback. Props are well-named.
Verify the "active" class on line 35 is actually styled, as it may be unused:
className="active mr-[2px]"Search for
.activeclass definitions in CSS/SCSS files in the ResponsePane and ButtonDropdown component directories.packages/bruno-app/src/utils/response/index.js (2)
1-59: LGTM!Clean rule-based MIME type matching with sensible fallback. The pattern is extensible for future content types.
61-70: LGTM!Standard HTML entity escaping implementation - covers the essential characters for XSS prevention.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (2)
64-80: LGTM!Clean separation of the editor rendering path with proper prop forwarding.
121-132: LGTM!Good fallback UX for unsupported content types instead of failing silently.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (4)
14-32: LGTM!Well-structured format options constant with appropriate CodeMirror modes.
48-56: LGTM!Proper error handling for buffer conversion with fallback to null.
66-86: LGTM!Sensible restriction of format options for binary content types.
213-227: LGTM!Clean integration of the new preview system with proper prop forwarding to
QueryResultPreview.
| const ButtonIcon = forwardRef((iconProps, ref) => { | ||
| return ( | ||
| <button | ||
| ref={ref} | ||
| className={classnames('flex items-center gap-1.5 text-xs', | ||
| 'cursor-pointer select-none', | ||
| 'h-7 rounded-[6px] border px-2 transition-colors', | ||
| // Text color | ||
| 'text-gray-700 dark:text-gray-300', | ||
| // Border color | ||
| 'border-gray-300 dark:border-[#343434]', | ||
| // Background color | ||
| // Hover background color | ||
| 'hover:bg-gray-200 dark:hover:bg-[#303030]', | ||
| { | ||
| // Disabled styles | ||
| 'opacity-50 cursor-not-allowed': disabled | ||
| }, | ||
| className)} | ||
| data-testid={props['data-testid']} | ||
| style={style} | ||
| role="button" | ||
| {...iconProps} | ||
| > | ||
| {prefix && ( | ||
| <span>{prefix}</span> | ||
| )} | ||
| <span className="active">{selectedLabel}</span> | ||
| {suffix && ( | ||
| <span>{suffix}</span> | ||
| )} | ||
| <IconCaretDown className="caret ml-1" size={14} strokeWidth={2} /> | ||
| </button> | ||
| ); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Move ButtonIcon outside the component or memoize it.
Defining a forwardRef component inside the render function causes React to recreate it on every render, losing internal state and causing unnecessary DOM updates.
+const ButtonIcon = forwardRef(({ disabled, className, style, prefix, selectedLabel, suffix, ...props }, ref) => {
+ return (
+ <button
+ ref={ref}
+ className={classnames('flex items-center gap-1.5 text-xs',
+ 'cursor-pointer select-none',
+ 'h-7 rounded-[6px] border px-2 transition-colors',
+ 'text-gray-700 dark:text-gray-300',
+ 'border-gray-300 dark:border-[#343434]',
+ 'hover:bg-gray-200 dark:hover:bg-[#303030]',
+ { 'opacity-50 cursor-not-allowed': disabled },
+ className)}
+ data-testid={props['data-testid']}
+ style={style}
+ role="button"
+ {...props}
+ >
+ {prefix && <span>{prefix}</span>}
+ <span className="active">{selectedLabel}</span>
+ {suffix && <span>{suffix}</span>}
+ <IconCaretDown className="caret ml-1" size={14} strokeWidth={2} />
+ </button>
+ );
+});
+ButtonIcon.displayName = 'ButtonIcon';
+
const ButtonDropdown = ({ ... }) => {
// ...
- const ButtonIcon = forwardRef((iconProps, ref) => { ... });
- ButtonIcon.displayName = 'ButtonIcon';Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ButtonDropdown/index.jsx around lines 99 to
133, ButtonIcon is defined inline with forwardRef inside the render which causes
it to be recreated on every render; move the forwardRef component definition out
of the parent component (to module scope) or memoize it with React.memo so it is
stable between renders, ensure it receives needed props (disabled, className,
style, prefix, suffix, selectedLabel, props/data-testid) via parameters rather
than closing over local variables, and update any refs/prop forwarding
accordingly so behavior and styling remain identical.
| <button | ||
| ref={ref} | ||
| className={classnames('flex items-center gap-1.5 text-xs', | ||
| 'cursor-pointer select-none', | ||
| 'h-7 rounded-[6px] border px-2 transition-colors', | ||
| // Text color | ||
| 'text-gray-700 dark:text-gray-300', | ||
| // Border color | ||
| 'border-gray-300 dark:border-[#343434]', | ||
| // Background color | ||
| // Hover background color | ||
| 'hover:bg-gray-200 dark:hover:bg-[#303030]', | ||
| { | ||
| // Disabled styles | ||
| 'opacity-50 cursor-not-allowed': disabled | ||
| }, | ||
| className)} | ||
| data-testid={props['data-testid']} | ||
| style={style} | ||
| role="button" | ||
| {...iconProps} |
There was a problem hiding this comment.
Add disabled attribute to the button for accessibility.
The button has visual disabled styling but lacks the actual disabled attribute, which means keyboard/screen reader users can still interact with it.
<button
ref={ref}
className={classnames(...)}
data-testid={props['data-testid']}
style={style}
role="button"
+ disabled={disabled}
{...iconProps}
>🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ButtonDropdown/index.jsx around lines
101-121, the <button> uses visual disabled styles but lacks the actual disabled
attribute so assistive tech and keyboard users can still interact; update the
element to include disabled={disabled} and also set tabIndex={disabled ? -1 :
undefined} (and optionally aria-disabled={disabled}) so the button is
non-interactive to screen readers and keyboard when disabled while preserving
semantics.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
Outdated
Show resolved
Hide resolved
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
Outdated
Show resolved
Hide resolved
| } else if (typeof data === 'object') { | ||
| displayContent = JSON.stringify(data, null); |
There was a problem hiding this comment.
Missing indentation argument in JSON.stringify.
The comment states "render safely as formatted text", but objects won't be prettified without the third argument.
- } else if (typeof data === 'object') {
- displayContent = JSON.stringify(data, null);
+ } else if (typeof data === 'object') {
+ displayContent = JSON.stringify(data, null, 2);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (typeof data === 'object') { | |
| displayContent = JSON.stringify(data, null); | |
| } else if (typeof data === 'object') { | |
| displayContent = JSON.stringify(data, null, 2); |
🤖 Prompt for AI Agents
In
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js
around lines 24-25, the code uses JSON.stringify(data, null) which omits the
indentation argument so objects are not prettified; update the call to include a
spacing argument (for example JSON.stringify(data, null, 2)) so the object is
rendered as formatted text and remains safe for display.
...ges/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js
Show resolved
Hide resolved
| try { | ||
| // Parse with DOMParser | ||
| const parser = new DOMParser(); | ||
| const doc = parser.parseFromString(trimmed, 'text/html'); | ||
|
|
||
| // Check for parsing errors | ||
| const parseError = doc.querySelector('parsererror'); | ||
| if (parseError) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } catch (error) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
DOMParser with 'text/html' never produces parsererror.
The HTML parser is lenient and won't generate a parsererror element - that behavior is specific to XML parsing. The try-catch and parsererror check are effectively dead code here. Consider removing them or clarifying the intent.
try {
- // Parse with DOMParser
const parser = new DOMParser();
const doc = parser.parseFromString(trimmed, 'text/html');
-
- // Check for parsing errors
- const parseError = doc.querySelector('parsererror');
- if (parseError) {
- return false;
- }
-
+ // HTML parser is lenient; if we reach here with valid tags, consider it valid
return true;
} catch (error) {
return false;
}🤖 Prompt for AI Agents
In packages/bruno-app/src/utils/response/index.js around lines 154-168, the
DOMParser parseFromString(..., 'text/html') branch will never produce a
'parsererror' node and the try-catch/parseError check is therefore dead; remove
the parsererror check and unnecessary try-catch, and either (a) simply return
true after parsing since the HTML parser is lenient, or (b) if strict validation
is required, switch to parsing as XML (e.g., 'application/xml') and
handle/document parseError reliably or use a proper HTML validation library
(htmlparser2/validator) to check for well-formedness — implement one of these
fixes and update comments accordingly.
| /** | ||
| * Switch to the editor tab | ||
| * @param page - The page object | ||
| */ | ||
| const switchToEditorTab = async (page: Page) => { | ||
| await test.step('Switch to editor tab', async () => { | ||
| const responseFormatTab = page.getByTestId('format-response-tab'); | ||
| await responseFormatTab.click(); | ||
| const previewTab = page.getByTestId('preview-response-tab'); | ||
| await previewTab.click(); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Copy-paste bug: switchToEditorTab is identical to switchToPreviewTab.
Both functions click the same preview-response-tab toggle. While this might accidentally work (toggling the switch), the implementation is misleading. If the preview is already off, switchToEditorTab would incorrectly turn it on.
const switchToEditorTab = async (page: Page) => {
await test.step('Switch to editor tab', async () => {
const responseFormatTab = page.getByTestId('format-response-tab');
await responseFormatTab.click();
const previewTab = page.getByTestId('preview-response-tab');
- await previewTab.click();
+ // Only toggle off if preview is currently on
+ const isPreviewOn = await previewTab.isChecked();
+ if (isPreviewOn) {
+ await previewTab.click();
+ }
});
};Alternatively, if the toggle state is managed differently, verify the intended behavior matches the implementation.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In tests/utils/page/actions.ts around lines 191 to 202, the switchToEditorTab
function mistakenly clicks the preview tab (copy-paste from switchToPreviewTab);
change the second click to the editor tab test id (e.g., 'editor-response-tab')
so it explicitly selects the editor view, and verify the test-id name matches
the app implementation; keep the outer test.step and await calls intact.
| @@ -0,0 +1,159 @@ | |||
| import React, { useRef, forwardRef } from 'react'; | |||
There was a problem hiding this comment.
Please move generic components to ui folder
| 'cursor-pointer select-none', | ||
| 'h-7 rounded-[6px] border px-2 transition-colors', | ||
| // Text color | ||
| 'text-gray-700 dark:text-gray-300', |
There was a problem hiding this comment.
No tailwind class colors please
| border-top: 1px solid transparent; | ||
| border-right: 1px solid transparent; | ||
| border-bottom: 1px solid transparent; | ||
| border-radius: 0.375rem; |
There was a problem hiding this comment.
Please use base radius from theme
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
53-63: Pretty‑print objects in the text fallback (same as earlier review)The comment says “render safely as formatted text”, but
JSON.stringify(data, null)won’t pretty‑print objects; it produces a single‑line JSON string.You can pass a spacing argument to improve readability:
- } else if (typeof data === 'object') { - displayContent = JSON.stringify(data, null); + } else if (typeof data === 'object') { + displayContent = JSON.stringify(data, null, 2);This matches the intent of a “formatted” safe text fallback without changing behavior for other types.
🧹 Nitpick comments (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (2)
10-28: Tighten MutationObserver usage and avoid redundant state updatesThe drag detection works, but observing
watchTargetwithsubtree: trueplus unguardedsetIsDraggingcan cause a lot of state churn when classes change frequently.Consider:
- Deduping updates so React state only changes when the value actually flips.
- Running an initial
checkDragging()soisDraggingis correct even before the first mutation.For example:
useEffect(() => { if (!webviewContainerRef.current) return; const checkDragging = () => { - const hasDraggingParent = webviewContainerRef.current?.closest('.dragging'); - setIsDragging(!!hasDraggingParent); + const hasDraggingParent = !!webviewContainerRef.current?.closest('.dragging'); + setIsDragging(prev => (prev === hasDraggingParent ? prev : hasDraggingParent)); }; const watchTarget = webviewContainerRef.current.closest('[class*="flex"]') || document.body; const mutationObserver = new MutationObserver(checkDragging); mutationObserver.observe(watchTarget, { attributes: true, attributeFilter: ['class'], subtree: true }); + checkDragging(); + return () => mutationObserver.disconnect(); }, []);If feasible, also narrowing
watchTargetto the smallest container that actually toggles the.draggingclass would help keep observer load down.
30-36: Guard HTML detection to string data before running validatorsRight now
isValidHtml(data) || isValidHtmlSnippet(data)is evaluated for anydatatype. If those utilities are written for strings, it’s safer and a bit cheaper to short‑circuit non‑string values before running HTML heuristics and calling.includes:- if (isValidHtml(data) || isValidHtmlSnippet(data)) { + if (typeof data === 'string' && (isValidHtml(data) || isValidHtmlSnippet(data))) {This makes the intent explicit and avoids surprising behavior if non‑string data is ever passed in.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses. Keep 'em tight
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Avoid single line abstractions where all that's being done is increasing the call stack with one additional function
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - macOS
- GitHub Check: SSL Tests - Linux
e3842ec to
88ce6eb
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (7)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
8-8: Use shared theme border radius instead of literal rem
border-radius: 0.375rem;should likely use the base radius from the theme (whatever the canonical token is here) for consistency with the rest of the design system, instead of an inline rem value. Based on learnings, surface radii should come from theme tokens.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
57-58: Add indentation argument toJSON.stringifyfor readable output.The fallback text rendering stringifies objects without indentation, making complex objects harder to read.
} else if (typeof data === 'object') { - displayContent = JSON.stringify(data, null); + displayContent = JSON.stringify(data, null, 2); }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
228-262: DOM manipulation bypasses React reconciliation.The array collapse/expand logic uses direct DOM queries (
parentElement.querySelector,classList.toggle) instead of React state. Extract this into a separate component with its ownuseStatefor expansion control, consistent with howXmlNodehandles expansion.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
87-88: Addaltattribute for accessibility.The image element is missing an
altattribute, which is required for screen readers.- return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} />; + return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} alt="Response preview" />;packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (3)
107-115: ConditiondetectedContentType !== undefinedis always true.
detectContentTypeFromBufferreturnsstring | null, neverundefined. The check should be!== nullor simplified.useEffect(() => { - if (isInitialRun && (detectedContentType !== undefined && contentType !== undefined)) { + if (isInitialRun && contentType !== undefined) { const initial = getDefaultResponseFormat(contentType); setSelectedFormat(initial.format); setSelectedTab(initial.tab); setIsInitialRun(false); } - }, [contentType, isInitialRun, detectedContentType]); + }, [contentType, isInitialRun]);
131-150: Add fallback return and fix dependencies.The useMemo can implicitly return
undefinedifselectedFormatis unexpected. Also,contentTypeis in the dependency array butdetectedContentTypeis what's actually used.// for all other content types, return preview-text return 'preview-text'; } - }, [selectedFormat, contentType, dataBuffer]); + return 'preview-text'; // fallback for unexpected formats + }, [selectedFormat, detectedContentType]);
168-178: Missing dependencies inuseMemo.
previewFormatOptions,handleFormatChange, andonPreviewTabSelectare referenced but not in the dependency array, which can cause stale closures.- }, [selectedFormat, selectedTab]); + }, [selectedFormat, selectedTab, previewFormatOptions, handleFormatChange, onPreviewTabSelect]);Alternatively, wrap
handleFormatChangeandonPreviewTabSelectinuseCallbackto stabilize their references.
🧹 Nitpick comments (6)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (2)
4-13: Avoid hard‑coded theme.bg comparison for background colorBranching on
props.theme.bg === '#1e1e1e'is brittle and couples this component to a specific dark theme implementation. Prefer a dedicated theme token (e.g. per‑mode background or anerrorAlertsurface color) so future theme changes don’t require touching this component. Based on learnings, theme props should encapsulate color decisions rather than magic hex checks.
34-39: Revisitword-break: break-allfor error message readability
word-break: break-allwill split words arbitrarily, which can hurt readability for typical error text. If the primary concern is long tokens (e.g. stack traces, UUIDs), consideroverflow-wrap: anywhereorword-break: break-wordto keep long segments wrapping without aggressively breaking normal words.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
43-48: Consider hardening webview security preferences.Explicitly disabling Node integration adds defense in depth for rendering untrusted HTML content.
<webview src={`data:text/html; charset=utf-8,${encodeURIComponent(htmlContent)}`} - webpreferences="disableDialogs=true, javascript=yes" + webpreferences="disableDialogs=true, javascript=yes, nodeIntegration=no, contextIsolation=yes" className="h-full bg-white" style={dragStyles} />packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
27-32: Move helper function outside component body.
isValidTreeDatais redefined on every render. Extract it to module scope since it has no dependencies on component state or props.+const isValidTreeData = (data) => { + if (data === null || data === undefined) return false; + if (typeof data === 'object' && !Array.isArray(data)) return true; + if (Array.isArray(data)) return true; + return false; +}; + export default function XmlPreview({ data, defaultExpanded = true }) { // Parse XML string const parsedData = useMemo(() => { // ... }, [data]); // Check for parsing error if (parsedData && typeof parsedData === 'object' && parsedData.error) { // ... } - // Validate that data can be rendered as a tree - const isValidTreeData = (data) => { - if (data === null || data === undefined) return false; - if (typeof data === 'object' && !Array.isArray(data)) return true; - if (Array.isArray(data)) return true; - return false; - }; - if (!isValidTreeData(parsedData)) {packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
1-1: Remove unused import.
useMemois imported but not used in this file.-import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react';packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
117-125: Remove unused dependencyresponseEncoding.
responseEncodingis in the dependency array but not used in the memoized function.- [data, dataBuffer, responseEncoding, selectedFormat, filter, isLargeResponse, showLargeResponse] + [data, dataBuffer, selectedFormat, filter, isLargeResponse, showLargeResponse]
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ErrorAlert/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js
- packages/bruno-app/src/components/ErrorAlert/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions:() => {}
No space between function name and parentheses:func()notfunc ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments
Files:
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.jspackages/bruno-app/src/components/ButtonDropdown/index.jsxpackages/bruno-app/src/components/ResponsePane/QueryResult/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{jsx,tsx}: JSX is enabled and should be used where it makes sense
Multiple expressions per line in JSX are acceptable for flexibility
In styled components and React components using styled components, use the theme prop to manage CSS colors instead of CSS variables
Styled Components should be used as wrappers to define both self and children component styles; Tailwind classes should be used specifically for layout-based styles
Styled Component CSS may change layout, but Tailwind classes should not define colors
Files:
packages/bruno-app/src/components/ButtonDropdown/index.jsx
🧠 Learnings (3)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components should be used as wrappers to define both self and children component styles; Tailwind classes should be used specifically for layout-based styles
Applied to files:
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS may change layout, but Tailwind classes should not define colors
Applied to files:
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ButtonDropdown/index.jsx
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : In styled components and React components using styled components, use the theme prop to manage CSS colors instead of CSS variables
Applied to files:
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js
🧬 Code graph analysis (4)
packages/bruno-app/src/components/ButtonDropdown/index.jsx (3)
packages/bruno-app/src/components/Icons/IconCaretDown/index.js (1)
IconCaretDown(3-16)packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js (1)
StyledWrapper(3-7)packages/bruno-app/src/components/Dropdown/index.js (1)
Dropdown(5-25)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (5)
packages/bruno-app/src/utils/common/index.js (7)
contentType(93-93)getContentType(83-119)getContentType(83-119)getEncoding(191-195)getEncoding(191-195)formatResponse(273-429)formatResponse(273-429)packages/bruno-app/src/utils/response/index.js (4)
detectContentTypeFromBuffer(176-258)detectContentTypeFromBuffer(176-258)getDefaultResponseFormat(8-59)getDefaultResponseFormat(8-59)packages/bruno-app/src/providers/Theme/index.js (3)
useTheme(61-69)useTheme(61-69)displayedTheme(11-11)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
QueryResultPreview(20-133)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx (1)
QueryResultTypeSelector(6-39)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (3)
packages/bruno-app/src/utils/common/index.js (3)
parsed(318-318)i(21-21)i(496-496)packages/bruno-app/src/components/ErrorAlert/index.js (1)
ErrorAlert(5-23)packages/bruno-app/src/utils/response/index.js (2)
parser(156-156)i(79-79)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (4)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
HtmlPreview(5-72)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js (1)
JsonPreview(5-61)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
TextPreview(3-23)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
XmlPreview(5-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: CLI Tests
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - macOS
🔇 Additional comments (5)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
1-25: LGTM!Clean implementation. The memoization strategy is appropriate and the fallback handling for different data types is solid.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
86-98: Index keys acceptable for static parsed data.Using array indices as keys is fine here since the parsed XML structure is immutable during the component's lifecycle.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
109-119: LGTM!Clean integration of the new preview components with appropriate mode switching.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
48-55: LGTM!Proper error handling for buffer conversion with graceful fallback.
packages/bruno-app/src/components/ButtonDropdown/index.jsx (1)
2-2: Verify IconCaretDown import source.The import on line 2 references
@tabler/icons, but the review suggests a localIconCaretDowncomponent may exist atpackages/bruno-app/src/components/Icons/IconCaretDown/index.js. Confirm whether the local component is available and whether it should be imported instead.
| {header && ( | ||
| <div className="dropdown-header-container"> | ||
| {header} | ||
| <div className="h-px bg-[#e7e7e7] dark:bg-[#444] my-1"></div> |
There was a problem hiding this comment.
Avoid Tailwind color classes per coding guidelines.
Line 149 uses Tailwind classes with hex color values (bg-[#e7e7e7] and dark:bg-[#444]). Per the coding guidelines, Tailwind classes should not define colors - use styled components with theme props instead.
As per coding guidelines, Tailwind classes should be used for layout only, not colors.
Move color definitions to StyledWrapper:
+const StyledWrapper = styled.div`
+ .caret {
+ fill: currentColor;
+ }
+ .dropdown-header-separator {
+ height: 1px;
+ background-color: ${(props) => props.theme.dropdown.separator};
+ margin: 0.25rem 0;
+ }
+`;Then update the JSX:
- <div className="h-px bg-[#e7e7e7] dark:bg-[#444] my-1"></div>
+ <div className="dropdown-header-separator"></div>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
packages/bruno-app/src/components/ButtonDropdown/index.jsx around line 149: the
divider uses Tailwind hex color classes (bg-[#e7e7e7] and dark:bg-[#444]) which
violates the guideline to keep Tailwind for layout only. Remove the color
classes from the JSX (leave layout classes like h-px and my-1) and move the
color rules into the component's StyledWrapper: add a selector for the divider
(e.g., .divider or a direct child selector) that sets background-color using
theme props (use the appropriate theme token such as theme.colors.separator /
theme.colors.surface and the dark theme equivalent) so the divider color comes
from styled-components/theme rather than Tailwind.
There was a problem hiding this comment.
Actionable comments posted: 3
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/ResponsePane/StatusCode/index.js (1)
8-15: Duplicate keys in classnames object cause unintended status code coloringObject literals retain only the last value for duplicate keys. This means:
- 1xx responses get no class (first 'text-ok' is overwritten)
- 2xx responses get 'text-ok'
- 3xx–4xx responses get no class (first two 'text-error' entries are overwritten)
- 5xx responses get 'text-error'
Consolidate the ranges with unique keys:
const getTabClassname = (status) => { return classnames({ - 'text-ok': status >= 100 && status < 200, - 'text-ok': status >= 200 && status < 300, - 'text-error': status >= 300 && status < 400, - 'text-error': status >= 400 && status < 500, - 'text-error': status >= 500 && status < 600 + 'text-ok': status >= 100 && status < 300, + 'text-error': status >= 300 && status < 600 }); };
♻️ Duplicate comments (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
57-67: Prettify object fallback with JSON.stringify indentationRight now objects are rendered on a single line because
JSON.stringify(data, null)omits the spacing argument. Adding an indentation level will better match the “formatted text” comment and keep output readable.- } else if (typeof data === 'object') { - displayContent = JSON.stringify(data, null); + } else if (typeof data === 'object') { + displayContent = JSON.stringify(data, null, 2);packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
46-68: Condition checksundefinedbut function returnsnull.
detectContentTypeFromBufferreturnsstring | null, neverundefined. The check on line 61 (detectedContentType === undefined) will never be true, making this condition ineffective.- if (detectedContentType === undefined || contentType === undefined) { + if (detectedContentType === null && contentType === undefined) { return { initialFormat: null, initialTab: null }; }Alternatively, if the intent is to wait for valid content type detection, consider
!detectedContentType && !contentType.
165-184: Missing explicit return and incorrect dependency.This
useMemocan returnundefinedimplicitly if no condition matches (though current logic covers all cases). Also,contentTypeis in the dependency array butdetectedContentTypeis what's actually used for binary format detection.return 'preview-text'; } + return 'preview-text'; // fallback for any unhandled case - }, [selectedFormat, contentType, dataBuffer]); + }, [selectedFormat, detectedContentType]);
🧹 Nitpick comments (17)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
1-13: Consider consolidating .active styling.The
.activeclass here duplicates the same styling rule in the parentQueryResult/StyledWrapper.js(line 34-36). If the selector component is always nested within QueryResult, the parent's.activerule should cascade. Evaluate if this duplication is necessary for scoping or can be removed.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
34-38: Consider more robust<head>injection for complex HTML
data.includes('<head>')only catches the exact lowercase token with no attributes; documents with<head>variants (e.g.<head lang="en">, uppercase tags) will be treated as snippets and get a prepended<head>...</head>, which is a bit fragile. You could tighten this later with a case‑insensitive regex on<head[^>]*>if you see real‑world HTML where this matters.packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js (1)
3-5: Remove empty StyledWrapper or add styles.An empty styled component adds no value and increases bundle size. If styling is planned for future iterations, add a TODO comment; otherwise, remove this wrapper and render the component directly.
Apply this diff if no styling is needed:
-import styled from 'styled-components'; - -const StyledWrapper = styled.div` - -`; - -export default StyledWrapper;Then update the consuming component (ResponsePaneActions/index.js) to remove the StyledWrapper import and usage.
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
19-27: Consider initializing state from hooks to prevent flicker.The state is initialized to hardcoded defaults ('raw', 'editor') and updated via useEffect. If
initialFormat/initialTabare computed asynchronously, the component renders with incorrect defaults first, potentially causing visual flicker.Consider initializing state directly from the hooks:
- const [selectedFormat, setSelectedFormat] = useState('raw'); - const [selectedTab, setSelectedTab] = useState('editor'); - - useEffect(() => { - if (initialFormat !== null && initialTab !== null) { - setSelectedFormat(initialFormat); - setSelectedTab(initialTab); - } - }, [initialFormat, initialTab]); + const [selectedFormat, setSelectedFormat] = useState(initialFormat ?? 'raw'); + const [selectedTab, setSelectedTab] = useState(initialTab ?? 'editor');If initialFormat/initialTab update after mount, add the useEffect back to sync changes.
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
53-65: Consider adding aria-disabled for better accessibility.The outer div acts as a button with
role="button"and receives focus, but when!hasData, clicks and key presses are silently ignored. Screen readers may still announce the element as interactive.Apply this diff to improve accessibility:
- <div role="button" tabIndex={0} onClick={handleClick} title={!children ? 'Copy response to clipboard' : null} onKeyDown={handleKeyDown}> + <div + role="button" + tabIndex={hasData ? 0 : -1} + onClick={handleClick} + title={!children ? 'Copy response to clipboard' : null} + onKeyDown={handleKeyDown} + aria-disabled={!hasData} + >This prevents focus on the disabled state and properly announces the disabled status to assistive technologies.
packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (2)
17-26: Unused Promise return value.
saveResponseToFilereturns a Promise, but callers (onClick,handleKeyDown) ignore it. Either remove the return statement or useasync/awaitto handle potential errors consistently.const saveResponseToFile = () => { if (isDisabled) { return; } - return new Promise((resolve, reject) => { - ipcRenderer - .invoke('renderer:save-response-to-file', response, item?.requestSent?.url, item.pathname) - .then(resolve) - .catch((err) => { - toast.error(get(err, 'error.message') || 'Something went wrong!'); - reject(err); - }); - }); + ipcRenderer + .invoke('renderer:save-response-to-file', response, item?.requestSent?.url, item.pathname) + .catch((err) => { + toast.error(get(err, 'error.message') || 'Something went wrong!'); + }); };
52-58: Nested interactive elements cause accessibility issues.A
<button>inside a<div role="button">creates confusing semantics for assistive technologies. Since the outer<div>handles click/keyboard events, the inner button is redundant.{children ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <span className="p-1"> <IconDownload size={16} strokeWidth={2} /> - </button> + </span> </StyledWrapper> )}packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (2)
27-31: Inconsistent keyboard event handling.Unlike
ResponseDownloadandResponseBookmark, this handler doesn't calle.preventDefault()for Enter/Space. This could cause unintended form submission if used in a form context.const handleKeyDown = (e) => { if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); clearResponse(); } };
35-41: Nested interactive elements cause accessibility issues.Same issue as
ResponseDownload— a<button>inside a<div role="button">is semantically redundant and confusing for assistive technologies.{children ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <span className="p-1"> <IconEraser size={16} strokeWidth={2} /> - </button> + </span> </StyledWrapper> )}packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (3)
53-72: Redundant disabled check.The
isDisabledcheck at lines 68-72 is unnecessary —isDisabledis derived fromisResponseTooLarge || isStreamingResponse, and you've already handledisResponseTooLargeabove. The streaming case is unreachable here sinceisDisabledwould have been caught by click handler being skipped.if (isResponseTooLarge) { toast.error('Response size exceeds 5MB limit. Cannot save as example.'); e.preventDefault(); e.stopPropagation(); return; } - if (isDisabled) { - e.preventDefault(); - e.stopPropagation(); - return; - } + if (isStreamingResponse) { + e.preventDefault(); + e.stopPropagation(); + return; + }
148-150: Complex ternary for title attribute.The logic is functionally correct but hard to parse. Consider simplifying.
title={ - !children ? disabledMessage : (isDisabled ? disabledMessage : null) + (!children || isDisabled) ? disabledMessage : null }This shows the intent more clearly: show the message when there are no children OR when disabled.
156-162: Nested interactive elements cause accessibility issues.Same pattern as other action components — a
<button>inside a<div role="button">is problematic. Replace with a non-interactive element.{children ?? ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <span className="p-1"> <IconBookmark size={16} strokeWidth={2} /> - </button> + </span> </StyledWrapper> )}packages/bruno-app/src/components/ResponsePane/index.js (1)
206-212: Arrow functions recreated on every render.The inline arrow functions for
onFormatChangeandonPreviewTabSelectcreate new function references each render. For a component this size, it's unlikely to cause performance issues, but memoization would be cleaner.packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
91-104: Unnecessary ResizeObserver recreation on item change.The
itemin the dependency array causes the ResizeObserver to disconnect and reconnect whenever the item prop changes. SincecheckSpaceonly reads DOM dimensions and doesn't depend onitemdata, this is wasteful.useEffect(() => { checkSpace(); const rightSideContainer = actionsRef.current?.closest('.right-side-container'); if (!rightSideContainer) return; const resizeObserver = new ResizeObserver(debouncedCheckSpace); resizeObserver.observe(rightSideContainer); return () => { resizeObserver.disconnect(); debouncedCheckSpace.cancel(); }; -}, [item, debouncedCheckSpace]); +}, [debouncedCheckSpace]);packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
75-79: Consider preventing default on Space key.The keyboard handler follows the pattern from
ResponseCopyandResponseClear. However, Space key may cause page scroll. Addinge.preventDefault()would ensure consistent behavior.const handleKeyDown = (e) => { if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); toggleOrientation(); } };packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
46-68: Duplicate Buffer conversion logic across three locations.The same
Buffer.from(dataBuffer, 'base64')with try-catch appears inuseInitialResponseFormat,useResponsePreviewFormatOptions, and theQueryResultcomponent body. Extract this to a shared utility or custom hook.// Consider a helper function in utils or a local helper const safeBufferFromBase64 = (dataBuffer) => { if (!dataBuffer) return null; try { return Buffer.from(dataBuffer, 'base64'); } catch (error) { console.error('Error converting dataBuffer to Buffer:', error); return null; } };Also applies to: 70-105, 118-125
156-158: UnusedresponseEncodingin dependency array.
responseEncodingis listed as a dependency but isn't used in theformattedDatamemo body.- [data, dataBuffer, responseEncoding, selectedFormat, filter, isLargeResponse, showLargeResponse] + [data, dataBuffer, selectedFormat, filter, isLargeResponse, showLargeResponse]
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (38)
packages/bruno-app/src/components/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js(2 hunks)packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/index.js(5 hunks)packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js(2 hunks)
💤 Files with no reviewable changes (3)
- packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/bruno-app/src/components/ButtonDropdown/index.jsx
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions:() => {}
No space between function name and parentheses:func()notfunc ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments
Files:
packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ClearTimeline/index.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResponse/index.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseTime/index.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jspackages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseActions/index.jspackages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.jspackages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.jspackages/bruno-app/src/components/ResponsePane/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseSize/index.jspackages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.jspackages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StatusCode/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/index.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.jspackages/bruno-app/src/components/RunnerResults/ResponsePane/index.js
🧠 Learnings (4)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS may change layout, but Tailwind classes should not define colors
Applied to files:
packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseSize/index.jspackages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components should be used as wrappers to define both self and children component styles; Tailwind classes should be used specifically for layout-based styles
Applied to files:
packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : In styled components and React components using styled components, use the theme prop to manage CSS colors instead of CSS variables
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js
📚 Learning: 2025-12-02T09:45:31.698Z
Learnt from: sid-bruno
Repo: usebruno/bruno PR: 6266
File: packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js:38-38
Timestamp: 2025-12-02T09:45:31.698Z
Learning: In the ResponseCopy component (packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js), the copy button is intentionally disabled using `!response.data` to prevent copying stream resets which result in empty strings.
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jspackages/bruno-app/src/components/ResponsePane/ResponseActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/ResponsePane/index.js
🧬 Code graph analysis (18)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (1)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/StyledWrapper.js (1)
StyledWrapper(3-6)
packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (3)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
StyledWrapper(3-13)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (5)
useInitialResponseFormat(47-68)useInitialResponseFormat(47-68)useResponsePreviewFormatOptions(71-105)useResponsePreviewFormatOptions(71-105)QueryResult(107-245)packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js (1)
StyledWrapper(3-14)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
StyledWrapper(3-11)
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js (6)
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js (1)
StyledWrapper(3-330)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
StyledWrapper(3-11)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js (1)
StyledWrapper(3-58)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js (1)
StyledWrapper(3-58)
packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js (1)
packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js (1)
packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (1)
ResponseDownload(8-61)
packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
StyledWrapper(3-11)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
QueryResponse(8-58)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (3)
packages/bruno-app/src/utils/common/index.js (2)
isValidHtml(487-490)isValidHtml(487-490)packages/bruno-app/src/utils/response/index.js (2)
isValidHtmlSnippet(97-169)isValidHtmlSnippet(97-169)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
baseUrl(84-84)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (2)
packages/bruno-app/src/components/ResponsePane/index.js (1)
response(67-67)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (2)
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (9)
useResponseLayoutToggle(52-70)useResponseLayoutToggle(52-70)useResponseLayoutToggle(73-73)ResponseLayoutToggle(72-104)orientation(55-55)IconDockToBottom(6-26)IconDockToBottom(6-26)IconDockToRight(28-49)IconDockToRight(28-49)packages/bruno-app/src/components/Dropdown/index.js (1)
Dropdown(5-25)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
StyledWrapper(3-13)
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (3)
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
useResponseLayoutToggle(44-44)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
handleKeyDown(27-31)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
handleKeyDown(40-44)
packages/bruno-app/src/components/ResponsePane/index.js (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (4)
useInitialResponseFormat(47-68)useInitialResponseFormat(47-68)useResponsePreviewFormatOptions(71-105)useResponsePreviewFormatOptions(71-105)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx (1)
QueryResultTypeSelector(7-40)
packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
QueryResponse(8-58)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (4)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (2)
useInitialResponseFormat(17-17)selectedTab(20-20)packages/bruno-app/src/utils/common/index.js (7)
dataBuffer(280-280)dataBuffer(384-384)contentType(93-93)getContentType(83-119)getContentType(83-119)formatResponse(273-429)formatResponse(273-429)packages/bruno-app/src/utils/response/index.js (4)
detectContentTypeFromBuffer(176-258)detectContentTypeFromBuffer(176-258)getDefaultResponseFormat(8-59)getDefaultResponseFormat(8-59)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
QueryResultPreview(20-133)
packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
QueryResponse(8-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Playwright E2E Tests
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - macOS
🔇 Additional comments (31)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (1)
18-22: Styling change toStyledWrapperis safe and consistentRemoving the left margin and using
className="flex items-center"keeps layout simple and doesn’t affect behavior; change looks good.packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
37-41: Separator styling looks good and follows theming guidelinesThe
.separatorblock is concise, uses theme-based color viaprops.theme.preferences.sidebar.border, and keeps spacing tight, which fits the existing styled-components pattern here. No issues from a layout/theming standpoint.Based on learnings, this correctly uses the theme prop for colors in styled components.
packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js (1)
5-5: LGTM! Consistent emphasis adjustment.The font-weight increase to 600 aligns with similar status text emphasis in ResponseTime and StatusCode wrappers.
packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js (1)
24-24: LGTM! Spacing adjustment aligns with layout updates.The reduced margin (ml-4 → ml-2) is consistent with spacing standardization across ResponsePane components.
packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js (1)
5-5: LGTM! Consistent emphasis adjustment.Font-weight increase to 600 maintains visual consistency with other response status wrappers.
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
6-12: LGTM! Interactive styling aligns with action button pattern.The hover states and theming match the pattern established in ResponseClear, ResponseCopy, and ResponseDownload wrappers.
packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js (1)
4-5: Layout system change from grid to flex.The shift from grid to flexbox better supports the new modular preview components. Verify that CodeMirror editor sizing (absolute positioning hack at lines 8-18) and PDF viewer still render correctly across different response types.
Manually test the following scenarios:
- JSON response rendering in CodeMirror editor
- PDF response rendering with react-pdf
- HTML/XML preview rendering
- Switching between different response format types
packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js (1)
1-16: LGTM! Clean wrapper with consistent theming.The flex-column layout and border theming align with the broader ResponsePane refactor. The nested
.query-response-contentborder provides clear visual separation for response content.packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js (1)
6-6: Component replacement requires verification against the full codebase.The transition from ResponseSave to ResponseDownload at lines 6 and 29 appears correct structurally, but verification that all remaining ResponseSave references have been removed cannot be completed due to repository access limitations. Run the provided verification script locally or ensure the following checks pass:
- No
ResponseSaveimports remain in the codebase- No
<ResponseSaveJSX usages exist- No orphaned ResponseSave files or exports remain
- All call sites use the new ResponseDownload component with the expected props (item, asDropdownItem, onClose)
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js (1)
258-306: Response body tab strip styling looks consistent and theme‑drivenThe
.response-body-containerand nesteddiv[role="tablist"] > divstyles cleanly align the response preview tabs with console theming, without introducing layout risks. Looks good to me.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
9-32: Drag‑guard via MutationObserver is reasonableWatching a common ancestor for
.draggingand flippingpointerEvents/userSelecton the container and<webview>is a pragmatic way to avoid interaction glitches while resizing/dragging. The effect is well‑scoped and cleaned up viadisconnect().Also applies to: 39-52
packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js (1)
21-25: Spacing tweak on size label is fineReducing the left margin to
ml-2keeps the existing size formatting logic intact while aligning spacing with the rest of the status bar.packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js (1)
3-11: Clear‑button styling aligns with shared theme patternsUsing
theme.codemirror.variable.info.iconColorplus the shared hover background/text tokens and a 4px radius keeps the Clear action visually consistent with the other response toolbar controls. Looks good.Based on learnings, this correctly relies on theme colors rather than raw CSS variables.
packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js (1)
20-21: Margin adjustment on response time is safeSwitching the wrapper to
ml-2only affects spacing; the duration formatting andisNumberguard remain intact.packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js (1)
4-18: Layout toggle button theming is consistent with other controlsDeriving the icon color from
theme.codemirror.variable.info.iconColorand adding the shared hover background/text styling keeps the layout toggle visually in line with the rest of the response toolbar.Based on learnings, this is the expected pattern of using styled‑components with theme tokens for colors.
packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js (1)
4-5: Runner results now correctly route through QueryResponseSwapping to
<QueryResponse>in the “response” tab keeps the existing props wiring (item/collection/data/dataBuffer/headers/error/disableRunEventListener) while aligning this pane with the new response‑format/preview flow. The change is self‑contained and looks correct.Also applies to: 41-52
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js (1)
10-10: LGTM: Component swap aligned with refactor.The QueryResult → QueryResponse migration is consistent with the broader refactor pattern across the codebase. Props remain unchanged, maintaining the same API surface.
Also applies to: 119-127
packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js (1)
5-6: LGTM: Visual refinement for status display.The font-weight increase and nowrap constraint improve status code readability without affecting functionality.
packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
5-11: LGTM: Theming aligned with CodeMirror conventions.The updated color tokens and hover state provide visual consistency across ResponsePane components. Theme usage follows coding guidelines.
packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js (1)
1-1: LGTM: Component migration consistent with refactor.The QueryResult → QueryResponse swap maintains the same prop interface and aligns with the broader refactor pattern.
Also applies to: 17-25
packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (1)
3-12: LGTM: Consistent styling pattern.The wrapper follows established theming conventions and matches the styling pattern used across other ResponsePane action components (ResponseCopy, ResponseClear, ResponseBookmark).
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (2)
7-35: LGTM: Clean hook extraction.Extracting the copy logic into
useResponseCopyimproves reusability and testability. The hook properly manages state and cleanup.
37-67: LGTM: Children prop enhances flexibility.Adding support for custom rendering via the
childrenprop improves component reusability while maintaining backward compatibility with the default button rendering.packages/bruno-app/src/components/ResponsePane/index.js (1)
41-46: Format state may become stale when switching between requests.When switching to a different request item,
selectedFormatandselectedTabonly update ifinitialFormat/initialTabare non-null. If the new item has no response yet, the previous item's format settings persist, which may confuse users.Consider resetting to defaults when the item changes:
useEffect(() => { if (initialFormat !== null && initialTab !== null) { setSelectedFormat(initialFormat); setSelectedTab(initialTab); + } else { + setSelectedFormat('raw'); + setSelectedTab('editor'); } }, [initialFormat, initialTab]);Alternatively, add
item.uidas a dependency to force reset on item change.packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (3)
126-133: Action may not execute when dropdown closes.
closeDropdownis called synchronously on click, which hides the Tippy dropdown. Depending on Tippy's implementation, this might unmount children beforeResponseCopy's click handler completes. Consider deferring the close or ensuring action completion first.Verify dropdown items work correctly in the UI. If issues occur, defer close:
<ResponseCopy item={item}> - <div className="dropdown-item" onClick={closeDropdown}> + <div className="dropdown-item" onClick={() => setTimeout(closeDropdown, 0)}>
31-41: LGTM on MenuIcon implementation.Clean use of
forwardRefwith properdisplayNamefor DevTools debugging.
120-184: Solid responsive action bar implementation.The space detection logic with ResizeObserver and the fallback to a dropdown menu is well thought out for handling varying viewport widths. Good separation between collapsed and expanded states.
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (2)
6-26: LGTM on icon exports.Exporting
IconDockToBottomandIconDockToRightenables reuse across the codebase. The SVG structure is consistent.Also applies to: 28-49
51-70: LGTM onuseResponseLayoutTogglehook.Clean extraction of orientation state and toggle logic. This aligns with usage in
ResponsePaneActionsand promotes reusability.packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
13-31: LGTM on format options constant.The
PREVIEW_FORMAT_OPTIONSstructure is well-organized with clear groupings and CodeMirror mode mappings.
222-236: LGTM on QueryResultPreview integration.The component properly passes all required props including the new
selectedTab,previewMode, andcodeMirrorMode.
| <div | ||
| role="button" | ||
| tabIndex={0} | ||
| onClick={toggleOrientation} | ||
| title={title} | ||
| onKeyDown={handleKeyDown} | ||
| > | ||
| {children ? children : ( | ||
| <StyledWrapper className="flex items-center w-full"> | ||
| <button className="p-1"> | ||
| {orientation === 'horizontal' ? ( | ||
| <IconDockToBottom /> | ||
| ) : ( | ||
| <IconDockToRight /> | ||
| )} | ||
| </button> | ||
| </StyledWrapper> | ||
| )} | ||
| </div> |
There was a problem hiding this comment.
Nested interactive elements create accessibility issues.
The outer <div> with role="button" wraps a <button> (line 93). This creates nested interactive elements which is problematic for screen readers and keyboard navigation. When children is not provided, clicking the inner button also triggers the outer div's click handler.
Consider removing the inner <button> when rendering the default UI:
{children ? children : (
<StyledWrapper className="flex items-center w-full">
- <button className="p-1">
+ <span className="p-1">
{orientation === 'horizontal' ? (
<IconDockToBottom />
) : (
<IconDockToRight />
)}
- </button>
+ </span>
</StyledWrapper>
)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div | |
| role="button" | |
| tabIndex={0} | |
| onClick={toggleOrientation} | |
| title={title} | |
| onKeyDown={handleKeyDown} | |
| > | |
| {children ? children : ( | |
| <StyledWrapper className="flex items-center w-full"> | |
| <button className="p-1"> | |
| {orientation === 'horizontal' ? ( | |
| <IconDockToBottom /> | |
| ) : ( | |
| <IconDockToRight /> | |
| )} | |
| </button> | |
| </StyledWrapper> | |
| )} | |
| </div> | |
| <div | |
| role="button" | |
| tabIndex={0} | |
| onClick={toggleOrientation} | |
| title={title} | |
| onKeyDown={handleKeyDown} | |
| > | |
| {children ? children : ( | |
| <StyledWrapper className="flex items-center w-full"> | |
| <span className="p-1"> | |
| {orientation === 'horizontal' ? ( | |
| <IconDockToBottom /> | |
| ) : ( | |
| <IconDockToRight /> | |
| )} | |
| </span> | |
| </StyledWrapper> | |
| )} | |
| </div> |
🤖 Prompt for AI Agents
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js
lines 84-102: the outer div with role="button" currently contains a real
<button>, creating nested interactive elements; remove the inner <button> when
rendering the default UI and render the icon directly inside the StyledWrapper
so there is only one interactive control. Keep the outer element as the single
interactive control (or convert it to a native <button>), retain title, onClick,
tabIndex and onKeyDown handlers, and add appropriate ARIA (e.g., aria-pressed or
aria-label) so keyboard (Enter/Space) and screen readers behave correctly.
b434b82 to
dac8f7d
Compare
There was a problem hiding this comment.
Actionable comments posted: 8
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/ResponsePane/StatusCode/index.js (1)
8-15: Duplicate keys inclassnamesobject break status coloring logicThe object passed to
classnamescontains duplicate keys for both'text-ok'and'text-error'. In JavaScript object literals, later property definitions overwrite earlier ones, so the effective behavior is:
'text-ok'only for200 <= status < 300'text-error'only for500 <= status < 600Responses with status codes 100–199, 300–399, and 400–499 receive no status color class, which defeats the purpose of the color indicator.
Fix by collapsing the ranges to eliminate duplicate keys:
const getTabClassname = (status) => { return classnames({ - 'text-ok': status >= 100 && status < 200, - 'text-ok': status >= 200 && status < 300, - 'text-error': status >= 300 && status < 400, - 'text-error': status >= 400 && status < 500, - 'text-error': status >= 500 && status < 600 + 'text-ok': status >= 100 && status < 300, + 'text-error': status >= 300 && status < 600 }); };
♻️ Duplicate comments (7)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
8-8: Prefer theme radius token instead of hard-coded border-radius
border-radius: 0.375rem;should ideally use the shared radius token from the theme (e.g. whatever the base/small radius is) for consistency with the rest of the design system.tests/utils/page/actions.ts (1)
542-566:switchToEditorTabstill clicks the preview tab
switchToEditorTabis currently identical toswitchToPreviewTaband still clickspreview-response-tab, so it doesn’t actually ensure the editor view is active and makes tests misleading.Update
switchToEditorTabto target the editor control (whatever test id represents it) or otherwise distinguish its behavior from the preview helper.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
87-88: Addaltattribute to image for accessibility.Missing
altattribute on the image element.- return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} />; + return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} alt="Response preview" />;packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
91-100: Nested interactive elements create accessibility issues.The outer
<div>withrole="button"wraps a<button>(line 93). This creates nested interactive elements which is problematic for screen readers and keyboard navigation.{children ? children : ( <StyledWrapper className="flex items-center w-full"> - <button className="p-1"> + <span className="p-1"> {orientation === 'horizontal' ? ( <IconDockToBottom /> ) : ( <IconDockToRight /> )} - </button> + </span> </StyledWrapper> )}packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (3)
186-190: Fallback CodeMirror mode is non-standard.
'application/text'is not a valid MIME type. Use'text/plain'instead.- .find((option) => option.value === selectedFormat)?.codeMirrorMode || 'application/text'; + .find((option) => option.value === selectedFormat)?.codeMirrorMode || 'text/plain';
86-88:isByteFormatTypewill throw ifcontentTypeis null.If
getContentTypeToCheck()returns null, calling.includes()on it will throw a TypeError.const isByteFormatType = (contentType) => { + if (!contentType) return false; return byteFormatTypes.some((type) => contentType.includes(type)); };
165-184: Missing fallback return and incorrect dependency.The
useMemocan returnundefinedimplicitly. Also,contentTypeis in the dependency array butdetectedContentType(which is actually used) is not.// for all other content types, return preview-text return 'preview-text'; } + return 'preview-text'; // fallback for unhandled formats - }, [selectedFormat, contentType, dataBuffer]); + }, [selectedFormat, detectedContentType]);
🧹 Nitpick comments (17)
packages/bruno-app/src/components/ErrorAlert/index.js (1)
5-22: Solid ErrorAlert implementation; consider minor refinements for message handling and accessibilityOverall component behavior looks good and fits the existing styling approach.
Two small, optional polish points:
- Line 6:
if (!message) return nullwill also hide intentionally falsy-but-valid values (e.g.0,false). If you ever expect those, you might want a stricter check likemessage == nullinstead.- Lines 13 & 15–18: For non‑string messages,
JSON.stringifyworks for plain objects but will produce{}forErrorinstances and can throw on circular structures. Also, the close affordance is adivwithonClick; switching to a<button type="button">with anaria-labelwould improve accessibility and keyboard operability.These are non-blocking; fine to defer if they don’t match current usage.
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
13-13: Consider moving background colors to theme tokens instead of hard-coded RGBAThe conditional background:
background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(40, 40, 40, 0.5)' : 'rgba(250, 250, 250, 0.9)')};relies on a specific hex and literal RGBA values. Wiring these through semantic theme tokens (e.g. dedicated surface/alert background colors) would make it easier to adjust themes and avoid coupling to a single
bghex value.Based on learnings, using theme-driven colors in styled components keeps them consistent across themes.
tests/utils/page/actions.ts (4)
472-479: Avoid keeping commented-out legacyopenRequestIf the old, request-only
openRequestis no longer needed, you can drop this commented block and rely on git history instead of keeping dead code in the helper file.
480-494: Restore a post-condition assertion and consider centralizing locatorsThe new
openRequest(page, collectionName, requestName)correctly scopes the request to its collection, but you dropped the previous assertion that the active request tab matchesrequestName. Re‑adding that check would make tests more robust and keep failures localized.You might also consider factoring this DOM traversal into
buildCommonLocators(e.g., asidebar.collectionRequest(collectionName, requestName)helper) so selectors live in one place.
510-527: Merge default timeout with overrides or delegate tosendRequest
sendRequestAndWaitForResponseis handy, but callers that pass{ ignoreCase: true }lose the defaulttimeout: 15000. Either:
- Merge options with a default inside the function, e.g.
const finalOptions = { timeout: 15000, ...options }, or- Delegate to
sendRequestto keep a single place that clicks “send” and waits for the status element.This avoids subtle differences in behavior and duplicated logic.
529-540: Optional: tighten typing forformatImplementation looks fine. If you already have a central enum/union for response formats, consider typing
formatas that union instead ofstringto catch typos at compile time.packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (3)
9-11: Consider hardening against missingipcRenderer/itemin non‑Electron contextsDestructuring
const { ipcRenderer } = windowand assumingitemis always defined will throw in tests or any non‑Electron environment that mounts this component. If this component can be rendered in such contexts, consider guarding:
- Use
const ipcRenderer = window?.ipcRendererand early‑return (or no‑op + warning) when it’s absent- Optionally use
const response = item?.response || {}anditem?.pathnamefor a bit more safetyThis stays cheap at runtime while making the component more robust to accidental reuse.
13-26: Avoid wrappingipcRenderer.invokein a manual Promise and improve error extraction
ipcRenderer.invokealready returns a Promise, so the extranew Promisewrapper is unnecessary. You can simplify and keep the same behavior:- const saveResponseToFile = () => { - if (isDisabled) { - return; - } - return new Promise((resolve, reject) => { - ipcRenderer - .invoke('renderer:save-response-to-file', response, item?.requestSent?.url, item.pathname) - .then(resolve) - .catch((err) => { - toast.error(get(err, 'error.message') || 'Something went wrong!'); - reject(err); - }); - }); - }; + const saveResponseToFile = () => { + if (isDisabled) { + return; + } + + return ipcRenderer + .invoke('renderer:save-response-to-file', response, item?.requestSent?.url, item.pathname) + .catch((err) => { + const message = + get(err, 'message') || + get(err, 'error.message') || + 'Something went wrong!'; + toast.error(message); + throw err; + }); + };This removes a Promise anti‑pattern and also makes the error message more resilient if the rejection is a plain
Error.
28-38: Clean up button semantics to avoid nested interactive controlsRight now the outer element is a
divwithrole="button"/tabIndexand keyboard handling, but the default UI also renders a nested<button>. This leads to:
- Two focusable/interactive elements (outer div and inner button) for a single action
- Confusing semantics for assistive tech and an extra tab stop
Since the outer
divis already the interactive surface, you can drop the inner<button>and keep the click/keyboard logic on the wrapper:- className={classnames({ - 'opacity-50 cursor-not-allowed': isDisabled - })} + className={classnames({ + 'opacity-50 cursor-not-allowed': isDisabled + })} > - {children ? children : ( - <StyledWrapper className="flex items-center"> - <button className="p-1"> - <IconDownload size={16} strokeWidth={2} /> - </button> - </StyledWrapper> - )} + {children ? children : ( + <StyledWrapper className="flex items-center p-1"> + <IconDownload size={16} strokeWidth={2} /> + </StyledWrapper> + )}This keeps the existing visual structure and keyboard behavior while avoiding nested interactive controls.
Also applies to: 40-59
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
43-185: Responsive actions bar logic looks solidThe measurement logic (siblings + padding) with a debounced
ResizeObserverand theshowMenuRef/individualButtonsWidthRefpattern avoids stale state and flicker when switching between inline buttons and the overflow menu. The early return for non‑http-requestitems also keeps this scoped to HTTP responses.If you ever need this to run in environments without
ResizeObserver(e.g., some tests), a small feature check around its usage would make it even more robust, but it isn’t a blocker for an Electron app.packages/bruno-app/src/utils/common/index.js (1)
7-9: New formatting pipeline (HTML/JS/hex/base64/text) looks coherent, with minor perf nitsOverall this extension of
formatResponseand the new helpers hangs together well:
getContentType’sJAVASCRIPT_PATTERNand mapping to'application/javascript'aligns with the new JavaScript mode handling elsewhere.- The
htmlandjavascriptbranches both honorisVeryLargeResponsebefore attempting pretty‑printing, and fall back cleanly to the original string if formatting fails.- Hex handling (
mode.includes('hex')) correctly avoids re-encoding strings already detected as hex viaisHexFormat, and otherwise generates a standard hex+ASCII dump usingformatHexView.- The
text/rawbranch reusesrawDatafor small responses and falls back to type-aware stringification for large payloads, matching the intent to avoid decoding very large buffers.Two small, non-blocking nits you might consider (now or later):
- For very large but still <50MB payloads, pretty‑printing JS/HTML or generating a full hex dump can still be expensive; if you see UI stalls in practice, it may be worth introducing a lower, format-specific threshold (especially for the hex view).
- In the hex branch, you could potentially reuse the already-decoded buffer from the top of
formatResponseinstead of re‑decodingdataBufferString, but that’s a micro-optimization and not required for correctness.Nothing here looks functionally off; the behavior matches the new preview modes.
Also applies to: 105-116, 273-422, 466-557
packages/bruno-app/src/components/ResponsePane/index.js (1)
19-22: Response format/tab wiring and header layout look consistentThe new state + hooks flow is clean:
useInitialResponseFormat/useResponsePreviewFormatOptionsare wired withitem.response?.dataBufferandheaders, and the effect only initializesselectedFormat/selectedTabonce valid initial values are available—so user changes aren’t overwritten on re-render.- Passing
selectedFormatandselectedTabintoQueryResultgives the preview pipeline the context it needs without bloating props elsewhere.- The header’s right-side container nicely groups the preview selector, status (code/time/size), and actions, and only shows the selector when the Response tab is focused and the pane isn’t loading.
One minor UI edge case to be aware of: for non‑
http-requestitems with a successfulresponse, the parent still renders the actions container, butResponsePaneActionswill early-returnnull, leaving an empty actions area between separators. If that ever becomes visible for WebSocket or other request types, a parent-level type check before rendering the actions block would tidy it up.Also applies to: 34-47, 85-103, 173-238
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
53-72: Redundant and inefficient guard checks.The
isResponseTooLargecheck (lines 61-66) is redundant sinceisDisabledalready encompasses it. Additionally, theisDisabledcheck (lines 68-72) should be at the top to short-circuit before showing individual error toasts.const handleSaveClick = (e) => { + if (isDisabled) { + e.preventDefault(); + e.stopPropagation(); + return; + } + if (!response || response.error) { toast.error('No valid response to save as example'); e.preventDefault(); e.stopPropagation(); return; } - if (isResponseTooLarge) { - toast.error('Response size exceeds 5MB limit. Cannot save as example.'); - e.preventDefault(); - e.stopPropagation(); - return; - } - - if (isDisabled) { - e.preventDefault(); - e.stopPropagation(); - return; - } - setShowSaveResponseExampleModal(true); };packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
1-1: Remove unuseduseMemoimport.
useMemois imported but never used in this file.-import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react';packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (2)
182-188: Consider keyboard accessibility for expand/collapse buttons.Using
tabIndex={-1}removes these buttons from keyboard navigation. While focus is managed through the parent, users relying solely on keyboard may not be able to expand/collapse individual nodes.Consider using
tabIndex={0}and adding keyboard event handling for Enter/Space to toggle expansion.
27-32: MoveisValidTreeDataoutside component.This function is recreated on every render. Since it has no dependencies on component state or props, move it outside the component.
+const isValidTreeData = (data) => { + if (data === null || data === undefined) return false; + if (typeof data === 'object' && !Array.isArray(data)) return true; + if (Array.isArray(data)) return true; + return false; +}; + export default function XmlPreview({ data, defaultExpanded = true }) { // ... - const isValidTreeData = (data) => { - if (data === null || data === undefined) return false; - if (typeof data === 'object' && !Array.isArray(data)) return true; - if (Array.isArray(data)) return true; - return false; - };packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
46-68: Consider extracting duplicate buffer conversion logic.The
Buffer.from(dataBuffer, 'base64')conversion with try-catch is repeated inuseInitialResponseFormat,useResponsePreviewFormatOptions, and in the component itself. Extract to a shared utility.// utils/buffer.js or inline helper const safeBase64ToBuffer = (dataBuffer) => { if (!dataBuffer) return null; try { return Buffer.from(dataBuffer, 'base64'); } catch (error) { console.error('Error converting dataBuffer to Buffer:', error); return null; } };Also applies to: 71-105, 118-124
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (58)
packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js(2 hunks)packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js(2 hunks)packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ErrorAlert/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/index.js(5 hunks)packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js(2 hunks)packages/bruno-app/src/utils/common/codemirror.js(1 hunks)packages/bruno-app/src/utils/common/index.js(4 hunks)packages/bruno-app/src/utils/response/index.js(1 hunks)tests/response/large-response-crash-prevention.spec.ts(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/bruno.json(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru(1 hunks)tests/response/response-format-select-and-preview/init-user-data/collection-security.json(1 hunks)tests/response/response-format-select-and-preview/init-user-data/preferences.json(1 hunks)tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts(1 hunks)tests/utils/page/actions.ts(3 hunks)tests/utils/page/locators.ts(2 hunks)
💤 Files with no reviewable changes (3)
- packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
- packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js
✅ Files skipped from review due to trivial changes (2)
- packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js
🚧 Files skipped from review as they are similar to previous changes (29)
- packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js
- tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts
- packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js
- packages/bruno-app/src/components/ButtonDropdown/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js
- packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js
- tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru
- tests/response/large-response-crash-prevention.spec.ts
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
- packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js
- packages/bruno-app/src/components/ButtonDropdown/index.jsx
- packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js
- tests/response/response-format-select-and-preview/init-user-data/preferences.json
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js
- packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js
- tests/utils/page/locators.ts
- packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js
- packages/bruno-app/src/components/ResponsePane/StyledWrapper.js
- packages/bruno-app/src/utils/response/index.js
- packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js
- tests/response/response-format-select-and-preview/fixtures/collection/bruno.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/ErrorAlert/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.jspackages/bruno-app/src/components/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/utils/common/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jstests/utils/page/actions.tspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.jspackages/bruno-app/src/utils/common/codemirror.jspackages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jspackages/bruno-app/src/components/ResponsePane/StatusCode/index.jspackages/bruno-app/src/components/ResponsePane/index.jspackages/bruno-app/src/components/ResponsePane/ResponseSize/index.js
tests/**/**.*
⚙️ CodeRabbit configuration file
tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:
Follow best practices for Playwright code and e2e automation
Try to reduce usage of
page.waitForTimeout();in code unless absolutely necessary and the locator cannot be found using existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture Example*: Here's an example of possible fixture and test pair
. ├── fixtures │ └── collection │ ├── base.bru │ ├── bruno.json │ ├── collection.bru │ ├── ws-test-request-with-headers.bru │ ├── ws-test-request-with-subproto.bru │ └── ws-test-request.bru ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection ├── headers.spec.ts ├── persistence.spec.ts ├── variable-interpolation │ ├── fixtures │ │ └── collection │ │ ├── environments │ │ ├── bruno.json │ │ └── ws-interpolation-test.bru │ ├── init-user-data │ └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection └── subproto.spec.ts
Files:
tests/utils/page/actions.tstests/response/response-format-select-and-preview/init-user-data/collection-security.jsontests/response/response-format-select-and-preview/fixtures/collection/request-json.bru
🧠 Learnings (6)
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Applied to files:
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.jspackages/bruno-app/src/components/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseSize/index.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Applied to files:
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseSize/index.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Applied to files:
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Applied to files:
packages/bruno-app/src/utils/common/index.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Add in JSDoc comments to add more details to the abstractions if needed
Applied to files:
packages/bruno-app/src/utils/common/index.js
📚 Learning: 2025-12-02T09:45:31.698Z
Learnt from: sid-bruno
Repo: usebruno/bruno PR: 6266
File: packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js:38-38
Timestamp: 2025-12-02T09:45:31.698Z
Learning: In the ResponseCopy component (packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js), the copy button is intentionally disabled using `!response.data` to prevent copying stream resets which result in empty strings.
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jspackages/bruno-app/src/components/ResponsePane/index.js
🧬 Code graph analysis (11)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (2)
packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
StyledWrapper(3-42)packages/bruno-app/src/components/ResponsePane/ClearTimeline/StyledWrapper.js (1)
StyledWrapper(3-6)
packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (2)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
handleKeyDown(40-44)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/components/ErrorAlert/index.js (1)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/utils/common/index.js (1)
packages/bruno-js/src/bru.js (1)
xmlFormat(2-2)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (2)
packages/bruno-app/src/components/ResponsePane/index.js (1)
response(67-67)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
contentType(126-126)packages/bruno-app/src/utils/common/index.js (3)
contentType(93-93)dataBuffer(280-280)dataBuffer(384-384)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (3)
packages/bruno-app/src/components/ResponsePane/index.js (2)
response(67-67)responseSize(69-83)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
responseSize(132-147)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
StyledWrapper(3-13)
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (5)
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
useResponseLayoutToggle(44-44)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
handleKeyDown(41-51)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
handleKeyDown(27-31)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
handleKeyDown(40-44)packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (1)
handleKeyDown(28-38)
packages/bruno-app/src/utils/common/codemirror.js (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
contentType(126-126)packages/bruno-app/src/utils/common/index.js (1)
contentType(93-93)
packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (3)
packages/bruno-app/src/components/ResponsePane/index.js (1)
response(67-67)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/components/ResponsePane/index.js (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (5)
useInitialResponseFormat(47-68)useInitialResponseFormat(47-68)useResponsePreviewFormatOptions(71-105)useResponsePreviewFormatOptions(71-105)responseSize(132-147)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx (1)
QueryResultTypeSelector(7-40)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
ResponsePaneActions(43-186)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - macOS
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: CLI Tests
🔇 Additional comments (21)
tests/response/response-format-select-and-preview/init-user-data/collection-security.json (1)
1-10: Well-structured fixture configuration with appropriate security defaults.The JSON is valid, fixtures are properly nested per guidelines, and
jsSandboxMode: "safe"is the right choice for testing HTML/JS preview rendering without risking malicious execution.tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru (1)
1-17: Fixture structure and placement look correctThe
.brufixture is well-formed (meta,postblock, andbody:jsonblock) and the file path follows the expectedtests/.../fixtures/collectionlayout for e2e collections. No changes needed here.packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (1)
18-18: Verify visual spacing after removing left margin.The removal of
ml-2reduces left margin spacing on the ClearTimeline button. Ensure this doesn't create visual crowding or misalignment with adjacent ResponsePane elements, particularly in the context of the broader layout adjustments mentioned in this PR.packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
7-22: LGTM! Clean hook extraction.The
useResponseClearhook encapsulates the dispatch logic nicely and provides a reusable abstraction.packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (3)
6-35: LGTM! Clean hook extraction.The hook encapsulates the copy logic well, properly manages state and side effects, and exposes a clear API. The
hasDatacheck aligns with the existing pattern of preventing copies when response data is unavailable.
37-38: LGTM! Component signature properly updated.The addition of the
childrenprop enables custom rendering while maintaining the default behavior. Hook usage is clean and idiomatic.
46-50: LGTM! Click handler is straightforward.The guard ensures copy only happens when data is available.
tests/utils/page/actions.ts (1)
612-616: Export additions look consistentExporting the new helpers (
sendRequestAndWaitForResponse,switchResponseFormat,switchToPreviewTab,switchToEditorTab) from this barrel is consistent with the existing pattern and keeps test utilities discoverable.packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js (1)
3-14: StatusCode wrapper typography + nowrap look goodUsing
font-weight: 600andwhite-space: nowraphere is reasonable for making the status more prominent and keeping it on a single line, and colors still come from the theme via.text-ok/.text-error, which aligns with our styled-components theming guidance. Based on learnings, this is consistent with how we want colors handled.packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (2)
8-12: Confirm disabled condition vs other response actions
isDisabledis keyed off!response.dataBuffer, while ResponseCopy is intentionally disabled via!response.datato avoid copying stream resets that yield empty strings. Please double‑check that usingdataBufferhere is intentional and matches how streamed responses are buffered and exposed for downloads, so we don’t end up enabling download when the underlying data is still incomplete or unavailable. Based on learnings, this difference is worth verifying.
40-51: Overall interaction wiring looks solidThe combination of
role="button",tabIndex,aria-disabled, disabled styling, and theisDisabledguard insaveResponseToFilegives you a predictable UX and prevents accidental downloads when there’s no buffer. Aside from the minor refinements noted above, the interaction model is sound.packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js (1)
21-24: Spacing tweak is fineReducing the left margin on
ResponseSizetoml-2is a safe visual-only change; no functional impact on size computation or tooltip.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
1-13: Theme-based styling looks consistentUsing
props.theme.colors.text.yellowfor.activeandtext.mutedfor the label matches the styled-components + Tailwind split (colors in theme, layout elsewhere). No issues here.packages/bruno-app/src/utils/common/codemirror.js (1)
104-108: JavaScript content-type mapping looks correctAdding the
javascript/ecmascriptcheck and returning'application/javascript'fits the existing pattern and should give JS responses an appropriate editor mode instead of plain text.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
3-25: TextPreview’s normalization logic is soundThe memoized
displayDatacorrectly handlesnull/undefined, objects (with a safeJSON.stringify), and primitives viaString(data), and the wrapper uses only layout/typography classes. This is a solid generic text preview.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js (1)
5-28: Harden the effect against invalidcontentType/dataBufferThe
useEffectlacks guards for its inputs, so bad data (or missingcontentType) could throw and break rendering. Add defensive checks:- useEffect(() => { - const videoType = contentType.split(';')[0]; - const byteArray = Buffer.from(dataBuffer, 'base64'); - const blob = new Blob([byteArray], { type: videoType }); - const url = URL.createObjectURL(blob); - setVideoUrl(url); - return () => URL.revokeObjectURL(url); - }, [contentType, dataBuffer]); + useEffect(() => { + if (!contentType || !dataBuffer) { + setVideoUrl(null); + return; + } + + let url; + try { + const videoType = contentType.split(';')[0]; + const byteArray = Buffer.from(dataBuffer, 'base64'); + const blob = new Blob([byteArray], { type: videoType }); + url = URL.createObjectURL(blob); + setVideoUrl(url); + } catch (error) { + console.error('Error preparing video preview:', error); + setVideoUrl(null); + } + + return () => { + if (url) { + URL.revokeObjectURL(url); + } + }; + }, [contentType, dataBuffer]);packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
41-51: LGTM on keyboard accessibility.Good addition of keyboard event handling for Enter and Space keys, consistent with other ResponsePane components.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
64-79: LGTM on editor tab rendering.Clean conditional rendering for the editor path with appropriate props passed to CodeEditor.
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
51-70: LGTM on the new hook extraction.Clean separation of concerns. The
useResponseLayoutTogglehook nicely encapsulates the orientation state and toggle logic for reuse.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview.js (1)
5-68: LGTM on the overall XmlPreview structure.Good use of
useMemofor parsing, proper error handling withErrorAlert, and clean data validation flow.packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
220-236: LGTM on QueryResultPreview integration.Clean prop passing to the refactored preview component with the new format-driven architecture.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
Outdated
Show resolved
Hide resolved
| <button | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| const target = e.currentTarget.parentElement.parentElement; | ||
| const content = target.querySelector('.array-content'); | ||
| if (content) { | ||
| content.classList.toggle('hidden'); | ||
| e.currentTarget.textContent = content.classList.contains('hidden') ? '▶' : '▼'; | ||
| } | ||
| }} |
There was a problem hiding this comment.
DOM manipulation bypasses React reconciliation.
Direct DOM manipulation via parentElement, querySelector, and classList.toggle is an anti-pattern in React. This can cause state/UI inconsistencies. Use React state instead.
Extract array child rendering into a separate component with its own expansion state:
const ArrayChildNode = ({ nodeKey, value, depth }) => {
const [expanded, setExpanded] = useState(true);
return (
<div style={{ paddingLeft: `${(depth + 1) * 20}px` }}>
<div className="flex items-center mb-1">
<button
onClick={(e) => {
e.stopPropagation();
setExpanded((v) => !v);
}}
className="mr-2 cursor-pointer w-4 h-4 flex items-center justify-center text-[#666] dark:text-[#569cd6] flex-shrink-0 rounded transition-colors hover:bg-[#e0e0e0] dark:hover:bg-[#2d2e30]"
tabIndex={-1}
>
{expanded ? '▼' : '▶'}
</button>
<span className="text-black dark:text-[#f9f8f5] font-medium">{nodeKey}</span>
<span className="text-[#666] dark:text-[#808080] ml-2">[{value.length}]</span>
</div>
{expanded && value.map((item, itemIdx) => (
<XmlNode
key={itemIdx}
node={item}
nodeName={`${itemIdx}`}
isLast={itemIdx === value.length - 1}
defaultExpanded={false}
depth={depth + 2}
/>
))}
</div>
);
};| {children ?? ( | ||
| <StyledWrapper className="flex items-center"> | ||
| <button className="p-1"> | ||
| <IconBookmark size={16} strokeWidth={2} /> | ||
| </button> | ||
| </StyledWrapper> | ||
| )} |
There was a problem hiding this comment.
Nested interactive elements create accessibility issues.
When children is not provided, you have a <div role="button"> wrapping a <button>. This is problematic for screen readers and keyboard navigation. Replace the inner <button> with a non-interactive element.
{children ?? (
<StyledWrapper className="flex items-center">
- <button className="p-1">
+ <span className="p-1">
<IconBookmark size={16} strokeWidth={2} />
- </button>
+ </span>
</StyledWrapper>
)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {children ?? ( | |
| <StyledWrapper className="flex items-center"> | |
| <button className="p-1"> | |
| <IconBookmark size={16} strokeWidth={2} /> | |
| </button> | |
| </StyledWrapper> | |
| )} | |
| {children ?? ( | |
| <StyledWrapper className="flex items-center"> | |
| <span className="p-1"> | |
| <IconBookmark size={16} strokeWidth={2} /> | |
| </span> | |
| </StyledWrapper> | |
| )} |
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js
around lines 156-162, there's a nested interactive element: a div with
role="button" is wrapping an actual <button>, which harms accessibility; replace
the inner <button> with a non-interactive element (e.g., a <span> or <div>) that
preserves the visual styling (classes p-1 and IconBookmark) and mark it
aria-hidden="true" if appropriate, and ensure all keyboard/click handling
remains on the outer role="button" wrapper (no duplicate event handlers on the
inner element).
| <div role="button" tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown}> | ||
| {children ? children : ( | ||
| <StyledWrapper className="flex items-center"> | ||
| <button className="p-1"> | ||
| <IconEraser size={16} strokeWidth={2} /> | ||
| </button> | ||
| </StyledWrapper> | ||
| )} | ||
| </div> |
There was a problem hiding this comment.
Remove nested interactive elements.
The structure creates nested interactive elements: a div[role="button"] wrapping a <button> element. This violates accessibility best practices and creates redundant tab stops.
Apply this diff to flatten the structure:
return (
- <div role="button" tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown}>
- {children ? children : (
- <StyledWrapper className="flex items-center">
- <button className="p-1">
- <IconEraser size={16} strokeWidth={2} />
- </button>
- </StyledWrapper>
- )}
- </div>
+ <div role="button" tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown}>
+ {children ? children : (
+ <StyledWrapper className="flex items-center p-1">
+ <IconEraser size={16} strokeWidth={2} />
+ </StyledWrapper>
+ )}
+ </div>
);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div role="button" tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown}> | |
| {children ? children : ( | |
| <StyledWrapper className="flex items-center"> | |
| <button className="p-1"> | |
| <IconEraser size={16} strokeWidth={2} /> | |
| </button> | |
| </StyledWrapper> | |
| )} | |
| </div> | |
| <div role="button" tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown}> | |
| {children ? children : ( | |
| <StyledWrapper className="flex items-center p-1"> | |
| <IconEraser size={16} strokeWidth={2} /> | |
| </StyledWrapper> | |
| )} | |
| </div> |
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js around
lines 34-42, the markup nests a <button> inside a div[role="button"], which
creates redundant interactive elements and duplicate tab stops; to fix, flatten
the structure by keeping a single interactive control: either convert the outer
div into a non-interactive wrapper and keep the inner <button> as the sole
control (move onClick, onKeyDown, title to the button and remove role/tabIndex
from the wrapper), or remove the inner <button> and replace it with a
non-interactive element (e.g., a span with aria-hidden) so the outer
div[role="button"] remains the only interactive element; ensure only one element
is focusable/tabbable and that keyboard handlers and accessibility attributes
are attached to that single control.
| return ( | ||
| <StyledWrapper className="ml-2 flex items-center"> | ||
| <button onClick={copyResponse} disabled={!response.data} title="Copy response to clipboard"> | ||
| {copied ? ( | ||
| <IconCheck size={16} strokeWidth={1.5} /> | ||
| ) : ( | ||
| <IconCopy size={16} strokeWidth={1.5} /> | ||
| )} | ||
| </button> | ||
| </StyledWrapper> | ||
| <div role="button" tabIndex={0} onClick={handleClick} title={!children ? 'Copy response to clipboard' : null} onKeyDown={handleKeyDown}> | ||
| {children ? children : ( | ||
| <StyledWrapper className="flex items-center"> | ||
| <button className="p-1" disabled={!hasData}> | ||
| {copied ? ( | ||
| <IconCheck size={16} strokeWidth={2} /> | ||
| ) : ( | ||
| <IconCopy size={16} strokeWidth={2} /> | ||
| )} | ||
| </button> | ||
| </StyledWrapper> | ||
| )} | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
Fix nested interactive elements accessibility issue.
The outer div with role="button", tabIndex={0}, onClick, and onKeyDown wraps an actual <button> element when children is not provided. This creates nested interactive elements, an accessibility anti-pattern that:
- Creates duplicate tab stops for the same action
- Confuses screen readers about which element to interact with
- Violates WCAG guidelines
The outer div should only be interactive when custom children are provided. When using the default button rendering, the inner <button> should be the sole interactive element.
Apply this refactor:
+ const isCustomRender = !!children;
+
return (
- <div role="button" tabIndex={0} onClick={handleClick} title={!children ? 'Copy response to clipboard' : null} onKeyDown={handleKeyDown}>
+ <div
+ role={isCustomRender ? "button" : undefined}
+ tabIndex={isCustomRender ? 0 : undefined}
+ onClick={isCustomRender ? handleClick : undefined}
+ onKeyDown={isCustomRender ? handleKeyDown : undefined}
+ title={isCustomRender ? 'Copy response to clipboard' : undefined}
+ >
- {children ? children : (
+ {isCustomRender ? children : (
<StyledWrapper className="flex items-center">
- <button className="p-1" disabled={!hasData}>
+ <button
+ className="p-1"
+ disabled={!hasData}
+ onClick={handleClick}
+ onKeyDown={handleKeyDown}
+ title="Copy response to clipboard"
+ >
{copied ? (
<IconCheck size={16} strokeWidth={2} />
) : (
<IconCopy size={16} strokeWidth={2} />
)}
</button>
</StyledWrapper>
)}
</div>
);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js around
lines 52–66, the outer div with role="button", tabIndex and click/keydown
handlers currently wraps a native <button>, creating nested interactive
elements; change rendering so the outer wrapper is only interactive when custom
children are provided: if children exist, keep the outer div as the interactive
element with role/tabIndex/onClick/onKeyDown and render children inside; if
children are not provided, render a non-interactive wrapper (no role, no
tabIndex, no handlers) and attach the click handler (and title) to the inner
native <button> (or move onClick/onKeyDown to the button) so the button is the
sole focusable/interactive element and still respects hasData/disabled and
copied state.
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (7)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
53-67: Nested interactive elements issue persists.The outer
divstill hastabIndex={0},onClick, andonKeyDownregardless of whetherchildrenis provided. When rendering the default UI, you end up with a focusable wrapper containing a<button>— two tab stops for one action.Apply this diff to ensure single interactive element:
+ const isCustomRender = !!children; + return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={handleClick} title={!children ? 'Copy response to clipboard' : null} onKeyDown={handleKeyDown}> - {children ? children : ( + <div + role={isCustomRender ? 'button' : undefined} + tabIndex={isCustomRender ? 0 : undefined} + onClick={isCustomRender ? handleClick : undefined} + onKeyDown={isCustomRender ? handleKeyDown : undefined} + title={isCustomRender ? 'Copy response to clipboard' : undefined} + > + {isCustomRender ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1" disabled={!hasData}> + <button + className="p-1" + disabled={!hasData} + onClick={handleClick} + onKeyDown={handleKeyDown} + title="Copy response to clipboard" + > {copied ? ( <IconCheck size={16} strokeWidth={2} /> ) : ( <IconCopy size={16} strokeWidth={2} /> )} </button> </StyledWrapper> )} </div> );packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
34-44: Same nested interactive elements issue as ResponseCopy.When rendering the default UI, the outer
div[tabIndex=0]wraps an inner<button>, creating duplicate tab stops. This is the same anti-pattern flagged in the past review.Apply similar fix as suggested for ResponseCopy:
+ const isCustomRender = !!children; + return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown}> - {children ? children : ( - <StyledWrapper className="flex items-center"> - <button className="p-1"> - <IconEraser size={16} strokeWidth={2} /> - </button> - </StyledWrapper> - )} - </div> + <div + role={isCustomRender ? 'button' : undefined} + tabIndex={isCustomRender ? 0 : undefined} + onClick={isCustomRender ? clearResponse : undefined} + onKeyDown={isCustomRender ? handleKeyDown : undefined} + title={isCustomRender ? 'Clear response' : undefined} + > + {isCustomRender ? children : ( + <StyledWrapper className="flex items-center"> + <button + className="p-1" + onClick={clearResponse} + onKeyDown={handleKeyDown} + title="Clear response" + > + <IconEraser size={16} strokeWidth={2} /> + </button> + </StyledWrapper> + )} + </div> );packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
143-164: Nested interactive elements and inconsistent role assignment.Two issues here:
Nested button (duplicate): When
childrenis not provided, you have a focusable div withonClick/tabIndexwrapping a<button>. This creates duplicate focus targets and is problematic for screen readers. Replace the inner<button>with a<span>.Inverted role logic: The
role="button"is only set whenchildrenis provided (line 144), but whenchildrenis absent, the div still acts as a button (hasonClick,tabIndex,onKeyDown) without the proper role.<div - role={!!children ? 'button' : undefined} + role="button" tabIndex={isDisabled ? -1 : 0} aria-disabled={isDisabled} onKeyDown={handleKeyDown} onClick={handleSaveClick} title={ !children ? disabledMessage : (isDisabled ? disabledMessage : null) } className={classnames({ 'opacity-50 cursor-not-allowed': isDisabled })} data-testid="response-bookmark-btn" > {children ?? ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <span className="p-1"> <IconBookmark size={16} strokeWidth={2} /> - </button> + </span> </StyledWrapper> )} </div>packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
72-103: Fix nested interactive controls and improve accessibility of the layout toggleThe outer
<div role="button">wraps a real<button>(Line 94), which creates nested interactive elements. This is problematic for screen readers and keyboard users and was already flagged in a previous review.You can keep the outer element as the single interactive control and make the inner element non-interactive:
- {children ? children : ( - <StyledWrapper className="flex items-center w-full"> - <button className="p-1"> - {orientation === 'horizontal' ? ( - <IconDockToBottom /> - ) : ( - <IconDockToRight /> - )} - </button> - </StyledWrapper> - )} + {children ? children : ( + <StyledWrapper className="flex items-center w-full"> + <span className="p-1"> + {orientation === 'horizontal' ? ( + <IconDockToBottom /> + ) : ( + <IconDockToRight /> + )} + </span> + </StyledWrapper> + )}While you're here, consider wiring up an explicit accessible label on the outer control (e.g.
aria-label={title}and optionallyaria-pressed={orientation === 'vertical'}if you treat it as a toggle) so screen readers get a clear description instead of relying solely ontitle.packages/bruno-app/src/utils/response/index.js (1)
155-170:parsererrorcheck is dead code for HTML parsing.As noted in past review,
DOMParserwith'text/html'never produces aparsererrorelement - that's XML-specific behavior. This try-catch block effectively just returnstrue.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
87-89: Missingaltattribute on image.Screen readers need alt text for accessibility.
- return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} />; + return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} alt="Response preview" />;packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
165-184: Incorrect dependencies inuseMemo.
previewModeusesdetectedContentTypebut dependency array includescontentTypeanddataBufferinstead. Also, no explicit fallback return at the end.const previewMode = useMemo(() => { // Derive preview mode based on selected format if (selectedFormat === 'html') return 'preview-web'; if (selectedFormat === 'json') return 'preview-json'; if (selectedFormat === 'xml') return 'preview-xml'; if (selectedFormat === 'raw') return 'preview-text'; if (selectedFormat === 'javascript') return 'preview-web'; // For base64/hex, check content type to determine binary preview type if (selectedFormat === 'base64' || selectedFormat === 'hex') { if (detectedContentType) { if (detectedContentType.includes('image')) return 'preview-image'; if (detectedContentType.includes('pdf')) return 'preview-pdf'; if (detectedContentType.includes('audio')) return 'preview-audio'; if (detectedContentType.includes('video')) return 'preview-video'; } // for all other content types, return preview-text return 'preview-text'; } - }, [selectedFormat, contentType, dataBuffer]); + return 'preview-text'; // fallback for unexpected formats + }, [selectedFormat, detectedContentType]);
🧹 Nitpick comments (14)
packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
24-25: Consider adding ahasDataguard for consistency.Unlike
ResponseCopy, this component doesn't check if there's a response before enabling the clear action. While clearing an empty response is harmless, adding a similarhasDatapattern would improve UX consistency and prevent unnecessary dispatches.-const ResponseClear = ({ collection, item, children }) => { - const { clearResponse } = useResponseClear(item, collection); +const ResponseClear = ({ collection, item, children }) => { + const { clearResponse } = useResponseClear(item, collection); + const response = item.response || {}; + const hasData = !!response.data;Then use
hasDatato conditionally disable the button when rendering the default UI.packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
62-73: Redundant guard and missing toast for streaming.The
isDisabledcheck on line 69 is redundant sinceisResponseTooLarge(checked on line 62) is already part ofisDisabled. Also, streaming responses silently fail without a toast, which is inconsistent with the large response case.Consider consolidating:
- if (isResponseTooLarge) { - toast.error('Response size exceeds 5MB limit. Cannot save as example.'); - e.preventDefault(); - e.stopPropagation(); - return; - } - - if (isDisabled) { + if (isDisabled) { + if (isResponseTooLarge) { + toast.error('Response size exceeds 5MB limit. Cannot save as example.'); + } else if (isStreamingResponse) { + toast.error('Response Examples aren\'t supported in streaming responses yet.'); + } e.preventDefault(); e.stopPropagation(); return; }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (1)
40-74: Duplicate CSS:.xml-toggle-buttonand.xml-array-toggle-buttonare identical.Both classes share the exact same styles. Consider consolidating into a single class or using a shared base.
- .xml-toggle-button { - margin-right: 8px; - cursor: pointer; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - color: ${(props) => props.theme.codemirror.tokens.atom}; - flex-shrink: 0; - border-radius: 4px; - transition: background-color 0.2s; - - &:hover { - background-color: ${(props) => props.theme.console.buttonHoverBg}; - } - } - - .xml-array-toggle-button { - margin-right: 8px; - cursor: pointer; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - color: ${(props) => props.theme.codemirror.tokens.atom}; - flex-shrink: 0; - border-radius: 4px; - transition: background-color 0.2s; - - &:hover { - background-color: ${(props) => props.theme.console.buttonHoverBg}; - } - } + .xml-toggle-button, + .xml-array-toggle-button { + margin-right: 8px; + cursor: pointer; + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + color: ${(props) => props.theme.codemirror.tokens.atom}; + flex-shrink: 0; + border-radius: 4px; + transition: background-color 0.2s; + + &:hover { + background-color: ${(props) => props.theme.console.buttonHoverBg}; + } + }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (3)
28-33: Function defined inside render.
isValidTreeDatais recreated on every render. Since it's a pure utility, move it outside the component or wrap inuseCallback.+// Move outside component +const isValidTreeData = (data) => { + if (data === null || data === undefined) return false; + if (typeof data === 'object' && !Array.isArray(data)) return true; + if (Array.isArray(data)) return true; + return false; +}; + export default function XmlPreview({ data, defaultExpanded = true }) { // ... - const isValidTreeData = (data) => { - if (data === null || data === undefined) return false; - if (typeof data === 'object' && !Array.isArray(data)) return true; - if (Array.isArray(data)) return true; - return false; - };
219-221: Unused variables.
hasArrayValueandarrayLengthare declared but never used.- // Determine if this node's value is an array - const hasArrayValue = Array.isArray(node); - const arrayLength = hasArrayValue ? node.length : 0;
126-143: Array index as React key.Using
key={idx}for repeated elements can cause rendering issues if the array order changes. Consider using a more stable key if available.{node.map((item, idx) => ( <XmlNode - key={idx} + key={`${displayNodeName}-${idx}`} node={item}packages/bruno-app/src/utils/common/index.js (1)
466-483: Comment/behavior mismatch.Comment says "Simple HTML formatter" but uses
xmlFormat. This works but may produce unexpected output for non-XHTML content (e.g., void elements without self-closing syntax).-// Simple HTML formatter that indents HTML properly +// Formats HTML/XML-like strings using xml-formatter with fallback export function prettifyHtmlString(htmlString) {packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (2)
1-1: Unused import.
useMemois imported but not used in this component.-import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react';
121-131: Hardcoded colors in fallback UI.Per coding guidelines, Tailwind classes shouldn't define colors - use styled components with theme props instead.
Consider moving this to StyledWrapper with theme-aware colors:
default: return ( - <div className="p-4 flex flex-col items-center justify-center h-full text-center"> - <div className="text-lg font-semibold text-gray-700 dark:text-gray-200 mb-2"> + <div className="p-4 flex flex-col items-center justify-center h-full text-center no-preview-message"> + <div className="text-lg font-semibold mb-2 title"> No Preview Available </div> - <div className="text-sm text-gray-500 dark:text-gray-400"> + <div className="text-sm subtitle"> Sorry, no preview is available for this content type. </div> </div> );Then add theme-aware styles in StyledWrapper. Based on learnings about using theme props for colors.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
151-159: Unused dependency inuseMemo.
responseEncodingis in the dependency array but not used in theformattedDatacomputation.[data, dataBuffer, responseEncoding, selectedFormat, filter, isLargeResponse, showLargeResponse] + // Note: responseEncoding removed if not actually used [data, dataBuffer, selectedFormat, filter, isLargeResponse, showLargeResponse]packages/bruno-app/src/ui/ButtonDropdown/index.jsx (4)
9-19: Remove redundant role and add explicit button type.The
role="button"attribute is unnecessary on a<button>element. Additionally, addtype="button"to prevent unintended form submissions.<button ref={ref} className={classnames('button-dropdown-button flex items-center gap-1.5 text-xs', 'cursor-pointer select-none', 'h-7 rounded-[6px] border px-2 transition-colors', { 'opacity-50 cursor-not-allowed': disabled }, className)} data-testid={props['data-testid']} style={style} - role="button" + type="button" {...props} >
48-60: Simplify unnecessary variable assignments.The assignments
const groups = options(line 50, 76) andconst flatOptions = options(line 56, 104) don't add clarity or type safety. Consider usingoptionsdirectly.const findSelectedLabel = () => { if (isGrouped) { - const groups = options; - for (const group of groups) { + for (const group of options) { const option = group.options.find((opt) => opt.value === value); if (option) return option.label; } } else { - const flatOptions = options; - const option = flatOptions.find((opt) => opt.value === value); + const option = options.find((opt) => opt.value === value); if (option) return option.label; } return label; };Apply similar changes to
renderOptionsat lines 76 and 104.
74-120: Consider extracting option rendering logic.The
renderOptionsfunction handles both grouped and flat options in a single 46-line function. Extracting separate helper functions for each case would improve maintainability.Example refactor:
const renderFlatOptions = () => { return options.map((option) => ( <div key={option.value} className={classnames('dropdown-item flex items-center gap-2', { active: option.value === value })} onClick={() => handleOptionSelect(option.value)} > <span>{option.label}</span> {option.value === value && ( <span className="ml-auto">✓</span> )} </div> )); }; const renderGroupedOptions = () => { return options.map((group, groupIndex) => ( <React.Fragment key={groupIndex}> {group.options.map((option, optionIndex) => { const isFirstInGroup = optionIndex === 0; const isFirstGroup = groupIndex === 0; const showSeparator = !isFirstGroup && isFirstInGroup; return ( <div key={option.value} className={classnames('dropdown-item flex items-center gap-2', { 'active': option.value === value, 'border-top': showSeparator })} onClick={() => handleOptionSelect(option.value)} > <span>{option.label}</span> {option.value === value && ( <span className="ml-auto">✓</span> )} </div> ); })} </React.Fragment> )); }; const renderOptions = () => { return isGrouped ? renderGroupedOptions() : renderFlatOptions(); };
130-130: Simplify data-testid spreading.The conditional spread for
data-testidcan be simplified for better readability.-<div {...(props['data-testid'] && { 'data-testid': props['data-testid'] + '-dropdown' })}> +<div data-testid={props['data-testid'] ? `${props['data-testid']}-dropdown` : undefined}>
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js(4 hunks)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/utils/common/index.js(4 hunks)packages/bruno-app/src/utils/response/index.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.jspackages/bruno-app/src/ui/ButtonDropdown/index.jsxpackages/bruno-app/src/utils/common/index.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.jspackages/bruno-app/src/utils/response/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/index.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{jsx,tsx}: Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Files:
packages/bruno-app/src/ui/ButtonDropdown/index.jsx
🧠 Learnings (6)
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Applied to files:
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Applied to files:
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Applied to files:
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Add in JSDoc comments to add more details to the abstractions if needed
Applied to files:
packages/bruno-app/src/utils/common/index.js
📚 Learning: 2025-12-05T20:31:32.975Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:32.975Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Applied to files:
packages/bruno-app/src/utils/common/index.js
📚 Learning: 2025-12-02T09:45:31.698Z
Learnt from: sid-bruno
Repo: usebruno/bruno PR: 6266
File: packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js:38-38
Timestamp: 2025-12-02T09:45:31.698Z
Learning: In the ResponseCopy component (packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js), the copy button is intentionally disabled using `!response.data` to prevent copying stream resets which result in empty strings.
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js
🧬 Code graph analysis (11)
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (2)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (1)
StyledWrapper(3-75)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (2)
packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
StyledWrapper(3-22)
packages/bruno-app/src/ui/ButtonDropdown/index.jsx (3)
packages/bruno-app/src/components/Icons/IconCaretDown/index.js (1)
IconCaretDown(3-16)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
StyledWrapper(3-22)packages/bruno-app/src/components/Dropdown/index.js (1)
Dropdown(5-25)
packages/bruno-app/src/utils/common/index.js (1)
packages/bruno-js/src/bru.js (1)
xmlFormat(2-2)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (3)
packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js (1)
response(39-39)packages/bruno-app/src/components/ResponsePane/index.js (1)
response(67-67)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (5)
packages/bruno-app/src/utils/common/index.js (3)
parsed(318-318)i(21-21)i(515-515)packages/bruno-app/src/components/ErrorAlert/index.js (1)
ErrorAlert(5-23)packages/bruno-app/src/components/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (1)
StyledWrapper(3-75)packages/bruno-app/src/utils/response/index.js (2)
parser(157-157)i(80-80)
packages/bruno-app/src/utils/response/index.js (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
contentType(126-126)buffer(118-118)packages/bruno-app/src/utils/common/index.js (4)
contentType(93-93)i(21-21)i(515-515)trimmed(537-537)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (3)
packages/bruno-app/src/utils/common/index.js (7)
dataBuffer(280-280)dataBuffer(384-384)contentType(93-93)getContentType(83-119)getContentType(83-119)formatResponse(273-429)formatResponse(273-429)packages/bruno-app/src/utils/response/index.js (4)
detectContentTypeFromBuffer(178-260)detectContentTypeFromBuffer(178-260)getDefaultResponseFormat(8-59)getDefaultResponseFormat(8-59)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
QueryResultPreview(20-133)
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (3)
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
useResponseLayoutToggle(44-44)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
handleKeyDown(27-32)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
handleKeyDown(40-45)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (4)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
HtmlPreview(5-76)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js (1)
JsonPreview(5-61)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
TextPreview(3-23)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
XmlPreview(6-71)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
StyledWrapper(3-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - macOS
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
🔇 Additional comments (21)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (2)
6-35: LGTM on hook extraction!The
useResponseCopyhook cleanly encapsulates the copy logic, state management, and proper cleanup. Good use of thehasDataflag to prevent copying empty streams (per existing design intent).
40-51: Space key fix applied correctly.The
e.preventDefault()is now in place for both Enter and Space keys, preventing unwanted page scroll.packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (2)
7-22: LGTM on hook extraction!Clean encapsulation of the Redux dispatch logic. The hook signature is clear and the return value is appropriate.
27-32: Space key fix applied correctly.The
e.preventDefault()is now in place, addressing the previous review feedback.packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
41-52: LGTM!Keyboard handling is correctly implemented with proper guards for the disabled state and standard Enter/Space key triggers.
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (2)
6-49: Icon components look good and are ready for reuse
IconDockToBottomandIconDockToRightare self-contained, consistent SVG components and safe to export for reuse. No changes needed here.
53-67: Guard against undefinedpreferences/layoutwhen buildingupdatedPreferencesIn
toggleOrientation, you spreadpreferencesandpreferences.layoutdirectly. If either is undefined, this will throw at runtime when the user toggles the layout. The optional chaining inpreferences?.layout?.responsePaneOrientationindicates these values can be undefined.Defensively default these to empty objects when spreading:
- const updatedPreferences = { - ...preferences, - layout: { - ...preferences.layout, - responsePaneOrientation: newOrientation - } - }; + const updatedPreferences = { + ...(preferences || {}), + layout: { + ...(preferences?.layout || {}), + responsePaneOrientation: newOrientation + } + };This avoids a TypeError when either object is missing, while preserving behavior when present.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (1)
1-39: LGTM!Good use of theme props for colors and codemirror tokens. The monospace font stack and styling are appropriate for code/XML display.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (2)
349-359: Case sensitivity inconsistency in nodeName handling.Line 349 uses
toLowerCase()for comparison but line 346 preserves original casing. This could cause duplicate entries if XML has mixed-case elements like<Item>and<item>.Verify if mixed-case XML element names should be treated as distinct or merged.
1-71: LGTM on overall structure.Good error handling with ErrorAlert, proper useMemo for parsing, and clean component composition with XmlNode/XmlArrayNode.
packages/bruno-app/src/utils/common/index.js (3)
105-116: LGTM!Good addition of JavaScript content-type pattern matching, consistent with existing JSON/XML patterns.
325-422: LGTM on new format handlers.Clean implementation of html, javascript, hex, base64, and text/raw format branches with appropriate large response handling and fallbacks.
530-558: Improved hex detection heuristics.Past review concern about false positives addressed with minimum length of 6 and requiring at least one digit. Good defensive approach.
packages/bruno-app/src/utils/response/index.js (3)
1-59: LGTM!Clean rule-based MIME type mapping with proper fallbacks. Good separation of concerns.
72-93: Guard for empty buffer added.Line 76 now properly handles empty buffers, addressing the previous division-by-zero concern.
173-260: Solid magic number detection.Comprehensive coverage of common binary formats (images, video, audio, archives) with appropriate fallback to text detection.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
109-119: LGTM!Clean integration of new preview components for JSON, text, and XML formats.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (3)
13-31: LGTM!Well-structured format options with clear CodeMirror mode mappings.
46-105: LGTM on custom hooks.Clean separation of concerns for initial format detection and preview option filtering. Buffer conversion error handling is appropriate.
186-190: Fallback mode corrected.Good - now uses
'text/plain'instead of the non-standard'application/text'.packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
8-14: Verify that all referenced theme properties are defined in the theme configuration.The component uses
theme.dropdown.primaryText,theme.dropdown.hoverBg,theme.dropdown.separator, andtheme.workspace.border. Confirm these properties exist in your theme configuration to prevent runtime errors.
6b16847 to
ac51080
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/bruno-app/src/ui/ErrorAlert/index.js (1)
5-19: Harden message rendering and make the close control properly accessibleTwo small improvements here:
JSON.stringify(message, null, 2)will throw for non-serializable objects (e.g. circular structures) and gives slightly odd output forErrorinstances. Consider normalizing to a string with a helper that handlesErrorand wrapsJSON.stringifyin a try/catch so the alert itself never crashes.- The close control is a
divwithonClick. For accessibility, it should be a real<button>(withtype="button"and anaria-label) so it’s focusable and activatable via keyboard:- <div className="close-button flex-shrink-0 cursor-pointer" onClick={onClose}> - <IconX size={16} strokeWidth={1.5} /> - </div> + <button + type="button" + className="close-button flex-shrink-0 cursor-pointer" + onClick={onClose} + aria-label="Dismiss error" + > + <IconX size={16} strokeWidth={1.5} /> + </button>packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
83-95: Make expand/collapse buttons keyboard-accessibleBoth toggle controls are
<button>elements but usetabIndex={-1}, which removes them from the normal tab order. That makes the XML tree hard to operate via keyboard:<button onClick={toggle} className="xml-array-toggle-button" - tabIndex={-1} + tabIndex={0} // or omit; default is focusable aria-expanded={expanded} >Same for the main node toggle button. Consider also adding an
aria-labelthat describes what’s being expanded (e.g. includingnodeName) for screen readers.Also applies to: 224-243
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js(1 hunks)packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ErrorAlert/index.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/ui/ErrorAlert/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js
🧠 Learnings (3)
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Applied to files:
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Applied to files:
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Applied to files:
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js
🧬 Code graph analysis (2)
packages/bruno-app/src/ui/ErrorAlert/index.js (1)
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (5)
packages/bruno-app/src/utils/common/index.js (3)
parsed(318-318)i(21-21)i(515-515)packages/bruno-app/src/ui/ErrorAlert/index.js (1)
ErrorAlert(5-23)packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (1)
StyledWrapper(3-75)packages/bruno-app/src/utils/response/index.js (2)
parser(157-157)i(80-80)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: SSL Tests - macOS
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
- GitHub Check: Playwright E2E Tests
🔇 Additional comments (3)
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (1)
3-41: LGTM: theming and layout responsibilities are well splitThis wrapper cleanly delegates layout to Tailwind (via caller classNames) while keeping all colors and typography theme-driven in styled-components. Border/background choices and scroll bounds look consistent with the rest of the app and coding standards.
Based on learnings, styled components owning color/theming is exactly what we want here.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (2)
6-71: XML preview flow and error handling look solidThe main XmlPreview component wiring (type-checking
data,useMemofor parsing, clear ErrorAlert branches, and unwrapping the single-root object) is straightforward and easy to follow. The tree rendering viaXmlNode/XmlArrayNodewith localuseStatefor expansion nicely avoids any direct DOM manipulation and aligns with the previous review feedback.
155-167: Avoid renderingnullXML nodes as the string"null"
xmlToObjectcan legitimately returnnullfor empty elements (especially in repeated arrays), andisTextNodecurrently treatsnullas a text node, which causes empty XML nodes to render as the literal text"null"in the UI rather than displaying as empty values.The issue flows through this path:
function isTextNode(node) { return typeof node === 'string' || typeof node === 'number' || node === null; }When a leaf node is null, the code calls
String(node), converting it to the string"null":if (isLeaf && isTextNode(node)) { const value = String(node); // null → "null" // ... }To fix this, remove
node === nullfromisTextNode()so null leaves render through the empty‑leaf branch with proper empty value styling:-function isTextNode(node) { - return typeof node === 'string' || typeof node === 'number' || node === null; -} +function isTextNode(node) { + return typeof node === 'string' || typeof node === 'number'; +}
...ges/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js
Show resolved
Hide resolved
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/ResponsePane/Timeline/TimelineItem/Common/Body/index.js (1)
17-25: Remove unnecessarykeyprop.The
keyprop on line 24 is unnecessary here. React'skeyis only meaningful when rendering lists/arrays to help with reconciliation. SinceQueryResponseis a single element (not mapped from an array), this prop serves no purpose and should be removed.Apply this diff:
<div className="h-96 overflow-auto"> <QueryResponse item={item} collection={collection} data={data} dataBuffer={dataBuffer} headers={headers} error={error} - key={item?.uid} /> </div>
♻️ Duplicate comments (11)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
157-163: Nested interactive elements create accessibility issues.When
childrenis not provided, the fallback UI wraps a<button>element (line 159) inside a<div>that handles all the click and keyboard events (lines 147-148). This nested structure is problematic for screen readers and keyboard navigation, as previously noted.Replace the inner
<button>with a non-interactive element:{children ?? ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <span className="p-1"> <IconBookmark size={16} strokeWidth={2} /> - </button> + </span> </StyledWrapper> )}packages/bruno-app/src/utils/response/index.js (1)
155-170:parsererrorcheck is dead code for HTML parsing.
DOMParserwith'text/html'is lenient and never generates aparsererrorelement — that behavior is XML-specific. This check and try-catch are effectively unreachable code.try { - // Parse with DOMParser const parser = new DOMParser(); const doc = parser.parseFromString(trimmed, 'text/html'); - - // Check for parsing errors - const parseError = doc.querySelector('parsererror'); - if (parseError) { - return false; - } - - // HTML parser is lenient; if we reach here with valid tags, consider it valid return true; - } catch (error) { - return false; - } + } catch { + return false; // Defensive, though unlikely to throw + }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
345-359: Case-sensitivity bug remains unaddressed.The comparison at line 349 compares
c.nodeName.toLowerCase()againstchildNamewhich preserves original casing. Tags like<Item></Item>won't match ('item' === 'Item'is false), causing empty repeated elements to be dropped.elementChildren.forEach((child) => { const childName = child.nodeName; // Preserve original casing const childValue = xmlToObject(child); - - if (childValue !== null || elementChildren.filter((c) => c.nodeName.toLowerCase() === childName).length > 1) { + const sameNameCount = elementChildren.filter( + (c) => c.nodeName.toLowerCase() === childName.toLowerCase() + ).length; + + if (childValue !== null || sameNameCount > 1) {packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
85-103: Nested interactive elements still present.The outer
<div role="button">wraps an inner<button>(line 94), creating nested interactive elements. This was flagged in a prior review. Screen readers and keyboard navigation will have issues with this pattern.Consider the previously suggested fix:
{children ? children : ( <StyledWrapper className="flex items-center w-full"> - <button className="p-1"> + <span className="p-1"> {orientation === 'horizontal' ? ( <IconDockToBottom /> ) : ( <IconDockToRight /> )} - </button> + </span> </StyledWrapper> )}packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
87-89: Missingaltattribute on image.Accessibility requires an
altattribute on<img>elements.- return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} />; + return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} alt="Response preview" />;packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (1)
42-60: Fix nested interactive elements and inconsistent accessibility attributes.The wrapper div has inconsistent interactive attributes and creates nested interactive elements when no custom children are provided:
tabIndex={0}makes it focusable even whenroleis undefined- When children are not provided, the outer div wraps a
<button>element, creating duplicate tab stopsThis is the same accessibility issue flagged in ResponseCopy and ResponseClear.
Apply this fix:
+ const isCustomRender = !!children; + return ( <div - role={!!children ? 'button' : undefined} - tabIndex={isDisabled ? -1 : 0} + role={isCustomRender ? 'button' : undefined} + tabIndex={isCustomRender && !isDisabled ? 0 : undefined} aria-disabled={isDisabled} - onClick={saveResponseToFile} - onKeyDown={handleKeyDown} + onClick={isCustomRender ? saveResponseToFile : undefined} + onKeyDown={isCustomRender ? handleKeyDown : undefined} title={!children ? 'Save response to file' : null} className={classnames({ 'opacity-50 cursor-not-allowed': isDisabled })} > - {children ? children : ( + {isCustomRender ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <button + className="p-1" + onClick={saveResponseToFile} + onKeyDown={handleKeyDown} + disabled={isDisabled} + title="Save response to file" + > <IconDownload size={16} strokeWidth={2} /> </button> </StyledWrapper> )} </div> );Based on learnings, the disabled check using
!response.dataBufferis intentional to prevent saving incomplete stream data.packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
54-66: Nested interactive elements issue remains unresolved.The accessibility issue flagged in the previous review is still present. The outer div has
tabIndex={0}for all cases butrole="button"only when children exist, creating inconsistent behavior. When no children are provided, the outer div wraps a<button>element, resulting in nested interactive elements.Apply the previously suggested fix:
+ const isCustomRender = !!children; + return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={handleClick} title={!children ? 'Copy response to clipboard' : null} onKeyDown={handleKeyDown}> - {children ? children : ( + <div + role={isCustomRender ? 'button' : undefined} + tabIndex={isCustomRender ? 0 : undefined} + onClick={isCustomRender ? handleClick : undefined} + onKeyDown={isCustomRender ? handleKeyDown : undefined} + title={isCustomRender ? 'Copy response to clipboard' : undefined} + > + {isCustomRender ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1" disabled={!hasData}> + <button + className="p-1" + disabled={!hasData} + onClick={handleClick} + onKeyDown={handleKeyDown} + title="Copy response to clipboard" + > {copied ? ( <IconCheck size={16} strokeWidth={2} /> ) : ( <IconCopy size={16} strokeWidth={2} /> )} </button> </StyledWrapper> )} </div> );packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
35-43: Nested interactive elements issue remains unresolved.The accessibility issue flagged in the previous review is still present. The structure nests a
<button>inside adivwithtabIndex={0}, creating duplicate tab stops and an accessibility anti-pattern.Apply the previously suggested fix:
+ const isCustomRender = !!children; + return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown}> - {children ? children : ( + <div + role={isCustomRender ? 'button' : undefined} + tabIndex={isCustomRender ? 0 : undefined} + onClick={isCustomRender ? clearResponse : undefined} + onKeyDown={isCustomRender ? handleKeyDown : undefined} + title={isCustomRender ? 'Clear response' : undefined} + > + {isCustomRender ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <button + className="p-1" + onClick={clearResponse} + onKeyDown={handleKeyDown} + title="Clear response" + > <IconEraser size={16} strokeWidth={2} /> </button> </StyledWrapper> )} </div> );packages/bruno-app/src/ui/ButtonDropdown/index.jsx (2)
2-2: Use custom IconCaretDown component for consistency.The codebase has a custom
IconCaretDownatcomponents/Icons/IconCaretDown/index.js. Import that instead of the external@tabler/iconspackage to maintain consistency.-import { IconCaretDown } from '@tabler/icons'; +import IconCaretDown from 'components/Icons/IconCaretDown';Based on past review comments.
69-71: Close dropdown after selection.The dropdown remains open after an option is selected, requiring users to click outside to dismiss it.
const handleOptionSelect = (optionValue) => { onChange(optionValue); + dropdownTippyRef.current?.hide(); };Based on past review comments.
tests/utils/page/actions.ts (1)
546-565:switchToEditorTabis still identical toswitchToPreviewTaband likely incorrect.Both helpers click the same
preview-response-tabcontrol, soswitchToEditorTabdoes not guarantee ending up in the editor view and can leave tests in the wrong state (or toggle preview back on).If the preview is a toggle,
switchToEditorTabshould only click when preview is currently on. For example:const switchToEditorTab = async (page: Page) => { await test.step('Switch to editor tab', async () => { const responseFormatTab = page.getByTestId('format-response-tab'); await responseFormatTab.click(); const previewTab = page.getByTestId('preview-response-tab'); - await previewTab.click(); + const isPreviewOn = await previewTab.isChecked(); + if (isPreviewOn) { + await previewTab.click(); + } }); };Adjust the state check if the underlying control is not checkable, but the key point is:
switchToEditorTabshould reliably land in editor mode, not mirrorswitchToPreviewTab.
🧹 Nitpick comments (13)
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (1)
13-13: Avoid hardcoded hex comparison for theme detection.Comparing against
#1e1e1eis fragile and couples this component to a specific theme value. If the dark theme background changes, this logic breaks silently. Based on learnings, use the theme prop to manage colors rather than hardcoded checks.Consider using a dedicated theme property (e.g.,
props.theme.isDarkor similar) or define the background color directly in your theme:- background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(40, 40, 40, 0.5)' : 'rgba(250, 250, 250, 0.9)')}; + background-color: ${(props) => props.theme.colors.bg.danger};Or if a semantic token doesn't exist yet, add one to the theme definition and reference it here.
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
54-76: Consider consolidating disabled checks.The function has two overlapping checks:
- Line 62 checks
isResponseTooLarge(part ofisDisabled)- Line 69 checks the full
isDisabledconditionSince
isDisabled = isResponseTooLarge || isStreamingResponse, and line 62 handles the size case with a specific error message, line 69 effectively only guards againstisStreamingResponse. Consider either:
- Moving the streaming-specific error message to line 69 for clarity, or
- Consolidating to a single check with conditional error messages
This improves code maintainability.
Example refactor:
- if (isResponseTooLarge) { - toast.error('Response size exceeds 5MB limit. Cannot save as example.'); - e.preventDefault(); - e.stopPropagation(); - return; - } - if (isDisabled) { + const errorMessage = isResponseTooLarge + ? 'Response size exceeds 5MB limit. Cannot save as example.' + : 'Response Examples aren\'t supported in streaming responses yet.'; + toast.error(errorMessage); e.preventDefault(); e.stopPropagation(); return; }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (2)
1-2: Import order nit.React import should typically come first before other imports for consistency.
-import ErrorAlert from 'ui/ErrorAlert/index'; -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo } from 'react'; +import ErrorAlert from 'ui/ErrorAlert/index';
130-132: Consider more stable key for array mapping.Using index alone as
keycan cause reconciliation issues if the array order changes. Since this is read-only preview, it's acceptable but combining with the node name would be safer:- <XmlNode - key={idx} + <XmlNode + key={`${displayNodeName}-${idx}`}packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js (1)
5-25: Consider movingvalidateJsonDataoutside the component.This helper is pure and doesn't depend on component state or props. Defining it inside recreates the function on every render.
+// Helper function to validate and parse JSON data +const validateJsonData = (data) => { + if (typeof data === 'object' && data !== null) { + return { data, error: null }; + } + + if (typeof data === 'string') { + try { + const parsed = JSON.parse(data); + return { data: parsed, error: null }; + } catch (e) { + return { data: null, error: `Invalid JSON format: ${e.message}` }; + } + } + + return { data: null, error: 'Invalid input. Expected a JSON object, array, or valid JSON string.' }; +}; + const JsonPreview = ({ data, displayedTheme }) => { - // Helper function to validate and parse JSON data - const validateJsonData = (data) => { - // If data is already an object or array, use it directly - if (typeof data === 'object' && data !== null) { - return { data, error: null }; - } - ... - };packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
1-1: Unused import:useMemo.
useMemois imported but not used in this file.-import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react';packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
46-68: Duplicated buffer conversion logic across hooks.Both
useInitialResponseFormatanduseResponsePreviewFormatOptionshave identical try-catch blocks for convertingdataBufferto a Buffer. Consider extracting this to a shared helper.// Could extract to a helper at the top of the file const safeBufferFromBase64 = (dataBuffer) => { try { return dataBuffer ? Buffer.from(dataBuffer, 'base64') : null; } catch (error) { console.error('Error converting dataBuffer to Buffer:', error); return null; } };Also applies to: 70-105
151-159: Unused dependency:responseEncoding.
responseEncodingis in the dependency array but not used in theformatResponsecall.const formattedData = useMemo( () => { if (isLargeResponse && !showLargeResponse) { return ''; } return formatResponse(data, dataBuffer, selectedFormat, filter); }, - [data, dataBuffer, responseEncoding, selectedFormat, filter, isLargeResponse, showLargeResponse] + [data, dataBuffer, selectedFormat, filter, isLargeResponse, showLargeResponse] );packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (1)
28-32: Consider removing stopPropagation unless required.The
e.stopPropagation()in the disabled state prevents event bubbling. Unless there's a specific need to stop propagation (e.g., nested components that should not handle the event), this may be unnecessary.If not needed:
const handleKeyDown = (e) => { if (isDisabled) { e.preventDefault(); - e.stopPropagation(); return; }packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
51-80: LGTM with one observation.The space checking logic correctly measures the container and siblings to determine whether to show the dropdown menu. The use of
individualButtonsWidthRefto cache the buttons' width is a good optimization.One minor note: Line 74 calculates
shouldShowMenuusing a strict less-than comparison. Consider whether equality should also trigger the menu (i.e.,<=) to handle exact-fit edge cases where content might wrap or overflow.packages/bruno-app/src/ui/ButtonDropdown/index.jsx (1)
18-18: Remove redundant role attribute.The
role="button"is unnecessary since<button>elements already have the button role implicitly.- role="button"tests/utils/page/actions.ts (2)
472-494: Tidy upopenRequesthelper and consider re-adding an assertion.The new collection-scoped
openRequestmakes sense for disambiguating requests within a collection, but:
- The old implementation is now commented out; leaving it in the file adds noise and can confuse future readers.
- The new version no longer asserts that the active tab matches
requestName, which the old helper did vialocators.tabs.activeRequestTab().Consider:
- Removing the commented-out implementation once you’re confident all callers are migrated.
- Reintroducing an assertion that the opened tab matches
requestNameto catch navigation issues early.
534-540: Optional: reuse common response locators insideswitchResponseFormat.Implementation is straightforward, but you could avoid duplicating selectors by using
buildCommonLocators(page).response.formatTab()and.formatTabDropdown()instead of hardcoding the test IDs here. That keeps all selector definitions centralized inlocators.ts.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (59)
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js(2 hunks)packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/index.js(5 hunks)packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js(2 hunks)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ErrorAlert/index.js(1 hunks)packages/bruno-app/src/utils/common/codemirror.js(1 hunks)packages/bruno-app/src/utils/common/index.js(4 hunks)packages/bruno-app/src/utils/response/index.js(1 hunks)tests/response/large-response-crash-prevention.spec.ts(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/bruno.json(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru(1 hunks)tests/response/response-format-select-and-preview/init-user-data/collection-security.json(1 hunks)tests/response/response-format-select-and-preview/init-user-data/preferences.json(1 hunks)tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts(1 hunks)tests/utils/page/actions.ts(3 hunks)tests/utils/page/locators.ts(2 hunks)
💤 Files with no reviewable changes (3)
- packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
- packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js
🚧 Files skipped from review as they are similar to previous changes (31)
- packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js
- tests/response/response-format-select-and-preview/init-user-data/collection-security.json
- packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js
- tests/response/response-format-select-and-preview/init-user-data/preferences.json
- packages/bruno-app/src/utils/common/codemirror.js
- tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js
- packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js
- packages/bruno-app/src/ui/ErrorAlert/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js
- packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js
- tests/response/response-format-select-and-preview/fixtures/collection/bruno.json
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js
- packages/bruno-app/src/utils/common/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
- packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js
- packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js
- tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru
- packages/bruno-app/src/components/ResponsePane/StatusCode/index.js
- packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js
- tests/response/large-response-crash-prevention.spec.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/ui/ButtonDropdown/index.jsxpackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.jspackages/bruno-app/src/components/RunnerResults/ResponsePane/index.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/ResponsePane/ResponseSize/index.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.jspackages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jstests/utils/page/actions.tspackages/bruno-app/src/utils/response/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/index.jstests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.jspackages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jstests/utils/page/locators.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{jsx,tsx}: Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Files:
packages/bruno-app/src/ui/ButtonDropdown/index.jsx
tests/**/**.*
⚙️ CodeRabbit configuration file
tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:
Follow best practices for Playwright code and e2e automation
Try to reduce usage of
page.waitForTimeout();in code unless absolutely necessary and the locator cannot be found using existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture Example*: Here's an example of possible fixture and test pair
. ├── fixtures │ └── collection │ ├── base.bru │ ├── bruno.json │ ├── collection.bru │ ├── ws-test-request-with-headers.bru │ ├── ws-test-request-with-subproto.bru │ └── ws-test-request.bru ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection ├── headers.spec.ts ├── persistence.spec.ts ├── variable-interpolation │ ├── fixtures │ │ └── collection │ │ ├── environments │ │ ├── bruno.json │ │ └── ws-interpolation-test.bru │ ├── init-user-data │ └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection └── subproto.spec.ts
Files:
tests/utils/page/actions.tstests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tstests/utils/page/locators.ts
🧠 Learnings (5)
📚 Learning: 2025-12-02T09:45:31.709Z
Learnt from: sid-bruno
Repo: usebruno/bruno PR: 6266
File: packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js:38-38
Timestamp: 2025-12-02T09:45:31.709Z
Learning: In the ResponseCopy component (packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js), the copy button is intentionally disabled using `!response.data` to prevent copying stream resets which result in empty strings.
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.jspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Applied to files:
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.jspackages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.test.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created
Applied to files:
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts
🧬 Code graph analysis (10)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js (2)
packages/bruno-app/src/ui/ErrorAlert/index.js (1)
ErrorAlert(5-23)packages/bruno-app/src/providers/Theme/index.js (1)
displayedTheme(11-11)
packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
QueryResponse(8-58)
packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (3)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
handleKeyDown(40-45)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
handleKeyDown(75-80)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/utils/response/index.js (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
contentType(126-126)packages/bruno-app/src/utils/common/index.js (4)
contentType(93-93)i(21-21)i(515-515)trimmed(537-537)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
parser(302-302)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
packages/bruno-app/src/utils/common/index.js (7)
dataBuffer(280-280)dataBuffer(384-384)contentType(93-93)getContentType(83-119)getContentType(83-119)formatResponse(273-429)formatResponse(273-429)packages/bruno-app/src/utils/response/index.js (4)
detectContentTypeFromBuffer(178-260)detectContentTypeFromBuffer(178-260)getDefaultResponseFormat(8-59)getDefaultResponseFormat(8-59)
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (2)
tests/utils/page/actions.ts (6)
closeAllCollections(593-593)openRequest(609-609)sendRequestAndWaitForResponse(613-613)switchToPreviewTab(615-615)switchToEditorTab(616-616)switchResponseFormat(614-614)packages/bruno-app/src/utils/codemirror/brunoVarInfo.js (1)
editorContainer(298-298)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
StyledWrapper(3-13)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (3)
packages/bruno-app/src/components/ResponsePane/index.js (1)
response(67-67)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
handleKeyDown(27-32)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)
packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
QueryResponse(8-58)
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (1)
StyledWrapper(3-75)
🪛 GitHub Check: Unit Tests
tests/utils/page/locators.ts
[failure] 88-88:
Unexpected trailing comma
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Playwright E2E Tests
- GitHub Check: SSL Tests - Windows
🔇 Additional comments (39)
packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js (1)
3-7: Typography update looks good and consistentThe font-weight bump to 600 is a safe, purely visual change, keeps using theme-based typography and colors, and matches the documented styling patterns for wrappers.
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (3)
1-2: LGTM!Clean import statement.
15-26: LGTM!Clean close-button styling with appropriate hover transition and theme-based icon color.
28-41: LGTM!Error title and message styles are well-structured, using theme tokens for colors and font sizing as expected.
packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js (1)
22-22: LGTM! Styling adjustment aligns with UI consistency goals.The reduced left margin (ml-4 → ml-2) tightens the spacing and matches the broader ResponsePane layout updates in this PR.
packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js (2)
1-1: LGTM! Import updated to use new QueryResponse wrapper.The import change aligns with the PR's objective to add response format selection capabilities.
17-25: Verify ifdisableRunEventListenerprop should be passed toQueryResponse.The
QueryResponsecomponent may accept adisableRunEventListenerprop based on its signature. Confirm whether this prop was previously passed and if its omission here is intentional or an oversight during the refactor.packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js (2)
6-11: LGTM! Clean hover interaction.The border-radius and hover state styling are well-implemented with appropriate theme tokens for an interactive button control.
5-5: Verify semantic appropriateness of CodeMirror color token for UI button.Using
theme.codemirror.variable.info.iconColorfor a ResponsePane UI control may be semantically misaligned if CodeMirror tokens are reserved for editor syntax highlighting. The hover state correctly usestheme.workspace.button.bg, suggesting the default color should also come from a UI-specific token. Consider verifying the theme structure and checking if a more appropriate token exists (e.g.,theme.text,theme.icon, or a workspace-specific variant).packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
6-12: LGTM! Styling enhancements follow theme conventions.The color migration to
theme.codemirror.variable.info.iconColor, border-radius, and hover state provide better visual consistency and interactive feedback. Theme props are correctly used per coding standards.packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (3)
27-27: LGTM! Optional children prop adds flexibility.The signature change supports custom rendering while maintaining backward compatibility.
34-34: LGTM! Disabled state logic is clear and consistent.The derived state correctly prevents actions for oversized or streaming responses.
41-52: LGTM! Keyboard handling enhances accessibility.The function correctly guards against disabled state and supports standard button activation keys (Enter/Space).
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js (1)
171-171: LGTM — Whitespace-only adjustments.These lines contain minor formatting tweaks with no functional impact. The file correctly uses theme props for colors as per coding standards.
Also applies to: 281-281
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
73-112: Good refactor using React state for expansion.The
XmlArrayNodecomponent properly usesuseStatefor expansion control instead of direct DOM manipulation. This addresses the previous review concern.packages/bruno-app/src/utils/response/index.js (4)
8-59: Clean MIME-based format detection.The rule-based approach with regex tests is maintainable and extensible. Good coverage of common content types with sensible defaults.
62-70: LGTM — Standard HTML escaping.Correctly escapes all necessary HTML special characters for safe webview content injection.
75-93: Division guard added — looks good.The empty buffer check at line 76 properly addresses the potential division-by-zero issue.
216-223: MP4/WebM detection may have edge cases.The MP4 detection checks for
ftypbox with specific size bytes (0x18 or 0x20), which works for common MP4s but may miss variants with different box sizes. Similarly, the EBML signature (0x1A45DFA3) matches both WebM and MKV containers.For this use case (preview type selection), these edge cases are acceptable — just noting for awareness.
packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
36-41: LGTM — Separator styling added correctly.Uses theme prop for the border color as per coding standards. Layout properties appropriately defined in styled component.
packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js (2)
4-4: Component import updated correctly.Clean swap from
QueryResulttoQueryResponsealigning with the PR's refactoring.
42-52: Remove the unusedwidthprop if not referenced inQueryResponse.The
width={rightPaneWidth}prop passed on line 45 should be verified in theQueryResponsecomponent definition. If the component doesn't destructure or use this prop, remove it to keep the interface clean and reduce unnecessary prop passing.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js (1)
44-60: LGTM!Clean ReactJson configuration with appropriate theme selection and styling. The collapsed depth of 1 is sensible for large responses.
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
51-70: LGTM!The
useResponseLayoutTogglehook cleanly encapsulates orientation state and toggle logic, making it reusable across components.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (2)
64-80: LGTM!Clean separation of editor vs preview rendering. The CodeEditor receives all necessary props for proper functionality.
109-119: LGTM!New preview modes for JSON, text, and XML are well-integrated with their respective components.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
13-31: LGTM!Clean structure for
PREVIEW_FORMAT_OPTIONSwith appropriate CodeMirror mode mappings. The grouped structure supports future UI enhancements like optgroup rendering.
107-130: LGTM!Component signature is clear with well-documented props. Buffer conversion error handling is appropriately defensive.
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (4)
1-11: LGTM!Imports are well-organized and follow the coding guidelines. The component structure with hooks and styled components is appropriate for this use case.
126-173: Potential click handler conflict in dropdown items.Each dropdown item has
onClick={closeDropdown}on the wrapper div, but the action components (ResponseCopy, ResponseDownload, ResponseClear, ResponseLayoutToggle) also have their own click handlers. This could lead to:
- Race conditions in event handling
- Both handlers firing if not properly managed
- Unexpected behavior if the action component's handler prevents event propagation
Verify that the action components properly handle being wrapped and that both click handlers execute as intended.
Consider explicitly passing an
onCompletecallback to action components instead:- <ResponseCopy item={item}> - <div className="dropdown-item" onClick={closeDropdown}> + <ResponseCopy item={item} onComplete={closeDropdown}> + <div className="dropdown-item">
176-182: LGTM!The individual action buttons rendering is clean and straightforward. The
gap-[2px]spacing is consistent with the UI design.
91-104: Reviewitemdependency for stability—consider depending on a stable identifier instead.The
useEffectincludesitemin its dependency array, which causes the ResizeObserver setup/teardown to re-run whenever theitemobject reference changes. Ifitemis recreated on each render in the parent component, this will trigger unnecessary effect re-runs, setup, and cleanup operations. Consider whether depending on a stable primitive (e.g.,item.id) would be sufficient instead of the entire object, or memoizeitemin the parent component to prevent reference instability.packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
1-24: LGTM! Styling follows guidelines correctly.The styled wrapper properly uses theme props for all colors and follows the project's styling conventions.
packages/bruno-app/src/ui/ButtonDropdown/index.jsx (2)
74-142: Render logic is well-structured.The component correctly handles both grouped and flat options, maintains proper state, and propagates test IDs appropriately. The implementation is clean and follows React best practices.
48-61: Verify fallback when no option matches.If
valuedoesn't match any option andlabelprop is undefined, this function returnsundefined, which could display as "undefined" in the UI. Confirm whether thelabelprop has a default value or type guard, and check actual component usage to determine if this scenario occurs in practice.tests/utils/page/locators.ts (1)
99-100: Import locators for file input and env option look appropriate.The new
fileInputandenvOptionlocators underimportare consistent with existing patterns and should make the import flow tests clearer and less brittle.tests/utils/page/actions.ts (2)
516-527:sendRequestAndWaitForResponsehelper is clear and matches test usage.The helper wraps send + status-code assertion with configurable options and a sensible default timeout, which keeps the new response-format tests concise.
612-616: Exporting the new helpers keeps response-format tests DRY.Adding
sendRequestAndWaitForResponse,switchResponseFormat,switchToPreviewTab, andswitchToEditorTabto the exports makes them reusable across specs and matches how the new response-format tests consume them.tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (1)
1-161: Well-structured E2E coverage for response format & preview flows.The spec:
- Uses shared helpers (
openRequest,sendRequestAndWaitForResponse, format/preview switches) andbuildCommonLocatorsto keep selectors centralized.- Wraps logical chunks in
test.step, uses locator variables, and avoidswaitForTimeout/page.pause.- Adds comprehensive assertions for default views plus JSON/HTML/XML/JavaScript/Raw/Hex/Base64 editor and preview behaviour.
Once the
switchToEditorTabhelper is fixed, these scenarios should give solid end-to-end coverage for the new feature.Based on learnings, this satisfies the “add tests for new functionality” expectation.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
tests/utils/page/actions.ts (1)
555-566: Copy-paste bug:switchToEditorTabis identical toswitchToPreviewTab.Both functions click
preview-response-tab. This was flagged in a previous review but remains unfixed. The editor function should target a different test ID or toggle off the preview if that's the intended behavior.const switchToEditorTab = async (page: Page) => { await test.step('Switch to editor tab', async () => { const responseFormatTab = page.getByTestId('format-response-tab'); await responseFormatTab.click(); - const previewTab = page.getByTestId('preview-response-tab'); - await previewTab.click(); + const editorTab = page.getByTestId('editor-response-tab'); + await editorTab.click(); }); };Note: Verify the correct test ID matches your implementation (e.g.,
editor-response-tabor similar).packages/bruno-app/src/ui/ButtonDropdown/index.jsx (1)
2-5: Use sharedIconCaretDowncomponent instead of Tabler iconThe codebase already exposes a shared
IconCaretDownatcomponents/Icons/IconCaretDown; using it keeps icon styling and behavior consistent across the app.-import { IconCaretDown } from '@tabler/icons'; +import IconCaretDown from 'components/Icons/IconCaretDown';
🧹 Nitpick comments (3)
tests/utils/page/actions.ts (2)
472-478: Remove commented code.The old implementation is preserved in version control; keeping it commented adds clutter.
-// const openRequest = async (page: Page, requestName: string) => { -// await test.step(`Open request "${requestName}"`, async () => { -// const locators = buildCommonLocators(page); -// await locators.sidebar.request(requestName).click(); -// await expect(locators.tabs.activeRequestTab()).toContainText(requestName); -// }); -// }; -
516-527: Consider consolidating with existingsendRequestfunction.
sendRequestAndWaitForResponseduplicates much of the logic fromsendRequest(lines 448-464). Consider merging them or deprecating one to reduce maintenance burden.packages/bruno-app/src/ui/ButtonDropdown/index.jsx (1)
45-61: Defensively handle non-array or missingoptionsto avoid runtime errorsBoth
findSelectedLabelandrenderOptionsassumeoptionsis an array in the flat (non-grouped) case. Ifoptionsis everundefinedor a non-array, calling.find/.mapwill throw. Since this is a generic UI building block, it’s worth hardening:- const isGrouped = Array.isArray(options) && options.length > 0 && 'options' in options[0]; + const isGrouped = Array.isArray(options) && options.length > 0 && 'options' in options[0]; // Find the selected option's label const findSelectedLabel = () => { if (isGrouped) { - const groups = options; + const groups = Array.isArray(options) ? options : []; for (const group of groups) { const option = group.options.find((opt) => opt.value === value); if (option) return option.label; } } else { - const flatOptions = options; + const flatOptions = Array.isArray(options) ? options : []; const option = flatOptions.find((opt) => opt.value === value); if (option) return option.label; } return label; };const renderOptions = () => { if (isGrouped) { - const groups = options; + const groups = Array.isArray(options) ? options : []; return groups.map((group, groupIndex) => ( @@ } else { - const flatOptions = options; + const flatOptions = Array.isArray(options) ? options : []; return flatOptions.map((option) => (Also applies to: 75-121
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js(1 hunks)packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js(1 hunks)packages/bruno-app/src/ui/ButtonDropdown/index.jsx(1 hunks)tests/response/response-actions.spec.ts(1 hunks)tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts(1 hunks)tests/utils/page/actions.ts(3 hunks)tests/utils/page/locators.ts(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
- tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts
- tests/utils/page/locators.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/components/Sidebar/Collections/Collection/index.jstests/response/response-actions.spec.tstests/utils/page/actions.tspackages/bruno-app/src/ui/ButtonDropdown/index.jsx
tests/**/**.*
⚙️ CodeRabbit configuration file
tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:
Follow best practices for Playwright code and e2e automation
Try to reduce usage of
page.waitForTimeout();in code unless absolutely necessary and the locator cannot be found using existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture Example*: Here's an example of possible fixture and test pair
. ├── fixtures │ └── collection │ ├── base.bru │ ├── bruno.json │ ├── collection.bru │ ├── ws-test-request-with-headers.bru │ ├── ws-test-request-with-subproto.bru │ └── ws-test-request.bru ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection ├── headers.spec.ts ├── persistence.spec.ts ├── variable-interpolation │ ├── fixtures │ │ └── collection │ │ ├── environments │ │ ├── bruno.json │ │ └── ws-interpolation-test.bru │ ├── init-user-data │ └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection └── subproto.spec.ts
Files:
tests/response/response-actions.spec.tstests/utils/page/actions.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{jsx,tsx}: Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Files:
packages/bruno-app/src/ui/ButtonDropdown/index.jsx
🧠 Learnings (1)
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.test.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created
Applied to files:
tests/response/response-actions.spec.ts
🧬 Code graph analysis (1)
packages/bruno-app/src/ui/ButtonDropdown/index.jsx (3)
packages/bruno-app/src/components/Icons/IconCaretDown/index.js (1)
IconCaretDown(3-16)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
StyledWrapper(3-22)packages/bruno-app/src/components/Dropdown/index.js (1)
Dropdown(5-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Playwright E2E Tests
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - macOS
- GitHub Check: Unit Tests
- GitHub Check: CLI Tests
- GitHub Check: SSL Tests - Linux
🔇 Additional comments (6)
packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js (1)
286-297: Test id addition is clean and consistentAdding
data-testid="sidebar-collection-row"on the main collection row div is consistent with existingdata-testidusage in this component and improves selector stability for tests without impacting runtime behavior. Looks good as-is.tests/response/response-actions.spec.ts (1)
21-21: LGTM – URL change to internal test endpoint.Switching to the internal testbench endpoint provides better control and reliability for E2E tests.
tests/utils/page/actions.ts (4)
534-540: LGTM – clear and straightforward format switching.
546-553: LGTM – preview tab switching looks correct.
612-616: Export list properly updated.All new helper functions are correctly exported.
486-494: Verify all call sites updated for newopenRequestsignature.The function signature changed from
openRequest(page, requestName)toopenRequest(page, collectionName, requestName). Ensure all call sites throughout the test suite have been updated to pass the collection name as the second parameter.
There was a problem hiding this comment.
Actionable comments posted: 1
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/ResponsePane/QueryResult/index.js (1)
151-159: Unused dependencyresponseEncodingin useMemo.
responseEncodingis included in the dependency array but isn't used within the memo's computation. Remove it to avoid unnecessary recalculations.- [data, dataBuffer, responseEncoding, selectedFormat, filter, isLargeResponse, showLargeResponse] + [data, dataBuffer, selectedFormat, filter, isLargeResponse, showLargeResponse]
♻️ Duplicate comments (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
118-124: Third instance of buffer conversion logic.This is the same try/catch Buffer.from pattern used in both hooks above. After extracting to a helper, use it here as well.
🧹 Nitpick comments (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (2)
13-31: Consider removing commented-outnameproperties.The commented
// name: 'Structured'and// name: 'Raw'lines appear to be unused. If these aren't planned for future use, remove them to keep the code clean.
49-55: Duplicate buffer conversion logic.The Buffer.from conversion with try/catch is duplicated in both
useInitialResponseFormatanduseResponsePreviewFormatOptions. Extract this to a shared helper function:const safeBufferFromBase64 = (dataBuffer) => { try { return dataBuffer ? Buffer.from(dataBuffer, 'base64') : null; } catch (error) { console.error('Error converting dataBuffer to Buffer:', error); return null; } };Also applies to: 73-79
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/ui/ButtonDropdown/index.jsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/bruno-app/src/ui/ButtonDropdown/index.jsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js
🧬 Code graph analysis (1)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (4)
packages/bruno-app/src/utils/common/index.js (5)
dataBuffer(280-280)dataBuffer(384-384)contentType(93-93)getContentType(83-119)getContentType(83-119)packages/bruno-app/src/utils/response/index.js (4)
detectContentTypeFromBuffer(178-260)detectContentTypeFromBuffer(178-260)getDefaultResponseFormat(8-59)getDefaultResponseFormat(8-59)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
QueryResultPreview(20-133)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultFilter/index.js (1)
QueryResultFilter(7-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: CLI Tests
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - macOS
- GitHub Check: Playwright E2E Tests
- GitHub Check: Unit Tests
🔇 Additional comments (4)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (4)
1-11: LGTM!Imports are well-organized and appropriate for the component's needs.
165-185: LGTM!The
previewModememo correctly derives the preview type fromselectedFormatanddetectedContentType, with proper fallback handling for all format cases.
187-191: LGTM!Clean approach using
flatMapto search across option groups. Fallback to'text/plain'is the correct standard MIME type.
223-237: LGTM!Props are correctly wired to
QueryResultPreview, and thedata-testidattribute enables proper E2E testing.
| // Wait until both content types are available | ||
| if (detectedContentType === null || contentType === undefined) { | ||
| return { initialFormat: null, initialTab: null }; | ||
| } |
There was a problem hiding this comment.
Early return may be overly restrictive.
The hook returns { initialFormat: null, initialTab: null } when detectedContentType === null, even if contentType is available from headers. This could cause format selection to fail for valid text-based responses where buffer detection returns null but header-based content-type is present.
Consider proceeding with contentType alone when buffer detection fails:
- if (detectedContentType === null || contentType === undefined) {
+ if (contentType === undefined) {
return { initialFormat: null, initialTab: null };
}🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ResponsePane/QueryResult/index.js around
lines 60-63, the hook currently returns early when detectedContentType === null
even if contentType (from headers) is present; update the condition so you only
early-return when both detection and header contentType are absent (e.g., when
detectedContentType is null/undefined AND contentType is null/undefined). This
lets the logic proceed using header-derived contentType when buffer detection
fails, while preserving the early return when no content type information exists
at all.
5664e1e to
fd05725
Compare
…proved format handling and error display
…t version in JSON fixture
…e for improved user interaction
…unctionality; replace QueryResult with QueryResponse, enhance layout handling, and streamline response actions
…ponents for improved testability
…nse action handling in tests
854826d to
1db2ef6
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (11)
packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
24-43: Avoid nested interactive elements by removing the inner<button>The default branch still renders a
<button>inside a focusabledivthat acts as a button (tabIndex={0},onClick,onKeyDown), which creates nested interactive controls and redundant tab stops. Flatten this by making the outer wrapper the sole interactive element and rendering only the icon inside.Apply a minimal diff like this:
return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown} data-testid="response-clear-button"> - {children ? children : ( - <StyledWrapper className="flex items-center"> - <button className="p-1"> - <IconEraser size={16} strokeWidth={2} /> - </button> - </StyledWrapper> - )} - </div> + <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown} data-testid="response-clear-button"> + {children ? children : ( + <StyledWrapper className="flex items-center p-1"> + <IconEraser size={16} strokeWidth={2} /> + </StyledWrapper> + )} + </div> );This keeps the current API/behavior but removes the inner
<button>so there is only one interactive element.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
342-374: Fix case-sensitivity when detecting repeated child elements in xmlToObjectRepeated children are currently detected with:
if ( childValue !== null || elementChildren.filter((c) => c.nodeName.toLowerCase() === childName).length > 1 ) {Here
childNamepreserves the original casing (e.g.'Item'), so the comparison againstc.nodeName.toLowerCase()fails for mixed‑case tags. For empty repeated elements, this can drop all instances from the parsed object.Consider normalizing both sides and avoiding repeated filters:
- if (elementChildren.length > 0) { - const childMap = {}; - elementChildren.forEach((child) => { - const childName = child.nodeName; // Preserve original casing - const childValue = xmlToObject(child); - - if (childValue !== null || elementChildren.filter((c) => c.nodeName.toLowerCase() === childName).length > 1) { + if (elementChildren.length > 0) { + const childMap = {}; + const nameCounts = elementChildren.reduce((acc, c) => { + const key = c.nodeName.toLowerCase(); + acc[key] = (acc[key] || 0) + 1; + return acc; + }, {}); + + elementChildren.forEach((child) => { + const childName = child.nodeName; // preserve original casing for keys + const childValue = xmlToObject(child); + const sameNameCount = nameCounts[childName.toLowerCase()] || 0; + + if (childValue !== null || sameNameCount > 1) { if (childMap[childName]) { // Multiple children with same name - convert to array if (!Array.isArray(childMap[childName])) { childMap[childName] = [childMap[childName]]; } childMap[childName].push(childValue); } else { childMap[childName] = childValue; } } });This keeps the exposed keys case‑preserving while making the repeated‑child detection case‑insensitive and avoids O(n²) filtering.
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
141-164: Fix nested interactive elements and always expose button semantics on the wrapperThe current markup:
<div role={!!children ? 'button' : undefined} tabIndex={isDisabled ? -1 : 0} aria-disabled={isDisabled} onKeyDown={handleKeyDown} onClick={handleSaveClick} // ... > {children ?? ( <StyledWrapper className="flex items-center"> <button className="p-1"> <IconBookmark size={16} strokeWidth={2} /> </button> </StyledWrapper> )} </div>has two problems:
- Nested interactive elements – a focusable
<div>wrapping a real<button>creates duplicate focus targets and confusing screen‑reader semantics.- Inconsistent role – when
childrenis not provided, the outerdivis focusable (tabIndex=0) but has norole, so assistive tech won’t identify it as a button.You can fix both by making the wrapper the only interactive element and rendering a non‑interactive icon inside:
- <div - role={!!children ? 'button' : undefined} + <div + role="button" tabIndex={isDisabled ? -1 : 0} aria-disabled={isDisabled} onKeyDown={handleKeyDown} onClick={handleSaveClick} @@ - {children ?? ( - <StyledWrapper className="flex items-center"> - <button className="p-1"> - <IconBookmark size={16} strokeWidth={2} /> - </button> - </StyledWrapper> - )} + {children ?? ( + <StyledWrapper className="flex items-center"> + <span className="p-1" aria-hidden="true"> + <IconBookmark size={16} strokeWidth={2} /> + </span> + </StyledWrapper> + )} </div>This keeps click/keyboard handling on the outer wrapper and avoids nested interactive controls.
packages/bruno-app/src/utils/response/index.js (1)
155-171: Dead code: HTML parser never generatesparsererror.The
parsererrorcheck at lines 161-164 is ineffective becauseDOMParserwith'text/html'uses a lenient parser that never producesparsererrorelements. This was flagged in a previous review. Consider removing the try-catch and parsererror check, or switch to'application/xml'if strict validation is required.packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
53-67: Nested interactive elements accessibility issue persists.The outer
div(line 54) withrole,tabIndex,onClick, andonKeyDownwraps the inner<button>(line 57) when no children are provided. This creates nested interactive elements - an accessibility anti-pattern that was flagged in a previous review.When rendering the default UI (no children), the wrapper should not be interactive; only the inner
<button>should handle interactions. When custom children are provided, the wrapper can be interactive.Apply the fix from the previous review:
+ const isCustomRender = !!children; + return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={handleClick} title={!children ? 'Copy response to clipboard' : null} onKeyDown={handleKeyDown} data-testid="response-copy-btn"> + <div + role={isCustomRender ? "button" : undefined} + tabIndex={isCustomRender ? 0 : undefined} + onClick={isCustomRender ? handleClick : undefined} + onKeyDown={isCustomRender ? handleKeyDown : undefined} + title={isCustomRender ? 'Copy response to clipboard' : undefined} + data-testid="response-copy-btn" + > - {children ? children : ( + {isCustomRender ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1" disabled={!hasData}> + <button + className="p-1" + disabled={!hasData} + onClick={handleClick} + onKeyDown={handleKeyDown} + title="Copy response to clipboard" + > {copied ? ( <IconCheck size={16} strokeWidth={2} /> ) : ( <IconCopy size={16} strokeWidth={2} /> )} </button> </StyledWrapper> )} </div> );packages/bruno-app/src/ui/ButtonDropdown/index.jsx (1)
2-2: Use customIconCaretDownfor codebase consistency.The codebase has a custom
IconCaretDowncomponent atcomponents/Icons/IconCaretDown/index.js. Using it ensures visual consistency.-import { IconCaretDown } from '@tabler/icons'; +import IconCaretDown from 'components/Icons/IconCaretDown';Note: You may need to adjust props since the custom icon has different defaults (e.g.,
sizevswidth/height).packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
46-68: Early return may be too restrictive.Per past review: when
detectedContentType === null, the hook returns nulls even ifcontentTypefrom headers is available. Consider proceeding with header-basedcontentTypealone when buffer detection fails.- if (detectedContentType === null || contentType === undefined) { + if (contentType === undefined) { return { initialFormat: null, initialTab: null }; }tests/utils/page/locators.ts (1)
82-82: Redundantbodylocator.
body(line 82) uses the identical selector.response-paneaspane(line 80). Either removebodyor update its selector if targeting a different element.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
87-88: Addaltattribute to image for accessibility.The
<img>element is missing analtattribute, which is required for accessibility compliance.- return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} />; + return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} alt="Response preview" />;tests/utils/page/actions.ts (2)
617-628: Copy-paste bug:switchToEditorTabis identical toswitchToPreviewTab.Both functions click the same
preview-response-tabtoggle. This is incorrect -switchToEditorTabshould switch away from preview mode, not enable it.const switchToEditorTab = async (page: Page) => { await test.step('Switch to editor tab', async () => { const responseFormatTab = page.getByTestId('format-response-tab'); await responseFormatTab.click(); - const previewTab = page.getByTestId('preview-response-tab'); - await previewTab.click(); + const editorTab = page.getByTestId('editor-response-tab'); + await editorTab.click(); }); };Verify the correct test ID for the editor tab in the app implementation.
654-664: Add JSDoc and improve timing reliability.Missing JSDoc per coding guidelines. Also, after clicking the menu, there's no wait for the dropdown to appear before clicking the action button.
+/** + * Click a response action button + * @param page - The page object + * @param actionTestId - The test ID of the action button to click + */ const clickResponseAction = async (page: Page, actionTestId: string) => { - const actionButton = await page.getByTestId(actionTestId); + const actionButton = page.getByTestId(actionTestId); if (await actionButton.isVisible()) { await actionButton.click(); } else { - const menu = await page.getByTestId('response-actions-menu'); + const menu = page.getByTestId('response-actions-menu'); await menu.click(); + await actionButton.waitFor({ state: 'visible' }); await actionButton.click(); } };Note:
getByTestIdreturns a locator synchronously; noawaitneeded.
🧹 Nitpick comments (7)
packages/bruno-app/src/utils/common/index.js (2)
273-423: New formatResponse branches cover the extra modes with sensible fallbacksThe added branches for
html,javascript,hex,base64, andtext/rawmodes look logically sound:
- Large responses short‑circuit to inexpensive stringification instead of heavy formatting.
- HTML/JS modes use
rawDataand delegate toprettifyHtmlString/prettifyJavaScriptStringwith try/catch fallbacks.- Hex mode first returns existing hex‑like strings as‑is, otherwise formats a hex dump from the base64 buffer.
- Base64 just returns
dataBufferString, which is exactly what a “base64 view” would expect.- Text/raw returns
rawDatafor normal-size responses and reasonable stringified fallbacks for very large ones.One thing to keep an eye on is the top guard:
if (data === undefined || !dataBufferString || !mode) { return ''; }If you ever introduce modes that intentionally work with an undefined
databut a validdataBufferString(e.g., strictly binary responses), this early return will bypass thehex/base64branches. Today that may not happen, but it’s worth verifying the call sites once you start wiring more binary formats through.
466-558: HTML/JS prettifiers and hex helpers are mostly solid; two small points
HTML detection is very strict
isValidHtmlcurrently checks only for an<html>tag:export const isValidHtml = (str) => { if (typeof str !== 'string' || !str.trim()) return false; return /<\s*html[\s>]/i.test(str); };That means HTML fragments like
<div>ok</div>will be treated as “not HTML”. If callers intend to treat fragments as HTML (which is common for API responses), consider relaxing this to a broader structural check (e.g., presence of any tag) or document that “valid HTML” here specifically means a full document.Hex detection heuristics are conservative (which is good)
isHexFormat’s combination of:
- hex-dump pattern detection, and
- plain-hex detection with min length, even length, and numeric‑digit requirement
is a nice compromise to avoid misclassifying short words. No issues, just calling out that this is deliberately strict, which is appropriate for a “don’t auto-guess too aggressively” preview.
If you want, I can draft an
isValidHtmlvariant that better matches the actual HtmlPreview use‑cases.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
114-222: XmlNode rendering is generally solid; consider removing unused array bookkeeping
XmlNodealready special‑cases arrays at the top:if (Array.isArray(node)) { // ... return (<>{node.map(...</>); }Later you compute:
const hasArrayValue = Array.isArray(node); const arrayLength = hasArrayValue ? node.length : 0;but never actually use
hasArrayValueorarrayLength. They can be safely removed to reduce noise:- // Determine if this node's value is an array - const hasArrayValue = Array.isArray(node); - const arrayLength = hasArrayValue ? node.length : 0; - return (Everything else in XmlNode/XmlArrayNode (expansion state, padding by depth, array rendering) looks good.
packages/bruno-app/src/ui/ButtonDropdown/index.jsx (1)
19-19: Remove redundantrole="button"attribute.Native
<button>elements already have an implicit button role. The attribute is unnecessary.disabled={disabled} data-testid={props['data-testid']} style={style} - role="button" {...props}packages/bruno-app/src/components/ResponsePane/index.js (1)
34-46: Duplicate state management pattern withQueryResponse.This initialization pattern (hooks + state + effect) is duplicated in
QueryResponse/index.js. Consider extracting a custom hook if both need to remain separate, or ensure only one component manages this state to avoid drift.If intentional (different contexts), a shared custom hook would reduce duplication:
// useResponseFormatState.js export const useResponseFormatState = (dataBuffer, headers) => { const { initialFormat, initialTab } = useInitialResponseFormat(dataBuffer, headers); const previewFormatOptions = useResponsePreviewFormatOptions(dataBuffer, headers); const [selectedFormat, setSelectedFormat] = useState('raw'); const [selectedTab, setSelectedTab] = useState('editor'); useEffect(() => { if (initialFormat !== null && initialTab !== null) { setSelectedFormat(initialFormat); setSelectedTab(initialTab); } }, [initialFormat, initialTab]); return { selectedFormat, setSelectedFormat, selectedTab, setSelectedTab, previewFormatOptions }; };packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
1-1: UnuseduseMemoimport.
useMemois imported but never used in this component. Remove it to keep imports clean.-import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react';packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
49-55: Duplicate buffer conversion logic.Buffer conversion with identical try-catch appears in three places:
useInitialResponseFormat(lines 49-55),useResponsePreviewFormatOptions(lines 73-79), andQueryResult(lines 118-124). Extract to a shared utility.// In a utils file const safeBufferFromBase64 = (dataBuffer) => { try { return dataBuffer ? Buffer.from(dataBuffer, 'base64') : null; } catch (error) { console.error('Error converting dataBuffer to Buffer:', error); return null; } };Also applies to: 73-79, 118-124
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (66)
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js(2 hunks)packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.spec.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/index.js(5 hunks)packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js(2 hunks)packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js(1 hunks)packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js(1 hunks)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ErrorAlert/index.js(1 hunks)packages/bruno-app/src/utils/common/codemirror.js(1 hunks)packages/bruno-app/src/utils/common/index.js(4 hunks)packages/bruno-app/src/utils/response/index.js(1 hunks)tests/response-examples/create-example.spec.ts(6 hunks)tests/response-examples/edit-example.spec.ts(6 hunks)tests/response-examples/menu-operations.spec.ts(4 hunks)tests/response/large-response-crash-prevention.spec.ts(1 hunks)tests/response/response-actions.spec.ts(2 hunks)tests/response/response-format-select-and-preview/fixtures/collection/bruno.json(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru(1 hunks)tests/response/response-format-select-and-preview/init-user-data/collection-security.json(1 hunks)tests/response/response-format-select-and-preview/init-user-data/preferences.json(1 hunks)tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts(1 hunks)tests/utils/page/actions.ts(4 hunks)tests/utils/page/locators.ts(3 hunks)
💤 Files with no reviewable changes (3)
- packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
✅ Files skipped from review due to trivial changes (3)
- packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js
🚧 Files skipped from review as they are similar to previous changes (30)
- packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js
- packages/bruno-app/src/utils/common/codemirror.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js
- tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru
- packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js
- tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js
- packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js
- packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/StatusCode/index.js
- tests/response-examples/edit-example.spec.ts
- packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js
- packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
- tests/response-examples/create-example.spec.ts
- packages/bruno-app/src/ui/ErrorAlert/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js
- tests/response/large-response-crash-prevention.spec.ts
- tests/response/response-format-select-and-preview/init-user-data/collection-security.json
- packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.spec.jspackages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jstests/response-examples/menu-operations.spec.tspackages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.jspackages/bruno-app/src/components/ResponsePane/QueryResponse/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ClearTimeline/index.jspackages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.jspackages/bruno-app/src/components/ResponsePane/index.jstests/utils/page/actions.tspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.jspackages/bruno-app/src/components/Sidebar/Collections/Collection/index.jspackages/bruno-app/src/utils/common/index.jspackages/bruno-app/src/utils/response/index.jstests/response/response-actions.spec.tspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseActions/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/index.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.jstests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/ui/ButtonDropdown/index.jsxpackages/bruno-app/src/components/ResponsePane/ResponseClear/index.jstests/utils/page/locators.tspackages/bruno-app/src/components/ResponsePane/StyledWrapper.js
tests/**/**.*
⚙️ CodeRabbit configuration file
tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:
Follow best practices for Playwright code and e2e automation
Try to reduce usage of
page.waitForTimeout();in code unless absolutely necessary and the locator cannot be found using existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture Example*: Here's an example of possible fixture and test pair
. ├── fixtures │ └── collection │ ├── base.bru │ ├── bruno.json │ ├── collection.bru │ ├── ws-test-request-with-headers.bru │ ├── ws-test-request-with-subproto.bru │ └── ws-test-request.bru ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection ├── headers.spec.ts ├── persistence.spec.ts ├── variable-interpolation │ ├── fixtures │ │ └── collection │ │ ├── environments │ │ ├── bruno.json │ │ └── ws-interpolation-test.bru │ ├── init-user-data │ └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection └── subproto.spec.ts
Files:
tests/response/response-format-select-and-preview/fixtures/collection/bruno.jsontests/response-examples/menu-operations.spec.tstests/utils/page/actions.tstests/response/response-format-select-and-preview/init-user-data/preferences.jsontests/response/response-actions.spec.tstests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tstests/utils/page/locators.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{jsx,tsx}: Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Files:
packages/bruno-app/src/ui/ButtonDropdown/index.jsx
🧠 Learnings (7)
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Applied to files:
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Applied to files:
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Applied to files:
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ClearTimeline/index.jspackages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.test.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created
Applied to files:
tests/response-examples/menu-operations.spec.tstests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts
📚 Learning: 2025-12-02T09:45:31.709Z
Learnt from: sid-bruno
Repo: usebruno/bruno PR: 6266
File: packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js:38-38
Timestamp: 2025-12-02T09:45:31.709Z
Learning: In the ResponseCopy component (packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js), the copy button is intentionally disabled using `!response.data` to prevent copying stream resets which result in empty strings.
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.jspackages/bruno-app/src/components/ResponsePane/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/index.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Add in JSDoc comments to add more details to the abstractions if needed
Applied to files:
packages/bruno-app/src/utils/common/index.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : No trailing commas
Applied to files:
tests/utils/page/locators.ts
🧬 Code graph analysis (12)
packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (3)
packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
StyledWrapper(3-13)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)
tests/response-examples/menu-operations.spec.ts (1)
tests/utils/page/actions.ts (1)
clickResponseAction(692-692)
packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
QueryResponse(8-58)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (1)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/StyledWrapper.js (1)
StyledWrapper(3-6)
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js (1)
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
QueryResponse(8-58)
packages/bruno-app/src/components/ResponsePane/index.js (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (4)
useInitialResponseFormat(47-68)useInitialResponseFormat(47-68)useResponsePreviewFormatOptions(71-105)useResponsePreviewFormatOptions(71-105)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx (1)
QueryResultTypeSelector(7-44)
packages/bruno-app/src/utils/common/index.js (1)
packages/bruno-js/src/bru.js (1)
xmlFormat(2-2)
packages/bruno-app/src/utils/response/index.js (2)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
contentType(126-126)packages/bruno-app/src/utils/common/index.js (3)
contentType(93-93)i(21-21)i(515-515)
tests/response/response-actions.spec.ts (1)
tests/utils/page/actions.ts (1)
clickResponseAction(692-692)
packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js (1)
packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (1)
ResponseDownload(8-62)
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (2)
tests/utils/page/actions.ts (6)
closeAllCollections(667-667)openRequest(684-684)sendRequestAndWaitForResponse(688-688)switchToPreviewTab(690-690)switchToEditorTab(691-691)switchResponseFormat(689-689)tests/utils/page/locators.ts (1)
buildCommonLocators(3-102)
packages/bruno-app/src/ui/ButtonDropdown/index.jsx (3)
packages/bruno-app/src/components/Icons/IconCaretDown/index.js (1)
IconCaretDown(3-16)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
StyledWrapper(3-22)packages/bruno-app/src/components/Dropdown/index.js (1)
Dropdown(5-25)
🔇 Additional comments (51)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (1)
18-18: LGTM! Intentional margin removal as part of UI refactor.The removal of
ml-2aligns with the broader spacing adjustments across the response pane components in this PR. The component follows coding standards and maintains correct functionality.packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js (1)
171-171: LGTM – Whitespace changes align with styling cleanup.The whitespace adjustments on these lines are harmless. Per the broader context, the removal of presentational styling from this wrapper aligns well with the shift toward modular preview components (HtmlPreview, JsonPreview, etc.), consistent with the learnings on separating Styled Components from layout concerns.
Optional: Consider visual regression testing to confirm that styling removals don't introduce unexpected UI changes in the Console RequestDetailsPanel.
Also applies to: 281-281
packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js (1)
286-297: Test id addition is clear and non-invasive
data-testid="sidebar-collection-row"on the main row div is a good, descriptive hook for tests and does not alter runtime behavior. Looks good as-is.packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
37-41: Separator styling aligns well with theme usageThe new
.separatorrule is minimal, uses the theme for border color as expected, and fits cleanly into the existing StyledWrapper; no issues from a layout or theming perspective.packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
7-22: Hook extraction looks clean and idiomatic
useResponseClearneatly encapsulates the dispatch logic and keepsResponseClearlean. No issues from a hooks/Redux usage perspective.packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.spec.js (1)
83-107: Stable test locators for layout toggle look goodSwitching to
getByTestId('response-layout-toggle-button')keeps the tests robust against internal markup changes while still asserting the same UX (titles and state transitions). No issues from a Testing Library or behavior standpoint.Also applies to: 109-172
packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (1)
1-14: StyledWrapper is consistent with other response action wrappersTheme usage and hover styling are aligned with the existing ResponseCopy/ResponseClear/ResponseBookmark wrappers, so this is safe and consistent.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
1-13: Selector styling via theme tokens looks correctUsing
.activeand.preview-response-tab-labelscoped within the StyledWrapper and driven bytheme.colors.text.*is in line with the styling conventions and keeps colors centralized.tests/response/response-format-select-and-preview/init-user-data/preferences.json (1)
1-6: Init user-data JSON is well-scoped to the new fixtureThe preferences file correctly points
lastOpenedCollectionsat the newfixtures/collectionpath and won’t affect non-test flows.packages/bruno-app/src/utils/common/index.js (2)
83-119: JavaScript content-type detection is reasonableThe new
JAVASCRIPT_PATTERN(/^(application|text)\/(javascript|ecmascript)/i) and mapping to'application/javascript'cover the common JS MIME types (application/javascript,text/javascript,application/ecmascript,text/ecmascript) and fit the existing normalization pattern used for JSON/XML/SVG. No issues here.
7-8: Prettier integration pattern is current best practice; bundle size consideration is appropriateThe
prettier/standalone+parser-babelpattern with explicit plugin passing is the recommended approach for browser/Electron renderer environments. Standalone Prettier requires plugins to be passed explicitly in theformat()call options, which is correctly handled in the code.Note: For complete JavaScript/JSX support, ensure the
estreeplugin is also included alongsidebabelin the plugins array, as both are standard for the babel parser. The bundle size trade-off is reasonable given the existing 50MB threshold and conditional formatting logic.packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js (1)
27-40: Disabled and gating logic for streaming/large responses is aligned with copy behaviorThe
isResponseTooLarge/isStreamingResponsechecks, combined intoisDisabled, plus the early returns inhandleSaveClickmirror the pattern used for copy actions (disabling on streaming and very large responses). This is a good guardrail to avoid saving unusable examples or accidentally triggering large operations.packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js (1)
6-30: Verify ResponseDownload prop handling and dropdown close behaviorThe review raises a valid concern: if
ResponseDownloaddoesn't accept and invoke theonCloseprop, the dropdown menu won't close after downloading. Verify whether:
ResponseDownloadcurrently receives and handles theonCloseprop- The
Dropdowncomponent auto-closes on item click (confirming if manual close is needed)ResponseClearhandlesonClosefor comparisonIf these props are not wired through, implement the suggested change to ensure consistent UX where the menu closes after download completes.
packages/bruno-app/src/utils/response/index.js (5)
1-6: LGTM! Clean MIME type extraction.The function safely handles edge cases with String() coercion and proper parameter stripping.
8-59: LGTM! Comprehensive format mapping.The rules-based approach with sequential evaluation handles standard and custom MIME types (+json, +xml variants) effectively. The fallback ensures robustness.
61-70: LGTM! Standard HTML escaping.Type guard and proper entity escaping for webview safety.
75-93: LGTM! Text detection with proper safeguards.The empty buffer guard addresses the previous review concern. The 85% threshold and byte sampling provide reasonable text heuristics.
178-260: LGTM! Comprehensive content-type detection.Excellent coverage of common binary formats via magic numbers with proper fallback chain. The MP3 detection bitwise operation correctly matches the sync word pattern.
packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js (1)
1-1: LGTM! Clean migration to QueryResponse.The import and component usage updates align with the PR's modular response rendering architecture. The key prop provides stable React identity.
Also applies to: 17-25
tests/response/response-format-select-and-preview/fixtures/collection/bruno.json (1)
1-9: LGTM! Valid collection fixture.The collection metadata follows the expected structure for test fixtures. The version "1" format is correct for this schema.
tests/response-examples/menu-operations.spec.ts (1)
4-4: LGTM! Consistent action helper usage.Refactoring to use
clickResponseActionimproves test maintainability and provides consistent timeout/synchronization handling across bookmark actions.Also applies to: 21-21, 52-52, 85-85
packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js (1)
1-24: LGTM! Proper styled-components pattern.Theme prop usage for colors aligns with project conventions. Clean component styling with proper hover states and separation concerns.
Based on learnings, styled-components with theme props for colors is the correct pattern here.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
1-25: LGTM! Robust text preview with proper memoization.The displayData transformation handles edge cases (null, objects, JSON.stringify failures) gracefully. Direct
useMemoimport andmemowrapper provide proper optimization.packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (2)
40-45: LGTM! Proper keyboard handling with preventDefault.The Space key preventDefault call addresses the previous review concern and prevents unwanted page scrolling.
6-35: LGTM! Clean hook extraction.The
useResponseCopyhook properly encapsulates copy logic, state management, and data validation. The 2-second copied state timeout and error handling are well implemented.tests/response/response-actions.spec.ts (1)
3-3: LGTM! Consistent test helper adoption.Migration to
clickResponseActionaligns with the broader test refactoring pattern, improving consistency and maintainability across the test suite.Also applies to: 30-30
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js (2)
10-10: LGTM!Import correctly updated to use the new
QueryResponsecomponent which wrapsQueryResultwith format selection capabilities.
119-127: LGTM!Props forwarded to
QueryResponsematch the expected interface. ThedisableRunEventListener={true}is appropriate for the Devtools console context.tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (3)
12-16: Good use of serial execution and cleanup.Using
test.describe.serialensures tests run in order, and theafterAllcleanup properly closes collections.
29-33: Well-structured test steps with clear assertions.Good use of
test.stepfor readable test reports and multiple assertions per step.
22-27: Clean locator setup pattern.Locator variables are properly defined and reused throughout the test, following best practices.
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (3)
17-27: LGTM!State initialization and synchronization with initial format/tab is cleanly handled. The null checks before setting state prevent unnecessary re-renders.
31-41: LGTM!
QueryResultTypeSelectoris properly wired with format options and callbacks. The toggle logic forselectedTabis concise.
43-55: LGTM!Props correctly forwarded to
QueryResult. Conditional padding applied viaclassnamesbased on active tab.packages/bruno-app/src/ui/ButtonDropdown/index.jsx (3)
70-73: LGTM!Dropdown now properly closes after option selection. This was previously flagged and has been addressed.
7-28: LGTM!
ButtonIconcorrectly forwards ref, passesdisabledto the native button, and usesdisplayNamefor debugging. Clean implementation.
46-46: Guard against empty options array.If
optionsis an empty array, accessingoptions[0]returnsundefined, and'options' in undefinedthrows a TypeError.- const isGrouped = Array.isArray(options) && options.length > 0 && 'options' in options[0]; + const isGrouped = Array.isArray(options) && options.length > 0 && options[0] && 'options' in options[0];Likely an incorrect or invalid review comment.
packages/bruno-app/src/components/ResponsePane/index.js (4)
101-103: LGTM!
selectedFormatandselectedTabcorrectly passed toQueryResultfor format-aware rendering.
201-216: LGTM!
QueryResultTypeSelectorproperly integrated with callbacks to update format and toggle preview mode. Conditional rendering for the 'response' tab is correct.
217-223: LGTM!Status block cleanly displays response metadata with proper conditional rendering for streaming vs completed responses.
226-232: LGTM!Actions correctly switch between
ClearTimelinefor timeline tab andResponsePaneActionsfor response data. The null check onitem?.response?.errorprevents actions on errored responses.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (2)
64-80: LGTM!Clean separation of the editor tab rendering path. The CodeEditor receives all necessary props.
109-119: LGTM!New preview modes for JSON, text, and XML are well-structured and delegate to appropriate preview components.
tests/utils/page/actions.ts (3)
572-589: LGTM!Well-structured helper with proper JSDoc, configurable options, and sensible defaults.
591-602: LGTM!Clean implementation with proper
test.stepwrapper for readable reports.
548-556: Breaking change:openRequestsignature modified.The function signature changed from
(page, requestName)to(page, collectionName, requestName). Ensure all existing test callsites are updated to include thecollectionNameparameter when calling this function.packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (3)
165-185: LGTM!The
previewModememo has proper fallback handling and correct dependencies. Good fix from past review.
187-191: LGTM!Fallback mode corrected to
'text/plain'- standard MIME type.
223-237: LGTM!QueryResultPreview receives all necessary props with proper data flow.
tests/utils/page/locators.ts (2)
83-88: LGTM!New locators (
editorContainer,formatTab,formatTabDropdown,previewContainer,codeLine,jsonTreeLine) are well-structured with appropriate test IDs. No trailing comma on the last property - compliant with coding guidelines.
122-122: LGTM!Good migration to test-id-based locator for
clearResponse.
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts
Outdated
Show resolved
Hide resolved
| // const openRequest = async (page: Page, requestName: string) => { | ||
| // await test.step(`Open request "${requestName}"`, async () => { | ||
| // const locators = buildCommonLocators(page); | ||
| // await locators.sidebar.request(requestName).click(); | ||
| // await expect(locators.tabs.activeRequestTab()).toContainText(requestName); | ||
| // }); | ||
| // }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Remove commented-out code.
Dead code should be removed rather than commented out. Version control preserves history if needed.
-// const openRequest = async (page: Page, requestName: string) => {
-// await test.step(`Open request "${requestName}"`, async () => {
-// const locators = buildCommonLocators(page);
-// await locators.sidebar.request(requestName).click();
-// await expect(locators.tabs.activeRequestTab()).toContainText(requestName);
-// });
-// };
-📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // const openRequest = async (page: Page, requestName: string) => { | |
| // await test.step(`Open request "${requestName}"`, async () => { | |
| // const locators = buildCommonLocators(page); | |
| // await locators.sidebar.request(requestName).click(); | |
| // await expect(locators.tabs.activeRequestTab()).toContainText(requestName); | |
| // }); | |
| // }; |
🤖 Prompt for AI Agents
In tests/utils/page/actions.ts around lines 534–540 there is a commented-out
helper function block (openRequest) that is dead code; remove this entire
commented-out block from the file, run project lint/tests to ensure no
references break, and commit the change so version control retains history if
needed.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (9)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
54-66: Nested interactive elements accessibility issue persists.While
rolewas made conditional,tabIndex={0},onClick, andonKeyDownremain on the outer div regardless of whetherchildrenis provided. When children is falsy, this creates nested interactive elements (focusable outer div + focusable inner button), violating WCAG guidelines.Solution: Make the outer wrapper interactive only when
childrenexists:
- When
childrenis provided: outer div should haverole="button",tabIndex={0}, and handlers- When
childrenis falsy: outer div should be non-interactive (notabIndex, no handlers on div), and the inner<button>should receiveonClick/onKeyDownApply this refactor:
+ const isCustomRender = !!children; + return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={handleClick} title={!children ? 'Copy response to clipboard' : null} onKeyDown={handleKeyDown} data-testid="response-copy-btn"> + <div + role={isCustomRender ? 'button' : undefined} + tabIndex={isCustomRender ? 0 : undefined} + onClick={isCustomRender ? handleClick : undefined} + onKeyDown={isCustomRender ? handleKeyDown : undefined} + title={isCustomRender ? 'Copy response to clipboard' : undefined} + data-testid="response-copy-btn" + > - {children ? children : ( + {isCustomRender ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1" disabled={!hasData}> + <button + className="p-1" + disabled={!hasData} + onClick={handleClick} + onKeyDown={handleKeyDown} + title="Copy response to clipboard" + > {copied ? ( <IconCheck size={16} strokeWidth={2} /> ) : ( <IconCopy size={16} strokeWidth={2} /> )} </button> </StyledWrapper> )} </div> );packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
85-104: Nested interactive elements still present.The outer
<div>withrole="button"wraps an inner<button>at line 95, creating nested interactive elements. This was flagged in a previous review. Screen readers and keyboard navigation can behave unexpectedly with this structure.{children ? children : ( <StyledWrapper className="flex items-center w-full"> - <button className="p-1"> + <span className="p-1"> {orientation === 'horizontal' ? ( <IconDockToBottom /> ) : ( <IconDockToRight /> )} - </button> + </span> </StyledWrapper> )}packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (1)
61-62: Missing indentation inJSON.stringify.This was flagged in a previous review. Objects won't be prettified without the third argument.
} else if (typeof data === 'object') { - displayContent = JSON.stringify(data, null); + displayContent = JSON.stringify(data, null, 2); } else if (typeof data === 'string') {packages/bruno-app/src/utils/response/index.js (1)
155-170:parsererrorcheck is dead code for HTML parsing.
DOMParserwith'text/html'never produces aparsererrorelement - that's specific to XML parsing. The check is misleading.try { const parser = new DOMParser(); const doc = parser.parseFromString(trimmed, 'text/html'); - - // Check for parsing errors - const parseError = doc.querySelector('parsererror'); - if (parseError) { - return false; - } - - // HTML parser is lenient; if we reach here with valid tags, consider it valid return true; } catch (error) { return false; }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
342-360: Case-sensitivity bug in repeated child detection.The filter compares
c.nodeName.toLowerCase()withchildName(original casing). Tags like<Item>won't match because'item' === 'Item'is false.elementChildren.forEach((child) => { const childName = child.nodeName; // Preserve original casing const childValue = xmlToObject(child); - if (childValue !== null || elementChildren.filter((c) => c.nodeName.toLowerCase() === childName).length > 1) { + if (childValue !== null || elementChildren.filter((c) => c.nodeName.toLowerCase() === childName.toLowerCase()).length > 1) { if (childMap[childName]) {packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
87-89: Addaltattribute for accessibility.Images should have an
altattribute for screen readers.- return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} />; + return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} alt="Response preview" />;packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
60-63: Early return may discard valid header-based content type.When
detectedContentType === null(buffer detection fails) butcontentTypeis available from headers, this returns null. Consider proceeding with header-based content type alone.// Wait until both content types are available - if (detectedContentType === null || contentType === undefined) { + if (contentType === undefined) { return { initialFormat: null, initialTab: null }; }packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
35-43: Flatten interactive structure and always expose a single button control.The wrapper is focusable and clickable, but the default branch still renders a nested
<button>, which recreates the “nested interactive elements” issue previously flagged. Also, the wrapper only getsrole="button"whenchildrenare provided, so the default icon-only case is focusable but not semantically a button.Keep a single interactive element (the wrapper) and remove the inner
<button>, while always giving the wrapperrole="button":- return ( - <div role={!!children ? 'button' : undefined} tabIndex={0} onClick={clearResponse} title={!children ? 'Clear response' : null} onKeyDown={handleKeyDown} data-testid="response-clear-button"> - {children ? children : ( - <StyledWrapper className="flex items-center"> - <button className="p-1"> - <IconEraser size={16} strokeWidth={2} /> - </button> - </StyledWrapper> - )} - </div> - ); + return ( + <div + role="button" + tabIndex={0} + onClick={clearResponse} + title={!children ? 'Clear response' : null} + onKeyDown={handleKeyDown} + data-testid="response-clear-button" + > + {children ? children : ( + <StyledWrapper className="flex items-center p-1"> + <IconEraser size={16} strokeWidth={2} /> + </StyledWrapper> + )} + </div> + );This removes nested controls, keeps a single tab stop, and ensures the default icon case is properly announced as a button.
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (1)
22-29: Removepage.pause()debugging call to keep tests non-interactive.There’s a leftover
await page.pause();which will block automated runs and violates the “avoidpage.pausein committed tests” guideline; the surroundingexpectcalls already provide deterministic synchronization, so this line should be deleted without replacement. As per coding guidelines, tests should rely on locators/expectations rather than interactive pauses.
🧹 Nitpick comments (28)
packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (2)
3-14: Consider moving background colors into the theme instead of hard-coded RGBA + hex check
background-coloris branching onprops.theme.bg === '#1e1e1e'with literal RGBA values. That tightly couples this component to a specific hex and makes it harder to evolve themes or add new ones.It would be more in line with your theming approach to have dedicated theme tokens for error/surface backgrounds (e.g. dark/light variants) and read them directly here instead of checking for a particular color value.
For example (adjust naming to your actual theme):
- background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(40, 40, 40, 0.5)' : 'rgba(250, 250, 250, 0.9)')}; + background-color: ${(props) => props.theme.colors.background.error};Then wire
colors.background.error(or similar) to suitable dark/light values in the theme.Based on learnings, using theme tokens for colors is preferred over inline color literals in styled components.
34-41: Revisitword-break: break-allfor error messages (readability vs overflow)
word-break: break-allwill break anywhere, including inside normal words, which can make stack traces or plain-text messages harder to read. Given you already usewhite-space: pre-wrap, you might get a better balance with something likeoverflow-wrap: anywhereorword-break: break-word(plus horizontal scrolling if needed), depending on how other panes handle long content.Not a blocker, but worth considering if you see many long-but-readable messages truncated into hard-to-scan fragments.
packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (3)
13-26: Consider memoizing with useCallback.The function works correctly but could benefit from memoization:
+import React, { useCallback } from 'react'; ... - const saveResponseToFile = () => { + const saveResponseToFile = useCallback(() => { if (isDisabled) { return; } return new Promise((resolve, reject) => { ipcRenderer .invoke('renderer:save-response-to-file', response, item?.requestSent?.url, item.pathname) .then(resolve) .catch((err) => { toast.error(get(err, 'error.message') || 'Something went wrong!'); reject(err); }); }); - }; + }, [isDisabled, ipcRenderer, response, item?.requestSent?.url, item.pathname]);
28-39: Keyboard handling is correct.Good accessibility implementation with Enter/Space support and proper disabled state handling. Optionally, consider memoizing with
useCallback:- const handleKeyDown = (e) => { + const handleKeyDown = useCallback((e) => { if (isDisabled) { e.preventDefault(); e.stopPropagation(); return; } if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); saveResponseToFile(); } - }; + }, [isDisabled, saveResponseToFile]);
48-48: Use undefined consistently for conditional attributes.For consistency with the role attribute pattern (which should use undefined), use
undefinedinstead ofnullfor the conditional title.- title={!children ? 'Save response to file' : null} + title={!children ? 'Save response to file' : undefined}packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
8-13: Add indentation toJSON.stringifyfor readability.Objects will render as a single line without the third argument.
try { - return JSON.stringify(data); + return JSON.stringify(data, null, 2); } catch { return String(data); }packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
17-27: Consider extracting shared format initialization logic.This initialization pattern (hook calls + useEffect sync) is duplicated in
ResponsePane/index.js. A custom hook likeuseFormatState(dataBuffer, headers)could encapsulate both the initial format derivation and the state management.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (3)
28-33: MoveisValidTreeDataoutside the component.This pure function is recreated on every render. Move it to module scope to avoid unnecessary allocations.
+const isValidTreeData = (data) => { + if (data === null || data === undefined) return false; + if (typeof data === 'object' && !Array.isArray(data)) return true; + if (Array.isArray(data)) return true; + return false; +}; + export default function XmlPreview({ data, defaultExpanded = true }) { // ... - const isValidTreeData = (data) => { - if (data === null || data === undefined) return false; - if (typeof data === 'object' && !Array.isArray(data)) return true; - if (Array.isArray(data)) return true; - return false; - };
219-221: Remove unused variablearrayLength.
arrayLengthis computed but never used. This is dead code.// Determine if this node's value is an array const hasArrayValue = Array.isArray(node); - const arrayLength = hasArrayValue ? node.length : 0;
130-132: Consider more stable keys for array items.Using index alone as key (
key={idx}) can cause issues if items are reordered. Consider prefixing withnodeNamefor better stability:- key={idx} + key={`${displayNodeName}-${idx}`}packages/bruno-app/src/utils/response/index.js (1)
254-257: Usesubarrayinstead ofslicefor consistency.
buffer.slice()creates a copy whilesubarray()creates a view. Since you already usesubarrayon line 184, use it here too for consistency and slight memory efficiency.// Check if it's likely text (UTF-8) - if (isLikelyText(buffer.slice(0, Math.min(512, buffer.length)))) { + if (isLikelyText(buffer.subarray(0, Math.min(512, buffer.length)))) { return 'text/plain'; }packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (1)
1-1: Remove unuseduseMemoimport.
useMemois imported but not used in this file.-import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react';packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (3)
46-79: Consider extracting duplicate buffer creation logic.The buffer creation with try-catch is duplicated in both
useInitialResponseFormatanduseResponsePreviewFormatOptions. Extract to a shared helper for DRY.+const createBufferFromBase64 = (dataBuffer) => { + try { + return dataBuffer ? Buffer.from(dataBuffer, 'base64') : null; + } catch (error) { + console.error('Error converting dataBuffer to Buffer:', error); + return null; + } +}; + export const useInitialResponseFormat = (dataBuffer, headers) => { return useMemo(() => { - let buffer = null; - try { - buffer = dataBuffer ? Buffer.from(dataBuffer, 'base64') : null; - } catch (error) { - console.error('Error converting dataBuffer to Buffer:', error); - buffer = null; - } + const buffer = createBufferFromBase64(dataBuffer); // ... rest
118-125: Buffer creation duplicated third time.This is the third instance of the same buffer creation pattern. Consolidate with the helper suggested earlier.
- let buffer = null; - try { - buffer = Buffer.from(dataBuffer, 'base64'); - } catch (error) { - console.error('Error converting dataBuffer to Buffer:', error); - buffer = null; - } + const buffer = createBufferFromBase64(dataBuffer);
151-159: Remove staleresponseEncodingdependency.
responseEncodingis in the dependency array but not used in the computation.- [data, dataBuffer, responseEncoding, selectedFormat, filter, isLargeResponse, showLargeResponse] + [data, dataBuffer, selectedFormat, filter, isLargeResponse, showLargeResponse]packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js (1)
1-7: Empty StyledWrapper and potential dead codeThis
StyledWrapperdoesn’t define any styles and isn’t used byResponsePaneActions/index.js(that file imports../StyledWrapper, not./StyledWrapper). Right now it’s effectively a no-op wrapper plus an extra module.Either:
- Wire this up from
ResponsePaneActions/index.js(likely./StyledWrapper) and add any intended theme-based styles, or- Drop this file and use a plain
divuntil you actually need a styled wrapper.packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (4)
15-41: Addtype="button"and consider basic accessibility props on StyledMenuIcon
StyledMenuIconis a plainbuttonwith notype, so it defaults totype="submit"and could accidentally submit a surrounding<form>if this component is ever rendered inside one.Consider:
-const StyledMenuIcon = styled.button` +const StyledMenuIcon = styled.button.attrs({ type: 'button' })`You may also want to add
aria-label="More actions"toMenuIconfor screen readers (thetitlehelps butaria-labelis more explicit).
43-80: Space detection logic is DOM-structure dependent; confirm container assumptions
checkSpacerelies onactionsRef.current?.parentElementas the “actions container” and walkspreviousElementSiblingto sum sibling widths. This assumes:
- The parent of
StyledWrapperis the horizontal container whose siblings represent the other right-side elements, and- All content to consider is to the left of that parent (no following siblings are relevant).
If the DOM around this component changes (e.g., another wrapper/div is inserted or the actions node moves), this logic could silently break and flip between inline/buttons and menu incorrectly.
At minimum, I’d suggest:
- Adding a short comment documenting the expected structure (
.right-side-container > [siblings...] + actions-container), and- Verifying in the surrounding JSX that
actionsRefis attached to an element with the expected parent/ancestors.
82-105: ResizeObserver + debounce lifecycle looks good, but watch for stale refsThe
ResizeObserversetup with a debouncedcheckSpaceand cleanup viadisconnect()anddebouncedCheckSpace.cancel()is solid and should avoid leaks or post-unmount calls.Given
checkSpacecloses overactionsRefand the ref value can change if this component ever re-renders into a different DOM position, just ensureactionsRefalways refers to the same DOM node across the component’s lifetime (or be ready to re-run the effect if that assumption changes). Right now this is fine but a brief code comment about that assumption would help future maintainers.
120-185: Action rendering is clear; consider minor consistency and ResponseCopy behavior checksThe inline vs dropdown rendering is straightforward and the dropdown items correctly close the menu via
onClick={closeDropdown}.A few small points:
- The dropdown “Download response” item is plain text, while the others wrap the label in a
<span>; for consistency/selector reuse, you might want to wrap that label in a<span>too.- The same action set is duplicated between dropdown and inline modes. If this grows further, consider extracting a small config list (icon, label, component, props) to reduce divergence risk.
- For
ResponseCopy, please double-check that the dropdown version still respects the!response.datadisabling behavior used in the toolbar button, so we don’t allow a menu click that attempts to copy an empty/stream-reset response. Based on learnings, this is intentionally guarded inResponseCopy.tests/response-examples/create-example.spec.ts (1)
4-4: Good reuse ofclickResponseAction; update the stale timeout comment.Routing all
response-bookmark-btnclicks throughclickResponseActionimproves resilience and keeps the response-actions logic centralized. The inline comment on Line 20 still talks about “wait for 30 seconds” tied to the old explicit-timeout click; consider rephrasing it to describe the helper’s behavior generically (or just mention “wait for the bookmark button to appear”) so the comment doesn’t drift from what the helper actually does.Also applies to: 20-22, 41-44, 65-68, 79-82, 87-88, 102-105
packages/bruno-app/src/utils/common/index.js (7)
105-107: JS content-type detection and normalization look good; legacy types optional.The
JAVASCRIPT_PATTERNcorrectly normalizes common JS MIME types toapplication/javascript, consistent with how JSON/XML are handled. If you care about older servers, you could optionally extend the regex to acceptapplication/x-javascript/text/x-javascript, but it’s not strictly necessary.Also applies to: 114-115
325-347: HTML formatting branch is consistent; consider a defensive fallback when rawData is empty.The HTML path mirrors the large-response handling used elsewhere, which keeps behavior consistent. As a small robustness tweak, you might fall back to
data(string or stringified) whenrawDatais empty (e.g., if base64 decoding ever fails) so you don’t end up with a blank preview despite having some usabledata.
349-372: JavaScript formatting logic is sound; large-response fallback could be shared.The JavaScript branch mirrors the HTML behavior and safely falls back to the original string on formatter failure. The large-response fallback logic is repeated across HTML, JavaScript, and text/raw; extracting a small helper (e.g.,
formatLargeResponseFallback(data)) would reduce duplication and keep behavior aligned.
374-423: Hex view may be expensive for very large responses; consider reusing buffer and enforcing a size cap.The hex branch always re-decodes
dataBufferStringand generates a full hex dump regardless ofbufferThreshold, which can allocate a large Buffer plus a very large string for big payloads. To avoid UI stalls or OOM on huge responses, consider (a) reusing the buffer already decoded at the top offormatResponseinstead of decoding again, and (b) short-circuiting or truncating hex output whenbufferSizeexceeds a safe limit (similar to how other modes respectisVeryLargeResponse).
485-503: JavaScript prettifier behavior is reasonable; optional logging on failure.Falling back to the original string on formatting errors avoids breaking the UI. If you ever need to debug malformed input, you might optionally log or surface the error (as you do in
prettifyHtmlString), but that’s not required.
530-558: Hex detection heuristic looks robust; note short-hex false negatives.The combination of hex-dump pattern, minimum length, even length, and requiring at least one digit should avoid prior false positives like
dead/beefwhile still catching realistic hex data. This will intentionally treat short all-letter hex strings (e.g.,ff,abcd) as non-hex, which seems like a reasonable trade-off.
7-8: Bundle size impact from Prettier runtime import.Importing
prettier/standalonewith the babel parser will bundle the entire Prettier runtime into the application. For Electron-only usage this is acceptable, but if this utility is reused in web builds, consider lazy-loading these imports or scoping them only to contexts that require JS formatting to minimize bundle size.Note: The
prettier/parser-babelimport path is compatible across Prettier v2 and v3, so no version-specific adjustment is needed.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (66)
packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js(2 hunks)packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/QueryResult/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.spec.js(4 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js(0 hunks)packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StatusCode/index.js(1 hunks)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js(1 hunks)packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js(2 hunks)packages/bruno-app/src/components/ResponsePane/index.js(5 hunks)packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js(2 hunks)packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js(1 hunks)packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js(1 hunks)packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ButtonDropdown/index.jsx(1 hunks)packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js(1 hunks)packages/bruno-app/src/ui/ErrorAlert/index.js(1 hunks)packages/bruno-app/src/utils/common/codemirror.js(1 hunks)packages/bruno-app/src/utils/common/index.js(4 hunks)packages/bruno-app/src/utils/response/index.js(1 hunks)tests/response-examples/create-example.spec.ts(6 hunks)tests/response-examples/edit-example.spec.ts(6 hunks)tests/response-examples/menu-operations.spec.ts(4 hunks)tests/response/large-response-crash-prevention.spec.ts(1 hunks)tests/response/response-actions.spec.ts(2 hunks)tests/response/response-format-select-and-preview/fixtures/collection/bruno.json(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru(1 hunks)tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru(1 hunks)tests/response/response-format-select-and-preview/init-user-data/collection-security.json(1 hunks)tests/response/response-format-select-and-preview/init-user-data/preferences.json(1 hunks)tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts(1 hunks)tests/utils/page/actions.ts(4 hunks)tests/utils/page/locators.ts(3 hunks)
💤 Files with no reviewable changes (3)
- packages/bruno-app/src/components/ResponsePane/GrpcResponsePane/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseSave/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
✅ Files skipped from review due to trivial changes (2)
- tests/response/response-format-select-and-preview/fixtures/collection/request-html.bru
- packages/bruno-app/src/components/ResponsePane/StatusCode/StyledWrapper.js
🚧 Files skipped from review as they are similar to previous changes (34)
- packages/bruno-app/src/ui/ButtonDropdown/StyledWrapper.js
- packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/StyledWrapper.js
- packages/bruno-app/src/ui/ButtonDropdown/index.jsx
- packages/bruno-app/src/utils/common/codemirror.js
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/StyledWrapper.js
- tests/response/response-format-select-and-preview/init-user-data/preferences.json
- packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js
- tests/utils/page/actions.ts
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/VideoPreview.js
- packages/bruno-app/src/components/ResponsePane/QueryResponse/StyledWrapper.js
- tests/response-examples/edit-example.spec.ts
- packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/JsonPreview.js
- packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js
- packages/bruno-app/src/ui/ErrorAlert/index.js
- packages/bruno-app/src/components/Devtools/Console/RequestDetailsPanel/StyledWrapper.js
- tests/response/large-response-crash-prevention.spec.ts
- packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js
- tests/response/response-format-select-and-preview/fixtures/collection/request-json.bru
- packages/bruno-app/src/components/ResponsePane/ResponseTime/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js
- packages/bruno-app/src/components/ResponsePane/ResponseStopWatch/index.js
- packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.spec.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js
- tests/response/response-format-select-and-preview/init-user-data/collection-security.json
- packages/bruno-app/src/components/ResponsePane/StatusCode/index.js
- packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/index.jsx
- packages/bruno-app/src/components/ResponsePane/Timeline/TimelineItem/Common/Body/index.js
- tests/response/response-format-select-and-preview/fixtures/collection/bruno.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-app/src/components/Sidebar/Collections/Collection/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.jspackages/bruno-app/src/components/ResponsePane/QueryResult/index.jspackages/bruno-app/src/components/ResponsePane/QueryResponse/index.jspackages/bruno-app/src/utils/common/index.jspackages/bruno-app/src/utils/response/index.jstests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tstests/response-examples/create-example.spec.tspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/ResponsePane/ClearTimeline/index.jspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.jspackages/bruno-app/src/components/ResponsePane/ResponseTime/index.jstests/response/response-actions.spec.tspackages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.jspackages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jstests/utils/page/locators.tstests/response-examples/menu-operations.spec.tspackages/bruno-app/src/components/ResponsePane/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.jspackages/bruno-app/src/components/ResponsePane/ResponseSize/index.jspackages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js
tests/**/**.*
⚙️ CodeRabbit configuration file
tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:
Follow best practices for Playwright code and e2e automation
Try to reduce usage of
page.waitForTimeout();in code unless absolutely necessary and the locator cannot be found using existingexpect()playwright callsAvoid using
page.pause()in codeUse locator variables for locators
Avoid using test.only
Use multiple assertions
Promote the use of
test.stepas much as possible so the generated reports are easier to readEnsure that the
fixtureslike the collections are nested inside thefixturesfolderFixture Example*: Here's an example of possible fixture and test pair
. ├── fixtures │ └── collection │ ├── base.bru │ ├── bruno.json │ ├── collection.bru │ ├── ws-test-request-with-headers.bru │ ├── ws-test-request-with-subproto.bru │ └── ws-test-request.bru ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection ├── headers.spec.ts ├── persistence.spec.ts ├── variable-interpolation │ ├── fixtures │ │ └── collection │ │ ├── environments │ │ ├── bruno.json │ │ └── ws-interpolation-test.bru │ ├── init-user-data │ └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection └── subproto.spec.ts
Files:
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tstests/response-examples/create-example.spec.tstests/response/response-actions.spec.tstests/utils/page/locators.tstests/response-examples/menu-operations.spec.ts
🧠 Learnings (8)
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any React component using the styled component
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js
📚 Learning: 2025-12-02T09:45:31.709Z
Learnt from: sid-bruno
Repo: usebruno/bruno PR: 6266
File: packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js:38-38
Timestamp: 2025-12-02T09:45:31.709Z
Learning: In the ResponseCopy component (packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js), the copy button is intentionally disabled using `!response.data` to prevent copying stream resets which result in empty strings.
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.jspackages/bruno-app/src/components/ResponsePane/ResponseClear/index.jspackages/bruno-app/src/components/ResponsePane/ResponseDownload/index.jspackages/bruno-app/src/components/ResponsePane/index.jspackages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS might also change layout but Tailwind classes shouldn't define colors
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components are used as wrappers to define both self and children components style; Tailwind classes are used specifically for layout based styles
Applied to files:
packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.jspackages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.jspackages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Add in JSDoc comments to add more details to the abstractions if needed
Applied to files:
packages/bruno-app/src/utils/common/index.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Applied to files:
packages/bruno-app/src/utils/common/index.js
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.test.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created
Applied to files:
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.tstests/response-examples/menu-operations.spec.ts
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : No trailing commas
Applied to files:
tests/utils/page/locators.ts
🧬 Code graph analysis (12)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (2)
packages/bruno-app/src/utils/common/index.js (2)
isValidHtml(506-509)isValidHtml(506-509)packages/bruno-app/src/utils/response/index.js (2)
isValidHtmlSnippet(98-171)isValidHtmlSnippet(98-171)
packages/bruno-app/src/utils/response/index.js (3)
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (1)
contentType(126-126)packages/bruno-app/src/utils/common/index.js (4)
contentType(93-93)i(21-21)i(515-515)trimmed(537-537)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (1)
parser(302-302)
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (3)
tests/utils/page/actions.ts (6)
closeAllCollections(694-694)openRequest(711-711)sendRequestAndWaitForResponse(716-716)switchToPreviewTab(718-718)switchToEditorTab(719-719)switchResponseFormat(717-717)tests/utils/page/locators.ts (1)
buildCommonLocators(3-102)packages/bruno-app/src/utils/codemirror/brunoVarInfo.js (1)
editorContainer(298-298)
tests/response-examples/create-example.spec.ts (1)
tests/utils/page/actions.ts (1)
clickResponseAction(720-720)
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/StyledWrapper.js (7)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultTypeSelector/StyledWrapper.js (1)
StyledWrapper(3-11)packages/bruno-app/src/components/ResponsePane/QueryResult/StyledWrapper.js (1)
StyledWrapper(3-58)packages/bruno-app/src/components/ResponsePane/ResponseBookmark/StyledWrapper.js (1)
StyledWrapper(3-13)packages/bruno-app/src/components/ResponsePane/ResponseClear/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (1)
StyledWrapper(3-12)packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (1)
packages/bruno-app/src/components/ResponsePane/ClearTimeline/StyledWrapper.js (1)
StyledWrapper(3-6)
packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js (1)
packages/bruno-app/src/components/ResponsePane/StyledWrapper.js (1)
StyledWrapper(3-42)
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (5)
packages/bruno-app/src/utils/common/index.js (3)
parsed(318-318)i(21-21)i(515-515)packages/bruno-app/src/ui/ErrorAlert/index.js (1)
ErrorAlert(5-23)packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (1)
StyledWrapper(3-42)packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/StyledWrapper.js (1)
StyledWrapper(3-75)packages/bruno-app/src/utils/response/index.js (2)
parser(157-157)i(80-80)
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (3)
packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js (1)
useResponseLayoutToggle(44-44)packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (1)
handleKeyDown(40-45)packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (1)
handleKeyDown(27-32)
packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (2)
packages/bruno-app/src/components/ResponsePane/index.js (1)
response(67-67)packages/bruno-app/src/components/ResponsePane/ResponseDownload/StyledWrapper.js (1)
StyledWrapper(3-12)
tests/response-examples/menu-operations.spec.ts (1)
tests/utils/page/actions.ts (1)
clickResponseAction(720-720)
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (2)
packages/bruno-app/src/components/ResponsePane/index.js (1)
response(67-67)packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
StyledWrapper(3-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: SSL Tests - Windows
- GitHub Check: Playwright E2E Tests
🔇 Additional comments (41)
packages/bruno-app/src/components/ResponsePane/ResponseSize/index.js (1)
21-24: Spacing tweak in wrapper looks goodThe switch to
ml-2is consistent with the tighter ResponsePane spacing described in the PR and does not affect behavior or logic. No further changes needed here.packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js (1)
286-297: Stable test selector for collection row looks goodAdding
data-testid="sidebar-collection-row"on the main row container is a clean way to target collections in tests without coupling to layout classes or IDs. No behavioral impact, and consistent with the sidebar naming scheme.packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js (1)
497-508: Item row test id is appropriate and consistent
data-testid="sidebar-collection-item-row"on the main item row wrapper provides a stable, semantic selector aligned with the collection row test id. Works well for E2E and unit tests to distinguish item rows from collection rows.packages/bruno-app/src/components/ResponsePane/ClearTimeline/index.js (1)
17-22: Layout-only tweak looks goodRemoving the
ml-2and keepingflex items-centersimplifies spacing on this wrapper without affecting behavior. As long as this still visually aligns with neighboring controls in the ResponsePane, this change is good to go.Please double-check the rendered ResponsePane to confirm that Clear Timeline still lines up correctly with the new response data type selector and other actions.
packages/bruno-app/src/components/ResponsePane/ResponseTime/index.js (1)
20-20: LGTM! Spacing adjustment aligns with layout changes.The margin reduction from
ml-4toml-2is consistent with the broader UI layout refinements in this PR and maintains proper visual hierarchy in the response pane.packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js (1)
5-11: LGTM! Theme-based styling applied correctly.The updated color tokens and hover states follow the established pattern of using theme props for colors, consistent with the project's styling approach.
Based on learnings, styled components correctly use theme props for color management.
packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js (2)
6-35: Well-structured hook extraction.The
useResponseCopyhook properly encapsulates the copy logic and state management, making it reusable across the application.
40-45: Keyboard handler correctly prevents default behavior.The Space key now includes
preventDefault(), resolving the previous scroll issue.packages/bruno-app/src/ui/ErrorAlert/StyledWrapper.js (2)
15-26: Close button styling looks consistent and minimalUsing opacity + a short transition for hover, and pulling the SVG color from
props.theme.text, fits well with the rest of your theming and keeps the control visually lightweight.
28-32: Title styling matches intent and theme usageThe
.error-titleusing a medium weight, small bottom margin, andtheme.colors.text.dangeraligns well with an error-heading role and keeps the color consistent with the rest of the design system.packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js (3)
1-6: LGTM!Import statements are clean and all dependencies are utilized in the component.
8-11: LGTM!Component initialization is correct. The
!response.dataBufferdisabled check aligns with the pattern used in ResponseCopy and ensures the download action only works when buffer data is available.
53-59: Critical: Nested button elements violate accessibility guidelines.Line 55 creates a
<button>element inside a parent div that already has button semantics (role, onClick, keyboard handlers). This nested interactive element pattern creates accessibility issues for screen readers and keyboard navigation.Apply this diff to replace the inner button with a non-interactive element:
{children ? children : ( <StyledWrapper className="flex items-center"> - <button className="p-1"> + <div className="p-1"> <IconDownload size={16} strokeWidth={2} /> - </button> + </div> </StyledWrapper> )}Likely an incorrect or invalid review comment.
packages/bruno-app/src/components/ResponsePane/ResponseLayoutToggle/index.js (1)
51-70: LGTM! Clean hook extraction.The
useResponseLayoutTogglehook cleanly encapsulates orientation state and toggle logic, making it reusable across components likeResponsePaneActions.packages/bruno-app/src/components/ResponsePane/index.js (3)
41-46: Format resets on response change — intentional?This effect resets
selectedFormatandselectedTabwheneverinitialFormat/initialTabchange (i.e., when response content changes). If a user manually selects a format then re-runs the request, their selection will be overwritten.If this is intentional, consider adding a brief comment. If not, you may want to track whether the user has manually changed the format.
101-102: LGTM! Props correctly passed to QueryResult.The
selectedFormatandselectedTabprops are properly wired to the child component, enabling format-driven rendering.
203-213: LGTM! Format selector integration is clean.The
QueryResultTypeSelectoris properly wired with format options, current value, and handlers. The toggle logic forselectedTabis correct.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/TextPreview.js (1)
18-22: LGTM! Styling is appropriate for text preview.Monospace font, whitespace preservation, and overflow handling are well-configured.
packages/bruno-app/src/components/ResponsePane/QueryResponse/index.js (1)
28-56: LGTM! Component structure is clean.The header/content separation and prop passing to
QueryResultfollow the established patterns fromResponsePane.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/HtmlPreview.js (2)
9-32: MutationObserver for drag detection is effective but has overhead.This observer runs on every class attribute change in the subtree. If many
HtmlPreviewcomponents are mounted simultaneously, this could add up. Consider using a React context or event-based approach if performance becomes an issue.
34-54: LGTM! HTML rendering with base URL injection is well-handled.The
escapeHtmlusage prevents XSS in the base URL, and the webview configuration disables dialogs appropriately. The drag-blocking styles ensure smooth UX during pane resizing.packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/XmlPreview/index.js (2)
74-112: LGTM!Clean implementation with proper React state management for expand/collapse. Good use of
aria-expandedfor accessibility.
379-396: LGTM!Helper functions are simple and correctly implemented.
packages/bruno-app/src/utils/response/index.js (4)
1-6: LGTM!Clean MIME type extraction with proper fallback handling.
8-59: LGTM!Well-structured rule-based format detection with comprehensive coverage.
61-70: LGTM!Standard HTML escaping implementation for XSS prevention.
75-93: LGTM!Good heuristic for text detection with proper guard against empty buffers.
packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js (2)
64-80: LGTM!Clean conditional rendering for editor mode with proper prop passing.
109-119: LGTM!Clean implementation of the new preview modes for JSON, text, and XML content.
packages/bruno-app/src/components/ResponsePane/QueryResult/index.js (3)
13-31: Verify JSON CodeMirror mode.
'application/ld+json'is specific to JSON-LD. For generic JSON responses,'application/json'may be more appropriate. Both should work, but confirm this is intentional.
165-185: LGTM!Clean preview mode derivation with proper fallback handling and correct dependencies.
223-237: LGTM!QueryResultPreview integration with new props is clean and well-structured.
packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js (2)
7-22: Hook extraction looks good.
useResponseClearcleanly encapsulates the dispatch logic and keepsResponseClearfocused on UI concerns. No issues from a React/Redux perspective.
24-32: Keyboard handling for Enter/Space is now correct.Using
onKeyDownwithEnter/Spacepluse.preventDefault()avoids page scroll and makes the control keyboard-activatable, which is appropriate given therole="button"usage on the wrapper.tests/response-examples/menu-operations.spec.ts (1)
4-4: Centralized bookmark clicks viaclickResponseActionlook solid.Using
clickResponseAction(page, 'response-bookmark-btn')for all three example-creation flows keeps the tests DRY and delegates timing/visibility concerns to the shared helper without changing test intent.Also applies to: 20-22, 50-53, 83-86
tests/response/response-format-select-and-preview/response-format-select-and-preview.spec.ts (1)
12-88: Comprehensive coverage of response format selection and preview.This suite does a nice job exercising the new format selector end-to-end: JSON and HTML responses, editor vs preview tabs, and all major formats (JSON, HTML, XML, JavaScript, Raw, Hex, Base64) with both editor contents and preview behavior asserted. The use of common locators and action helpers keeps the tests readable and aligned with our E2E guidelines for new functionality. Based on learnings, this gives the new feature solid automated coverage.
Also applies to: 90-160
tests/response/response-actions.spec.ts (1)
3-3: Consistent handling of response copy viaclickResponseAction.Swapping the explicit
response-copy-btnclick forclickResponseAction(page, 'response-copy-btn')keeps response actions consistent with the rest of the suite while the toast assertion still verifies the user-visible behavior.Also applies to: 29-31
tests/utils/page/locators.ts (1)
60-60: New response/environment/import locators look consistent with the new flows.The added response locators (copy button, editor/preview containers, format tab/dropdown, code/json tree lines), the
envOptionhelpers, and theclearResponsetest-id locator all line up with how the new tests exercise the response pane and import flows, and they respect the no-trailing-comma style rule. As per coding guidelines, this keeps selectors centralized and maintainable for the expanded response UI.Also applies to: 81-88, 99-100, 122-122
packages/bruno-app/src/utils/common/index.js (3)
466-483: HTML prettifier is straightforward and safely falls back.Using
xmlFormatwith sensible options and catching errors to return the original string gives a nice best-effort formatter without risking broken output. This helper looks good as-is.
505-509: HTML validity check is intentionally strict; confirm callers expect full documents.
isValidHtmlonly returns true when an<html>root tag is present, so HTML fragments (<div>...) will be treated as invalid. That’s fine if you only consider full documents as “HTML” for your preview logic; just ensure call sites don’t expect snippets to pass this check.
511-528: Hex dump formatter looks correct and readable.
formatHexViewproduces a conventional 16-byte-per-line hex + ASCII view with stable offsets and padding; the implementation is clear and matches typical hex-dump output.
|
|
||
| return ( | ||
| <div | ||
| role={!!children ? 'button' : undefined} |
There was a problem hiding this comment.
Fix: role should always be "button".
The conditional role logic is incorrect. This div always behaves as a button (has onClick, onKeyDown, and keyboard interaction), so it should always have role="button" regardless of whether children are provided.
Apply this diff:
- role={!!children ? 'button' : undefined}
+ role="button"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| role={!!children ? 'button' : undefined} | |
| role="button" |
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ResponsePane/ResponseDownload/index.js
around line 43, the element conditionally sets role={!!children ? 'button' :
undefined} but the element always behaves like a button (has
onClick/onKeyDown/keyboard interaction); change it to always set role="button"
unconditionally so assistive tech and accessibility tools see the correct role.
| import React, { useState, useEffect, useRef, forwardRef, useCallback, useMemo } from 'react'; | ||
| import { debounce } from 'lodash'; | ||
| import styled from 'styled-components'; | ||
| import { IconDots, IconDownload, IconEraser, IconBookmark, IconCopy } from '@tabler/icons'; | ||
| import Dropdown from 'components/Dropdown'; | ||
| import ResponseDownload from '../ResponseDownload'; | ||
| import ResponseBookmark from '../ResponseBookmark'; | ||
| import ResponseClear from '../ResponseClear'; | ||
| import ResponseLayoutToggle, { useResponseLayoutToggle, IconDockToBottom, IconDockToRight } from '../ResponseLayoutToggle'; | ||
| import ResponseCopy from '../ResponseCopy/index'; | ||
| import StyledWrapper from '../StyledWrapper'; | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
StyledWrapper import path likely incorrect and leaves new wrapper unused
ResponsePaneActions/index.js imports StyledWrapper from ../StyledWrapper, which is the parent ResponsePane wrapper, while this folder also defines ResponsePaneActions/StyledWrapper.js. That new wrapper is currently unused and this component ends up reusing the broader ResponsePane styles.
Recommend either:
- Switch this import to
./StyledWrapperto use the dedicated wrapper for actions, or - Delete
ResponsePaneActions/StyledWrapper.jsand keep using the shared../StyledWrapperexplicitly.
Right now this is confusing and effectively leaves the new file as dead code.
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/ResponsePane/ResponsePaneActions/index.js
lines 1-12, the file imports StyledWrapper from ../StyledWrapper (the parent
ResponsePane wrapper) while there is a dedicated
ResponsePaneActions/StyledWrapper.js that is unused; update the import to
./StyledWrapper so this component uses its action-specific wrapper, or
alternatively remove ResponsePaneActions/StyledWrapper.js if you prefer the
shared wrapper—prefer switching the import to ./StyledWrapper to avoid dead code
and ensure the actions pane uses its intended styles.
Description
Added Response Data Type selector with preview mode for each type. Closes #4963
JIRA
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
Tests
✏️ Tip: You can customize this high-level summary in your review settings.