mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-21 23:43:15 +01:00
Added Keybindings tab. (#3204)
* Added Keybindings tab. * Minor Refactoring
This commit is contained in:
parent
02a82c5371
commit
95e56cd9c9
@ -0,0 +1,46 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const StyledWrapper = styled.div`
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
thead,
|
||||||
|
td {
|
||||||
|
border: 2px solid ${(props) => props.theme.table.border};
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
color: ${(props) => props.theme.table.thead.color};
|
||||||
|
font-size: 1rem;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-button {
|
||||||
|
display: inline-block;
|
||||||
|
color: ${(props) => props.theme.colors.text.white};
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 1px 5px;
|
||||||
|
font-family: monospace;
|
||||||
|
margin-right: 8px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default StyledWrapper;
|
@ -0,0 +1,45 @@
|
|||||||
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
import React from 'react';
|
||||||
|
import { getKeyBindingsForOS } from 'providers/Hotkeys/keyMappings';
|
||||||
|
import { isMacOS } from 'utils/common/platform';
|
||||||
|
|
||||||
|
const Keybindings = ({ close }) => {
|
||||||
|
const keyMapping = getKeyBindingsForOS(isMacOS() ? 'mac' : 'windows');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledWrapper className="w-full">
|
||||||
|
<div className="table-container">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Command</th>
|
||||||
|
<th>Keybinding</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{keyMapping ? (
|
||||||
|
Object.entries(keyMapping).map(([action, { name, keys }], index) => (
|
||||||
|
<tr key={index}>
|
||||||
|
<td>{name}</td>
|
||||||
|
<td>
|
||||||
|
{keys.split('+').map((key, i) => (
|
||||||
|
<div className="key-button" key={i}>
|
||||||
|
{key}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan="2">No key bindings available</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Keybindings;
|
@ -1,11 +1,14 @@
|
|||||||
import Modal from 'components/Modal/index';
|
import Modal from 'components/Modal/index';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
import Support from './Support';
|
import Support from './Support';
|
||||||
import General from './General';
|
import General from './General';
|
||||||
import Proxy from './ProxySettings';
|
import Proxy from './ProxySettings';
|
||||||
|
import Display from './Display';
|
||||||
|
import Keybindings from './Keybindings';
|
||||||
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
import Display from './Display/index';
|
|
||||||
|
|
||||||
const Preferences = ({ onClose }) => {
|
const Preferences = ({ onClose }) => {
|
||||||
const [tab, setTab] = useState('general');
|
const [tab, setTab] = useState('general');
|
||||||
@ -30,6 +33,10 @@ const Preferences = ({ onClose }) => {
|
|||||||
return <Display close={onClose} />;
|
return <Display close={onClose} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'keybindings': {
|
||||||
|
return <Keybindings close={onClose} />;
|
||||||
|
}
|
||||||
|
|
||||||
case 'support': {
|
case 'support': {
|
||||||
return <Support />;
|
return <Support />;
|
||||||
}
|
}
|
||||||
@ -50,6 +57,9 @@ const Preferences = ({ onClose }) => {
|
|||||||
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
|
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
|
||||||
Proxy
|
Proxy
|
||||||
</div>
|
</div>
|
||||||
|
<div className={getTabClassname('keybindings')} role="tab" onClick={() => setTab('keybindings')}>
|
||||||
|
Keybindings
|
||||||
|
</div>
|
||||||
<div className={getTabClassname('support')} role="tab" onClick={() => setTab('support')}>
|
<div className={getTabClassname('support')} role="tab" onClick={() => setTab('support')}>
|
||||||
Support
|
Support
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,6 +10,7 @@ import NewRequest from 'components/Sidebar/NewRequest';
|
|||||||
import { sendRequest, saveRequest, saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
import { sendRequest, saveRequest, saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
import { findCollectionByUid, findItemInCollection } from 'utils/collections';
|
import { findCollectionByUid, findItemInCollection } from 'utils/collections';
|
||||||
import { closeTabs, switchTab } from 'providers/ReduxStore/slices/tabs';
|
import { closeTabs, switchTab } from 'providers/ReduxStore/slices/tabs';
|
||||||
|
import { getKeyBindingsForActionAllOS } from './keyMappings';
|
||||||
|
|
||||||
export const HotkeysContext = React.createContext();
|
export const HotkeysContext = React.createContext();
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ export const HotkeysProvider = (props) => {
|
|||||||
|
|
||||||
// save hotkey
|
// save hotkey
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+s', 'ctrl+s'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('save')], (e) => {
|
||||||
if (isEnvironmentSettingsModalOpen) {
|
if (isEnvironmentSettingsModalOpen) {
|
||||||
console.log('todo: save environment settings');
|
console.log('todo: save environment settings');
|
||||||
} else {
|
} else {
|
||||||
@ -68,13 +69,13 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+s', 'ctrl+s']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('save')]);
|
||||||
};
|
};
|
||||||
}, [activeTabUid, tabs, saveRequest, collections, isEnvironmentSettingsModalOpen]);
|
}, [activeTabUid, tabs, saveRequest, collections, isEnvironmentSettingsModalOpen]);
|
||||||
|
|
||||||
// send request (ctrl/cmd + enter)
|
// send request (ctrl/cmd + enter)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+enter', 'ctrl+enter'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('sendRequest')], (e) => {
|
||||||
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
||||||
if (activeTab) {
|
if (activeTab) {
|
||||||
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
||||||
@ -95,13 +96,13 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+enter', 'ctrl+enter']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('sendRequest')]);
|
||||||
};
|
};
|
||||||
}, [activeTabUid, tabs, saveRequest, collections]);
|
}, [activeTabUid, tabs, saveRequest, collections]);
|
||||||
|
|
||||||
// edit environments (ctrl/cmd + e)
|
// edit environments (ctrl/cmd + e)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+e', 'ctrl+e'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('editEnvironment')], (e) => {
|
||||||
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
||||||
if (activeTab) {
|
if (activeTab) {
|
||||||
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
||||||
@ -115,13 +116,13 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+e', 'ctrl+e']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('editEnvironment')]);
|
||||||
};
|
};
|
||||||
}, [activeTabUid, tabs, collections, setShowEnvSettingsModal]);
|
}, [activeTabUid, tabs, collections, setShowEnvSettingsModal]);
|
||||||
|
|
||||||
// new request (ctrl/cmd + b)
|
// new request (ctrl/cmd + b)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+b', 'ctrl+b'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('newRequest')], (e) => {
|
||||||
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
||||||
if (activeTab) {
|
if (activeTab) {
|
||||||
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
||||||
@ -135,13 +136,13 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+b', 'ctrl+b']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('newRequest')]);
|
||||||
};
|
};
|
||||||
}, [activeTabUid, tabs, collections, setShowNewRequestModal]);
|
}, [activeTabUid, tabs, collections, setShowNewRequestModal]);
|
||||||
|
|
||||||
// close tab hotkey
|
// close tab hotkey
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+w', 'ctrl+w'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('closeTab')], (e) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
closeTabs({
|
closeTabs({
|
||||||
tabUids: [activeTabUid]
|
tabUids: [activeTabUid]
|
||||||
@ -152,13 +153,13 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+w', 'ctrl+w']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('closeTab')]);
|
||||||
};
|
};
|
||||||
}, [activeTabUid]);
|
}, [activeTabUid]);
|
||||||
|
|
||||||
// Switch to the previous tab
|
// Switch to the previous tab
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+pageup', 'ctrl+pageup'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('switchToPreviousTab')], (e) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
switchTab({
|
switchTab({
|
||||||
direction: 'pageup'
|
direction: 'pageup'
|
||||||
@ -169,13 +170,13 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+pageup', 'ctrl+pageup']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('switchToPreviousTab')]);
|
||||||
};
|
};
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
// Switch to the next tab
|
// Switch to the next tab
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+pagedown', 'ctrl+pagedown'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('switchToNextTab')], (e) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
switchTab({
|
switchTab({
|
||||||
direction: 'pagedown'
|
direction: 'pagedown'
|
||||||
@ -186,13 +187,13 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+pagedown', 'ctrl+pagedown']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('switchToNextTab')]);
|
||||||
};
|
};
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
// Close all tabs
|
// Close all tabs
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Mousetrap.bind(['command+shift+w', 'ctrl+shift+w'], (e) => {
|
Mousetrap.bind([...getKeyBindingsForActionAllOS('closeAllTabs')], (e) => {
|
||||||
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
|
||||||
if (activeTab) {
|
if (activeTab) {
|
||||||
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
const collection = findCollectionByUid(collections, activeTab.collectionUid);
|
||||||
@ -211,7 +212,7 @@ export const HotkeysProvider = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Mousetrap.unbind(['command+shift+w', 'ctrl+shift+w']);
|
Mousetrap.unbind([...getKeyBindingsForActionAllOS('closeAllTabs')]);
|
||||||
};
|
};
|
||||||
}, [activeTabUid, tabs, collections, dispatch]);
|
}, [activeTabUid, tabs, collections, dispatch]);
|
||||||
|
|
||||||
|
60
packages/bruno-app/src/providers/Hotkeys/keyMappings.js
Normal file
60
packages/bruno-app/src/providers/Hotkeys/keyMappings.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
const KeyMapping = {
|
||||||
|
save: { mac: 'command+s', windows: 'ctrl+s', name: 'Save' },
|
||||||
|
sendRequest: { mac: 'command+enter', windows: 'ctrl+enter', name: 'Send Request' },
|
||||||
|
editEnvironment: { mac: 'command+e', windows: 'ctrl+e', name: 'Edit Environment' },
|
||||||
|
newRequest: { mac: 'command+b', windows: 'ctrl+b', name: 'New Request' },
|
||||||
|
closeTab: { mac: 'command+w', windows: 'ctrl+w', name: 'Close Tab' },
|
||||||
|
openPreferences: { mac: 'command+,', windows: 'ctrl+,', name: 'Open Preferences' },
|
||||||
|
minimizeWindow: {
|
||||||
|
mac: 'command+Shift+Q',
|
||||||
|
windows: 'control+Shift+Q',
|
||||||
|
name: 'Minimize Window'
|
||||||
|
},
|
||||||
|
switchToPreviousTab: {
|
||||||
|
mac: 'command+pageup',
|
||||||
|
windows: 'ctrl+pageup',
|
||||||
|
name: 'Switch to Previous Tab'
|
||||||
|
},
|
||||||
|
switchToNextTab: {
|
||||||
|
mac: 'command+pagedown',
|
||||||
|
windows: 'ctrl+pagedown',
|
||||||
|
name: 'Switch to Next Tab'
|
||||||
|
},
|
||||||
|
closeAllTabs: { mac: 'command+shift+w', windows: 'ctrl+shift+w', name: 'Close All Tabs' }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the key bindings for a specific operating system.
|
||||||
|
*
|
||||||
|
* @param {string} os - The operating system (e.g., 'mac', 'windows').
|
||||||
|
* @returns {Object} An object containing the key bindings for the specified OS.
|
||||||
|
*/
|
||||||
|
export const getKeyBindingsForOS = (os) => {
|
||||||
|
const keyBindings = {};
|
||||||
|
for (const [action, { name, ...keys }] of Object.entries(KeyMapping)) {
|
||||||
|
if (keys[os]) {
|
||||||
|
keyBindings[action] = {
|
||||||
|
keys: keys[os],
|
||||||
|
name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keyBindings;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the key bindings for a specific action across all operating systems.
|
||||||
|
*
|
||||||
|
* @param {string} action - The action for which to retrieve key bindings.
|
||||||
|
* @returns {Object|null} An object containing the key bindings for macOS, Windows, or null if the action is not found.
|
||||||
|
*/
|
||||||
|
export const getKeyBindingsForActionAllOS = (action) => {
|
||||||
|
const actionBindings = KeyMapping[action];
|
||||||
|
|
||||||
|
if (!actionBindings) {
|
||||||
|
console.warn(`Action "${action}" not found in KeyMapping.`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [actionBindings.mac, actionBindings.windows];
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user