preload.js
Rama main · réplica de lectura del repositorio original.
const { contextBridge, ipcRenderer, webUtils } = require("electron");
// BYOK API-key bridges, built once instead of hand-listed per key. Sandboxed
// preloads can't require local modules, so the {base, get, save} tuples are
// inlined here; keep them in sync with the BYOK_API_KEYS manifest in
// src/config/secretKeys.js (the main process derives its plumbing from that).
const BYOK_KEY_BRIDGES = [
{ base: "openai", get: "getOpenAIKey", save: "saveOpenAIKey" },
{ base: "anthropic", get: "getAnthropicKey", save: "saveAnthropicKey" },
{ base: "gemini", get: "getGeminiKey", save: "saveGeminiKey" },
{ base: "groq", get: "getGroqKey", save: "saveGroqKey" },
{ base: "xai", get: "getXaiKey", save: "saveXaiKey" },
{ base: "mistral", get: "getMistralKey", save: "saveMistralKey" },
{ base: "openrouter", get: "getOpenrouterKey", save: "saveOpenrouterKey" },
{ base: "tinfoil", get: "getTinfoilKey", save: "saveTinfoilKey" },
{ base: "corti", get: "getCortiKey", save: "saveCortiKey" },
{ base: "deepgram", get: "getDeepgramKey", save: "saveDeepgramKey" },
{ base: "assemblyai", get: "getAssemblyAIKey", save: "saveAssemblyAIKey" },
{
base: "note-formatting-custom",
get: "getNoteFormattingCustomKey",
save: "saveNoteFormattingCustomKey",
},
{ base: "translation-custom", get: "getTranslationCustomKey", save: "saveTranslationCustomKey" },
{
base: "dictation-agent-custom",
get: "getDictationAgentCustomKey",
save: "saveDictationAgentCustomKey",
},
{
base: "dictation-agent-vision-custom",
get: "getDictationAgentVisionCustomKey",
save: "saveDictationAgentVisionCustomKey",
},
{ base: "chat-agent-custom", get: "getChatAgentCustomKey", save: "saveChatAgentCustomKey" },
];
const secretKeyApi = {};
for (const k of BYOK_KEY_BRIDGES) {
secretKeyApi[k.get] = () => ipcRenderer.invoke(`get-${k.base}-key`);
secretKeyApi[k.save] = (key) => ipcRenderer.invoke(`save-${k.base}-key`, key);
}
/**
* Helper to register an IPC listener and return a cleanup function.
* Ensures renderer code can easily remove listeners to avoid leaks.
*/
const registerListener = (channel, handlerFactory) => {
return (callback) => {
if (typeof callback !== "function") {
return () => {};
}
const listener =
typeof handlerFactory === "function"
? handlerFactory(callback)
: (event, ...args) => callback(event, ...args);
ipcRenderer.on(channel, listener);
return () => {
ipcRenderer.removeListener(channel, listener);
};
};
};
contextBridge.exposeInMainWorld("electronAPI", {
initialModelRegistrySnapshot: ipcRenderer.sendSync?.("model-registry-initial"),
getModelRegistry: () => ipcRenderer.invoke("get-model-registry"),
onModelRegistryUpdated: registerListener(
"model-registry-updated",
(callback) => (_event, snapshot) => callback(snapshot)
),
setOnboardingWindowMode: (mode) => ipcRenderer.invoke("onboarding-set-window-mode", mode),
setOnboardingActive: (active) => ipcRenderer.invoke("onboarding-set-active", active),
openPermissionGuide: (state) => ipcRenderer.invoke("permission-guide-open", state),
closePermissionGuide: () => ipcRenderer.invoke("permission-guide-close"),
getPermissionGuideState: () => ipcRenderer.invoke("permission-guide-state"),
permissionGuideAction: (action) => ipcRenderer.send("permission-guide-action", action),
startPermissionGuideDrag: (target) => ipcRenderer.send("permission-guide-drag", target),
onPermissionGuideState: registerListener(
"permission-guide-state-changed",
(callback) => (_event, state) => callback(state)
),
onPermissionGuideAction: registerListener(
"permission-guide-action",
(callback) => (_event, action) => callback(action)
),
// The agent window (`?agent=true`) and the dictation window's ways into it.
agentWindowReady: () => ipcRenderer.send("agent-window-ready"),
agentWindowSetLayout: (request) => ipcRenderer.invoke("agent-window-set-layout", request),
agentWindowHide: () => ipcRenderer.invoke("agent-window-hide"),
agentWindowSetDragging: (dragging) => ipcRenderer.send("agent-window-drag", dragging),
agentWindowSetVoiceState: (state, attempt) =>
ipcRenderer.invoke("agent-window-voice-state", state, attempt),
openAgentWindow: () => ipcRenderer.invoke("open-agent-window"),
submitAgentCommand: (command) => ipcRenderer.invoke("agent-window-submit-command", command),
onAgentWindowHotkey: registerListener(
"agent-window-hotkey",
(callback) => (_event, payload) => callback(payload)
),
onAgentWindowOpen: registerListener(
"agent-window-open",
(callback) => (_event, payload) => callback(payload)
),
onAgentWindowCommand: registerListener(
"agent-window-command",
(callback) => (_event, command) => callback(command)
),
onAgentWindowDismiss: registerListener("agent-window-dismiss", (callback) => () => callback()),
onAgentRecordingChanged: registerListener(
"agent-recording-changed",
(callback) => (_event, recording) => callback(recording)
),
markMacAccessibilityFeaturesReady: (expectedAccountScope) =>
expectedAccountScope
? ipcRenderer.send("mac-accessibility-features-ready", expectedAccountScope)
: ipcRenderer.send("mac-accessibility-features-ready"),
beginOnboardingDemo: (session) => ipcRenderer.invoke("onboarding-demo-begin", session),
endOnboardingDemo: (id) => ipcRenderer.invoke("onboarding-demo-end", id),
stopOnboardingDemo: (id) => ipcRenderer.invoke("onboarding-demo-stop", id),
publishOnboardingDemoEvent: (event) => ipcRenderer.invoke("onboarding-demo-publish", event),
onOnboardingDemoEvent: registerListener(
"onboarding-demo-event",
(callback) => (_event, payload) => callback(payload)
),
testProviderConnection: (config) => ipcRenderer.invoke("test-provider-connection", config),
pasteText: (text, options) => ipcRenderer.invoke("paste-text", text, options),
captureSelectedText: () => ipcRenderer.invoke("capture-selected-text"),
cancelSelectionEdit: (sessionId) => ipcRenderer.invoke("cancel-selection-edit", sessionId),
replaceSelectedText: (sessionId, text, options) =>
ipcRenderer.invoke("replace-selected-text", sessionId, text, options),
hideWindow: () => ipcRenderer.invoke("hide-window"),
showDictationPanel: () => ipcRenderer.invoke("show-dictation-panel"),
openSettingsSection: (section) => ipcRenderer.invoke("open-settings-section", section),
captureDictationTarget: () => ipcRenderer.invoke("capture-dictation-target"),
captureDictationAppCategory: () => ipcRenderer.invoke("capture-dictation-app-category"),
onToggleDictation: registerListener("toggle-dictation", (callback) => () => callback()),
onToggleVoiceAgent: registerListener("toggle-voice-agent", (callback) => () => callback()),
onToggleTranslation: registerListener("toggle-translation", (callback) => () => callback()),
onMeetingStartRefused: registerListener(
"meeting-start-refused",
(callback) => (_event, code) => callback(code)
),
onStartDictation: registerListener("start-dictation", (callback) => () => callback()),
onStopDictation: registerListener("stop-dictation", (callback) => () => callback()),
onPrepareDictation: registerListener("prepare-dictation", (callback) => () => callback()),
onCancelDictationPreparation: registerListener(
"cancel-dictation-preparation",
(callback) => () => callback()
),
onDictationForceStopped: registerListener(
"dictation-force-stopped",
(callback) => (_event, payload) => callback(payload)
),
micWarmHoldChanged: (active) => ipcRenderer.send("mic-warm-hold-changed", active),
dictationLifecycleStateChanged: (state, attempt) =>
ipcRenderer.invoke("dictation-lifecycle-state-changed", state, attempt),
// Database functions
saveTranscription: (text, rawText, options) =>
ipcRenderer.invoke("db-save-transcription", text, rawText, options),
getTranscriptions: (limit, options) =>
ipcRenderer.invoke("db-get-transcriptions", limit, options),
recordAnalyticsEvent: (input) => ipcRenderer.invoke("analytics-record-event", input),
closeActivationStream: () => ipcRenderer.invoke("activation-close"),
beginActivationCapture: () => ipcRenderer.invoke("activation-begin"),
stopActivationCapture: (id) => ipcRenderer.invoke("activation-stop", id),
finishActivationCapture: (id, words) => ipcRenderer.invoke("activation-finish", id, words),
cancelActivationCapture: (id) => ipcRenderer.invoke("activation-cancel", id),
getAnalyticsSummary: () => ipcRenderer.invoke("analytics-get-summary"),
getPendingAnalyticsEvents: (limit, context) =>
ipcRenderer.invoke("analytics-get-pending", limit, context),
markAnalyticsEventsSynced: (eventIds, context) =>
ipcRenderer.invoke("analytics-mark-synced", eventIds, context),
getPendingAnalyticsDeletes: (limit, context) =>
ipcRenderer.invoke("analytics-get-pending-deletes", limit, context),
hardDeleteAnalyticsEvents: (eventIds, context) =>
ipcRenderer.invoke("analytics-hard-delete", eventIds, context),
getPendingAnalyticsClear: (context) => ipcRenderer.invoke("analytics-get-pending-clear", context),
completeAnalyticsClear: (clearedThrough, context) =>
ipcRenderer.invoke("analytics-complete-clear", clearedThrough, context),
countUnclaimedAnalyticsEvents: (context) =>
ipcRenderer.invoke("analytics-count-unclaimed", context),
countAnalyticsEventsAwaitingUpload: (context) =>
ipcRenderer.invoke("analytics-count-awaiting-upload", context),
claimAnonymousAnalyticsEvents: (accountId, expectedAuthGeneration) =>
ipcRenderer.invoke("analytics-claim-anonymous", accountId, expectedAuthGeneration),
clearTranscriptions: () => ipcRenderer.invoke("db-clear-transcriptions"),
deleteTranscription: (id) => ipcRenderer.invoke("db-delete-transcription", id),
// Audio storage functions
saveTranscriptionAudio: (id, audioBuffer, metadata) =>
ipcRenderer.invoke("save-transcription-audio", id, audioBuffer, metadata),
mergeAudioSegments: (segments) => ipcRenderer.invoke("merge-audio-segments", segments),
getAudioPath: (id) => ipcRenderer.invoke("get-audio-path", id),
showAudioInFolder: (id) => ipcRenderer.invoke("show-audio-in-folder", id),
getAudioBuffer: (id) => ipcRenderer.invoke("get-audio-buffer", id),
deleteTranscriptionAudio: (id) => ipcRenderer.invoke("delete-transcription-audio", id),
getAudioStorageUsage: () => ipcRenderer.invoke("get-audio-storage-usage"),
deleteAllAudio: () => ipcRenderer.invoke("delete-all-audio"),
syncRetentionSettings: (settings) => ipcRenderer.send("retention-settings-changed", settings),
retryTranscription: (id, settings) => ipcRenderer.invoke("retry-transcription", id, settings),
updateTranscriptionText: (id, text, rawText) =>
ipcRenderer.invoke("update-transcription-text", id, text, rawText),
getTranscriptionById: (id) => ipcRenderer.invoke("get-transcription-by-id", id),
// Dictionary functions
getDictionary: () => ipcRenderer.invoke("db-get-dictionary"),
setDictionary: (words, listsAccountId) =>
ipcRenderer.invoke("db-set-dictionary", words, listsAccountId),
seedDictionary: (words) => ipcRenderer.invoke("db-seed-dictionary", words),
applyDictionaryChanges: (changes, listsAccountId) =>
ipcRenderer.invoke("db-apply-dictionary-changes", changes, listsAccountId),
onDictionaryUpdated: (callback) => {
const listener = (_event, words) => callback?.(words);
ipcRenderer.on("dictionary-updated", listener);
return () => ipcRenderer.removeListener("dictionary-updated", listener);
},
getSnippets: () => ipcRenderer.invoke("db-get-snippets"),
setSnippets: (snippets, listsAccountId) =>
ipcRenderer.invoke("db-set-snippets", snippets, listsAccountId),
seedSnippets: (snippets) => ipcRenderer.invoke("db-seed-snippets", snippets),
onSnippetsUpdated: (callback) => {
const listener = (_event, snippets) => callback?.(snippets);
ipcRenderer.on("snippets-updated", listener);
return () => ipcRenderer.removeListener("snippets-updated", listener);
},
skillsList: () => ipcRenderer.invoke("skills-list"),
skillsGet: (name) => ipcRenderer.invoke("skills-get", name),
skillsReadFile: (name, path) => ipcRenderer.invoke("skills-read-file", name, path),
skillsSetEnabled: (name, enabled) => ipcRenderer.invoke("skills-set-enabled", name, enabled),
skillsUninstall: (name) => ipcRenderer.invoke("skills-uninstall", name),
skillsReveal: (name) => ipcRenderer.invoke("skills-reveal", name),
skillsPreviewUpload: (items) => ipcRenderer.invoke("skills-preview-upload", items),
skillsCommitUpload: (request) => ipcRenderer.invoke("skills-commit-upload", request),
skillsDiscardUploads: (tokens) => ipcRenderer.invoke("skills-discard-uploads", tokens),
skillsCreate: (input) => ipcRenderer.invoke("skills-create", input),
skillsAddRecommended: (id, copy) => ipcRenderer.invoke("skills-add-recommended", id, copy),
skillsReadForModel: (ref) => ipcRenderer.invoke("skills-read-for-model", ref),
skillsCheckProposal: (request) => ipcRenderer.invoke("skills-check-proposal", request),
skillsSaveProposal: (proposal) => ipcRenderer.invoke("skills-save-proposal", proposal),
skillsApplyUpdate: (update) => ipcRenderer.invoke("skills-apply-update", update),
skillsMarkUsed: (name) => ipcRenderer.invoke("skills-mark-used", name),
skillsUndo: (name, versionId) => ipcRenderer.invoke("skills-undo", name, versionId),
onSkillsChanged: (callback) => {
const listener = () => callback?.();
ipcRenderer.on("skills-changed", listener);
return () => ipcRenderer.removeListener("skills-changed", listener);
},
setAutoLearnEnabled: (enabled) => ipcRenderer.send("auto-learn-changed", enabled),
onCorrectionsLearned: (callback) => {
const listener = (_event, words) => callback?.(words);
ipcRenderer.on("corrections-learned", listener);
return () => ipcRenderer.removeListener("corrections-learned", listener);
},
undoLearnedCorrections: (words) => ipcRenderer.invoke("undo-learned-corrections", words),
// Note functions
saveNote: (title, content, noteType, sourceFile, audioDuration, folderId, spaceId, transcript) =>
ipcRenderer.invoke(
"db-save-note",
title,
content,
noteType,
sourceFile,
audioDuration,
folderId,
spaceId,
transcript
),
getNote: (id) => ipcRenderer.invoke("db-get-note", id),
getNotes: (noteType, limit, folderId, spaceId, spaceIds) =>
ipcRenderer.invoke("db-get-notes", noteType, limit, folderId, spaceId, spaceIds),
getSpaceNotes: (spaceId, limit) => ipcRenderer.invoke("db-get-space-notes", spaceId, limit),
updateNote: (id, updates, options) => ipcRenderer.invoke("db-update-note", id, updates, options),
getNoteUndos: () => ipcRenderer.invoke("db-get-note-undos"),
claimNoteUndo: (token) => ipcRenderer.invoke("db-claim-note-undo", token),
undoNoteUpdate: (token) => ipcRenderer.invoke("db-undo-note-update", token),
discardNoteUndo: (id, token) => ipcRenderer.invoke("db-discard-note-undo", id, token),
deleteNote: (id) => ipcRenderer.invoke("db-delete-note", id),
exportNote: (noteId, format) => ipcRenderer.invoke("export-note", noteId, format),
exportTranscript: (noteId, format) => ipcRenderer.invoke("export-transcript", noteId, format),
exportDictionary: (words) => ipcRenderer.invoke("export-dictionary", words),
searchNotes: (query, limit, spaceId, folderId, spaceIds) =>
ipcRenderer.invoke("db-search-notes", query, limit, spaceId, folderId, spaceIds),
semanticSearchNotes: (query, limit, spaceId, folderId) =>
ipcRenderer.invoke("db-semantic-search-notes", query, limit, spaceId, folderId),
updateNoteCloudId: (id, cloudId) => ipcRenderer.invoke("db-update-note-cloud-id", id, cloudId),
updateNoteShareState: (id, state) => ipcRenderer.invoke("db-update-note-share-state", id, state),
// Folder functions
getFolders: (spaceId) => ipcRenderer.invoke("db-get-folders", spaceId),
createFolder: (name, spaceId) => ipcRenderer.invoke("db-create-folder", name, spaceId),
deleteFolder: (id) => ipcRenderer.invoke("db-delete-folder", id),
renameFolder: (id, name) => ipcRenderer.invoke("db-rename-folder", id, name),
moveFolderToSpace: (id, spaceId) => ipcRenderer.invoke("db-move-folder-to-space", id, spaceId),
getFolderNoteCounts: () => ipcRenderer.invoke("db-get-folder-note-counts"),
// Space functions
getSpaces: () => ipcRenderer.invoke("db-get-spaces"),
hasOtherAccountRows: () => ipcRenderer.invoke("db-has-other-account-rows"),
accountChangedSince: (occurredAt) => ipcRenderer.invoke("db-account-changed-since", occurredAt),
hasUnclaimedLegacyRows: () => ipcRenderer.invoke("db-has-unclaimed-legacy-rows"),
setActiveAccountScope: (accountId, expectedAuthGeneration) =>
ipcRenderer.invoke("set-active-account-scope", accountId, expectedAuthGeneration),
getActiveAccountScope: () => ipcRenderer.invoke("get-active-account-scope"),
getDatabaseAccountScope: () => ipcRenderer.invoke("get-database-account-scope"),
onDatabaseAccountScopeChanged: registerListener(
"database-account-scope-changed",
(callback) => (_event, scope) => callback(scope)
),
onActiveAccountScopeChanged: registerListener(
"active-account-scope-changed",
(callback) => (_event, scope) => callback(scope)
),
deleteAccountData: (accountId, expectedAuthGeneration, options) =>
ipcRenderer.invoke("delete-account-data", accountId, expectedAuthGeneration, options),
updateSpace: (id, updates) => ipcRenderer.invoke("db-update-space", id, updates),
purgeSpace: (id, options) => ipcRenderer.invoke("db-purge-space", id, options),
upsertSpaceFromCloud: (space) => ipcRenderer.invoke("db-upsert-space-from-cloud", space),
setSpaceSyncStatus: (id, status) => ipcRenderer.invoke("db-set-space-sync-status", id, status),
onSpacePurged: (callback) => {
const listener = (_event, payload) => callback?.(payload);
ipcRenderer.on("space-purged", listener);
return () => ipcRenderer.removeListener("space-purged", listener);
},
onSpaceSynced: (callback) => {
const listener = (_event, space) => callback?.(space);
ipcRenderer.on("space-synced", listener);
return () => ipcRenderer.removeListener("space-synced", listener);
},
// Note files (markdown mirror) functions
noteFilesSetEnabled: (enabled, customPath, options) =>
ipcRenderer.invoke("note-files-set-enabled", enabled, customPath, options),
noteFilesSetPath: (path) => ipcRenderer.invoke("note-files-set-path", path),
noteFilesRebuild: () => ipcRenderer.invoke("note-files-rebuild"),
noteFilesGetDefaultPath: () => ipcRenderer.invoke("note-files-get-default-path"),
noteFilesPickFolder: () => ipcRenderer.invoke("note-files-pick-folder"),
granolaImportPickAndPreview: () => ipcRenderer.invoke("granola-import-pick-and-preview"),
granolaImportRun: () => ipcRenderer.invoke("granola-import-run"),
dragonImportPickFile: () => ipcRenderer.invoke("dragon-import-pick-file"),
showNoteFile: (noteId) => ipcRenderer.invoke("show-note-file", noteId),
showFolderInExplorer: (folderName) => ipcRenderer.invoke("show-folder-in-explorer", folderName),
// Action functions
getActions: () => ipcRenderer.invoke("db-get-actions"),
getAction: (id) => ipcRenderer.invoke("db-get-action", id),
createAction: (name, description, prompt, icon, fields) =>
ipcRenderer.invoke("db-create-action", name, description, prompt, icon, fields),
updateAction: (id, updates) => ipcRenderer.invoke("db-update-action", id, updates),
deleteAction: (id) => ipcRenderer.invoke("db-delete-action", id),
// Audio file operations
selectAudioFile: (options) => ipcRenderer.invoke("select-audio-file", options),
getFileSize: (filePath) => ipcRenderer.invoke("get-file-size", filePath),
transcribeAudioFile: (filePath, options) =>
ipcRenderer.invoke("transcribe-audio-file", filePath, options),
getPathForFile: (file) => {
const filePath = webUtils.getPathForFile(file);
// Register real dropped-file paths so the main-process audio allowlist accepts them.
if (filePath) ipcRenderer.send("approve-audio-path", filePath);
return filePath;
},
// URL audio download
downloadUrlAudio: (url, downloadId) => ipcRenderer.invoke("download-url-audio", url, downloadId),
cancelUrlDownload: (downloadId) => ipcRenderer.invoke("cancel-url-download", downloadId),
deleteTempFile: (filePath) => ipcRenderer.invoke("delete-temp-file", filePath),
onUrlDownloadProgress: registerListener(
"url-download-progress",
(callback) => (_event, data) => callback(data)
),
onNoteAdded: (callback) => {
const listener = (_event, note) => callback?.(note);
ipcRenderer.on("note-added", listener);
return () => ipcRenderer.removeListener("note-added", listener);
},
onNoteUpdated: (callback) => {
const listener = (_event, note) => callback?.(note);
ipcRenderer.on("note-updated", listener);
return () => ipcRenderer.removeListener("note-updated", listener);
},
onNoteDeleted: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("note-deleted", listener);
return () => ipcRenderer.removeListener("note-deleted", listener);
},
onNoteSynced: (callback) => {
const listener = (_event, note) => callback?.(note);
ipcRenderer.on("note-synced", listener);
return () => ipcRenderer.removeListener("note-synced", listener);
},
onFolderSynced: (callback) => {
const listener = (_event, folder) => callback?.(folder);
ipcRenderer.on("folder-synced", listener);
return () => ipcRenderer.removeListener("folder-synced", listener);
},
onFolderDeleted: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("folder-deleted", listener);
return () => ipcRenderer.removeListener("folder-deleted", listener);
},
emitSyncEvent: (name, payload) => ipcRenderer.invoke("broadcast-sync-event", name, payload),
onSyncEvent: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("sync-event", listener);
return () => ipcRenderer.removeListener("sync-event", listener);
},
onActionCreated: (callback) => {
const listener = (_event, action) => callback?.(action);
ipcRenderer.on("action-created", listener);
return () => ipcRenderer.removeListener("action-created", listener);
},
onActionUpdated: (callback) => {
const listener = (_event, action) => callback?.(action);
ipcRenderer.on("action-updated", listener);
return () => ipcRenderer.removeListener("action-updated", listener);
},
onActionDeleted: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("action-deleted", listener);
return () => ipcRenderer.removeListener("action-deleted", listener);
},
onTranscriptionAdded: (callback) => {
const listener = (_event, transcription) => callback?.(transcription);
ipcRenderer.on("transcription-added", listener);
return () => ipcRenderer.removeListener("transcription-added", listener);
},
onTranscriptionDeleted: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("transcription-deleted", listener);
return () => ipcRenderer.removeListener("transcription-deleted", listener);
},
onTranscriptionsCleared: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("transcriptions-cleared", listener);
return () => ipcRenderer.removeListener("transcriptions-cleared", listener);
},
onTranscriptionUpdated: (callback) => {
const listener = (_event, transcription) => callback?.(transcription);
ipcRenderer.on("transcription-updated", listener);
return () => ipcRenderer.removeListener("transcription-updated", listener);
},
onAnalyticsChanged: (callback) => {
const listener = () => callback?.();
ipcRenderer.on("analytics-changed", listener);
return () => ipcRenderer.removeListener("analytics-changed", listener);
},
// BYOK API keys (get/save for every provider in the secretKeys manifest)
...secretKeyApi,
// Clipboard functions
checkAccessibilityPermission: (silent) =>
ipcRenderer.invoke("check-accessibility-permission", silent),
promptAccessibilityPermission: () => ipcRenderer.invoke("prompt-accessibility-permission"),
readClipboard: () => ipcRenderer.invoke("read-clipboard"),
writeClipboard: (text) => ipcRenderer.invoke("write-clipboard", text),
copyLeaderboardImage: (dataUrl) => ipcRenderer.invoke("leaderboard-copy-image", dataUrl),
saveLeaderboardImage: (dataUrl, suggestedName) =>
ipcRenderer.invoke("leaderboard-save-image", dataUrl, suggestedName),
checkPasteTools: () => ipcRenderer.invoke("check-paste-tools"),
// Voice drafts (chat input recordings)
saveTempAudio: (buffer) => ipcRenderer.invoke("save-temp-audio", buffer),
deleteTempAudio: (tempPath) => ipcRenderer.invoke("delete-temp-audio", tempPath),
// Local Whisper functions (whisper.cpp)
transcribeLocalWhisper: (audioBlob, options) =>
ipcRenderer.invoke("transcribe-local-whisper", audioBlob, options),
checkWhisperInstallation: () => ipcRenderer.invoke("check-whisper-installation"),
downloadWhisperModel: (modelName) => ipcRenderer.invoke("download-whisper-model", modelName),
onWhisperDownloadProgress: registerListener("whisper-download-progress"),
checkModelStatus: (modelName) => ipcRenderer.invoke("check-model-status", modelName),
listWhisperModels: () => ipcRenderer.invoke("list-whisper-models"),
deleteWhisperModel: (modelName) => ipcRenderer.invoke("delete-whisper-model", modelName),
deleteAllWhisperModels: () => ipcRenderer.invoke("delete-all-whisper-models"),
cancelWhisperDownload: () => ipcRenderer.invoke("cancel-whisper-download"),
checkFFmpegAvailability: () => ipcRenderer.invoke("check-ffmpeg-availability"),
getAudioDiagnostics: () => ipcRenderer.invoke("get-audio-diagnostics"),
// Whisper server functions (faster repeated transcriptions)
whisperServerStart: (modelName) => ipcRenderer.invoke("whisper-server-start", modelName),
whisperServerStop: () => ipcRenderer.invoke("whisper-server-stop"),
whisperServerStatus: () => ipcRenderer.invoke("whisper-server-status"),
whisperGpuRetry: () => ipcRenderer.invoke("whisper-gpu-retry"),
// CUDA GPU acceleration
listGpus: () => ipcRenderer.invoke("list-gpus"),
setGpuDeviceIndex: (purpose, uuid) => ipcRenderer.invoke("set-gpu-device-index", purpose, uuid),
getGpuDeviceIndex: (purpose) => ipcRenderer.invoke("get-gpu-device-index", purpose),
detectGpu: () => ipcRenderer.invoke("detect-gpu"),
getCudaWhisperStatus: () => ipcRenderer.invoke("get-cuda-whisper-status"),
downloadCudaWhisperBinary: () => ipcRenderer.invoke("download-cuda-whisper-binary"),
cancelCudaWhisperDownload: () => ipcRenderer.invoke("cancel-cuda-whisper-download"),
deleteCudaWhisperBinary: () => ipcRenderer.invoke("delete-cuda-whisper-binary"),
onCudaDownloadProgress: registerListener(
"cuda-download-progress",
(callback) => (_event, data) => callback(data)
),
onCudaFallbackNotification: registerListener(
"cuda-fallback-notification",
(callback) => () => callback()
),
// Vulkan GPU acceleration (whisper on AMD/Intel GPUs)
getVulkanWhisperStatus: () => ipcRenderer.invoke("get-vulkan-whisper-status"),
downloadVulkanWhisperBinary: () => ipcRenderer.invoke("download-vulkan-whisper-binary"),
cancelVulkanWhisperDownload: () => ipcRenderer.invoke("cancel-vulkan-whisper-download"),
deleteVulkanWhisperBinary: () => ipcRenderer.invoke("delete-vulkan-whisper-binary"),
onVulkanWhisperDownloadProgress: registerListener(
"vulkan-whisper-download-progress",
(callback) => (_event, data) => callback(data)
),
onGpuFallbackNotification: registerListener(
"gpu-fallback-notification",
(callback) => () => callback()
),
// Main changed the installed packs or the remembered GPU failure (#1736)
onWhisperGpuStatusChanged: registerListener(
"whisper-gpu-status-changed",
(callback) => () => callback()
),
// One-time "GPU pack needs re-downloading" notice
takeGpuPackMigrationNotice: () => ipcRenderer.invoke("take-gpu-pack-migration-notice"),
onGpuPackMigrationNoticeReady: registerListener(
"gpu-pack-migration-notice-ready",
(callback) => () => callback()
),
// Local Parakeet (NVIDIA) functions
transcribeLocalParakeet: (audioBlob, options) =>
ipcRenderer.invoke("transcribe-local-parakeet", audioBlob, options),
checkParakeetInstallation: () => ipcRenderer.invoke("check-parakeet-installation"),
downloadParakeetModel: (modelName) => ipcRenderer.invoke("download-parakeet-model", modelName),
onParakeetDownloadProgress: registerListener("parakeet-download-progress"),
checkParakeetModelStatus: (modelName) =>
ipcRenderer.invoke("check-parakeet-model-status", modelName),
listParakeetModels: () => ipcRenderer.invoke("list-parakeet-models"),
deleteParakeetModel: (modelName) => ipcRenderer.invoke("delete-parakeet-model", modelName),
deleteAllParakeetModels: () => ipcRenderer.invoke("delete-all-parakeet-models"),
cancelParakeetDownload: () => ipcRenderer.invoke("cancel-parakeet-download"),
getParakeetDiagnostics: () => ipcRenderer.invoke("get-parakeet-diagnostics"),
// Parakeet server functions (faster repeated transcriptions)
parakeetServerStart: (modelName) => ipcRenderer.invoke("parakeet-server-start", modelName),
parakeetServerStop: () => ipcRenderer.invoke("parakeet-server-stop"),
parakeetServerStatus: () => ipcRenderer.invoke("parakeet-server-status"),
// Diarization (speaker identification) functions
downloadDiarizationModels: () => ipcRenderer.invoke("download-diarization-models"),
getDiarizationModelStatus: () => ipcRenderer.invoke("get-diarization-model-status"),
deleteDiarizationModels: () => ipcRenderer.invoke("delete-diarization-models"),
cancelDiarizationDownload: () => ipcRenderer.invoke("cancel-diarization-download"),
diarizeAudioFile: (filePath, options) =>
ipcRenderer.invoke("diarize-audio-file", filePath, options),
mergeSpeakerText: (segments, text, duration) =>
ipcRenderer.invoke("merge-speaker-text", { segments, text, duration }),
onDiarizationDownloadProgress: registerListener(
"diarization-download-progress",
(callback) => (_event, data) => callback(data)
),
onMeetingDiarizationComplete: registerListener(
"meeting-diarization-complete",
(callback) => (_event, data) => callback(data)
),
// Speaker name mapping
getSpeakerMappings: (noteId) => ipcRenderer.invoke("get-speaker-mappings", noteId),
setSpeakerMapping: (noteId, speakerId, displayName, email, profileId) =>
ipcRenderer.invoke("set-speaker-mapping", noteId, speakerId, displayName, email, profileId),
removeSpeakerMapping: (noteId, speakerId) =>
ipcRenderer.invoke("remove-speaker-mapping", noteId, speakerId),
getSpeakerProfiles: () => ipcRenderer.invoke("get-speaker-profiles"),
attachSpeakerEmail: (profileId, email) =>
ipcRenderer.invoke("attach-speaker-email", profileId, email),
saveNoteSpeakerEmbeddings: (noteId, embeddings) =>
ipcRenderer.invoke("save-note-speaker-embeddings", noteId, embeddings),
// Window control functions
windowMinimize: () => ipcRenderer.invoke("window-minimize"),
windowMaximize: () => ipcRenderer.invoke("window-maximize"),
windowClose: () => ipcRenderer.invoke("window-close"),
windowIsMaximized: () => ipcRenderer.invoke("window-is-maximized"),
snapToMeetingMode: () => ipcRenderer.invoke("snap-to-meeting-mode"),
restoreFromMeetingMode: () => ipcRenderer.invoke("restore-from-meeting-mode"),
meetingNoteOpenedWithoutRecording: (id) =>
ipcRenderer.invoke("meeting-note-opened-without-recording", id),
meetingNoteClosed: (id) => ipcRenderer.invoke("meeting-note-closed", id),
getPlatform: () => process.platform,
// Cleanup function
cleanupApp: () => ipcRenderer.invoke("cleanup-app"),
relaunchApp: () => ipcRenderer.invoke("relaunch-app"),
updateHotkey: (hotkey) => ipcRenderer.invoke("update-hotkey", hotkey),
setHotkeyListeningMode: (enabled) => ipcRenderer.invoke("set-hotkey-listening-mode", enabled),
getHotkeyModeInfo: (hotkey) => ipcRenderer.invoke("get-hotkey-mode-info", hotkey),
getHyprlandConfigStatus: () => ipcRenderer.invoke("get-hyprland-config-status"),
startWindowDrag: () => ipcRenderer.invoke("start-window-drag"),
stopWindowDrag: () => ipcRenderer.invoke("stop-window-drag"),
startControlPanelDrag: () => ipcRenderer.invoke("start-control-panel-drag"),
stopControlPanelDrag: () => ipcRenderer.invoke("stop-control-panel-drag"),
getMainWindowHorizontalDirection: () =>
ipcRenderer.invoke("get-main-window-horizontal-direction"),
onMainWindowHorizontalDirectionChanged: registerListener(
"main-window-horizontal-direction-changed",
(callback) => (_event, direction) => callback(direction)
),
onMainWindowWillResize: registerListener(
"main-window-will-resize",
(callback) => (_event, resize) => callback(resize)
),
ackMainWindowResizeMask: (token) => ipcRenderer.send("main-window-resize-mask-ready", token),
setMainWindowInteractivity: (interactive) =>
ipcRenderer.invoke("set-main-window-interactivity", interactive),
setMainWindowInputRegion: (region) => ipcRenderer.invoke("set-main-window-input-region", region),
onMainWindowVisibilityChanged: registerListener(
"main-window-visibility-changed",
(callback) => (_event, visible) => callback(visible)
),
setNotificationInteractivity: (interactive) =>
ipcRenderer.invoke("set-notification-interactivity", interactive),
resizeMainWindow: (sizeKey) => ipcRenderer.invoke("resize-main-window", sizeKey),
resizeAssistantWindowToContent: (surfaceHeight) =>
ipcRenderer.invoke("resize-assistant-window-to-content", surfaceHeight),
resizeDictationErrorWindowToContent: (surfaceHeight) =>
ipcRenderer.invoke("resize-dictation-error-window-to-content", surfaceHeight),
// Update functions
checkForUpdates: () => ipcRenderer.invoke("check-for-updates"),
downloadUpdate: () => ipcRenderer.invoke("download-update"),
installUpdate: () => ipcRenderer.invoke("install-update"),
getAppVersion: () => ipcRenderer.invoke("get-app-version"),
getPostMigrationState: () => ipcRenderer.invoke("get-post-migration-state"),
getOAuthProtocolRegistered: () => ipcRenderer.invoke("get-oauth-protocol-registered"),
getOAuthProtocol: () => ipcRenderer.invoke("get-oauth-protocol"),
markBundleMigrated: () => ipcRenderer.invoke("mark-bundle-migrated"),
markBundleMigrationDismissed: () => ipcRenderer.invoke("mark-bundle-migration-dismissed"),
getUpdateStatus: () => ipcRenderer.invoke("get-update-status"),
getUpdateInfo: () => ipcRenderer.invoke("get-update-info"),
setAutoUpdatesEnabled: (enabled) => ipcRenderer.invoke("set-auto-updates-enabled", enabled),
// Update event listeners
onUpdateAvailable: registerListener("update-available"),
onUpdateNotAvailable: registerListener("update-not-available"),
onUpdateDownloaded: registerListener("update-downloaded"),
onUpdateDownloadProgress: registerListener("update-download-progress"),
onUpdateError: registerListener("update-error"),
// Audio event listeners
onCancelHotkeyPressed: registerListener("cancel-hotkey-pressed", (cb) => () => cb()),
registerCancelHotkey: (key) => ipcRenderer.invoke("register-cancel-hotkey", key),
unregisterCancelHotkey: () => ipcRenderer.invoke("unregister-cancel-hotkey"),
// External link opener
openExternal: (url) => ipcRenderer.invoke("open-external", url),
// Model management functions
modelGetAll: () => ipcRenderer.invoke("model-get-all"),
modelGetActiveDownloads: () => ipcRenderer.invoke("model-get-active-downloads"),
modelCheck: (modelId) => ipcRenderer.invoke("model-check", modelId),
modelDownload: (modelId) => ipcRenderer.invoke("model-download", modelId),
listRetiredLocalModels: (inUse) => ipcRenderer.invoke("list-retired-local-models", inUse),
deleteRetiredLocalModels: (inUse) => ipcRenderer.invoke("delete-retired-local-models", inUse),
modelDelete: (modelId) => ipcRenderer.invoke("model-delete", modelId),
modelDeleteAll: () => ipcRenderer.invoke("model-delete-all"),
modelCheckRuntime: () => ipcRenderer.invoke("model-check-runtime"),
modelCancelDownload: (modelId) => ipcRenderer.invoke("model-cancel-download", modelId),
onModelDownloadProgress: registerListener("model-download-progress"),
getUiLanguage: () => ipcRenderer.invoke("get-ui-language"),
saveUiLanguage: (language) => ipcRenderer.invoke("save-ui-language", language),
setUiLanguage: (language) => ipcRenderer.invoke("set-ui-language", language),
// xAI / Mistral transcription proxies (keys handled by the manifest bridge)
proxyXaiTranscription: (data) => ipcRenderer.invoke("proxy-xai-transcription", data),
proxyMistralTranscription: (data) => ipcRenderer.invoke("proxy-mistral-transcription", data),
proxyGeminiTranscription: (data) => ipcRenderer.invoke("proxy-gemini-transcription", data),
// Corti API
getCortiClientId: () => ipcRenderer.invoke("get-corti-client-id"),
saveCortiClientId: (key) => ipcRenderer.invoke("save-corti-client-id", key),
getCortiClientSecret: () => ipcRenderer.invoke("get-corti-client-secret"),
saveCortiClientSecret: (key) => ipcRenderer.invoke("save-corti-client-secret", key),
proxyCortiTranscription: (data) => ipcRenderer.invoke("proxy-corti-transcription", data),
getTinfoilChatModels: () => ipcRenderer.invoke("get-tinfoil-chat-models"),
proxyTinfoilTranscription: (data) => ipcRenderer.invoke("proxy-tinfoil-transcription", data),
// Custom endpoint API keys
getCustomTranscriptionKey: () => ipcRenderer.invoke("get-custom-transcription-key"),
saveCustomTranscriptionKey: (key) => ipcRenderer.invoke("save-custom-transcription-key", key),
getCleanupCustomKey: () => ipcRenderer.invoke("get-cleanup-custom-key"),
saveCleanupCustomKey: (key) => ipcRenderer.invoke("save-cleanup-custom-key", key),
// Enterprise provider key management
getBedrockRegion: () => ipcRenderer.invoke("get-bedrock-region"),
saveBedrockRegion: (value) => ipcRenderer.invoke("save-bedrock-region", value),
getBedrockProfile: () => ipcRenderer.invoke("get-bedrock-profile"),
saveBedrockProfile: (value) => ipcRenderer.invoke("save-bedrock-profile", value),
getBedrockAccessKeyId: () => ipcRenderer.invoke("get-bedrock-access-key-id"),
saveBedrockAccessKeyId: (key) => ipcRenderer.invoke("save-bedrock-access-key-id", key),
getBedrockSecretAccessKey: () => ipcRenderer.invoke("get-bedrock-secret-access-key"),
saveBedrockSecretAccessKey: (key) => ipcRenderer.invoke("save-bedrock-secret-access-key", key),
getBedrockSessionToken: () => ipcRenderer.invoke("get-bedrock-session-token"),
saveBedrockSessionToken: (key) => ipcRenderer.invoke("save-bedrock-session-token", key),
getAzureEndpoint: () => ipcRenderer.invoke("get-azure-endpoint"),
saveAzureEndpoint: (value) => ipcRenderer.invoke("save-azure-endpoint", value),
getAzureApiKey: () => ipcRenderer.invoke("get-azure-api-key"),
saveAzureApiKey: (key) => ipcRenderer.invoke("save-azure-api-key", key),
getAzureDeployment: () => ipcRenderer.invoke("get-azure-deployment"),
saveAzureDeployment: (value) => ipcRenderer.invoke("save-azure-deployment", value),
getAzureApiVersion: () => ipcRenderer.invoke("get-azure-api-version"),
saveAzureApiVersion: (value) => ipcRenderer.invoke("save-azure-api-version", value),
getVertexProject: () => ipcRenderer.invoke("get-vertex-project"),
saveVertexProject: (value) => ipcRenderer.invoke("save-vertex-project", value),
getVertexLocation: () => ipcRenderer.invoke("get-vertex-location"),
saveVertexLocation: (value) => ipcRenderer.invoke("save-vertex-location", value),
getVertexApiKey: () => ipcRenderer.invoke("get-vertex-api-key"),
saveVertexApiKey: (key) => ipcRenderer.invoke("save-vertex-api-key", key),
testEnterpriseConnection: (provider, config) =>
ipcRenderer.invoke("test-enterprise-connection", provider, config),
// Dictation key persistence (file-based for reliable startup)
getDictationKey: () => ipcRenderer.invoke("get-dictation-key"),
getActiveDictationKey: () => ipcRenderer.invoke("get-active-dictation-key"),
getEffectiveDefaultHotkey: () => ipcRenderer.invoke("get-effective-default-hotkey"),
saveDictationKey: (key) => ipcRenderer.invoke("save-dictation-key", key),
// Activation mode persistence (file-based for reliable startup)
getActivationMode: () => ipcRenderer.invoke("get-activation-mode"),
saveActivationMode: (mode) => ipcRenderer.invoke("save-activation-mode", mode),
saveAllKeysToEnv: () => ipcRenderer.invoke("save-all-keys-to-env"),
syncStartupPreferences: (prefs) => ipcRenderer.invoke("sync-startup-preferences", prefs),
// Local reasoning
processLocalReasoning: (text, modelId, agentName, config) =>
ipcRenderer.invoke("process-local-reasoning", text, modelId, agentName, config),
checkLocalReasoningAvailable: () => ipcRenderer.invoke("check-local-reasoning-available"),
getLocalContextBudget: (modelId) => ipcRenderer.invoke("get-local-context-budget", modelId),
cancelLocalReasoning: (requestId) => ipcRenderer.invoke("cancel-local-reasoning", requestId),
// Anthropic reasoning
processAnthropicReasoning: (text, modelId, agentName, config) =>
ipcRenderer.invoke("process-anthropic-reasoning", text, modelId, agentName, config),
// Enterprise reasoning (Bedrock, Azure, Vertex) — runs in main process so
// Node-only SDKs (AWS/Azure/Google credential providers) can resolve.
processEnterpriseReasoning: (text, modelId, agentName, config) =>
ipcRenderer.invoke("process-enterprise-reasoning", text, modelId, agentName, config),
cancelEnterpriseReasoning: () => ipcRenderer.send("enterprise-reasoning-cancel"),
enterpriseStreamStart: (payload) => ipcRenderer.invoke("enterprise-stream-start", payload),
enterpriseStreamCancel: (streamId) => ipcRenderer.invoke("enterprise-stream-cancel", streamId),
onEnterpriseStreamPart: registerListener(
"enterprise-stream-part",
(callback) => (_event, payload) => callback(payload)
),
listBedrockModels: (config) => ipcRenderer.invoke("bedrock-list-models", config),
getManagedEnterpriseConfig: (accountId, workspaceId, expectedAuthGeneration, forceRefresh) =>
ipcRenderer.invoke(
"get-managed-enterprise-config",
accountId,
workspaceId,
expectedAuthGeneration,
forceRefresh
),
onManagedEnterpriseConfigChanged: registerListener(
"managed-enterprise-config-changed",
(callback) => (_event, snapshot) => callback(snapshot)
),
clearManagedEnterpriseIdentity: (options) =>
ipcRenderer.invoke("clear-managed-enterprise-identity", options),
managedTranscribe: (data) => ipcRenderer.invoke("managed-transcribe", data),
getPendingEnterpriseAudioTest: (consume = false) =>
ipcRenderer.invoke("get-pending-enterprise-audio-test", consume),
claimEnterpriseAudioTest: () => ipcRenderer.invoke("claim-enterprise-audio-test"),
onEnterpriseAudioTestRequested: registerListener(
"enterprise-audio-test-requested",
(callback) => () => callback()
),
enterpriseAudioImportBootstrap: (data) =>
ipcRenderer.invoke("enterprise-audio-import-bootstrap", data),
enterpriseAudioStatus: (data) => ipcRenderer.invoke("enterprise-audio-status", data),
enterpriseAudioSaveCredential: (data) =>
ipcRenderer.invoke("enterprise-audio-save-credential", data),
enterpriseAudioTest: (data) => ipcRenderer.invoke("enterprise-audio-test", data),
// llama.cpp
llamaCppCheck: () => ipcRenderer.invoke("llama-cpp-check"),
llamaCppInstall: () => ipcRenderer.invoke("llama-cpp-install"),
llamaCppUninstall: () => ipcRenderer.invoke("llama-cpp-uninstall"),
// llama-server
llamaServerStart: (modelId, requestId) =>
ipcRenderer.invoke("llama-server-start", modelId, requestId),
llamaServerCancelStart: (requestId) => ipcRenderer.invoke("llama-server-cancel-start", requestId),
llamaServerStatus: () => ipcRenderer.invoke("llama-server-status"),
llamaGpuReset: () => ipcRenderer.invoke("llama-gpu-reset"),
// Vulkan GPU acceleration
detectVulkanGpu: () => ipcRenderer.invoke("detect-vulkan-gpu"),
getComputerProfile: () => ipcRenderer.invoke("get-computer-profile"),
getLocalSetupSpace: () => ipcRenderer.invoke("get-local-setup-space"),
getLlamaVulkanStatus: () => ipcRenderer.invoke("get-llama-vulkan-status"),
downloadLlamaVulkanBinary: () => ipcRenderer.invoke("download-llama-vulkan-binary"),
cancelLlamaVulkanDownload: () => ipcRenderer.invoke("cancel-llama-vulkan-download"),
deleteLlamaVulkanBinary: () => ipcRenderer.invoke("delete-llama-vulkan-binary"),
onLlamaVulkanDownloadProgress: registerListener(
"llama-vulkan-download-progress",
(callback) => (_event, data) => callback(data)
),
getLogLevel: () => ipcRenderer.invoke("get-log-level"),
onLogLevelChanged: registerListener("log-level-changed", (callback) => () => callback()),
log: (entry) => ipcRenderer.invoke("app-log", entry),
// ydotool status check
getYdotoolStatus: () => ipcRenderer.invoke("get-ydotool-status"),
// Debug logging management
getDebugState: () => ipcRenderer.invoke("get-debug-state"),
setDebugLogging: (enabled) => ipcRenderer.invoke("set-debug-logging", enabled),
getModelRegistryUpdates: () => ipcRenderer.invoke("get-model-registry-updates"),
setModelRegistryUpdates: (enabled) => ipcRenderer.invoke("set-model-registry-updates", enabled),
openLogsFolder: () => ipcRenderer.invoke("open-logs-folder"),
// System settings helpers for microphone/audio permissions
requestMicrophoneAccess: () => ipcRenderer.invoke("request-microphone-access"),
checkMicrophoneAccess: () => ipcRenderer.invoke("check-microphone-access"),
getSystemDefaultMicrophone: (options) =>
ipcRenderer.invoke("get-system-default-microphone", options),
checkSystemAudioAccess: () => ipcRenderer.invoke("check-system-audio-access"),
verifySystemAudioAccess: () => ipcRenderer.invoke("permission-guide-verify-system-audio"),
requestSystemAudioAccess: () => ipcRenderer.invoke("request-system-audio-access"),
openMicrophoneSettings: () => ipcRenderer.invoke("open-microphone-settings"),
openSoundInputSettings: () => ipcRenderer.invoke("open-sound-input-settings"),
openAccessibilitySettings: () => ipcRenderer.invoke("open-accessibility-settings"),
openSystemAudioSettings: () => ipcRenderer.invoke("open-system-audio-settings"),
openScreenRecordingSettings: () => ipcRenderer.invoke("open-screen-recording-settings"),
openLoginItemsSettings: () => ipcRenderer.invoke("open-login-items-settings"),
checkScreenRecordingAccess: () => ipcRenderer.invoke("check-screen-recording-access"),
requestScreenRecordingAccess: () => ipcRenderer.invoke("request-screen-recording-access"),
captureScreenContext: () => ipcRenderer.invoke("capture-screen-context"),
setScreenContextEnabled: (enabled) => ipcRenderer.invoke("screen-context-set-enabled", enabled),
toggleMediaPlayback: () => ipcRenderer.invoke("toggle-media-playback"),
pauseMediaPlayback: () => ipcRenderer.invoke("pause-media-playback"),
resumeMediaPlayback: () => ipcRenderer.invoke("resume-media-playback"),
getModelCacheRoot: () => ipcRenderer.invoke("get-model-cache-root"),
openWhisperModelsFolder: () => ipcRenderer.invoke("open-whisper-models-folder"),
authClearSession: () => ipcRenderer.invoke("auth-clear-session"),
authGetToken: () => ipcRenderer.invoke("auth-get-token"),
authGetTokenState: () => ipcRenderer.invoke("auth-get-token-state"),
authSetToken: (token, expectedGeneration) =>
ipcRenderer.invoke("auth-set-token", token, expectedGeneration),
onAuthTokenStateChanged: registerListener(
"auth-token-state-changed",
(callback) => (_event, state) => callback(state)
),
// OpenWhispr Cloud API
cloudHealthCheck: () => ipcRenderer.invoke("cloud-health-check"),
cloudTranscribe: (audioBuffer, opts) => ipcRenderer.invoke("cloud-transcribe", audioBuffer, opts),
cancelCloudTranscription: () => ipcRenderer.send("cloud-transcribe-cancel"),
cloudReason: (text, opts) => ipcRenderer.invoke("cloud-reason", text, opts),
cancelCloudReason: () => ipcRenderer.send("cloud-reason-cancel"),
cloudStreamingUsage: (text, audioDurationSeconds, opts) =>
ipcRenderer.invoke("cloud-streaming-usage", text, audioDurationSeconds, opts),
cloudUsage: () => ipcRenderer.invoke("cloud-usage"),
cloudCheckout: (opts) => ipcRenderer.invoke("cloud-checkout", opts),
cloudBillingPortal: () => ipcRenderer.invoke("cloud-billing-portal"),
cloudSwitchPlan: (opts) => ipcRenderer.invoke("cloud-switch-plan", opts),
cloudPreviewSwitch: (opts) => ipcRenderer.invoke("cloud-preview-switch", opts),
cloudApiRequest: (opts) => ipcRenderer.invoke("cloud-api-request", opts),
getSttConfig: () => ipcRenderer.invoke("get-stt-config"),
getWorkspacePolicy: (accountId, expectedAuthGeneration) =>
ipcRenderer.invoke("get-workspace-policy", accountId, expectedAuthGeneration),
onWorkspacePolicyChanged: (callback) => {
const listener = (_event, snapshot) => callback(snapshot);
ipcRenderer.on("workspace-policy-changed", listener);
return () => ipcRenderer.removeListener("workspace-policy-changed", listener);
},
getNoteRecordingConfig: () => ipcRenderer.invoke("get-note-recording-config"),
// Cloud audio file transcription
transcribeAudioFileCloud: (filePath, options) =>
ipcRenderer.invoke("transcribe-audio-file-cloud", filePath, options),
cancelUploadTranscription: (requestId) =>
ipcRenderer.invoke("cancel-upload-transcription", requestId),
transcribeAudioFileByok: (options) => ipcRenderer.invoke("transcribe-audio-file-byok", options),
onUploadTranscriptionProgress: registerListener(
"upload-transcription-progress",
(callback) => (_event, data) => callback(data)
),
// Referral stats
getReferralStats: () => ipcRenderer.invoke("get-referral-stats"),
sendReferralInvite: (email) => ipcRenderer.invoke("send-referral-invite", email),
getReferralInvites: () => ipcRenderer.invoke("get-referral-invites"),
// Assembly AI Streaming
assemblyAiStreamingWarmup: (options) =>
ipcRenderer.invoke("assemblyai-streaming-warmup", options),
assemblyAiStreamingStart: (options) => ipcRenderer.invoke("assemblyai-streaming-start", options),
assemblyAiStreamingSend: (audioBuffer) =>
ipcRenderer.send("assemblyai-streaming-send", audioBuffer),
assemblyAiStreamingForceEndpoint: () => ipcRenderer.send("assemblyai-streaming-force-endpoint"),
assemblyAiStreamingStop: () => ipcRenderer.invoke("assemblyai-streaming-stop"),
assemblyAiStreamingStatus: () => ipcRenderer.invoke("assemblyai-streaming-status"),
onAssemblyAiPartialTranscript: registerListener(
"assemblyai-partial-transcript",
(callback) => (_event, text) => callback(text)
),
onAssemblyAiFinalTranscript: registerListener(
"assemblyai-final-transcript",
(callback) => (_event, text) => callback(text)
),
onAssemblyAiError: registerListener(
"assemblyai-error",
(callback) => (_event, error) => callback(error)
),
onAssemblyAiSessionEnd: registerListener(
"assemblyai-session-end",
(callback) => (_event, data) => callback(data)
),
// Deepgram Streaming
deepgramStreamingWarmup: (options) => ipcRenderer.invoke("deepgram-streaming-warmup", options),
deepgramStreamingStart: (options) => ipcRenderer.invoke("deepgram-streaming-start", options),
deepgramStreamingSend: (audioBuffer) => ipcRenderer.send("deepgram-streaming-send", audioBuffer),
deepgramStreamingFinalize: () => ipcRenderer.send("deepgram-streaming-finalize"),
deepgramStreamingStop: () => ipcRenderer.invoke("deepgram-streaming-stop"),
deepgramStreamingStatus: () => ipcRenderer.invoke("deepgram-streaming-status"),
onDeepgramPartialTranscript: registerListener(
"deepgram-partial-transcript",
(callback) => (_event, text) => callback(text)
),
onDeepgramFinalTranscript: registerListener(
"deepgram-final-transcript",
(callback) => (_event, text) => callback(text)
),
onDeepgramError: registerListener(
"deepgram-error",
(callback) => (_event, error) => callback(error)
),
onDeepgramSessionEnd: registerListener(
"deepgram-session-end",
(callback) => (_event, data) => callback(data)
),
// Gemini Live Streaming
geminiStreamingWarmup: (options) => ipcRenderer.invoke("gemini-streaming-warmup", options),
geminiStreamingStart: (options) => ipcRenderer.invoke("gemini-streaming-start", options),
geminiStreamingSend: (audioBuffer) => ipcRenderer.send("gemini-streaming-send", audioBuffer),
geminiStreamingFinalize: () => ipcRenderer.send("gemini-streaming-finalize"),
geminiStreamingStop: () => ipcRenderer.invoke("gemini-streaming-stop"),
geminiStreamingStatus: () => ipcRenderer.invoke("gemini-streaming-status"),
onGeminiPartialTranscript: registerListener(
"gemini-partial-transcript",
(callback) => (_event, text) => callback(text)
),
onGeminiFinalTranscript: registerListener(
"gemini-final-transcript",
(callback) => (_event, text) => callback(text)
),
onGeminiError: registerListener("gemini-error", (callback) => (_event, error) => callback(error)),
onGeminiSessionEnd: registerListener(
"gemini-session-end",
(callback) => (_event, data) => callback(data)
),
// Corti streaming (BYOK)
cortiStreamingWarmup: (options) => ipcRenderer.invoke("corti-streaming-warmup", options),
cortiStreamingStart: (options) => ipcRenderer.invoke("corti-streaming-start", options),
cortiStreamingSend: (audioBuffer) => ipcRenderer.send("corti-streaming-send", audioBuffer),
cortiStreamingFinalize: () => ipcRenderer.send("corti-streaming-finalize"),
cortiStreamingStop: () => ipcRenderer.invoke("corti-streaming-stop"),
cortiStreamingStatus: () => ipcRenderer.invoke("corti-streaming-status"),
onCortiPartialTranscript: registerListener(
"corti-partial-transcript",
(callback) => (_event, text) => callback(text)
),
onCortiFinalTranscript: registerListener(
"corti-final-transcript",
(callback) => (_event, text) => callback(text)
),
onCortiError: registerListener("corti-error", (callback) => (_event, error) => callback(error)),
onCortiSessionEnd: registerListener(
"corti-session-end",
(callback) => (_event, data) => callback(data)
),
// Meeting transcription (streaming, dual-channel)
meetingTranscriptionPrepare: (options) =>
ipcRenderer.invoke("meeting-transcription-prepare", options),
meetingTranscriptionStart: (options) =>
ipcRenderer.invoke("meeting-transcription-start", options),
meetingTranscriptionSend: (buffer, source, capturedAt) =>
ipcRenderer.send("meeting-transcription-send", buffer, source, capturedAt),
meetingTranscriptionSetSystemAudioAvailable: (sessionId, available) =>
ipcRenderer.invoke("meeting-transcription-set-system-audio-available", sessionId, available),
meetingTranscriptionStop: (expectedSessionId) =>
ipcRenderer.invoke("meeting-transcription-stop", expectedSessionId),
meetingTranscriptionCancel: () => ipcRenderer.invoke("meeting-transcription-cancel"),
onMeetingTranscriptionSegment: registerListener(
"meeting-transcription-segment",
(callback) => (_event, data) => callback(data)
),
onMeetingSpeakerIdentified: registerListener(
"meeting-speaker-identified",
(callback) => (_event, data) => callback(data)
),
onMeetingSpeakersMerged: registerListener(
"meeting-speakers-merged",
(callback) => (_event, data) => callback(data)
),
onMeetingSessionSpeakerConfigUpdated: registerListener(
"meeting-session-speaker-config-updated",
(callback) => (_event, data) => callback(data)
),
onMeetingTranscriptionError: registerListener(
"meeting-transcription-error",
(callback) => (_event, data) => callback(data)
),
onMeetingTranscriptionFatalError: registerListener(
"meeting-transcription-fatal-error",
(callback) => (_event, data) => callback(data)
),
onMeetingSystemAudioSilent: registerListener(
"meeting-system-audio-silent",
(callback) => (_event, data) => callback(data)
),
onMeetingSystemAudioDegraded: registerListener(
"meeting-system-audio-degraded",
(callback) => () => callback()
),
onMeetingSystemAudioInterrupted: registerListener(
"meeting-system-audio-interrupted",
(callback) => (_event, data) => callback(data)
),
onMeetingSystemAudioResumed: registerListener(
"meeting-system-audio-resumed",
(callback) => () => callback()
),
// Dictation realtime streaming
dictationRealtimeWarmup: (options) => ipcRenderer.invoke("dictation-realtime-warmup", options),
dictationRealtimeStart: (options) => ipcRenderer.invoke("dictation-realtime-start", options),
dictationRealtimeSend: (buffer) => ipcRenderer.send("dictation-realtime-send", buffer),
dictationRealtimeFinalize: () => ipcRenderer.invoke("dictation-realtime-finalize"),
dictationRealtimeStop: () => ipcRenderer.invoke("dictation-realtime-stop"),
onDictationRealtimePartial: registerListener(
"dictation-realtime-partial",
(callback) => (_event, data) => callback(data)
),
onDictationRealtimeFinal: registerListener(
"dictation-realtime-final",
(callback) => (_event, data) => callback(data)
),
onDictationRealtimeLanguage: registerListener(
"dictation-realtime-language",
(callback) => (_event, data) => callback(data)
),
onDictationRealtimeError: registerListener(
"dictation-realtime-error",
(callback) => (_event, data) => callback(data)
),
onDictationRealtimeSessionEnd: registerListener(
"dictation-realtime-session-end",
(callback) => (_event, data) => callback(data)
),
// Usage limit events (for showing UpgradePrompt in ControlPanel)
notifyLimitReached: (data) => ipcRenderer.send("limit-reached", data),
onLimitReached: registerListener("limit-reached", (callback) => (_event, data) => callback(data)),
// Workspace invitation deep link
onWorkspaceInvitationToken: registerListener(
"workspace-invitation-token",
(callback) => (_event, token) => callback(token)
),
getPendingInvitationToken: () => ipcRenderer.invoke("get-pending-invitation-token"),
// Globe key listener for hotkey capture (macOS only)
onGlobeKeyPressed: (callback) => {
const listener = () => callback?.();
ipcRenderer.on("globe-key-pressed", listener);
return () => ipcRenderer.removeListener("globe-key-pressed", listener);
},
onGlobeKeyReleased: (callback) => {
const listener = () => callback?.();
ipcRenderer.on("globe-key-released", listener);
return () => ipcRenderer.removeListener("globe-key-released", listener);
},
// Hotkey registration events (for notifying user when hotkey fails)
onHotkeyFallbackUsed: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("hotkey-fallback-used", listener);
return () => ipcRenderer.removeListener("hotkey-fallback-used", listener);
},
onHotkeyRegistrationFailed: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("hotkey-registration-failed", listener);
return () => ipcRenderer.removeListener("hotkey-registration-failed", listener);
},
onApiKeyUpdated: registerListener(
"api-key-updated",
(callback) => (_event, storeKey) => callback(storeKey)
),
onSettingUpdated: (callback) => {
const listener = (_event, data) => callback?.(data);
ipcRenderer.on("setting-updated", listener);
return () => ipcRenderer.removeListener("setting-updated", listener);
},
onDictationKeyActive: (callback) => {
const listener = (_event, key) => callback?.(key);
ipcRenderer.on("dictation-key-active", listener);
return () => ipcRenderer.removeListener("dictation-key-active", listener);
},
onWindowsPushToTalkUnavailable: registerListener("windows-ptt-unavailable"),
onLinuxPttPermissionDenied: registerListener(
"linux-ptt-permission-denied",
(callback) => () => callback()
),
// Settings shortcut (Cmd+, / Ctrl+,)
onShowSettings: registerListener("show-settings", (callback) => () => callback()),
getPendingSettingsSection: () => ipcRenderer.invoke("get-pending-settings-section"),
// Accessibility permission events (macOS)
onAccessibilityMissing: (callback) => {
const listener = () => callback?.();
ipcRenderer.on("accessibility-missing", listener);
return () => ipcRenderer.removeListener("accessibility-missing", listener);
},
checkAccessibilityTrusted: () => ipcRenderer.invoke("check-accessibility-trusted"),
// Notify main process of activation mode changes (for Windows Push-to-Talk)
notifyActivationModeChanged: (mode) => ipcRenderer.send("activation-mode-changed", mode),
notifyHotkeyChanged: (hotkey) => ipcRenderer.send("hotkey-changed", hotkey),
registerMeetingHotkey: (hotkey) => ipcRenderer.invoke("register-meeting-hotkey", hotkey),
// Floating icon auto-hide
notifyFloatingIconAutoHideChanged: (enabled) =>
ipcRenderer.send("floating-icon-auto-hide-changed", enabled),
onFloatingIconAutoHideChanged: registerListener(
"floating-icon-auto-hide-changed",
(callback) => (_event, enabled) => callback(enabled)
),
// Panel start position
notifyPanelStartPositionChanged: (position) =>
ipcRenderer.send("panel-start-position-changed", position),
// Start minimized
notifyStartMinimizedChanged: (enabled) => ipcRenderer.send("start-minimized-changed", enabled),
// Auto-start management
getAutoStartEnabled: () => ipcRenderer.invoke("get-auto-start-enabled"),
setAutoStartEnabled: (enabled) => ipcRenderer.invoke("set-auto-start-enabled", enabled),
// Agent mode
updateVoiceAgentHotkey: (hotkey) => ipcRenderer.invoke("update-voice-agent-hotkey", hotkey),
getVoiceAgentKey: () => ipcRenderer.invoke("get-voice-agent-key"),
updateTranslationHotkey: (hotkey) => ipcRenderer.invoke("update-translation-hotkey", hotkey),
getTranslationKey: () => ipcRenderer.invoke("get-translation-key"),
onPreviewText: registerListener("preview-text", (callback) => (_event, text) => callback(text)),
onPreviewAppend: registerListener(
"preview-append",
(callback) => (_event, text) => callback(text)
),
onPreviewHold: registerListener(
"preview-hold",
(callback) => (_event, payload) => callback(payload)
),
onPreviewResult: registerListener(
"preview-result",
(callback) => (_event, payload) => callback(payload)
),
onPreviewHide: registerListener("preview-hide", (callback) => () => callback()),
startDictationPreview: (opts) => ipcRenderer.invoke("start-dictation-preview", opts),
stopDictationPreview: (opts) => ipcRenderer.invoke("stop-dictation-preview", opts),
dismissDictationPreview: () => ipcRenderer.invoke("dismiss-dictation-preview"),
updateDictationPreview: (text) => ipcRenderer.invoke("update-dictation-preview", text),
completeDictationPreview: (payload) => ipcRenderer.invoke("complete-dictation-preview", payload),
hideDictationPreview: () => ipcRenderer.invoke("hide-dictation-preview"),
sendDictationPreviewAudio: (data) => ipcRenderer.send("dictation-preview-audio", data),
acquireRecordingLock: (pipeline) => ipcRenderer.invoke("acquire-recording-lock", pipeline),
releaseRecordingLock: (pipeline) => ipcRenderer.invoke("release-recording-lock", pipeline),
// Agent cloud streaming (event-based for real-time chunks)
startAgentStream: (requestId, messages, opts) =>
ipcRenderer.send("cloud-agent-stream-start", requestId, messages, opts),
cancelAgentStream: (requestId) => ipcRenderer.send("cloud-agent-stream-cancel", requestId),
onAgentStreamChunk: registerListener(
"cloud-agent-stream-chunk",
(callback) => (_event, payload) => callback(payload)
),
onAgentStreamError: registerListener(
"cloud-agent-stream-error",
(callback) => (_event, payload) => callback(payload)
),
onAgentStreamEnd: registerListener(
"cloud-agent-stream-end",
(callback) => (_event, payload) => callback(payload)
),
// Agent cloud tools
productHelp: (id, input) => ipcRenderer.invoke("product-help", id, input),
cancelProductHelp: (id) => ipcRenderer.send("product-help-cancel", id),
productHelpBasics: () => ipcRenderer.invoke("product-help-basics"),
agentWebSearch: (query, numResults) => ipcRenderer.invoke("agent-web-search", query, numResults),
agentOpenNote: (noteId) => ipcRenderer.invoke("agent-open-note", noteId),
// Agent conversation persistence
claimAgentConversation: (id, ownerId) =>
ipcRenderer.invoke("claim-agent-conversation", id, ownerId),
releaseAgentConversation: (id, ownerId) =>
ipcRenderer.invoke("release-agent-conversation", id, ownerId),
onAgentConversationReleased: registerListener(
"agent-conversation-released",
(callback) => (_event, payload) => callback(payload)
),
createAgentConversation: (title, noteId, spaceId, folderId) =>
ipcRenderer.invoke("db-create-agent-conversation", title, noteId, spaceId, folderId),
getAgentConversations: (limit) => ipcRenderer.invoke("db-get-agent-conversations", limit),
getAgentConversation: (id) => ipcRenderer.invoke("db-get-agent-conversation", id),
deleteAgentConversation: (id) => ipcRenderer.invoke("db-delete-agent-conversation", id),
updateAgentConversationTitle: (id, title) =>
ipcRenderer.invoke("db-update-agent-conversation-title", id, title),
addAgentMessage: (conversationId, role, content, metadata) =>
ipcRenderer.invoke("db-add-agent-message", conversationId, role, content, metadata),
updateAgentMessage: (id, content, metadata) =>
ipcRenderer.invoke("db-update-agent-message", id, content, metadata),
forkAgentConversation: (conversationId, throughMessageId, forkContext) =>
ipcRenderer.invoke("db-fork-agent-conversation", conversationId, throughMessageId, forkContext),
getAgentMessages: (conversationId) => ipcRenderer.invoke("db-get-agent-messages", conversationId),
getAgentConversationsWithPreview: (limit, offset, includeArchived) =>
ipcRenderer.invoke("db-get-agent-conversations-with-preview", limit, offset, includeArchived),
searchAgentConversations: (query, limit) =>
ipcRenderer.invoke("db-search-agent-conversations", query, limit),
getConversationsForNote: (noteId, limit) =>
ipcRenderer.invoke("db-get-conversations-for-note", noteId, limit),
getConversationsForContainer: (spaceId, folderId, limit) =>
ipcRenderer.invoke("db-get-conversations-for-container", spaceId, folderId, limit),
archiveAgentConversation: (id) => ipcRenderer.invoke("db-archive-agent-conversation", id),
unarchiveAgentConversation: (id) => ipcRenderer.invoke("db-unarchive-agent-conversation", id),
updateAgentConversationCloudId: (id, cloudId) =>
ipcRenderer.invoke("db-update-agent-conversation-cloud-id", id, cloudId),
semanticSearchConversations: (query, limit) =>
ipcRenderer.invoke("db-semantic-search-conversations", query, limit),
// Sync operations
getPendingNotes: (spaceKind) => ipcRenderer.invoke("db-get-pending-notes", spaceKind),
getPendingNoteDeletes: () => ipcRenderer.invoke("db-get-pending-note-deletes"),
getNoteByClientId: (clientNoteId) => ipcRenderer.invoke("db-get-note-by-client-id", clientNoteId),
getNoteForCloudRow: (clientNoteId, cloudId) =>
ipcRenderer.invoke("db-get-note-for-cloud-row", clientNoteId, cloudId),
upsertNoteFromCloud: (cloudNote, localFolderId, localSpaceId) =>
ipcRenderer.invoke("db-upsert-note-from-cloud", cloudNote, localFolderId, localSpaceId),
markNoteCreatesAttempted: (clientNoteIds) =>
ipcRenderer.invoke("db-mark-note-creates-attempted", clientNoteIds),
acknowledgeNoteCreate: (id, snapshot, cloudId, cloudUpdatedAt, ownerUserId, options) =>
ipcRenderer.invoke(
"db-acknowledge-note-create",
id,
snapshot,
cloudId,
cloudUpdatedAt,
ownerUserId,
options
),
markNoteSyncedIfUnchanged: (
id,
snapshot,
expectedCloudId,
cloudUpdatedAt,
ownerUserId,
cloudRevision,
cloudNote
) =>
ipcRenderer.invoke(
"db-mark-note-synced-if-unchanged",
id,
snapshot,
expectedCloudId,
cloudUpdatedAt,
ownerUserId,
cloudRevision,
cloudNote
),
setNoteCloudBase: (id, cloudUpdatedAt, cloudRevision, options) =>
ipcRenderer.invoke("db-set-note-cloud-base", id, cloudUpdatedAt, cloudRevision, options),
setNoteOwnerFromCloud: (id, ownerUserId) =>
ipcRenderer.invoke("db-set-note-owner-from-cloud", id, ownerUserId),
countTeamNotesMissingOwner: () => ipcRenderer.invoke("db-count-team-notes-missing-owner"),
countNotesMissingRevision: (spaceKind) =>
ipcRenderer.invoke("db-count-notes-missing-revision", spaceKind),
markNoteSyncError: (id) => ipcRenderer.invoke("db-mark-note-sync-error", id),
restoreNoteAfterDeniedDelete: (id) =>
ipcRenderer.invoke("db-restore-note-after-denied-delete", id),
hardDeleteNote: (id) => ipcRenderer.invoke("db-hard-delete-note", id),
getPendingFolders: (spaceKind) => ipcRenderer.invoke("db-get-pending-folders", spaceKind),
getFolderByClientId: (clientFolderId) =>
ipcRenderer.invoke("db-get-folder-by-client-id", clientFolderId),
upsertFolderFromCloud: (cloudFolder, localSpaceId) =>
ipcRenderer.invoke("db-upsert-folder-from-cloud", cloudFolder, localSpaceId),
acknowledgeFolderCreate: (
id,
snapshot,
expectedCloudId,
responseClientFolderId,
cloudId,
cloudUpdatedAt
) =>
ipcRenderer.invoke(
"db-acknowledge-folder-create",
id,
snapshot,
expectedCloudId,
responseClientFolderId,
cloudId,
cloudUpdatedAt
),
markFolderSyncedIfUnchanged: (id, snapshot, expectedCloudId) =>
ipcRenderer.invoke("db-mark-folder-synced-if-unchanged", id, snapshot, expectedCloudId),
getFolderIdMap: () => ipcRenderer.invoke("db-get-folder-id-map"),
getPendingFolderDeletes: () => ipcRenderer.invoke("db-get-pending-folder-deletes"),
restoreFolderAfterDeniedDelete: (id) =>
ipcRenderer.invoke("db-restore-folder-after-denied-delete", id),
hardDeleteFolder: (id) => ipcRenderer.invoke("db-hard-delete-folder", id),
relocateRevokedFolder: (id, privateSpaceId, preserveFolder) =>
ipcRenderer.invoke("db-relocate-revoked-folder", id, privateSpaceId, preserveFolder),
getPendingConversations: () => ipcRenderer.invoke("db-get-pending-conversations"),
getPendingConversationDeletes: () => ipcRenderer.invoke("db-get-pending-conversation-deletes"),
getConversationByClientId: (clientId) =>
ipcRenderer.invoke("db-get-conversation-by-client-id", clientId),
claimConversationFromCloud: (cloudConv, expectedAuthGeneration) =>
ipcRenderer.invoke("db-claim-conversation-from-cloud", cloudConv, expectedAuthGeneration),
upsertConversationFromCloud: (cloudConv, messages, snapshot, expectedAuthGeneration) =>
ipcRenderer.invoke(
"db-upsert-conversation-from-cloud",
cloudConv,
messages,
snapshot,
expectedAuthGeneration
),
markConversationCreateAttempted: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-mark-conversation-create-attempted", id, expectedAuthGeneration),
clearConversationCreateAttempted: (id) =>
ipcRenderer.invoke("db-clear-conversation-create-attempted", id),
acknowledgeConversationCreate: (id, snapshot, cloudId, accountId, authGeneration) =>
ipcRenderer.invoke(
"db-acknowledge-conversation-create",
id,
snapshot,
cloudId,
accountId,
authGeneration
),
markConversationSynced: (id, cloudId, snapshot, cloudUpdatedAt, accountId, authGeneration) =>
ipcRenderer.invoke(
"db-mark-conversation-synced",
id,
cloudId,
snapshot,
cloudUpdatedAt,
accountId,
authGeneration
),
replaceConversationClientId: (id, expectedClientId, accountId, authGeneration) =>
ipcRenderer.invoke(
"db-replace-conversation-client-id",
id,
expectedClientId,
accountId,
authGeneration
),
hardDeleteConversation: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-hard-delete-conversation", id, expectedAuthGeneration),
getPendingTranscriptions: () => ipcRenderer.invoke("db-get-pending-transcriptions"),
getTranscriptionByClientId: (clientId) =>
ipcRenderer.invoke("db-get-transcription-by-client-id", clientId),
upsertTranscriptionFromCloud: (cloudTranscription, expectedAuthGeneration) =>
ipcRenderer.invoke(
"db-upsert-transcription-from-cloud",
cloudTranscription,
expectedAuthGeneration
),
markTranscriptionSynced: (id, cloudId, expectedAuthGeneration) =>
ipcRenderer.invoke("db-mark-transcription-synced", id, cloudId, expectedAuthGeneration),
getPendingTranscriptionDeletes: () => ipcRenderer.invoke("db-get-pending-transcription-deletes"),
hardDeleteTranscription: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-hard-delete-transcription", id, expectedAuthGeneration),
getPendingDictionary: () => ipcRenderer.invoke("db-get-pending-dictionary"),
getPendingDictionaryDeletes: () => ipcRenderer.invoke("db-get-pending-dictionary-deletes"),
getDictionaryByClientId: (clientDictId) =>
ipcRenderer.invoke("db-get-dictionary-by-client-id", clientDictId),
upsertDictionaryFromCloud: (cloudEntry, expectedAuthGeneration) =>
ipcRenderer.invoke("db-upsert-dictionary-from-cloud", cloudEntry, expectedAuthGeneration),
markDictionarySynced: (
id,
cloudId,
serverUpdatedAt,
expectedWord,
expectedSource,
serverSource,
pushed,
expectedAuthGeneration
) =>
ipcRenderer.invoke(
"db-mark-dictionary-synced",
id,
cloudId,
serverUpdatedAt,
expectedWord,
expectedSource,
serverSource,
pushed,
expectedAuthGeneration
),
hardDeleteDictionary: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-hard-delete-dictionary", id, expectedAuthGeneration),
clearDictionaryCloudId: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-clear-dictionary-cloud-id", id, expectedAuthGeneration),
keepNewerDictionaryEdit: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-keep-newer-dictionary-edit", id, expectedAuthGeneration),
broadcastDictionaryUpdated: () => ipcRenderer.invoke("db-broadcast-dictionary-updated"),
getDictionaryEntriesByWords: (words) =>
ipcRenderer.invoke("db-get-dictionary-entries-by-words", words),
markDictionaryWordsShared: (words, sharedAt, options) =>
ipcRenderer.invoke("db-mark-dictionary-words-shared", words, sharedAt, options),
getMovedDictionaryWords: () => ipcRenderer.invoke("db-get-moved-dictionary-words"),
retireMovedDictionaryWords: (words, options) =>
ipcRenderer.invoke("db-retire-moved-dictionary-words", words, options),
reviveRetiredDictionaryWords: (words, options) =>
ipcRenderer.invoke("db-revive-retired-dictionary-words", words, options),
getSharedDictionary: (workspaceId) => ipcRenderer.invoke("db-shared-dictionary-get", workspaceId),
applySharedDictionaryPull: (workspaceId, change) =>
ipcRenderer.invoke("db-shared-dictionary-apply-pull", workspaceId, change),
replaceSharedDictionary: (workspaceId, full) =>
ipcRenderer.invoke("db-shared-dictionary-replace", workspaceId, full),
applySharedDictionaryHides: (workspaceId, change) =>
ipcRenderer.invoke("db-shared-dictionary-apply-hides", workspaceId, change),
replaceSharedDictionaryHides: (workspaceId, hidden) =>
ipcRenderer.invoke("db-shared-dictionary-replace-hides", workspaceId, hidden),
clearSharedDictionary: (workspaceId) =>
ipcRenderer.invoke("db-shared-dictionary-clear", workspaceId),
pruneSharedDictionary: (keepWorkspaceIds) =>
ipcRenderer.invoke("db-shared-dictionary-prune", keepWorkspaceIds),
setSharedDictionaryHints: (promptHints, hotwordHints) =>
ipcRenderer.invoke("dictionary-set-shared-hints", promptHints, hotwordHints),
getPendingSnippets: () => ipcRenderer.invoke("db-get-pending-snippets"),
getPendingSnippetDeletes: () => ipcRenderer.invoke("db-get-pending-snippet-deletes"),
getSnippetForCloudMerge: (cloudEntry, expectedAuthGeneration) =>
ipcRenderer.invoke("db-get-snippet-for-cloud-merge", cloudEntry, expectedAuthGeneration),
upsertSnippetFromCloud: (cloudEntry, expectedAuthGeneration) =>
ipcRenderer.invoke("db-upsert-snippet-from-cloud", cloudEntry, expectedAuthGeneration),
markSnippetSynced: (
id,
cloudId,
serverUpdatedAt,
expectedTrigger,
expectedReplacement,
expectedAuthGeneration
) =>
ipcRenderer.invoke(
"db-mark-snippet-synced",
id,
cloudId,
serverUpdatedAt,
expectedTrigger,
expectedReplacement,
expectedAuthGeneration
),
hardDeleteSnippet: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-hard-delete-snippet", id, expectedAuthGeneration),
clearSnippetCloudId: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-clear-snippet-cloud-id", id, expectedAuthGeneration),
keepNewerSnippetEdit: (id, expectedAuthGeneration) =>
ipcRenderer.invoke("db-keep-newer-snippet-edit", id, expectedAuthGeneration),
broadcastSnippetsUpdated: () => ipcRenderer.invoke("db-broadcast-snippets-updated"),
// Google Calendar
gcalStartOAuth: (requestId) => ipcRenderer.invoke("gcal-start-oauth", requestId),
calendarCancelConnect: (requestId) => ipcRenderer.invoke("calendar-cancel-connect", requestId),
gcalDisconnect: (email) => ipcRenderer.invoke("gcal-disconnect", email),
gcalGetConnectionStatus: () => ipcRenderer.invoke("gcal-get-connection-status"),
gcalGetCalendars: () => ipcRenderer.invoke("gcal-get-calendars"),
gcalSetCalendarSelection: (calendarId, isSelected) =>
ipcRenderer.invoke("gcal-set-calendar-selection", calendarId, isSelected),
gcalSetPrimaryOnly: (value) => ipcRenderer.invoke("gcal-set-primary-only", value),
gcalSyncEvents: () => ipcRenderer.invoke("gcal-sync-events"),
gcalGetUpcomingEvents: (windowMinutes, options) =>
ipcRenderer.invoke("gcal-get-upcoming-events", windowMinutes, options),
connectorStatus: () => ipcRenderer.invoke("connector-status"),
connectorPrepare: (connectorId, action, args) =>
ipcRenderer.invoke("connector-prepare", connectorId, action, args),
connectorQuery: (connectorId, action, args) =>
ipcRenderer.invoke("connector-query", connectorId, action, args),
connectorCommit: (actionId, edits) => ipcRenderer.invoke("connector-commit", actionId, edits),
connectorCancel: (actionId, reason) => ipcRenderer.invoke("connector-cancel", actionId, reason),
connectorRunDirect: (connectorId, action, args, runId) =>
ipcRenderer.invoke("connector-run-direct", connectorId, action, args, runId),
connectorRecentActions: (connectorId, limit) =>
ipcRenderer.invoke("connector-recent-actions", connectorId, limit),
connectorFindContacts: (query) => ipcRenderer.invoke("connector-find-contacts", query),
connectorNoteAttendees: (request) => ipcRenderer.invoke("connector-note-attendees", request),
connectorConnect: (connectorId) => ipcRenderer.invoke("connector-connect", connectorId),
connectorCancelConnect: (connectorId) =>
ipcRenderer.invoke("connector-cancel-connect", connectorId),
connectorDisconnect: (connectorId) => ipcRenderer.invoke("connector-disconnect", connectorId),
onConnectorStatusChanged: (callback) => {
const listener = (_event, statuses) => callback(statuses);
ipcRenderer.on("connector-status-changed", listener);
return () => ipcRenderer.removeListener("connector-status-changed", listener);
},
// A connect's progress, such as the code GitHub's device flow shows.
onConnectorConnectProgress: (callback) => {
const listener = (_event, progress) => callback(progress);
ipcRenderer.on("connector-connect-progress", listener);
return () => ipcRenderer.removeListener("connector-connect-progress", listener);
},
calendarGetAvailability: (request) => ipcRenderer.invoke("calendar-get-availability", request),
gcalGetEvent: (eventId) => ipcRenderer.invoke("gcal-get-event", eventId),
calendarGetLinkableEvents: () => ipcRenderer.invoke("calendar-get-linkable-events"),
// Microsoft Calendar
mcalStartOAuth: (requestId) => ipcRenderer.invoke("mcal-start-oauth", requestId),
mcalDisconnect: (email) => ipcRenderer.invoke("mcal-disconnect", email),
mcalGetConnectionStatus: () => ipcRenderer.invoke("mcal-get-connection-status"),
mcalSetPrimaryOnly: (value) => ipcRenderer.invoke("mcal-set-primary-only", value),
// Apple Calendar (macOS EventKit)
acalConnect: (requestId) => ipcRenderer.invoke("acal-connect", requestId),
acalDisconnect: () => ipcRenderer.invoke("acal-disconnect"),
acalGetConnectionStatus: () => ipcRenderer.invoke("acal-get-connection-status"),
openCalendarPrivacySettings: () => ipcRenderer.invoke("open-calendar-privacy-settings"),
// Contacts
searchContacts: (query) => ipcRenderer.invoke("search-contacts", query),
searchInviteContacts: (query, exclude) =>
ipcRenderer.invoke("search-invite-contacts", query, exclude),
upsertContact: (contact) => ipcRenderer.invoke("upsert-contact", contact),
getMD5Hash: (text) => ipcRenderer.invoke("get-md5-hash", text),
// Google Calendar event listeners
onGcalConnectionChanged: registerListener(
"gcal-connection-changed",
(callback) => (_event, data) => callback(data)
),
onGcalEventsSynced: registerListener(
"gcal-events-synced",
(callback) => (_event, data) => callback(data)
),
// Microsoft Calendar event listeners
onMcalConnectionChanged: registerListener(
"mcal-connection-changed",
(callback) => (_event, data) => callback(data)
),
onMcalEventsSynced: registerListener(
"mcal-events-synced",
(callback) => (_event, data) => callback(data)
),
// Apple Calendar event listeners
onAcalConnectionChanged: registerListener(
"acal-connection-changed",
(callback) => (_event, data) => callback(data)
),
onAcalEventsSynced: registerListener(
"acal-events-synced",
(callback) => (_event, data) => callback(data)
),
// Meeting detection
syncNotificationPreferences: (prefs) =>
ipcRenderer.invoke("sync-notification-preferences", prefs),
setSpeakerDiarizationEnabled: (enabled) =>
ipcRenderer.invoke("meeting-set-speaker-diarization-enabled", { enabled }),
setMeetingSessionSpeakerConfig: (config) =>
ipcRenderer.invoke("meeting-set-session-speaker-config", config),
getWhisperVadConfig: () => ipcRenderer.invoke("whisper-vad-get-config"),
setWhisperVadConfig: (config) => ipcRenderer.invoke("whisper-vad-set-config", config),
onMeetingNotificationData: registerListener(
"meeting-notification-data",
(callback) => (_event, data) => callback(data)
),
onMeetingAutoEndRequested: registerListener(
"meeting-auto-end-requested",
(callback) => (_event, data) => callback(data)
),
getMeetingNotificationDestination: () =>
ipcRenderer.invoke("get-meeting-notification-destination"),
selectMeetingNotificationFolder: (folder) =>
ipcRenderer.invoke("select-meeting-notification-folder", folder),
createMeetingNotificationFolder: (request) =>
ipcRenderer.invoke("create-meeting-notification-folder", request),
onMeetingNotificationFolderCreated: registerListener(
"meeting-notification-folder-created",
(callback) => (_event, data) => callback(data)
),
setMeetingNotificationSurface: (state) =>
ipcRenderer.invoke("set-meeting-notification-surface", state),
onMeetingNotificationSurfaceClosed: registerListener(
"meeting-notification-surface-closed",
(callback) => (_event, data) => callback(data)
),
onMeetingNotificationSurfaceResized: registerListener(
"meeting-notification-surface-resized",
(callback) => (_event, data) => callback(data)
),
getMeetingNotificationData: () => ipcRenderer.invoke("get-meeting-notification-data"),
meetingNotificationReady: () => ipcRenderer.invoke("meeting-notification-ready"),
meetingNotificationRespond: (detectionId, action, options) =>
ipcRenderer.invoke("meeting-notification-respond", detectionId, action, options),
confirmMeetingNoteNavigation: (navigationId, status) =>
ipcRenderer.invoke("confirm-meeting-note-navigation", navigationId, status),
joinCalendarMeeting: (eventId) => ipcRenderer.invoke("join-calendar-meeting", eventId),
startManualMeeting: () => ipcRenderer.invoke("start-manual-meeting"),
getPendingMeetingNoteNavigation: () => ipcRenderer.invoke("get-pending-meeting-note-navigation"),
onMeetingNoteNavigationPending: registerListener(
"meeting-note-navigation-pending",
(callback) => () => callback()
),
getPendingNoteNavigation: () => ipcRenderer.invoke("get-pending-note-navigation"),
onNoteNavigationPending: registerListener(
"note-navigation-pending",
(callback) => () => callback()
),
});