@flowgram-vue/shortcuts-plugin 0.2.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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
4
+ Copyright (c) 2026 Crayon
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/dist/index.cjs ADDED
@@ -0,0 +1,289 @@
1
+ 'use strict';
2
+
3
+ var core = require('@flowgram-vue/core');
4
+ var inversify = require('inversify');
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
+ var __decorateClass = (decorators, target, key, kind) => {
9
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
10
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
+ if (decorator = decorators[i])
12
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
13
+ if (kind && result) __defProp(target, key, result);
14
+ return result;
15
+ };
16
+ var ShortcutsContribution = /* @__PURE__ */ Symbol("ShortcutsContribution");
17
+ exports.ShortcutsRegistry = class ShortcutsRegistry {
18
+ constructor() {
19
+ this.shortcutsHandlers = [];
20
+ }
21
+ addHandlers(...handlers) {
22
+ handlers.forEach((handler) => {
23
+ if (!this.commandRegistry.getCommand(handler.commandId)) {
24
+ this.commandRegistry.registerCommand(
25
+ { id: handler.commandId, ...handler.commandDetail || {} },
26
+ { execute: handler.execute, isEnabled: handler.isEnabled }
27
+ );
28
+ } else {
29
+ this.commandRegistry.registerHandler(handler.commandId, {
30
+ execute: handler.execute,
31
+ isEnabled: handler.isEnabled
32
+ });
33
+ }
34
+ });
35
+ this.shortcutsHandlers.unshift(...handlers);
36
+ }
37
+ addHandlersIfNotFound(...handlers) {
38
+ handlers.forEach((handler) => {
39
+ if (!this.has(handler.commandId)) {
40
+ this.addHandlers(handler);
41
+ }
42
+ });
43
+ }
44
+ removeHandler(commandId) {
45
+ this.shortcutsHandlers = this.shortcutsHandlers.filter(
46
+ (handler) => handler.commandId !== commandId
47
+ );
48
+ }
49
+ has(commandId) {
50
+ return this.shortcutsHandlers.some((handler) => handler.commandId === commandId);
51
+ }
52
+ init() {
53
+ this.contribs?.forEach((contrib) => contrib.registerShortcuts(this));
54
+ }
55
+ };
56
+ __decorateClass([
57
+ inversify.inject(core.ContributionProvider),
58
+ inversify.named(ShortcutsContribution),
59
+ inversify.optional()
60
+ ], exports.ShortcutsRegistry.prototype, "contribs", 2);
61
+ __decorateClass([
62
+ inversify.inject(core.CommandRegistry)
63
+ ], exports.ShortcutsRegistry.prototype, "commandRegistry", 2);
64
+ __decorateClass([
65
+ inversify.postConstruct()
66
+ ], exports.ShortcutsRegistry.prototype, "init", 1);
67
+ exports.ShortcutsRegistry = __decorateClass([
68
+ inversify.injectable()
69
+ ], exports.ShortcutsRegistry);
70
+
71
+ // src/shortcuts-utils.ts
72
+ var isAppleDevice = /(mac|iphone|ipod|ipad)/i.test(
73
+ typeof navigator !== "undefined" ? navigator?.platform : ""
74
+ );
75
+ var aliasKeyCodeMap = {
76
+ "0": 48,
77
+ "1": 49,
78
+ "2": 50,
79
+ "3": 51,
80
+ "4": 52,
81
+ "5": 53,
82
+ "6": 54,
83
+ "7": 55,
84
+ "8": 56,
85
+ "9": 57,
86
+ backspace: 8,
87
+ tab: 9,
88
+ enter: 13,
89
+ shift: 16,
90
+ ctrl: 17,
91
+ alt: 18,
92
+ pausebreak: 19,
93
+ capslock: 20,
94
+ esc: 27,
95
+ space: 32,
96
+ pageup: 33,
97
+ pagedown: 34,
98
+ end: 35,
99
+ home: 36,
100
+ leftarrow: 37,
101
+ uparrow: 38,
102
+ rightarrow: 39,
103
+ downarrow: 40,
104
+ insert: 45,
105
+ delete: 46,
106
+ a: 65,
107
+ b: 66,
108
+ c: 67,
109
+ d: 68,
110
+ e: 69,
111
+ f: 70,
112
+ g: 71,
113
+ h: 72,
114
+ i: 73,
115
+ j: 74,
116
+ k: 75,
117
+ l: 76,
118
+ m: 77,
119
+ n: 78,
120
+ o: 79,
121
+ p: 80,
122
+ q: 81,
123
+ r: 82,
124
+ s: 83,
125
+ t: 84,
126
+ u: 85,
127
+ v: 86,
128
+ w: 87,
129
+ x: 88,
130
+ y: 89,
131
+ z: 90,
132
+ leftwindowkey: 91,
133
+ rightwindowkey: 92,
134
+ meta: isAppleDevice ? [91, 93] : [91, 92],
135
+ selectkey: 93,
136
+ numpad0: 96,
137
+ numpad1: 97,
138
+ numpad2: 98,
139
+ numpad3: 99,
140
+ numpad4: 100,
141
+ numpad5: 101,
142
+ numpad6: 102,
143
+ numpad7: 103,
144
+ numpad8: 104,
145
+ numpad9: 105,
146
+ multiply: 106,
147
+ add: 107,
148
+ subtract: 109,
149
+ decimalpoint: 110,
150
+ divide: 111,
151
+ f1: 112,
152
+ f2: 113,
153
+ f3: 114,
154
+ f4: 115,
155
+ f5: 116,
156
+ f6: 117,
157
+ f7: 118,
158
+ f8: 119,
159
+ f9: 120,
160
+ f10: 121,
161
+ f11: 122,
162
+ f12: 123,
163
+ numlock: 144,
164
+ scrolllock: 145,
165
+ semicolon: 186,
166
+ equalsign: 187,
167
+ "=": 187,
168
+ comma: 188,
169
+ dash: 189,
170
+ "-": 189,
171
+ period: 190,
172
+ forwardslash: 191,
173
+ graveaccent: 192,
174
+ openbracket: 219,
175
+ backslash: 220,
176
+ closebracket: 221,
177
+ singlequote: 222
178
+ };
179
+ var modifierKey = {
180
+ ctrl: (event) => event.ctrlKey,
181
+ shift: (event) => event.shiftKey,
182
+ alt: (event) => event.altKey,
183
+ meta: (event) => {
184
+ if (event.type === "keyup") {
185
+ return aliasKeyCodeMap.meta.includes(event.keyCode);
186
+ }
187
+ return event.metaKey;
188
+ }
189
+ };
190
+ function countKeyByEvent(event) {
191
+ const countOfModifier = Object.keys(modifierKey).reduce((total, key) => {
192
+ if (modifierKey[key](event)) {
193
+ return total + 1;
194
+ }
195
+ return total;
196
+ }, 0);
197
+ return [16, 17, 18, 91, 92].includes(event.keyCode) ? countOfModifier : countOfModifier + 1;
198
+ }
199
+ function isKeyStringMatch(event, keyString, exactMatch = true) {
200
+ if (!event.key || !keyString) {
201
+ return false;
202
+ }
203
+ const genArr = keyString.split(/\s+/);
204
+ let genLen = 0;
205
+ for (const key of genArr) {
206
+ const genModifier = modifierKey[key];
207
+ const aliasKeyCode = aliasKeyCodeMap[key.toLowerCase()];
208
+ if (genModifier && genModifier(event) || aliasKeyCode && aliasKeyCode === event.keyCode) {
209
+ genLen++;
210
+ }
211
+ }
212
+ if (exactMatch) {
213
+ return genLen === genArr.length && countKeyByEvent(event) === genArr.length;
214
+ }
215
+ return genLen === genArr.length;
216
+ }
217
+ function isShortcutsMatch(event, shortcuts) {
218
+ return shortcuts.some((keyString) => isKeyStringMatch(event, keyString));
219
+ }
220
+
221
+ // src/layers/shortcuts-layer.ts
222
+ var ShortcutsLayer = class extends core.Layer {
223
+ onReady() {
224
+ this.shortcuts.addHandlersIfNotFound(
225
+ /**
226
+ * 放大
227
+ */
228
+ {
229
+ commandId: core.Command.Default.ZOOM_IN,
230
+ shortcuts: ["meta =", "ctrl ="],
231
+ execute: () => {
232
+ this.config.zoomin();
233
+ }
234
+ },
235
+ /**
236
+ * 缩小
237
+ */
238
+ {
239
+ commandId: core.Command.Default.ZOOM_OUT,
240
+ shortcuts: ["meta -", "ctrl -"],
241
+ execute: () => {
242
+ this.config.zoomout();
243
+ }
244
+ }
245
+ );
246
+ this.toDispose.pushAll([
247
+ // 监听画布鼠标移动事件
248
+ this.listenPlaygroundEvent("keydown", (e) => {
249
+ if (!this.isFocused || e.target !== this.playgroundNode) {
250
+ return;
251
+ }
252
+ this.shortcuts.shortcutsHandlers.some((shortcutsHandler) => {
253
+ if (isShortcutsMatch(e, shortcutsHandler.shortcuts) && (!shortcutsHandler.isEnabled || shortcutsHandler.isEnabled(e))) {
254
+ shortcutsHandler.execute(e);
255
+ e.preventDefault();
256
+ return true;
257
+ }
258
+ });
259
+ })
260
+ ]);
261
+ }
262
+ };
263
+ ShortcutsLayer.type = "ShortcutsLayer";
264
+ __decorateClass([
265
+ inversify.inject(exports.ShortcutsRegistry)
266
+ ], ShortcutsLayer.prototype, "shortcuts", 2);
267
+ __decorateClass([
268
+ inversify.inject(core.SelectionService)
269
+ ], ShortcutsLayer.prototype, "selection", 2);
270
+ ShortcutsLayer = __decorateClass([
271
+ inversify.injectable()
272
+ ], ShortcutsLayer);
273
+
274
+ // src/create-shortcuts-plugin.ts
275
+ var createShortcutsPlugin = core.definePluginCreator({
276
+ onBind: ({ bind }) => {
277
+ bind(exports.ShortcutsRegistry).toSelf().inSingletonScope();
278
+ core.bindContributionProvider(bind, ShortcutsContribution);
279
+ },
280
+ onInit: (ctx) => {
281
+ ctx.playground.registerLayer(ShortcutsLayer);
282
+ },
283
+ contributionKeys: [ShortcutsContribution]
284
+ });
285
+
286
+ exports.ShortcutsContribution = ShortcutsContribution;
287
+ exports.createShortcutsPlugin = createShortcutsPlugin;
288
+ //# sourceMappingURL=index.cjs.map
289
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shortcuts-contribution.ts","../src/shortcuts-utils.ts","../src/layers/shortcuts-layer.ts","../src/create-shortcuts-plugin.ts"],"names":["ShortcutsRegistry","inject","ContributionProvider","named","optional","CommandRegistry","postConstruct","injectable","Layer","Command","SelectionService","definePluginCreator","bindContributionProvider"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { inject, injectable, named, optional, postConstruct } from 'inversify';\nimport { Command, CommandRegistry, ContributionProvider } from '@flowgram-vue/core';\n\nexport interface ShortcutsHandler {\n commandId: string;\n commandDetail?: Omit<Command, 'id'>;\n shortcuts: string[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n isEnabled?: (...args: any[]) => boolean;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execute: (...args: any[]) => void;\n}\n\nexport const ShortcutsContribution = Symbol('ShortcutsContribution');\n\nexport interface ShortcutsContribution {\n registerShortcuts: (registry: ShortcutsRegistry) => void;\n}\n\n@injectable()\nexport class ShortcutsRegistry {\n @inject(ContributionProvider)\n @named(ShortcutsContribution)\n @optional()\n protected contribs: ContributionProvider<ShortcutsContribution>;\n\n @inject(CommandRegistry) protected commandRegistry: CommandRegistry;\n\n shortcutsHandlers: ShortcutsHandler[] = [];\n\n addHandlers(...handlers: ShortcutsHandler[]): void {\n // 注册 command\n handlers.forEach((handler) => {\n if (!this.commandRegistry.getCommand(handler.commandId)) {\n this.commandRegistry.registerCommand(\n { id: handler.commandId, ...(handler.commandDetail || {}) },\n { execute: handler.execute, isEnabled: handler.isEnabled }\n );\n } else {\n this.commandRegistry.registerHandler(handler.commandId, {\n execute: handler.execute,\n isEnabled: handler.isEnabled,\n });\n }\n });\n // Insert before for override pre handlers\n this.shortcutsHandlers.unshift(...handlers);\n }\n\n addHandlersIfNotFound(...handlers: ShortcutsHandler[]): void {\n handlers.forEach((handler) => {\n if (!this.has(handler.commandId)) {\n this.addHandlers(handler);\n }\n });\n }\n\n removeHandler(commandId: string): void {\n this.shortcutsHandlers = this.shortcutsHandlers.filter(\n (handler) => handler.commandId !== commandId\n );\n }\n\n has(commandId: string): boolean {\n return this.shortcutsHandlers.some((handler) => handler.commandId === commandId);\n }\n\n @postConstruct()\n protected init(): void {\n this.contribs?.forEach((contrib) => contrib.registerShortcuts(this));\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nconst isAppleDevice = /(mac|iphone|ipod|ipad)/i.test(\n typeof navigator !== 'undefined' ? navigator?.platform : '',\n);\n// 键盘事件 keyCode 别名\nconst aliasKeyCodeMap: Record<string, number | number[]> = {\n '0': 48,\n '1': 49,\n '2': 50,\n '3': 51,\n '4': 52,\n '5': 53,\n '6': 54,\n '7': 55,\n '8': 56,\n '9': 57,\n backspace: 8,\n tab: 9,\n enter: 13,\n shift: 16,\n ctrl: 17,\n alt: 18,\n pausebreak: 19,\n capslock: 20,\n esc: 27,\n space: 32,\n pageup: 33,\n pagedown: 34,\n end: 35,\n home: 36,\n leftarrow: 37,\n uparrow: 38,\n rightarrow: 39,\n downarrow: 40,\n insert: 45,\n delete: 46,\n a: 65,\n b: 66,\n c: 67,\n d: 68,\n e: 69,\n f: 70,\n g: 71,\n h: 72,\n i: 73,\n j: 74,\n k: 75,\n l: 76,\n m: 77,\n n: 78,\n o: 79,\n p: 80,\n q: 81,\n r: 82,\n s: 83,\n t: 84,\n u: 85,\n v: 86,\n w: 87,\n x: 88,\n y: 89,\n z: 90,\n leftwindowkey: 91,\n rightwindowkey: 92,\n meta: isAppleDevice ? [91, 93] : [91, 92],\n selectkey: 93,\n numpad0: 96,\n numpad1: 97,\n numpad2: 98,\n numpad3: 99,\n numpad4: 100,\n numpad5: 101,\n numpad6: 102,\n numpad7: 103,\n numpad8: 104,\n numpad9: 105,\n multiply: 106,\n add: 107,\n subtract: 109,\n decimalpoint: 110,\n divide: 111,\n f1: 112,\n f2: 113,\n f3: 114,\n f4: 115,\n f5: 116,\n f6: 117,\n f7: 118,\n f8: 119,\n f9: 120,\n f10: 121,\n f11: 122,\n f12: 123,\n numlock: 144,\n scrolllock: 145,\n semicolon: 186,\n equalsign: 187,\n '=': 187,\n comma: 188,\n dash: 189,\n '-': 189,\n period: 190,\n forwardslash: 191,\n graveaccent: 192,\n openbracket: 219,\n backslash: 220,\n closebracket: 221,\n singlequote: 222,\n};\n\nconst modifierKey: any = {\n ctrl: (event: KeyboardEvent) => event.ctrlKey,\n shift: (event: KeyboardEvent) => event.shiftKey,\n alt: (event: KeyboardEvent) => event.altKey,\n meta: (event: KeyboardEvent) => {\n if (event.type === 'keyup') {\n return (aliasKeyCodeMap.meta as number[]).includes(event.keyCode);\n }\n return event.metaKey;\n },\n};\n\n// 根据 event 计算激活键数量\nfunction countKeyByEvent(event: KeyboardEvent): number {\n const countOfModifier = Object.keys(modifierKey).reduce((total, key) => {\n if (modifierKey[key](event)) {\n return total + 1;\n }\n\n return total;\n }, 0);\n\n // 16 17 18 91 92 是修饰键的 keyCode,如果 keyCode 是修饰键,那么激活数量就是修饰键的数量,如果不是,那么就需要 +1\n return [16, 17, 18, 91, 92].includes(event.keyCode) ? countOfModifier : countOfModifier + 1;\n}\n\n/**\n *\n * @param event\n * @param keyString 'ctrl.s' 'meta.s'\n * @param exactMatch\n */\nfunction isKeyStringMatch(event: KeyboardEvent, keyString: string, exactMatch = true): boolean {\n // 浏览器自动补全 input 的时候,会触发 keyDown、keyUp 事件,但此时 event.key 等为空\n if (!event.key || !keyString) {\n return false;\n }\n // 字符串依次判断是否有组合键\n const genArr = keyString.split(/\\s+/);\n let genLen = 0;\n\n for (const key of genArr) {\n // 组合键\n const genModifier = modifierKey[key];\n // keyCode 别名\n const aliasKeyCode: number | number[] = aliasKeyCodeMap[key.toLowerCase()];\n\n if ((genModifier && genModifier(event)) || (aliasKeyCode && aliasKeyCode === event.keyCode)) {\n genLen++;\n }\n }\n\n /**\n * 需要判断触发的键位和监听的键位完全一致,判断方法就是触发的键位里有且等于监听的键位\n * genLen === genArr.length 能判断出来触发的键位里有监听的键位\n * countKeyByEvent(event) === genArr.length 判断出来触发的键位数量里有且等于监听的键位数量\n * 主要用来防止按组合键其子集也会触发的情况,例如监听 ctrl+a 会触发监听 ctrl 和 a 两个键的事件。\n */\n if (exactMatch) {\n return genLen === genArr.length && countKeyByEvent(event) === genArr.length;\n }\n return genLen === genArr.length;\n}\n\n/**\n * 匹配指定的快捷键\n * @param event\n * @param shortcuts\n */\nexport function isShortcutsMatch(event: KeyboardEvent, shortcuts: string[]): boolean {\n return shortcuts.some(keyString => isKeyStringMatch(event, keyString));\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { inject, injectable } from 'inversify';\nimport { Layer, SelectionService, Command } from '@flowgram-vue/core';\n\nimport { isShortcutsMatch } from '../shortcuts-utils';\nimport { ShortcutsRegistry } from '../shortcuts-contribution';\n\n@injectable()\nexport class ShortcutsLayer extends Layer<object> {\n static type = 'ShortcutsLayer';\n\n @inject(ShortcutsRegistry) shortcuts: ShortcutsRegistry;\n\n @inject(SelectionService) selection: SelectionService;\n\n onReady(): void {\n this.shortcuts.addHandlersIfNotFound(\n /**\n * 放大\n */\n {\n commandId: Command.Default.ZOOM_IN,\n shortcuts: ['meta =', 'ctrl ='],\n execute: () => {\n // TODO 这里要判断 CurrentEditor\n this.config.zoomin();\n },\n },\n /**\n * 缩小\n */\n {\n commandId: Command.Default.ZOOM_OUT,\n shortcuts: ['meta -', 'ctrl -'],\n execute: () => {\n // TODO 这里要判断 CurrentEditor\n this.config.zoomout();\n },\n },\n );\n this.toDispose.pushAll([\n // 监听画布鼠标移动事件\n this.listenPlaygroundEvent('keydown', (e: KeyboardEvent) => {\n if (!this.isFocused || e.target !== this.playgroundNode) {\n return;\n }\n this.shortcuts.shortcutsHandlers.some(shortcutsHandler => {\n if (\n isShortcutsMatch(e, shortcutsHandler.shortcuts) &&\n (!shortcutsHandler.isEnabled || shortcutsHandler.isEnabled(e))\n ) {\n shortcutsHandler.execute(e);\n e.preventDefault();\n return true;\n }\n });\n }),\n ]);\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { bindContributionProvider, definePluginCreator } from '@flowgram-vue/core';\n\nimport { ShortcutsRegistry, ShortcutsContribution } from './shortcuts-contribution';\nimport { ShortcutsLayer } from './layers';\n\n/**\n * @param opts\n *\n * createShortcutsPlugin({\n * registerShortcuts(registry) {\n * }\n * })\n */\nexport const createShortcutsPlugin = definePluginCreator<ShortcutsContribution>({\n onBind: ({ bind }) => {\n bind(ShortcutsRegistry).toSelf().inSingletonScope();\n bindContributionProvider(bind, ShortcutsContribution);\n },\n onInit: (ctx) => {\n ctx.playground.registerLayer(ShortcutsLayer);\n },\n contributionKeys: [ShortcutsContribution],\n});\n"]}
@@ -0,0 +1,41 @@
1
+ import * as _flowgram_vue_core from '@flowgram-vue/core';
2
+ import { ContributionProvider, CommandRegistry, Command } from '@flowgram-vue/core';
3
+
4
+ /**
5
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
6
+ * SPDX-License-Identifier: MIT
7
+ */
8
+
9
+ interface ShortcutsHandler {
10
+ commandId: string;
11
+ commandDetail?: Omit<Command, 'id'>;
12
+ shortcuts: string[];
13
+ isEnabled?: (...args: any[]) => boolean;
14
+ execute: (...args: any[]) => void;
15
+ }
16
+ declare const ShortcutsContribution: unique symbol;
17
+ interface ShortcutsContribution {
18
+ registerShortcuts: (registry: ShortcutsRegistry) => void;
19
+ }
20
+ declare class ShortcutsRegistry {
21
+ protected contribs: ContributionProvider<ShortcutsContribution>;
22
+ protected commandRegistry: CommandRegistry;
23
+ shortcutsHandlers: ShortcutsHandler[];
24
+ addHandlers(...handlers: ShortcutsHandler[]): void;
25
+ addHandlersIfNotFound(...handlers: ShortcutsHandler[]): void;
26
+ removeHandler(commandId: string): void;
27
+ has(commandId: string): boolean;
28
+ protected init(): void;
29
+ }
30
+
31
+ /**
32
+ * @param opts
33
+ *
34
+ * createShortcutsPlugin({
35
+ * registerShortcuts(registry) {
36
+ * }
37
+ * })
38
+ */
39
+ declare const createShortcutsPlugin: _flowgram_vue_core.PluginCreator<ShortcutsContribution>;
40
+
41
+ export { ShortcutsContribution, type ShortcutsHandler, ShortcutsRegistry, createShortcutsPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,286 @@
1
+ import { ContributionProvider, CommandRegistry, SelectionService, definePluginCreator, bindContributionProvider, Layer, Command } from '@flowgram-vue/core';
2
+ import { inject, named, optional, postConstruct, injectable } from 'inversify';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __decorateClass = (decorators, target, key, kind) => {
7
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
8
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
9
+ if (decorator = decorators[i])
10
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
11
+ if (kind && result) __defProp(target, key, result);
12
+ return result;
13
+ };
14
+ var ShortcutsContribution = /* @__PURE__ */ Symbol("ShortcutsContribution");
15
+ var ShortcutsRegistry = class {
16
+ constructor() {
17
+ this.shortcutsHandlers = [];
18
+ }
19
+ addHandlers(...handlers) {
20
+ handlers.forEach((handler) => {
21
+ if (!this.commandRegistry.getCommand(handler.commandId)) {
22
+ this.commandRegistry.registerCommand(
23
+ { id: handler.commandId, ...handler.commandDetail || {} },
24
+ { execute: handler.execute, isEnabled: handler.isEnabled }
25
+ );
26
+ } else {
27
+ this.commandRegistry.registerHandler(handler.commandId, {
28
+ execute: handler.execute,
29
+ isEnabled: handler.isEnabled
30
+ });
31
+ }
32
+ });
33
+ this.shortcutsHandlers.unshift(...handlers);
34
+ }
35
+ addHandlersIfNotFound(...handlers) {
36
+ handlers.forEach((handler) => {
37
+ if (!this.has(handler.commandId)) {
38
+ this.addHandlers(handler);
39
+ }
40
+ });
41
+ }
42
+ removeHandler(commandId) {
43
+ this.shortcutsHandlers = this.shortcutsHandlers.filter(
44
+ (handler) => handler.commandId !== commandId
45
+ );
46
+ }
47
+ has(commandId) {
48
+ return this.shortcutsHandlers.some((handler) => handler.commandId === commandId);
49
+ }
50
+ init() {
51
+ this.contribs?.forEach((contrib) => contrib.registerShortcuts(this));
52
+ }
53
+ };
54
+ __decorateClass([
55
+ inject(ContributionProvider),
56
+ named(ShortcutsContribution),
57
+ optional()
58
+ ], ShortcutsRegistry.prototype, "contribs", 2);
59
+ __decorateClass([
60
+ inject(CommandRegistry)
61
+ ], ShortcutsRegistry.prototype, "commandRegistry", 2);
62
+ __decorateClass([
63
+ postConstruct()
64
+ ], ShortcutsRegistry.prototype, "init", 1);
65
+ ShortcutsRegistry = __decorateClass([
66
+ injectable()
67
+ ], ShortcutsRegistry);
68
+
69
+ // src/shortcuts-utils.ts
70
+ var isAppleDevice = /(mac|iphone|ipod|ipad)/i.test(
71
+ typeof navigator !== "undefined" ? navigator?.platform : ""
72
+ );
73
+ var aliasKeyCodeMap = {
74
+ "0": 48,
75
+ "1": 49,
76
+ "2": 50,
77
+ "3": 51,
78
+ "4": 52,
79
+ "5": 53,
80
+ "6": 54,
81
+ "7": 55,
82
+ "8": 56,
83
+ "9": 57,
84
+ backspace: 8,
85
+ tab: 9,
86
+ enter: 13,
87
+ shift: 16,
88
+ ctrl: 17,
89
+ alt: 18,
90
+ pausebreak: 19,
91
+ capslock: 20,
92
+ esc: 27,
93
+ space: 32,
94
+ pageup: 33,
95
+ pagedown: 34,
96
+ end: 35,
97
+ home: 36,
98
+ leftarrow: 37,
99
+ uparrow: 38,
100
+ rightarrow: 39,
101
+ downarrow: 40,
102
+ insert: 45,
103
+ delete: 46,
104
+ a: 65,
105
+ b: 66,
106
+ c: 67,
107
+ d: 68,
108
+ e: 69,
109
+ f: 70,
110
+ g: 71,
111
+ h: 72,
112
+ i: 73,
113
+ j: 74,
114
+ k: 75,
115
+ l: 76,
116
+ m: 77,
117
+ n: 78,
118
+ o: 79,
119
+ p: 80,
120
+ q: 81,
121
+ r: 82,
122
+ s: 83,
123
+ t: 84,
124
+ u: 85,
125
+ v: 86,
126
+ w: 87,
127
+ x: 88,
128
+ y: 89,
129
+ z: 90,
130
+ leftwindowkey: 91,
131
+ rightwindowkey: 92,
132
+ meta: isAppleDevice ? [91, 93] : [91, 92],
133
+ selectkey: 93,
134
+ numpad0: 96,
135
+ numpad1: 97,
136
+ numpad2: 98,
137
+ numpad3: 99,
138
+ numpad4: 100,
139
+ numpad5: 101,
140
+ numpad6: 102,
141
+ numpad7: 103,
142
+ numpad8: 104,
143
+ numpad9: 105,
144
+ multiply: 106,
145
+ add: 107,
146
+ subtract: 109,
147
+ decimalpoint: 110,
148
+ divide: 111,
149
+ f1: 112,
150
+ f2: 113,
151
+ f3: 114,
152
+ f4: 115,
153
+ f5: 116,
154
+ f6: 117,
155
+ f7: 118,
156
+ f8: 119,
157
+ f9: 120,
158
+ f10: 121,
159
+ f11: 122,
160
+ f12: 123,
161
+ numlock: 144,
162
+ scrolllock: 145,
163
+ semicolon: 186,
164
+ equalsign: 187,
165
+ "=": 187,
166
+ comma: 188,
167
+ dash: 189,
168
+ "-": 189,
169
+ period: 190,
170
+ forwardslash: 191,
171
+ graveaccent: 192,
172
+ openbracket: 219,
173
+ backslash: 220,
174
+ closebracket: 221,
175
+ singlequote: 222
176
+ };
177
+ var modifierKey = {
178
+ ctrl: (event) => event.ctrlKey,
179
+ shift: (event) => event.shiftKey,
180
+ alt: (event) => event.altKey,
181
+ meta: (event) => {
182
+ if (event.type === "keyup") {
183
+ return aliasKeyCodeMap.meta.includes(event.keyCode);
184
+ }
185
+ return event.metaKey;
186
+ }
187
+ };
188
+ function countKeyByEvent(event) {
189
+ const countOfModifier = Object.keys(modifierKey).reduce((total, key) => {
190
+ if (modifierKey[key](event)) {
191
+ return total + 1;
192
+ }
193
+ return total;
194
+ }, 0);
195
+ return [16, 17, 18, 91, 92].includes(event.keyCode) ? countOfModifier : countOfModifier + 1;
196
+ }
197
+ function isKeyStringMatch(event, keyString, exactMatch = true) {
198
+ if (!event.key || !keyString) {
199
+ return false;
200
+ }
201
+ const genArr = keyString.split(/\s+/);
202
+ let genLen = 0;
203
+ for (const key of genArr) {
204
+ const genModifier = modifierKey[key];
205
+ const aliasKeyCode = aliasKeyCodeMap[key.toLowerCase()];
206
+ if (genModifier && genModifier(event) || aliasKeyCode && aliasKeyCode === event.keyCode) {
207
+ genLen++;
208
+ }
209
+ }
210
+ if (exactMatch) {
211
+ return genLen === genArr.length && countKeyByEvent(event) === genArr.length;
212
+ }
213
+ return genLen === genArr.length;
214
+ }
215
+ function isShortcutsMatch(event, shortcuts) {
216
+ return shortcuts.some((keyString) => isKeyStringMatch(event, keyString));
217
+ }
218
+
219
+ // src/layers/shortcuts-layer.ts
220
+ var ShortcutsLayer = class extends Layer {
221
+ onReady() {
222
+ this.shortcuts.addHandlersIfNotFound(
223
+ /**
224
+ * 放大
225
+ */
226
+ {
227
+ commandId: Command.Default.ZOOM_IN,
228
+ shortcuts: ["meta =", "ctrl ="],
229
+ execute: () => {
230
+ this.config.zoomin();
231
+ }
232
+ },
233
+ /**
234
+ * 缩小
235
+ */
236
+ {
237
+ commandId: Command.Default.ZOOM_OUT,
238
+ shortcuts: ["meta -", "ctrl -"],
239
+ execute: () => {
240
+ this.config.zoomout();
241
+ }
242
+ }
243
+ );
244
+ this.toDispose.pushAll([
245
+ // 监听画布鼠标移动事件
246
+ this.listenPlaygroundEvent("keydown", (e) => {
247
+ if (!this.isFocused || e.target !== this.playgroundNode) {
248
+ return;
249
+ }
250
+ this.shortcuts.shortcutsHandlers.some((shortcutsHandler) => {
251
+ if (isShortcutsMatch(e, shortcutsHandler.shortcuts) && (!shortcutsHandler.isEnabled || shortcutsHandler.isEnabled(e))) {
252
+ shortcutsHandler.execute(e);
253
+ e.preventDefault();
254
+ return true;
255
+ }
256
+ });
257
+ })
258
+ ]);
259
+ }
260
+ };
261
+ ShortcutsLayer.type = "ShortcutsLayer";
262
+ __decorateClass([
263
+ inject(ShortcutsRegistry)
264
+ ], ShortcutsLayer.prototype, "shortcuts", 2);
265
+ __decorateClass([
266
+ inject(SelectionService)
267
+ ], ShortcutsLayer.prototype, "selection", 2);
268
+ ShortcutsLayer = __decorateClass([
269
+ injectable()
270
+ ], ShortcutsLayer);
271
+
272
+ // src/create-shortcuts-plugin.ts
273
+ var createShortcutsPlugin = definePluginCreator({
274
+ onBind: ({ bind }) => {
275
+ bind(ShortcutsRegistry).toSelf().inSingletonScope();
276
+ bindContributionProvider(bind, ShortcutsContribution);
277
+ },
278
+ onInit: (ctx) => {
279
+ ctx.playground.registerLayer(ShortcutsLayer);
280
+ },
281
+ contributionKeys: [ShortcutsContribution]
282
+ });
283
+
284
+ export { ShortcutsContribution, ShortcutsRegistry, createShortcutsPlugin };
285
+ //# sourceMappingURL=index.js.map
286
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shortcuts-contribution.ts","../src/shortcuts-utils.ts","../src/layers/shortcuts-layer.ts","../src/create-shortcuts-plugin.ts"],"names":["Command","inject","injectable"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { inject, injectable, named, optional, postConstruct } from 'inversify';\nimport { Command, CommandRegistry, ContributionProvider } from '@flowgram-vue/core';\n\nexport interface ShortcutsHandler {\n commandId: string;\n commandDetail?: Omit<Command, 'id'>;\n shortcuts: string[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n isEnabled?: (...args: any[]) => boolean;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execute: (...args: any[]) => void;\n}\n\nexport const ShortcutsContribution = Symbol('ShortcutsContribution');\n\nexport interface ShortcutsContribution {\n registerShortcuts: (registry: ShortcutsRegistry) => void;\n}\n\n@injectable()\nexport class ShortcutsRegistry {\n @inject(ContributionProvider)\n @named(ShortcutsContribution)\n @optional()\n protected contribs: ContributionProvider<ShortcutsContribution>;\n\n @inject(CommandRegistry) protected commandRegistry: CommandRegistry;\n\n shortcutsHandlers: ShortcutsHandler[] = [];\n\n addHandlers(...handlers: ShortcutsHandler[]): void {\n // 注册 command\n handlers.forEach((handler) => {\n if (!this.commandRegistry.getCommand(handler.commandId)) {\n this.commandRegistry.registerCommand(\n { id: handler.commandId, ...(handler.commandDetail || {}) },\n { execute: handler.execute, isEnabled: handler.isEnabled }\n );\n } else {\n this.commandRegistry.registerHandler(handler.commandId, {\n execute: handler.execute,\n isEnabled: handler.isEnabled,\n });\n }\n });\n // Insert before for override pre handlers\n this.shortcutsHandlers.unshift(...handlers);\n }\n\n addHandlersIfNotFound(...handlers: ShortcutsHandler[]): void {\n handlers.forEach((handler) => {\n if (!this.has(handler.commandId)) {\n this.addHandlers(handler);\n }\n });\n }\n\n removeHandler(commandId: string): void {\n this.shortcutsHandlers = this.shortcutsHandlers.filter(\n (handler) => handler.commandId !== commandId\n );\n }\n\n has(commandId: string): boolean {\n return this.shortcutsHandlers.some((handler) => handler.commandId === commandId);\n }\n\n @postConstruct()\n protected init(): void {\n this.contribs?.forEach((contrib) => contrib.registerShortcuts(this));\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nconst isAppleDevice = /(mac|iphone|ipod|ipad)/i.test(\n typeof navigator !== 'undefined' ? navigator?.platform : '',\n);\n// 键盘事件 keyCode 别名\nconst aliasKeyCodeMap: Record<string, number | number[]> = {\n '0': 48,\n '1': 49,\n '2': 50,\n '3': 51,\n '4': 52,\n '5': 53,\n '6': 54,\n '7': 55,\n '8': 56,\n '9': 57,\n backspace: 8,\n tab: 9,\n enter: 13,\n shift: 16,\n ctrl: 17,\n alt: 18,\n pausebreak: 19,\n capslock: 20,\n esc: 27,\n space: 32,\n pageup: 33,\n pagedown: 34,\n end: 35,\n home: 36,\n leftarrow: 37,\n uparrow: 38,\n rightarrow: 39,\n downarrow: 40,\n insert: 45,\n delete: 46,\n a: 65,\n b: 66,\n c: 67,\n d: 68,\n e: 69,\n f: 70,\n g: 71,\n h: 72,\n i: 73,\n j: 74,\n k: 75,\n l: 76,\n m: 77,\n n: 78,\n o: 79,\n p: 80,\n q: 81,\n r: 82,\n s: 83,\n t: 84,\n u: 85,\n v: 86,\n w: 87,\n x: 88,\n y: 89,\n z: 90,\n leftwindowkey: 91,\n rightwindowkey: 92,\n meta: isAppleDevice ? [91, 93] : [91, 92],\n selectkey: 93,\n numpad0: 96,\n numpad1: 97,\n numpad2: 98,\n numpad3: 99,\n numpad4: 100,\n numpad5: 101,\n numpad6: 102,\n numpad7: 103,\n numpad8: 104,\n numpad9: 105,\n multiply: 106,\n add: 107,\n subtract: 109,\n decimalpoint: 110,\n divide: 111,\n f1: 112,\n f2: 113,\n f3: 114,\n f4: 115,\n f5: 116,\n f6: 117,\n f7: 118,\n f8: 119,\n f9: 120,\n f10: 121,\n f11: 122,\n f12: 123,\n numlock: 144,\n scrolllock: 145,\n semicolon: 186,\n equalsign: 187,\n '=': 187,\n comma: 188,\n dash: 189,\n '-': 189,\n period: 190,\n forwardslash: 191,\n graveaccent: 192,\n openbracket: 219,\n backslash: 220,\n closebracket: 221,\n singlequote: 222,\n};\n\nconst modifierKey: any = {\n ctrl: (event: KeyboardEvent) => event.ctrlKey,\n shift: (event: KeyboardEvent) => event.shiftKey,\n alt: (event: KeyboardEvent) => event.altKey,\n meta: (event: KeyboardEvent) => {\n if (event.type === 'keyup') {\n return (aliasKeyCodeMap.meta as number[]).includes(event.keyCode);\n }\n return event.metaKey;\n },\n};\n\n// 根据 event 计算激活键数量\nfunction countKeyByEvent(event: KeyboardEvent): number {\n const countOfModifier = Object.keys(modifierKey).reduce((total, key) => {\n if (modifierKey[key](event)) {\n return total + 1;\n }\n\n return total;\n }, 0);\n\n // 16 17 18 91 92 是修饰键的 keyCode,如果 keyCode 是修饰键,那么激活数量就是修饰键的数量,如果不是,那么就需要 +1\n return [16, 17, 18, 91, 92].includes(event.keyCode) ? countOfModifier : countOfModifier + 1;\n}\n\n/**\n *\n * @param event\n * @param keyString 'ctrl.s' 'meta.s'\n * @param exactMatch\n */\nfunction isKeyStringMatch(event: KeyboardEvent, keyString: string, exactMatch = true): boolean {\n // 浏览器自动补全 input 的时候,会触发 keyDown、keyUp 事件,但此时 event.key 等为空\n if (!event.key || !keyString) {\n return false;\n }\n // 字符串依次判断是否有组合键\n const genArr = keyString.split(/\\s+/);\n let genLen = 0;\n\n for (const key of genArr) {\n // 组合键\n const genModifier = modifierKey[key];\n // keyCode 别名\n const aliasKeyCode: number | number[] = aliasKeyCodeMap[key.toLowerCase()];\n\n if ((genModifier && genModifier(event)) || (aliasKeyCode && aliasKeyCode === event.keyCode)) {\n genLen++;\n }\n }\n\n /**\n * 需要判断触发的键位和监听的键位完全一致,判断方法就是触发的键位里有且等于监听的键位\n * genLen === genArr.length 能判断出来触发的键位里有监听的键位\n * countKeyByEvent(event) === genArr.length 判断出来触发的键位数量里有且等于监听的键位数量\n * 主要用来防止按组合键其子集也会触发的情况,例如监听 ctrl+a 会触发监听 ctrl 和 a 两个键的事件。\n */\n if (exactMatch) {\n return genLen === genArr.length && countKeyByEvent(event) === genArr.length;\n }\n return genLen === genArr.length;\n}\n\n/**\n * 匹配指定的快捷键\n * @param event\n * @param shortcuts\n */\nexport function isShortcutsMatch(event: KeyboardEvent, shortcuts: string[]): boolean {\n return shortcuts.some(keyString => isKeyStringMatch(event, keyString));\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { inject, injectable } from 'inversify';\nimport { Layer, SelectionService, Command } from '@flowgram-vue/core';\n\nimport { isShortcutsMatch } from '../shortcuts-utils';\nimport { ShortcutsRegistry } from '../shortcuts-contribution';\n\n@injectable()\nexport class ShortcutsLayer extends Layer<object> {\n static type = 'ShortcutsLayer';\n\n @inject(ShortcutsRegistry) shortcuts: ShortcutsRegistry;\n\n @inject(SelectionService) selection: SelectionService;\n\n onReady(): void {\n this.shortcuts.addHandlersIfNotFound(\n /**\n * 放大\n */\n {\n commandId: Command.Default.ZOOM_IN,\n shortcuts: ['meta =', 'ctrl ='],\n execute: () => {\n // TODO 这里要判断 CurrentEditor\n this.config.zoomin();\n },\n },\n /**\n * 缩小\n */\n {\n commandId: Command.Default.ZOOM_OUT,\n shortcuts: ['meta -', 'ctrl -'],\n execute: () => {\n // TODO 这里要判断 CurrentEditor\n this.config.zoomout();\n },\n },\n );\n this.toDispose.pushAll([\n // 监听画布鼠标移动事件\n this.listenPlaygroundEvent('keydown', (e: KeyboardEvent) => {\n if (!this.isFocused || e.target !== this.playgroundNode) {\n return;\n }\n this.shortcuts.shortcutsHandlers.some(shortcutsHandler => {\n if (\n isShortcutsMatch(e, shortcutsHandler.shortcuts) &&\n (!shortcutsHandler.isEnabled || shortcutsHandler.isEnabled(e))\n ) {\n shortcutsHandler.execute(e);\n e.preventDefault();\n return true;\n }\n });\n }),\n ]);\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { bindContributionProvider, definePluginCreator } from '@flowgram-vue/core';\n\nimport { ShortcutsRegistry, ShortcutsContribution } from './shortcuts-contribution';\nimport { ShortcutsLayer } from './layers';\n\n/**\n * @param opts\n *\n * createShortcutsPlugin({\n * registerShortcuts(registry) {\n * }\n * })\n */\nexport const createShortcutsPlugin = definePluginCreator<ShortcutsContribution>({\n onBind: ({ bind }) => {\n bind(ShortcutsRegistry).toSelf().inSingletonScope();\n bindContributionProvider(bind, ShortcutsContribution);\n },\n onInit: (ctx) => {\n ctx.playground.registerLayer(ShortcutsLayer);\n },\n contributionKeys: [ShortcutsContribution],\n});\n"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@flowgram-vue/shortcuts-plugin",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "homepage": "https://flowgram.ai/",
7
+ "repository": "https://github.com/Crayon-hua/flowgram-vue",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "files": [
19
+ "src",
20
+ "dist"
21
+ ],
22
+ "dependencies": {
23
+ "inversify": "^6.0.1",
24
+ "reflect-metadata": "~0.2.2",
25
+ "@flowgram-vue/core": "0.2.0"
26
+ },
27
+ "peerDependencies": {
28
+ "vue": "^3.3.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20",
32
+ "eslint": "^9.0.0",
33
+ "typescript": "^5.8.3",
34
+ "vitest": "^3.2.4",
35
+ "vue": "^3.5.18",
36
+ "@flowgram-vue/ts-config": "0.2.0",
37
+ "@flowgram-vue/eslint-config": "0.2.0",
38
+ "@flowgram-vue/build-config": "0.2.0"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "registry": "https://registry.npmjs.org/"
43
+ },
44
+ "scripts": {
45
+ "build": "flowgram-build",
46
+ "test": "exit 0",
47
+ "test:cov": "exit 0",
48
+ "lint": "eslint .",
49
+ "ts-check": "tsc --noEmit",
50
+ "build:fast": "flowgram-build --fast",
51
+ "build:watch": "flowgram-build --watch"
52
+ }
53
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { bindContributionProvider, definePluginCreator } from '@flowgram-vue/core';
7
+
8
+ import { ShortcutsRegistry, ShortcutsContribution } from './shortcuts-contribution';
9
+ import { ShortcutsLayer } from './layers';
10
+
11
+ /**
12
+ * @param opts
13
+ *
14
+ * createShortcutsPlugin({
15
+ * registerShortcuts(registry) {
16
+ * }
17
+ * })
18
+ */
19
+ export const createShortcutsPlugin = definePluginCreator<ShortcutsContribution>({
20
+ onBind: ({ bind }) => {
21
+ bind(ShortcutsRegistry).toSelf().inSingletonScope();
22
+ bindContributionProvider(bind, ShortcutsContribution);
23
+ },
24
+ onInit: (ctx) => {
25
+ ctx.playground.registerLayer(ShortcutsLayer);
26
+ },
27
+ contributionKeys: [ShortcutsContribution],
28
+ });
package/src/env.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ declare module '*.vue' {
7
+ import type { DefineComponent } from 'vue';
8
+ const component: DefineComponent<object, object, unknown>;
9
+ export default component;
10
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './create-shortcuts-plugin';
7
+ export * from './shortcuts-contribution';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './shortcuts-layer';
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { inject, injectable } from 'inversify';
7
+ import { Layer, SelectionService, Command } from '@flowgram-vue/core';
8
+
9
+ import { isShortcutsMatch } from '../shortcuts-utils';
10
+ import { ShortcutsRegistry } from '../shortcuts-contribution';
11
+
12
+ @injectable()
13
+ export class ShortcutsLayer extends Layer<object> {
14
+ static type = 'ShortcutsLayer';
15
+
16
+ @inject(ShortcutsRegistry) shortcuts: ShortcutsRegistry;
17
+
18
+ @inject(SelectionService) selection: SelectionService;
19
+
20
+ onReady(): void {
21
+ this.shortcuts.addHandlersIfNotFound(
22
+ /**
23
+ * 放大
24
+ */
25
+ {
26
+ commandId: Command.Default.ZOOM_IN,
27
+ shortcuts: ['meta =', 'ctrl ='],
28
+ execute: () => {
29
+ // TODO 这里要判断 CurrentEditor
30
+ this.config.zoomin();
31
+ },
32
+ },
33
+ /**
34
+ * 缩小
35
+ */
36
+ {
37
+ commandId: Command.Default.ZOOM_OUT,
38
+ shortcuts: ['meta -', 'ctrl -'],
39
+ execute: () => {
40
+ // TODO 这里要判断 CurrentEditor
41
+ this.config.zoomout();
42
+ },
43
+ },
44
+ );
45
+ this.toDispose.pushAll([
46
+ // 监听画布鼠标移动事件
47
+ this.listenPlaygroundEvent('keydown', (e: KeyboardEvent) => {
48
+ if (!this.isFocused || e.target !== this.playgroundNode) {
49
+ return;
50
+ }
51
+ this.shortcuts.shortcutsHandlers.some(shortcutsHandler => {
52
+ if (
53
+ isShortcutsMatch(e, shortcutsHandler.shortcuts) &&
54
+ (!shortcutsHandler.isEnabled || shortcutsHandler.isEnabled(e))
55
+ ) {
56
+ shortcutsHandler.execute(e);
57
+ e.preventDefault();
58
+ return true;
59
+ }
60
+ });
61
+ }),
62
+ ]);
63
+ }
64
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { inject, injectable, named, optional, postConstruct } from 'inversify';
7
+ import { Command, CommandRegistry, ContributionProvider } from '@flowgram-vue/core';
8
+
9
+ export interface ShortcutsHandler {
10
+ commandId: string;
11
+ commandDetail?: Omit<Command, 'id'>;
12
+ shortcuts: string[];
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ isEnabled?: (...args: any[]) => boolean;
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ execute: (...args: any[]) => void;
17
+ }
18
+
19
+ export const ShortcutsContribution = Symbol('ShortcutsContribution');
20
+
21
+ export interface ShortcutsContribution {
22
+ registerShortcuts: (registry: ShortcutsRegistry) => void;
23
+ }
24
+
25
+ @injectable()
26
+ export class ShortcutsRegistry {
27
+ @inject(ContributionProvider)
28
+ @named(ShortcutsContribution)
29
+ @optional()
30
+ protected contribs: ContributionProvider<ShortcutsContribution>;
31
+
32
+ @inject(CommandRegistry) protected commandRegistry: CommandRegistry;
33
+
34
+ shortcutsHandlers: ShortcutsHandler[] = [];
35
+
36
+ addHandlers(...handlers: ShortcutsHandler[]): void {
37
+ // 注册 command
38
+ handlers.forEach((handler) => {
39
+ if (!this.commandRegistry.getCommand(handler.commandId)) {
40
+ this.commandRegistry.registerCommand(
41
+ { id: handler.commandId, ...(handler.commandDetail || {}) },
42
+ { execute: handler.execute, isEnabled: handler.isEnabled }
43
+ );
44
+ } else {
45
+ this.commandRegistry.registerHandler(handler.commandId, {
46
+ execute: handler.execute,
47
+ isEnabled: handler.isEnabled,
48
+ });
49
+ }
50
+ });
51
+ // Insert before for override pre handlers
52
+ this.shortcutsHandlers.unshift(...handlers);
53
+ }
54
+
55
+ addHandlersIfNotFound(...handlers: ShortcutsHandler[]): void {
56
+ handlers.forEach((handler) => {
57
+ if (!this.has(handler.commandId)) {
58
+ this.addHandlers(handler);
59
+ }
60
+ });
61
+ }
62
+
63
+ removeHandler(commandId: string): void {
64
+ this.shortcutsHandlers = this.shortcutsHandlers.filter(
65
+ (handler) => handler.commandId !== commandId
66
+ );
67
+ }
68
+
69
+ has(commandId: string): boolean {
70
+ return this.shortcutsHandlers.some((handler) => handler.commandId === commandId);
71
+ }
72
+
73
+ @postConstruct()
74
+ protected init(): void {
75
+ this.contribs?.forEach((contrib) => contrib.registerShortcuts(this));
76
+ }
77
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ const isAppleDevice = /(mac|iphone|ipod|ipad)/i.test(
7
+ typeof navigator !== 'undefined' ? navigator?.platform : '',
8
+ );
9
+ // 键盘事件 keyCode 别名
10
+ const aliasKeyCodeMap: Record<string, number | number[]> = {
11
+ '0': 48,
12
+ '1': 49,
13
+ '2': 50,
14
+ '3': 51,
15
+ '4': 52,
16
+ '5': 53,
17
+ '6': 54,
18
+ '7': 55,
19
+ '8': 56,
20
+ '9': 57,
21
+ backspace: 8,
22
+ tab: 9,
23
+ enter: 13,
24
+ shift: 16,
25
+ ctrl: 17,
26
+ alt: 18,
27
+ pausebreak: 19,
28
+ capslock: 20,
29
+ esc: 27,
30
+ space: 32,
31
+ pageup: 33,
32
+ pagedown: 34,
33
+ end: 35,
34
+ home: 36,
35
+ leftarrow: 37,
36
+ uparrow: 38,
37
+ rightarrow: 39,
38
+ downarrow: 40,
39
+ insert: 45,
40
+ delete: 46,
41
+ a: 65,
42
+ b: 66,
43
+ c: 67,
44
+ d: 68,
45
+ e: 69,
46
+ f: 70,
47
+ g: 71,
48
+ h: 72,
49
+ i: 73,
50
+ j: 74,
51
+ k: 75,
52
+ l: 76,
53
+ m: 77,
54
+ n: 78,
55
+ o: 79,
56
+ p: 80,
57
+ q: 81,
58
+ r: 82,
59
+ s: 83,
60
+ t: 84,
61
+ u: 85,
62
+ v: 86,
63
+ w: 87,
64
+ x: 88,
65
+ y: 89,
66
+ z: 90,
67
+ leftwindowkey: 91,
68
+ rightwindowkey: 92,
69
+ meta: isAppleDevice ? [91, 93] : [91, 92],
70
+ selectkey: 93,
71
+ numpad0: 96,
72
+ numpad1: 97,
73
+ numpad2: 98,
74
+ numpad3: 99,
75
+ numpad4: 100,
76
+ numpad5: 101,
77
+ numpad6: 102,
78
+ numpad7: 103,
79
+ numpad8: 104,
80
+ numpad9: 105,
81
+ multiply: 106,
82
+ add: 107,
83
+ subtract: 109,
84
+ decimalpoint: 110,
85
+ divide: 111,
86
+ f1: 112,
87
+ f2: 113,
88
+ f3: 114,
89
+ f4: 115,
90
+ f5: 116,
91
+ f6: 117,
92
+ f7: 118,
93
+ f8: 119,
94
+ f9: 120,
95
+ f10: 121,
96
+ f11: 122,
97
+ f12: 123,
98
+ numlock: 144,
99
+ scrolllock: 145,
100
+ semicolon: 186,
101
+ equalsign: 187,
102
+ '=': 187,
103
+ comma: 188,
104
+ dash: 189,
105
+ '-': 189,
106
+ period: 190,
107
+ forwardslash: 191,
108
+ graveaccent: 192,
109
+ openbracket: 219,
110
+ backslash: 220,
111
+ closebracket: 221,
112
+ singlequote: 222,
113
+ };
114
+
115
+ const modifierKey: any = {
116
+ ctrl: (event: KeyboardEvent) => event.ctrlKey,
117
+ shift: (event: KeyboardEvent) => event.shiftKey,
118
+ alt: (event: KeyboardEvent) => event.altKey,
119
+ meta: (event: KeyboardEvent) => {
120
+ if (event.type === 'keyup') {
121
+ return (aliasKeyCodeMap.meta as number[]).includes(event.keyCode);
122
+ }
123
+ return event.metaKey;
124
+ },
125
+ };
126
+
127
+ // 根据 event 计算激活键数量
128
+ function countKeyByEvent(event: KeyboardEvent): number {
129
+ const countOfModifier = Object.keys(modifierKey).reduce((total, key) => {
130
+ if (modifierKey[key](event)) {
131
+ return total + 1;
132
+ }
133
+
134
+ return total;
135
+ }, 0);
136
+
137
+ // 16 17 18 91 92 是修饰键的 keyCode,如果 keyCode 是修饰键,那么激活数量就是修饰键的数量,如果不是,那么就需要 +1
138
+ return [16, 17, 18, 91, 92].includes(event.keyCode) ? countOfModifier : countOfModifier + 1;
139
+ }
140
+
141
+ /**
142
+ *
143
+ * @param event
144
+ * @param keyString 'ctrl.s' 'meta.s'
145
+ * @param exactMatch
146
+ */
147
+ function isKeyStringMatch(event: KeyboardEvent, keyString: string, exactMatch = true): boolean {
148
+ // 浏览器自动补全 input 的时候,会触发 keyDown、keyUp 事件,但此时 event.key 等为空
149
+ if (!event.key || !keyString) {
150
+ return false;
151
+ }
152
+ // 字符串依次判断是否有组合键
153
+ const genArr = keyString.split(/\s+/);
154
+ let genLen = 0;
155
+
156
+ for (const key of genArr) {
157
+ // 组合键
158
+ const genModifier = modifierKey[key];
159
+ // keyCode 别名
160
+ const aliasKeyCode: number | number[] = aliasKeyCodeMap[key.toLowerCase()];
161
+
162
+ if ((genModifier && genModifier(event)) || (aliasKeyCode && aliasKeyCode === event.keyCode)) {
163
+ genLen++;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * 需要判断触发的键位和监听的键位完全一致,判断方法就是触发的键位里有且等于监听的键位
169
+ * genLen === genArr.length 能判断出来触发的键位里有监听的键位
170
+ * countKeyByEvent(event) === genArr.length 判断出来触发的键位数量里有且等于监听的键位数量
171
+ * 主要用来防止按组合键其子集也会触发的情况,例如监听 ctrl+a 会触发监听 ctrl 和 a 两个键的事件。
172
+ */
173
+ if (exactMatch) {
174
+ return genLen === genArr.length && countKeyByEvent(event) === genArr.length;
175
+ }
176
+ return genLen === genArr.length;
177
+ }
178
+
179
+ /**
180
+ * 匹配指定的快捷键
181
+ * @param event
182
+ * @param shortcuts
183
+ */
184
+ export function isShortcutsMatch(event: KeyboardEvent, shortcuts: string[]): boolean {
185
+ return shortcuts.some(keyString => isKeyStringMatch(event, keyString));
186
+ }