Skip to content

Commit

Permalink
Added Keybindings tab. (usebruno#3204)
Browse files Browse the repository at this point in the history
* Added Keybindings tab.

* Minor Refactoring
  • Loading branch information
sanjai0py authored Sep 30, 2024
1 parent 02a82c5 commit 95e56cd
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -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;
45 changes: 45 additions & 0 deletions packages/bruno-app/src/components/Preferences/Keybindings/index.js
Original file line number Diff line number Diff line change
@@ -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;
12 changes: 11 additions & 1 deletion packages/bruno-app/src/components/Preferences/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import Modal from 'components/Modal/index';
import classnames from 'classnames';
import React, { useState } from 'react';

import Support from './Support';
import General from './General';
import Proxy from './ProxySettings';
import Display from './Display';
import Keybindings from './Keybindings';

import StyledWrapper from './StyledWrapper';
import Display from './Display/index';

const Preferences = ({ onClose }) => {
const [tab, setTab] = useState('general');
Expand All @@ -30,6 +33,10 @@ const Preferences = ({ onClose }) => {
return <Display close={onClose} />;
}

case 'keybindings': {
return <Keybindings close={onClose} />;
}

case 'support': {
return <Support />;
}
Expand All @@ -50,6 +57,9 @@ const Preferences = ({ onClose }) => {
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
Proxy
</div>
<div className={getTabClassname('keybindings')} role="tab" onClick={() => setTab('keybindings')}>
Keybindings
</div>
<div className={getTabClassname('support')} role="tab" onClick={() => setTab('support')}>
Support
</div>
Expand Down
33 changes: 17 additions & 16 deletions packages/bruno-app/src/providers/Hotkeys/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import NewRequest from 'components/Sidebar/NewRequest';
import { sendRequest, saveRequest, saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { findCollectionByUid, findItemInCollection } from 'utils/collections';
import { closeTabs, switchTab } from 'providers/ReduxStore/slices/tabs';
import { getKeyBindingsForActionAllOS } from './keyMappings';

export const HotkeysContext = React.createContext();

Expand Down Expand Up @@ -43,7 +44,7 @@ export const HotkeysProvider = (props) => {

// save hotkey
useEffect(() => {
Mousetrap.bind(['command+s', 'ctrl+s'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('save')], (e) => {
if (isEnvironmentSettingsModalOpen) {
console.log('todo: save environment settings');
} else {
Expand All @@ -68,13 +69,13 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+s', 'ctrl+s']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('save')]);
};
}, [activeTabUid, tabs, saveRequest, collections, isEnvironmentSettingsModalOpen]);

// send request (ctrl/cmd + enter)
useEffect(() => {
Mousetrap.bind(['command+enter', 'ctrl+enter'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('sendRequest')], (e) => {
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
if (activeTab) {
const collection = findCollectionByUid(collections, activeTab.collectionUid);
Expand All @@ -95,13 +96,13 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+enter', 'ctrl+enter']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('sendRequest')]);
};
}, [activeTabUid, tabs, saveRequest, collections]);

// edit environments (ctrl/cmd + e)
useEffect(() => {
Mousetrap.bind(['command+e', 'ctrl+e'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('editEnvironment')], (e) => {
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
if (activeTab) {
const collection = findCollectionByUid(collections, activeTab.collectionUid);
Expand All @@ -115,13 +116,13 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+e', 'ctrl+e']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('editEnvironment')]);
};
}, [activeTabUid, tabs, collections, setShowEnvSettingsModal]);

// new request (ctrl/cmd + b)
useEffect(() => {
Mousetrap.bind(['command+b', 'ctrl+b'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('newRequest')], (e) => {
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
if (activeTab) {
const collection = findCollectionByUid(collections, activeTab.collectionUid);
Expand All @@ -135,13 +136,13 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+b', 'ctrl+b']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('newRequest')]);
};
}, [activeTabUid, tabs, collections, setShowNewRequestModal]);

// close tab hotkey
useEffect(() => {
Mousetrap.bind(['command+w', 'ctrl+w'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('closeTab')], (e) => {
dispatch(
closeTabs({
tabUids: [activeTabUid]
Expand All @@ -152,13 +153,13 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+w', 'ctrl+w']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('closeTab')]);
};
}, [activeTabUid]);

// Switch to the previous tab
useEffect(() => {
Mousetrap.bind(['command+pageup', 'ctrl+pageup'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('switchToPreviousTab')], (e) => {
dispatch(
switchTab({
direction: 'pageup'
Expand All @@ -169,13 +170,13 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+pageup', 'ctrl+pageup']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('switchToPreviousTab')]);
};
}, [dispatch]);

// Switch to the next tab
useEffect(() => {
Mousetrap.bind(['command+pagedown', 'ctrl+pagedown'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('switchToNextTab')], (e) => {
dispatch(
switchTab({
direction: 'pagedown'
Expand All @@ -186,13 +187,13 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+pagedown', 'ctrl+pagedown']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('switchToNextTab')]);
};
}, [dispatch]);

// Close all tabs
useEffect(() => {
Mousetrap.bind(['command+shift+w', 'ctrl+shift+w'], (e) => {
Mousetrap.bind([...getKeyBindingsForActionAllOS('closeAllTabs')], (e) => {
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
if (activeTab) {
const collection = findCollectionByUid(collections, activeTab.collectionUid);
Expand All @@ -211,7 +212,7 @@ export const HotkeysProvider = (props) => {
});

return () => {
Mousetrap.unbind(['command+shift+w', 'ctrl+shift+w']);
Mousetrap.unbind([...getKeyBindingsForActionAllOS('closeAllTabs')]);
};
}, [activeTabUid, tabs, collections, dispatch]);

Expand Down
60 changes: 60 additions & 0 deletions packages/bruno-app/src/providers/Hotkeys/keyMappings.js
Original file line number Diff line number Diff line change
@@ -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];
};

0 comments on commit 95e56cd

Please sign in to comment.