Search...Search plugins and themes...
⌘K
Sign in
  • Get started
  • Download
  • Pricing
  • Enterprise
  • Account
  • Obsidian
  • Overview
  • Sync
  • Publish
  • Canvas
  • Mobile
  • Web Clipper
  • CLI
  • Learn
  • Help
  • Developers
  • Changelog
  • About
  • Roadmap
  • Blog
  • Resources
  • System status
  • License overview
  • Terms of service
  • Privacy policy
  • Security
  • Community
  • Plugins
  • Themes
  • Discord
  • Forum / 中文论坛
  • Merch store
  • Brand guidelines
Follow us
DiscordTwitterBlueskyThreadsMastodonYouTubeGitHub
© 2026 Obsidian

More Events

Michael NaumovMichael Naumov11 downloads

Publishes named, typed workspace events the public API does not expose, so other plugins can react without patching internals. A growing set, starting with core and community plugins being enabled and disabled.

Add to Obsidian
More Events screenshot
More Events screenshot
More Events screenshot
More Events screenshot
  • Overview
  • Scorecard
  • Updates2

A home for the events Obsidian does not publish. Its public API exposes a fixed set of workspace events, and anything outside that set leaves a plugin reading undocumented internals or patching a shared prototype. More Events publishes the missing ones instead — named, typed and per subject, on app.workspace, subscribable with no dependency beyond obsidian itself. The first set is plugin lifecycle, which is what the rest of this page is about; further sets arrive the same way, under the same names-and-types contract, and that is what the plugin is for rather than an afterthought to it.

A plugin that reacts to another plugin — one of Obsidian's core plugins like Backlinks, Canvas or Graph, or a community plugin it integrates with — has to know when that plugin goes away and when it comes back. Obsidian does emit something: app.internalPlugins fires an untyped change when a core plugin's state moves, and app.plugins fires an untyped changed when a community plugin's does. Neither says which plugin moved, or in which direction — the community one carries no payload whatsoever — and both hang off internal objects with no published types. So the usual answer is to monkey-patch InternalPlugin.prototype.enable — and when two plugins in the same vault do that, there are two patches on one prototype.

This plugin does that patching once, for everybody. It watches the signals Obsidian already sends, works out what actually changed, recovers the one thing those signals drop — whether Obsidian was told the user did it — and re-publishes the lot on app.workspace as four named, typed, per-plugin events any plugin can listen for with no dependency beyond obsidian itself. One patch on the shared prototype instead of one per interested plugin is the whole point: you do not patch.

It has no user interface. Nothing to configure, nothing to click: install it, and the events are there for the plugins that need them. So the screenshots below are of another plugin reacting — a listener raising a notice as a plugin is toggled in Settings, which is the only thing here there is to see.

Canvas enabled in Settings — a listening plugin is told

More screenshots
Any community plugin too, named the moment it loads Canvas enabled in Settings — a listening plugin is told Any community plugin too, named the moment it loads

Demo vault

The documentation is a demo vault. Its notes explain what the events are and when they fire, with buttons that subscribe to them live so you can toggle a plugin in Settings and watch them arrive.

Start reading here — it is plain markdown, so it works on GitHub with nothing installed.

A copy of the vault ships with every release. You can access it via any of the following:

  1. Running the More Events: Open demo vault command.
  2. Downloading more-events-demo-vault.zip from the Releases. It unzips into a single more-events-demo-vault-<version> folder.
  3. Browsing its source in demo-vault/ in this repository.

What it does

Today that is the plugin-lifecycle set — four events, and a small API answering the question events cannot answer. It is the first set rather than the whole of it.

  • more-events:core-plugin-enabled fires on app.workspace after a core plugin has been enabled, carrying that plugin's id and display name. 01 Core plugin events
  • more-events:core-plugin-disabled fires the same way after one has been disabled. 01 Core plugin events
  • more-events:community-plugin-enabled and more-events:community-plugin-disabled are the same pair for community plugins. 02 Community plugin events
  • A small API answers the question the events cannot: which plugins are enabled right now, which is what a listener needs when it first loads. 03 For plugin developers
  • Every payload says whether the user did it, as isUserInitiated. Obsidian takes that answer as an argument, uses it to decide whether the plugin's own onUserEnable hook runs, and then drops it before it raises the signal these events are built on. More Events keeps it.

Every event is per plugin — one event per plugin that actually changed, so a listener never has to diff anything — and past tense: by the time yours runs, the plugin has already been enabled or disabled.

For community plugins, enabled means loaded: the plugin's code is running. That is deliberately not the same as it being ticked in Settings -> Community plugins, which Obsidian tracks separately and which stays untouched when the master Community plugins switch unloads every one of them.

isUserInitiated is Obsidian's own notion of the user doing it, not a broader one: it means that plugin's own toggle was flipped. Turning the master Community plugins switch off is a person's doing and reports false, because Obsidian passes no flag down that path — and so does starting a vault in restricted mode. It is also a claim rather than a proof: a plugin that calls enable(true) itself reports true, because that is what Obsidian itself believes.

Why it patches, when the point was to stop you patching

It patches exactly one thing, and that is not a contradiction of the paragraph above — it is what makes it true. The hazard is never a patch; it is N plugins patching one shared prototype, at different versions, each unaware of the others. A plugin is where such a patch belongs, precisely because a vault holds one copy of it. So the prototype carries one patch, installed once, removed cleanly when the plugin unloads, and every consumer gets the context without adding a second.

The patch supplements the diff rather than replacing it. What it intercepts is the transition; what makes the events per-plugin — and correct when Obsidian collapses several transitions into one debounced signal — is still the diff.

What it deliberately does not do

  • It does not fire for the plugins that were already enabled when it loaded. Nothing changed, so nothing is announced; read the starting state from the API instead.
  • It does not replace obsidian-dev-utils' broadcast, and it overlaps with it. That library broadcasts obsidian-dev-utils:plugin-loaded / -unloaded, but only for plugins built on it. The community-plugin events here cover every plugin in the vault, including the ones that have never heard of that library — which is the gap they exist to fill. A plugin built on it raises both, so subscribe to one and not to both.

For plugin developers

The events and the API are this plugin's whole point, and both are declared in api.d.ts — hand-written, and importing nothing but obsidian, so you can depend on it without depending on anything of mine.

The events need no API handle at all. Copy the declarations from api.d.ts into your own project, or hardcode the names, and subscribe:

this.registerEvent(
  this.app.workspace.on('more-events:core-plugin-enabled', ({ corePluginId, corePluginName, isUserInitiated }) => {
    if (isUserInitiated) {
      console.log(`${corePluginName} (${corePluginId}) is back, because someone turned it back on`);
    }
  })
);

this.registerEvent(
  this.app.workspace.on('more-events:community-plugin-disabled', ({ communityPluginId, communityPluginName }) => {
    console.log(`${communityPluginName} (${communityPluginId}) has gone`);
  })
);

For the current state, and to know whether More Events is installed at all, take the API through the registry. The contract is 1.2.0 and moves independently of the plugin's own version, so ask for a range:

import { watchPluginApi } from 'obsidian-dev-utils/obsidian/plugin/plugin-api';

const moreEventsApiRef = watchPluginApi<MoreEventsApi>({
  apiVersionRange: '^1',
  app: this.app,
  component: this,
  pluginId: 'more-events'
});

// `value` is always current and never stale: it is `null` while More Events is not loaded, and becomes
// non-`null` on its own when it is.
const moreEventsApi = moreEventsApiRef.value;
if (moreEventsApi) {
  console.log(moreEventsApi.getEnabledCorePluginIds());
  console.log(moreEventsApi.isCorePluginEnabled('backlink'));
  console.log(moreEventsApi.getEnabledCommunityPluginIds());
  console.log(moreEventsApi.isCommunityPluginEnabled('dataview'));
}

If you would rather not depend on obsidian-dev-utils for that, the registry is a documented wire protocol you can read directly — see Cross-plugin APIs.

Installation

The plugin is available in the official Community Plugins repository.

Beta versions

To install the latest beta release of this plugin (regardless if it is available in the official Community Plugins repository or not), follow these steps:

  1. Ensure you have the BRAT plugin installed and enabled.
  2. Click Install via BRAT.
  3. An Obsidian pop-up window should appear. In the window, click the Add plugin button once and wait a few seconds for the plugin to install.

Debugging

By default, debug messages for this plugin are hidden.

To show them, run the following command in the DevTools Console:

window.DEBUG.enable('more-events');

For more details, refer to the documentation.

Changelog

All notable changes to this project will be documented in the CHANGELOG.

Contributing

Contributions are welcome — see CONTRIBUTING to get set up.

Support

My other Obsidian resources

See my other Obsidian resources.

License

© Michael Naumov

HealthExcellent
ReviewSatisfactory
About
Expose missing workspace events on app.workspace as named, typed, per-plugin events so other plugins can subscribe without monkey-patching shared prototypes. Detect plugin lifecycle changes, recover whether the change was user-initiated, and re-publish four per-plugin lifecycle events for reliable inter-plugin coordination; no UI or config.
WorkspacesDevelopers
Details
Current version
1.0.1
Last updated
5 days ago
Created
2 weeks ago
Updates
2 releases
Downloads
11
Compatible with
Obsidian 1.13.7+
Platforms
Desktop, Mobile
License
MIT
Report bugRequest featureReport plugin
Sponsor
Buy Me a Coffee
Author
Michael NaumovMichael Naumovmnaoumov
mnaoumov.dev
GitHubmnaoumov
Xmnaoumov
  1. Community
  2. Plugins
  3. Workspaces
  4. More Events

Related plugins

BRAT

Easily install a beta version of a plugin for testing.

Homepage

Open a note, base, or workspace on startup, or set it for quick access later.

Claudian

Embeds Claude Code/Codex and other local Agents as AI collaborators in your vault.

Local REST API with MCP

Unlock your automation needs by interacting with your notes over a secure REST API.

Git

Integrate Git version control with automatic backup and other advanced features.

Hot Reload

Automatically reload in-development plugins when their files are changed

Vertical Tabs

Offer an alternative view that displays open tabs vertically, allowing users to group and organize tabs for a better navigation experience.

Terminal

Integrate consoles, shells, and terminals.

Open Tab Settings

Adds options to customize how tabs are opened, including open in new tab by default, preventing duplicate tabs, and more.

Sync Engine

The extensible vault synchronization engine: Fast · Free · Reliable. Supports WebDAV, S3, and Google Drive.