| ... | ... | @@ -35,6 +35,7 @@ import { useRemoteOpenState, type RemoteOpenMode } from "../../remoteOpen"; |
| 35 | 35 | import { usePrimaryEnvironmentId } from "../../state/environments"; |
| 36 | 36 | import { useT3ProjectFileScripts } from "~/hooks/useT3ProjectFileScripts"; |
| 37 | 37 | import { useThreadActionMenu } from "~/hooks/useThreadActionMenu"; |
| 38 | import { readLocalApi } from "~/localApi"; |
| 38 | 39 | import { threadEnvironment } from "../../state/threads"; |
| 39 | 40 | import { useAtomCommand } from "../../state/use-atom-command"; |
| 40 | 41 | import { observeResponsiveBreakpointFade, usePanelAnimationSettings } from "../../panelAnimations"; |
| ... | ... | @@ -66,6 +67,7 @@ interface ChatHeaderProps { |
| 66 | 67 | gitCwd: string | null; |
| 67 | 68 | readonly onOpenPullRequest?: ((number: number) => void) | undefined; |
| 68 | 69 | onNewThreadInProject: () => void; |
| 70 | onOpenProjectSettings?: (() => void) | undefined; |
| 69 | 71 | onRunProjectScript: (script: ProjectScript) => void; |
| 70 | 72 | onAddProjectScript: (input: NewProjectScriptInput) => Promise<ProjectScriptActionResult>; |
| 71 | 73 | onUpdateProjectScript: ( |
| ... | ... | @@ -137,6 +139,7 @@ export const ChatHeader = memo(function ChatHeader({ |
| 137 | 139 | gitCwd, |
| 138 | 140 | onOpenPullRequest, |
| 139 | 141 | onNewThreadInProject, |
| 142 | onOpenProjectSettings, |
| 140 | 143 | onRunProjectScript, |
| 141 | 144 | onAddProjectScript, |
| 142 | 145 | onUpdateProjectScript, |
| ... | ... | @@ -275,15 +278,29 @@ export const ChatHeader = memo(function ChatHeader({ |
| 275 | 278 | ); |
| 276 | 279 | const handleHeaderContextMenu = useCallback( |
| 277 | 280 | (event: ReactMouseEvent) => { |
| 278 | | if (!isServerThread || renamingTitle !== null) return; |
| 281 | if (renamingTitle !== null) return; |
| 279 | 282 | // The right-side controls (git, scripts, open-in) keep their own |
| 280 | 283 | // behavior; only the breadcrumb area opens the thread menu. |
| 281 | 284 | if ((event.target as HTMLElement).closest("[data-chat-header-actions]")) return; |
| 285 | if (!isServerThread && onOpenProjectSettings === undefined) return; |
| 282 | 286 | cancelPendingTitleMenu(); |
| 283 | 287 | event.preventDefault(); |
| 288 | if (!isServerThread) { |
| 289 | const api = readLocalApi(); |
| 290 | if (!api) return; |
| 291 | void api.contextMenu |
| 292 | .show([{ id: "project-settings", label: "Project settings", icon: "settings" }], { |
| 293 | x: event.clientX, |
| 294 | y: event.clientY, |
| 295 | }) |
| 296 | .then((action) => { |
| 297 | if (action === "project-settings") onOpenProjectSettings?.(); |
| 298 | }); |
| 299 | return; |
| 300 | } |
| 284 | 301 | openMenu({ x: event.clientX, y: event.clientY }); |
| 285 | 302 | }, |
| 286 | | [cancelPendingTitleMenu, isServerThread, openMenu, renamingTitle], |
| 303 | [cancelPendingTitleMenu, isServerThread, onOpenProjectSettings, openMenu, renamingTitle], |
| 287 | 304 | ); |
| 288 | 305 | const handleRenameKeyDown = useCallback( |
| 289 | 306 | (event: ReactKeyboardEvent<HTMLInputElement>) => { |