# Beaver - Obsidian plugin [![AI Slop Inside](https://sladge.net/badge.svg)](https://sladge.net) 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_id` unless 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. ![The panel as a tab: the conversation rail on the left, the master thread with its tool calls on the right](docs/panel-tab.webp) ![The panel as a column in the right sidedock, next to the note](docs/panel-column.webp) ## 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_id` in 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** - `messages` scope for sending, `api` scope 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/# 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 `/.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.