diff options
| author | haoyuren <13851610112@163.com> | 2026-08-18 20:24:40 +0800 |
|---|---|---|
| committer | haoyuren <13851610112@163.com> | 2026-08-18 20:24:40 +0800 |
| commit | ec8214a4d111d92ce7c44de29d1706f241aa9514 (patch) | |
| tree | d5ffce9791bb94b4a4336ae26d091ba2e010d364 /src/preload/index.ts | |
| parent | df3092328865949c088eed321e6675e3b7758c36 (diff) | |
Add browser-style project tabs: persistent home + one tab per projectv0.6.0
The project list is now a persistent home tab; each opened project runs
in its own WebContentsView tab below a 38px tab strip (hidden when only
home is open). Background tabs stay fully live (sync, collab, terminals).
Main process: per-project singletons (socket, sync bridge, compilation
manager, MCP state, compile watcher) become per-tab ProjectSession
objects keyed by webContents.id; PTYs are scoped per webContents.
Hardened against races found in review: concurrent ot:connect dedupe,
teardown/reopen serialization on the shared sync dir, destroyed-window
guards on all bridge sends, real logout that closes project tabs.
Renderer: tabs strip in the home renderer (wt-* classes, distinct from
the editor's .tab-bar file tabs), project tabs boot from ?projectId= and
connect straight into the editor; Back closes the tab.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'src/preload/index.ts')
| -rw-r--r-- | src/preload/index.ts | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/src/preload/index.ts b/src/preload/index.ts index a4d8179..ee9fd9b 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -63,6 +63,7 @@ const api = { // Overleaf web session (comments) overleafWebLogin: () => ipcRenderer.invoke('overleaf:webLogin') as Promise<{ success: boolean }>, overleafHasWebSession: () => ipcRenderer.invoke('overleaf:hasWebSession') as Promise<{ loggedIn: boolean }>, + overleafLogout: () => ipcRenderer.invoke('overleaf:logout') as Promise<void>, overleafGetThreads: (projectId: string) => ipcRenderer.invoke('overleaf:getThreads', projectId) as Promise<{ success: boolean; threads?: Record<string, unknown>; message?: string }>, overleafReplyThread: (projectId: string, threadId: string, content: string) => @@ -370,6 +371,20 @@ const api = { getApiKeys: () => ipcRenderer.invoke('settings:getApiKeys') as Promise<Record<string, string>>, setApiKeys: (keys: Record<string, string>) => ipcRenderer.invoke('settings:setApiKeys', keys), + // Project tabs (browser-tab model: home = project list, one tab per project) + openProjectTab: (projectId: string, name?: string) => + ipcRenderer.invoke('project:openTab', projectId, name) as Promise<{ success: boolean; focusedExisting?: boolean }>, + tabsList: () => + ipcRenderer.invoke('tabs:list') as Promise<{ tabs: Array<{ id: string; title: string }>; active: string }>, + tabsActivate: (id: string) => ipcRenderer.invoke('tabs:activate', id) as Promise<void>, + tabsClose: (id: string) => ipcRenderer.invoke('tabs:close', id) as Promise<void>, + onTabsChanged: (cb: (state: { tabs: Array<{ id: string; title: string }>; active: string }) => void) => { + const handler = (_e: Electron.IpcRendererEvent, state: { tabs: Array<{ id: string; title: string }>; active: string }) => cb(state) + ipcRenderer.on('tabs:changed', handler) + return () => { ipcRenderer.removeListener('tabs:changed', handler) } + }, + closeWindow: () => ipcRenderer.invoke('window:close') as Promise<void>, + // Shell openExternal: (url: string) => ipcRenderer.invoke('shell:openExternal', url), showInFinder: (path: string) => ipcRenderer.invoke('shell:showInFinder', path), |
