import { App, Notice, PluginSettingTab, Setting } from "obsidian"; import { BeaverApiError, apiBase, listAgents, listApiAgents } from "./api"; import type BeaverPlugin from "./main"; export interface BeaverSettings { baseUrl: string; apiBaseUrl: string; token: string; vaultSubpath: string; } export const DEFAULT_SETTINGS: BeaverSettings = { baseUrl: "http://localhost:62993", apiBaseUrl: "", token: "", vaultSubpath: "", }; export function normalizeSubpath(raw: string): string { return raw.trim().replace(/^\/+|\/+$/g, ""); } function errorText(err: unknown): string { return err instanceof BeaverApiError ? `${err.status}: ${err.message}` : err instanceof Error ? err.message : String(err); } export class BeaverSettingsTab extends PluginSettingTab { plugin: BeaverPlugin; constructor(app: App, plugin: BeaverPlugin) { super(app, plugin); this.plugin = plugin; } display(): void { const { containerEl } = this; containerEl.empty(); new Setting(containerEl) .setName("Base URL") .setDesc("Markdown frontend root, e.g. http://localhost:62993") .addText((text) => text .setPlaceholder("http://localhost:62993") .setValue(this.plugin.settings.baseUrl) .onChange(async (value) => { this.plugin.settings.baseUrl = value.trim().replace(/\/+$/, ""); await this.plugin.saveSettings(); }), ); new Setting(containerEl) .setName("API origin") .setDesc( "Origin of the conversations API; `/api/…` is appended to it. " + "Leave empty to derive from Base URL (`…/md` → `…`, port 62993 → 62994). " + "Example: https://beaver.example.com or http://localhost:62994.", ) .addText((text) => text .setPlaceholder("(derived)") .setValue(this.plugin.settings.apiBaseUrl) .onChange(async (value) => { this.plugin.settings.apiBaseUrl = value.trim().replace(/\/+$/, ""); await this.plugin.saveSettings(); }), ); new Setting(containerEl) .setName("Bearer token") .setDesc( "Token with the `messages` scope for sending and the `api` scope " + "for the activity panel (or a `*` bootstrap token).", ) .addText((text) => { text.inputEl.type = "password"; text .setPlaceholder("paste token") .setValue(this.plugin.settings.token) .onChange(async (value) => { this.plugin.settings.token = value; await this.plugin.saveSettings(); }); }); new Setting(containerEl) .setName("Vault subpath") .setDesc( "Folder in this Obsidian vault that maps to the gateway's vault root. " + "Leave empty if the two vault roots match. Example: `💬 чаты`.", ) .addText((text) => text .setPlaceholder("") .setValue(this.plugin.settings.vaultSubpath) .onChange(async (value) => { this.plugin.settings.vaultSubpath = normalizeSubpath(value); await this.plugin.saveSettings(); }), ); new Setting(containerEl) .setName("Test connection") .setDesc("Calls GET /agents on the markdown frontend and reports the count.") .addButton((btn) => btn.setButtonText("Test").onClick(async () => { try { const agents = await listAgents(this.plugin.settings); this.plugin.cacheAgents(agents); new Notice(`Beaver: found ${agents.length} agents`); } catch (err) { new Notice(`Beaver: ${errorText(err)}`, 8000); } }), ); new Setting(containerEl) .setName("Test API") .setDesc("Calls GET /api/agents on the API origin (needs the `api` scope).") .addButton((btn) => btn.setButtonText("Test").onClick(async () => { try { const agents = await listApiAgents(this.plugin.settings); new Notice( `Beaver: API at ${apiBase(this.plugin.settings)} - ${agents.length} agents`, ); } catch (err) { new Notice(`Beaver: ${errorText(err)}`, 8000); } }), ); } }