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 vault subpath).
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). - Vault subpath - the folder in this vault that maps to the gateway's vault root.
Changing settings remounts open panels.
Build
bun install
bun run dev # css + esbuild watch
bun run build # svelte-check + css (minified) + 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.
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.