From 4b97ca423fafa8f397d1bd314c9b3a8aa0bd2e50 Mon Sep 17 00:00:00 2001
From: Sanjai Kumar <84461672+sanjai0py@users.noreply.github.com>
Date: Mon, 30 Sep 2024 17:20:46 +0530
Subject: [PATCH] Added Keybindings tab. (#3204)
---
.../Preferences/Keybindings/StyledWrapper.js | 46 ++++++++++++++
.../Preferences/Keybindings/index.js | 45 ++++++++++++++
.../src/components/Preferences/index.js | 10 ++++
.../bruno-app/src/providers/Hotkeys/index.js | 33 +++++-----
.../src/providers/Hotkeys/keyMappings.js | 60 +++++++++++++++++++
5 files changed, 178 insertions(+), 16 deletions(-)
create mode 100644 packages/bruno-app/src/components/Preferences/Keybindings/StyledWrapper.js
create mode 100644 packages/bruno-app/src/components/Preferences/Keybindings/index.js
create mode 100644 packages/bruno-app/src/providers/Hotkeys/keyMappings.js
diff --git a/packages/bruno-app/src/components/Preferences/Keybindings/StyledWrapper.js b/packages/bruno-app/src/components/Preferences/Keybindings/StyledWrapper.js
new file mode 100644
index 0000000000..e129693887
--- /dev/null
+++ b/packages/bruno-app/src/components/Preferences/Keybindings/StyledWrapper.js
@@ -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;
diff --git a/packages/bruno-app/src/components/Preferences/Keybindings/index.js b/packages/bruno-app/src/components/Preferences/Keybindings/index.js
new file mode 100644
index 0000000000..d2bc918aa5
--- /dev/null
+++ b/packages/bruno-app/src/components/Preferences/Keybindings/index.js
@@ -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 (
+
+
+
+
+
+ Command |
+ Keybinding |
+
+
+
+ {keyMapping ? (
+ Object.entries(keyMapping).map(([action, { name, keys }], index) => (
+
+ {name} |
+
+ {keys.split('+').map((key, i) => (
+
+ {key}
+
+ ))}
+ |
+
+ ))
+ ) : (
+
+ No key bindings available |
+
+ )}
+
+
+
+
+ );
+};
+
+export default Keybindings;
diff --git a/packages/bruno-app/src/components/Preferences/index.js b/packages/bruno-app/src/components/Preferences/index.js
index 782c3361c4..562bf143e7 100644
--- a/packages/bruno-app/src/components/Preferences/index.js
+++ b/packages/bruno-app/src/components/Preferences/index.js
@@ -1,9 +1,12 @@
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 Keybindings from './Keybindings';
+
import StyledWrapper from './StyledWrapper';
import Interface from 'components/Preferences/Interface';
@@ -30,6 +33,10 @@ const Preferences = ({ onClose }) => {
return ;
}
+ case 'keybindings': {
+ return ;
+ }
+
case 'support': {
return ;
}
@@ -49,6 +56,9 @@ const Preferences = ({ onClose }) => {
setTab('proxy')}>
Proxy
+ setTab('keybindings')}>
+ Keybindings
+
setTab('support')}>
Support
diff --git a/packages/bruno-app/src/providers/Hotkeys/index.js b/packages/bruno-app/src/providers/Hotkeys/index.js
index 7f7ab98b0d..3e64234c5f 100644
--- a/packages/bruno-app/src/providers/Hotkeys/index.js
+++ b/packages/bruno-app/src/providers/Hotkeys/index.js
@@ -10,6 +10,7 @@ import { findCollectionByUid, findItemInCollection } from 'utils/collections';
import { closeTabs, switchTab } from 'providers/ReduxStore/slices/tabs';
import { EnvironmentDrawer } from 'src/feature/environment-editor';
import { NewRequestModal } from 'src/feature/sidebar-menu/components/modals/NewRequestModal';
+import { getKeyBindingsForActionAllOS } from './keyMappings';
export const HotkeysContext = React.createContext();
@@ -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 {
@@ -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);
@@ -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);
@@ -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);
@@ -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]
@@ -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'
@@ -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'
@@ -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);
@@ -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]);
diff --git a/packages/bruno-app/src/providers/Hotkeys/keyMappings.js b/packages/bruno-app/src/providers/Hotkeys/keyMappings.js
new file mode 100644
index 0000000000..05ad4531b9
--- /dev/null
+++ b/packages/bruno-app/src/providers/Hotkeys/keyMappings.js
@@ -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];
+};