feat(app): embed indexer in Electron with file-watching service and UI refinements
Extract schema DDL into scripts/schema.sql shared between CLI and app. Add an in-process chokidar-based indexer-service that watches ~/.claude/projects for JSONL changes, debounces, and triggers background rebuilds via a worker thread. Rename Usage view to Activity, flesh out MemoryDetail and SubagentDetail views, and refine App.vue layout/routing. The main process now starts/stops the indexer lifecycle and notifies renderer windows on index updates.
This commit is contained in:
+34
-1
@@ -3,18 +3,44 @@ const path = require('path');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const Database = require('better-sqlite3');
|
||||
const { writeHeartbeat } = require('./indexer');
|
||||
const { createIndexerService } = require('./indexer-service');
|
||||
const { createWorkerBuildIndex } = require('./indexer-worker-client');
|
||||
|
||||
const DB_PATH = path.join(os.homedir(), '.claude', 'obelisk.sqlite');
|
||||
|
||||
let db;
|
||||
let indexerService;
|
||||
let indexerWorker;
|
||||
|
||||
function openDb() {
|
||||
if (!fs.existsSync(DB_PATH)) return null;
|
||||
if (db) db.close();
|
||||
db = new Database(DB_PATH, { readonly: false });
|
||||
db.pragma('journal_mode = WAL');
|
||||
return db;
|
||||
}
|
||||
|
||||
function notifyIndexUpdated() {
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send('obelisk:index-updated');
|
||||
}
|
||||
}
|
||||
|
||||
function startIndexerService() {
|
||||
indexerService = createIndexerService({
|
||||
buildIndex: async ({ reason }) => {
|
||||
const result = await indexerWorker.buildIndex({ reason });
|
||||
openDb();
|
||||
notifyIndexUpdated();
|
||||
return result;
|
||||
},
|
||||
writeHeartbeat,
|
||||
});
|
||||
indexerService.start({ buildOnStart: false });
|
||||
return indexerService;
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
width: 1200,
|
||||
@@ -41,14 +67,21 @@ function createWindow() {
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
indexerWorker = createWorkerBuildIndex();
|
||||
openDb();
|
||||
createWindow();
|
||||
startIndexerService().runBuildNow('startup');
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||
});
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
if (indexerService) indexerService.stop();
|
||||
if (indexerWorker) indexerWorker.stop();
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (db) db.close();
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
@@ -137,7 +170,7 @@ ipcMain.handle('db:getSessionSummaries', (_, sessionId) => {
|
||||
ipcMain.handle('db:getMemories', () => {
|
||||
if (!db) return [];
|
||||
return db.prepare(`
|
||||
SELECT id, session_id, project, message_start, message_end, path, summary, created_at, deleted_at, deleted_reason
|
||||
SELECT id, session_id, project, message_start, message_end, path, anchors, summary, created_at, deleted_at, deleted_reason
|
||||
FROM memories ORDER BY created_at DESC
|
||||
`).all();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user