@monbolc/lowcode-plugin-command 2.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/es/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — barrel export
3
+ *
4
+ * Command pattern + undo/redo history + keyboard shortcuts.
5
+ * SapuLowcodeEngine L2.
6
+ */
7
+ export { CommandManager } from './manager';
8
+ export { parseShortcut, bindShortcuts, isMacOS, } from './shortcut';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EACL,aAAa,EACb,aAAa,EACb,OAAO,GACR,MAAM,YAAY,CAAC"}
package/es/manager.js ADDED
@@ -0,0 +1,149 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — CommandManager
3
+ *
4
+ * Stores registered commands in a Map, runs them on demand, and maintains
5
+ * an undo/redo history. Each history entry is `{ name, args, returnValue, command }`
6
+ * so we can replay or undo without re-fetching the command.
7
+ *
8
+ * The manager is the only stateful piece; commands themselves are stateless.
9
+ */
10
+ import { Emitter } from '@monbolc/lowcode-utils';
11
+ /** Maximum size of the undo stack. Older entries are dropped. */
12
+ const DEFAULT_HISTORY_LIMIT = 200;
13
+ export class CommandManager {
14
+ constructor(options = {}) {
15
+ this.events = new Emitter();
16
+ this.commands = new Map();
17
+ this.undoStack = [];
18
+ this.redoStack = [];
19
+ this.historyLimit = Math.max(0, options.historyLimit ?? DEFAULT_HISTORY_LIMIT);
20
+ this.autoMerge = options.autoMerge ?? true;
21
+ }
22
+ register(command) {
23
+ if (this.commands.has(command.name)) {
24
+ throw new Error(`[CommandManager] command "${command.name}" is already registered`);
25
+ }
26
+ this.commands.set(command.name, command);
27
+ this.events.emit('registered', { name: command.name });
28
+ }
29
+ unregister(name) {
30
+ const existed = this.commands.delete(name);
31
+ if (existed) {
32
+ this.events.emit('unregistered', { name });
33
+ // Also drop any in-flight history entries for that command.
34
+ this.pruneHistoryFor(name);
35
+ }
36
+ return existed;
37
+ }
38
+ has(name) {
39
+ return this.commands.has(name);
40
+ }
41
+ get(name) {
42
+ return this.commands.get(name);
43
+ }
44
+ list() {
45
+ return Array.from(this.commands.values());
46
+ }
47
+ async execute(name, args) {
48
+ const command = this.commands.get(name);
49
+ if (!command) {
50
+ throw new Error(`[CommandManager] no command registered as "${name}"`);
51
+ }
52
+ let returnValue;
53
+ try {
54
+ returnValue = await command.execute(args);
55
+ }
56
+ catch (err) {
57
+ // Do NOT push to history on failure. Re-throw so caller can react.
58
+ throw err;
59
+ }
60
+ this.recordHistory(name, args, returnValue, command);
61
+ this.events.emit('executed', { name, args, result: returnValue });
62
+ return returnValue;
63
+ }
64
+ async undo() {
65
+ const entry = this.undoStack.pop();
66
+ if (!entry)
67
+ return;
68
+ try {
69
+ if (entry.command.undo) {
70
+ await entry.command.undo(entry.args, entry.returnValue);
71
+ }
72
+ this.redoStack.push(entry);
73
+ this.events.emit('undone', { name: entry.name });
74
+ }
75
+ catch (err) {
76
+ // Restore the entry on failure so the user doesn't lose the chance to retry.
77
+ this.undoStack.push(entry);
78
+ throw err;
79
+ }
80
+ }
81
+ async redo() {
82
+ const entry = this.redoStack.pop();
83
+ if (!entry)
84
+ return;
85
+ try {
86
+ const returnValue = await entry.command.execute(entry.args);
87
+ // Replace the entry with a fresh one (new timestamp + return value).
88
+ this.undoStack.push({ ...entry, returnValue, lastTouchedAt: Date.now() });
89
+ this.events.emit('redone', { name: entry.name });
90
+ }
91
+ catch (err) {
92
+ this.redoStack.push(entry);
93
+ throw err;
94
+ }
95
+ }
96
+ canUndo() {
97
+ return this.undoStack.length > 0;
98
+ }
99
+ canRedo() {
100
+ return this.redoStack.length > 0;
101
+ }
102
+ undoStackSize() {
103
+ return this.undoStack.length;
104
+ }
105
+ redoStackSize() {
106
+ return this.redoStack.length;
107
+ }
108
+ clearHistory() {
109
+ this.undoStack.length = 0;
110
+ this.redoStack.length = 0;
111
+ this.events.emit('cleared', {});
112
+ }
113
+ // --- internals ---
114
+ recordHistory(name, args, returnValue, command) {
115
+ // Reset redo stack on any new execute (standard undo/redo semantics).
116
+ if (this.redoStack.length > 0)
117
+ this.redoStack.length = 0;
118
+ if (this.autoMerge && command.mergeable) {
119
+ const top = this.undoStack[this.undoStack.length - 1];
120
+ const mergeWindow = command.mergeWindowMs ?? 500;
121
+ const now = Date.now();
122
+ if (top &&
123
+ top.name === name &&
124
+ now - top.lastTouchedAt <= mergeWindow) {
125
+ top.args = args;
126
+ top.returnValue = returnValue;
127
+ top.lastTouchedAt = now;
128
+ return;
129
+ }
130
+ }
131
+ this.undoStack.push({
132
+ name,
133
+ args,
134
+ returnValue,
135
+ command,
136
+ lastTouchedAt: Date.now(),
137
+ });
138
+ // Trim oldest if over the limit.
139
+ while (this.undoStack.length > this.historyLimit) {
140
+ this.undoStack.shift();
141
+ }
142
+ }
143
+ pruneHistoryFor(name) {
144
+ const filter = (entry) => entry.name !== name;
145
+ this.undoStack.splice(0, this.undoStack.length, ...this.undoStack.filter(filter));
146
+ this.redoStack.splice(0, this.redoStack.length, ...this.redoStack.filter(filter));
147
+ }
148
+ }
149
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
package/es/shortcut.js ADDED
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — keyboard shortcut binding
3
+ *
4
+ * Parses shortcut strings like `"Mod+Z"` / `"Ctrl+Shift+P"` and binds them
5
+ * to a command manager so users can undo/redo with the keyboard.
6
+ *
7
+ * "Mod" maps to Cmd on macOS and Ctrl elsewhere — the conventional behavior
8
+ * in editor apps (VSCode, Figma, etc.).
9
+ */
10
+ const MODIFIER_KEYS = new Set([
11
+ 'Mod',
12
+ 'Ctrl',
13
+ 'Meta',
14
+ 'Shift',
15
+ 'Alt',
16
+ ]);
17
+ export function isMacOS() {
18
+ if (typeof navigator === 'undefined')
19
+ return false;
20
+ const platform = navigator.userAgentData?.platform ??
21
+ navigator.platform ??
22
+ '';
23
+ return /mac|iphone|ipad|ipod/i.test(platform);
24
+ }
25
+ /**
26
+ * Parse a shortcut string. Examples:
27
+ * "Mod+Z" -> { modifiers: {Mod}, key: "z" }
28
+ * "Ctrl+Shift+P" -> { modifiers: {Ctrl, Shift}, key: "p" }
29
+ */
30
+ export function parseShortcut(input) {
31
+ if (!input || typeof input !== 'string') {
32
+ throw new Error(`[shortcut] invalid shortcut: ${String(input)}`);
33
+ }
34
+ const parts = input
35
+ .split('+')
36
+ .map((p) => p.trim())
37
+ .filter(Boolean);
38
+ if (parts.length < 2) {
39
+ throw new Error(`[shortcut] shortcut must include at least one modifier + a key, got: "${input}"`);
40
+ }
41
+ const key = parts[parts.length - 1].toLowerCase();
42
+ const modifiers = new Set();
43
+ for (const p of parts.slice(0, -1)) {
44
+ const token = p;
45
+ if (!MODIFIER_KEYS.has(token)) {
46
+ throw new Error(`[shortcut] unknown modifier "${p}" in "${input}"`);
47
+ }
48
+ modifiers.add(token);
49
+ }
50
+ return { raw: input, modifiers, key };
51
+ }
52
+ function eventModifiersPressed(e) {
53
+ const set = new Set();
54
+ const mod = isMacOS() ? 'Meta' : 'Ctrl';
55
+ if (e.getModifierState(mod))
56
+ set.add('Mod');
57
+ if (e.shiftKey)
58
+ set.add('Shift');
59
+ if (e.altKey)
60
+ set.add('Alt');
61
+ // Direct Meta key on macOS is already counted as Mod; on non-mac the Meta key
62
+ // (Windows key) is intentionally ignored.
63
+ return set;
64
+ }
65
+ function sameModifiers(a, b) {
66
+ if (a.size !== b.size)
67
+ return false;
68
+ for (const m of a)
69
+ if (!b.has(m))
70
+ return false;
71
+ return true;
72
+ }
73
+ /**
74
+ * Walk all registered commands, find any with a `shortcut` field, and wire them
75
+ * up to a global keydown listener on the chosen target.
76
+ */
77
+ export function bindShortcuts(manager, options = {}) {
78
+ if (typeof window === 'undefined') {
79
+ return { dispose: () => undefined, bindings: () => [] };
80
+ }
81
+ const target = options.target ?? window;
82
+ const preventDefault = options.preventDefault ?? true;
83
+ const parsed = [];
84
+ for (const cmd of manager.list()) {
85
+ const shortcuts = [].concat(cmd.shortcut ?? []);
86
+ for (const s of shortcuts) {
87
+ try {
88
+ parsed.push({ command: cmd, shortcut: parseShortcut(s) });
89
+ }
90
+ catch {
91
+ // Skip malformed shortcuts silently — they shouldn't break the binder.
92
+ }
93
+ }
94
+ }
95
+ const handler = (e) => {
96
+ const ke = e;
97
+ const pressed = eventModifiersPressed(ke);
98
+ const key = ke.key.toLowerCase();
99
+ for (const { command, shortcut } of parsed) {
100
+ if (shortcut.key === key && sameModifiers(pressed, shortcut.modifiers)) {
101
+ if (preventDefault)
102
+ ke.preventDefault();
103
+ // Fire-and-forget; errors land in console.
104
+ void manager.execute(command.name).catch((err) => {
105
+ if (typeof console !== 'undefined' && console.error) {
106
+ console.error(`[shortcut] "${command.name}" failed:`, err);
107
+ }
108
+ });
109
+ return;
110
+ }
111
+ }
112
+ };
113
+ target.addEventListener('keydown', handler);
114
+ return {
115
+ dispose: () => target.removeEventListener('keydown', handler),
116
+ bindings: () => parsed.map((p) => p.command),
117
+ };
118
+ }
119
+ //# sourceMappingURL=shortcut.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcut.js","sourceRoot":"","sources":["../src/shortcut.ts"],"names":[],"mappings":"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"}
package/es/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — public types
3
+ *
4
+ * The command pattern lets plugins expose "actions" that the user can invoke
5
+ * by name (from the menu, a button, or a keyboard shortcut). Each command
6
+ * knows how to do its work, and — if reversible — how to undo it.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — barrel export
3
+ *
4
+ * Command pattern + undo/redo history + keyboard shortcuts.
5
+ * SapuLowcodeEngine L2.
6
+ */
7
+ export type { CommandArgs, CommandResult, CommandManagerEvents, ICommand, ICommandManager, } from './types';
8
+ export { CommandManager } from './manager';
9
+ export type { CommandManagerOptions } from './manager';
10
+ export { parseShortcut, bindShortcuts, isMacOS, } from './shortcut';
11
+ export type { ParsedShortcut, ShortcutBinder, ShortcutBinderOptions, ShortcutToken, } from './shortcut';
package/lib/index.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /**
3
+ * @monbolc/lowcode-plugin-command — barrel export
4
+ *
5
+ * Command pattern + undo/redo history + keyboard shortcuts.
6
+ * SapuLowcodeEngine L2.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isMacOS = exports.bindShortcuts = exports.parseShortcut = exports.CommandManager = void 0;
10
+ var manager_1 = require("./manager");
11
+ Object.defineProperty(exports, "CommandManager", { enumerable: true, get: function () { return manager_1.CommandManager; } });
12
+ var shortcut_1 = require("./shortcut");
13
+ Object.defineProperty(exports, "parseShortcut", { enumerable: true, get: function () { return shortcut_1.parseShortcut; } });
14
+ Object.defineProperty(exports, "bindShortcuts", { enumerable: true, get: function () { return shortcut_1.bindShortcuts; } });
15
+ Object.defineProperty(exports, "isMacOS", { enumerable: true, get: function () { return shortcut_1.isMacOS; } });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAUH,qCAA2C;AAAlC,yGAAA,cAAc,OAAA;AAGvB,uCAIoB;AAHlB,yGAAA,aAAa,OAAA;AACb,yGAAA,aAAa,OAAA;AACb,mGAAA,OAAO,OAAA"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — CommandManager
3
+ *
4
+ * Stores registered commands in a Map, runs them on demand, and maintains
5
+ * an undo/redo history. Each history entry is `{ name, args, returnValue, command }`
6
+ * so we can replay or undo without re-fetching the command.
7
+ *
8
+ * The manager is the only stateful piece; commands themselves are stateless.
9
+ */
10
+ import { Emitter } from '@monbolc/lowcode-utils';
11
+ import type { CommandManagerEvents, CommandArgs, CommandResult, ICommand, ICommandManager } from './types';
12
+ export interface CommandManagerOptions {
13
+ /** Max undo-stack depth. Defaults to 200. */
14
+ historyLimit?: number;
15
+ /**
16
+ * If true, two consecutive execute() calls of the same `mergeable` command
17
+ * within `mergeWindowMs` are collapsed into one history entry. Defaults to true.
18
+ */
19
+ autoMerge?: boolean;
20
+ }
21
+ export declare class CommandManager implements ICommandManager {
22
+ readonly events: Emitter<CommandManagerEvents>;
23
+ private readonly commands;
24
+ private readonly undoStack;
25
+ private readonly redoStack;
26
+ private readonly historyLimit;
27
+ private readonly autoMerge;
28
+ constructor(options?: CommandManagerOptions);
29
+ register<TArgs = CommandArgs, TReturn = CommandResult>(command: ICommand<TArgs, TReturn>): void;
30
+ unregister(name: string): boolean;
31
+ has(name: string): boolean;
32
+ get(name: string): ICommand | undefined;
33
+ list(): ICommand[];
34
+ execute<TArgs = CommandArgs, TReturn = CommandResult>(name: string, args?: TArgs): Promise<TReturn | undefined>;
35
+ undo(): Promise<void>;
36
+ redo(): Promise<void>;
37
+ canUndo(): boolean;
38
+ canRedo(): boolean;
39
+ undoStackSize(): number;
40
+ redoStackSize(): number;
41
+ clearHistory(): void;
42
+ private recordHistory;
43
+ private pruneHistoryFor;
44
+ }
package/lib/manager.js ADDED
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ /**
3
+ * @monbolc/lowcode-plugin-command — CommandManager
4
+ *
5
+ * Stores registered commands in a Map, runs them on demand, and maintains
6
+ * an undo/redo history. Each history entry is `{ name, args, returnValue, command }`
7
+ * so we can replay or undo without re-fetching the command.
8
+ *
9
+ * The manager is the only stateful piece; commands themselves are stateless.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.CommandManager = void 0;
13
+ const lowcode_utils_1 = require("@monbolc/lowcode-utils");
14
+ /** Maximum size of the undo stack. Older entries are dropped. */
15
+ const DEFAULT_HISTORY_LIMIT = 200;
16
+ class CommandManager {
17
+ constructor(options = {}) {
18
+ this.events = new lowcode_utils_1.Emitter();
19
+ this.commands = new Map();
20
+ this.undoStack = [];
21
+ this.redoStack = [];
22
+ this.historyLimit = Math.max(0, options.historyLimit ?? DEFAULT_HISTORY_LIMIT);
23
+ this.autoMerge = options.autoMerge ?? true;
24
+ }
25
+ register(command) {
26
+ if (this.commands.has(command.name)) {
27
+ throw new Error(`[CommandManager] command "${command.name}" is already registered`);
28
+ }
29
+ this.commands.set(command.name, command);
30
+ this.events.emit('registered', { name: command.name });
31
+ }
32
+ unregister(name) {
33
+ const existed = this.commands.delete(name);
34
+ if (existed) {
35
+ this.events.emit('unregistered', { name });
36
+ // Also drop any in-flight history entries for that command.
37
+ this.pruneHistoryFor(name);
38
+ }
39
+ return existed;
40
+ }
41
+ has(name) {
42
+ return this.commands.has(name);
43
+ }
44
+ get(name) {
45
+ return this.commands.get(name);
46
+ }
47
+ list() {
48
+ return Array.from(this.commands.values());
49
+ }
50
+ async execute(name, args) {
51
+ const command = this.commands.get(name);
52
+ if (!command) {
53
+ throw new Error(`[CommandManager] no command registered as "${name}"`);
54
+ }
55
+ let returnValue;
56
+ try {
57
+ returnValue = await command.execute(args);
58
+ }
59
+ catch (err) {
60
+ // Do NOT push to history on failure. Re-throw so caller can react.
61
+ throw err;
62
+ }
63
+ this.recordHistory(name, args, returnValue, command);
64
+ this.events.emit('executed', { name, args, result: returnValue });
65
+ return returnValue;
66
+ }
67
+ async undo() {
68
+ const entry = this.undoStack.pop();
69
+ if (!entry)
70
+ return;
71
+ try {
72
+ if (entry.command.undo) {
73
+ await entry.command.undo(entry.args, entry.returnValue);
74
+ }
75
+ this.redoStack.push(entry);
76
+ this.events.emit('undone', { name: entry.name });
77
+ }
78
+ catch (err) {
79
+ // Restore the entry on failure so the user doesn't lose the chance to retry.
80
+ this.undoStack.push(entry);
81
+ throw err;
82
+ }
83
+ }
84
+ async redo() {
85
+ const entry = this.redoStack.pop();
86
+ if (!entry)
87
+ return;
88
+ try {
89
+ const returnValue = await entry.command.execute(entry.args);
90
+ // Replace the entry with a fresh one (new timestamp + return value).
91
+ this.undoStack.push({ ...entry, returnValue, lastTouchedAt: Date.now() });
92
+ this.events.emit('redone', { name: entry.name });
93
+ }
94
+ catch (err) {
95
+ this.redoStack.push(entry);
96
+ throw err;
97
+ }
98
+ }
99
+ canUndo() {
100
+ return this.undoStack.length > 0;
101
+ }
102
+ canRedo() {
103
+ return this.redoStack.length > 0;
104
+ }
105
+ undoStackSize() {
106
+ return this.undoStack.length;
107
+ }
108
+ redoStackSize() {
109
+ return this.redoStack.length;
110
+ }
111
+ clearHistory() {
112
+ this.undoStack.length = 0;
113
+ this.redoStack.length = 0;
114
+ this.events.emit('cleared', {});
115
+ }
116
+ // --- internals ---
117
+ recordHistory(name, args, returnValue, command) {
118
+ // Reset redo stack on any new execute (standard undo/redo semantics).
119
+ if (this.redoStack.length > 0)
120
+ this.redoStack.length = 0;
121
+ if (this.autoMerge && command.mergeable) {
122
+ const top = this.undoStack[this.undoStack.length - 1];
123
+ const mergeWindow = command.mergeWindowMs ?? 500;
124
+ const now = Date.now();
125
+ if (top &&
126
+ top.name === name &&
127
+ now - top.lastTouchedAt <= mergeWindow) {
128
+ top.args = args;
129
+ top.returnValue = returnValue;
130
+ top.lastTouchedAt = now;
131
+ return;
132
+ }
133
+ }
134
+ this.undoStack.push({
135
+ name,
136
+ args,
137
+ returnValue,
138
+ command,
139
+ lastTouchedAt: Date.now(),
140
+ });
141
+ // Trim oldest if over the limit.
142
+ while (this.undoStack.length > this.historyLimit) {
143
+ this.undoStack.shift();
144
+ }
145
+ }
146
+ pruneHistoryFor(name) {
147
+ const filter = (entry) => entry.name !== name;
148
+ this.undoStack.splice(0, this.undoStack.length, ...this.undoStack.filter(filter));
149
+ this.redoStack.splice(0, this.redoStack.length, ...this.redoStack.filter(filter));
150
+ }
151
+ }
152
+ exports.CommandManager = CommandManager;
153
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — keyboard shortcut binding
3
+ *
4
+ * Parses shortcut strings like `"Mod+Z"` / `"Ctrl+Shift+P"` and binds them
5
+ * to a command manager so users can undo/redo with the keyboard.
6
+ *
7
+ * "Mod" maps to Cmd on macOS and Ctrl elsewhere — the conventional behavior
8
+ * in editor apps (VSCode, Figma, etc.).
9
+ */
10
+ import type { ICommand } from './types';
11
+ import type { ICommandManager } from './types';
12
+ export type ShortcutToken = 'Mod' | 'Ctrl' | 'Meta' | 'Shift' | 'Alt';
13
+ export interface ParsedShortcut {
14
+ /** Original input, normalized. */
15
+ raw: string;
16
+ /** Required modifier keys. */
17
+ modifiers: ReadonlySet<ShortcutToken>;
18
+ /** Lower-cased key code, e.g. "z", "p", "escape". */
19
+ key: string;
20
+ }
21
+ export declare function isMacOS(): boolean;
22
+ /**
23
+ * Parse a shortcut string. Examples:
24
+ * "Mod+Z" -> { modifiers: {Mod}, key: "z" }
25
+ * "Ctrl+Shift+P" -> { modifiers: {Ctrl, Shift}, key: "p" }
26
+ */
27
+ export declare function parseShortcut(input: string): ParsedShortcut;
28
+ export interface ShortcutBinderOptions {
29
+ /**
30
+ * The keyboard event target. Defaults to `window` when running in a browser.
31
+ * Pass a different HTMLElement for scoped bindings (e.g. inside a Shadow DOM).
32
+ */
33
+ target?: EventTarget;
34
+ /** If true (default), `preventDefault()` is called on a matched shortcut. */
35
+ preventDefault?: boolean;
36
+ }
37
+ export interface ShortcutBinder {
38
+ /** Stop listening and release references. */
39
+ dispose(): void;
40
+ /** Currently bound commands (snapshot). */
41
+ bindings(): ICommand[];
42
+ }
43
+ /**
44
+ * Walk all registered commands, find any with a `shortcut` field, and wire them
45
+ * up to a global keydown listener on the chosen target.
46
+ */
47
+ export declare function bindShortcuts(manager: ICommandManager, options?: ShortcutBinderOptions): ShortcutBinder;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * @monbolc/lowcode-plugin-command — keyboard shortcut binding
4
+ *
5
+ * Parses shortcut strings like `"Mod+Z"` / `"Ctrl+Shift+P"` and binds them
6
+ * to a command manager so users can undo/redo with the keyboard.
7
+ *
8
+ * "Mod" maps to Cmd on macOS and Ctrl elsewhere — the conventional behavior
9
+ * in editor apps (VSCode, Figma, etc.).
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.isMacOS = isMacOS;
13
+ exports.parseShortcut = parseShortcut;
14
+ exports.bindShortcuts = bindShortcuts;
15
+ const MODIFIER_KEYS = new Set([
16
+ 'Mod',
17
+ 'Ctrl',
18
+ 'Meta',
19
+ 'Shift',
20
+ 'Alt',
21
+ ]);
22
+ function isMacOS() {
23
+ if (typeof navigator === 'undefined')
24
+ return false;
25
+ const platform = navigator.userAgentData?.platform ??
26
+ navigator.platform ??
27
+ '';
28
+ return /mac|iphone|ipad|ipod/i.test(platform);
29
+ }
30
+ /**
31
+ * Parse a shortcut string. Examples:
32
+ * "Mod+Z" -> { modifiers: {Mod}, key: "z" }
33
+ * "Ctrl+Shift+P" -> { modifiers: {Ctrl, Shift}, key: "p" }
34
+ */
35
+ function parseShortcut(input) {
36
+ if (!input || typeof input !== 'string') {
37
+ throw new Error(`[shortcut] invalid shortcut: ${String(input)}`);
38
+ }
39
+ const parts = input
40
+ .split('+')
41
+ .map((p) => p.trim())
42
+ .filter(Boolean);
43
+ if (parts.length < 2) {
44
+ throw new Error(`[shortcut] shortcut must include at least one modifier + a key, got: "${input}"`);
45
+ }
46
+ const key = parts[parts.length - 1].toLowerCase();
47
+ const modifiers = new Set();
48
+ for (const p of parts.slice(0, -1)) {
49
+ const token = p;
50
+ if (!MODIFIER_KEYS.has(token)) {
51
+ throw new Error(`[shortcut] unknown modifier "${p}" in "${input}"`);
52
+ }
53
+ modifiers.add(token);
54
+ }
55
+ return { raw: input, modifiers, key };
56
+ }
57
+ function eventModifiersPressed(e) {
58
+ const set = new Set();
59
+ const mod = isMacOS() ? 'Meta' : 'Ctrl';
60
+ if (e.getModifierState(mod))
61
+ set.add('Mod');
62
+ if (e.shiftKey)
63
+ set.add('Shift');
64
+ if (e.altKey)
65
+ set.add('Alt');
66
+ // Direct Meta key on macOS is already counted as Mod; on non-mac the Meta key
67
+ // (Windows key) is intentionally ignored.
68
+ return set;
69
+ }
70
+ function sameModifiers(a, b) {
71
+ if (a.size !== b.size)
72
+ return false;
73
+ for (const m of a)
74
+ if (!b.has(m))
75
+ return false;
76
+ return true;
77
+ }
78
+ /**
79
+ * Walk all registered commands, find any with a `shortcut` field, and wire them
80
+ * up to a global keydown listener on the chosen target.
81
+ */
82
+ function bindShortcuts(manager, options = {}) {
83
+ if (typeof window === 'undefined') {
84
+ return { dispose: () => undefined, bindings: () => [] };
85
+ }
86
+ const target = options.target ?? window;
87
+ const preventDefault = options.preventDefault ?? true;
88
+ const parsed = [];
89
+ for (const cmd of manager.list()) {
90
+ const shortcuts = [].concat(cmd.shortcut ?? []);
91
+ for (const s of shortcuts) {
92
+ try {
93
+ parsed.push({ command: cmd, shortcut: parseShortcut(s) });
94
+ }
95
+ catch {
96
+ // Skip malformed shortcuts silently — they shouldn't break the binder.
97
+ }
98
+ }
99
+ }
100
+ const handler = (e) => {
101
+ const ke = e;
102
+ const pressed = eventModifiersPressed(ke);
103
+ const key = ke.key.toLowerCase();
104
+ for (const { command, shortcut } of parsed) {
105
+ if (shortcut.key === key && sameModifiers(pressed, shortcut.modifiers)) {
106
+ if (preventDefault)
107
+ ke.preventDefault();
108
+ // Fire-and-forget; errors land in console.
109
+ void manager.execute(command.name).catch((err) => {
110
+ if (typeof console !== 'undefined' && console.error) {
111
+ console.error(`[shortcut] "${command.name}" failed:`, err);
112
+ }
113
+ });
114
+ return;
115
+ }
116
+ }
117
+ };
118
+ target.addEventListener('keydown', handler);
119
+ return {
120
+ dispose: () => target.removeEventListener('keydown', handler),
121
+ bindings: () => parsed.map((p) => p.command),
122
+ };
123
+ }
124
+ //# sourceMappingURL=shortcut.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shortcut.js","sourceRoot":"","sources":["../src/shortcut.ts"],"names":[],"mappings":"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"}
package/lib/types.d.ts ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @monbolc/lowcode-plugin-command — public types
3
+ *
4
+ * The command pattern lets plugins expose "actions" that the user can invoke
5
+ * by name (from the menu, a button, or a keyboard shortcut). Each command
6
+ * knows how to do its work, and — if reversible — how to undo it.
7
+ */
8
+ import type { Emitter } from '@monbolc/lowcode-utils';
9
+ /** Anything a command can receive when invoked. */
10
+ export type CommandArgs = unknown;
11
+ /** What a command returns after a successful execute. */
12
+ export type CommandResult = void | CommandArgs | Promise<void | CommandArgs>;
13
+ /** A single command. Stateless; all per-instance state lives in the manager. */
14
+ export interface ICommand<TArgs = CommandArgs, TReturn = CommandResult> {
15
+ /** Globally unique name within a CommandManager (e.g. `"designer.duplicate"`). */
16
+ readonly name: string;
17
+ /** Human-readable label, for menus / tooltips. */
18
+ readonly label?: string;
19
+ /** Optional grouping key (e.g. `"file"`, `"edit"`). */
20
+ readonly group?: string;
21
+ /** Optional keyboard binding (e.g. `"Mod+Z"`, `"Ctrl+Shift+P"`). */
22
+ readonly shortcut?: string | string[];
23
+ /** Run the command. If it throws, the manager will not push to history. */
24
+ execute(args?: TArgs): TReturn;
25
+ /** Undo a previous execute. If absent, the command is not reversible. */
26
+ undo?(args?: TArgs, returnValue?: TReturn): TReturn | Promise<TReturn>;
27
+ /**
28
+ * Merge with the previous command of the same name if both
29
+ * `mergeable` and the time delta is below `mergeWindowMs`.
30
+ * Useful for drag-resize, text typing, etc.
31
+ */
32
+ readonly mergeable?: boolean;
33
+ readonly mergeWindowMs?: number;
34
+ }
35
+ /** Events emitted by CommandManager. */
36
+ export interface CommandManagerEvents extends Record<string, unknown> {
37
+ /** A command was registered. Payload: the command's name. */
38
+ registered: {
39
+ name: string;
40
+ };
41
+ /** A command was unregistered. */
42
+ unregistered: {
43
+ name: string;
44
+ };
45
+ /** A command finished executing. */
46
+ executed: {
47
+ name: string;
48
+ args: unknown;
49
+ result: unknown;
50
+ };
51
+ /** The manager undid the most recent command. */
52
+ undone: {
53
+ name: string;
54
+ };
55
+ /** The manager redid a previously undone command. */
56
+ redone: {
57
+ name: string;
58
+ };
59
+ /** History stack was cleared (e.g. on document reset). */
60
+ cleared: Record<string, never>;
61
+ }
62
+ export interface ICommandManager {
63
+ /** Underlying event bus. Subscribe to `CommandManagerEvents`. */
64
+ readonly events: Emitter<CommandManagerEvents>;
65
+ /** Register a command. Throws if `name` is already taken. */
66
+ register<TArgs = CommandArgs, TReturn = CommandResult>(command: ICommand<TArgs, TReturn>): void;
67
+ /** Unregister a command by name. */
68
+ unregister(name: string): boolean;
69
+ /** True if a command with this name is registered. */
70
+ has(name: string): boolean;
71
+ /** Get a registered command by name, or undefined. */
72
+ get(name: string): ICommand | undefined;
73
+ /** All registered commands (snapshot). */
74
+ list(): ICommand[];
75
+ /** Execute a command by name. */
76
+ execute<TArgs = CommandArgs, TReturn = CommandResult>(name: string, args?: TArgs): Promise<TReturn | undefined>;
77
+ /** Undo the most recent reversible command. No-op if stack is empty. */
78
+ undo(): Promise<void>;
79
+ /** Redo the most recent undone command. No-op if redo stack is empty. */
80
+ redo(): Promise<void>;
81
+ canUndo(): boolean;
82
+ canRedo(): boolean;
83
+ /** Number of commands in the undo stack. */
84
+ undoStackSize(): number;
85
+ /** Number of commands in the redo stack. */
86
+ redoStackSize(): number;
87
+ /** Clear both stacks (e.g. on document reset). */
88
+ clearHistory(): void;
89
+ }
package/lib/types.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * @monbolc/lowcode-plugin-command — public types
4
+ *
5
+ * The command pattern lets plugins expose "actions" that the user can invoke
6
+ * by name (from the menu, a button, or a keyboard shortcut). Each command
7
+ * knows how to do its work, and — if reversible — how to undo it.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@monbolc/lowcode-plugin-command",
3
+ "version": "2.0.0",
4
+ "description": "Command pattern + undo/redo history for SapuLowcodeEngine (L2)",
5
+ "license": "MIT",
6
+ "main": "lib/index.js",
7
+ "module": "es/index.js",
8
+ "types": "lib/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./lib/index.d.ts",
12
+ "import": "./es/index.js",
13
+ "require": "./lib/index.js"
14
+ }
15
+ },
16
+ "files": ["lib", "es"],
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.json",
19
+ "build:es": "tsc -p tsconfig.esm.json",
20
+ "clean": "rimraf lib es"
21
+ },
22
+ "dependencies": {
23
+ "@monbolc/lowcode-types": "^2.0.0",
24
+ "@monbolc/lowcode-utils": "^2.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "rimraf": "^5.0.5",
28
+ "typescript": "^5.4.5"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "registry": "https://registry.npmjs.org/"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git@github.com:monbolc/sapu-lowcode-engine.git",
37
+ "directory": "packages/plugin-command"
38
+ },
39
+ "keywords": ["lowcode", "engine", "command", "undo", "redo"]
40
+ }