feat(electrobun): final 5% — full integration, real data, polish

1. Claude CLI: additionalDirectories + worktreeName passthrough
2. Agent-store: reads settings (default_cwd, provider model, permission)
3. Project hydration: SQLite replaces hardcoded PROJECTS, add/remove UI
4. Group hydration: SQLite groups, add/delete in sidebar
5. Terminal auto-spawn: reads default_cwd from settings
6. Context tab: real tokens from agent-store, file refs, turn count
7. Memory tab: Memora DB integration (read-only, graceful if missing)
8. Docs tab: markdown viewer (files.list + files.read + inline renderer)
9. SSH tab: CRUD connections, spawn PTY with ssh command
10. Error handling: global unhandledrejection → toast notifications
11. Notifications: agent done/error/stall → toasts, 15min stall timer
12. Command palette: all 18 commands (was 10)

+1,198 lines, 13 files. Electrobun now 100% feature-complete vs Tauri v3.
This commit is contained in:
Hibryda 2026-03-22 02:02:54 +01:00
parent 4826b9dffa
commit 8e756d3523
13 changed files with 1199 additions and 239 deletions

View file

@ -8,6 +8,7 @@ import { btmsgDb } from "./btmsg-db.ts";
import { bttaskDb } from "./bttask-db.ts";
import { SidecarManager } from "./sidecar-manager.ts";
import type { PtyRPCSchema } from "../shared/pty-rpc-schema.ts";
import { Database } from "bun:sqlite";
import { randomUUID } from "crypto";
import { SearchDb } from "./search-db.ts";
import { checkForUpdates, getLastCheckTimestamp } from "./updater.ts";
@ -188,6 +189,16 @@ const rpc = BrowserView.defineRPC<PtyRPCSchema>({
}
},
"settings.deleteProject": ({ id }) => {
try {
settingsDb.deleteProject(id);
return { ok: true };
} catch (err) {
console.error("[settings.deleteProject]", err);
return { ok: false };
}
},
// ── Custom Themes handlers ───────────────────────────────────────────
"themes.getCustom": () => {
@ -303,6 +314,26 @@ const rpc = BrowserView.defineRPC<PtyRPCSchema>({
}
},
"groups.create": ({ id, name, icon, position }) => {
try {
settingsDb.createGroup(id, name, icon, position);
return { ok: true };
} catch (err) {
console.error("[groups.create]", err);
return { ok: false };
}
},
"groups.delete": ({ id }) => {
try {
settingsDb.deleteGroup(id);
return { ok: true };
} catch (err) {
console.error("[groups.delete]", err);
return { ok: false };
}
},
// ── Project clone handler ────────────────────────────────────────────
"project.clone": async ({ projectId, branchName }) => {
@ -456,7 +487,7 @@ const rpc = BrowserView.defineRPC<PtyRPCSchema>({
// ── Agent handlers ──────────────────────────────────────────────────
"agent.start": ({ sessionId, provider, prompt, cwd, model, systemPrompt, maxTurns, permissionMode, claudeConfigDir, extraEnv }) => {
"agent.start": ({ sessionId, provider, prompt, cwd, model, systemPrompt, maxTurns, permissionMode, claudeConfigDir, extraEnv, additionalDirectories, worktreeName }) => {
try {
const result = sidecarManager.startSession(sessionId, provider, prompt, {
cwd,
@ -466,6 +497,8 @@ const rpc = BrowserView.defineRPC<PtyRPCSchema>({
permissionMode,
claudeConfigDir,
extraEnv,
additionalDirectories,
worktreeName,
});
if (result.ok) {
@ -970,6 +1003,58 @@ const rpc = BrowserView.defineRPC<PtyRPCSchema>({
}
},
// ── Memora handlers (read-only) ─────────────────────────────────────
"memora.search": ({ query, limit }) => {
try {
const dbPath = join(homedir(), ".local", "share", "memora", "memories.db");
if (!fs.existsSync(dbPath)) return { memories: [] };
const db = new Database(dbPath, { readonly: true });
try {
const rows = db
.query("SELECT id, content, tags, metadata, created_at as createdAt, updated_at as updatedAt FROM memories WHERE content LIKE ? ORDER BY updated_at DESC LIMIT ?")
.all(`%${query}%`, limit ?? 20) as Array<{
id: number; content: string; tags: string;
metadata: string; createdAt: string; updatedAt: string;
}>;
return { memories: rows };
} finally {
db.close();
}
} catch (err) {
console.error("[memora.search]", err);
return { memories: [] };
}
},
"memora.list": ({ limit, tag }) => {
try {
const dbPath = join(homedir(), ".local", "share", "memora", "memories.db");
if (!fs.existsSync(dbPath)) return { memories: [] };
const db = new Database(dbPath, { readonly: true });
try {
let sql = "SELECT id, content, tags, metadata, created_at as createdAt, updated_at as updatedAt FROM memories";
const params: unknown[] = [];
if (tag) {
sql += " WHERE tags LIKE ?";
params.push(`%${tag}%`);
}
sql += " ORDER BY updated_at DESC LIMIT ?";
params.push(limit ?? 20);
const rows = db.query(sql).all(...params) as Array<{
id: number; content: string; tags: string;
metadata: string; createdAt: string; updatedAt: string;
}>;
return { memories: rows };
} finally {
db.close();
}
} catch (err) {
console.error("[memora.list]", err);
return { memories: [] };
}
},
// ── Telemetry handler ────────────────────────────────────────────────
"telemetry.log": ({ level, message, attributes }) => {