feat(shortcuts): open data in a new tab

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-07-15 21:43:03 +08:00
1 parent 75652b10fe
commit 11536da7e5
16 files changed
+265 -23

No files matched your search

@@ -1,4 +1,4 @@
import { parseShortcutParts } from "@/lib/editor/shortcutDisplay";
import { isMacShortcutPlatform, parseShortcutParts } from "@/lib/editor/shortcutDisplay";
import { normalizeShortcutSettings, type ShortcutActionId, type ShortcutSettings } from "@/lib/editor/shortcutRegistry";
export interface ShortcutLikeEvent {
@@ -41,6 +41,29 @@ export function eventToShortcut(event: ShortcutLikeEvent): string | null {
return parts.join("+");
}
export function eventToModifierOnlyShortcut(event: ShortcutLikeEvent, platform = globalThis.navigator?.platform || ""): string | null {
if (event.isComposing) return null;
if (event.key === "Alt") return "Alt";
if (event.key === "Shift") return "Shift";
if (event.key === "Meta") return isMacShortcutPlatform(platform) ? "Mod" : "Meta";
if (event.key === "Control") return isMacShortcutPlatform(platform) ? "Ctrl" : "Mod";
return null;
}
export function matchesModifierOnlyShortcut(event: Omit<ShortcutLikeEvent, "key">, shortcut: string): boolean {
if (event.isComposing || !shortcut) return false;
const meta = !!event.metaKey;
const ctrl = !!event.ctrlKey;
const alt = !!event.altKey;
const shift = !!event.shiftKey;
if (shortcut === "Mod") return meta !== ctrl && !alt && !shift;
if (shortcut === "Meta") return meta && !ctrl && !alt && !shift;
if (shortcut === "Ctrl") return ctrl && !meta && !alt && !shift;
if (shortcut === "Alt") return alt && !meta && !ctrl && !shift;
if (shortcut === "Shift") return shift && !meta && !ctrl && !alt;
return false;
}
export function matchesShortcut(event: ShortcutLikeEvent, shortcut: string): boolean {
if (event.isComposing || !shortcut) return false;
const parts = parseShortcutParts(shortcut);
@@ -50,6 +50,7 @@ export type ShortcutActionId =
| "copySidebarSelection"
| "pasteSidebarSelection"
| "editSidebarConnection"
| "openDataInNewTab"
| "sendSelectionToAi";
export type ShortcutScope = "global" | "editor" | "grid" | "search" | "sidebar";
@@ -59,6 +60,7 @@ export interface ShortcutDefinition {
labelKey: string;
scope: ShortcutScope;
defaultShortcut: string;
inputKind?: "keyboard" | "modifier-only";
}
export type ShortcutSettings = Record<ShortcutActionId, string>;
@@ -358,6 +360,13 @@ export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [
scope: "sidebar",
defaultShortcut: "Mod+E",
},
{
id: "openDataInNewTab",
labelKey: "settings.shortcutOpenDataInNewTab",
scope: "sidebar",
defaultShortcut: "Alt",
inputKind: "modifier-only",
},
{
id: "sendSelectionToAi",
labelKey: "settings.shortcutSendSelectionToAi",
@@ -368,8 +377,23 @@ export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [
export const DEFAULT_SHORTCUT_SETTINGS: ShortcutSettings = Object.fromEntries(SHORTCUT_DEFINITIONS.map((definition) => [definition.id, definition.defaultShortcut])) as ShortcutSettings;
const modifierOnlyShortcuts = new Set(["Alt", "Shift", "Mod", "Ctrl", "Meta"]);
export function normalizeModifierOnlyShortcut(shortcut: string, fallback = ""): string {
const normalized = shortcut.trim() === "Control" ? "Ctrl" : shortcut.trim();
if (normalized === "") return "";
return modifierOnlyShortcuts.has(normalized) ? normalized : fallback;
}
export function normalizeShortcutSettings(settings?: Partial<ShortcutSettings>): ShortcutSettings {
return Object.fromEntries(SHORTCUT_DEFINITIONS.map((definition) => [definition.id, typeof settings?.[definition.id] === "string" ? settings[definition.id] : definition.defaultShortcut])) as ShortcutSettings;
return Object.fromEntries(
SHORTCUT_DEFINITIONS.map((definition) => {
const configuredValue = settings?.[definition.id];
const configured = typeof configuredValue === "string" ? configuredValue : definition.defaultShortcut;
const normalized = definition.inputKind === "modifier-only" ? normalizeModifierOnlyShortcut(configured, definition.defaultShortcut) : configured;
return [definition.id, normalized];
}),
) as ShortcutSettings;
}
export function shortcutToCodeMirrorKey(shortcut: string): string {