Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/en_US/keyboard_shortcuts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ When using main browser window, the following keyboard shortcuts are available:
+----------------------------+--------------------+------------------------------------+
| Shift + Alt + s | Shift + Option + s | Search objects |
+----------------------------+--------------------+------------------------------------+
| Shift + Alt + [ | Shift + Option + [ | Tabbed panel backward |
| Ctrl + Alt + [ | Ctrl + Option + [ | Tabbed panel backward |
+----------------------------+--------------------+------------------------------------+
| Shift + Alt + ] | Shift + Option + ] | Tabbed panel forward |
| Ctrl + Alt + ] | Ctrl + Option + ] | Tabbed panel forward |
+----------------------------+--------------------+------------------------------------+
| Shift + Alt + w | Shift + Ctrl + w | Close tab panel |
+----------------------------+--------------------+------------------------------------+
Expand Down Expand Up @@ -98,6 +98,8 @@ When using the syntax-highlighting SQL editors, the following shortcuts are avai
+--------------------------+----------------------+-------------------------------------+
| Ctrl + / | Cmd + / | Comment/Uncomment code (Block) |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + Shift + d | Cmd + Shift + d | Duplicate current line/selection |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + a | Cmd + a | Select all |
+--------------------------+----------------------+-------------------------------------+
| Ctrl + c | Cmd + c | Copy selected text to the clipboard |
Expand Down
5 changes: 5 additions & 0 deletions docs/en_US/release_notes_9_16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Bundled PostgreSQL Utilities
New features
************

| `Issue #3834 <https://github.com/pgadmin-org/pgadmin4/issues/3834>`_ - Add a keyboard shortcut (Ctrl/Cmd+Shift+D) to duplicate the current line or selection in the SQL editor.
| `Issue #5196 <https://github.com/pgadmin-org/pgadmin4/issues/5196>`_ - Allow the close/unsaved-changes confirmation and other dialogs to be operated from the keyboard (Enter to confirm/save, Escape to cancel) without tabbing to the buttons.
| `Issue #5691 <https://github.com/pgadmin-org/pgadmin4/issues/5691>`_ - Allow closing the Properties, Backup and other object/utility dialogs with the Escape key.
| `Issue #7167 <https://github.com/pgadmin-org/pgadmin4/issues/7167>`_ - Add Ctrl/Cmd+Enter to save and close object and utility dialogs, including the Query Tool sort/filter dialog.
| `Issue #9626 <https://github.com/pgadmin-org/pgadmin4/issues/9626>`_ - Add support for the TOAST tuple target storage parameter in the Materialized View dialog.
| `Issue #9646 <https://github.com/pgadmin-org/pgadmin4/issues/9646>`_ - Make the init container security context in the Helm chart configurable via containerSecurityContext, consistent with the main container.
| `Issue #9699 <https://github.com/pgadmin-org/pgadmin4/issues/9699>`_ - Add support for closing a tab with a middle-click on its title.
Expand All @@ -40,6 +44,7 @@ Bug fixes
*********

| `Issue #6308 <https://github.com/pgadmin-org/pgadmin4/issues/6308>`_ - Fix the infinite loading spinner after an idle database connection is silently dropped, by detecting stale connections and offering a reconnect dialog.
| `Issue #7232 <https://github.com/pgadmin-org/pgadmin4/issues/7232>`_ - Fix the tabbed panel forward/backward shortcut not switching the main tabs when keyboard focus is inside a tool (SQL editor, PSQL terminal, ERD or Schema Diff). The default shortcut is now Ctrl/Cmd+Alt+] / [ to avoid colliding with the Query Tool's inner-panel navigation.
| `Issue #9595 <https://github.com/pgadmin-org/pgadmin4/issues/9595>`_ - Fix missing ALTER ... SET DEFAULT statements for inherited columns in the generated table SQL/EDIT script.
| `Issue #9677 <https://github.com/pgadmin-org/pgadmin4/issues/9677>`_ - Fix the Unlogged table toggle in table properties not generating any ALTER TABLE ... SET LOGGED/UNLOGGED statement.
| `Issue #9828 <https://github.com/pgadmin-org/pgadmin4/issues/9828>`_ - Fix tool calls failing against OpenAI-compatible providers that emit empty/null name, arguments, or id fields in streaming continuation deltas.
Expand Down
12 changes: 6 additions & 6 deletions web/pgadmin/browser/register_browser_preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ def register_browser_preferences(self):
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 91, 'char': '['}
'shift': False,
'control': True,
'key': {'key_code': 219, 'char': '['}
},
category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
fields=fields
Expand Down Expand Up @@ -200,9 +200,9 @@ def register_browser_preferences(self):
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 93, 'char': ']'}
'shift': False,
'control': True,
'key': {'key_code': 221, 'char': ']'}
},
category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
fields=fields
Expand Down
61 changes: 28 additions & 33 deletions web/pgadmin/browser/static/js/keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,42 +149,37 @@ _.extend(pgBrowser.keyboardNavigation, {
bindRightPanel: function(event, combo) {
const self = this;
const shortcutObj = this.keyboardShortcut;
const activeElement = document.activeElement;
const rootDock = document.getElementById('root');
if (!rootDock) return;

if (activeElement.closest('.dock-tab-btn')) {
const currDockTab = activeElement.closest('.dock-tab-btn');
const dockLayout = currDockTab.closest('.dock-layout');
const dockLayoutTabs = dockLayout ? Array.from(dockLayout.querySelectorAll('.dock-tab-btn')) : null;
// Find the active workspace tab independently of where the keyboard focus
// currently sits. Previously this relied on document.activeElement, which
// breaks when focus is inside a tool's own (nested) dock layout - the SQL
// editor, an ERD/Schema Diff canvas - or inside the PSQL iframe, because
// the resolved tab id then belonged to the tool's inner tab rather than a
// main workspace tab (issue #7232).
//
// rc-dock renders the object explorer and the workspace as separate
// tab-sets, and each marks its own active tab with `dock-tab-active`, so
// prefer the active tab that is not the object explorer.
const activeTabBtns = Array.from(
rootDock.querySelectorAll('.dock-tab.dock-tab-active .dock-tab-btn'));
const activeTabBtn =
activeTabBtns.find(tab => !tab.id.includes('id-object-explorer')) ||
activeTabBtns[0];
if (!activeTabBtn) return;

if (dockLayoutTabs && dockLayoutTabs.length > 1) {
const activeTabIndex = dockLayoutTabs.indexOf(currDockTab);
self._focusTab(dockLayoutTabs, activeTabIndex, shortcutObj, combo);
}
}
else if (activeElement.nodeName === 'IFRAME' || activeElement.closest('.dock-tabpane.dock-tabpane-active')) {
let activeTabId = '';
activeTabId = (activeElement.nodeName === 'IFRAME') ? activeElement.id : activeElement.closest('.dock-tabpane.dock-tabpane-active').id;
const dockLayout = document.getElementById('root');
const dockLayoutTabs = dockLayout ? Array.from(dockLayout.querySelectorAll('.dock-tab-btn')) : null;

if (dockLayoutTabs && dockLayoutTabs.length > 1 && activeTabId) {
const activeTabIndex = dockLayoutTabs.findIndex(tab => tab.id.slice(14) === activeTabId);
self._focusTab(dockLayoutTabs, activeTabIndex, shortcutObj, combo);
}
}
else if (activeElement === document.body || document.querySelector('div[data-test="app-menu-bar"]')) {
const activeTabs = document.getElementsByClassName('dock-tabpane dock-tabpane-active');

if (activeTabs.length > 1) {
const activeTabId = activeTabs[1].id;
const dockLayout = document.getElementById('root');
const dockLayoutTabs = dockLayout ? Array.from(dockLayout.querySelectorAll('.dock-tab-btn')) : null;
// Restrict navigation to the tabs of the same tab-set (dock panel) as the
// active tab, so cycling stays within the workspace tabs and does not
// include the object explorer or a tool's nested tabs.
const panel = activeTabBtn.closest('.dock-panel');
const dockLayoutTabs = panel ? Array.from(
panel.querySelectorAll('.dock-tab-btn'))
.filter(tab => tab.closest('.dock-panel') === panel) : [];

if (dockLayoutTabs && dockLayoutTabs.length > 1 && activeTabId) {
const activeTabIndex = dockLayoutTabs.findIndex(tab => tab.id.slice(14) === activeTabId);
self._focusTab(dockLayoutTabs, activeTabIndex, shortcutObj, combo);
}
}
if (dockLayoutTabs.length > 1) {
const activeTabIndex = dockLayoutTabs.indexOf(activeTabBtn);
self._focusTab(dockLayoutTabs, activeTabIndex, shortcutObj, combo);
}
},
_focusTab: function(dockLayoutTabs, activeTabIdx, shortcut_obj, combo){
Expand Down
25 changes: 24 additions & 1 deletion web/pgadmin/static/js/SchemaView/SchemaDialogView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,32 @@ export default function SchemaDialogView({
return <SaveIcon />;
};

const onKeyDown = (e) => {
// Ctrl/Cmd+Enter saves and closes the dialog from anywhere within it
// (issue #7167). onSaveClick is a no-op when there is nothing to save or
// there is a validation error, so this is safe to call unconditionally.
Comment on lines +181 to +183
if ((e.ctrlKey || e.metaKey) && !e.altKey && e.key === 'Enter') {
e.preventDefault();
onSaveClick();
return;
}

// Escape closes the dialog, mirroring the Close button (issue #5691).
// This is needed for dialogs rendered as dockable panels (Properties,
// Backup, and other utility dialogs); dialogs rendered inside a MUI modal
// already close on Escape, so skip those to avoid a double close. The
// !e.defaultPrevented guard lets an inner control that handles Escape
// (e.g. an open dropdown) consume it first.
if (e.key === 'Escape' && !e.defaultPrevented && props.onClose &&
!e.currentTarget.closest('.MuiDialog-root')) {
e.preventDefault();
props.onClose();
}
};

/* I am Groot */
return useMemo(() =>
<StyledBox>
<StyledBox onKeyDown={onKeyDown}>
<SchemaStateContext.Provider value={schemaState}>
<Box className='Dialog-form'>
<FormLoader/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
keymap,
} from '@codemirror/view';
import { EditorState, Compartment } from '@codemirror/state';
import { history, defaultKeymap, historyKeymap, indentLess, indentMore, deleteCharBackwardStrict } from '@codemirror/commands';
import { history, defaultKeymap, historyKeymap, indentLess, indentMore, deleteCharBackwardStrict, copyLineDown } from '@codemirror/commands';
import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap, acceptCompletion } from '@codemirror/autocomplete';
import {
foldGutter,
Expand Down Expand Up @@ -137,6 +137,12 @@ const defaultExtensions = [
key: 'Backspace',
preventDefault: true,
run: deleteCharBackwardStrict,
},{
// Duplicate the current line, or the selected lines if there is a
// selection (issue #3834).
key: 'Mod-Shift-d',
preventDefault: true,
run: copyLineDown,
}]),
PgSQL.language.data.of({
autocomplete: false,
Expand Down
Loading