# Event bus globnotes uses a lightweight, mitt-based client-side event bus so components can react to facts without importing each other. Publishers announce what happened; consumers decide what to do. ## Architecture ``` client/bus/index.js — mitt wrapper (subscribe / publish) client/bus/topics.js — topic constants + payload docs ``` `subscribe(topic, handler)` returns an `unsubscribe` function. `publish(topic, payload)` delivers the payload to every subscriber on that topic. Unknown topics are silently ignored (mitt has no schema enforcement — the topics.js file IS the contract). ## Conventions - **Present tense** — topic strings read as "on X": `note:open`, `theme:change`, `plugin:toggle`. - **Payload shape is documented in topics.js** — no schema library; the constant comment block is the spec. - **Publish at the module-level choke point** (state-transition function), not the DOM handler — so every code path fires the event regardless of how the action was triggered (click, keyboard, URL fragment, API result). - **One publish per fact** — if three code paths enter edit mode, they all funnel through `setEditMode()`, which publishes once. ## Topic registry | # | Topic | Payload | Published from | |---|---|---|---| | 1 | `note:open` | `{ path }` | `router.js` afterEach | | 2 | `note:create` | `{ path }` | `Note.vue` saveNew / saveNote | | 3 | `note:save` | `{ path }` | `Note.vue` saveExisting / saveNote (content-only) | | 4 | `note:rename` | `{ oldPath, newPath }` | `Note.vue` saveExisting / saveNote (path changed) | | 5 | `note:delete` | `{ path }` | `Note.vue` deleteConfirmedHandler | | 6 | `note:refs-rewritten` | `{ oldPath, newPath }` | `SearchResults.vue` fixAllRefs / fixSingleRef | | 7 | `file:upload` | `{ name }` | `Note.vue` postAttachment (success) | | 8 | `sidepanel:open` | `{}` | `SidebarPanel.vue` openSidebar | | 9 | `sidepanel:close` | `{}` | `SidebarPanel.vue` toggleSidebar | | 10 | `sidepanel:section-show` | `{ section }` | `SidebarPanel.vue` toggleRecent (show) | | 11 | `sidepanel:section-hide` | `{ section }` | `SidebarPanel.vue` toggleRecent (hide) | | 12 | `settings-menu:open` | `{}` | `NavBar.vue` PrimeMenu show | | 13 | `settings-menu:close` | `{}` | `NavBar.vue` PrimeMenu hide | | 14 | `search-menu:open` | `{}` | `SearchResults.vue` sort-menu show | | 15 | `search-menu:close` | `{}` | `SearchResults.vue` sort-menu hide | | 16 | `modal:open` | `{ name }` | `Modal.vue` visibility watch | | 17 | `modal:close` | `{ name }` | `Modal.vue` visibility watch | | 18 | `theme:change` | `{ id, resolvedId, mode }` | `themes.js` applyTheme | | 19 | `plugin:toggle` | `{ id, enabled }` | `pluginSettings.js` saveSwitch | | 20 | `plugin:auto-enable` | `{ enabled }` | `pluginSettings.js` saveAutoEnable | | 21 | `settings:line-numbers` | `{ value }` | `pluginSettings.js` saveViewLineNumbers | | 22 | `debug:change` | `{ enabled }` | `debug.js` toggleDebug | | 23 | `search:perform` | `{ term }` | `router.js` afterEach | | 24 | `search:change` | `{ term }` | `SearchInput.vue` searchTerm watch | | 25 | `search:include-nested` | `{ value }` | `SearchResults.vue` toggleNested | | 26 | `editor:mode-change` | `{ mode }` | `Note.vue` setEditorMode | | 27 | `note:edit-start` | `{ path }` | `Note.vue` setEditMode | | 28 | `note:edit-end` | `{ path }` | `Note.vue` exitEditState | ## Built-in consumers | Consumer | Subscribes to | Action | |---|---|---| | `noteIndex.js` | `note:create`, `note:rename`, `note:delete` | Calls `refreshNoteIndex()` — keeps the sidebar filter and wiki-link resolver fresh without a page reload. | | `ServerViewer.vue` | `plugin:toggle`, `plugin:auto-enable` | Re-renders the open note so plugin enable/disable changes take effect immediately. | ## Subscribing from a component ```js import { subscribe, TOPICS } from "../bus/index.js"; // In