summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks/use-keyboard-shortcuts-help.ts
blob: 7ea3a55554ec9b0d7a1f59fa97b5fa0163dbe6d0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
"use client";

import { useMemo } from "react";
import { useKeybindingsStore } from "@/stores/keybindings-store";
import { ACTIONS, type TActionWithOptionalArgs } from "@/lib/actions";
import {
	getPlatformAlternateKey,
	getPlatformSpecialKey,
} from "@/utils/platform";

export interface KeyboardShortcut {
	id: string;
	keys: string[];
	description: string;
	category: string;
	action: TActionWithOptionalArgs;
	icon?: React.ReactNode;
}

function formatKey({ key }: { key: string }): string {
	return key
		.replace("ctrl", getPlatformSpecialKey())
		.replace("alt", getPlatformAlternateKey())
		.replace("shift", "Shift")
		.replace("left", "←")
		.replace("right", "→")
		.replace("up", "↑")
		.replace("down", "↓")
		.replace("space", "Space")
		.replace("home", "Home")
		.replace("enter", "Enter")
		.replace("end", "End")
		.replace("delete", "Delete")
		.replace("backspace", "Backspace")
		.replace("-", "+");
}

export function useKeyboardShortcutsHelp() {
	const { keybindings } = useKeybindingsStore();

	const shortcuts = useMemo(() => {
		const result: KeyboardShortcut[] = [];
		const actionToKeys: Partial<Record<TActionWithOptionalArgs, string[]>> = {};

		for (const [key, action] of Object.entries(keybindings) as Array<
			[string, TActionWithOptionalArgs | undefined]
		>) {
			if (action) {
				if (!actionToKeys[action]) {
					actionToKeys[action] = [];
				}
				actionToKeys[action].push(formatKey({ key }));
			}
		}

		for (const [action, keys] of Object.entries(actionToKeys) as Array<
			[TActionWithOptionalArgs, string[]]
		>) {
			const actionDef = ACTIONS[action];
			result.push({
				id: action,
				keys,
				description: actionDef.description,
				category: actionDef.category,
				action,
			});
		}

		return result.sort((a, b) => {
			if (a.category !== b.category) {
				return a.category.localeCompare(b.category);
			}
			return a.description.localeCompare(b.description);
		});
	}, [keybindings]);

	return {
		shortcuts,
	};
}