Beaver - Obsidian plugin
Two things:
- The panel: Beaver's conversations next to your notes - the master thread, branches, deep chats - with the live tree of tool calls and subagents, the composer, and the same actions the admin has (branch with a seed, return a branch to Telegram or hide it, the memory flag, close with a digest). A narrow sidedock column, a full tab, or the phone screen. It follows the active note's
conversation_idunless you pin it. - Send the current note to Beaver's markdown frontend (
POST /chat/stream) and stream the agent's reply back into the buffer.
Commands
- Beaver: Open panel in a tab - the wide layout with the conversation rail on the left (also the ribbon icon).
- Beaver: Open panel - the narrow column in the right sidedock; a tab and a column can be open at once, each command reuses its own.
- Beaver: Open chat in panel - the active note has
conversation_idin its frontmatter; the panel shows that conversation. - Beaver: Send using selected agent / Send using different agent - the note has
agent:in its frontmatter.
The panel
The UI is beaver-gateway/ui/src/lib/panel, compiled into this plugin by esbuild.config.mjs ($lib/* points at the gateway's ui/src/lib; every bare import resolves from this plugin's node_modules, so there is one Svelte runtime; svelte-sonner becomes Obsidian notices). The same components render the browser admin and /admin/panel.
Theme: src/tailwind.css maps the panel's tokens onto Obsidian's variables on .beaver-root, exactly as beaver-calendar does with .bcal-root - the app's font, accent, radii and the current theme, light or dark. Menus and dialogs portal to a .beaver-root.beaver-portal layer on body, above the sidedocks.
Markdown in the thread goes through Obsidian's MarkdownRenderer: [[links]] open the note, external links open the browser. A deep chat's row offers Open note (its markdown binding, under the chats folder).
Layout: below 48rem of panel width the conversation switcher sits in the header (kind, title, live dot, follow toggle); wider than that the grouped rail (master, branches, deep chats, jobs) sits on the left. Right-click a row for its actions; on touch the … button is always visible. ↑/↓ walk the rows, / jumps to the search, ⌘↩ sends.
Settings
- Base URL - markdown frontend root, e.g.
https://host/md. - API origin - where
/api/…lives; empty derives it from the base URL (…/md→…). - Bearer token -
messagesscope for sending,apiscope for the panel (or a*bootstrap token). - Chats folder - the folder in this vault where the deep chats live, the one the gateway's markdown frontend writes to. Paths the gateway hands out are relative to it.
Changing settings remounts open panels.
Build
bun install
bun run dev # css + esbuild watch
bun run build # svelte-check + css (minified, scoped) + bundle
bun run test # vitest (theme rules) + tests/smoke.mjs (jsdom, fake gateway)
bun run preview # the panel in a browser, no Obsidian: http://localhost:4174/#<scene>
make check # ultracite + svelte-check
make install VAULT=~/Obsidian/my-vault
make zip
Preview scenes (#thread default, #switcher, #closed, #menu, #actions, #activity, #branch, #question, #deep, #empty, #offline, #stream; suffix -light for the light theme). Width is the window: run headless Chrome with --window-size=340,720 for the sidedock, 1100,720 for a tab, 390,800 for a phone. Set VAULT= or THEME= to preview against a community theme; app.css is read from the Obsidian install.
styles.css is Tailwind output run through scripts/scope-css.mjs: every selector gets :is(.beaver-root, .beaver-root *) in front. Other Tailwind plugins (beaver-calendar for one) put their utilities into the same utilities cascade layer, and without the extra specificity whichever plugin loaded last would win - its .hidden over ours and the other way round.
Install
make install VAULT=… copies manifest.json, main.js, styles.css, versions.json into <vault>/.obsidian/plugins/beaver; then enable Beaver in Settings → Community plugins. On a phone, sync the four files with Obsidian Sync ("Installed community plugins") or any file sync, and point the settings at a URL the phone can reach.
Version
Edit manifest.json, package.json and versions.json by hand.

