**Layout refactor (Phase 1):** - UnifiedTopBar replacing Header.tsx — chat title editing, project folder, commit/push split button, Git branch+dirty badge, terminal/file-tree toggles - PanelZone renders FileTree, GitPanel, PreviewPanel side-by-side on the right - Independent panel toggle states in PanelContext - ResizeHandle unified across all panels - Removed old RightPanel, Header, DocPreview components **Git backend (Phase 2):** - src/lib/git/service.ts — execFile-based Git operations with timeout + injection safety - API routes: status, branches, checkout, log, commit, push, commit-detail, worktrees, derive - Worktree derive creates DB session inheriting source session settings **Git frontend (Phase 3):** - GitPanel with 4 collapsible sections: Status, Branches, History, Worktrees - CommitDialog with commit vs commit-and-push modes - GitBranchSelector with inline error, worktree-occupied branches disabled - GitWorktreeSection with switch-to/derive actions, current/dirty indicators - useGitStatus polled in AppShell, derived into context (no redundant setState) **Terminal (Phase 4):** - electron/terminal-manager.ts — child_process.spawn fallback (PTY upgrade documented) - IPC handlers + TerminalDrawer/TerminalInstance components **Polish (Phase 5):** - Push inline feedback with 4s auto-dismiss - Branch detection fix, commit diff exit-code handling - Removed redundant git state: currentBranch/gitDirtyCount now derived, not setState - useTerminal isElectron uses lazy initializer instead of effect - i18n keys (en + zh), user tutorial docs, handover document Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
52 lines
2.2 KiB
TypeScript
52 lines
2.2 KiB
TypeScript
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
const { contextBridge, ipcRenderer } = require('electron');
|
|
|
|
contextBridge.exposeInMainWorld('electronAPI', {
|
|
versions: {
|
|
electron: process.versions.electron,
|
|
node: process.versions.node,
|
|
chrome: process.versions.chrome,
|
|
},
|
|
shell: {
|
|
openPath: (folderPath: string) => ipcRenderer.invoke('shell:open-path', folderPath),
|
|
},
|
|
dialog: {
|
|
openFolder: (options?: { defaultPath?: string; title?: string }) =>
|
|
ipcRenderer.invoke('dialog:open-folder', options),
|
|
},
|
|
install: {
|
|
checkPrerequisites: () => ipcRenderer.invoke('install:check-prerequisites'),
|
|
start: () => ipcRenderer.invoke('install:start'),
|
|
cancel: () => ipcRenderer.invoke('install:cancel'),
|
|
getLogs: () => ipcRenderer.invoke('install:get-logs'),
|
|
onProgress: (callback: (data: unknown) => void) => {
|
|
const listener = (_event: unknown, data: unknown) => callback(data);
|
|
ipcRenderer.on('install:progress', listener);
|
|
return () => { ipcRenderer.removeListener('install:progress', listener); };
|
|
},
|
|
},
|
|
bridge: {
|
|
isActive: () => ipcRenderer.invoke('bridge:is-active'),
|
|
},
|
|
terminal: {
|
|
create: (opts: { id: string; cwd: string; cols: number; rows: number }) =>
|
|
ipcRenderer.invoke('terminal:create', opts),
|
|
write: (id: string, data: string) =>
|
|
ipcRenderer.send('terminal:write', { id, data }),
|
|
resize: (id: string, cols: number, rows: number) =>
|
|
ipcRenderer.invoke('terminal:resize', { id, cols, rows }),
|
|
kill: (id: string) =>
|
|
ipcRenderer.invoke('terminal:kill', id),
|
|
onData: (callback: (data: { id: string; data: string }) => void) => {
|
|
const listener = (_event: unknown, data: { id: string; data: string }) => callback(data);
|
|
ipcRenderer.on('terminal:data', listener);
|
|
return () => { ipcRenderer.removeListener('terminal:data', listener); };
|
|
},
|
|
onExit: (callback: (data: { id: string; code: number }) => void) => {
|
|
const listener = (_event: unknown, data: { id: string; code: number }) => callback(data);
|
|
ipcRenderer.on('terminal:exit', listener);
|
|
return () => { ipcRenderer.removeListener('terminal:exit', listener); };
|
|
},
|
|
},
|
|
});
|