diff --git a/package-lock.json b/package-lock.json
index 5a2b2a8e0..4ef0ae308 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26860,6 +26860,7 @@
"graphiql": "3.7.1",
"graphql": "^16.6.0",
"graphql-request": "^3.7.0",
+ "hexy": "^0.3.5",
"httpsnippet": "^3.0.9",
"i18next": "24.1.2",
"idb": "^7.0.0",
diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json
index a55bb9f64..1959a15dc 100644
--- a/packages/bruno-app/package.json
+++ b/packages/bruno-app/package.json
@@ -37,6 +37,7 @@
"graphiql": "3.7.1",
"graphql": "^16.6.0",
"graphql-request": "^3.7.0",
+ "hexy": "^0.3.5",
"httpsnippet": "^3.0.9",
"i18next": "24.1.2",
"idb": "^7.0.0",
diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
index cfbe77f58..a1d7dd86a 100644
--- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
+++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
@@ -41,9 +41,9 @@ const QueryUrl = ({ item, collection, handleRun }) => {
if (!editorRef.current?.editor) return;
const editor = editorRef.current.editor;
const cursor = editor.getCursor();
-
+
const finalUrl = value?.trim() ?? value;
-
+
dispatch(
requestUrlChanged({
itemUid: item.uid,
@@ -51,7 +51,7 @@ const QueryUrl = ({ item, collection, handleRun }) => {
url: finalUrl
})
);
-
+
// Restore cursor position only if URL was trimmed
if (finalUrl !== value) {
setTimeout(() => {
@@ -81,7 +81,9 @@ const QueryUrl = ({ item, collection, handleRun }) => {
}
};
- const handleCancelRequest = () => {
+ const handleCancelRequest = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
dispatch(cancelRequest(item.cancelTokenUid, item, collection));
};
@@ -92,7 +94,6 @@ const QueryUrl = ({ item, collection, handleRun }) => {
gRPC
-
) : (
)}
@@ -126,15 +127,8 @@ const QueryUrl = ({ item, collection, handleRun }) => {
handleGenerateCode(e);
}}
>
-
-
- Generate Code
-
+
+ Generate Code
{
Save ({saveShortcut})
-
- {isLoading ? (
+ {isLoading || item.response?.stream?.running ? (
{
{generateCodeItemModalOpen && (
- setGenerateCodeItemModalOpen(false)} />
+ setGenerateCodeItemModalOpen(false)}
+ />
)}
);
diff --git a/packages/bruno-app/src/components/RequestTabPanel/index.js b/packages/bruno-app/src/components/RequestTabPanel/index.js
index f7880e509..f8d3aba83 100644
--- a/packages/bruno-app/src/components/RequestTabPanel/index.js
+++ b/packages/bruno-app/src/components/RequestTabPanel/index.js
@@ -10,7 +10,7 @@ import GrpcResponsePane from 'components/ResponsePane/GrpcResponsePane';
import Welcome from 'components/Welcome';
import { findItemInCollection } from 'utils/collections';
import { updateRequestPaneTabWidth } from 'providers/ReduxStore/slices/tabs';
-import { sendRequest } from 'providers/ReduxStore/slices/collections/actions';
+import { cancelRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions';
import RequestNotFound from './RequestNotFound';
import QueryUrl from 'components/RequestPane/QueryUrl/index';
import GrpcQueryUrl from 'components/RequestPane/GrpcQueryUrl/index';
@@ -263,11 +263,17 @@ const RequestTabPanel = () => {
return;
}
- dispatch(sendRequest(item, collection.uid)).catch((err) =>
- toast.custom((t) => toast.dismiss(t.id)} />, {
- duration: 5000
- })
- );
+ if (item.response?.stream?.running) {
+ dispatch(cancelRequest(item.cancelTokenUid, item, collection)).catch((err) =>
+ toast.custom((t) => toast.dismiss(t.id)} />, {
+ duration: 5000
+ }));
+ } else if (item.requestState !== 'sending' && item.requestState !== 'queued') {
+ dispatch(sendRequest(item, collection.uid)).catch((err) =>
+ toast.custom((t) => toast.dismiss(t.id)} />, {
+ duration: 5000
+ }));
+ }
};
// TODO: reaper, improve selection of panes
diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js b/packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js
index b00440c3a..9efe51f2e 100644
--- a/packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/ResponseBookmark/index.js
@@ -12,12 +12,25 @@ import { getInitialExampleName } from 'utils/collections/index';
import classnames from 'classnames';
import StyledWrapper from './StyledWrapper';
+const getTitleText = ({ isResponseTooLarge, isStreamingResponse }) => {
+ if (isStreamingResponse) {
+ return 'Response Examples aren\'t supported in streaming responses yet.';
+ }
+
+ if (isResponseTooLarge) {
+ return 'Response size exceeds 5MB limit. Cannot save as example.';
+ }
+
+ return 'Save current response as example';
+};
+
const ResponseBookmark = ({ item, collection, responseSize }) => {
const dispatch = useDispatch();
const [showSaveResponseExampleModal, setShowSaveResponseExampleModal] = useState(false);
const response = item.response || {};
const isResponseTooLarge = responseSize >= 5 * 1024 * 1024; // 5 MB
+ const isStreamingResponse = response.stream;
// Only show for HTTP requests
if (item.type !== 'http-request') {
@@ -96,19 +109,22 @@ const ResponseBookmark = ({ item, collection, responseSize }) => {
toast.success(`Example "${name}" created successfully`);
};
+ const disabledMessage = getTitleText({
+ isResponseTooLarge,
+ isStreamingResponse
+ });
+
return (
<>