Skip to content

Commit 5d5526d

Browse files
committed
feat(ipc): add IpcChannel enum for all channel names
1 parent 2462a58 commit 5d5526d

18 files changed

Lines changed: 182 additions & 102 deletions

electron/ipc/channels.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
export enum IPC {
2+
// Agent/PTY
3+
SpawnAgent = "spawn_agent",
4+
WriteToAgent = "write_to_agent",
5+
ResizeAgent = "resize_agent",
6+
PauseAgent = "pause_agent",
7+
ResumeAgent = "resume_agent",
8+
KillAgent = "kill_agent",
9+
CountRunningAgents = "count_running_agents",
10+
KillAllAgents = "kill_all_agents",
11+
ListAgents = "list_agents",
12+
13+
// Task
14+
CreateTask = "create_task",
15+
DeleteTask = "delete_task",
16+
17+
// Git
18+
GetChangedFiles = "get_changed_files",
19+
GetFileDiff = "get_file_diff",
20+
GetGitignoredDirs = "get_gitignored_dirs",
21+
GetWorktreeStatus = "get_worktree_status",
22+
CheckMergeStatus = "check_merge_status",
23+
MergeTask = "merge_task",
24+
GetBranchLog = "get_branch_log",
25+
PushTask = "push_task",
26+
RebaseTask = "rebase_task",
27+
GetMainBranch = "get_main_branch",
28+
GetCurrentBranch = "get_current_branch",
29+
30+
// Persistence
31+
SaveAppState = "save_app_state",
32+
LoadAppState = "load_app_state",
33+
34+
// Window
35+
WindowIsFocused = "__window_is_focused",
36+
WindowIsMaximized = "__window_is_maximized",
37+
WindowMinimize = "__window_minimize",
38+
WindowToggleMaximize = "__window_toggle_maximize",
39+
WindowClose = "__window_close",
40+
WindowForceClose = "__window_force_close",
41+
WindowHide = "__window_hide",
42+
WindowMaximize = "__window_maximize",
43+
WindowUnmaximize = "__window_unmaximize",
44+
WindowSetSize = "__window_set_size",
45+
WindowSetPosition = "__window_set_position",
46+
WindowGetPosition = "__window_get_position",
47+
WindowGetSize = "__window_get_size",
48+
WindowFocus = "__window_focus",
49+
WindowBlur = "__window_blur",
50+
WindowResized = "__window_resized",
51+
WindowMoved = "__window_moved",
52+
WindowCloseRequested = "__window_close_requested",
53+
54+
// Dialog
55+
DialogConfirm = "__dialog_confirm",
56+
DialogOpen = "__dialog_open",
57+
58+
// Shell
59+
ShellReveal = "__shell_reveal",
60+
}

electron/ipc/register.ts

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ipcMain, dialog, shell, BrowserWindow } from "electron";
2+
import { IPC } from "./channels.js";
23
import {
34
spawnAgent,
45
writeToAgent,
@@ -28,75 +29,75 @@ import { saveAppState, loadAppState } from "./persistence.js";
2829

2930
export function registerAllHandlers(win: BrowserWindow): void {
3031
// --- PTY commands ---
31-
ipcMain.handle("spawn_agent", (_e, args) => spawnAgent(win, args));
32-
ipcMain.handle("write_to_agent", (_e, args) => writeToAgent(args.agentId, args.data));
33-
ipcMain.handle("resize_agent", (_e, args) => resizeAgent(args.agentId, args.cols, args.rows));
34-
ipcMain.handle("pause_agent", (_e, args) => pauseAgent(args.agentId));
35-
ipcMain.handle("resume_agent", (_e, args) => resumeAgent(args.agentId));
36-
ipcMain.handle("kill_agent", (_e, args) => killAgent(args.agentId));
37-
ipcMain.handle("count_running_agents", () => countRunningAgents());
38-
ipcMain.handle("kill_all_agents", () => killAllAgents());
32+
ipcMain.handle(IPC.SpawnAgent, (_e, args) => spawnAgent(win, args));
33+
ipcMain.handle(IPC.WriteToAgent, (_e, args) => writeToAgent(args.agentId, args.data));
34+
ipcMain.handle(IPC.ResizeAgent, (_e, args) => resizeAgent(args.agentId, args.cols, args.rows));
35+
ipcMain.handle(IPC.PauseAgent, (_e, args) => pauseAgent(args.agentId));
36+
ipcMain.handle(IPC.ResumeAgent, (_e, args) => resumeAgent(args.agentId));
37+
ipcMain.handle(IPC.KillAgent, (_e, args) => killAgent(args.agentId));
38+
ipcMain.handle(IPC.CountRunningAgents, () => countRunningAgents());
39+
ipcMain.handle(IPC.KillAllAgents, () => killAllAgents());
3940

4041
// --- Agent commands ---
41-
ipcMain.handle("list_agents", () => listAgents());
42+
ipcMain.handle(IPC.ListAgents, () => listAgents());
4243

4344
// --- Task commands ---
44-
ipcMain.handle("create_task", (_e, args) =>
45+
ipcMain.handle(IPC.CreateTask, (_e, args) =>
4546
createTask(args.name, args.projectRoot, args.symlinkDirs, args.branchPrefix)
4647
);
47-
ipcMain.handle("delete_task", (_e, args) =>
48+
ipcMain.handle(IPC.DeleteTask, (_e, args) =>
4849
deleteTask(args.agentIds, args.branchName, args.deleteBranch, args.projectRoot)
4950
);
5051

5152
// --- Git commands ---
52-
ipcMain.handle("get_changed_files", (_e, args) => getChangedFiles(args.worktreePath));
53-
ipcMain.handle("get_file_diff", (_e, args) => getFileDiff(args.worktreePath, args.filePath));
54-
ipcMain.handle("get_gitignored_dirs", (_e, args) => getGitIgnoredDirs(args.projectRoot));
55-
ipcMain.handle("get_worktree_status", (_e, args) => getWorktreeStatus(args.worktreePath));
56-
ipcMain.handle("check_merge_status", (_e, args) => checkMergeStatus(args.worktreePath));
57-
ipcMain.handle("merge_task", (_e, args) =>
53+
ipcMain.handle(IPC.GetChangedFiles, (_e, args) => getChangedFiles(args.worktreePath));
54+
ipcMain.handle(IPC.GetFileDiff, (_e, args) => getFileDiff(args.worktreePath, args.filePath));
55+
ipcMain.handle(IPC.GetGitignoredDirs, (_e, args) => getGitIgnoredDirs(args.projectRoot));
56+
ipcMain.handle(IPC.GetWorktreeStatus, (_e, args) => getWorktreeStatus(args.worktreePath));
57+
ipcMain.handle(IPC.CheckMergeStatus, (_e, args) => checkMergeStatus(args.worktreePath));
58+
ipcMain.handle(IPC.MergeTask, (_e, args) =>
5859
mergeTask(args.projectRoot, args.branchName, args.squash, args.message, args.cleanup)
5960
);
60-
ipcMain.handle("get_branch_log", (_e, args) => getBranchLog(args.worktreePath));
61-
ipcMain.handle("push_task", (_e, args) => pushTask(args.projectRoot, args.branchName));
62-
ipcMain.handle("rebase_task", (_e, args) => rebaseTask(args.worktreePath));
63-
ipcMain.handle("get_main_branch", (_e, args) => getMainBranch(args.projectRoot));
64-
ipcMain.handle("get_current_branch", (_e, args) => getCurrentBranch(args.projectRoot));
61+
ipcMain.handle(IPC.GetBranchLog, (_e, args) => getBranchLog(args.worktreePath));
62+
ipcMain.handle(IPC.PushTask, (_e, args) => pushTask(args.projectRoot, args.branchName));
63+
ipcMain.handle(IPC.RebaseTask, (_e, args) => rebaseTask(args.worktreePath));
64+
ipcMain.handle(IPC.GetMainBranch, (_e, args) => getMainBranch(args.projectRoot));
65+
ipcMain.handle(IPC.GetCurrentBranch, (_e, args) => getCurrentBranch(args.projectRoot));
6566

6667
// --- Persistence ---
67-
ipcMain.handle("save_app_state", (_e, args) => saveAppState(args.json));
68-
ipcMain.handle("load_app_state", () => loadAppState());
68+
ipcMain.handle(IPC.SaveAppState, (_e, args) => saveAppState(args.json));
69+
ipcMain.handle(IPC.LoadAppState, () => loadAppState());
6970

7071
// --- Window management ---
71-
ipcMain.handle("__window_is_focused", () => win.isFocused());
72-
ipcMain.handle("__window_is_maximized", () => win.isMaximized());
73-
ipcMain.handle("__window_minimize", () => win.minimize());
74-
ipcMain.handle("__window_toggle_maximize", () => {
72+
ipcMain.handle(IPC.WindowIsFocused, () => win.isFocused());
73+
ipcMain.handle(IPC.WindowIsMaximized, () => win.isMaximized());
74+
ipcMain.handle(IPC.WindowMinimize, () => win.minimize());
75+
ipcMain.handle(IPC.WindowToggleMaximize, () => {
7576
if (win.isMaximized()) win.unmaximize();
7677
else win.maximize();
7778
});
78-
ipcMain.handle("__window_close", () => win.close());
79-
ipcMain.handle("__window_force_close", () => win.destroy());
80-
ipcMain.handle("__window_hide", () => win.hide());
81-
ipcMain.handle("__window_maximize", () => win.maximize());
82-
ipcMain.handle("__window_unmaximize", () => win.unmaximize());
83-
ipcMain.handle("__window_set_size", (_e, args) =>
79+
ipcMain.handle(IPC.WindowClose, () => win.close());
80+
ipcMain.handle(IPC.WindowForceClose, () => win.destroy());
81+
ipcMain.handle(IPC.WindowHide, () => win.hide());
82+
ipcMain.handle(IPC.WindowMaximize, () => win.maximize());
83+
ipcMain.handle(IPC.WindowUnmaximize, () => win.unmaximize());
84+
ipcMain.handle(IPC.WindowSetSize, (_e, args) =>
8485
win.setSize(args.width, args.height)
8586
);
86-
ipcMain.handle("__window_set_position", (_e, args) =>
87+
ipcMain.handle(IPC.WindowSetPosition, (_e, args) =>
8788
win.setPosition(args.x, args.y)
8889
);
89-
ipcMain.handle("__window_get_position", () => {
90+
ipcMain.handle(IPC.WindowGetPosition, () => {
9091
const [x, y] = win.getPosition();
9192
return { x, y };
9293
});
93-
ipcMain.handle("__window_get_size", () => {
94+
ipcMain.handle(IPC.WindowGetSize, () => {
9495
const [width, height] = win.getSize();
9596
return { width, height };
9697
});
9798

9899
// --- Dialog ---
99-
ipcMain.handle("__dialog_confirm", async (_e, args) => {
100+
ipcMain.handle(IPC.DialogConfirm, async (_e, args) => {
100101
const result = await dialog.showMessageBox(win, {
101102
type: args.kind === "warning" ? "warning" : "question",
102103
title: args.title || "Confirm",
@@ -108,7 +109,7 @@ export function registerAllHandlers(win: BrowserWindow): void {
108109
return result.response === 0;
109110
});
110111

111-
ipcMain.handle("__dialog_open", async (_e, args) => {
112+
ipcMain.handle(IPC.DialogOpen, async (_e, args) => {
112113
const properties: Array<
113114
"openDirectory" | "openFile" | "multiSelections"
114115
> = [];
@@ -121,25 +122,25 @@ export function registerAllHandlers(win: BrowserWindow): void {
121122
});
122123

123124
// --- Shell/Opener ---
124-
ipcMain.handle("__shell_reveal", (_e, filePath) => {
125+
ipcMain.handle(IPC.ShellReveal, (_e, filePath) => {
125126
shell.showItemInFolder(filePath as string);
126127
});
127128

128129
// --- Forward window events to renderer ---
129130
win.on("focus", () => {
130-
if (!win.isDestroyed()) win.webContents.send("__window_focus");
131+
if (!win.isDestroyed()) win.webContents.send(IPC.WindowFocus);
131132
});
132133
win.on("blur", () => {
133-
if (!win.isDestroyed()) win.webContents.send("__window_blur");
134+
if (!win.isDestroyed()) win.webContents.send(IPC.WindowBlur);
134135
});
135136
win.on("resize", () => {
136-
if (!win.isDestroyed()) win.webContents.send("__window_resized");
137+
if (!win.isDestroyed()) win.webContents.send(IPC.WindowResized);
137138
});
138139
win.on("move", () => {
139-
if (!win.isDestroyed()) win.webContents.send("__window_moved");
140+
if (!win.isDestroyed()) win.webContents.send(IPC.WindowMoved);
140141
});
141142
win.on("close", (e) => {
142143
e.preventDefault();
143-
if (!win.isDestroyed()) win.webContents.send("__window_close_requested");
144+
if (!win.isDestroyed()) win.webContents.send(IPC.WindowCloseRequested);
144145
});
145146
}

src/App.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import "@xterm/xterm/css/xterm.css";
22
import "./styles.css";
33
import { onMount, onCleanup, createEffect, Show, ErrorBoundary, createSignal } from "solid-js";
44
import { invoke } from "./lib/ipc";
5+
import { IPC } from "../electron/ipc/channels";
56
import { appWindow } from "./lib/window";
67
import { confirm } from "./lib/dialog";
78
import { Sidebar } from "./components/Sidebar";
@@ -187,7 +188,7 @@ function App() {
187188
return;
188189
}
189190

190-
const runningCount = await invoke<number>("count_running_agents").catch(() => 0);
191+
const runningCount = await invoke<number>(IPC.CountRunningAgents).catch(() => 0);
191192
if (runningCount <= 0) return;
192193

193194
event.preventDefault();
@@ -205,7 +206,7 @@ function App() {
205206
).catch(() => false);
206207

207208
if (shouldKill) {
208-
await invoke("kill_all_agents").catch(console.error);
209+
await invoke(IPC.KillAllAgents).catch(console.error);
209210
allowClose = true;
210211
await appWindow.close().catch(console.error);
211212
return;

src/components/ChangedFilesList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createSignal, createMemo, createEffect, onCleanup, For, Show } from "solid-js";
22
import { invoke } from "../lib/ipc";
3+
import { IPC } from "../../electron/ipc/channels";
34
import { theme } from "../lib/theme";
45
import { sf } from "../lib/fontScale";
56
import { getStatusColor } from "../lib/status-colors";
@@ -46,7 +47,7 @@ export function ChangedFilesList(props: ChangedFilesListProps) {
4647
if (!path || inFlight) return;
4748
inFlight = true;
4849
try {
49-
const result = await invoke<ChangedFile[]>("get_changed_files", {
50+
const result = await invoke<ChangedFile[]>(IPC.GetChangedFiles, {
5051
worktreePath: path,
5152
});
5253
if (!cancelled) setFiles(result);

src/components/DiffViewerDialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Show, createSignal, createEffect, onCleanup } from "solid-js";
22
import { Portal } from "solid-js/web";
33
import { createFocusRestore } from "../lib/focus-restore";
44
import { invoke } from "../lib/ipc";
5+
import { IPC } from "../../electron/ipc/channels";
56
import { DiffView, DiffModeEnum } from "@git-diff-view/solid";
67
import "@git-diff-view/solid/styles/diff-view.css";
78
import { theme } from "../lib/theme";
@@ -84,7 +85,7 @@ export function DiffViewerDialog(props: DiffViewerDialogProps) {
8485
setBinary(false);
8586
setRawDiff("");
8687

87-
invoke<string>("get_file_diff", {
88+
invoke<string>(IPC.GetFileDiff, {
8889
worktreePath: props.worktreePath,
8990
filePath: file.path,
9091
})

src/components/NewTaskDialog.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createSignal, createEffect, For, Show, onMount, onCleanup } from "solid-js";
22
import { createFocusRestore } from "../lib/focus-restore";
33
import { invoke } from "../lib/ipc";
4+
import { IPC } from "../../electron/ipc/channels";
45
import { store, createTask, createDirectTask, toggleNewTaskDialog, loadAgents, getProjectPath, getProject, getProjectBranchPrefix, updateProject, hasDirectModeTask } from "../store/store";
56
import { toBranchName, sanitizeBranchPrefix } from "../lib/branch-name";
67
import { cleanTaskName } from "../lib/clean-task-name";
@@ -63,7 +64,7 @@ export function NewTaskDialog() {
6364

6465
void (async () => {
6566
try {
66-
const dirs = await invoke<string[]>("get_gitignored_dirs", { projectRoot: path });
67+
const dirs = await invoke<string[]>(IPC.GetGitignoredDirs, { projectRoot: path });
6768
if (cancelled) return;
6869
setIgnoredDirs(dirs);
6970
setSelectedDirs(new Set(dirs)); // all checked by default
@@ -149,8 +150,8 @@ export function NewTaskDialog() {
149150
if (directMode()) {
150151
const projectPath = getProjectPath(projectId);
151152
if (!projectPath) { setError("Project path not found"); return; }
152-
const mainBranch = await invoke<string>("get_main_branch", { projectRoot: projectPath });
153-
const currentBranch = await invoke<string>("get_current_branch", { projectRoot: projectPath });
153+
const mainBranch = await invoke<string>(IPC.GetMainBranch, { projectRoot: projectPath });
154+
const currentBranch = await invoke<string>(IPC.GetCurrentBranch, { projectRoot: projectPath });
154155
if (currentBranch !== mainBranch) {
155156
setError(`Repository is on branch "${currentBranch}", not "${mainBranch}". Please checkout ${mainBranch} first.`);
156157
return;

src/components/PromptInput.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createSignal, createEffect, onMount, onCleanup } from "solid-js";
22
import { invoke } from "../lib/ipc";
3+
import { IPC } from "../../electron/ipc/channels";
34
import {
45
sendPrompt,
56
registerFocusFn,
@@ -227,7 +228,7 @@ export function PromptInput(props: PromptInputProps) {
227228
const val = text().trim();
228229
if (!val) {
229230
if (mode === "auto") return;
230-
await invoke("write_to_agent", { agentId: props.agentId, data: "\r" });
231+
await invoke(IPC.WriteToAgent, { agentId: props.agentId, data: "\r" });
231232
return;
232233
}
233234

src/components/TaskDialogs.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Show, For, createSignal, createResource, createEffect } from "solid-js";
22
import { invoke } from "../lib/ipc";
3+
import { IPC } from "../../electron/ipc/channels";
34
import {
45
closeTask,
56
mergeTask,
@@ -46,15 +47,15 @@ export function TaskDialogs(props: TaskDialogsProps) {
4647
// --- Resources ---
4748
const [branchLog] = createResource(
4849
() => props.showMergeConfirm ? props.task.worktreePath : null,
49-
(path) => invoke<string>("get_branch_log", { worktreePath: path }),
50+
(path) => invoke<string>(IPC.GetBranchLog, { worktreePath: path }),
5051
);
5152
const [worktreeStatus] = createResource(
5253
() => (props.showMergeConfirm || (props.showCloseConfirm && !props.task.directMode)) ? props.task.worktreePath : null,
53-
(path) => invoke<WorktreeStatus>("get_worktree_status", { worktreePath: path }),
54+
(path) => invoke<WorktreeStatus>(IPC.GetWorktreeStatus, { worktreePath: path }),
5455
);
5556
const [mergeStatus, { refetch: refetchMergeStatus }] = createResource(
5657
() => props.showMergeConfirm ? props.task.worktreePath : null,
57-
(path) => invoke<MergeStatus>("check_merge_status", { worktreePath: path }),
58+
(path) => invoke<MergeStatus>(IPC.CheckMergeStatus, { worktreePath: path }),
5859
);
5960

6061
const hasConflicts = () => (mergeStatus()?.conflicting_files.length ?? 0) > 0;
@@ -243,7 +244,7 @@ export function TaskDialogs(props: TaskDialogsProps) {
243244
setRebaseError("");
244245
setRebaseSuccess(false);
245246
try {
246-
await invoke("rebase_task", { worktreePath: props.task.worktreePath });
247+
await invoke(IPC.RebaseTask, { worktreePath: props.task.worktreePath });
247248
setRebaseSuccess(true);
248249
refetchMergeStatus();
249250
} catch (err) {

0 commit comments

Comments
 (0)