@lytjs/common-keyboard 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,91 @@
1
+ 'use strict';
2
+
3
+ // src/index.ts
4
+ var MODIFIER_KEYS = /* @__PURE__ */ new Set([
5
+ "ctrl",
6
+ "shift",
7
+ "alt",
8
+ "meta"
9
+ ]);
10
+ var SPECIAL_KEYS = {
11
+ enter: "Enter",
12
+ escape: "Escape",
13
+ tab: "Tab",
14
+ space: " ",
15
+ backspace: "Backspace",
16
+ delete: "Delete",
17
+ arrowup: "ArrowUp",
18
+ arrowdown: "ArrowDown",
19
+ arrowleft: "ArrowLeft",
20
+ arrowright: "ArrowRight",
21
+ home: "Home",
22
+ end: "End",
23
+ pageup: "PageUp",
24
+ pagedown: "PageDown",
25
+ f1: "F1",
26
+ f2: "F2",
27
+ f3: "F3",
28
+ f4: "F4",
29
+ f5: "F5",
30
+ f6: "F6",
31
+ f7: "F7",
32
+ f8: "F8",
33
+ f9: "F9",
34
+ f10: "F10",
35
+ f11: "F11",
36
+ f12: "F12"
37
+ };
38
+ function parseShortcut(shortcut) {
39
+ const parts = shortcut.toLowerCase().split("+").map((p) => p.trim());
40
+ const result = {
41
+ key: "",
42
+ ctrl: false,
43
+ shift: false,
44
+ alt: false,
45
+ meta: false
46
+ };
47
+ for (const part of parts) {
48
+ if (part === "ctrl" || part === "control") {
49
+ result.ctrl = true;
50
+ } else if (part === "shift") {
51
+ result.shift = true;
52
+ } else if (part === "alt") {
53
+ result.alt = true;
54
+ } else if (part === "meta" || part === "cmd" || part === "command") {
55
+ result.meta = true;
56
+ } else {
57
+ result.key = SPECIAL_KEYS[part] || part.toUpperCase();
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ function matchShortcut(event, shortcut) {
63
+ const parsed = parseShortcut(shortcut);
64
+ return event.ctrlKey === parsed.ctrl && event.shiftKey === parsed.shift && event.altKey === parsed.alt && event.metaKey === parsed.meta && event.key.toLowerCase() === parsed.key.toLowerCase();
65
+ }
66
+ function createKeySequence(keys) {
67
+ let currentIndex = 0;
68
+ return (event) => {
69
+ if (currentIndex >= keys.length) {
70
+ currentIndex = 0;
71
+ }
72
+ if (matchShortcut(event, keys[currentIndex])) {
73
+ currentIndex++;
74
+ if (currentIndex >= keys.length) {
75
+ currentIndex = 0;
76
+ return true;
77
+ }
78
+ } else {
79
+ currentIndex = 0;
80
+ }
81
+ return false;
82
+ };
83
+ }
84
+
85
+ exports.MODIFIER_KEYS = MODIFIER_KEYS;
86
+ exports.SPECIAL_KEYS = SPECIAL_KEYS;
87
+ exports.createKeySequence = createKeySequence;
88
+ exports.matchShortcut = matchShortcut;
89
+ exports.parseShortcut = parseShortcut;
90
+ //# sourceMappingURL=index.cjs.map
91
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * @lytjs/common-keyboard\r\n * 轻量级键盘快捷键工具\r\n */\r\n\r\ndeclare const __DEV__: boolean;\r\n\r\nexport interface ParsedShortcut {\r\n key: string;\r\n ctrl: boolean;\r\n shift: boolean;\r\n alt: boolean;\r\n meta: boolean;\r\n}\r\n\r\n/** 修饰键名称集合 */\r\nexport const MODIFIER_KEYS: Set<string> = new Set([\r\n 'ctrl',\r\n 'shift',\r\n 'alt',\r\n 'meta',\r\n]);\r\n\r\n/** 特殊键名称到 KeyboardEvent.key 的映射 */\r\nexport const SPECIAL_KEYS: Record<string, string> = {\r\n enter: 'Enter',\r\n escape: 'Escape',\r\n tab: 'Tab',\r\n space: ' ',\r\n backspace: 'Backspace',\r\n delete: 'Delete',\r\n arrowup: 'ArrowUp',\r\n arrowdown: 'ArrowDown',\r\n arrowleft: 'ArrowLeft',\r\n arrowright: 'ArrowRight',\r\n home: 'Home',\r\n end: 'End',\r\n pageup: 'PageUp',\r\n pagedown: 'PageDown',\r\n f1: 'F1',\r\n f2: 'F2',\r\n f3: 'F3',\r\n f4: 'F4',\r\n f5: 'F5',\r\n f6: 'F6',\r\n f7: 'F7',\r\n f8: 'F8',\r\n f9: 'F9',\r\n f10: 'F10',\r\n f11: 'F11',\r\n f12: 'F12',\r\n};\r\n\r\n/**\r\n * 解析快捷键字符串为结构化对象\r\n *\r\n * @param shortcut - 快捷键字符串,如 \"ctrl+s\", \"shift+alt+t\", \"enter\"\r\n * @returns 解析后的快捷键对象\r\n */\r\nexport function parseShortcut(shortcut: string): ParsedShortcut {\r\n const parts = shortcut.toLowerCase().split('+').map((p) => p.trim());\r\n const result: ParsedShortcut = {\r\n key: '',\r\n ctrl: false,\r\n shift: false,\r\n alt: false,\r\n meta: false,\r\n };\r\n\r\n for (const part of parts) {\r\n if (part === 'ctrl' || part === 'control') {\r\n result.ctrl = true;\r\n } else if (part === 'shift') {\r\n result.shift = true;\r\n } else if (part === 'alt') {\r\n result.alt = true;\r\n } else if (part === 'meta' || part === 'cmd' || part === 'command') {\r\n result.meta = true;\r\n } else {\r\n // Resolve special keys\r\n result.key = SPECIAL_KEYS[part] || part.toUpperCase();\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 匹配快捷键字符串\r\n *\r\n * @param event - KeyboardEvent\r\n * @param shortcut - 快捷键字符串,如 \"ctrl+s\", \"shift+alt+t\", \"enter\"\r\n * @returns 是否匹配\r\n */\r\nexport function matchShortcut(event: KeyboardEvent, shortcut: string): boolean {\r\n const parsed = parseShortcut(shortcut);\r\n\r\n return (\r\n event.ctrlKey === parsed.ctrl &&\r\n event.shiftKey === parsed.shift &&\r\n event.altKey === parsed.alt &&\r\n event.metaKey === parsed.meta &&\r\n event.key.toLowerCase() === parsed.key.toLowerCase()\r\n );\r\n}\r\n\r\n/**\r\n * 创建一个按键序列匹配器\r\n *\r\n * @param keys - 按键序列,如 [\"ctrl\", \"k\", \"s\"] 表示先按 Ctrl+K 再按 S\r\n * @returns 匹配函数,每次调用传入 KeyboardEvent,按顺序匹配\r\n */\r\nexport function createKeySequence(\r\n keys: string[],\r\n): (event: KeyboardEvent) => boolean {\r\n let currentIndex = 0;\r\n\r\n return (event: KeyboardEvent): boolean => {\r\n if (currentIndex >= keys.length) {\r\n currentIndex = 0;\r\n }\r\n\r\n if (matchShortcut(event, keys[currentIndex]!)) {\r\n currentIndex++;\r\n if (currentIndex >= keys.length) {\r\n currentIndex = 0;\r\n return true;\r\n }\r\n } else {\r\n // Reset if the sequence is broken\r\n currentIndex = 0;\r\n }\r\n\r\n return false;\r\n };\r\n}\r\n"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @lytjs/common-keyboard
3
+ * 轻量级键盘快捷键工具
4
+ */
5
+ interface ParsedShortcut {
6
+ key: string;
7
+ ctrl: boolean;
8
+ shift: boolean;
9
+ alt: boolean;
10
+ meta: boolean;
11
+ }
12
+ /** 修饰键名称集合 */
13
+ declare const MODIFIER_KEYS: Set<string>;
14
+ /** 特殊键名称到 KeyboardEvent.key 的映射 */
15
+ declare const SPECIAL_KEYS: Record<string, string>;
16
+ /**
17
+ * 解析快捷键字符串为结构化对象
18
+ *
19
+ * @param shortcut - 快捷键字符串,如 "ctrl+s", "shift+alt+t", "enter"
20
+ * @returns 解析后的快捷键对象
21
+ */
22
+ declare function parseShortcut(shortcut: string): ParsedShortcut;
23
+ /**
24
+ * 匹配快捷键字符串
25
+ *
26
+ * @param event - KeyboardEvent
27
+ * @param shortcut - 快捷键字符串,如 "ctrl+s", "shift+alt+t", "enter"
28
+ * @returns 是否匹配
29
+ */
30
+ declare function matchShortcut(event: KeyboardEvent, shortcut: string): boolean;
31
+ /**
32
+ * 创建一个按键序列匹配器
33
+ *
34
+ * @param keys - 按键序列,如 ["ctrl", "k", "s"] 表示先按 Ctrl+K 再按 S
35
+ * @returns 匹配函数,每次调用传入 KeyboardEvent,按顺序匹配
36
+ */
37
+ declare function createKeySequence(keys: string[]): (event: KeyboardEvent) => boolean;
38
+
39
+ export { MODIFIER_KEYS, type ParsedShortcut, SPECIAL_KEYS, createKeySequence, matchShortcut, parseShortcut };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @lytjs/common-keyboard
3
+ * 轻量级键盘快捷键工具
4
+ */
5
+ interface ParsedShortcut {
6
+ key: string;
7
+ ctrl: boolean;
8
+ shift: boolean;
9
+ alt: boolean;
10
+ meta: boolean;
11
+ }
12
+ /** 修饰键名称集合 */
13
+ declare const MODIFIER_KEYS: Set<string>;
14
+ /** 特殊键名称到 KeyboardEvent.key 的映射 */
15
+ declare const SPECIAL_KEYS: Record<string, string>;
16
+ /**
17
+ * 解析快捷键字符串为结构化对象
18
+ *
19
+ * @param shortcut - 快捷键字符串,如 "ctrl+s", "shift+alt+t", "enter"
20
+ * @returns 解析后的快捷键对象
21
+ */
22
+ declare function parseShortcut(shortcut: string): ParsedShortcut;
23
+ /**
24
+ * 匹配快捷键字符串
25
+ *
26
+ * @param event - KeyboardEvent
27
+ * @param shortcut - 快捷键字符串,如 "ctrl+s", "shift+alt+t", "enter"
28
+ * @returns 是否匹配
29
+ */
30
+ declare function matchShortcut(event: KeyboardEvent, shortcut: string): boolean;
31
+ /**
32
+ * 创建一个按键序列匹配器
33
+ *
34
+ * @param keys - 按键序列,如 ["ctrl", "k", "s"] 表示先按 Ctrl+K 再按 S
35
+ * @returns 匹配函数,每次调用传入 KeyboardEvent,按顺序匹配
36
+ */
37
+ declare function createKeySequence(keys: string[]): (event: KeyboardEvent) => boolean;
38
+
39
+ export { MODIFIER_KEYS, type ParsedShortcut, SPECIAL_KEYS, createKeySequence, matchShortcut, parseShortcut };
package/dist/index.mjs ADDED
@@ -0,0 +1,85 @@
1
+ // src/index.ts
2
+ var MODIFIER_KEYS = /* @__PURE__ */ new Set([
3
+ "ctrl",
4
+ "shift",
5
+ "alt",
6
+ "meta"
7
+ ]);
8
+ var SPECIAL_KEYS = {
9
+ enter: "Enter",
10
+ escape: "Escape",
11
+ tab: "Tab",
12
+ space: " ",
13
+ backspace: "Backspace",
14
+ delete: "Delete",
15
+ arrowup: "ArrowUp",
16
+ arrowdown: "ArrowDown",
17
+ arrowleft: "ArrowLeft",
18
+ arrowright: "ArrowRight",
19
+ home: "Home",
20
+ end: "End",
21
+ pageup: "PageUp",
22
+ pagedown: "PageDown",
23
+ f1: "F1",
24
+ f2: "F2",
25
+ f3: "F3",
26
+ f4: "F4",
27
+ f5: "F5",
28
+ f6: "F6",
29
+ f7: "F7",
30
+ f8: "F8",
31
+ f9: "F9",
32
+ f10: "F10",
33
+ f11: "F11",
34
+ f12: "F12"
35
+ };
36
+ function parseShortcut(shortcut) {
37
+ const parts = shortcut.toLowerCase().split("+").map((p) => p.trim());
38
+ const result = {
39
+ key: "",
40
+ ctrl: false,
41
+ shift: false,
42
+ alt: false,
43
+ meta: false
44
+ };
45
+ for (const part of parts) {
46
+ if (part === "ctrl" || part === "control") {
47
+ result.ctrl = true;
48
+ } else if (part === "shift") {
49
+ result.shift = true;
50
+ } else if (part === "alt") {
51
+ result.alt = true;
52
+ } else if (part === "meta" || part === "cmd" || part === "command") {
53
+ result.meta = true;
54
+ } else {
55
+ result.key = SPECIAL_KEYS[part] || part.toUpperCase();
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+ function matchShortcut(event, shortcut) {
61
+ const parsed = parseShortcut(shortcut);
62
+ return event.ctrlKey === parsed.ctrl && event.shiftKey === parsed.shift && event.altKey === parsed.alt && event.metaKey === parsed.meta && event.key.toLowerCase() === parsed.key.toLowerCase();
63
+ }
64
+ function createKeySequence(keys) {
65
+ let currentIndex = 0;
66
+ return (event) => {
67
+ if (currentIndex >= keys.length) {
68
+ currentIndex = 0;
69
+ }
70
+ if (matchShortcut(event, keys[currentIndex])) {
71
+ currentIndex++;
72
+ if (currentIndex >= keys.length) {
73
+ currentIndex = 0;
74
+ return true;
75
+ }
76
+ } else {
77
+ currentIndex = 0;
78
+ }
79
+ return false;
80
+ };
81
+ }
82
+
83
+ export { MODIFIER_KEYS, SPECIAL_KEYS, createKeySequence, matchShortcut, parseShortcut };
84
+ //# sourceMappingURL=index.mjs.map
85
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * @lytjs/common-keyboard\r\n * 轻量级键盘快捷键工具\r\n */\r\n\r\ndeclare const __DEV__: boolean;\r\n\r\nexport interface ParsedShortcut {\r\n key: string;\r\n ctrl: boolean;\r\n shift: boolean;\r\n alt: boolean;\r\n meta: boolean;\r\n}\r\n\r\n/** 修饰键名称集合 */\r\nexport const MODIFIER_KEYS: Set<string> = new Set([\r\n 'ctrl',\r\n 'shift',\r\n 'alt',\r\n 'meta',\r\n]);\r\n\r\n/** 特殊键名称到 KeyboardEvent.key 的映射 */\r\nexport const SPECIAL_KEYS: Record<string, string> = {\r\n enter: 'Enter',\r\n escape: 'Escape',\r\n tab: 'Tab',\r\n space: ' ',\r\n backspace: 'Backspace',\r\n delete: 'Delete',\r\n arrowup: 'ArrowUp',\r\n arrowdown: 'ArrowDown',\r\n arrowleft: 'ArrowLeft',\r\n arrowright: 'ArrowRight',\r\n home: 'Home',\r\n end: 'End',\r\n pageup: 'PageUp',\r\n pagedown: 'PageDown',\r\n f1: 'F1',\r\n f2: 'F2',\r\n f3: 'F3',\r\n f4: 'F4',\r\n f5: 'F5',\r\n f6: 'F6',\r\n f7: 'F7',\r\n f8: 'F8',\r\n f9: 'F9',\r\n f10: 'F10',\r\n f11: 'F11',\r\n f12: 'F12',\r\n};\r\n\r\n/**\r\n * 解析快捷键字符串为结构化对象\r\n *\r\n * @param shortcut - 快捷键字符串,如 \"ctrl+s\", \"shift+alt+t\", \"enter\"\r\n * @returns 解析后的快捷键对象\r\n */\r\nexport function parseShortcut(shortcut: string): ParsedShortcut {\r\n const parts = shortcut.toLowerCase().split('+').map((p) => p.trim());\r\n const result: ParsedShortcut = {\r\n key: '',\r\n ctrl: false,\r\n shift: false,\r\n alt: false,\r\n meta: false,\r\n };\r\n\r\n for (const part of parts) {\r\n if (part === 'ctrl' || part === 'control') {\r\n result.ctrl = true;\r\n } else if (part === 'shift') {\r\n result.shift = true;\r\n } else if (part === 'alt') {\r\n result.alt = true;\r\n } else if (part === 'meta' || part === 'cmd' || part === 'command') {\r\n result.meta = true;\r\n } else {\r\n // Resolve special keys\r\n result.key = SPECIAL_KEYS[part] || part.toUpperCase();\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 匹配快捷键字符串\r\n *\r\n * @param event - KeyboardEvent\r\n * @param shortcut - 快捷键字符串,如 \"ctrl+s\", \"shift+alt+t\", \"enter\"\r\n * @returns 是否匹配\r\n */\r\nexport function matchShortcut(event: KeyboardEvent, shortcut: string): boolean {\r\n const parsed = parseShortcut(shortcut);\r\n\r\n return (\r\n event.ctrlKey === parsed.ctrl &&\r\n event.shiftKey === parsed.shift &&\r\n event.altKey === parsed.alt &&\r\n event.metaKey === parsed.meta &&\r\n event.key.toLowerCase() === parsed.key.toLowerCase()\r\n );\r\n}\r\n\r\n/**\r\n * 创建一个按键序列匹配器\r\n *\r\n * @param keys - 按键序列,如 [\"ctrl\", \"k\", \"s\"] 表示先按 Ctrl+K 再按 S\r\n * @returns 匹配函数,每次调用传入 KeyboardEvent,按顺序匹配\r\n */\r\nexport function createKeySequence(\r\n keys: string[],\r\n): (event: KeyboardEvent) => boolean {\r\n let currentIndex = 0;\r\n\r\n return (event: KeyboardEvent): boolean => {\r\n if (currentIndex >= keys.length) {\r\n currentIndex = 0;\r\n }\r\n\r\n if (matchShortcut(event, keys[currentIndex]!)) {\r\n currentIndex++;\r\n if (currentIndex >= keys.length) {\r\n currentIndex = 0;\r\n return true;\r\n }\r\n } else {\r\n // Reset if the sequence is broken\r\n currentIndex = 0;\r\n }\r\n\r\n return false;\r\n };\r\n}\r\n"]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@lytjs/common-keyboard",
3
+ "version": "6.0.0",
4
+ "description": "Lightweight keyboard shortcut utilities for LytJS",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
19
+ "scripts": {
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "test:coverage": "vitest run --coverage",
23
+ "lint": "eslint \"src/**/*.ts\"",
24
+ "build": "tsup",
25
+ "type-check": "tsc --noEmit",
26
+ "clean": "rm -rf dist"
27
+ },
28
+ "sideEffects": false,
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "jsdom": "^26.0.0",
32
+ "tsup": "^8.4.0",
33
+ "typescript": "^5.8.2",
34
+ "vitest": "^3.0.7"
35
+ }
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * @lytjs/common-keyboard
3
+ * 轻量级键盘快捷键工具
4
+ */
5
+
6
+ declare const __DEV__: boolean;
7
+
8
+ export interface ParsedShortcut {
9
+ key: string;
10
+ ctrl: boolean;
11
+ shift: boolean;
12
+ alt: boolean;
13
+ meta: boolean;
14
+ }
15
+
16
+ /** 修饰键名称集合 */
17
+ export const MODIFIER_KEYS: Set<string> = new Set([
18
+ 'ctrl',
19
+ 'shift',
20
+ 'alt',
21
+ 'meta',
22
+ ]);
23
+
24
+ /** 特殊键名称到 KeyboardEvent.key 的映射 */
25
+ export const SPECIAL_KEYS: Record<string, string> = {
26
+ enter: 'Enter',
27
+ escape: 'Escape',
28
+ tab: 'Tab',
29
+ space: ' ',
30
+ backspace: 'Backspace',
31
+ delete: 'Delete',
32
+ arrowup: 'ArrowUp',
33
+ arrowdown: 'ArrowDown',
34
+ arrowleft: 'ArrowLeft',
35
+ arrowright: 'ArrowRight',
36
+ home: 'Home',
37
+ end: 'End',
38
+ pageup: 'PageUp',
39
+ pagedown: 'PageDown',
40
+ f1: 'F1',
41
+ f2: 'F2',
42
+ f3: 'F3',
43
+ f4: 'F4',
44
+ f5: 'F5',
45
+ f6: 'F6',
46
+ f7: 'F7',
47
+ f8: 'F8',
48
+ f9: 'F9',
49
+ f10: 'F10',
50
+ f11: 'F11',
51
+ f12: 'F12',
52
+ };
53
+
54
+ /**
55
+ * 解析快捷键字符串为结构化对象
56
+ *
57
+ * @param shortcut - 快捷键字符串,如 "ctrl+s", "shift+alt+t", "enter"
58
+ * @returns 解析后的快捷键对象
59
+ */
60
+ export function parseShortcut(shortcut: string): ParsedShortcut {
61
+ const parts = shortcut.toLowerCase().split('+').map((p) => p.trim());
62
+ const result: ParsedShortcut = {
63
+ key: '',
64
+ ctrl: false,
65
+ shift: false,
66
+ alt: false,
67
+ meta: false,
68
+ };
69
+
70
+ for (const part of parts) {
71
+ if (part === 'ctrl' || part === 'control') {
72
+ result.ctrl = true;
73
+ } else if (part === 'shift') {
74
+ result.shift = true;
75
+ } else if (part === 'alt') {
76
+ result.alt = true;
77
+ } else if (part === 'meta' || part === 'cmd' || part === 'command') {
78
+ result.meta = true;
79
+ } else {
80
+ // Resolve special keys
81
+ result.key = SPECIAL_KEYS[part] || part.toUpperCase();
82
+ }
83
+ }
84
+
85
+ return result;
86
+ }
87
+
88
+ /**
89
+ * 匹配快捷键字符串
90
+ *
91
+ * @param event - KeyboardEvent
92
+ * @param shortcut - 快捷键字符串,如 "ctrl+s", "shift+alt+t", "enter"
93
+ * @returns 是否匹配
94
+ */
95
+ export function matchShortcut(event: KeyboardEvent, shortcut: string): boolean {
96
+ const parsed = parseShortcut(shortcut);
97
+
98
+ return (
99
+ event.ctrlKey === parsed.ctrl &&
100
+ event.shiftKey === parsed.shift &&
101
+ event.altKey === parsed.alt &&
102
+ event.metaKey === parsed.meta &&
103
+ event.key.toLowerCase() === parsed.key.toLowerCase()
104
+ );
105
+ }
106
+
107
+ /**
108
+ * 创建一个按键序列匹配器
109
+ *
110
+ * @param keys - 按键序列,如 ["ctrl", "k", "s"] 表示先按 Ctrl+K 再按 S
111
+ * @returns 匹配函数,每次调用传入 KeyboardEvent,按顺序匹配
112
+ */
113
+ export function createKeySequence(
114
+ keys: string[],
115
+ ): (event: KeyboardEvent) => boolean {
116
+ let currentIndex = 0;
117
+
118
+ return (event: KeyboardEvent): boolean => {
119
+ if (currentIndex >= keys.length) {
120
+ currentIndex = 0;
121
+ }
122
+
123
+ if (matchShortcut(event, keys[currentIndex]!)) {
124
+ currentIndex++;
125
+ if (currentIndex >= keys.length) {
126
+ currentIndex = 0;
127
+ return true;
128
+ }
129
+ } else {
130
+ // Reset if the sequence is broken
131
+ currentIndex = 0;
132
+ }
133
+
134
+ return false;
135
+ };
136
+ }