use themes within grpc (#6568)

* rm: redundant code

* add: theme to grpc

* keep same color for grpc as that of sidebar

* fix: checkbox color

* fix: use dropdown colors

* rm: redundant lines

* use: theme for grpc icons
This commit is contained in:
sanish chirayath
2025-12-31 00:34:33 +05:30
committed by GitHub
parent 1c9db0886d
commit ab04850367
10 changed files with 377 additions and 190 deletions

View File

@@ -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' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -14,16 +14,16 @@ export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '' })
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right */}
<path d="M3 8h18" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left */}
<path d="M21 16h-18" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);
// 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' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -36,17 +36,17 @@ export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, classNam
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right with double heads */}
<path d="M3 8h18" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left */}
<path d="M21 16h-18" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);
// 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' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -59,17 +59,17 @@ export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, classNam
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right */}
<path d="M3 8h18" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left with double heads */}
<path d="M21 16h-18" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);
// 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' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -82,12 +82,12 @@ export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right with double heads */}
<path d="M3 8h18" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left with double heads */}
<path d="M21 16h-18" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);

View File

@@ -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;

View File

@@ -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 <IconGrpcUnary size={20} strokeWidth={2} />;
return <IconGrpcUnary size={20} strokeWidth={2} color={theme.request.methods.get} />;
case 'client-streaming':
return <IconGrpcClientStreaming size={20} strokeWidth={2} />;
return <IconGrpcClientStreaming size={20} strokeWidth={2} color={theme.request.methods.post} />;
case 'server-streaming':
return <IconGrpcServerStreaming size={20} strokeWidth={2} />;
return <IconGrpcServerStreaming size={20} strokeWidth={2} color={theme.request.methods.put} />;
case 'bidi-streaming':
return <IconGrpcBidiStreaming size={20} strokeWidth={2} />;
return <IconGrpcBidiStreaming size={20} strokeWidth={2} color={theme.colors.text.purple} />;
default:
return <IconGrpcUnary size={20} strokeWidth={2} />;
return <IconGrpcUnary size={20} strokeWidth={2} color={theme.request.methods.get} />;
}
};
const MethodsDropdownIcon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-center ml-2 cursor-pointer select-none" data-testid="grpc-method-dropdown-trigger">
{selectedGrpcMethod && <div className="mr-2">{getIconForMethodType(selectedGrpcMethod.type)}</div>}
<span className="text-xs">
{selectedGrpcMethod ? (
<span className="dark:text-neutral-300 text-neutral-700 text-nowrap" data-testid="selected-grpc-method-name">
{selectedGrpcMethod.path.split('.').at(-1) || selectedGrpcMethod.path}
</span>
) : (
<span className="dark:text-neutral-300 text-neutral-700 text-nowrap">Select Method </span>
)}
<div ref={ref} className="method-dropdown-trigger" data-testid="grpc-method-dropdown-trigger">
{selectedGrpcMethod && <div className="method-dropdown-trigger-icon">{getIconForMethodType(selectedGrpcMethod.type)}</div>}
<span className="method-dropdown-trigger-text" data-testid="selected-grpc-method-name">
{selectedGrpcMethod ? (selectedGrpcMethod.path.split('.').at(-1) || selectedGrpcMethod.path) : 'Select Method'}
</span>
<IconChevronDown className="caret ml-1" size={14} strokeWidth={2} />
<IconChevronDown className="method-dropdown-caret" size={14} strokeWidth={2} />
</div>
);
});
@@ -145,76 +142,75 @@ const MethodDropdown = ({
}
return (
<div className="flex items-center h-full mr-2" data-testid="grpc-methods-dropdown">
<Dropdown onCreate={onMethodDropdownCreate} icon={<MethodsDropdownIcon />} placement="bottom-end" style={{ maxWidth: 'unset' }} onShow={handleDropdownShow}>
<SearchInput
searchText={searchText}
setSearchText={setSearchText}
placeholder="Search"
ref={searchInputRef}
onKeyDown={handleKeyDown}
onBlur={focusSearchInput}
onChange={handleSearchChange}
className="mt-2 mb-3 "
data-testid="grpc-methods-search-input"
/>
<div ref={listRef} className="max-h-96 overflow-y-auto w-96 min-w-60" data-testid="grpc-methods-list">
{Object.entries(groupedMethods).map(([serviceName, methods], serviceIndex) => (
<div key={serviceIndex} className="service-group mb-2" onKeyDown={handleKeyDown} tabIndex={0}>
<div className="service-header px-3 py-1 bg-neutral-100 dark:bg-neutral-800 font-medium truncate sticky top-0 z-10">
{serviceName || 'Default Service'}
</div>
<div className="service-methods">
{methods.map((method, methodIndex) => {
const globalMethodIndex
= Object.values(groupedMethods)
.slice(0, serviceIndex)
.reduce((acc, group) => acc + group.length, 0) + methodIndex;
return (
<div
key={`${serviceIndex}-${methodIndex}`}
className={`py-2 px-3 w-full border-l-2 transition-all duration-200 relative group ${
selectedGrpcMethod && selectedGrpcMethod.path === method.path
? 'border-yellow-500 bg-yellow-500/20 dark:bg-yellow-900/20'
: 'border-transparent hover:bg-black/5 dark:hover:bg-white/5'
} ${focusedIndex === globalMethodIndex
? 'bg-black/5 dark:bg-white/5' : ''}`}
onClick={() => handleGrpcMethodSelect(method)}
data-index={globalMethodIndex}
data-testid="grpc-method-item"
>
<div className="flex items-center">
<div className="text-xs mr-3 text-gray-500">
{getIconForMethodType(method.type)}
</div>
<div className="flex flex-col flex-1">
<div className="font-medium text-gray-900 dark:text-gray-100">
{method.methodName}
<StyledWrapper>
<div className="method-dropdown-container" data-testid="grpc-methods-dropdown">
<Dropdown onCreate={onMethodDropdownCreate} icon={<MethodsDropdownIcon />} placement="bottom-end" style={{ maxWidth: 'unset' }} onShow={handleDropdownShow}>
<SearchInput
searchText={searchText}
setSearchText={setSearchText}
placeholder="Search"
ref={searchInputRef}
onKeyDown={handleKeyDown}
onBlur={focusSearchInput}
onChange={handleSearchChange}
className="mt-2 mb-3 "
data-testid="grpc-methods-search-input"
/>
<div ref={listRef} className="method-dropdown-list" data-testid="grpc-methods-list">
{Object.entries(groupedMethods).map(([serviceName, methods], serviceIndex) => (
<div key={serviceIndex} className="method-dropdown-service-group" onKeyDown={handleKeyDown} tabIndex={0}>
<div className="method-dropdown-service-header">
{serviceName || 'Default Service'}
</div>
<div>
{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 (
<div
key={`${serviceIndex}-${methodIndex}`}
className={`method-dropdown-method-item ${
isSelected ? 'method-dropdown-method-item--selected' : ''
} ${isFocused ? 'method-dropdown-method-item--focused' : ''}`}
onClick={() => handleGrpcMethodSelect(method)}
data-index={globalMethodIndex}
data-testid="grpc-method-item"
>
<div className="method-dropdown-method-content">
<div className="method-dropdown-method-icon">
{getIconForMethodType(method.type)}
</div>
<div className="text-xs text-gray-500">
{method.type}
<div className="method-dropdown-method-details">
<div className="method-dropdown-method-name">
{method.methodName}
</div>
<div className="method-dropdown-method-type">
{method.type}
</div>
</div>
</div>
</div>
</div>
);
})}
);
})}
</div>
</div>
</div>
))}
))}
{filteredMethods.length === 0 && (
<div className="py-2 px-3 w-full transition-all duration-200 relative group">
<div className="flex items-center">
<div className="text-xs mr-3 text-gray-500">
{filteredMethods.length === 0 && (
<div className="method-dropdown-empty-state">
<div className="method-dropdown-empty-state-text">
No methods found for the search term
</div>
</div>
</div>
)}
</div>
</Dropdown>
</div>
)}
</div>
</Dropdown>
</div>
</StyledWrapper>
);
};

View File

@@ -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;

View File

@@ -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 (
<div ref={ref} className="flex items-center justify-center cursor-pointer select-none" onClick={() => setShowProtoDropdown((prev) => !prev)} data-testid="grpc-proto-file-dropdown-icon">
{isReflectionMode ? (<></>
) : (
<IconFile size={20} strokeWidth={1.5} className="mr-1 text-neutral-400" />
<div ref={ref} className="proto-file-dropdown-container" onClick={() => setShowProtoDropdown((prev) => !prev)} data-testid="grpc-proto-file-dropdown-icon">
{!isReflectionMode && (
<IconFile size={20} strokeWidth={1.5} className="proto-file-dropdown-icon" />
)}
<span className="text-xs dark:text-neutral-300 text-neutral-700 text-nowrap">
<span className="proto-file-dropdown-text">
{isReflectionMode ? 'Using Reflection' : (protoFilePath ? getBasename(collection.pathname, protoFilePath) : 'Select Proto File')}
</span>
<IconChevronDown className="caret ml-1" size={14} strokeWidth={2} />
<IconChevronDown className="proto-file-dropdown-caret" size={14} strokeWidth={2} />
</div>
);
});
return (
<div className="proto-file-dropdown">
<Dropdown
onCreate={onProtoDropdownCreate}
icon={<ProtoFileDropdownIcon />}
placement="bottom-end"
visible={showProtoDropdown}
onClickOutside={() => setShowProtoDropdown(false)}
data-testid="grpc-proto-file-dropdown"
>
<div className="max-h-fit overflow-y-auto w-[30rem]">
<div className="px-3 py-2 border-b border-neutral-200 dark:border-neutral-700" data-testid="grpc-mode-toggle">
<div className="flex items-center justify-between">
<span>Mode</span>
<div className="flex items-center gap-2">
<span className={`text-xs ${!isReflectionMode ? 'font-medium' : 'text-neutral-500'}`} style={{ color: !isReflectionMode ? theme.colors.text.yellow : undefined }}>
Proto File
</span>
<ToggleSwitch
isOn={isReflectionMode}
handleToggle={onReflectionModeToggle}
size="2xs"
activeColor={theme.colors.text.yellow}
/>
<span className={`text-xs ${isReflectionMode ? 'font-medium' : 'text-neutral-500'}`} style={{ color: isReflectionMode ? theme.colors.text.yellow : undefined }}>
Reflection
</span>
<StyledWrapper>
<div className="proto-file-dropdown">
<Dropdown
onCreate={onProtoDropdownCreate}
icon={<ProtoFileDropdownIcon />}
placement="bottom-end"
visible={showProtoDropdown}
onClickOutside={() => setShowProtoDropdown(false)}
data-testid="grpc-proto-file-dropdown"
>
<div className="proto-file-dropdown-content">
<div className="proto-file-dropdown-mode-section" data-testid="grpc-mode-toggle">
<div className="proto-file-dropdown-mode-controls">
<span>Mode</span>
<div className="proto-file-dropdown-mode-options">
<span className={`proto-file-dropdown-mode-option ${!isReflectionMode ? 'proto-file-dropdown-mode-option--active' : ''}`} style={{ color: !isReflectionMode ? theme.primary.solid : undefined }}>
Proto File
</span>
<ToggleSwitch
isOn={isReflectionMode}
handleToggle={onReflectionModeToggle}
size="2xs"
activeColor={theme.primary.solid}
/>
<span className={`proto-file-dropdown-mode-option ${isReflectionMode ? 'proto-file-dropdown-mode-option--active' : ''}`} style={{ color: isReflectionMode ? theme.primary.solid : undefined }}>
Reflection
</span>
</div>
</div>
</div>
</div>
{!isReflectionMode && (
<TabNavigation
activeTab={activeTab}
onTabChange={setActiveTab}
collectionProtoFiles={protoFileManagement.protoFiles}
collectionImportPaths={protoFileManagement.importPaths}
/>
)}
{!isReflectionMode && (
<TabNavigation
activeTab={activeTab}
onTabChange={setActiveTab}
collectionProtoFiles={protoFileManagement.protoFiles}
collectionImportPaths={protoFileManagement.importPaths}
/>
)}
{!isReflectionMode && (
<>
{activeTab === 'protofiles' && (
<ProtoFilesTab
collectionProtoFiles={protoFileManagement.protoFiles}
invalidProtoFiles={invalidProtoFiles}
protoFilePath={protoFilePath}
collection={collection}
onSelectCollectionProtoFile={handleSelectCollectionProtoFile}
onOpenCollectionProtobufSettings={handleOpenCollectionProtobufSettings}
onSelectProtoFile={handleSelectProtoFile}
setShowProtoDropdown={setShowProtoDropdown}
/>
)}
{!isReflectionMode && (
<>
{activeTab === 'protofiles' && (
<ProtoFilesTab
collectionProtoFiles={protoFileManagement.protoFiles}
invalidProtoFiles={invalidProtoFiles}
protoFilePath={protoFilePath}
collection={collection}
onSelectCollectionProtoFile={handleSelectCollectionProtoFile}
onOpenCollectionProtobufSettings={handleOpenCollectionProtobufSettings}
onSelectProtoFile={handleSelectProtoFile}
setShowProtoDropdown={setShowProtoDropdown}
/>
)}
{activeTab === 'importpaths' && (
<ImportPathsTab
collectionImportPaths={protoFileManagement.importPaths}
invalidImportPaths={invalidImportPaths}
onOpenCollectionProtobufSettings={handleOpenCollectionProtobufSettings}
onBrowseImportPath={handleBrowseImportPath}
onToggleImportPath={handleToggleImportPath}
/>
)}
</>
)}
{activeTab === 'importpaths' && (
<ImportPathsTab
collectionImportPaths={protoFileManagement.importPaths}
invalidImportPaths={invalidImportPaths}
onOpenCollectionProtobufSettings={handleOpenCollectionProtobufSettings}
onBrowseImportPath={handleBrowseImportPath}
onToggleImportPath={handleToggleImportPath}
/>
)}
</>
)}
{isReflectionMode && (
<div className="px-3 py-2">
<div className="text-neutral-600 dark:text-neutral-400 mb-2">
{isReflectionMode && (
<div className="proto-file-dropdown-reflection-message">
Using server reflection to discover gRPC methods.
</div>
</div>
)}
</div>
</Dropdown>
</div>
)}
</div>
</Dropdown>
</div>
</StyledWrapper>
);
};

View File

@@ -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 {

View File

@@ -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};

View File

@@ -297,7 +297,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
<StyledWrapper className="flex items-center relative" data-testid="grpc-query-url-container">
<div className="flex items-center h-full method-selector-container">
<div className="flex items-center justify-center h-full w-16" data-testid="grpc-method-indicator">
<span className="text-xs text-indigo-500 font-bold">gRPC</span>
<span className="text-xs font-bold" style={{ color: theme.request.grpc }}>gRPC</span>
</div>
</div>
<div className="flex items-center w-full input-container h-full relative">

View File

@@ -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 (
<StyledWrapper className="flex items-center w-full">
<div className="flex items-center h-full min-w-fit">
{isGrpc ? (
<div className="flex items-center justify-center h-full w-16">
<span className="text-xs text-indigo-500 font-bold">gRPC</span>
</div>
) : (
<HttpMethodSelector method={method} onMethodSelect={onMethodSelect} />
)}
<HttpMethodSelector method={method} onMethodSelect={onMethodSelect} />
</div>
<div
id="request-url"

View File

@@ -1,5 +1,6 @@
// VS Code Light+ Theme for Bruno
// Based on the default Visual Studio Code Light+ theme
import { rgba } from 'polished';
const colors = {
// VS Code Light+ Core Colors
@@ -488,12 +489,12 @@ const vscodeLightTheme = {
text: colors.RED,
link: {
color: colors.RED,
hoverColor: '#e03e3e'
hoverColor: colors.RED
}
},
item: {
bg: 'transparent',
hoverBg: 'rgba(0, 0, 0, 0.05)',
hoverBg: rgba(colors.BLACK, 0.05),
text: colors.TEXT,
icon: colors.TEXT_MUTED,
checkbox: {
@@ -527,14 +528,14 @@ const vscodeLightTheme = {
text: colors.RED,
link: {
color: colors.RED,
hoverColor: '#e03e3e'
hoverColor: colors.RED
}
},
item: {
bg: 'transparent',
hoverBg: 'rgba(0, 0, 0, 0.05)',
hoverBg: rgba(colors.BLACK, 0.05),
selected: {
bg: 'rgba(0, 122, 204, 0.15)',
bg: rgba(colors.BRAND, 0.15),
border: colors.BRAND
},
text: colors.TEXT,