Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,16 @@ pub fn run() {
let check_updates_item =
MenuItemBuilder::with_id("check_for_updates", "Check for Updates...")
.build(handle)?;
let settings_item =
MenuItemBuilder::with_id("file_open_settings", "Settings...").build(handle)?;
let app_menu = Submenu::with_items(
handle,
app_name.clone(),
true,
&[
&about_item,
&check_updates_item,
&settings_item,
&PredefinedMenuItem::separator(handle)?,
&PredefinedMenuItem::services(handle, None)?,
&PredefinedMenuItem::separator(handle)?,
Expand All @@ -63,6 +66,17 @@ pub fn run() {
],
)?;

let new_agent_item =
MenuItemBuilder::with_id("file_new_agent", "New Agent").build(handle)?;
let new_worktree_agent_item =
MenuItemBuilder::with_id("file_new_worktree_agent", "New Worktree Agent")
.build(handle)?;
let new_clone_agent_item =
MenuItemBuilder::with_id("file_new_clone_agent", "New Clone Agent")
.build(handle)?;
let add_workspace_item =
MenuItemBuilder::with_id("file_add_workspace", "Add Workspace...").build(handle)?;

#[cfg(target_os = "linux")]
let file_menu = {
let close_window_item =
Expand All @@ -72,7 +86,16 @@ pub fn run() {
handle,
"File",
true,
&[&close_window_item, &quit_item],
&[
&new_agent_item,
&new_worktree_agent_item,
&new_clone_agent_item,
&PredefinedMenuItem::separator(handle)?,
&add_workspace_item,
&PredefinedMenuItem::separator(handle)?,
&close_window_item,
&quit_item,
],
)?
};
#[cfg(not(target_os = "linux"))]
Expand All @@ -81,6 +104,12 @@ pub fn run() {
"File",
true,
&[
&new_agent_item,
&new_worktree_agent_item,
&new_clone_agent_item,
&PredefinedMenuItem::separator(handle)?,
&add_workspace_item,
&PredefinedMenuItem::separator(handle)?,
&PredefinedMenuItem::close_window(handle, None)?,
#[cfg(not(target_os = "macos"))]
&PredefinedMenuItem::quit(handle, None)?,
Expand Down Expand Up @@ -194,6 +223,21 @@ pub fn run() {
"check_for_updates" => {
let _ = app.emit("updater-check", ());
}
"file_new_agent" => {
let _ = app.emit("menu-new-agent", ());
}
"file_new_worktree_agent" => {
let _ = app.emit("menu-new-worktree-agent", ());
}
"file_new_clone_agent" => {
let _ = app.emit("menu-new-clone-agent", ());
}
"file_add_workspace" => {
let _ = app.emit("menu-add-workspace", ());
}
"file_open_settings" => {
let _ = app.emit("menu-open-settings", ());
}
"file_close_window" | "window_close" => {
if let Some(window) = app.get_webview_window("main") {
let _ = window.close();
Expand Down
171 changes: 136 additions & 35 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,14 @@ import { playNotificationSound } from "./utils/notificationSounds";
import {
pickWorkspacePath,
} from "./services/tauri";
import { subscribeUpdaterCheck } from "./services/events";
import {
subscribeMenuAddWorkspace,
subscribeMenuNewAgent,
subscribeMenuNewCloneAgent,
subscribeMenuNewWorktreeAgent,
subscribeMenuOpenSettings,
subscribeUpdaterCheck,
} from "./services/events";
import type {
AccessMode,
GitHubPullRequest,
Expand Down Expand Up @@ -1039,16 +1046,7 @@ function MainApp() {
listThreadsForWorkspace
});

useNewAgentShortcut({
isEnabled: Boolean(activeWorkspace),
onTrigger: () => {
if (activeWorkspace) {
void handleAddAgent(activeWorkspace);
}
},
});

async function handleAddWorkspace() {
const handleAddWorkspace = useCallback(async () => {
try {
const workspace = await addWorkspace();
if (workspace) {
Expand All @@ -1068,34 +1066,56 @@ function MainApp() {
});
alert(`Failed to add workspace.\n\n${message}`);
}
}
}, [addDebugEntry, addWorkspace, isCompact, setActiveTab, setActiveThreadId]);

const handleAddAgent = useCallback(
async (workspace: (typeof workspaces)[number]) => {
exitDiffView();
selectWorkspace(workspace.id);
if (!workspace.connected) {
await connectWorkspace(workspace);
}
await startThreadForWorkspace(workspace.id);
if (isCompact) {
setActiveTab("codex");
}
// Focus the composer input after creating the agent
setTimeout(() => composerInputRef.current?.focus(), 0);
},
[
connectWorkspace,
exitDiffView,
isCompact,
selectWorkspace,
setActiveTab,
startThreadForWorkspace,
],
);

async function handleAddAgent(workspace: (typeof workspaces)[number]) {
exitDiffView();
selectWorkspace(workspace.id);
if (!workspace.connected) {
await connectWorkspace(workspace);
}
await startThreadForWorkspace(workspace.id);
if (isCompact) {
setActiveTab("codex");
}
// Focus the composer input after creating the agent
setTimeout(() => composerInputRef.current?.focus(), 0);
}
const handleAddWorktreeAgent = useCallback(
async (workspace: (typeof workspaces)[number]) => {
exitDiffView();
openWorktreePrompt(workspace);
},
[exitDiffView, openWorktreePrompt],
);

async function handleAddWorktreeAgent(
workspace: (typeof workspaces)[number]
) {
exitDiffView();
openWorktreePrompt(workspace);
}
const handleAddCloneAgent = useCallback(
async (workspace: (typeof workspaces)[number]) => {
exitDiffView();
openClonePrompt(workspace);
},
[exitDiffView, openClonePrompt],
);

async function handleAddCloneAgent(workspace: (typeof workspaces)[number]) {
exitDiffView();
openClonePrompt(workspace);
}
useNewAgentShortcut({
isEnabled: Boolean(activeWorkspace),
onTrigger: () => {
if (activeWorkspace) {
void handleAddAgent(activeWorkspace);
}
},
});

function handleSelectDiff(path: string) {
setSelectedDiffPath(path);
Expand Down Expand Up @@ -1187,6 +1207,87 @@ function MainApp() {
[],
);

useEffect(() => {
let unlistenNewAgent: (() => void) | null = null;
let unlistenNewWorktree: (() => void) | null = null;
let unlistenNewClone: (() => void) | null = null;
let unlistenAddWorkspace: (() => void) | null = null;
let unlistenOpenSettings: (() => void) | null = null;
const baseWorkspace = activeParentWorkspace ?? activeWorkspace;

subscribeMenuNewAgent(() => {
if (activeWorkspace) {
void handleAddAgent(activeWorkspace);
}
})
.then((handler) => {
unlistenNewAgent = handler;
})
.catch(() => {});

subscribeMenuNewWorktreeAgent(() => {
if (baseWorkspace) {
void handleAddWorktreeAgent(baseWorkspace);
}
})
.then((handler) => {
unlistenNewWorktree = handler;
})
.catch(() => {});

subscribeMenuNewCloneAgent(() => {
if (baseWorkspace) {
void handleAddCloneAgent(baseWorkspace);
}
})
.then((handler) => {
unlistenNewClone = handler;
})
.catch(() => {});

subscribeMenuAddWorkspace(() => {
void handleAddWorkspace();
})
.then((handler) => {
unlistenAddWorkspace = handler;
})
.catch(() => {});

subscribeMenuOpenSettings(() => {
handleOpenSettings();
})
.then((handler) => {
unlistenOpenSettings = handler;
})
.catch(() => {});

return () => {
if (unlistenNewAgent) {
unlistenNewAgent();
}
if (unlistenNewWorktree) {
unlistenNewWorktree();
}
if (unlistenNewClone) {
unlistenNewClone();
}
if (unlistenAddWorkspace) {
unlistenAddWorkspace();
}
if (unlistenOpenSettings) {
unlistenOpenSettings();
}
};
}, [
activeParentWorkspace,
activeWorkspace,
handleAddAgent,
handleAddCloneAgent,
handleAddWorkspace,
handleAddWorktreeAgent,
handleOpenSettings,
]);

const orderValue = (entry: WorkspaceInfo) =>
typeof entry.settings.sortOrder === "number"
? entry.settings.sortOrder
Expand Down
40 changes: 40 additions & 0 deletions src/services/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,43 @@ export async function subscribeUpdaterCheck(
onEvent();
});
}

export async function subscribeMenuNewAgent(
onEvent: () => void,
): Promise<Unsubscribe> {
return listen("menu-new-agent", () => {
onEvent();
});
}

export async function subscribeMenuNewWorktreeAgent(
onEvent: () => void,
): Promise<Unsubscribe> {
return listen("menu-new-worktree-agent", () => {
onEvent();
});
}

export async function subscribeMenuNewCloneAgent(
onEvent: () => void,
): Promise<Unsubscribe> {
return listen("menu-new-clone-agent", () => {
onEvent();
});
}

export async function subscribeMenuAddWorkspace(
onEvent: () => void,
): Promise<Unsubscribe> {
return listen("menu-add-workspace", () => {
onEvent();
});
}

export async function subscribeMenuOpenSettings(
onEvent: () => void,
): Promise<Unsubscribe> {
return listen("menu-open-settings", () => {
onEvent();
});
}