hh cc46abc34f fix(view,panel): the composer clears the navbar and the keyboard, panel opens on the master
The inset only knew the desktop status bar, so on a phone the composer sat
under the navbar - which paints over a leaf instead of shortening it - and
the software keyboard hid it outright. Every bottom bar is measured now, and
the keyboard is read off `visualViewport`: it shrinks the visible area
without resizing the layout, so no workspace event fires and the panel had
no way to know. Focus re-measures once iOS has finished animating the keys in.

The panel asks the shell to fall back to the latest master, so opening it on
a phone lands in the day's thread rather than on a prompt.

The preview fixtures anchor at midday instead of the wall clock: spread over
hours, a run just after midnight pushed half of them into yesterday and
emptied the "Today" group the switcher smoke checks.
2026-09-05 00:20:33 +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%