diff --git a/src/main/clipboard-manager.ts b/src/main/clipboard-manager.ts index afae14a..cc63431 100644 --- a/src/main/clipboard-manager.ts +++ b/src/main/clipboard-manager.ts @@ -42,23 +42,27 @@ let lastClipboardText = ''; let lastClipboardImage: Buffer | null = null; let pollInterval: NodeJS.Timeout | null = null; let isEnabled = true; +let pendingSave: NodeJS.Timeout | null = null; // ─── Paths ────────────────────────────────────────────────────────── function getClipboardDir(): string { - const dir = path.join(app.getPath('userData'), 'clipboard-history'); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - return dir; + return path.join(app.getPath('userData'), 'clipboard-history'); } function getImagesDir(): string { - const dir = path.join(getClipboardDir(), 'images'); + return path.join(getClipboardDir(), 'images'); +} + +function ensureClipboardDirs(): void { + const dir = getClipboardDir(); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } - return dir; + const imagesDir = getImagesDir(); + if (!fs.existsSync(imagesDir)) { + fs.mkdirSync(imagesDir, { recursive: true }); + } } function getHistoryFilePath(): string { @@ -105,12 +109,15 @@ function loadHistory(): void { } function saveHistory(): void { - try { - const historyPath = getHistoryFilePath(); - fs.writeFileSync(historyPath, JSON.stringify(clipboardHistory, null, 2)); - } catch (e) { - console.error('Failed to save clipboard history:', e); - } + if (pendingSave) clearTimeout(pendingSave); + pendingSave = setTimeout(() => { + try { + const historyPath = getHistoryFilePath(); + fs.writeFileSync(historyPath, JSON.stringify(clipboardHistory, null, 2)); + } catch (e) { + console.error('Failed to save clipboard history:', e); + } + }, 100); // Batch writes within 100ms } // ─── Clipboard Monitoring ─────────────────────────────────────────── @@ -277,6 +284,9 @@ function pollClipboard(): void { // ─── Public API ───────────────────────────────────────────────────── export function startClipboardMonitor(): void { + // Ensure directories exist once at startup + ensureClipboardDirs(); + loadHistory(); // Initial read diff --git a/src/main/memory.ts b/src/main/memory.ts index 22bf621..0f89278 100644 --- a/src/main/memory.ts +++ b/src/main/memory.ts @@ -12,6 +12,8 @@ const MAX_TOP_K = 20; const MAX_MEMORY_ITEM_CHARS = 320; const MAX_MEMORY_CONTEXT_CHARS = 2400; +let pendingMemorySave: NodeJS.Timeout | null = null; + // ─── Local File-Based Memory Store ────────────────────────────────────────── // Used as a fallback when Supermemory is not configured. Persists to // /local-memories.json so memories survive restarts. @@ -37,11 +39,14 @@ function readLocalMemories(): LocalMemoryEntry[] { } function writeLocalMemories(memories: LocalMemoryEntry[]): void { - try { - fs.writeFileSync(getLocalMemoryPath(), JSON.stringify(memories, null, 2), 'utf8'); - } catch (err) { - console.error('[LocalMemory] Failed to write memories:', err); - } + if (pendingMemorySave) clearTimeout(pendingMemorySave); + pendingMemorySave = setTimeout(() => { + try { + fs.writeFileSync(getLocalMemoryPath(), JSON.stringify(memories, null, 2), 'utf8'); + } catch (err) { + console.error('[LocalMemory] Failed to write memories:', err); + } + }, 100); // Batch writes within 100ms } function addLocalMemory(text: string): { id: string } { diff --git a/src/main/settings-store.ts b/src/main/settings-store.ts index 43d3329..21ba844 100644 --- a/src/main/settings-store.ts +++ b/src/main/settings-store.ts @@ -168,6 +168,21 @@ export function saveSettings(patch: Partial): AppSettings { return { ...updated }; } +export async function saveSettingsAsync(patch: Partial): Promise { + const current = loadSettings(); + const updated = { ...current, ...patch }; + settingsCache = updated; + + // Fire-and-forget async write + try { + await fs.promises.writeFile(getSettingsPath(), JSON.stringify(updated, null, 2)); + } catch (e) { + console.error('Failed to save settings:', e); + } + + return { ...updated }; +} + export function resetSettingsCache(): void { settingsCache = null; } @@ -185,6 +200,7 @@ interface OAuthTokenEntry { } let oauthTokensCache: Record | null = null; +let pendingOAuthSave: NodeJS.Timeout | null = null; function getOAuthTokensPath(): string { return path.join(app.getPath('userData'), 'oauth-tokens.json'); @@ -203,11 +219,14 @@ function loadOAuthTokens(): Record { function saveOAuthTokens(tokens: Record): void { oauthTokensCache = tokens; - try { - fs.writeFileSync(getOAuthTokensPath(), JSON.stringify(tokens, null, 2)); - } catch (e) { - console.error('Failed to save OAuth tokens:', e); - } + if (pendingOAuthSave) clearTimeout(pendingOAuthSave); + pendingOAuthSave = setTimeout(() => { + try { + fs.writeFileSync(getOAuthTokensPath(), JSON.stringify(tokens, null, 2)); + } catch (e) { + console.error('Failed to save OAuth tokens:', e); + } + }, 100); // Batch writes within 100ms } export function setOAuthToken(provider: string, token: OAuthTokenEntry): void { diff --git a/src/main/snippet-store.ts b/src/main/snippet-store.ts index 719eb25..ba241c1 100644 --- a/src/main/snippet-store.ts +++ b/src/main/snippet-store.ts @@ -24,15 +24,19 @@ export interface Snippet { } let snippetsCache: Snippet[] | null = null; +let pendingSave: NodeJS.Timeout | null = null; // ─── Paths ────────────────────────────────────────────────────────── function getSnippetsDir(): string { - const dir = path.join(app.getPath('userData'), 'snippets'); + return path.join(app.getPath('userData'), 'snippets'); +} + +function ensureSnippetsDir(): void { + const dir = getSnippetsDir(); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } - return dir; } function getSnippetsFilePath(): string { @@ -66,17 +70,21 @@ function loadFromDisk(): Snippet[] { } function saveToDisk(): void { - try { - const filePath = getSnippetsFilePath(); - fs.writeFileSync(filePath, JSON.stringify(snippetsCache || [], null, 2), 'utf-8'); - } catch (e) { - console.error('Failed to save snippets to disk:', e); - } + if (pendingSave) clearTimeout(pendingSave); + pendingSave = setTimeout(() => { + try { + const filePath = getSnippetsFilePath(); + fs.writeFileSync(filePath, JSON.stringify(snippetsCache || [], null, 2), 'utf-8'); + } catch (e) { + console.error('Failed to save snippets to disk:', e); + } + }, 100); // Batch writes within 100ms } // ─── Public API ───────────────────────────────────────────────────── export function initSnippetStore(): void { + ensureSnippetsDir(); snippetsCache = loadFromDisk(); console.log(`[Snippets] Loaded ${snippetsCache.length} snippet(s)`); }