feat(sidebar): add connection copy paste shortcuts

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-07-04 12:19:56 +08:00
1 parent d8047a77b4
commit 90d2ede740
19 files changed
+509 -20

No files matched your search

@@ -3,6 +3,7 @@ import { DEFAULT_SHORTCUT_SETTINGS, SHORTCUT_DEFINITIONS, findShortcutConflict,
describe("shortcutRegistry editor actions", () => {
const formatterEditorActionIds: ShortcutActionId[] = ["formatSql", "indentMore", "indentLess", "duplicateLine", "deleteLine", "moveLineUp", "moveLineDown", "copyLineUp", "copyLineDown", "undo", "redo", "selectAll"];
const sidebarShortcutActionIds: ShortcutActionId[] = ["copySidebarSelection", "pasteSidebarSelection", "editSidebarConnection"];
it("registers formatter editor shortcuts in the generic editor scope", () => {
for (const actionId of formatterEditorActionIds) {
@@ -36,4 +37,20 @@ describe("shortcutRegistry editor actions", () => {
expect(findShortcutConflict("duplicateLine", shortcuts.duplicateLine, shortcuts)).toBe("find");
});
it("registers sidebar shortcuts in the sidebar scope", () => {
for (const actionId of sidebarShortcutActionIds) {
const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === actionId);
expect(definition?.scope).toBe("sidebar");
expect(DEFAULT_SHORTCUT_SETTINGS[actionId]).toBe(definition?.defaultShortcut);
}
});
it("detects conflicts only within sidebar shortcuts", () => {
const shortcuts = normalizeShortcutSettings({ copySidebarSelection: "Mod+E" });
expect(findShortcutConflict("copySidebarSelection", shortcuts.copySidebarSelection, shortcuts)).toBe("editSidebarConnection");
expect(findShortcutConflict("copyCurrentRow", shortcuts.copyCurrentRow, shortcuts)).toBe(null);
});
});
+12
View File
@@ -146,6 +146,18 @@ export function isToggleSidebarShortcut(event: ShortcutLikeEvent, shortcuts?: Pa
return matchesShortcut(event, actionShortcut("toggleSidebar", shortcuts));
}
export function isCopySidebarSelectionShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
return matchesShortcut(event, actionShortcut("copySidebarSelection", shortcuts));
}
export function isPasteSidebarSelectionShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
return matchesShortcut(event, actionShortcut("pasteSidebarSelection", shortcuts));
}
export function isEditSidebarConnectionShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
return matchesShortcut(event, actionShortcut("editSidebarConnection", shortcuts));
}
export function isQuickOpenShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
return matchesShortcut(event, actionShortcut("quickOpen", shortcuts));
}
+23 -2
View File
@@ -40,9 +40,12 @@ export type ShortcutActionId =
| "refreshData"
| "toggleTranspose"
| "cancelSearch"
| "toggleSidebar";
| "toggleSidebar"
| "copySidebarSelection"
| "pasteSidebarSelection"
| "editSidebarConnection";
export type ShortcutScope = "global" | "editor" | "grid" | "search";
export type ShortcutScope = "global" | "editor" | "grid" | "search" | "sidebar";
export interface ShortcutDefinition {
id: ShortcutActionId;
@@ -306,6 +309,24 @@ export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [
scope: "global",
defaultShortcut: "Mod+B",
},
{
id: "copySidebarSelection",
labelKey: "settings.shortcutCopySidebarSelection",
scope: "sidebar",
defaultShortcut: "Mod+C",
},
{
id: "pasteSidebarSelection",
labelKey: "settings.shortcutPasteSidebarSelection",
scope: "sidebar",
defaultShortcut: "Mod+V",
},
{
id: "editSidebarConnection",
labelKey: "settings.shortcutEditSidebarConnection",
scope: "sidebar",
defaultShortcut: "Mod+E",
},
];
export const DEFAULT_SHORTCUT_SETTINGS: ShortcutSettings = Object.fromEntries(SHORTCUT_DEFINITIONS.map((definition) => [definition.id, definition.defaultShortcut])) as ShortcutSettings;
@@ -2,7 +2,7 @@ import type { TreeNode } from "@/types/database";
type ConnectionTreeNode = TreeNode & { connectionId: string };
function isConnectionNode(node: TreeNode): node is ConnectionTreeNode {
export function isConnectionNode(node: TreeNode): node is ConnectionTreeNode {
return node.type === "connection" && !!node.connectionId;
}
@@ -22,3 +22,26 @@ export function selectedConnectionDeleteTargets(currentNode: TreeNode, selectedN
export function selectedConnectionDuplicateTargets(currentNode: TreeNode, selectedNodes: TreeNode[]): ConnectionTreeNode[] {
return selectedConnectionActionTargets(currentNode, selectedNodes);
}
export function selectedConnectionClipboardTargets(currentNode: TreeNode, selectedNodes: TreeNode[]): ConnectionTreeNode[] {
return selectedConnectionActionTargets(currentNode, selectedNodes);
}
export function selectedConnectionEditTarget(currentNode: TreeNode, selectedNodes: TreeNode[]): ConnectionTreeNode | null {
if (!isConnectionNode(currentNode)) return null;
const selectedContainsCurrent = selectedNodes.some((node) => node.id === currentNode.id);
if (selectedNodes.length > 1 && selectedContainsCurrent) return null;
return currentNode;
}
export function selectedConnectionClipboardNodes(selectedNodes: TreeNode[]): ConnectionTreeNode[] {
if (selectedNodes.length === 0 || !selectedNodes.every(isConnectionNode)) return [];
return selectedNodes;
}
export function connectionPasteTargetGroupId(node: TreeNode | null | undefined, groupIdForConnection: (connectionId: string) => string | null): string | null {
if (!node) return null;
if (node.type === "connection-group") return node.id;
if (isConnectionNode(node)) return groupIdForConnection(node.connectionId);
return null;
}