diff --git a/apps/obsidian-connector/src/main.ts b/apps/obsidian-connector/src/main.ts index af60c461..65a110d0 100644 --- a/apps/obsidian-connector/src/main.ts +++ b/apps/obsidian-connector/src/main.ts @@ -16,14 +16,29 @@ const DEFAULT_SETTINGS: SupermemorySettings = { includedFolders: "", } +function formatTimeAgo(ms: number): string { + const seconds = Math.floor((Date.now() - ms) / 1000) + if (seconds < 60) return "just now" + const minutes = Math.floor(seconds / 60) + if (minutes < 60) return `${minutes}m ago` + const hours = Math.floor(minutes / 60) + return `${hours}h ago` +} + export default class SupermemoryPlugin extends Plugin { settings: SupermemorySettings = DEFAULT_SETTINGS private syncEngine: SyncEngine | null = null + private statusBarEl: HTMLElement | null = null + private lastSyncTime: number | null = null + private lastSyncCount = 0 async onload() { await this.loadSettings() configure(this.settings) + this.statusBarEl = this.addStatusBarItem() + this.updateStatusBar() + this.addSettingTab(new SupermemorySettingTab(this.app, this)) this.addRibbonIcon("cloud-upload", "Sync vault to Supermemory", () => { @@ -45,6 +60,27 @@ export default class SupermemoryPlugin extends Plugin { } } + private updateStatusBar(syncing?: { current: number; total: number }) { + if (!this.statusBarEl) return + + if (syncing) { + this.statusBarEl.setText( + `Supermemory: syncing ${syncing.current}/${syncing.total}...`, + ) + return + } + + if (this.lastSyncTime) { + this.statusBarEl.setText( + `Supermemory: synced ${formatTimeAgo(this.lastSyncTime)} ยท ${this.lastSyncCount} notes`, + ) + } else if (!this.settings.apiKey) { + this.statusBarEl.setText("Supermemory: no API key") + } else { + this.statusBarEl.setText("Supermemory: ready") + } + } + private registerVaultEvents() { this.registerEvent( this.app.vault.on("modify", (file: TAbstractFile) => { @@ -108,7 +144,16 @@ export default class SupermemoryPlugin extends Plugin { this.syncEngine.updateSettings(this.settings) } - await this.syncEngine.fullSync() + const files = this.app.vault.getMarkdownFiles() + this.updateStatusBar({ current: 0, total: files.length }) + + const result = await this.syncEngine.fullSync((current, total) => { + this.updateStatusBar({ current, total }) + }) + + this.lastSyncTime = Date.now() + this.lastSyncCount = result.queued + this.updateStatusBar() } async loadSettings() { @@ -123,5 +168,6 @@ export default class SupermemoryPlugin extends Plugin { await this.saveData(this.settings) configure(this.settings) this.syncEngine?.updateSettings(this.settings) + this.updateStatusBar() } } diff --git a/apps/obsidian-connector/src/sync.ts b/apps/obsidian-connector/src/sync.ts index dca4e0a1..d45089bc 100644 --- a/apps/obsidian-connector/src/sync.ts +++ b/apps/obsidian-connector/src/sync.ts @@ -50,7 +50,9 @@ export class SyncEngine { return false } - async fullSync(): Promise<{ queued: number; failed: number }> { + async fullSync( + onProgress?: (current: number, total: number) => void, + ): Promise<{ queued: number; failed: number }> { if (this.state.syncing) { new Notice("Supermemory: sync already in progress.") return { queued: 0, failed: 0 } @@ -65,6 +67,7 @@ export class SyncEngine { .getMarkdownFiles() .filter((f) => this.shouldSync(f)) const batches = this.chunk(files, BATCH_SIZE) + let processed = 0 for (const batch of batches) { const notes = await Promise.all(batch.map((f) => this.fileToPayload(f))) @@ -77,6 +80,9 @@ export class SyncEngine { } catch { totalFailed += valid.length } + + processed += batch.length + onProgress?.(processed, files.length) } this.state.lastFullSync = Date.now()