diff options
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/index.ts | 45 | ||||
| -rw-r--r-- | src/main/overleafSocket.ts | 18 |
2 files changed, 63 insertions, 0 deletions
diff --git a/src/main/index.ts b/src/main/index.ts index 21b6e43..89a04b0 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -620,6 +620,17 @@ ipcMain.handle('ot:connect', async (_e, projectId: string) => { }) }) + // Relay collaborator cursor updates to renderer + overleafSock.on('serverEvent', (name: string, args: unknown[]) => { + if (name === 'clientTracking.clientUpdated') { + mainWindow?.webContents.send('cursor:remoteUpdate', args[0]) + } else if (name === 'clientTracking.clientDisconnected') { + mainWindow?.webContents.send('cursor:remoteDisconnected', args[0]) + } else if (name === 'new-chat-message') { + mainWindow?.webContents.send('chat:newMessage', args[0]) + } + }) + const projectResult = await overleafSock.connect(projectId, overleafSessionCookie) const { files, docPathMap, pathDocMap, fileRefs, rootFolderId } = walkRootFolder(projectResult.project.rootFolder) @@ -745,6 +756,40 @@ ipcMain.handle('sync:contentChanged', async (_e, docId: string, content: string) fileSyncBridge?.onEditorContentChanged(docId, content) }) +// ── Cursor Tracking ──────────────────────────────────────────── + +ipcMain.handle('cursor:update', async (_e, docId: string, row: number, column: number) => { + overleafSock?.updateCursorPosition(docId, row, column) +}) + +ipcMain.handle('cursor:getConnectedUsers', async () => { + if (!overleafSock) return [] + try { + return await overleafSock.getConnectedUsers() + } catch (e) { + console.log('[cursor:getConnectedUsers] error:', e) + return [] + } +}) + +// ── Chat ─────────────────────────────────────────────────────── + +ipcMain.handle('chat:getMessages', async (_e, projectId: string, limit?: number) => { + if (!overleafSessionCookie) return { success: false, messages: [] } + const result = await overleafFetch(`/project/${projectId}/messages?limit=${limit || 50}`) + if (!result.ok) return { success: false, messages: [] } + return { success: true, messages: result.data } +}) + +ipcMain.handle('chat:sendMessage', async (_e, projectId: string, content: string) => { + if (!overleafSessionCookie) return { success: false } + const result = await overleafFetch(`/project/${projectId}/messages`, { + method: 'POST', + body: JSON.stringify({ content }) + }) + return { success: result.ok } +}) + ipcMain.handle('overleaf:listProjects', async () => { if (!overleafSessionCookie) return { success: false, message: 'not_logged_in' } diff --git a/src/main/overleafSocket.ts b/src/main/overleafSocket.ts index f825c4c..52ac20f 100644 --- a/src/main/overleafSocket.ts +++ b/src/main/overleafSocket.ts @@ -92,6 +92,10 @@ export class OverleafSocket extends EventEmitter { return this._projectData } + get publicId(): string | null { + return this._projectData?.publicId || null + } + private setState(s: ConnectionState) { this._state = s this.emit('connectionState', s) @@ -270,6 +274,20 @@ export class OverleafSocket extends EventEmitter { this.ws?.send(encodeEvent('applyOtUpdate', [docId, { doc: docId, op: ops, v: version, hash, lastV: version }])) } + /** Get list of connected users with their cursor positions */ + async getConnectedUsers(): Promise<unknown[]> { + const result = await this.emitWithAck('clientTracking.getConnectedUsers', []) as unknown[] + // result format: [error, usersArray] + const err = result[0] + if (err) throw new Error(`getConnectedUsers failed: ${JSON.stringify(err)}`) + return (result[1] as unknown[]) || [] + } + + /** Send our cursor position */ + updateCursorPosition(docId: string, row: number, column: number): void { + this.ws?.send(encodeEvent('clientTracking.updatePosition', [{ row, column, doc_id: docId }])) + } + disconnect() { this.shouldReconnect = false this.stopHeartbeat() |
