1
0
silverbullet/packages/web/hooks/command.ts

99 lines
2.3 KiB
TypeScript
Raw Normal View History

2022-04-25 08:33:38 +00:00
import { Hook, Manifest } from "@plugos/plugos/types";
import { System } from "@plugos/plugos/system";
import { EventEmitter } from "@plugos/plugos/event";
2022-06-17 18:17:22 +00:00
import { ShortcutItem } from "../types";
export type CommandDef = {
name: string;
contexts?: string[];
// Bind to keyboard shortcut
key?: string;
mac?: string;
2022-05-06 16:55:04 +00:00
2022-06-17 18:17:22 +00:00
// Shortcuts in UI
shortcut?: ShortcutDef;
2022-05-06 16:55:04 +00:00
};
2022-06-17 18:17:22 +00:00
export type ShortcutDef = {
2022-05-06 16:55:04 +00:00
label: string;
};
export type AppCommand = {
command: CommandDef;
run: () => Promise<void>;
};
export type CommandHookT = {
command?: CommandDef;
};
export type CommandHookEvents = {
2022-05-06 16:55:04 +00:00
commandsUpdated(
commandMap: Map<string, AppCommand>,
2022-06-17 18:17:22 +00:00
appButtons: ShortcutItem[]
2022-05-06 16:55:04 +00:00
): void;
};
export class CommandHook
extends EventEmitter<CommandHookEvents>
implements Hook<CommandHookT>
{
editorCommands = new Map<string, AppCommand>();
2022-06-17 18:17:22 +00:00
shortcutItems: ShortcutItem[] = [];
buildAllCommands(system: System<CommandHookT>) {
this.editorCommands.clear();
2022-06-17 18:17:22 +00:00
this.shortcutItems = [];
for (let plug of system.loadedPlugs.values()) {
for (const [name, functionDef] of Object.entries(
plug.manifest!.functions
)) {
if (!functionDef.command) {
continue;
}
const cmd = functionDef.command;
this.editorCommands.set(cmd.name, {
command: cmd,
run: () => {
return plug.invoke(name, []);
},
});
2022-06-17 18:17:22 +00:00
if (cmd.shortcut) {
this.shortcutItems.push({
label: cmd.shortcut.label,
2022-05-06 16:55:04 +00:00
run: () => {
return plug.invoke(name, []);
},
});
}
}
}
2022-06-17 18:17:22 +00:00
this.emit("commandsUpdated", this.editorCommands, this.shortcutItems);
}
apply(system: System<CommandHookT>): void {
this.buildAllCommands(system);
system.on({
plugLoaded: () => {
this.buildAllCommands(system);
},
});
}
validateManifest(manifest: Manifest<CommandHookT>): string[] {
let errors = [];
for (const [name, functionDef] of Object.entries(manifest.functions)) {
if (!functionDef.command) {
continue;
}
const cmd = functionDef.command;
if (!cmd.name) {
errors.push(`Function ${name} has a command but no name`);
}
}
return [];
}
}