Merge main
This commit is contained in:
@@ -943,7 +943,12 @@ export const ContextPanel: React.FC = () => {
|
||||
: activeTab?.mode === 'notes'
|
||||
? <ProjectContextPanel />
|
||||
: activeTab?.mode === 'plan'
|
||||
? <React.Suspense fallback={null}><PlanView targetPath={activeTab.targetPath} projectPlanId={activeTab.projectPlanId} /></React.Suspense>
|
||||
? <React.Suspense fallback={null}><PlanView
|
||||
targetPath={activeTab.targetPath}
|
||||
savedProjectPlan={activeTab.projectPlanId && activeTab.projectPlanRef
|
||||
? { projectRef: activeTab.projectPlanRef, planId: activeTab.projectPlanId }
|
||||
: null}
|
||||
/></React.Suspense>
|
||||
: null;
|
||||
|
||||
const browserTabs = React.useMemo(
|
||||
|
||||
@@ -6,63 +6,53 @@ import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
import { formatDirectoryName } from '@/lib/utils';
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { CHAT_DRAFT_PROJECT_ID, getChatsRootForHome, getChatsRootFromDirectory, isChatDirectoryPath } from '@/lib/chatDirectories';
|
||||
import { useProjectContextOwner } from '@/hooks/useProjectContextOwner';
|
||||
import { CHAT_DRAFT_PROJECT_ID } from '@/lib/chatDirectories';
|
||||
import type { ProjectRef } from '@/lib/projectContextApi';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
export const ProjectContextPanel: React.FC<{
|
||||
onActionComplete?: () => void;
|
||||
onOpenPlan?: (plan: { id: string; title: string }) => void;
|
||||
onOpenPlan?: (plan: { id: string; title: string; projectRef: ProjectRef }) => void;
|
||||
}> = ({ onActionComplete, onOpenPlan }) => {
|
||||
const activeProjectId = useProjectsStore((state) => state.activeProjectId);
|
||||
const projects = useProjectsStore((state) => state.projects);
|
||||
const homeDirectory = useDirectoryStore((state) => state.homeDirectory);
|
||||
const { t } = useI18n();
|
||||
const gitDirectories = useGitStore((state) => state.directories);
|
||||
const isChatContext = useSessionUIStore((state) => (
|
||||
state.newSessionDraft.open
|
||||
? state.newSessionDraft.target === 'chat'
|
||||
: isChatDirectoryPath(state.currentSessionDirectory)
|
||||
));
|
||||
const chatSessionDirectory = useSessionUIStore((state) => state.currentSessionDirectory);
|
||||
const chatsRoot = getChatsRootFromDirectory(chatSessionDirectory) ?? getChatsRootForHome(homeDirectory);
|
||||
|
||||
const activeProject = React.useMemo(() => {
|
||||
if (isChatContext) return null;
|
||||
if (activeProjectId) {
|
||||
return projects.find((project) => project.id === activeProjectId) ?? projects[0] ?? null;
|
||||
}
|
||||
return projects[0] ?? null;
|
||||
}, [activeProjectId, isChatContext, projects]);
|
||||
// One owner decision shared with the panel, agent memory, and PlanView:
|
||||
// chats resolve to the Chats owner, worktrees to their project, and an
|
||||
// unrecognized directory owns nothing (null) rather than borrowing
|
||||
// whichever project happens to be active.
|
||||
const projectRef = useProjectContextOwner(chatSessionDirectory);
|
||||
|
||||
const projectRef = React.useMemo(() => {
|
||||
if (isChatContext && chatsRoot) {
|
||||
return { id: CHAT_DRAFT_PROJECT_ID, path: chatsRoot };
|
||||
}
|
||||
if (!activeProject) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id: activeProject.id,
|
||||
path: activeProject.path,
|
||||
};
|
||||
}, [activeProject, chatsRoot, isChatContext]);
|
||||
// Display-only lookup: a user-renamed project label wins over the directory
|
||||
// name. The owner decision stays with the hook — this must not reintroduce
|
||||
// a fallback.
|
||||
const projects = useProjectsStore((state) => state.projects);
|
||||
const labeledProject = React.useMemo(
|
||||
() => (projectRef ? projects.find((project) => project.id === projectRef.id) ?? null : null),
|
||||
[projectRef, projects],
|
||||
);
|
||||
|
||||
const projectLabel = React.useMemo(() => {
|
||||
if (isChatContext) return t('sessions.sidebar.activity.chatsTitle');
|
||||
if (!activeProject) {
|
||||
if (!projectRef) {
|
||||
return null;
|
||||
}
|
||||
return activeProject.label?.trim()
|
||||
|| formatDirectoryName(activeProject.path, homeDirectory)
|
||||
|| activeProject.path;
|
||||
}, [activeProject, homeDirectory, isChatContext, t]);
|
||||
if (projectRef.id === CHAT_DRAFT_PROJECT_ID) {
|
||||
return t('sessions.sidebar.activity.chatsTitle');
|
||||
}
|
||||
return labeledProject?.label?.trim()
|
||||
|| formatDirectoryName(projectRef.path, homeDirectory)
|
||||
|| projectRef.path;
|
||||
}, [homeDirectory, labeledProject, projectRef, t]);
|
||||
|
||||
const canCreateWorktree = React.useMemo(() => {
|
||||
if (!activeProject) {
|
||||
if (!projectRef || projectRef.id === CHAT_DRAFT_PROJECT_ID) {
|
||||
return false;
|
||||
}
|
||||
return gitDirectories.get(activeProject.path)?.isGitRepo === true;
|
||||
}, [activeProject, gitDirectories]);
|
||||
return gitDirectories.get(projectRef.path)?.isGitRepo === true;
|
||||
}, [gitDirectories, projectRef]);
|
||||
|
||||
return (
|
||||
/* The panel scrolls its own tab content; a scroller here would nest. */
|
||||
|
||||
Reference in New Issue
Block a user