diff --git a/packages/bruno-app/src/components/Icons/Grpc/index.js b/packages/bruno-app/src/components/Icons/Grpc/index.js
index cb10f810c..37b71c333 100644
--- a/packages/bruno-app/src/components/Icons/Grpc/index.js
+++ b/packages/bruno-app/src/components/Icons/Grpc/index.js
@@ -1,7 +1,7 @@
import React from 'react';
// UNARY - Single request, single response (Blue)
-export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
+export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '', color = '#3B82F6' }) => (
);
// CLIENT_STREAMING - Streaming request, single response (Purple)
-export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
+export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#8B5CF6' }) => (
);
// SERVER_STREAMING - Single request, streaming response (Green)
-export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
+export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#10B981' }) => (
);
// BIDI_STREAMING - Streaming request, streaming response (Orange)
-export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
+export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#F97316' }) => (
);
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/StyledWrapper.js
new file mode 100644
index 000000000..e952cae27
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/StyledWrapper.js
@@ -0,0 +1,128 @@
+import styled from 'styled-components';
+import { rgba } from 'polished';
+
+const StyledWrapper = styled.div`
+ .method-dropdown-container {
+ display: flex;
+ align-items: center;
+ height: 100%;
+ margin-right: 0.5rem;
+ }
+
+ .method-dropdown-trigger {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-left: 0.5rem;
+ cursor: pointer;
+ user-select: none;
+ }
+
+ .method-dropdown-trigger-icon {
+ margin-right: 0.5rem;
+ }
+
+ .method-dropdown-trigger-text {
+ font-size: ${(props) => props.theme.font.size.xs};
+ white-space: nowrap;
+ color: ${(props) => props.theme.dropdown.color};
+ }
+
+ .method-dropdown-caret {
+ margin-left: 0.25rem;
+ color: ${(props) => props.theme.colors.text.muted};
+ fill: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .method-dropdown-list {
+ max-height: 24rem;
+ overflow-y: auto;
+ width: 24rem;
+ min-width: 15rem;
+ }
+
+ .method-dropdown-service-group {
+ margin-bottom: 0.5rem;
+ }
+
+ .method-dropdown-service-header {
+ padding: 0.25rem 0.75rem;
+ font-weight: 500;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ background-color: ${(props) => props.theme.dropdown.separator};
+ color: ${(props) => props.theme.dropdown.color};
+ }
+
+ .method-dropdown-method-item {
+ padding: 0.5rem 0.75rem;
+ width: 100%;
+ border-left-width: 2px;
+ border-left-style: solid;
+ border-left-color: transparent;
+ transition: all 200ms;
+ position: relative;
+ cursor: pointer;
+
+ &:hover {
+ background-color: ${(props) => props.theme.dropdown.hoverBg};
+ }
+
+ &--selected {
+ border-left-color: ${(props) => props.theme.dropdown.selectedColor};
+ background-color: ${(props) => rgba(props.theme.dropdown.selectedColor, 0.2)};
+ }
+
+ &--focused {
+ background-color: ${(props) => props.theme.dropdown.hoverBg};
+ }
+ }
+
+ .method-dropdown-method-content {
+ display: flex;
+ align-items: center;
+ }
+
+ .method-dropdown-method-icon {
+ font-size: ${(props) => props.theme.font.size.xs};
+ margin-right: 0.75rem;
+ color: ${(props) => props.theme.dropdown.iconColor};
+ }
+
+ .method-dropdown-method-details {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ }
+
+ .method-dropdown-method-name {
+ font-weight: 500;
+ color: ${(props) => props.theme.dropdown.color};
+ }
+
+ .method-dropdown-method-type {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.dropdown.mutedText};
+ }
+
+ .method-dropdown-empty-state {
+ padding: 0.5rem 0.75rem;
+ width: 100%;
+ transition: all 200ms;
+ position: relative;
+ }
+
+ .method-dropdown-empty-state-text {
+ display: flex;
+ align-items: center;
+ font-size: ${(props) => props.theme.font.size.xs};
+ margin-right: 0.75rem;
+ color: ${(props) => props.theme.dropdown.mutedText};
+ }
+`;
+
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js
index 924eb1e71..3b7ba763d 100644
--- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js
+++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js
@@ -9,6 +9,8 @@ import {
import SearchInput from 'components/SearchInput/index';
import { search } from 'fast-fuzzy';
import React, { forwardRef, useEffect, useRef, useState } from 'react';
+import { useTheme } from 'providers/Theme';
+import StyledWrapper from './StyledWrapper';
const MethodDropdown = ({
grpcMethods,
@@ -16,6 +18,7 @@ const MethodDropdown = ({
onMethodSelect,
onMethodDropdownCreate
}) => {
+ const { theme } = useTheme();
const [searchText, setSearchText] = useState('');
const [focusedIndex, setFocusedIndex] = useState(-1);
const searchInputRef = useRef();
@@ -58,32 +61,26 @@ const MethodDropdown = ({
const getIconForMethodType = (type) => {
switch (type) {
case 'unary':
- return ;
+ return ;
case 'client-streaming':
- return ;
+ return ;
case 'server-streaming':
- return ;
+ return ;
case 'bidi-streaming':
- return ;
+ return ;
default:
- return ;
+ return ;
}
};
const MethodsDropdownIcon = forwardRef((props, ref) => {
return (
-
- {selectedGrpcMethod &&
{getIconForMethodType(selectedGrpcMethod.type)}
}
-
- {selectedGrpcMethod ? (
-
- {selectedGrpcMethod.path.split('.').at(-1) || selectedGrpcMethod.path}
-
- ) : (
- Select Method
- )}
+
+ {selectedGrpcMethod &&
{getIconForMethodType(selectedGrpcMethod.type)}
}
+
+ {selectedGrpcMethod ? (selectedGrpcMethod.path.split('.').at(-1) || selectedGrpcMethod.path) : 'Select Method'}
-
+
);
});
@@ -145,76 +142,75 @@ const MethodDropdown = ({
}
return (
-
-
} placement="bottom-end" style={{ maxWidth: 'unset' }} onShow={handleDropdownShow}>
-
-
- {Object.entries(groupedMethods).map(([serviceName, methods], serviceIndex) => (
-
-
- {serviceName || 'Default Service'}
-
-
- {methods.map((method, methodIndex) => {
- const globalMethodIndex
- = Object.values(groupedMethods)
- .slice(0, serviceIndex)
- .reduce((acc, group) => acc + group.length, 0) + methodIndex;
- return (
-
handleGrpcMethodSelect(method)}
- data-index={globalMethodIndex}
- data-testid="grpc-method-item"
- >
-
-
- {getIconForMethodType(method.type)}
-
-
-
- {method.methodName}
+
+
+
} placement="bottom-end" style={{ maxWidth: 'unset' }} onShow={handleDropdownShow}>
+
+
+ {Object.entries(groupedMethods).map(([serviceName, methods], serviceIndex) => (
+
+
+ {serviceName || 'Default Service'}
+
+
+ {methods.map((method, methodIndex) => {
+ const globalMethodIndex
+ = Object.values(groupedMethods)
+ .slice(0, serviceIndex)
+ .reduce((acc, group) => acc + group.length, 0) + methodIndex;
+ const isSelected = selectedGrpcMethod && selectedGrpcMethod.path === method.path;
+ const isFocused = focusedIndex === globalMethodIndex;
+ return (
+
handleGrpcMethodSelect(method)}
+ data-index={globalMethodIndex}
+ data-testid="grpc-method-item"
+ >
+
+
+ {getIconForMethodType(method.type)}
-
- {method.type}
+
+
+ {method.methodName}
+
+
+ {method.type}
+
-
- );
- })}
+ );
+ })}
+
-
- ))}
+ ))}
- {filteredMethods.length === 0 && (
-
-
-
+ {filteredMethods.length === 0 && (
+
+
No methods found for the search term
-
- )}
-
-
-
+ )}
+
+
+
+
);
};
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/StyledWrapper.js
new file mode 100644
index 000000000..082d4a67a
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/StyledWrapper.js
@@ -0,0 +1,68 @@
+import styled from 'styled-components';
+
+const StyledWrapper = styled.div`
+ .proto-file-dropdown-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ user-select: none;
+ }
+
+ .proto-file-dropdown-icon {
+ margin-right: 0.25rem;
+ color: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .proto-file-dropdown-text {
+ font-size: ${(props) => props.theme.font.size.xs};
+ white-space: nowrap;
+ color: ${(props) => props.theme.dropdown.color};
+ }
+
+ .proto-file-dropdown-caret {
+ margin-left: 0.25rem;
+ color: ${(props) => props.theme.colors.text.muted};
+ fill: ${(props) => props.theme.colors.text.muted};
+ }
+
+ .proto-file-dropdown-content {
+ max-height: fit-content;
+ overflow-y: auto;
+ width: 30rem;
+ }
+
+ .proto-file-dropdown-mode-section {
+ padding: 0.5rem 0.75rem;
+ border-bottom: 1px solid ${(props) => props.theme.border.border1};
+ }
+
+ .proto-file-dropdown-mode-controls {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ .proto-file-dropdown-mode-options {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ }
+
+ .proto-file-dropdown-mode-option {
+ font-size: ${(props) => props.theme.font.size.xs};
+ color: ${(props) => props.theme.colors.text.muted};
+
+ &--active {
+ font-weight: 500;
+ }
+ }
+
+ .proto-file-dropdown-reflection-message {
+ padding: 0.5rem 0.75rem;
+ color: ${(props) => props.theme.overlay.overlay1};
+ margin-bottom: 0.5rem;
+ }
+`;
+
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js
index 5e1983e54..d507d238e 100644
--- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js
+++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js
@@ -10,6 +10,7 @@ import Dropdown from 'components/Dropdown/index';
import ToggleSwitch from 'components/ToggleSwitch/index';
import { TabNavigation, ProtoFilesTab, ImportPathsTab } from '../Tabs';
import useProtoFileManagement from 'hooks/useProtoFileManagement/index';
+import StyledWrapper from './StyledWrapper';
const ProtoFileDropdown = ({
collection,
@@ -121,96 +122,95 @@ const ProtoFileDropdown = ({
const ProtoFileDropdownIcon = forwardRef((props, ref) => {
return (
-
setShowProtoDropdown((prev) => !prev)} data-testid="grpc-proto-file-dropdown-icon">
- {isReflectionMode ? (<>>
- ) : (
-
+
setShowProtoDropdown((prev) => !prev)} data-testid="grpc-proto-file-dropdown-icon">
+ {!isReflectionMode && (
+
)}
-
+
{isReflectionMode ? 'Using Reflection' : (protoFilePath ? getBasename(collection.pathname, protoFilePath) : 'Select Proto File')}
-
+
);
});
return (
-
-
}
- placement="bottom-end"
- visible={showProtoDropdown}
- onClickOutside={() => setShowProtoDropdown(false)}
- data-testid="grpc-proto-file-dropdown"
- >
-
-
-
-
Mode
-
-
- Proto File
-
-
-
- Reflection
-
+
+
+
}
+ placement="bottom-end"
+ visible={showProtoDropdown}
+ onClickOutside={() => setShowProtoDropdown(false)}
+ data-testid="grpc-proto-file-dropdown"
+ >
+
+
+
+
Mode
+
+
+ Proto File
+
+
+
+ Reflection
+
+
-
- {!isReflectionMode && (
-
- )}
+ {!isReflectionMode && (
+
+ )}
- {!isReflectionMode && (
- <>
- {activeTab === 'protofiles' && (
-
- )}
+ {!isReflectionMode && (
+ <>
+ {activeTab === 'protofiles' && (
+
+ )}
- {activeTab === 'importpaths' && (
-
- )}
- >
- )}
+ {activeTab === 'importpaths' && (
+
+ )}
+ >
+ )}
- {isReflectionMode && (
-
-
+ {isReflectionMode && (
+
Using server reflection to discover gRPC methods.
-
- )}
-
-
-
+ )}
+
+
+
+
);
};
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js
index f2a21a891..31926e65d 100644
--- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js
+++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js
@@ -99,6 +99,7 @@ const Wrapper = styled.div`
margin-right: 0.5rem;
cursor: pointer;
color: ${(props) => props.theme.grpc.importPaths.item.checkbox.color};
+ accent-color: ${(props) => props.theme.colors.accent};
}
.item-text {
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js
index 9d5314f1b..7aab9761b 100644
--- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js
+++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js
@@ -2,7 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
.tab-container {
- background-color: ${(props) => props.theme.grpc.tabNav.container.bg};
+ background-color: ${(props) => props.theme.dropdown.separator};
}
.tab-button {
background-color: ${(props) => props.theme.grpc.tabNav.button.inactive.bg};
diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js
index 30b37842d..3f521d1bc 100644
--- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js
+++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js
@@ -297,7 +297,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
index 0eeab0d9a..fb4ce8dc4 100644
--- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
+++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js
@@ -32,7 +32,6 @@ const QueryUrl = ({ item, collection, handleRun }) => {
const isMac = isMacOS();
const saveShortcut = isMac ? 'Cmd + S' : 'Ctrl + S';
const editorRef = useRef(null);
- const isGrpc = item.type === 'grpc-request';
const isLoading = ['queued', 'sending'].includes(item.requestState);
const [generateCodeItemModalOpen, setGenerateCodeItemModalOpen] = useState(false);
@@ -372,13 +371,7 @@ const QueryUrl = ({ item, collection, handleRun }) => {
return (
- {isGrpc ? (
-
- gRPC
-
- ) : (
-
- )}
+