| ... | @@ -3293,6 +3293,7 @@ export default function ChatView({ threadId }: ChatViewProps) { | ... | @@ -3293,6 +3293,7 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 3293 | activeThreadId={activeThread.id} | 3293 | activeThreadId={activeThread.id} |
| 3294 | activeThreadTitle={activeThread.title} | 3294 | activeThreadTitle={activeThread.title} |
| 3295 | activeProjectName={activeProject?.name} | 3295 | activeProjectName={activeProject?.name} |
| | 3296 | openInCwd={activeThread.worktreePath ?? activeProject?.cwd ?? null} |
| 3296 | activeProjectScripts={activeProject?.scripts} | 3297 | activeProjectScripts={activeProject?.scripts} |
| 3297 | preferredScriptId={ | 3298 | preferredScriptId={ |
| 3298 | activeProject ? (lastInvokedScriptByProjectId[activeProject.id] ?? null) : null | 3299 | activeProject ? (lastInvokedScriptByProjectId[activeProject.id] ?? null) : null |
| ... | @@ -3855,6 +3856,7 @@ interface ChatHeaderProps { | ... | @@ -3855,6 +3856,7 @@ interface ChatHeaderProps { |
| 3855 | activeThreadId: ThreadId; | 3856 | activeThreadId: ThreadId; |
| 3856 | activeThreadTitle: string; | 3857 | activeThreadTitle: string; |
| 3857 | activeProjectName: string | undefined; | 3858 | activeProjectName: string | undefined; |
| | 3859 | openInCwd: string | null; |
| 3858 | activeProjectScripts: ProjectScript[] | undefined; | 3860 | activeProjectScripts: ProjectScript[] | undefined; |
| 3859 | preferredScriptId: string | null; | 3861 | preferredScriptId: string | null; |
| 3860 | keybindings: ResolvedKeybindingsConfig; | 3862 | keybindings: ResolvedKeybindingsConfig; |
| ... | @@ -3872,6 +3874,7 @@ const ChatHeader = memo(function ChatHeader({ | ... | @@ -3872,6 +3874,7 @@ const ChatHeader = memo(function ChatHeader({ |
| 3872 | activeThreadId, | 3874 | activeThreadId, |
| 3873 | activeThreadTitle, | 3875 | activeThreadTitle, |
| 3874 | activeProjectName, | 3876 | activeProjectName, |
| | 3877 | openInCwd, |
| 3875 | activeProjectScripts, | 3878 | activeProjectScripts, |
| 3876 | preferredScriptId, | 3879 | preferredScriptId, |
| 3877 | keybindings, | 3880 | keybindings, |
| ... | @@ -3915,7 +3918,7 @@ const ChatHeader = memo(function ChatHeader({ | ... | @@ -3915,7 +3918,7 @@ const ChatHeader = memo(function ChatHeader({ |
| 3915 | <OpenInPicker | 3918 | <OpenInPicker |
| 3916 | keybindings={keybindings} | 3919 | keybindings={keybindings} |
| 3917 | availableEditors={availableEditors} | 3920 | availableEditors={availableEditors} |
| 3918 | activeThreadId={activeThreadId} | 3921 | openInCwd={openInCwd} |
| 3919 | /> | 3922 | /> |
| 3920 | )} | 3923 | )} |
| 3921 | {activeProjectName && <GitActionsControl gitCwd={gitCwd} activeThreadId={activeThreadId} />} | 3924 | {activeProjectName && <GitActionsControl gitCwd={gitCwd} activeThreadId={activeThreadId} />} |
| ... | @@ -5392,11 +5395,11 @@ const CodexTraitsPicker = memo(function CodexTraitsPicker(props: { | ... | @@ -5392,11 +5395,11 @@ const CodexTraitsPicker = memo(function CodexTraitsPicker(props: { |
| 5392 | const OpenInPicker = memo(function OpenInPicker({ | 5395 | const OpenInPicker = memo(function OpenInPicker({ |
| 5393 | keybindings, | 5396 | keybindings, |
| 5394 | availableEditors, | 5397 | availableEditors, |
| 5395 | activeThreadId, | 5398 | openInCwd, |
| 5396 | }: { | 5399 | }: { |
| 5397 | keybindings: ResolvedKeybindingsConfig; | 5400 | keybindings: ResolvedKeybindingsConfig; |
| 5398 | availableEditors: ReadonlyArray<EditorId>; | 5401 | availableEditors: ReadonlyArray<EditorId>; |
| 5399 | activeThreadId: ThreadId | null; | 5402 | openInCwd: string | null; |
| 5400 | }) { | 5403 | }) { |
| 5401 | const [lastEditor, setLastEditor] = useState<EditorId>(() => { | 5404 | const [lastEditor, setLastEditor] = useState<EditorId>(() => { |
| 5402 | const stored = localStorage.getItem(LAST_EDITOR_KEY); | 5405 | const stored = localStorage.getItem(LAST_EDITOR_KEY); |
| ... | @@ -5442,26 +5445,17 @@ const OpenInPicker = memo(function OpenInPicker({ | ... | @@ -5442,26 +5445,17 @@ const OpenInPicker = memo(function OpenInPicker({ |
| 5442 | : (options[0]?.value ?? null); | 5445 | : (options[0]?.value ?? null); |
| 5443 | const primaryOption = options.find(({ value }) => value === effectiveEditor) ?? null; | 5446 | const primaryOption = options.find(({ value }) => value === effectiveEditor) ?? null; |
| 5444 | | 5447 | |
| 5445 | const activeThread = useStore((store) => | | |
| 5446 | activeThreadId ? store.threads.find((thread) => thread.id === activeThreadId) : undefined, | | |
| 5447 | ); | | |
| 5448 | const activeProjectId = activeThread?.projectId ?? null; | | |
| 5449 | const activeProject = useStore((store) => | | |
| 5450 | activeProjectId ? store.projects.find((project) => project.id === activeProjectId) : undefined, | | |
| 5451 | ); | | |
| 5452 | | | |
| 5453 | const openInEditor = useCallback( | 5448 | const openInEditor = useCallback( |
| 5454 | (editorId: EditorId | null) => { | 5449 | (editorId: EditorId | null) => { |
| 5455 | const api = readNativeApi(); | 5450 | const api = readNativeApi(); |
| 5456 | if (!api || !activeProject) return; | 5451 | if (!api || !openInCwd) return; |
| 5457 | const editor = editorId ?? effectiveEditor; | 5452 | const editor = editorId ?? effectiveEditor; |
| 5458 | if (!editor) return; | 5453 | if (!editor) return; |
| 5459 | const cwd = activeThread?.worktreePath ?? activeProject.cwd; | 5454 | void api.shell.openInEditor(openInCwd, editor); |
| 5460 | void api.shell.openInEditor(cwd, editor); | | |
| 5461 | localStorage.setItem(LAST_EDITOR_KEY, editor); | 5455 | localStorage.setItem(LAST_EDITOR_KEY, editor); |
| 5462 | setLastEditor(editor); | 5456 | setLastEditor(editor); |
| 5463 | }, | 5457 | }, |
| 5464 | [activeProject, activeThread, effectiveEditor, setLastEditor], | 5458 | [effectiveEditor, openInCwd, setLastEditor], |
| 5465 | ); | 5459 | ); |
| 5466 | | 5460 | |
| 5467 | const openFavoriteEditorShortcutLabel = useMemo( | 5461 | const openFavoriteEditorShortcutLabel = useMemo( |
| ... | @@ -5473,23 +5467,22 @@ const OpenInPicker = memo(function OpenInPicker({ | ... | @@ -5473,23 +5467,22 @@ const OpenInPicker = memo(function OpenInPicker({ |
| 5473 | const handler = (e: globalThis.KeyboardEvent) => { | 5467 | const handler = (e: globalThis.KeyboardEvent) => { |
| 5474 | const api = readNativeApi(); | 5468 | const api = readNativeApi(); |
| 5475 | if (!isOpenFavoriteEditorShortcut(e, keybindings)) return; | 5469 | if (!isOpenFavoriteEditorShortcut(e, keybindings)) return; |
| 5476 | if (!api || !activeProject) return; | 5470 | if (!api || !openInCwd) return; |
| 5477 | if (!effectiveEditor) return; | 5471 | if (!effectiveEditor) return; |
| 5478 | | 5472 | |
| 5479 | e.preventDefault(); | 5473 | e.preventDefault(); |
| 5480 | const cwd = activeThread?.worktreePath ?? activeProject.cwd; | 5474 | void api.shell.openInEditor(openInCwd, effectiveEditor); |
| 5481 | void api.shell.openInEditor(cwd, effectiveEditor); | | |
| 5482 | }; | 5475 | }; |
| 5483 | window.addEventListener("keydown", handler); | 5476 | window.addEventListener("keydown", handler); |
| 5484 | return () => window.removeEventListener("keydown", handler); | 5477 | return () => window.removeEventListener("keydown", handler); |
| 5485 | }, [activeProject, activeThread, keybindings, effectiveEditor]); | 5478 | }, [effectiveEditor, keybindings, openInCwd]); |
| 5486 | | 5479 | |
| 5487 | return ( | 5480 | return ( |
| 5488 | <Group aria-label="Subscription actions"> | 5481 | <Group aria-label="Subscription actions"> |
| 5489 | <Button | 5482 | <Button |
| 5490 | size="xs" | 5483 | size="xs" |
| 5491 | variant="outline" | 5484 | variant="outline" |
| 5492 | disabled={!effectiveEditor || !activeProject} | 5485 | disabled={!effectiveEditor || !openInCwd} |
| 5493 | onClick={() => openInEditor(effectiveEditor)} | 5486 | onClick={() => openInEditor(effectiveEditor)} |
| 5494 | > | 5487 | > |
| 5495 | {primaryOption?.Icon && <primaryOption.Icon aria-hidden="true" className="size-3.5" />} | 5488 | {primaryOption?.Icon && <primaryOption.Icon aria-hidden="true" className="size-3.5" />} |