From 0148ba65109c4c3a9f6368cbe57ca2b10279558c Mon Sep 17 00:00:00 2001 From: Pooja Date: Mon, 6 Jul 2026 14:46:27 +0530 Subject: [PATCH] feat(ws): improve message panel UX (#8465) --- .../RequestPane/WSRequestPane/index.js | 2 +- .../WsBody/BodyMode/StyledWrapper.js | 2 +- .../WsBody/SingleWSMessage/StyledWrapper.js | 14 +++++++- .../RequestPane/WsBody/StyledWrapper.js | 1 + .../ReduxStore/slices/collections/actions.js | 2 +- .../src/themes/dark/catppuccin-frappe.js | 6 ++++ .../src/themes/dark/catppuccin-macchiato.js | 6 ++++ .../src/themes/dark/catppuccin-mocha.js | 6 ++++ .../src/themes/dark/dark-monochrome.js | 6 ++++ .../bruno-app/src/themes/dark/dark-pastel.js | 6 ++++ packages/bruno-app/src/themes/dark/dark.js | 6 ++++ packages/bruno-app/src/themes/dark/nord.js | 6 ++++ packages/bruno-app/src/themes/dark/vscode.js | 6 ++++ .../src/themes/light/catppuccin-latte.js | 6 ++++ .../src/themes/light/light-monochrome.js | 6 ++++ .../src/themes/light/light-pastel.js | 6 ++++ packages/bruno-app/src/themes/light/light.js | 6 ++++ packages/bruno-app/src/themes/light/vscode.js | 6 ++++ packages/bruno-app/src/themes/schema/oss.js | 16 +++++++++ .../collection/ws-default-body-request.bru | 19 +++++++++++ tests/websockets/message-body-default.spec.ts | 34 +++++++++++++++++++ 21 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 tests/websockets/fixtures/collection/ws-default-body-request.bru create mode 100644 tests/websockets/message-body-default.spec.ts diff --git a/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js index 0fa945bee..4d42c56a2 100644 --- a/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js +++ b/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js @@ -52,7 +52,7 @@ const WSRequestPane = ({ item, collection, handleRun }) => { currentMessages.push({ uid: uuid(), name: `message ${currentMessages.length + 1}`, - content: '{}', + content: '', type: 'json', selected: true }); diff --git a/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/StyledWrapper.js index bc835b3f2..34a81a362 100644 --- a/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/StyledWrapper.js @@ -17,7 +17,7 @@ const Wrapper = styled.div` } .selected-body-mode { - color: ${(props) => props.theme.primary.text}; + color: ${(props) => props.theme.ws.activeMessage.label}; } } diff --git a/packages/bruno-app/src/components/RequestPane/WsBody/SingleWSMessage/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/WsBody/SingleWSMessage/StyledWrapper.js index 3c27a00f4..653c127c4 100644 --- a/packages/bruno-app/src/components/RequestPane/WsBody/SingleWSMessage/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/WsBody/SingleWSMessage/StyledWrapper.js @@ -3,6 +3,11 @@ import styled from 'styled-components'; const StyledWrapper = styled.div` border-bottom: 1px solid ${(props) => props.theme.border.border0}; + /* No divider below the last message */ + &:last-child { + border-bottom: none; + } + /* Dim the row content when disabled, but not the tooltip */ .accordion-left > :not(.toolhint), .accordion-actions, @@ -18,6 +23,13 @@ const StyledWrapper = styled.div` } } + &:not(.disabled) .accordion-header { + position: sticky; + top: 0; + z-index: 1; + background: ${(props) => props.theme.bg}; + } + .accordion-header { display: flex; align-items: center; @@ -102,7 +114,7 @@ const StyledWrapper = styled.div` } &:not(.disabled) .accordion-header .message-label { - color: ${(props) => props.theme.primary.text}; + color: ${(props) => props.theme.ws.activeMessage.label}; } `; diff --git a/packages/bruno-app/src/components/RequestPane/WsBody/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/WsBody/StyledWrapper.js index e08f17db7..9429e361d 100644 --- a/packages/bruno-app/src/components/RequestPane/WsBody/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/WsBody/StyledWrapper.js @@ -9,6 +9,7 @@ const Wrapper = styled.div` .messages-container { flex: 1; overflow-y: auto; + isolation: isolate; } .empty-state { diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index dfbcf3680..468098f2e 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -1711,7 +1711,7 @@ export const newWsRequest = (params) => (dispatch, getState) => { uid: uuid(), name: 'message 1', type: 'json', - content: '{}' + content: '' } ] }, diff --git a/packages/bruno-app/src/themes/dark/catppuccin-frappe.js b/packages/bruno-app/src/themes/dark/catppuccin-frappe.js index b57680f55..7be855ddd 100644 --- a/packages/bruno-app/src/themes/dark/catppuccin-frappe.js +++ b/packages/bruno-app/src/themes/dark/catppuccin-frappe.js @@ -75,6 +75,12 @@ const catppuccinFrappeTheme = { subtle: colors.MAUVE }, + ws: { + activeMessage: { + label: colors.MAUVE + } + }, + accents: { primary: colors.MAUVE }, diff --git a/packages/bruno-app/src/themes/dark/catppuccin-macchiato.js b/packages/bruno-app/src/themes/dark/catppuccin-macchiato.js index c5863ec20..ae8c13633 100644 --- a/packages/bruno-app/src/themes/dark/catppuccin-macchiato.js +++ b/packages/bruno-app/src/themes/dark/catppuccin-macchiato.js @@ -75,6 +75,12 @@ const catppuccinMacchiatoTheme = { subtle: colors.MAUVE }, + ws: { + activeMessage: { + label: colors.MAUVE + } + }, + accents: { primary: colors.MAUVE }, diff --git a/packages/bruno-app/src/themes/dark/catppuccin-mocha.js b/packages/bruno-app/src/themes/dark/catppuccin-mocha.js index 46fe43b2d..79b21e469 100644 --- a/packages/bruno-app/src/themes/dark/catppuccin-mocha.js +++ b/packages/bruno-app/src/themes/dark/catppuccin-mocha.js @@ -75,6 +75,12 @@ const catppuccinMochaTheme = { subtle: colors.MAUVE }, + ws: { + activeMessage: { + label: colors.MAUVE + } + }, + accents: { primary: colors.MAUVE }, diff --git a/packages/bruno-app/src/themes/dark/dark-monochrome.js b/packages/bruno-app/src/themes/dark/dark-monochrome.js index be292a499..22371a1b4 100644 --- a/packages/bruno-app/src/themes/dark/dark-monochrome.js +++ b/packages/bruno-app/src/themes/dark/dark-monochrome.js @@ -212,6 +212,12 @@ const darkMonochromeTheme = { } }, + ws: { + activeMessage: { + label: colors.TEXT + } + }, + request: { methods: { get: '#a3a3a3', diff --git a/packages/bruno-app/src/themes/dark/dark-pastel.js b/packages/bruno-app/src/themes/dark/dark-pastel.js index 29ba300bb..d44270564 100644 --- a/packages/bruno-app/src/themes/dark/dark-pastel.js +++ b/packages/bruno-app/src/themes/dark/dark-pastel.js @@ -83,6 +83,12 @@ const darkPastelTheme = { subtle: colors.BRAND }, + ws: { + activeMessage: { + label: colors.BRAND + } + }, + accents: { primary: colors.BRAND }, diff --git a/packages/bruno-app/src/themes/dark/dark.js b/packages/bruno-app/src/themes/dark/dark.js index 237d7d0cc..fa6d8d886 100644 --- a/packages/bruno-app/src/themes/dark/dark.js +++ b/packages/bruno-app/src/themes/dark/dark.js @@ -107,6 +107,12 @@ const darkTheme = { subtle: palette.primary.SUBTLE }, + ws: { + activeMessage: { + label: palette.primary.TEXT + } + }, + accents: { primary: palette.primary.SOLID }, diff --git a/packages/bruno-app/src/themes/dark/nord.js b/packages/bruno-app/src/themes/dark/nord.js index 83c512141..0ec24948b 100644 --- a/packages/bruno-app/src/themes/dark/nord.js +++ b/packages/bruno-app/src/themes/dark/nord.js @@ -82,6 +82,12 @@ const nordTheme = { subtle: colors.BRAND }, + ws: { + activeMessage: { + label: colors.BRAND + } + }, + accents: { primary: colors.BRAND }, diff --git a/packages/bruno-app/src/themes/dark/vscode.js b/packages/bruno-app/src/themes/dark/vscode.js index 8cc0af4c3..26c2cc849 100644 --- a/packages/bruno-app/src/themes/dark/vscode.js +++ b/packages/bruno-app/src/themes/dark/vscode.js @@ -85,6 +85,12 @@ const vscodeDarkTheme = { subtle: '#005a9e' }, + ws: { + activeMessage: { + label: colors.BRAND_TEXT + } + }, + accents: { primary: colors.BRAND }, diff --git a/packages/bruno-app/src/themes/light/catppuccin-latte.js b/packages/bruno-app/src/themes/light/catppuccin-latte.js index f1c3edb9b..725a16017 100644 --- a/packages/bruno-app/src/themes/light/catppuccin-latte.js +++ b/packages/bruno-app/src/themes/light/catppuccin-latte.js @@ -75,6 +75,12 @@ const catppuccinLatteTheme = { subtle: colors.MAUVE }, + ws: { + activeMessage: { + label: colors.MAUVE + } + }, + accents: { primary: colors.MAUVE }, diff --git a/packages/bruno-app/src/themes/light/light-monochrome.js b/packages/bruno-app/src/themes/light/light-monochrome.js index 353bc0977..48f834155 100644 --- a/packages/bruno-app/src/themes/light/light-monochrome.js +++ b/packages/bruno-app/src/themes/light/light-monochrome.js @@ -213,6 +213,12 @@ const lightMonochromeTheme = { } }, + ws: { + activeMessage: { + label: colors.BRAND + } + }, + request: { methods: { get: '#525252', diff --git a/packages/bruno-app/src/themes/light/light-pastel.js b/packages/bruno-app/src/themes/light/light-pastel.js index 2b824d434..f1482fc1f 100644 --- a/packages/bruno-app/src/themes/light/light-pastel.js +++ b/packages/bruno-app/src/themes/light/light-pastel.js @@ -228,6 +228,12 @@ const lightPastelTheme = { } }, + ws: { + activeMessage: { + label: colors.BRAND + } + }, + request: { methods: { get: '#3c9d7c', // Soft emerald - success diff --git a/packages/bruno-app/src/themes/light/light.js b/packages/bruno-app/src/themes/light/light.js index 60929cb34..87dc6cae7 100644 --- a/packages/bruno-app/src/themes/light/light.js +++ b/packages/bruno-app/src/themes/light/light.js @@ -249,6 +249,12 @@ const lightTheme = { } }, + ws: { + activeMessage: { + label: palette.primary.TEXT + } + }, + request: { methods: { get: palette.hues.GREEN, diff --git a/packages/bruno-app/src/themes/light/vscode.js b/packages/bruno-app/src/themes/light/vscode.js index 9c258d615..7d14aa546 100644 --- a/packages/bruno-app/src/themes/light/vscode.js +++ b/packages/bruno-app/src/themes/light/vscode.js @@ -232,6 +232,12 @@ const vscodeLightTheme = { } }, + ws: { + activeMessage: { + label: colors.TEXT_LINK + } + }, + request: { methods: { get: colors.GREEN, diff --git a/packages/bruno-app/src/themes/schema/oss.js b/packages/bruno-app/src/themes/schema/oss.js index 42351eec4..45ed2a909 100644 --- a/packages/bruno-app/src/themes/schema/oss.js +++ b/packages/bruno-app/src/themes/schema/oss.js @@ -334,6 +334,22 @@ export const ossSchema = { additionalProperties: false }, + ws: { + type: 'object', + properties: { + activeMessage: { + type: 'object', + properties: { + label: { type: 'string' } + }, + required: ['label'], + additionalProperties: false + } + }, + required: ['activeMessage'], + additionalProperties: false + }, + requestTabPanel: { type: 'object', properties: { diff --git a/tests/websockets/fixtures/collection/ws-default-body-request.bru b/tests/websockets/fixtures/collection/ws-default-body-request.bru new file mode 100644 index 000000000..645007c40 --- /dev/null +++ b/tests/websockets/fixtures/collection/ws-default-body-request.bru @@ -0,0 +1,19 @@ +meta { + name: ws-default-body-request + type: ws + seq: 4 +} + +ws { + url: ws://localhost:8081/ws + auth: inherit +} + +body:ws { + name: message 1 + content: ''' + { + "foo":"bar" + } + ''' +} diff --git a/tests/websockets/message-body-default.spec.ts b/tests/websockets/message-body-default.spec.ts new file mode 100644 index 000000000..f166c0c43 --- /dev/null +++ b/tests/websockets/message-body-default.spec.ts @@ -0,0 +1,34 @@ +import { expect, test } from '../../playwright'; +import { closeAllCollections } from '../utils/page/actions'; + +const REQ_NAME = /^ws-default-body-request$/; + +test.describe('websocket message default body', () => { + test.afterAll(async ({ pageWithUserData: page }) => { + await closeAllCollections(page); + }); + + test('a newly added message defaults to an empty body showing the placeholder', async ({ + pageWithUserData: page + }) => { + // Open the preloaded websocket request + await page.getByTestId('sidebar-collection-row').click(); + await page.getByTitle(REQ_NAME).click(); + + const headers = page.getByTestId(/^ws-message-header-/); + const beforeCount = await headers.count(); + + await page.getByTestId('ws-add-message').click(); + await expect(headers).toHaveCount(beforeCount + 1); + + // The newly added message is the last one and auto-expands. + const newBody = page.getByTestId(`ws-message-body-${beforeCount}`); + await expect(newBody).toBeVisible(); + + const editor = newBody.locator('.CodeMirror'); + // Body should be empty (previously defaulted to '{}'); the editor should + // surface the '...' placeholder instead of any '{}' content. + await expect(editor.locator('.CodeMirror-placeholder')).toHaveText('...'); + await expect(editor.locator('.CodeMirror-code')).not.toContainText('{}'); + }); +});