Merge branch 'feature/dark-mode'

This commit is contained in:
Anoop M D 2022-10-23 12:37:20 +05:30
commit ef28637d0c
82 changed files with 1111 additions and 312 deletions

View File

@ -6,6 +6,8 @@
"paths": {
"assets/*": ["src/assets/*"],
"components/*": ["src/components/*"],
"hooks/*": ["src/hooks/*"],
"themes/*": ["src/themes/*"],
"api/*": ["src/api/*"],
"pageComponents/*": ["src/pageComponents/*"],
"providers/*": ["src/providers/*"],

View File

@ -0,0 +1,30 @@
const darkTheme = {
brand: '#546de5',
text: 'rgb(52 52 52)',
'primary-text': '#ffffff',
'primary-theme': '#1e1e1e',
'secondary-text': '#929292',
'sidebar-collection-item-active-indent-border': '#d0d0d0',
'sidebar-collection-item-active-background': '#e1e1e1',
'sidebar-background': '#252526',
'sidebar-bottom-bg': '#68217a',
'request-dragbar-background': '#efefef',
'request-dragbar-background-active': 'rgb(200, 200, 200)',
'tab-inactive': 'rgb(155 155 155)',
'tab-active-border': '#546de5',
'layout-border': '#dedede',
'codemirror-border': '#efefef',
'codemirror-background': 'rgb(243, 243, 243)',
'text-link': '#1663bb',
'text-danger': 'rgb(185, 28, 28)',
'background-danger': '#dc3545',
'method-get': 'rgb(5, 150, 105)',
'method-post': '#8e44ad',
'method-delete': 'rgb(185, 28, 28)',
'method-patch': 'rgb(52 52 52)',
'method-options': 'rgb(52 52 52)',
'method-head': 'rgb(52 52 52)',
'table-stripe': '#f3f3f3'
};
export default darkTheme;

View File

@ -0,0 +1,7 @@
import darkTheme from './dark';
import lightTheme from './light';
export default {
Light: lightTheme,
Dark: darkTheme
};

View File

@ -0,0 +1,30 @@
const lightTheme = {
brand: '#546de5',
text: 'rgb(52 52 52)',
'primary-text': 'rgb(52 52 52)',
'primary-theme': '#ffffff',
'secondary-text': '#929292',
'sidebar-collection-item-active-indent-border': '#d0d0d0',
'sidebar-collection-item-active-background': '#e1e1e1',
'sidebar-background': '#f3f3f3',
'sidebar-bottom-bg': '#f3f3f3',
'request-dragbar-background': '#efefef',
'request-dragbar-background-active': 'rgb(200, 200, 200)',
'tab-inactive': 'rgb(155 155 155)',
'tab-active-border': '#546de5',
'layout-border': '#dedede',
'codemirror-border': '#efefef',
'codemirror-background': 'rgb(243, 243, 243)',
'text-link': '#1663bb',
'text-danger': 'rgb(185, 28, 28)',
'background-danger': '#dc3545',
'method-get': 'rgb(5, 150, 105)',
'method-post': '#8e44ad',
'method-delete': 'rgb(185, 28, 28)',
'method-patch': 'rgb(52 52 52)',
'method-options': 'rgb(52 52 52)',
'method-head': 'rgb(52 52 52)',
'table-stripe': '#f3f3f3'
};
export default lightTheme;

View File

@ -1,7 +1,7 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
color: var(--color-text);
color: ${(props) => props.theme.text};
.collection-options {
svg {
position: relative;

View File

@ -1,8 +1,7 @@
import React from 'react';
import Modal from 'components/Modal/index';
import { IconSpeakerphone, IconBrandTwitter } from '@tabler/icons';
import { IconSpeakerphone, IconBrandTwitter, IconBrandGithub } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
import GithubSvg from 'assets/github.svg';
const BrunoSupport = ({ onClose }) => {
return (
@ -10,19 +9,19 @@ const BrunoSupport = ({ onClose }) => {
<Modal size="sm" title={'Support'} handleCancel={onClose} hideFooter={true}>
<div className="collection-options">
<div className="mt-2">
<a href="https://github.com/usebruno/bruno/issues" target="_blank" className="flex items-center">
<a href="https://github.com/usebruno/bruno/issues" target="_blank" className="flex items-end">
<IconSpeakerphone size={18} strokeWidth={2} />
<span className="label ml-2">Report Issues</span>
</a>
</div>
<div className="mt-2">
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-center">
<img src={GithubSvg.src} style={{ width: '18px' }} />
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-end">
<IconBrandGithub size={18} strokeWidth={2} />
<span className="label ml-2">Github</span>
</a>
</div>
<div className="mt-2">
<a href="https://twitter.com/use_bruno" target="_blank" className="flex items-center">
<a href="https://twitter.com/use_bruno" target="_blank" className="flex items-end">
<IconBrandTwitter size={18} strokeWidth={2} />
<span className="label ml-2">Twitter</span>
</a>

View File

@ -2,12 +2,31 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
div.CodeMirror {
border: solid 1px var(--color-codemirror-border);
background: ${(props) => props.theme.codemirror.bg};
border: solid 1px ${(props) => props.theme.codemirror.border};
}
textarea.cm-editor {
position: relative;
}
// Todo: dark mode temporary fix
// Clean this
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {
color: #9cdcfe !important;
}
.cm-s-monokai span.cm-string {
color: #ce9178 !important;
}
.cm-s-monokai span.cm-number{
color: #b5cea8 !important;
}
.cm-s-monokai span.cm-atom{
color: #569cd6 !important;
}
`;
export default StyledWrapper;

View File

@ -39,6 +39,7 @@ export default class QueryEditor extends React.Component {
foldGutter: true,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
readOnly: this.props.readOnly ? 'nocursor' : false,
theme: this.props.theme === 'dark' ? 'monokai' : 'default',
extraKeys: {
'Cmd-Enter': () => {
if (this.props.onRun) {
@ -87,6 +88,12 @@ export default class QueryEditor extends React.Component {
this.editor.setValue(this.props.value);
this.editor.setOption('mode', this.props.mode);
}
if (this.props.theme !== prevProps.theme && this.editor) {
this.cachedValue = this.props.value;
this.editor.setValue(this.props.value);
this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default');
}
this.ignoreChangeEvent = false;
}

View File

@ -9,11 +9,11 @@ const Wrapper = styled.div`
.tippy-box {
min-width: 135px;
background-color: white;
font-size: 0.8125rem;
color: rgb(48 48 48);
background: #fff;
box-shadow: rgb(50 50 93 / 25%) 0px 6px 12px -2px, rgb(0 0 0 / 30%) 0px 3px 7px -3px;
color: ${(props) => props.theme.dropdown.color};
background-color: ${(props) => props.theme.dropdown.bg};
box-shadow: ${(props) => props.theme.dropdown.shadow};
border-radius: 3px;
.tippy-content {
padding-left: 0;
@ -25,6 +25,7 @@ const Wrapper = styled.div`
display: flex;
align-items: center;
padding: 0.35rem 0.6rem;
background-color: ${(props) => props.theme.dropdown.labelBg};
}
.dropdown-item {
@ -33,8 +34,16 @@ const Wrapper = styled.div`
padding: 0.35rem 0.6rem;
cursor: pointer;
.icon {
color: ${(props) => props.theme.dropdown.iconColor};
}
&:hover {
background-color: #e9e9e9;
background-color: ${(props) => props.theme.dropdown.hoverBg};
}
&.border-top {
border-top: solid 1px ${(props) => props.theme.dropdown.seperator};
}
}
}

View File

@ -2,7 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
.current-enviroment {
background: #efefef;
background-color: ${(props) => props.theme.sidebar.workspace.bg};
border-radius: 15px;
.caret {

View File

@ -65,7 +65,7 @@ const EnvironmentSelector = ({ collection }) => {
>
<span>No Environment</span>
</div>
<div className="dropdown-item" style={{ borderTop: 'solid 1px #e7e7e7' }} onClick={() => setOpenSettingsModal(true)}>
<div className="dropdown-item border-top" onClick={() => setOpenSettingsModal(true)}>
<div className="pr-2 text-gray-600">
<IconSettings size={18} strokeWidth={1.5} />
</div>

View File

@ -8,11 +8,11 @@ const Wrapper = styled.div`
thead,
td {
border: 1px solid #efefef;
border: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder};
}
thead {
color: #616161;
color: ${(props) => props.theme.table.thead.color};;
font-size: 0.8125rem;
user-select: none;
}
@ -29,6 +29,7 @@ const Wrapper = styled.div`
width: 100%;
border: solid 1px transparent;
outline: none !important;
background-color: transparent;
&:focus {
outline: none !important;

View File

@ -4,8 +4,11 @@ const StyledWrapper = styled.div`
margin-inline: -1rem;
margin-block: -1.5rem;
background-color: ${(props) => props.theme.collection.environment.settings.bg};
.environments-sidebar {
background-color: #eaeaea;
background-color: ${(props) => props.theme.collection.environment.settings.sidebar.bg};
border-right: solid 1px ${(props) => props.theme.collection.environment.settings.sidebar.borderRight};
min-height: 400px;
}
@ -20,15 +23,15 @@ const StyledWrapper = styled.div`
&:hover {
text-decoration: none;
background-color: #e4e4e4;
background-color: ${(props) => props.theme.collection.environment.settings.item.hoverBg};
}
}
.active {
background-color: #dcdcdc !important;
border-left: solid 2px var(--color-brand);
background-color: ${(props) => props.theme.collection.environment.settings.item.active.bg} !important;
border-left: solid 2px ${(props) => props.theme.collection.environment.settings.item.border};
&:hover {
background-color: #dcdcdc !important;
background-color: ${(props) => props.theme.collection.environment.settings.item.active.hoverBg} !important;
}
}
@ -36,7 +39,7 @@ const StyledWrapper = styled.div`
padding: 8px 10px;
cursor: pointer;
border-bottom: none;
color: var(--color-text-link);
color: ${(props) => props.theme.textLink};
&:hover {
span {

View File

@ -9,8 +9,8 @@ const EnvironmentList = ({ collection }) => {
const [openCreateModal, setOpenCreateModal] = useState(false);
useEffect(() => {
setSelectedEnvironment(environments[0]);
}, []);
setSelectedEnvironment(environments && environments.length ? environments[0] : null);
}, [environments]);
if (!selectedEnvironment) {
return null;

View File

@ -0,0 +1,17 @@
import React from 'react';
const SendIcon = ({color, width}) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width}
height={width}
viewBox="0 0 48 48"
>
<path fill={color} d="M4.02 42l41.98-18-41.98-18-.02 14 30 4-30 4z"/>
<path d="M0 0h48v48h-48z" fill="none"/>
</svg>
);
}
export default SendIcon;

View File

@ -66,8 +66,8 @@ const Wrapper = styled.div`
justify-content: space-between;
align-items: center;
text-transform: uppercase;
color: rgb(86 86 86);
background-color: #f1f1f1;
color: ${(props) => props.theme.modal.title.color};
background-color: ${(props) => props.theme.modal.title.bg};
font-size: 0.75rem;
padding: 12px;
font-weight: 600;
@ -77,7 +77,7 @@ const Wrapper = styled.div`
.close {
font-size: 1.3rem;
line-height: 1;
color: #000;
color: ${(props) => props.theme.modal.iconColor};
text-shadow: 0 1px 0 #fff;
opacity: 0.5;
margin-top: -2px;
@ -90,7 +90,30 @@ const Wrapper = styled.div`
.bruno-modal-content {
flex-grow: 1;
background-color: #fff;
background-color: ${(props) => props.theme.modal.body.bg};
.textbox {
line-height: 1.42857143;
border: 1px solid #ccc;
padding: 0.45rem;
box-shadow: none;
border-radius: 0px;
outline: none;
box-shadow: none;
transition: border-color ease-in-out .1s;
border-radius: 3px;
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
outline: none !important;
}
}
.bruno-form {
color: ${(props) => props.theme.modal.body.color};
}
}
.bruno-modal-backdrop {
@ -107,7 +130,7 @@ const Wrapper = styled.div`
height: 100%;
width: 100%;
left: 0;
opacity: 0.4;
opacity: ${(props) => props.theme.modal.backdrop.opacity};
top: 0;
background: black;
position: fixed;
@ -117,7 +140,7 @@ const Wrapper = styled.div`
}
.bruno-modal-footer {
background-color: white;
background-color: ${(props) => props.theme.modal.body.bg};
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}

View File

@ -8,11 +8,11 @@ const Wrapper = styled.div`
thead,
td {
border: 1px solid #efefef;
border: 1px solid ${(props) => props.theme.table.border};
}
thead {
color: #616161;
color: ${(props) => props.theme.table.thead.color};;
font-size: 0.8125rem;
user-select: none;
}

View File

@ -35,8 +35,8 @@ const StyledWrapper = styled.div`
.react-tabs__tab--selected {
border: none;
color: #322e2c !important;
border-bottom: solid 2px var(--color-tab-active-border) !important;
color: ${(props) => props.theme.tabs.active.color} !important;
border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
border-color: var(--color-tab-active-border) !important;
background: inherit;
outline: none !important;
@ -50,7 +50,7 @@ const StyledWrapper = styled.div`
border: none;
outline: none !important;
box-shadow: none !important;
border-bottom: solid 2px var(--color-tab-active-border) !important;
border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
border-color: var(--color-tab-active-border) !important;
background: inherit;
outline: none !important;

View File

@ -20,8 +20,8 @@ const StyledWrapper = styled.div`
}
&.active {
color: #322e2c !important;
border-bottom: solid 2px var(--color-tab-active-border) !important;
color: ${(props) => props.theme.tabs.active.color} !important;
border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
}
}
}

View File

@ -8,11 +8,11 @@ const Wrapper = styled.div`
thead,
td {
border: 1px solid #efefef;
border: 1px solid ${(props) => props.theme.table.border};
}
thead {
color: #616161;
color: ${(props) => props.theme.table.thead.color};;
font-size: 0.8125rem;
user-select: none;
}

View File

@ -2,7 +2,8 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
div.CodeMirror {
border: solid 1px var(--color-codemirror-border);
background: ${(props) => props.theme.codemirror.bg};
border: solid 1px ${(props) => props.theme.codemirror.border};
/* todo: find a better way */
height: calc(100vh - 250px);
}

View File

@ -8,11 +8,11 @@ const Wrapper = styled.div`
thead,
td {
border: 1px solid #efefef;
border: 1px solid ${(props) => props.theme.table.border};
}
thead {
color: #616161;
color: ${(props) => props.theme.table.thead.color};;
font-size: 0.8125rem;
user-select: none;
}
@ -32,6 +32,7 @@ const Wrapper = styled.div`
width: 100%;
border: solid 1px transparent;
outline: none !important;
background-color: inherit;
&:focus {
outline: none !important;

View File

@ -4,18 +4,18 @@ const Wrapper = styled.div`
height: 2.3rem;
div.method-selector-container {
background-color: var(--color-sidebar-background);
background-color: ${(props) => props.theme.requestTabPanel.url.bg};
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
div.input-container {
background-color: var(--color-sidebar-background);
background-color: ${(props) => props.theme.requestTabPanel.url.bg};
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
input {
background-color: var(--color-sidebar-background);
background-color: ${(props) => props.theme.requestTabPanel.url.bg};
outline: none;
box-shadow: none;

View File

@ -3,10 +3,12 @@ import get from 'lodash/get';
import { useDispatch } from 'react-redux';
import { requestUrlChanged, updateRequestMethod } from 'providers/ReduxStore/slices/collections';
import HttpMethodSelector from './HttpMethodSelector';
import { useTheme } from 'providers/Theme';
import SendIcon from 'components/Icons/Send';
import StyledWrapper from './StyledWrapper';
import SendSvg from 'assets/send.svg';
const QueryUrl = ({ item, collection, handleRun }) => {
const { theme } = useTheme();
const dispatch = useDispatch();
const method = item.draft ? get(item, 'draft.request.method') : get(item, 'request.method');
let url = item.draft ? get(item, 'draft.request.url') : get(item, 'request.url');
@ -48,7 +50,7 @@ const QueryUrl = ({ item, collection, handleRun }) => {
onChange={(event) => onUrlChange(event.target.value)}
/>
<div className="flex items-center h-full mr-2 cursor-pointer" onClick={handleRun}>
<img src={SendSvg.src} style={{ width: '22px' }} />
<SendIcon color={theme.requestTabPanel.url.icon} width={22}/>
</div>
</div>
</StyledWrapper>

View File

@ -4,7 +4,7 @@ const Wrapper = styled.div`
font-size: 0.8125rem;
.body-mode-selector {
background: #efefef;
background: ${(props) => props.theme.requestTabPanel.bodyModeSelect.color};
border-radius: 3px;
.dropdown-item {

View File

@ -4,6 +4,7 @@ import CodeEditor from 'components/CodeEditor';
import FormUrlEncodedParams from 'components/RequestPane/FormUrlEncodedParams';
import MultipartFormParams from 'components/RequestPane/MultipartFormParams';
import { useDispatch } from 'react-redux';
import { useTheme } from 'providers/Theme';
import { updateRequestBody } from 'providers/ReduxStore/slices/collections';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
@ -12,6 +13,9 @@ const RequestBody = ({ item, collection }) => {
const dispatch = useDispatch();
const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body');
const bodyMode = item.draft ? get(item, 'draft.request.body.mode') : get(item, 'request.body.mode');
const {
storedTheme
} = useTheme();
const onEdit = (value) => {
dispatch(
@ -41,7 +45,7 @@ const RequestBody = ({ item, collection }) => {
return (
<StyledWrapper className="w-full">
<CodeEditor value={bodyContent[bodyMode] || ''} onEdit={onEdit} onRun={onRun} onSave={onSave} mode={codeMirrorMode[bodyMode]} />
<CodeEditor theme={storedTheme} value={bodyContent[bodyMode] || ''} onEdit={onEdit} onRun={onRun} onSave={onSave} mode={codeMirrorMode[bodyMode]} />
</StyledWrapper>
);
}

View File

@ -8,11 +8,11 @@ const Wrapper = styled.div`
thead,
td {
border: 1px solid #efefef;
border: 1px solid ${(props) => props.theme.table.border};
}
thead {
color: #616161;
color: ${(props) => props.theme.table.thead.color};
font-size: 0.8125rem;
user-select: none;
}
@ -23,14 +23,13 @@ const Wrapper = styled.div`
.btn-add-header {
font-size: 0.8125rem;
margin-block: 10px;
padding: 5px;
}
input[type='text'] {
width: 100%;
border: solid 1px transparent;
outline: none !important;
background-color: inherit;
&:focus {
outline: none !important;

View File

@ -124,7 +124,7 @@ const RequestHeaders = ({ item, collection }) => {
: null}
</tbody>
</table>
<button className="btn-add-header select-none" onClick={addHeader}>
<button className="btn-add-header text-link pr-2 py-3 mt-2 select-none" onClick={addHeader}>
+ Add Header
</button>
</StyledWrapper>

View File

@ -18,11 +18,11 @@ const StyledWrapper = styled.div`
display: flex;
height: 100%;
width: 1px;
border-left: solid 1px var(--color-request-dragbar-background);
border-left: solid 1px ${(props) => props.theme.requestTabPanel.dragbar.border};
}
&:hover div.drag-request-border {
border-left: solid 1px var(--color-request-dragbar-background-active);
border-left: solid 1px ${(props) => props.theme.requestTabPanel.dragbar.activeBorder};
}
}
`;

View File

@ -19,7 +19,7 @@ const StyledWrapper = styled.div`
.close-icon {
display: none;
color: #9f9f9f;
color: ${(props) => props.theme.requestTabs.icon.color};
width: 8px;
padding-bottom: 6px;
padding-top: 6px;
@ -27,8 +27,8 @@ const StyledWrapper = styled.div`
&:hover,
&:hover .close-icon {
background-color: #eaeaea;
color: rgb(76 76 76);
color: ${(props) => props.theme.requestTabs.icon.hoverColor};
background-color: ${(props) => props.theme.requestTabs.icon.hoverBg};
}
.has-changes-icon {

View File

@ -85,7 +85,7 @@ const RequestTab = ({ tab, collection }) => {
<span className="tab-method uppercase" style={{ color: getMethodColor(method), fontSize: 12 }}>
{method}
</span>
<span className="text-gray-700 ml-1 tab-name" title={item.name}>
<span className="ml-1 tab-name" title={item.name}>
{item.name}
</span>
</div>

View File

@ -1,7 +1,7 @@
import styled from 'styled-components';
const Wrapper = styled.div`
border-bottom: 1px solid var(--color-request-dragbar-background);
border-bottom: 1px solid ${(props) => props.theme.requestTabs.borromBorder};
ul {
padding: 0;
@ -28,7 +28,8 @@ const Wrapper = styled.div`
height: 38px;
margin-right: 6px;
background: #f7f7f7;
color: ${(props) => props.theme.requestTabs.color};
background: ${(props) => props.theme.requestTabs.bg};
border-radius: 0;
.tab-container {
@ -36,7 +37,7 @@ const Wrapper = styled.div`
}
&.active {
background: #e7e7e7;
background: ${(props) => props.theme.requestTabs.active.bg};
font-weight: 500;
}
@ -60,9 +61,9 @@ const Wrapper = styled.div`
padding: 3px 0px;
display: inline-flex;
justify-content: center;
color: rgb(117 117 117);
color: ${(props) => props.theme.requestTabs.shortTab.color};
background-color: ${(props) => props.theme.requestTabs.shortTab.bg};
position: relative;
background-color: white;
top: -1px;
> div {
@ -85,8 +86,8 @@ const Wrapper = styled.div`
&:hover {
> div {
background-color: #eaeaea;
color: rgb(76 76 76);
background-color: ${(props) => props.theme.requestTabs.shortTab.hoverBg};
color: ${(props) => props.theme.requestTabs.shortTab.hoverColor};
border-radius: 3px;
}
}

View File

@ -23,7 +23,7 @@ const ResponseLoadingOverlay = ({ item, collection }) => {
<IconRefresh size={24} className="animate-spin" />
<button
onClick={handleCancelRequest}
className="mt-4 uppercase bg-gray-200 active:bg-blueGray-600 text-xs px-4 py-2 rounded shadow hover:shadow-md outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
className="mt-4 uppercase btn-md rounded btn-secondary ease-linear transition-all duration-150"
type="button"
>
Cancel Request

View File

@ -3,6 +3,10 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
padding-top: 20%;
width: 100%;
.send-icon {
color: ${(props) => props.theme.requestTabPanel.responseSendIcon};
}
`;
export default StyledWrapper;

View File

@ -5,7 +5,7 @@ import StyledWrapper from './StyledWrapper';
const Placeholder = () => {
return (
<StyledWrapper>
<div className="text-gray-300 flex justify-center" style={{ fontSize: 200 }}>
<div className="send-icon flex justify-center" style={{ fontSize: 200 }}>
<IconSend size={150} strokeWidth={1} />
</div>
<div className="flex mt-4">

View File

@ -1,12 +1,17 @@
import React from 'react';
import CodeEditor from 'components/CodeEditor';
import { useTheme } from 'providers/Theme';
import StyledWrapper from './StyledWrapper';
const QueryResult = ({ value, width }) => {
const {
storedTheme
} = useTheme();
return (
<StyledWrapper className="px-3 w-full" style={{ maxWidth: width }}>
<div className="h-full">
<CodeEditor value={value || ''} readOnly />
<CodeEditor theme={storedTheme} value={value || ''} readOnly />
</div>
</StyledWrapper>
);

View File

@ -22,7 +22,7 @@ const Wrapper = styled.div`
tbody {
tr:nth-child(odd) {
background-color: var(--color-table-stripe);
background-color: ${(props) => props.theme.table.striped};
}
}
}

View File

@ -3,7 +3,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
font-size: 0.75rem;
font-weight: 600;
color: rgb(117 117 117);
color: ${(props) => props.theme.requestTabPanel.responseStatus};
`;
export default Wrapper;

View File

@ -3,7 +3,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
font-size: 0.75rem;
font-weight: 600;
color: rgb(117 117 117);
color: ${(props) => props.theme.requestTabPanel.responseStatus};
`;
export default Wrapper;

View File

@ -3,6 +3,14 @@ import styled from 'styled-components';
const Wrapper = styled.div`
font-size: 0.75rem;
font-weight: 600;
&.text-ok {
color: ${(props) => props.theme.requestTabPanel.responseOk};
}
&.text-error {
color: ${(props) => props.theme.requestTabPanel.responseError};
}
`;
export default Wrapper;

View File

@ -3,19 +3,20 @@ import classnames from 'classnames';
import statusCodePhraseMap from './get-status-code-phrase';
import StyledWrapper from './StyledWrapper';
// Todo: text-error class is not getting pulled in for 500 errors
const StatusCode = ({ status }) => {
const getTabClassname = () => {
const getTabClassname = (status) => {
return classnames('', {
'text-blue-700': status >= 100 && status < 200,
'text-green-700': status >= 200 && status < 300,
'text-purple-700': status >= 300 && status < 400,
'text-yellow-700': status >= 400 && status < 500,
'text-red-700': status >= 500 && status < 600
'text-ok': status >= 100 && status < 200,
'text-ok': status >= 200 && status < 300,
'text-error': status >= 300 && status < 400,
'text-error': status >= 400 && status < 500,
'text-error': status >= 500 && status < 600
});
};
return (
<StyledWrapper className={getTabClassname()}>
<StyledWrapper className={getTabClassname(status)}>
{status} {statusCodePhraseMap[status]}
</StyledWrapper>
);

View File

@ -20,8 +20,8 @@ const StyledWrapper = styled.div`
}
&.active {
color: #322e2c !important;
border-bottom: solid 2px var(--color-tab-active-border) !important;
color: ${(props) => props.theme.tabs.active.color} !important;
border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
}
}
}

View File

@ -13,25 +13,25 @@ const Wrapper = styled.div`
}
.method-get {
color: var(--color-method-get);
color: ${(props) => props.theme.request.methods.get};
}
.method-post {
color: var(--color-method-post);
color: ${(props) => props.theme.request.methods.post};
}
.method-put {
color: var(--color-method-put);
color: ${(props) => props.theme.request.methods.put};
}
.method-delete {
color: var(--color-method-delete);
color: ${(props) => props.theme.request.methods.delete};
}
.method-patch {
color: var(--color-method-patch);
color: ${(props) => props.theme.request.methods.put};
}
.method-options {
color: var(--color-method-options);
color: ${(props) => props.theme.request.methods.put};
}
.method-head {
color: var(--color-method-head);
color: ${(props) => props.theme.request.methods.put};
}
`;

View File

@ -2,7 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
.menu-icon {
color: rgb(110 110 110);
color: ${(props) => props.theme.sidebar.dropdownIcon.color};
.dropdown {
div[aria-expanded='true'] {
@ -15,7 +15,7 @@ const Wrapper = styled.div`
}
.indent-block {
border-right: solid 1px #e1e1e1;
border-right: ${(props) => props.theme.sidebar.collection.item.indentBorder};
}
.collection-item-name {
@ -34,7 +34,7 @@ const Wrapper = styled.div`
}
&:hover {
background: #e7e7e7;
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
.menu-icon {
.dropdown {
div[aria-expanded='false'] {
@ -45,14 +45,14 @@ const Wrapper = styled.div`
}
&.item-focused-in-tab {
background: var(--color-sidebar-collection-item-active-background);
background: ${(props) => props.theme.sidebar.collection.item.bg};
&:hover {
background: var(--color-sidebar-collection-item-active-background) !important;
background: ${(props) => props.theme.sidebar.collection.item.bg} !important;
}
.indent-block {
border-right: solid 1px var(--color-sidebar-collection-item-active-indent-border);
border-right: ${(props) => props.theme.sidebar.collection.item.active.indentBorder} !important;
}
}
@ -62,9 +62,9 @@ const Wrapper = styled.div`
}
div.dropdown-item.delete-item {
color: var(--color-text-danger);
color: ${(props) => props.theme.colors.danger};
&:hover {
background-color: var(--color-background-danger);
background-color: ${(props) => props.theme.colors.bg.danger};
color: white;
}
}

View File

@ -20,7 +20,6 @@ const RemoveCollectionFromWorkspace = ({ onClose, collection }) => {
})
);
})
.then(() => dispatch(removeLocalCollection(collection.uid)))
.then(() => toast.success('Collection removed from workspace'))
.catch((err) => console.log(err) && toast.error('An error occured while removing the collection'));
};

View File

@ -24,7 +24,7 @@ const Wrapper = styled.div`
svg {
height: 22px;
color: rgb(110 110 110);
color: ${(props) => props.theme.sidebar.dropdownIcon.color};
}
}
@ -45,9 +45,9 @@ const Wrapper = styled.div`
}
div.dropdown-item.delete-collection {
color: var(--color-text-danger);
color: ${(props) => props.theme.colors.text.danger};
&:hover {
background-color: var(--color-background-danger);
background-color: ${(props) => props.theme.colors.bg.danger};
color: white;
}
}

View File

@ -0,0 +1,7 @@
import styled from 'styled-components';
const Wrapper = styled.div`
color: ${(props) => props.theme.colors.text.muted};
`;
export default Wrapper;

View File

@ -1,12 +1,21 @@
import React, { useState } from 'react';
import toast from 'react-hot-toast';
import { useState } from 'react';
import { useTheme } from '../../../../providers/Theme';
import { useSelector, useDispatch } from 'react-redux';
import CreateCollection from 'components/Sidebar/CreateCollection';
import SelectCollection from 'components/Sidebar/Collections/SelectCollection';
import { createCollection } from 'providers/ReduxStore/slices/collections/actions';
import { addCollectionToWorkspace } from 'providers/ReduxStore/slices/workspaces/actions';
import toast from 'react-hot-toast';
import styled from 'styled-components';
import CreateCollection from 'components/Sidebar/CreateCollection';
import SelectCollection from 'components/Sidebar/Collections/SelectCollection';
import StyledWrapper from './StyledWrapper';
const LinkStyle = styled.span`
color: ${(props) => props.theme['text-link']};
`;
const CreateOrAddCollection = () => {
const { theme } = useTheme();
const dispatch = useDispatch();
const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false);
const [addCollectionToWSModalOpen, setAddCollectionToWSModalOpen] = useState(false);
@ -31,18 +40,18 @@ const CreateOrAddCollection = () => {
};
const CreateLink = () => (
<span className="underline text-link cursor-pointer" onClick={() => setCreateCollectionModalOpen(true)}>
<LinkStyle className="underline text-link cursor-pointer" theme={theme} onClick={() => setCreateCollectionModalOpen(true)}>
Create
</span>
</LinkStyle>
);
const AddLink = () => (
<span className="underline text-link cursor-pointer" onClick={() => setAddCollectionToWSModalOpen(true)}>
<LinkStyle className="underline text-link cursor-pointer" theme={theme} onClick={() => setAddCollectionToWSModalOpen(true)}>
Add
</span>
</LinkStyle>
);
return (
<div className="px-2 mt-4 text-gray-600">
<StyledWrapper className="px-2 mt-4">
{createCollectionModalOpen ? <CreateCollection onClose={() => setCreateCollectionModalOpen(false)} handleConfirm={handleCreateCollection} /> : null}
{addCollectionToWSModalOpen ? (
@ -55,7 +64,7 @@ const CreateOrAddCollection = () => {
<CreateLink /> or <AddLink /> Collection to Workspace.
</div>
</div>
</div>
</StyledWrapper>
);
};

View File

@ -10,7 +10,7 @@ const StyledWrapper = styled.div`
cursor: pointer;
&:hover {
background-color: #f4f4f4;
background-color: ${(props) => props.theme.plainGrid.hoverBg};;
}
}
`;

View File

@ -3,7 +3,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
.current-workspace {
margin-inline: 0.5rem;
background: #e1e1e1;
background-color: ${(props) => props.theme.sidebar.workspace.bg};
border-radius: 5px;
.caret {
@ -13,6 +13,11 @@ const Wrapper = styled.div`
}
}
.muted-message {
color: ${(props) => props.theme.sidebar.muted};
border-top: solid 1px ${(props) => props.theme.dropdown.seperator};
}
div[data-tippy-root] {
width: calc(100% - 1rem);
}

View File

@ -60,7 +60,7 @@ const LocalCollections = ({ searchText }) => {
<span>Open Collection</span>
</div>
<div className="px-2 pt-2 text-gray-600" style={{ fontSize: 10, borderTop: 'solid 1px #e7e7e7' }}>
<div className="px-2 pt-2 muted-message" style={{ fontSize: 10 }}>
Note: Local collections are not tied to a workspace
</div>
</Dropdown>

View File

@ -1,8 +1,8 @@
import styled from 'styled-components';
const Wrapper = styled.div`
background-color: rgb(44, 44, 44);
color: rgba(255, 255, 255, 0.5);
background-color: ${(props) => props.theme.menubar.bg};
color: rgba(255, 255, 255, 0.4);
min-height: 100vh;
.menu-item {

View File

@ -1,16 +1,19 @@
import React, { useState } from 'react';
import Link from 'next/link';
import { useState } from 'react';
import { useRouter } from 'next/router';
import { IconCode, IconFiles, IconUser, IconUsers, IconSettings, IconChevronsLeft, IconLifebuoy } from '@tabler/icons';
import { useDispatch } from 'react-redux';
import { toggleLeftMenuBar } from 'providers/ReduxStore/slices/app';
import BrunoSupport from 'components/BrunoSupport';
import { isElectron } from 'utils/common/platform';
import { toggleLeftMenuBar } from 'providers/ReduxStore/slices/app';
import { IconCode, IconFiles, IconMoon, IconChevronsLeft, IconLifebuoy } from '@tabler/icons';
import Link from 'next/link';
import StyledWrapper from './StyledWrapper';
import BrunoSupport from 'components/BrunoSupport';
import SwitchTheme from 'components/SwitchTheme';
const MenuBar = () => {
const router = useRouter();
const dispatch = useDispatch();
const [openTheme, setOpenTheme] = useState(false);
const [openBrunoSupport, setOpenBrunoSupport] = useState(false);
const isPlatformElectron = isElectron();
@ -20,6 +23,9 @@ const MenuBar = () => {
return (
<StyledWrapper className="h-full flex flex-col">
{openBrunoSupport && <BrunoSupport onClose={() => setOpenBrunoSupport(false)} />}
{openTheme && <SwitchTheme onClose={() => setOpenTheme(false)} />}
<div className="flex flex-col">
<Link href="/">
<div className={getClassName('/')}>
@ -43,14 +49,16 @@ const MenuBar = () => {
<IconUser size={28} strokeWidth={1.5}/>
</div>
</Link> */}
<div className="menu-item">
<IconLifebuoy size={28} strokeWidth={1.5} onClick={() => setOpenBrunoSupport(true)} />
<div className="menu-item" onClick={() => setOpenBrunoSupport(true)}>
<IconLifebuoy size={28} strokeWidth={1.5}/>
</div>
<div className="menu-item" onClick={() => setOpenTheme(true)}>
<IconMoon size={28} strokeWidth={1.5}/>
</div>
<div className="menu-item" onClick={() => dispatch(toggleLeftMenuBar())}>
<IconChevronsLeft size={28} strokeWidth={1.5} />
</div>
</div>
{openBrunoSupport && <BrunoSupport onClose={() => setOpenBrunoSupport(false)} />}
</StyledWrapper>
);
};

View File

@ -2,9 +2,9 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
div.method-selector-container {
border: solid 1px var(--color-layout-border);
border: solid 1px ${(props) => props.theme.modal.input.border};
border-right: none;
background-color: var(--color-sidebar-background);
background-color: ${(props) => props.theme.modal.input.bg};
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
@ -15,15 +15,17 @@ const StyledWrapper = styled.div`
div.method-selector-container,
div.input-container {
background-color: ${(props) => props.theme.modal.input.bg};
height: 2.3rem;
}
div.input-container {
border: solid 1px var(--color-layout-border);
border: solid 1px ${(props) => props.theme.modal.input.border};
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
input {
background-color: ${(props) => props.theme.modal.input.bg};
outline: none;
box-shadow: none;

View File

@ -1,8 +1,10 @@
import styled from 'styled-components';
const Wrapper = styled.div`
color: ${(props) => props.theme.sidebar.color};
aside {
background-color: var(--color-sidebar-background);
background-color: ${(props) => props.theme['sidebar-background']};
.collection-title {
line-height: 1.5;
@ -25,6 +27,18 @@ const Wrapper = styled.div`
top: -0.625rem;
}
}
.collection-filter {
input {
border: ${(props) => props.theme.sidebar.search.border};
border-radius: 2px;
background-color: ${(props) => props.theme.sidebar.search.bg};
&:focus {
outline: none;
}
}
}
}
div.drag-sidebar {

View File

@ -1,17 +1,14 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
.local-collection-label {
background-color: var(--color-sidebar-background);
}
.local-collections-unavailable {
padding: 0.35rem 0.6rem;
border-top: solid 1px #ddd;
color: ${(props) => props.theme.sidebar.muted};
border-top: solid 1px ${(props) => props.theme.dropdown.seperator};
font-size: 11px;
}
.collection-dropdown {
color: rgb(110 110 110);
color: ${(props) => props.theme.sidebar.dropdownIcon.color};
&:hover {
color: inherit;

View File

@ -1,18 +1,19 @@
import React, { useState, forwardRef, useRef } from 'react';
import toast from 'react-hot-toast';
import Dropdown from 'components/Dropdown';
import Bruno from 'components/Bruno';
import Dropdown from 'components/Dropdown';
import CreateCollection from '../CreateCollection';
import importCollection from 'utils/collections/import';
import SelectCollection from 'components/Sidebar/Collections/SelectCollection';
import { IconDots } from '@tabler/icons';
import { IconFolders } from '@tabler/icons';
import { isElectron } from 'utils/common/platform';
import { useState, forwardRef, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { showHomePage } from 'providers/ReduxStore/slices/app';
import { collectionImported } from 'providers/ReduxStore/slices/collections';
import { openLocalCollection } from 'providers/ReduxStore/slices/collections/actions';
import { addCollectionToWorkspace } from 'providers/ReduxStore/slices/workspaces/actions';
import { showHomePage } from 'providers/ReduxStore/slices/app';
import { IconDots } from '@tabler/icons';
import CreateCollection from '../CreateCollection';
import SelectCollection from 'components/Sidebar/Collections/SelectCollection';
import importCollection from 'utils/collections/import';
import { isElectron } from 'utils/common/platform';
import StyledWrapper from './StyledWrapper';
const TitleBar = () => {
@ -70,7 +71,7 @@ const TitleBar = () => {
</div>
<div
onClick={handleTitleClick}
className=" flex items-center font-medium select-none cursor-pointer"
className="flex items-center font-medium select-none cursor-pointer"
style={{ fontSize: 14, paddingLeft: 6, position: 'relative', top: -1 }}
>
bruno
@ -134,7 +135,7 @@ const TitleBar = () => {
</div>
</>
) : (
<div className="flex items-center select-none text-gray-400 text-xs local-collections-unavailable">
<div className="flex items-center select-none text-xs local-collections-unavailable">
Note: Local collections are only available on the desktop app.
</div>
)}

View File

@ -1,13 +1,14 @@
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import MenuBar from './MenuBar';
import TitleBar from './TitleBar';
import Collections from './Collections';
import LocalCollections from './LocalCollections';
import TitleBar from './TitleBar';
import MenuBar from './MenuBar';
import StyledWrapper, { BottomWrapper, VersionNumber } from './StyledWrapper';
import WorkspaceSelector from 'components/Workspaces/WorkspaceSelector';
import { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { IconSearch, IconChevronsRight } from '@tabler/icons';
import { updateLeftSidebarWidth, updateIsDragging, toggleLeftMenuBar } from 'providers/ReduxStore/slices/app';
import StyledWrapper from './StyledWrapper';
import WorkspaceSelector from 'components/Workspaces/WorkspaceSelector';
const MIN_LEFT_SIDEBAR_WIDTH = 222;
const MAX_LEFT_SIDEBAR_WIDTH = 600;
@ -106,7 +107,8 @@ const Sidebar = () => {
<Collections searchText={searchText} />
<LocalCollections searchText={searchText} />
</div>
<div className="flex px-1 py-2 items-center cursor-pointer text-gray-500 select-none">
<div className="footer flex px-1 py-2 items-center cursor-pointer select-none">
<div className="flex items-center ml-1 text-xs ">
{!leftMenuBarOpen && <IconChevronsRight size={24} strokeWidth={1.5} className="mr-2 hover:text-gray-700" onClick={() => dispatch(toggleLeftMenuBar())} />}
{/* <IconLayoutGrid size={20} strokeWidth={1.5} className="mr-2"/> */}
@ -122,7 +124,7 @@ const Sidebar = () => {
title="GitHub"
></iframe>
</div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.25.2</div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v0.2.0</div>
</div>
</div>
</div>

View File

@ -0,0 +1,20 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
color: var(--color-text);
.collection-options {
svg {
position: relative;
top: -1px;
}
.label {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
`;
export default StyledWrapper;

View File

@ -0,0 +1,67 @@
import React from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import Modal from 'components/Modal/index';
import StyledWrapper from './StyledWrapper';
import { useTheme } from 'providers/Theme';
const SwitchTheme = ({ onClose }) => {
const { storedTheme, setStoredTheme } = useTheme();
const formik = useFormik({
enableReinitialize: true,
initialValues: {
theme: storedTheme
},
validationSchema: Yup.object({
theme: Yup.string().oneOf(['light', 'dark']).required('theme is required')
}),
onSubmit: (values) => {
setStoredTheme(values.theme);
}
});
return (
<StyledWrapper>
<Modal size="sm" title={'Switch Theme'} handleCancel={onClose} hideFooter={true}>
<div className='bruno-form'>
<div className="flex items-center mt-2">
<input
id="light-theme"
className="cursor-pointer"
type="radio"
name="theme"
onChange={(e) => {
formik.handleChange(e);
formik.handleSubmit()
}}
value="light"
checked={formik.values.theme === 'light'}
/>
<label htmlFor="light-theme" className="ml-1 cursor-pointer select-none">
Light
</label>
<input
id="dark-theme"
className="ml-4 cursor-pointer"
type="radio"
name="theme"
onChange={(e) => {
formik.handleChange(e);
formik.handleSubmit()
}}
value="dark"
checked={formik.values.theme === 'dark'}
/>
<label htmlFor="dark-theme" className="ml-1 cursor-pointer select-none">
Dark
</label>
</div>
</div>
</Modal>
</StyledWrapper>
);
};
export default SwitchTheme;

View File

@ -1,11 +1,15 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
.create-request {
color: #737373;
.heading {
color: ${(props) => props.theme.welcome.heading};
font-size: 0.75rem;
}
.muted {
color: ${(props) => props.theme.welcome.muted};
}
.collection-options {
svg {
position: relative;

View File

@ -1,16 +1,16 @@
import React, { useState } from 'react';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { IconPlus, IconUpload, IconFiles, IconFolders, IconPlayerPlay, IconBrandChrome, IconSpeakerphone, IconDeviceDesktop } from '@tabler/icons';
import { isElectron } from 'utils/common/platform';
import { useSelector, useDispatch } from 'react-redux';
import { collectionImported } from 'providers/ReduxStore/slices/collections';
import { openLocalCollection } from 'providers/ReduxStore/slices/collections/actions';
import { addCollectionToWorkspace } from 'providers/ReduxStore/slices/workspaces/actions';
import { IconBrandGithub, IconPlus, IconUpload, IconFiles, IconFolders, IconPlayerPlay, IconBrandChrome, IconSpeakerphone, IconDeviceDesktop } from '@tabler/icons';
import Bruno from 'components/Bruno';
import CreateCollection from 'components/Sidebar/CreateCollection';
import SelectCollection from 'components/Sidebar/Collections/SelectCollection';
import importCollection, { importSampleCollection } from 'utils/collections/import';
import { isElectron } from 'utils/common/platform';
import GithubSvg from 'assets/github.svg';
import StyledWrapper from './StyledWrapper';
const Welcome = () => {
@ -69,7 +69,7 @@ const Welcome = () => {
<div className="text-xl font-semibold select-none">bruno</div>
<div className="mt-4">Opensource API Client.</div>
<div className="uppercase font-semibold create-request mt-10">Collections</div>
<div className="uppercase font-semibold heading mt-10">Collections</div>
<div className="mt-4 flex items-center collection-options select-none">
<div className="flex items-center">
<IconPlus size={18} strokeWidth={2} />
@ -93,7 +93,7 @@ const Welcome = () => {
</div>
</div>
<div className="uppercase font-semibold create-request mt-10 pt-6">Local Collections</div>
<div className="uppercase font-semibold heading mt-10 pt-6">Local Collections</div>
{isPlatformElectron ? (
<div className="mt-4 flex items-center collection-options select-none">
<div className="flex items-center">
@ -110,10 +110,10 @@ const Welcome = () => {
</div>
</div>
) : (
<div className="mt-4 flex items-center collection-options select-none text-gray-600 text-xs">Local collections are only available on the desktop app.</div>
<div className="muted mt-4 flex items-center collection-options select-none text-gray-600 text-xs">Local collections are only available on the desktop app.</div>
)}
<div className="uppercase font-semibold create-request mt-10 pt-6">Links</div>
<div className="uppercase font-semibold heading mt-10 pt-6">Links</div>
<div className="mt-4 flex flex-col collection-options select-none">
<div>
<a href="https://www.usebruno.com/downloads" target="_blank" className="flex items-center">
@ -138,7 +138,7 @@ const Welcome = () => {
</div> */}
<div className="mt-2">
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-center">
<img src={GithubSvg.src} style={{ width: '18px' }} />
<IconBrandGithub size={18} strokeWidth={2} />
<span className="label ml-2">Github</span>
</a>
</div>

View File

@ -10,7 +10,7 @@ const Wrapper = styled.div`
}
div:hover {
background-color: #f4f4f4;
background-color: ${(props) => props.theme.plainGrid.hoverBg};
}
`;

View File

@ -3,7 +3,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
.current-workspace {
margin-inline: 0.5rem;
background: #e1e1e1;
background-color: ${(props) => props.theme.sidebar.workspace.bg};
border-radius: 5px;
.caret {

View File

@ -49,14 +49,14 @@ const WorkspaceSelector = () => {
<div className="items-center cursor-pointer relative">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div className="dropdown-item" onClick={() => setOpenSwitchWorkspaceModal(true)}>
<div className="pr-2 text-gray-600">
<div className="pr-2 icon">
<IconSwitch3 size={18} strokeWidth={1.5} />
</div>
<span>Switch Workspace</span>
</div>
<div className="dropdown-item" onClick={() => setOpenWorkspacesModal(true)}>
<div className="pr-2 text-gray-600">
<div className="pr-2 icon">
<IconSettings size={18} strokeWidth={1.5} />
</div>
<span>Configure Workspaces</span>

View File

@ -2,28 +2,78 @@ import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
.CodeMirror-gutters {
background-color: var(--color-codemirror-background);
border-right: solid 1px var(--color-codemirror-border);
background-color: ${(props) => props.theme.codemirror.gutter.bg} !important;
border-right: solid 1px ${(props) => props.theme.codemirror.border};
}
.bruno-form {
.textbox {
line-height: 1.42857143;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
padding: 0.45rem;
box-shadow: none;
border-radius: 0px;
.text-link {
color: ${(props) => props.theme.textLink};
}
.btn {
text-align: center;
white-space: nowrap;
outline: none;
box-shadow: none;
border-radius: 3px;
}
.btn-sm {
padding: .215rem .6rem .215rem .6rem;
}
.btn-md {
padding: .4rem 1.1rem;
line-height: 1.47;
}
.btn-default {
&:active,
&:hover,
&:focus {
outline: none;
box-shadow: none;
transition: border-color ease-in-out .1s;
border-radius: 3px;
}
}
&:focus {
border: solid 1px #8b8b8b !important;
outline: none !important;
}
.btn-close {
color: ${(props) => props.theme.button.close.color};
background: ${(props) => props.theme.button.close.bg};
border: solid 1px ${(props) => props.theme.button.close.border};;
&:hover,
&:focus {
outline: none;
box-shadow: none;
border: solid 1px #696969;
}
}
.btn-secondary {
color: ${(props) => props.theme.button.secondary.color};
background: ${(props) => props.theme.button.secondary.bg};
border: solid 1px ${(props) => props.theme.button.secondary.border};
.btn-icon {
color: #3f3f3f;
}
&:hover,
&:focus {
border-color: ${(props) => props.theme.button.secondary.hoverBorder};
outline: none;
box-shadow: none;
}
&:disabled {
color: ${(props) => props.theme.button.disabled.color};
background: ${(props) => props.theme.button.disabled.bg};
border: solid 1px ${(props) => props.theme.button.disabled.border};
cursor: not-allowed;
}
&:disabled.btn-icon {
color: #545454;
}
}

View File

@ -17,7 +17,7 @@ const Wrapper = styled.div`
border-radius: 3px;
&:hover {
background-color: #f4f4f4;
background-color: ${(props) => props.theme.plainGrid.hoverBg};
margin-left: -8px;
margin-right: -8px;
padding-left: 8px;

View File

@ -32,17 +32,6 @@ const Wrapper = styled.div`
}
}
}
.collection-filter {
input {
border: 1px solid rgb(211 211 211);
border-radius: 2px;
&:focus {
outline: none;
}
}
}
`;
export default Wrapper;

View File

@ -6,6 +6,8 @@ import RequestTabPanel from 'components/RequestTabPanel';
import Sidebar from 'components/Sidebar';
import { useSelector } from 'react-redux';
import StyledWrapper from './StyledWrapper';
import 'codemirror/theme/material.css';
import 'codemirror/theme/monokai.css';
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
if (!SERVER_RENDERED) {

View File

@ -28,7 +28,7 @@ const Wrapper = styled.div`
}
a {
color: var(--color-text-link);
color: ${(props) => props.theme.textLink};
}
.error-msg {

View File

@ -28,7 +28,7 @@ const Wrapper = styled.div`
}
a {
color: var(--color-text-link);
color: ${(props) => props.theme.textLink};
}
.or {

View File

@ -1,18 +1,18 @@
import { HotkeysProvider } from 'providers/Hotkeys';
import { AuthProvider } from 'providers/Auth';
import { AppProvider } from 'providers/App';
import ReduxStore from 'providers/ReduxStore';
import { Provider } from 'react-redux';
import { Toaster } from 'react-hot-toast';
import { AppProvider } from 'providers/App';
import { ToastProvider } from 'providers/Toaster';
import { HotkeysProvider } from 'providers/Hotkeys';
import ReduxStore from 'providers/ReduxStore';
import ThemeProvider from 'providers/Theme/index';
import '../styles/app.scss';
import '../styles/globals.css';
import 'tailwindcss/dist/tailwind.min.css';
import 'react-tabs/style/react-tabs.css';
import 'codemirror/lib/codemirror.css';
import 'graphiql/graphiql.min.css';
import '../styles/app.scss';
function SafeHydrate({ children }) {
return <div suppressHydrationWarning>{typeof window === 'undefined' ? null : children}</div>;
}
@ -32,12 +32,15 @@ function MyApp({ Component, pageProps }) {
<SafeHydrate>
<NoSsr>
<Provider store={ReduxStore}>
<AppProvider>
<HotkeysProvider>
<Toaster toastOptions={{ duration: 2000 }} />
<Component {...pageProps} />
</HotkeysProvider>
</AppProvider>
<ThemeProvider>
<ToastProvider>
<AppProvider>
<HotkeysProvider>
<Component {...pageProps} />
</HotkeysProvider>
</AppProvider>
</ToastProvider>
</ThemeProvider>
</Provider>
</NoSsr>
</SafeHydrate>

View File

@ -0,0 +1,8 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
color: ${(props) => props.theme.text};
background-color: ${(props) => props.theme.bg};
`;
export default StyledWrapper;

View File

@ -3,6 +3,7 @@ import useIdb from './useIdb';
import useLocalCollectionTreeSync from './useLocalCollectionTreeSync';
import { useDispatch } from 'react-redux';
import { refreshScreenWidth } from 'providers/ReduxStore/slices/app';
import StyledWrapper from './StyledWrapper';
export const AppContext = React.createContext();
@ -28,7 +29,9 @@ export const AppProvider = (props) => {
return (
<AppContext.Provider {...props} value="appProvider">
{props.children}
<StyledWrapper>
{props.children}
</StyledWrapper>
</AppContext.Provider>
);
};

View File

@ -0,0 +1,37 @@
import themes from 'themes/index';
import useLocalStorage from 'src/hooks/useLocalStorage/index';
import { createContext, useContext } from 'react';
import { ThemeProvider as SCThemeProvider } from 'styled-components';
export const ThemeContext = createContext();
export const ThemeProvider = (props) => {
const [storedTheme, setStoredTheme] = useLocalStorage('bruno.theme', 'light');
const theme = themes[storedTheme];
const themeOptions = Object.keys(themes);
const value = {
theme,
themeOptions,
storedTheme,
setStoredTheme
};
return (
<ThemeContext.Provider value={value}>
<SCThemeProvider theme={theme} {...props} />
</ThemeContext.Provider>
);
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error(`useTheme must be used within a ThemeProvider`);
}
return context;
};
export default ThemeProvider;

View File

@ -0,0 +1,31 @@
import React from 'react';
import { Toaster } from 'react-hot-toast';
import { useTheme } from 'providers/Theme';
export const ToastContext = React.createContext();
export const ToastProvider = (props) => {
const {
storedTheme
} = useTheme();
const toastOptions = { duration: 2000 };
if(storedTheme === 'dark') {
toastOptions.style = {
borderRadius: '10px',
background: '#3d3d3d',
color: '#fff'
};
}
return (
<ToastContext.Provider {...props} value="toastProvider">
<Toaster toastOptions={toastOptions} />
<div>
{props.children}
</div>
</ToastContext.Provider>
);
};
export default ToastProvider;

View File

@ -1,89 +1 @@
.btn {
text-align: center;
white-space: nowrap;
outline: none;
box-shadow: none;
border-radius: 3px;
}
.btn-sm {
padding: .215rem .6rem .215rem .6rem;
}
.btn-md {
padding: .4rem 1.1rem;
line-height: 1.47;
}
.btn-default {
&:active,
&:hover,
&:focus {
outline: none;
box-shadow: none;
}
}
.btn-close {
color: #212529;
background: white;
border: solid 1px white;
&:hover,
&:focus {
outline: none;
box-shadow: none;
border: solid 1px #696969;
}
}
.btn-secondary {
color: #212529;
background: #e2e6ea;
border: solid 1px #dae0e5;
.btn-icon {
color: #3f3f3f;
}
&:hover,
&:focus {
border-color: #696969;
outline: none;
box-shadow: none;
}
&:disabled {
color: #545454;
background: #efefef;
border: solid 1px rgb(234, 234, 234);
cursor: not-allowed;
}
&:disabled.btn-icon {
color: #545454;
}
&.btn-gradient {
background: linear-gradient(#fff, #e0e0e0);
border: solid 1px rgb(191, 191, 191);
&:hover {
border: solid 1px rgb(179, 179, 179);
background: linear-gradient(#f6f6f6, #e0e0e0);
}
&:focus,
&:active {
border: solid 1px rgb(129, 129, 129);
outline: none;
box-shadow: 0 8px 6px -11px black;
}
&:disabled {
color: #545454;
background: linear-gradient(#fff, #ececec);
border: solid 1px rgb(234, 234, 234);
}
}
}

View File

@ -3,15 +3,12 @@
--color-brand: #546de5;
--color-text: rgb(52 52 52);
--color-sidebar-collection-item-active-indent-border: #d0d0d0;
--color-sidebar-collection-item-active-background: #e1e1e1;
--color-sidebar-background: #f3f3f3;
--color-request-dragbar-background: #efefef;
--color-request-dragbar-background-active: rgb(200, 200, 200);
--color-tab-inactive: rgb(155 155 155);
--color-tab-active-border: #546de5;
--color-layout-border: #dedede;
--color-codemirror-border: #efefef;
--color-codemirror-background: rgb(243, 243, 243);
--color-text-link: #1663bb;
--color-text-danger: rgb(185, 28, 28);
--color-background-danger: #dc3545;
@ -22,7 +19,6 @@
--color-method-patch: rgb(52 52 52);
--color-method-options: rgb(52 52 52);
--color-method-head: rgb(52 52 52);
--color-table-stripe: #f3f3f3;
}
html, body {
@ -54,7 +50,3 @@ body::-webkit-scrollbar-thumb, .CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
background-color: #cdcdcd;
border-radius: 5rem;
}
.text-link {
color: var(--color-text-link);
}

View File

@ -0,0 +1,226 @@
const darkTheme = {
brand: '#546de5',
text: '#d4d4d4',
textLink: '#569cd6',
bg: '#1e1e1e',
colors: {
text: {
danger: '#f06f57',
muted: '#9d9d9d'
},
bg: {
danger: '#d03544'
}
},
menubar: {
bg: '#333333',
},
sidebar: {
color: '#ccc',
muted: '#9d9d9d',
workspace: {
bg: '#3D3D3D'
},
search: {
border: '1px solid transparent',
bg: '#3D3D3D'
},
collection: {
item: {
bg: '#37373D',
hoverBg: '#2A2D2F',
indentBorder: 'solid 1px #585858',
active: {
indentBorder: 'solid 1px #4c4c4c'
}
}
},
dropdownIcon: {
color: '#ccc'
}
},
welcome: {
heading: '#569cd6',
muted: '#9d9d9d'
},
dropdown: {
color: "rgb(204, 204, 204)",
iconColor: "rgb(204, 204, 204)",
bg: 'rgb(48, 48, 49)',
hoverBg: '#0F395E',
shadow: 'rgb(0 0 0 / 36%) 0px 2px 8px',
seperator: '#444',
labelBg: '#4a4949'
},
request: {
methods: {
get: '#8cd656',
post: '#cd56d6',
put: '#d69956',
delete: '#f06f57'
}
},
requestTabPanel: {
url: {
bg: '#3D3D3D',
icon: 'rgb(204, 204, 204)'
},
dragbar: {
border: '#444',
activeBorder: '#8a8a8a'
},
bodyModeSelect: {
color: 'transparent'
},
responseSendIcon: '#555',
responseStatus: '#ccc',
responseOk: '#8cd656',
responseError: '#f06f57'
},
collection: {
environment: {
bg: '#3D3D3D',
settings: {
bg: '#3D3D3D',
sidebar: {
bg: '#3D3D3D',
borderRight: '#4f4f4f'
},
item: {
border: '#569cd6',
hoverBg: 'transparent',
active: {
bg: 'transparent',
hoverBg: 'transparent'
},
},
gridBorder: '#4f4f4f'
}
}
},
modal: {
title: {
color: '#ccc',
bg: 'rgb(48, 48, 49)',
iconColor: '#ccc'
},
body: {
color: '#ccc',
bg: 'rgb(48, 48, 49)',
},
input : {
bg: 'rgb(65, 65, 65)',
border: 'rgb(65, 65, 65)',
focusBorder: 'rgb(65, 65, 65)'
},
backdrop: {
opacity: 0.2
}
},
button: {
secondary: {
color: 'rgb(204, 204, 204)',
bg: '#0F395E',
border: '#0F395E',
hoverBorder: '#696969'
},
close: {
color: '#ccc',
bg: 'transparent',
border: 'transparent',
hoverBorder: ''
},
disabled: {
color: '#a5a5a5',
bg: '#626262',
border: '#626262'
}
},
tabs: {
active: {
color: '#ccc',
border: '#569cd6'
}
},
requestTabs: {
color: '#ccc',
bg: '#2A2D2F',
borromBorder: '#444',
icon: {
color: '#9f9f9f',
hoverColor: 'rgb(204, 204, 204)',
hoverBg: '#1e1e1e'
},
active: {
bg: '#3D3D3D'
},
shortTab: {
color: '#ccc',
bg: 'transparent',
hoverColor: '#ccc',
hoverBg: '#3D3D3D'
}
},
codemirror: {
bg: '#1e1e1e',
border: 'transparent',
gutter: {
bg: '#1e1e1e'
}
},
table: {
border: '#333',
thead : {
color: 'rgb(204, 204, 204)'
},
striped: '#2A2D2F'
},
plainGrid: {
hoverBg: '#3D3D3D'
},
'primary-text': '#ffffff',
'secondary-text': '#929292',
'sidebar-collection-item-active-background': '#e1e1e1',
'sidebar-background': '#252526',
'sidebar-bottom-bg': '#68217a',
'request-dragbar-background': '#efefef',
'request-dragbar-background-active': 'rgb(200, 200, 200)',
'tab-inactive': 'rgb(155 155 155)',
'tab-active-border': '#546de5',
'layout-border': '#dedede',
'codemirror-border': '#efefef',
'codemirror-background': 'rgb(243, 243, 243)',
'text-link': '#1663bb',
'text-danger': 'rgb(185, 28, 28)',
'background-danger': '#dc3545',
'method-get': 'rgb(5, 150, 105)',
'method-post': '#8e44ad',
'method-delete': 'rgb(185, 28, 28)',
'method-patch': 'rgb(52 52 52)',
'method-options': 'rgb(52 52 52)',
'method-head': 'rgb(52 52 52)',
'table-stripe': '#f3f3f3'
};
export default darkTheme;

View File

@ -0,0 +1,7 @@
import light from './light';
import dark from './dark';
export default {
light,
dark
};

View File

@ -0,0 +1,230 @@
const lightTheme = {
brand: '#546de5',
text: 'rgb(52, 52, 52)',
textLink: '#1663bb',
bg: '#fff',
colors: {
text: {
danger: 'rgb(185, 28, 28)',
muted: '#4b5563',
},
bg: {
danger: '#dc3545'
}
},
menubar: {
bg: 'rgb(44, 44, 44)',
},
sidebar: {
color: 'rgb(52, 52, 52)',
muted: '#4b5563',
workspace: {
bg: '#e1e1e1'
},
search: {
border: '1px solid rgb(211 211 211)',
bg: '#fff'
},
collection: {
item: {
bg: '#e1e1e1',
hoverBg: '#e7e7e7',
indentBorder: 'solid 1px #e1e1e1',
active: {
indentBorder: 'solid 1px #d0d0d0'
}
}
},
dropdownIcon: {
color: 'rgb(110 110 110)'
}
},
welcome: {
heading: '#737373',
muted: '#4b5563'
},
dropdown: {
color: "rgb(48 48 48)",
iconColor: "rgb(75, 85, 99)",
bg: '#fff',
hoverBg: '#e9e9e9',
shadow: 'rgb(50 50 93 / 25%) 0px 6px 12px -2px, rgb(0 0 0 / 30%) 0px 3px 7px -3px',
seperator: '#e7e7e7',
labelBg: '#f3f3f3'
},
request: {
methods: {
get: 'rgb(5, 150, 105)',
post: '#8e44ad',
put: '#ca7811',
delete: 'rgb(185, 28, 28)'
}
},
requestTabPanel: {
url: {
bg: '#f3f3f3',
icon: '#515151'
},
dragbar: {
border: '#efefef',
activeBorder: 'rgb(200, 200, 200)'
},
bodyModeSelect: {
color: '#efefef'
},
responseSendIcon: 'rgb(209, 213, 219)',
responseStatus: 'rgb(117 117 117)',
responseOk: '#047857',
responseError: 'rgb(185, 28, 28)'
},
collection: {
environment: {
bg: '#efefef',
settings: {
bg: 'white',
sidebar: {
bg: '#eaeaea',
borderRight: 'transparent'
},
item: {
border: '#546de5',
hoverBg: '#e4e4e4',
active: {
bg: '#dcdcdc',
hoverBg: '#dcdcdc'
},
},
gridBorder: '#f4f4f4'
}
},
sidebar: {
bg: '#eaeaea'
}
},
modal: {
title: {
color: 'rgb(86 86 86)',
bg: '#f1f1f1',
iconColor: 'black'
},
body: {
color: 'rgb(52, 52, 52)',
bg: 'white',
},
input : {
bg: 'white',
border: '#ccc',
focusBorder: '#8b8b8b'
},
backdrop: {
opacity: 0.4
}
},
button: {
secondary: {
color: '#212529',
bg: '#e2e6ea',
border: '#dae0e5',
hoverBorder: '#696969'
},
close: {
color: '212529',
bg: 'white',
border: 'white',
hoverBorder: ''
},
disabled: {
color: '#9f9f9f',
bg: '#efefef',
border: 'rgb(234, 234, 234)'
}
},
tabs: {
active: {
color: 'rgb(50, 46, 44)',
border: '#546de5'
}
},
requestTabs: {
color: 'rgb(52, 52, 52)',
bg: '#f7f7f7',
borromBorder: '#efefef',
icon: {
color: '#9f9f9f',
hoverColor: 'rgb(76 76 76)',
hoverBg: 'rgb(234, 234, 234)'
},
active: {
bg: '#e7e7e7'
},
shortTab: {
color: 'rgb(117 117 117)',
bg: 'white',
hoverColor: 'rgb(76 76 76)',
hoverBg: '#eaeaea'
}
},
codemirror: {
bg: 'white',
border: '#efefef',
gutter: {
bg: '#f3f3f3'
}
},
table: {
border: '#efefef',
thead : {
color: '#616161'
},
striped: '#f3f3f3'
},
plainGrid: {
hoverBg: '#f4f4f4'
},
'primary-text': 'rgb(52 52 52)',
'secondary-text': '#929292',
'sidebar-collection-item-active-background': '#e1e1e1',
'sidebar-background': '#f3f3f3',
'sidebar-bottom-bg': '#f3f3f3',
'request-dragbar-background': '#efefef',
'request-dragbar-background-active': 'rgb(200, 200, 200)',
'tab-inactive': 'rgb(155 155 155)',
'tab-active-border': '#546de5',
'layout-border': '#dedede',
'codemirror-border': '#efefef',
'codemirror-background': 'rgb(243, 243, 243)',
'text-link': '#1663bb',
'text-danger': 'rgb(185, 28, 28)',
'background-danger': '#dc3545',
'method-get': 'rgb(5, 150, 105)',
'method-post': '#8e44ad',
'method-delete': 'rgb(185, 28, 28)',
'method-patch': 'rgb(52 52 52)',
'method-options': 'rgb(52 52 52)',
'method-head': 'rgb(52 52 52)',
'table-stripe': '#f3f3f3'
};
export default lightTheme;

View File

@ -2,6 +2,7 @@ import each from 'lodash/each';
import get from 'lodash/get';
import fileDialog from 'file-dialog';
import toast from 'react-hot-toast';
import cloneDeep from 'lodash/cloneDeep';
import { uuid } from 'utils/common';
import { collectionSchema } from '@usebruno/schema';
import { saveCollectionToIdb } from 'utils/idb';
@ -29,7 +30,6 @@ const parseJsonCollection = (str) => {
};
const validateSchema = (collection = {}) => {
collection.uid = uuid();
return new Promise((resolve, reject) => {
collectionSchema
.validate(collection)
@ -41,7 +41,9 @@ const validateSchema = (collection = {}) => {
});
};
const updateUidsInCollection = (collection) => {
const updateUidsInCollection = (_collection) => {
const collection = cloneDeep(_collection);
collection.uid = uuid();
const updateItemUids = (items = []) => {
@ -86,7 +88,6 @@ const importCollection = () => {
export const importSampleCollection = () => {
return new Promise((resolve, reject) => {
validateSchema(sampleCollection)
.then(validateSchema)
.then(updateUidsInCollection)
.then(validateSchema)
.then((collection) => saveCollectionToIdb(window.__idb, collection))