mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
feat(plugins): add PluginContext, PluginRegistry, and SettingsStore
- PluginContext: scoped API with crash-safe command wrappers - PluginRegistry: lifecycle management with graceful init failure - SettingsStore: plugin-scoped key/value via IPC backend - Export hooks: pre/post hooks on registry for cross-plugin integration Amit Haridas
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
class PluginContext {
|
||||
/**
|
||||
* @param {object} deps - Injected dependencies
|
||||
* @param {string} deps.pluginId - Plugin unique ID (for namespacing)
|
||||
* @param {object} deps.sidebar - SidebarManager.registerPanel
|
||||
* @param {object} deps.commands - CommandPalette.register
|
||||
* @param {object} deps.statusBar - StatusBar.registerIndicator
|
||||
* @param {object} deps.eventBus - EventBus instance
|
||||
* @param {object} deps.settings - { get, set, onChanged }
|
||||
* @param {object} deps.editor - { getContent, getSelection, insertAtCursor, onContentChanged }
|
||||
* @param {object} deps.ipc - { invoke, on }
|
||||
* @param {object} deps.exportHooks - { preHooks: [], postHooks: [] }
|
||||
*/
|
||||
constructor(deps) {
|
||||
const { pluginId, sidebar, commands, statusBar, eventBus, settings, editor, ipc, exportHooks } = deps;
|
||||
|
||||
this.sidebar = {
|
||||
registerPanel: (id, opts) => sidebar.registerPanel(`${pluginId}:${id}`, opts)
|
||||
};
|
||||
|
||||
this.commands = {
|
||||
register: (id, label, handler, shortcut) => {
|
||||
const safeHandler = (...args) => {
|
||||
try {
|
||||
handler(...args);
|
||||
} catch (err) {
|
||||
console.error(`[Plugin:${pluginId}] Command "${id}" error:`, err);
|
||||
}
|
||||
};
|
||||
commands.register(`${pluginId}:${id}`, label, safeHandler, shortcut);
|
||||
}
|
||||
};
|
||||
|
||||
this.statusBar = {
|
||||
registerIndicator: (id, opts) => statusBar.registerIndicator(`${pluginId}:${id}`, opts)
|
||||
};
|
||||
|
||||
this.settings = {
|
||||
get: (key) => settings.get(`plugins.${pluginId}.${key}`),
|
||||
set: (key, value) => settings.set(`plugins.${pluginId}.${key}`, value),
|
||||
onChanged: (key, cb) => settings.onChanged(`plugins.${pluginId}.${key}`, cb)
|
||||
};
|
||||
|
||||
this.editor = {
|
||||
getContent: () => editor.getContent(),
|
||||
getSelection: () => editor.getSelection(),
|
||||
insertAtCursor: (text) => editor.insertAtCursor(text),
|
||||
onContentChanged: (cb) => editor.onContentChanged(cb)
|
||||
};
|
||||
|
||||
this.events = {
|
||||
on: (event, handler) => eventBus.on(event, handler),
|
||||
off: (event, handler) => eventBus.off(event, handler),
|
||||
emit: (event, payload) => eventBus.emit(event, payload),
|
||||
hasHandler: (event) => eventBus.hasHandler(event)
|
||||
};
|
||||
|
||||
this.ipc = {
|
||||
invoke: (channel, ...args) => ipc.invoke(channel, ...args),
|
||||
on: (channel, handler) => ipc.on(channel, handler)
|
||||
};
|
||||
|
||||
this.exports = {
|
||||
registerPreHook: (handler) => { if (exportHooks) exportHooks.preHooks.push(handler); },
|
||||
registerPostHook: (handler) => { if (exportHooks) exportHooks.postHooks.push(handler); }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { PluginContext };
|
||||
@@ -0,0 +1,80 @@
|
||||
const { PluginContext } = require('./plugin-context');
|
||||
const { PluginAPI } = require('./plugin-api');
|
||||
|
||||
class PluginRegistry {
|
||||
constructor(deps) {
|
||||
this.deps = deps;
|
||||
this.plugins = new Map();
|
||||
this.exportHooks = { preHooks: [], postHooks: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a discovered plugin. Creates instance, builds context, calls init.
|
||||
* If init throws, plugin is NOT registered.
|
||||
*/
|
||||
register(pluginInfo) {
|
||||
const { id, name, version, description, manifest, PluginClass, dir } = pluginInfo;
|
||||
|
||||
let instance;
|
||||
if (PluginClass) {
|
||||
instance = new PluginClass();
|
||||
} else {
|
||||
instance = new PluginAPI();
|
||||
}
|
||||
instance._manifest = manifest;
|
||||
|
||||
const context = new PluginContext({
|
||||
pluginId: id,
|
||||
sidebar: this.deps.sidebar,
|
||||
commands: this.deps.commands,
|
||||
statusBar: this.deps.statusBar,
|
||||
eventBus: this.deps.eventBus,
|
||||
settings: this.deps.settings,
|
||||
editor: this.deps.editor,
|
||||
ipc: this.deps.ipc,
|
||||
exportHooks: this.exportHooks
|
||||
});
|
||||
|
||||
try {
|
||||
instance.init(context);
|
||||
} catch (err) {
|
||||
console.error(`[PluginRegistry] Plugin "${id}" init failed:`, err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugins.set(id, { id, name, version, description, manifest, instance, dir, context });
|
||||
console.log(`[PluginRegistry] Registered plugin: ${name} v${version}`);
|
||||
}
|
||||
|
||||
getPlugin(id) {
|
||||
return this.plugins.get(id);
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return Array.from(this.plugins.values());
|
||||
}
|
||||
|
||||
activate(id) {
|
||||
const plugin = this.plugins.get(id);
|
||||
if (plugin?.instance) {
|
||||
try {
|
||||
plugin.instance.activate();
|
||||
} catch (err) {
|
||||
console.error(`[PluginRegistry] Plugin "${id}" activate error:`, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deactivate(id) {
|
||||
const plugin = this.plugins.get(id);
|
||||
if (plugin?.instance) {
|
||||
try {
|
||||
plugin.instance.deactivate();
|
||||
} catch (err) {
|
||||
console.error(`[PluginRegistry] Plugin "${id}" deactivate error:`, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { PluginRegistry };
|
||||
@@ -0,0 +1,23 @@
|
||||
class SettingsStore {
|
||||
/**
|
||||
* @param {object} backend - { get(key), set(key, value) } backed by main process store
|
||||
*/
|
||||
constructor(backend) {
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.backend.get(key);
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
this.backend.set(key, value);
|
||||
}
|
||||
|
||||
onChanged(key, callback) {
|
||||
// Deferred: plugins read settings on init/activate for MVP.
|
||||
// Full change notification requires IPC watcher in main process.
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { SettingsStore };
|
||||
Reference in New Issue
Block a user