@todesktop/plugin-recall 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +271 -0
- package/dist/main.d.ts +23 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +694 -0
- package/dist/main.js.map +1 -0
- package/dist/preload.d.ts +136 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +218 -0
- package/dist/preload.js.map +1 -0
- package/dist/shared.d.ts +139 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +35 -0
- package/dist/shared.js.map +1 -0
- package/dist/store.d.ts +128 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +231 -0
- package/dist/store.js.map +1 -0
- package/package.json +71 -0
- package/src/main.ts +474 -0
- package/src/preload.ts +237 -0
- package/src/shared.ts +204 -0
- package/src/store.ts +266 -0
- package/tsconfig.json +11 -0
package/src/preload.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToDesktop Recall Desktop SDK Plugin - Preload Script
|
|
3
|
+
*
|
|
4
|
+
* This file runs in the renderer process with Node.js access and exposes
|
|
5
|
+
* safe APIs to the web application through the contextBridge.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { contextBridge, ipcRenderer } from 'electron';
|
|
9
|
+
import {
|
|
10
|
+
IPC_CHANNELS,
|
|
11
|
+
ApiResponse,
|
|
12
|
+
PluginStatus,
|
|
13
|
+
StartRecordingRequest,
|
|
14
|
+
StopRecordingRequest,
|
|
15
|
+
PauseRecordingRequest,
|
|
16
|
+
ResumeRecordingRequest,
|
|
17
|
+
UploadRecordingRequest,
|
|
18
|
+
PermissionType,
|
|
19
|
+
RecallSdkConfig,
|
|
20
|
+
PrepareDesktopAudioResponse,
|
|
21
|
+
RecallSdkEventType
|
|
22
|
+
} from './shared';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Recall Desktop SDK API exposed to the renderer process
|
|
26
|
+
*/
|
|
27
|
+
const recallDesktopApi = {
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the Recall SDK
|
|
30
|
+
* @returns Promise resolving to initialization result
|
|
31
|
+
*/
|
|
32
|
+
async initSdk(): Promise<ApiResponse> {
|
|
33
|
+
return ipcRenderer.invoke(IPC_CHANNELS.INIT_SDK);
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Shutdown the Recall SDK
|
|
38
|
+
* @returns Promise resolving to shutdown result
|
|
39
|
+
*/
|
|
40
|
+
async shutdownSdk(): Promise<ApiResponse> {
|
|
41
|
+
return ipcRenderer.invoke(IPC_CHANNELS.SHUTDOWN_SDK);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get current plugin and SDK status
|
|
46
|
+
* @returns Promise resolving to plugin status
|
|
47
|
+
*/
|
|
48
|
+
async getStatus(): Promise<PluginStatus> {
|
|
49
|
+
return ipcRenderer.invoke(IPC_CHANNELS.GET_STATUS);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Start recording a meeting
|
|
54
|
+
* @param windowId The meeting window ID
|
|
55
|
+
* @param uploadToken Upload token from your backend
|
|
56
|
+
* @returns Promise resolving to recording start result
|
|
57
|
+
*/
|
|
58
|
+
async startRecording(windowId: string, uploadToken: string): Promise<ApiResponse> {
|
|
59
|
+
const request: StartRecordingRequest = { windowId, uploadToken };
|
|
60
|
+
return ipcRenderer.invoke(IPC_CHANNELS.START_RECORDING, request);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Stop recording a meeting
|
|
65
|
+
* @param windowId The meeting window ID
|
|
66
|
+
* @returns Promise resolving to recording stop result
|
|
67
|
+
*/
|
|
68
|
+
async stopRecording(windowId: string): Promise<ApiResponse> {
|
|
69
|
+
const request: StopRecordingRequest = { windowId };
|
|
70
|
+
return ipcRenderer.invoke(IPC_CHANNELS.STOP_RECORDING, request);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Pause recording a meeting
|
|
75
|
+
* @param windowId The meeting window ID
|
|
76
|
+
* @returns Promise resolving to recording pause result
|
|
77
|
+
*/
|
|
78
|
+
async pauseRecording(windowId: string): Promise<ApiResponse> {
|
|
79
|
+
const request: PauseRecordingRequest = { windowId };
|
|
80
|
+
return ipcRenderer.invoke(IPC_CHANNELS.PAUSE_RECORDING, request);
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resume recording a meeting
|
|
85
|
+
* @param windowId The meeting window ID
|
|
86
|
+
* @returns Promise resolving to recording resume result
|
|
87
|
+
*/
|
|
88
|
+
async resumeRecording(windowId: string): Promise<ApiResponse> {
|
|
89
|
+
const request: ResumeRecordingRequest = { windowId };
|
|
90
|
+
return ipcRenderer.invoke(IPC_CHANNELS.RESUME_RECORDING, request);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Upload a completed recording
|
|
95
|
+
* @param windowId The meeting window ID
|
|
96
|
+
* @returns Promise resolving to upload start result
|
|
97
|
+
*/
|
|
98
|
+
async uploadRecording(windowId: string): Promise<ApiResponse> {
|
|
99
|
+
const request: UploadRecordingRequest = { windowId };
|
|
100
|
+
return ipcRenderer.invoke(IPC_CHANNELS.UPLOAD_RECORDING, request);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Prepare desktop audio recording for non-meeting audio capture
|
|
105
|
+
* @returns Promise resolving to desktop audio preparation result
|
|
106
|
+
*/
|
|
107
|
+
async prepareDesktopAudioRecording(): Promise<ApiResponse<PrepareDesktopAudioResponse>> {
|
|
108
|
+
return ipcRenderer.invoke(IPC_CHANNELS.PREPARE_DESKTOP_AUDIO);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Request a specific permission from the user
|
|
113
|
+
* @param permission The permission to request
|
|
114
|
+
* @returns Promise resolving to permission request result
|
|
115
|
+
*/
|
|
116
|
+
async requestPermission(permission: PermissionType): Promise<ApiResponse> {
|
|
117
|
+
return ipcRenderer.invoke(IPC_CHANNELS.REQUEST_PERMISSION, permission);
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Update plugin configuration
|
|
122
|
+
* @param config Configuration updates
|
|
123
|
+
* @returns Promise resolving to update result
|
|
124
|
+
*/
|
|
125
|
+
async setConfig(config: Partial<RecallSdkConfig>): Promise<ApiResponse> {
|
|
126
|
+
return ipcRenderer.invoke(IPC_CHANNELS.SET_CONFIG, config);
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get current plugin configuration
|
|
131
|
+
* @returns Promise resolving to current configuration
|
|
132
|
+
*/
|
|
133
|
+
async getConfig(): Promise<ApiResponse<RecallSdkConfig>> {
|
|
134
|
+
return ipcRenderer.invoke(IPC_CHANNELS.GET_CONFIG);
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Subscribe to SDK events
|
|
139
|
+
* @param eventType The type of event to listen for
|
|
140
|
+
* @param callback Function to call when event occurs
|
|
141
|
+
* @returns Function to unsubscribe from the event
|
|
142
|
+
*/
|
|
143
|
+
addEventListener(eventType: RecallSdkEventType, callback: (data: any) => void): () => void {
|
|
144
|
+
const channel = `recall-desktop:event:${eventType}`;
|
|
145
|
+
|
|
146
|
+
// Fire-and-forget subscribe to main
|
|
147
|
+
ipcRenderer.invoke(IPC_CHANNELS.SUBSCRIBE_EVENTS, eventType).catch(console.error);
|
|
148
|
+
|
|
149
|
+
const listener = (_event: any, data: any) => {
|
|
150
|
+
callback(data);
|
|
151
|
+
};
|
|
152
|
+
ipcRenderer.on(channel, listener);
|
|
153
|
+
|
|
154
|
+
// Return unsubscribe function
|
|
155
|
+
return () => {
|
|
156
|
+
ipcRenderer.removeListener(channel, listener);
|
|
157
|
+
ipcRenderer.invoke(IPC_CHANNELS.UNSUBSCRIBE_EVENTS, eventType).catch(console.error);
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get plugin version
|
|
163
|
+
* @returns Plugin version string
|
|
164
|
+
*/
|
|
165
|
+
getVersion(): string {
|
|
166
|
+
return '1.0.0';
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Convenience method for handling meeting detection events
|
|
171
|
+
* @param callback Function to call when a meeting is detected
|
|
172
|
+
* @returns Function to unsubscribe from the event
|
|
173
|
+
*/
|
|
174
|
+
onMeetingDetected(callback: (meetingData: any) => void): () => void {
|
|
175
|
+
return this.addEventListener('meeting-detected', callback);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Convenience method for handling recording state changes
|
|
180
|
+
* @param callback Function to call when recording state changes
|
|
181
|
+
* @returns Function to unsubscribe from the event
|
|
182
|
+
*/
|
|
183
|
+
onRecordingStateChange(callback: (stateData: any) => void): () => void {
|
|
184
|
+
return this.addEventListener('sdk-state-change', callback);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Convenience method for handling upload progress updates
|
|
189
|
+
* @param callback Function to call when upload progress updates
|
|
190
|
+
* @returns Function to unsubscribe from the event
|
|
191
|
+
*/
|
|
192
|
+
onUploadProgress(callback: (progressData: any) => void): () => void {
|
|
193
|
+
return this.addEventListener('upload-progress', callback);
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Convenience method for handling permission status updates
|
|
198
|
+
* @param callback Function to call when permission status changes
|
|
199
|
+
* @returns Function to unsubscribe from the event
|
|
200
|
+
*/
|
|
201
|
+
onPermissionStatusChange(callback: (permissionData: any) => void): () => void {
|
|
202
|
+
return this.addEventListener('permission-status', callback);
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Convenience method for handling SDK errors
|
|
207
|
+
* @param callback Function to call when SDK errors occur
|
|
208
|
+
* @returns Function to unsubscribe from the event
|
|
209
|
+
*/
|
|
210
|
+
onError(callback: (errorData: any) => void): () => void {
|
|
211
|
+
return this.addEventListener('error', callback);
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Convenience method for handling real-time events (transcription, participants, etc.)
|
|
216
|
+
* @param callback Function to call when real-time events occur
|
|
217
|
+
* @returns Function to unsubscribe from the event
|
|
218
|
+
*/
|
|
219
|
+
onRealtimeEvent(callback: (realtimeData: any) => void): () => void {
|
|
220
|
+
return this.addEventListener('realtime-event', callback);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Export the API type for TypeScript support
|
|
225
|
+
export type RecallDesktopApi = typeof recallDesktopApi;
|
|
226
|
+
|
|
227
|
+
// Expose the API to the renderer process
|
|
228
|
+
contextBridge.exposeInMainWorld('recallDesktop', recallDesktopApi);
|
|
229
|
+
|
|
230
|
+
// Extend the global Window interface for TypeScript
|
|
231
|
+
declare global {
|
|
232
|
+
interface Window {
|
|
233
|
+
recallDesktop: RecallDesktopApi;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
console.log('RecallDesktop: Preload script loaded successfully');
|
package/src/shared.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types and constants for Recall Desktop SDK integration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// IPC channel names - use unique namespace to avoid conflicts
|
|
6
|
+
export const IPC_CHANNELS = {
|
|
7
|
+
// SDK lifecycle
|
|
8
|
+
INIT_SDK: 'recall-desktop:init-sdk',
|
|
9
|
+
SHUTDOWN_SDK: 'recall-desktop:shutdown-sdk',
|
|
10
|
+
GET_STATUS: 'recall-desktop:get-status',
|
|
11
|
+
|
|
12
|
+
// Recording management
|
|
13
|
+
START_RECORDING: 'recall-desktop:start-recording',
|
|
14
|
+
STOP_RECORDING: 'recall-desktop:stop-recording',
|
|
15
|
+
PAUSE_RECORDING: 'recall-desktop:pause-recording',
|
|
16
|
+
RESUME_RECORDING: 'recall-desktop:resume-recording',
|
|
17
|
+
UPLOAD_RECORDING: 'recall-desktop:upload-recording',
|
|
18
|
+
|
|
19
|
+
// Desktop audio recording
|
|
20
|
+
PREPARE_DESKTOP_AUDIO: 'recall-desktop:prepare-desktop-audio',
|
|
21
|
+
|
|
22
|
+
// Permission management
|
|
23
|
+
REQUEST_PERMISSION: 'recall-desktop:request-permission',
|
|
24
|
+
|
|
25
|
+
// Configuration
|
|
26
|
+
SET_CONFIG: 'recall-desktop:set-config',
|
|
27
|
+
GET_CONFIG: 'recall-desktop:get-config',
|
|
28
|
+
|
|
29
|
+
// Event subscription
|
|
30
|
+
SUBSCRIBE_EVENTS: 'recall-desktop:subscribe-events',
|
|
31
|
+
UNSUBSCRIBE_EVENTS: 'recall-desktop:unsubscribe-events',
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
// Recall SDK Configuration (mirrors RecallAiSdkConfig where applicable)
|
|
35
|
+
export interface RecallSdkConfig {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
apiUrl: string;
|
|
38
|
+
requestPermissionsOnStartup: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Meeting window information from SDK
|
|
42
|
+
export interface MeetingWindow {
|
|
43
|
+
id: string;
|
|
44
|
+
title?: string;
|
|
45
|
+
url?: string;
|
|
46
|
+
platform: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Recording request/response types
|
|
50
|
+
export interface StartRecordingRequest {
|
|
51
|
+
windowId: string;
|
|
52
|
+
uploadToken: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface StopRecordingRequest {
|
|
56
|
+
windowId: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PauseRecordingRequest {
|
|
60
|
+
windowId: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ResumeRecordingRequest {
|
|
64
|
+
windowId: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface UploadRecordingRequest {
|
|
68
|
+
windowId: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// SDK Events from Recall SDK (mirror upstream)
|
|
72
|
+
export type RecallSdkEventType =
|
|
73
|
+
| 'recording-started'
|
|
74
|
+
| 'recording-ended'
|
|
75
|
+
| 'upload-progress'
|
|
76
|
+
| 'meeting-detected'
|
|
77
|
+
| 'meeting-updated'
|
|
78
|
+
| 'meeting-closed'
|
|
79
|
+
| 'sdk-state-change'
|
|
80
|
+
| 'error'
|
|
81
|
+
| 'media-capture-status'
|
|
82
|
+
| 'participant-capture-status'
|
|
83
|
+
| 'permissions-granted'
|
|
84
|
+
| 'permission-status'
|
|
85
|
+
| 'realtime-event'
|
|
86
|
+
| 'shutdown';
|
|
87
|
+
|
|
88
|
+
export interface RecallSdkEvent {
|
|
89
|
+
type: RecallSdkEventType;
|
|
90
|
+
data: any;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// SDK Event payloads
|
|
94
|
+
export interface MeetingDetectedEvent {
|
|
95
|
+
window: MeetingWindow;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface SdkStateChangeEvent {
|
|
99
|
+
sdk: {
|
|
100
|
+
state: {
|
|
101
|
+
code: 'recording' | 'idle' | 'paused';
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface RecordingStartedEvent {
|
|
107
|
+
window: MeetingWindow;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface RecordingEndedEvent {
|
|
111
|
+
window: MeetingWindow;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface MeetingClosedEvent {
|
|
115
|
+
window: MeetingWindow;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface UploadProgressEvent {
|
|
119
|
+
window: { id: string };
|
|
120
|
+
progress: number; // 0-100
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface MeetingUpdatedEvent {
|
|
124
|
+
window: MeetingWindow;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface MediaCaptureStatusEvent {
|
|
128
|
+
window: MeetingWindow;
|
|
129
|
+
type: 'video' | 'audio';
|
|
130
|
+
capturing: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface ParticipantCaptureStatusEvent {
|
|
134
|
+
window: MeetingWindow;
|
|
135
|
+
type: 'video' | 'audio' | 'screenshare';
|
|
136
|
+
capturing: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface RealtimeEvent {
|
|
140
|
+
window: MeetingWindow;
|
|
141
|
+
event: string;
|
|
142
|
+
data: any;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface SdkErrorEvent {
|
|
146
|
+
window?: MeetingWindow;
|
|
147
|
+
type: string;
|
|
148
|
+
message: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface PermissionStatusEvent {
|
|
152
|
+
permission: PermissionType;
|
|
153
|
+
status: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface ShutdownEvent {
|
|
157
|
+
code: number;
|
|
158
|
+
signal: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Permission types (mirror upstream)
|
|
162
|
+
export type PermissionType = 'accessibility' | 'screen-capture' | 'microphone' | 'system-audio';
|
|
163
|
+
|
|
164
|
+
// API Response types
|
|
165
|
+
export interface ApiResponse<T = any> {
|
|
166
|
+
success: boolean;
|
|
167
|
+
message: string;
|
|
168
|
+
data?: T;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Plugin status
|
|
172
|
+
export interface PluginStatus {
|
|
173
|
+
initialized: boolean;
|
|
174
|
+
sdkInitialized: boolean;
|
|
175
|
+
version: string;
|
|
176
|
+
config: RecallSdkConfig;
|
|
177
|
+
sdkState?: 'recording' | 'idle' | 'paused';
|
|
178
|
+
permissions?: {
|
|
179
|
+
accessibility: boolean;
|
|
180
|
+
screenCapture: boolean;
|
|
181
|
+
microphone: boolean;
|
|
182
|
+
systemAudio: boolean;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Error types
|
|
187
|
+
export class RecallSdkError extends Error {
|
|
188
|
+
constructor(message: string, public code?: string) {
|
|
189
|
+
super(message);
|
|
190
|
+
this.name = 'RecallSdkError';
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// SDK Initialization options
|
|
195
|
+
export interface SdkInitOptions {
|
|
196
|
+
apiUrl: string;
|
|
197
|
+
acquirePermissionsOnStartup?: PermissionType[];
|
|
198
|
+
restartOnError?: boolean;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Desktop audio recording response
|
|
202
|
+
export interface PrepareDesktopAudioResponse {
|
|
203
|
+
windowId: string;
|
|
204
|
+
}
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recall Desktop SDK plugin state management and storage
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RecallSdkConfig, MeetingWindow, PermissionType } from './shared';
|
|
6
|
+
|
|
7
|
+
class RecallSdkStore {
|
|
8
|
+
private config: RecallSdkConfig = {
|
|
9
|
+
enabled: true,
|
|
10
|
+
apiUrl: 'https://us-east-1.recall.ai',
|
|
11
|
+
requestPermissionsOnStartup: true,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
private initialized = false;
|
|
15
|
+
private sdkInitialized = false;
|
|
16
|
+
private currentSdkState: 'recording' | 'idle' | 'paused' = 'idle';
|
|
17
|
+
|
|
18
|
+
// Active meetings and recordings
|
|
19
|
+
private activeMeetings = new Map<string, MeetingWindow>();
|
|
20
|
+
private activeRecordings = new Map<string, { window: MeetingWindow; uploadToken: string }>();
|
|
21
|
+
|
|
22
|
+
// Permission status
|
|
23
|
+
private permissions = {
|
|
24
|
+
accessibility: false,
|
|
25
|
+
screenCapture: false,
|
|
26
|
+
microphone: false,
|
|
27
|
+
systemAudio: false,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Event listeners for SDK events
|
|
31
|
+
private eventListeners = new Set<(event: any) => void>();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize the plugin store
|
|
35
|
+
*/
|
|
36
|
+
async initialize(): Promise<void> {
|
|
37
|
+
// Load configuration from ToDesktop preferences
|
|
38
|
+
try {
|
|
39
|
+
// In a real implementation, this would load from ToDesktop's preference system
|
|
40
|
+
// For now, we'll use the defaults
|
|
41
|
+
this.initialized = true;
|
|
42
|
+
console.log('RecallSdkStore: Initialized successfully');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('RecallSdkStore: Failed to initialize:', error);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get current plugin configuration
|
|
51
|
+
*/
|
|
52
|
+
getConfig(): RecallSdkConfig {
|
|
53
|
+
return { ...this.config };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Update plugin configuration
|
|
58
|
+
*/
|
|
59
|
+
setConfig(updates: Partial<RecallSdkConfig>): void {
|
|
60
|
+
this.config = { ...this.config, ...updates };
|
|
61
|
+
console.log('RecallSdkStore: Configuration updated:', this.config);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Load configuration from ToDesktop preferences
|
|
66
|
+
*/
|
|
67
|
+
loadFromPreferences(preferences: Record<string, any>): void {
|
|
68
|
+
if (preferences.enabled !== undefined) {
|
|
69
|
+
this.config.enabled = preferences.enabled;
|
|
70
|
+
}
|
|
71
|
+
if (preferences.apiUrl !== undefined) {
|
|
72
|
+
this.config.apiUrl = preferences.apiUrl;
|
|
73
|
+
}
|
|
74
|
+
// autoRecord removed by design
|
|
75
|
+
if (preferences.requestPermissionsOnStartup !== undefined) {
|
|
76
|
+
this.config.requestPermissionsOnStartup = preferences.requestPermissionsOnStartup;
|
|
77
|
+
}
|
|
78
|
+
console.log('RecallSdkStore: Loaded configuration from preferences:', this.config);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if plugin is initialized
|
|
83
|
+
*/
|
|
84
|
+
isInitialized(): boolean {
|
|
85
|
+
return this.initialized;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if plugin is enabled
|
|
90
|
+
*/
|
|
91
|
+
isEnabled(): boolean {
|
|
92
|
+
return this.config.enabled;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Set SDK initialization status
|
|
97
|
+
*/
|
|
98
|
+
setSdkInitialized(initialized: boolean): void {
|
|
99
|
+
this.sdkInitialized = initialized;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if SDK is initialized
|
|
104
|
+
*/
|
|
105
|
+
isSdkInitialized(): boolean {
|
|
106
|
+
return this.sdkInitialized;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Set current SDK state
|
|
111
|
+
*/
|
|
112
|
+
setSdkState(state: 'recording' | 'idle' | 'paused'): void {
|
|
113
|
+
this.currentSdkState = state;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get current SDK state
|
|
118
|
+
*/
|
|
119
|
+
getSdkState(): 'recording' | 'idle' | 'paused' {
|
|
120
|
+
return this.currentSdkState;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Add an active meeting
|
|
125
|
+
*/
|
|
126
|
+
addMeeting(meeting: MeetingWindow): void {
|
|
127
|
+
this.activeMeetings.set(meeting.id, meeting);
|
|
128
|
+
console.log(`RecallSdkStore: Added meeting ${meeting.id}:`, meeting);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Remove a meeting
|
|
133
|
+
*/
|
|
134
|
+
removeMeeting(windowId: string): void {
|
|
135
|
+
this.activeMeetings.delete(windowId);
|
|
136
|
+
console.log(`RecallSdkStore: Removed meeting ${windowId}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get active meeting by window ID
|
|
141
|
+
*/
|
|
142
|
+
getMeeting(windowId: string): MeetingWindow | undefined {
|
|
143
|
+
return this.activeMeetings.get(windowId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get all active meetings
|
|
148
|
+
*/
|
|
149
|
+
getAllMeetings(): MeetingWindow[] {
|
|
150
|
+
return Array.from(this.activeMeetings.values());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Start a recording
|
|
155
|
+
*/
|
|
156
|
+
startRecording(windowId: string, uploadToken: string): void {
|
|
157
|
+
const window = this.activeMeetings.get(windowId);
|
|
158
|
+
if (window) {
|
|
159
|
+
this.activeRecordings.set(windowId, { window, uploadToken });
|
|
160
|
+
console.log(`RecallSdkStore: Started recording for ${windowId}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Stop a recording
|
|
166
|
+
*/
|
|
167
|
+
stopRecording(windowId: string): void {
|
|
168
|
+
this.activeRecordings.delete(windowId);
|
|
169
|
+
console.log(`RecallSdkStore: Stopped recording for ${windowId}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get active recording by window ID
|
|
174
|
+
*/
|
|
175
|
+
getRecording(windowId: string): { window: MeetingWindow; uploadToken: string } | undefined {
|
|
176
|
+
return this.activeRecordings.get(windowId);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get all active recordings
|
|
181
|
+
*/
|
|
182
|
+
getAllRecordings(): { window: MeetingWindow; uploadToken: string }[] {
|
|
183
|
+
return Array.from(this.activeRecordings.values());
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Update permission status
|
|
188
|
+
*/
|
|
189
|
+
setPermissionStatus(permission: PermissionType, status: string): void {
|
|
190
|
+
const granted = status === 'granted' || status === 'authorized' || status === 'ALLOWED';
|
|
191
|
+
if (permission === 'screen-capture') {
|
|
192
|
+
this.permissions.screenCapture = granted;
|
|
193
|
+
} else if (permission === 'system-audio') {
|
|
194
|
+
this.permissions.systemAudio = granted;
|
|
195
|
+
} else {
|
|
196
|
+
// permission is 'accessibility' | 'microphone'
|
|
197
|
+
// @ts-ignore - index by key
|
|
198
|
+
this.permissions[permission] = granted;
|
|
199
|
+
}
|
|
200
|
+
console.log(`RecallSdkStore: Permission ${permission} status: ${status}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get permission status
|
|
205
|
+
*/
|
|
206
|
+
getPermissions(): { accessibility: boolean; screenCapture: boolean; microphone: boolean; systemAudio: boolean } {
|
|
207
|
+
return { ...this.permissions };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Check if all required permissions are granted
|
|
212
|
+
*/
|
|
213
|
+
arePermissionsGranted(): boolean {
|
|
214
|
+
return (
|
|
215
|
+
this.permissions.accessibility &&
|
|
216
|
+
this.permissions.screenCapture &&
|
|
217
|
+
this.permissions.microphone
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Add event listener for SDK events
|
|
223
|
+
*/
|
|
224
|
+
addEventListener(listener: (event: any) => void): void {
|
|
225
|
+
this.eventListeners.add(listener);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Remove event listener
|
|
230
|
+
*/
|
|
231
|
+
removeEventListener(listener: (event: any) => void): void {
|
|
232
|
+
this.eventListeners.delete(listener);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Emit event to all listeners
|
|
237
|
+
*/
|
|
238
|
+
emitEvent(event: any): void {
|
|
239
|
+
this.eventListeners.forEach(listener => {
|
|
240
|
+
try {
|
|
241
|
+
listener(event);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error('RecallSdkStore: Error in event listener:', error);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Clear all state (useful for shutdown/reset)
|
|
250
|
+
*/
|
|
251
|
+
clearState(): void {
|
|
252
|
+
this.activeMeetings.clear();
|
|
253
|
+
this.activeRecordings.clear();
|
|
254
|
+
this.setSdkInitialized(false);
|
|
255
|
+
this.setSdkState('idle');
|
|
256
|
+
this.permissions = {
|
|
257
|
+
accessibility: false,
|
|
258
|
+
screenCapture: false,
|
|
259
|
+
microphone: false,
|
|
260
|
+
systemAudio: false,
|
|
261
|
+
};
|
|
262
|
+
console.log('RecallSdkStore: Cleared all state');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export const recallSdkStore = new RecallSdkStore();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"composite": true,
|
|
7
|
+
"types": ["node", "electron"]
|
|
8
|
+
},
|
|
9
|
+
"include": ["src/**/*"],
|
|
10
|
+
"exclude": ["dist", "node_modules", "**/*.test.ts", "**/*.spec.ts"]
|
|
11
|
+
}
|