1
0
silverbullet/web/hooks/command.ts

78 lines
1.8 KiB
TypeScript

import { Hook, Manifest } from "../../plugos/types.ts";
import { System } from "../../plugos/system.ts";
import { EventEmitter } from "../../plugos/event.ts";
export type CommandDef = {
name: string;
contexts?: string[];
// Bind to keyboard shortcut
key?: string;
mac?: string;
};
export type AppCommand = {
command: CommandDef;
run: () => Promise<void>;
};
export type CommandHookT = {
command?: CommandDef;
};
export type CommandHookEvents = {
commandsUpdated(commandMap: Map<string, AppCommand>): void;
};
export class CommandHook extends EventEmitter<CommandHookEvents>
implements Hook<CommandHookT> {
editorCommands = new Map<string, AppCommand>();
buildAllCommands(system: System<CommandHookT>) {
this.editorCommands.clear();
for (const 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, [cmd]);
},
});
}
}
this.emit("commandsUpdated", this.editorCommands);
}
apply(system: System<CommandHookT>): void {
this.buildAllCommands(system);
system.on({
plugLoaded: () => {
this.buildAllCommands(system);
},
});
}
validateManifest(manifest: Manifest<CommandHookT>): string[] {
const 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 [];
}
}