hh 70cf985aa1 refactor(inset): the bottom-inset arithmetic becomes testable, and is tested
The measurement lived inside the view, where the only way to check it was to
run Obsidian on a phone. It moves to a pure `insetFor(leaf, bars, visible)`
against the numbers a phone reports - including the case the events hid: while
the keyboard is up the navbar is behind it, so the deeper of the two is the
inset and adding them would push the composer twice as far as it must go.
2026-09-05 00:49:54 +02:00
2026-09-02 04:35:13 +02:00

Beaver - Obsidian plugin

AI Slop Inside

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.

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 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 - messages scope for sending, api scope 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, 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.

S
Description
Obsidian plugin for Beaver's markdown frontend
Readme
465 KiB
Languages
TypeScript 66.9%
JavaScript 16.7%
CSS 12.2%
Svelte 2.2%
HTML 1.4%
Other 0.6%