diff --git a/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/StyledWrapper.js new file mode 100644 index 000000000..3d571b4bf --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/StyledWrapper.js @@ -0,0 +1,30 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + font-size: 0.8125rem; + + .body-mode-selector { + background: transparent; + border-radius: 3px; + + .dropdown-item { + padding: 0.2rem 0.6rem !important; + padding-left: 1.5rem !important; + } + + .label-item { + padding: 0.2rem 0.6rem !important; + } + + .selected-body-mode { + color: ${(props) => props.theme.colors.text.yellow}; + } + } + + .caret { + color: rgb(140, 140, 140); + fill: rgb(140 140 140); + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/index.js b/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/index.js new file mode 100644 index 000000000..b6d2b1f5f --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/WsBody/BodyMode/index.js @@ -0,0 +1,68 @@ +import React, { useRef, forwardRef } from 'react'; +import { IconCaretDown } from '@tabler/icons'; +import Dropdown from 'components/Dropdown'; +import { humanizeRequestBodyMode } from 'utils/collections'; +import StyledWrapper from './StyledWrapper'; + + +const WSRequestBodyMode = ({ mode, onModeChange }) => { + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + + + const Icon = forwardRef((props, ref) => { + return ( +
+ {humanizeRequestBodyMode(mode)} +
+ ); + }); + + return ( + +
+ } placement="bottom-end"> +
Raw
+
{ + dropdownTippyRef.current.hide(); + onModeChange('json'); + }} + > + JSON +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('xml'); + }} + > + XML +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('text'); + }} + > + TEXT +
+ {/*
Other
+
{ + dropdownTippyRef.current.hide(); + onModeChange('file'); + }} + > + File / Binary +
*/} +
+
+
+ ); +}; +export default WSRequestBodyMode; diff --git a/packages/bruno-app/src/components/RequestPane/WsBody/index.js b/packages/bruno-app/src/components/RequestPane/WsBody/index.js index 41a7d6eee..e99cc0576 100644 --- a/packages/bruno-app/src/components/RequestPane/WsBody/index.js +++ b/packages/bruno-app/src/components/RequestPane/WsBody/index.js @@ -4,14 +4,14 @@ import { saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import { useTheme } from 'providers/Theme'; import React, { useEffect, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { generateGrpcSampleMessage, sendWsRequest } from 'utils/network/index'; -import { IconChevronDown, IconChevronUp, IconPlus, IconRefresh, IconSend, IconTrash, IconWand } from '@tabler/icons'; +import { IconChevronDown, IconChevronUp, IconPlus, IconTrash, IconWand } from '@tabler/icons'; import CodeEditor from 'components/CodeEditor/index'; import ToolHint from 'components/ToolHint/index'; import { applyEdits, format } from 'jsonc-parser'; -import toast from 'react-hot-toast'; import { toastError } from 'utils/common/error'; import StyledWrapper from './StyledWrapper'; +import WSRequestBodyMode from './BodyMode/index'; +import { autoDetectLang } from 'utils/codemirror/lang-detect'; const SingleWSMessage = ({ message, @@ -25,14 +25,12 @@ const SingleWSMessage = ({ canClientSendMultipleMessages }) => { const dispatch = useDispatch(); - const { displayedTheme, theme } = useTheme(); + const { displayedTheme } = useTheme(); const preferences = useSelector((state) => state.app.preferences); const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body'); - const isConnectionActive = useSelector((state) => state.collections.activeConnections.includes(item.uid)); - - const canClientStream = methodType === 'client-streaming' || methodType === 'bidi-streaming'; const { name, content } = message; + const [messageFormat, setMessageFormat] = useState(autoDetectLang(content)); const onEdit = (value) => { const currentMessages = [...(body.ws || [])]; @@ -92,6 +90,12 @@ const SingleWSMessage = ({ const getContainerHeight = canClientSendMultipleMessages && body.ws.length > 1 ? `${isCollapsed ? '' : 'h-80'}` : 'h-full'; + const codemirrorMode = { + text: 'application/text', + xml: 'application/xml', + json: 'application/ld+json' + }; + return (
)} - {`Message ${canClientStream ? index + 1 : ''}`}
e.stopPropagation()}> +
- {!isCollapsed && (
diff --git a/packages/bruno-app/src/utils/codemirror/lang-detect.js b/packages/bruno-app/src/utils/codemirror/lang-detect.js new file mode 100644 index 000000000..49ea56a7f --- /dev/null +++ b/packages/bruno-app/src/utils/codemirror/lang-detect.js @@ -0,0 +1,34 @@ +/** + * @param {string} snippet + * @returns {boolean} + */ +export function isXML(snippet){ + return /<\/?[a-z][\s\S]*>/i.test(snippet) +} + +/** + * @param {string} snippet + * @returns {boolean} + */ +export function isJSON(snippet) { + try { + JSON.parse(snippet); + return true; + } catch (err) { + return false; + } +} + +/** + * @param {string} snippet + * @returns {string} + */ +export function autoDetectLang(snippet) { + if (isJSON(snippet)) { + return 'json'; + } + if(isXML(snippet)){ + return 'xml' + } + return 'text' +} diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index 572e126bd..732bf77b9 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -1016,14 +1016,13 @@ const sem = grammar.createSemantics().addAttribute('ast', { try { JSON.parse(messageContent); } catch (error) { - console.error('Error validating ws message JSON:', error); return { body: { mode: 'ws', ws: [ { name: messageName, - content: '{}' + content: messageContent } ] }