From ebec1a1073f9cc5b69e125d5b284669545ea3d9f Mon Sep 17 00:00:00 2001 From: haoyuren <13851610112@163.com> Date: Wed, 11 Mar 2026 18:16:45 -0500 Subject: Initial commit: ClaudeTeX - LaTeX editor with Overleaf sync Features: - Electron + React + TypeScript app with Cosmic Latte theme - CodeMirror 6 editor with LaTeX syntax highlighting - PDF preview with pdf.js, zoom controls, SyncTeX double-click jump - File tree with context menu (new/rename/delete/reveal) - Overleaf Git clone with token auth + macOS Keychain storage - Git pull/push sync for Overleaf projects - Embedded terminal (xterm.js + node-pty) with Claude CLI integration - LaTeX compilation via latexmk with auto package install detection - Structured compile log (errors/warnings) with click-to-navigate - Main document setting (auto-detect or right-click to set) - Custom modal system (input/confirm/alert) - Resizable panel layout (file tree | editor | PDF + terminal) Co-Authored-By: Claude Opus 4.6 --- src/preload/index.d.ts | 7 +++++ src/preload/index.ts | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/preload/index.d.ts create mode 100644 src/preload/index.ts (limited to 'src/preload') diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts new file mode 100644 index 0000000..ccfa000 --- /dev/null +++ b/src/preload/index.d.ts @@ -0,0 +1,7 @@ +import type { ElectronAPI } from './index' + +declare global { + interface Window { + api: ElectronAPI + } +} diff --git a/src/preload/index.ts b/src/preload/index.ts new file mode 100644 index 0000000..ab360c0 --- /dev/null +++ b/src/preload/index.ts @@ -0,0 +1,78 @@ +import { contextBridge, ipcRenderer } from 'electron' + +const api = { + // File system + openProject: () => ipcRenderer.invoke('dialog:openProject'), + selectSaveDir: () => ipcRenderer.invoke('dialog:selectSaveDir'), + readDir: (path: string) => ipcRenderer.invoke('fs:readDir', path), + readFile: (path: string) => ipcRenderer.invoke('fs:readFile', path), + findMainTex: (dir: string) => ipcRenderer.invoke('fs:findMainTex', dir) as Promise, + readBinary: (path: string) => ipcRenderer.invoke('fs:readBinary', path) as Promise, + writeFile: (path: string, content: string) => ipcRenderer.invoke('fs:writeFile', path, content), + createFile: (dir: string, name: string) => ipcRenderer.invoke('fs:createFile', dir, name), + createDir: (dir: string, name: string) => ipcRenderer.invoke('fs:createDir', dir, name), + renameFile: (oldPath: string, newPath: string) => ipcRenderer.invoke('fs:rename', oldPath, newPath), + deleteFile: (path: string) => ipcRenderer.invoke('fs:delete', path), + fileStat: (path: string) => ipcRenderer.invoke('fs:stat', path), + + // File watcher + watchStart: (path: string) => ipcRenderer.invoke('watcher:start', path), + watchStop: () => ipcRenderer.invoke('watcher:stop'), + onWatchChange: (cb: (data: { event: string; path: string }) => void) => { + const handler = (_e: Electron.IpcRendererEvent, data: { event: string; path: string }) => cb(data) + ipcRenderer.on('watcher:change', handler) + return () => ipcRenderer.removeListener('watcher:change', handler) + }, + + // LaTeX + compile: (path: string) => ipcRenderer.invoke('latex:compile', path), + getPdfPath: (texPath: string) => ipcRenderer.invoke('latex:getPdfPath', texPath), + onCompileLog: (cb: (log: string) => void) => { + const handler = (_e: Electron.IpcRendererEvent, log: string) => cb(log) + ipcRenderer.on('latex:log', handler) + return () => ipcRenderer.removeListener('latex:log', handler) + }, + + // Terminal + ptySpawn: (cwd: string) => ipcRenderer.invoke('pty:spawn', cwd), + ptyWrite: (data: string) => ipcRenderer.invoke('pty:write', data), + ptyResize: (cols: number, rows: number) => ipcRenderer.invoke('pty:resize', cols, rows), + ptyKill: () => ipcRenderer.invoke('pty:kill'), + onPtyData: (cb: (data: string) => void) => { + const handler = (_e: Electron.IpcRendererEvent, data: string) => cb(data) + ipcRenderer.on('pty:data', handler) + return () => ipcRenderer.removeListener('pty:data', handler) + }, + onPtyExit: (cb: () => void) => { + const handler = () => cb() + ipcRenderer.on('pty:exit', handler) + return () => ipcRenderer.removeListener('pty:exit', handler) + }, + + // Overleaf + overleafCloneWithAuth: (projectId: string, dest: string, token: string, remember: boolean) => + ipcRenderer.invoke('overleaf:cloneWithAuth', projectId, dest, token, remember) as Promise<{ success: boolean; message: string; detail: string }>, + overleafCheck: () => ipcRenderer.invoke('overleaf:check') as Promise<{ loggedIn: boolean; email: string }>, + overleafLogout: () => ipcRenderer.invoke('overleaf:logout'), + gitPull: (cwd: string) => ipcRenderer.invoke('git:pull', cwd), + gitPush: (cwd: string) => ipcRenderer.invoke('git:push', cwd), + gitStatus: (cwd: string) => ipcRenderer.invoke('git:status', cwd), + + // SyncTeX + synctexEdit: (pdfPath: string, page: number, x: number, y: number) => + ipcRenderer.invoke('synctex:editFromPdf', pdfPath, page, x, y) as Promise<{ file: string; line: number } | null>, + synctexView: (texPath: string, line: number, pdfPath: string) => + ipcRenderer.invoke('synctex:viewFromSource', texPath, line, pdfPath) as Promise<{ page: number; x: number; y: number } | null>, + + // LaTeX package management + installTexPackages: (packages: string[]) => + ipcRenderer.invoke('latex:installPackages', packages) as Promise<{ success: boolean; message: string; packages?: string[] }>, + + // Shell + openExternal: (url: string) => ipcRenderer.invoke('shell:openExternal', url), + showInFinder: (path: string) => ipcRenderer.invoke('shell:showInFinder', path) +} + +contextBridge.exposeInMainWorld('api', api) + +export type ElectronAPI = typeof api -- cgit v1.2.3