feat(ui): redesign workspace shell with context panel, tabbed sidebars, and faster diff UX (#433)
* feat: tabbed right sidebar, context panel, floating diff comments * fix: auto-close left sidebar when context panel opens - Increase default context panel width from 520 to 600 pixels - Increase sidebar minimum width from 200 to 300 pixels - Replace collapsible component with custom button in diff view * refactoring: rework sidebars, tabs, and file tree layout - Rewrite AnimatedTabs as segment-style with sliding indicator - Upgrade SidebarFilesTree to match FilesView features (context menus, git status, file icons, CRUD dialogs, fuzzy search ranking) - Restructure FilesView header: tabs row + actions row, remove breadcrumbs - Show relative path in context panel header, track active tab - Allow left sidebar to stay open alongside context panel - Hide diff/files tabs from header on desktop (mobile-only) - Move chevron after group name in session sidebar - Compact tab heights in right sidebar and git view - Size PreviewToggleButton to match other action buttons - Remove directory loading spinner from folder icons * feat: add project icon and color customization - Enable users to assign custom icons to projects - Allow users to choose accent colors for projects - Stabilize repo status UI during project switching * feat: add scroll fade indicators to editor tabs * style: reduce spacing and icon sizes in header * style: adjust tab component padding from uniform to vertical-horizontal * feat: Add session state indicators to project tabs * feat: Enhance session status handling and improve UI responsiveness * fix: preserve upstream tracking on branch rename * fix: improve initial remote selection for pull requests - Uses saved remote name from previous session when available - Selects remote based on tracking branch when possible - Falls back to origin or first available remote * perf(diff): faster highlight, stable stacked scroll - split/unified Pierre worker pools; prefer shiki-wasm - align diff CSS line-height; disable scroll anchoring; drop WebKit compositing hacks - harden stacked pin/align (cancel on user scroll/input); prevent overscroll - make overlay scrollbar MutationObserver optional; disable for diff container * feat: handle binary files in diff view * fix: adjust project tabs layout and drag regions * style: update drag overlay visual styling * feat: enable number keys to switch projects in the sidebar * fix: recognize octet-stream as text-based MIME type * feat: add keyboard navigation to context panel * feat: add session pinning to sidebar - Pin important sessions to keep them at the top - Pinned sessions persist across browser sessions * refactor: move context usage display from chat input to header
This commit is contained in:
committed by
GitHub
parent
12606b9e53
commit
47c943b487
@@ -43,6 +43,7 @@ import {
|
||||
RiFolderAddLine,
|
||||
RiGitBranchLine,
|
||||
RiGitPullRequestLine,
|
||||
RiGitRepositoryLine,
|
||||
RiStickyNoteLine,
|
||||
RiLinkUnlinkM,
|
||||
|
||||
@@ -50,8 +51,10 @@ import {
|
||||
|
||||
RiMore2Line,
|
||||
RiPencilAiLine,
|
||||
RiPushpinLine,
|
||||
RiShare2Line,
|
||||
RiShieldLine,
|
||||
RiUnpinLine,
|
||||
} from '@remixicon/react';
|
||||
import { sessionEvents } from '@/lib/sessionEvents';
|
||||
import { ArrowsMerge } from '@/components/icons/ArrowsMerge';
|
||||
@@ -74,6 +77,7 @@ import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import { GitHubIssuePickerDialog } from './GitHubIssuePickerDialog';
|
||||
import { GitHubPullRequestPickerDialog } from './GitHubPullRequestPickerDialog';
|
||||
import { ProjectNotesTodoPanel } from './ProjectNotesTodoPanel';
|
||||
import { BranchPickerDialog } from './BranchPickerDialog';
|
||||
|
||||
const ATTENTION_DIAMOND_INDICES = new Set([1, 3, 4, 5, 7]);
|
||||
|
||||
@@ -86,6 +90,7 @@ const GROUP_ORDER_STORAGE_KEY = 'oc.sessions.groupOrder';
|
||||
const GROUP_COLLAPSE_STORAGE_KEY = 'oc.sessions.groupCollapse';
|
||||
const PROJECT_ACTIVE_SESSION_STORAGE_KEY = 'oc.sessions.activeSessionByProject';
|
||||
const SESSION_EXPANDED_STORAGE_KEY = 'oc.sessions.expandedParents';
|
||||
const SESSION_PINNED_STORAGE_KEY = 'oc.sessions.pinned';
|
||||
|
||||
const formatDateLabel = (value: string | number) => {
|
||||
const targetDate = new Date(value);
|
||||
@@ -146,6 +151,28 @@ const toFiniteNumber = (value: unknown): number | undefined => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getSessionCreatedAt = (session: Session): number => {
|
||||
return toFiniteNumber(session.time?.created) ?? 0;
|
||||
};
|
||||
|
||||
const getSessionUpdatedAt = (session: Session): number => {
|
||||
return toFiniteNumber(session.time?.updated) ?? 0;
|
||||
};
|
||||
|
||||
const compareSessionsByPinnedAndTime = (a: Session, b: Session, pinnedSessionIds: Set<string>): number => {
|
||||
const aPinned = pinnedSessionIds.has(a.id);
|
||||
const bPinned = pinnedSessionIds.has(b.id);
|
||||
if (aPinned !== bPinned) {
|
||||
return aPinned ? -1 : 1;
|
||||
}
|
||||
|
||||
if (aPinned && bPinned) {
|
||||
return getSessionCreatedAt(b) - getSessionCreatedAt(a);
|
||||
}
|
||||
|
||||
return getSessionUpdatedAt(b) - getSessionUpdatedAt(a);
|
||||
};
|
||||
|
||||
const centerDragOverlayUnderPointer: Modifier = ({ transform, activeNodeRect, activatorEvent }) => {
|
||||
if (!(activatorEvent instanceof MouseEvent) || !activeNodeRect) {
|
||||
return transform;
|
||||
@@ -525,6 +552,7 @@ interface SessionSidebarProps {
|
||||
onSessionSelected?: (sessionId: string) => void;
|
||||
allowReselect?: boolean;
|
||||
hideDirectoryControls?: boolean;
|
||||
hideProjectSelector?: boolean;
|
||||
showOnlyMainWorkspace?: boolean;
|
||||
}
|
||||
|
||||
@@ -533,6 +561,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
onSessionSelected,
|
||||
allowReselect = false,
|
||||
hideDirectoryControls = false,
|
||||
hideProjectSelector = false,
|
||||
showOnlyMainWorkspace = false,
|
||||
}) => {
|
||||
const [editingId, setEditingId] = React.useState<string | null>(null);
|
||||
@@ -554,9 +583,22 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
const [hoveredProjectId, setHoveredProjectId] = React.useState<string | null>(null);
|
||||
const [issuePickerOpen, setIssuePickerOpen] = React.useState(false);
|
||||
const [pullRequestPickerOpen, setPullRequestPickerOpen] = React.useState(false);
|
||||
const [isBranchPickerOpen, setIsBranchPickerOpen] = React.useState(false);
|
||||
const [projectNotesPanelOpen, setProjectNotesPanelOpen] = React.useState(false);
|
||||
const [stuckProjectHeaders, setStuckProjectHeaders] = React.useState<Set<string>>(new Set());
|
||||
const [openMenuSessionId, setOpenMenuSessionId] = React.useState<string | null>(null);
|
||||
const [pinnedSessionIds, setPinnedSessionIds] = React.useState<Set<string>>(() => {
|
||||
try {
|
||||
const raw = getSafeStorage().getItem(SESSION_PINNED_STORAGE_KEY);
|
||||
if (!raw) {
|
||||
return new Set();
|
||||
}
|
||||
const parsed = JSON.parse(raw) as string[];
|
||||
return new Set(Array.isArray(parsed) ? parsed.filter((item) => typeof item === 'string') : []);
|
||||
} catch {
|
||||
return new Set();
|
||||
}
|
||||
});
|
||||
const [collapsedGroups, setCollapsedGroups] = React.useState<Set<string>>(() => {
|
||||
try {
|
||||
const raw = getSafeStorage().getItem(GROUP_COLLAPSE_STORAGE_KEY);
|
||||
@@ -722,10 +764,46 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
} catch { /* ignored */ }
|
||||
}, [safeStorage]);
|
||||
|
||||
const sortedSessions = React.useMemo(() => {
|
||||
return [...sessions].sort((a, b) => (b.time?.updated || 0) - (a.time?.updated || 0));
|
||||
React.useEffect(() => {
|
||||
const existingSessionIds = new Set(sessions.map((session) => session.id));
|
||||
setPinnedSessionIds((prev) => {
|
||||
let changed = false;
|
||||
const next = new Set<string>();
|
||||
prev.forEach((id) => {
|
||||
if (existingSessionIds.has(id)) {
|
||||
next.add(id);
|
||||
} else {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
return changed ? next : prev;
|
||||
});
|
||||
}, [sessions]);
|
||||
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
safeStorage.setItem(SESSION_PINNED_STORAGE_KEY, JSON.stringify(Array.from(pinnedSessionIds)));
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
}, [pinnedSessionIds, safeStorage]);
|
||||
|
||||
const togglePinnedSession = React.useCallback((sessionId: string) => {
|
||||
setPinnedSessionIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(sessionId)) {
|
||||
next.delete(sessionId);
|
||||
} else {
|
||||
next.add(sessionId);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const sortedSessions = React.useMemo(() => {
|
||||
return [...sessions].sort((a, b) => compareSessionsByPinnedAndTime(a, b, pinnedSessionIds));
|
||||
}, [sessions, pinnedSessionIds]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
const normalizedProjects = projects
|
||||
@@ -778,9 +856,9 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
collection.push(session);
|
||||
map.set(parentID, collection);
|
||||
});
|
||||
map.forEach((list) => list.sort((a, b) => (b.time?.updated || 0) - (a.time?.updated || 0)));
|
||||
map.forEach((list) => list.sort((a, b) => compareSessionsByPinnedAndTime(a, b, pinnedSessionIds)));
|
||||
return map;
|
||||
}, [sortedSessions]);
|
||||
}, [sortedSessions, pinnedSessionIds]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const directories = new Set<string>();
|
||||
@@ -1110,7 +1188,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
projectIsRepo: boolean,
|
||||
) => {
|
||||
const normalizedProjectRoot = normalizePath(projectRoot ?? null);
|
||||
const sortedProjectSessions = [...projectSessions].sort((a, b) => (b.time?.updated || 0) - (a.time?.updated || 0));
|
||||
const sortedProjectSessions = [...projectSessions].sort((a, b) => compareSessionsByPinnedAndTime(a, b, pinnedSessionIds));
|
||||
|
||||
const sessionMap = new Map(sortedProjectSessions.map((session) => [session.id, session]));
|
||||
const childrenMap = new Map<string, Session[]>();
|
||||
@@ -1123,7 +1201,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
collection.push(session);
|
||||
childrenMap.set(parentID, collection);
|
||||
});
|
||||
childrenMap.forEach((list) => list.sort((a, b) => (b.time?.updated || 0) - (a.time?.updated || 0)));
|
||||
childrenMap.forEach((list) => list.sort((a, b) => compareSessionsByPinnedAndTime(a, b, pinnedSessionIds)));
|
||||
|
||||
// Build worktree lookup map
|
||||
const worktreeByPath = new Map<string, WorktreeMetadata>();
|
||||
@@ -1245,7 +1323,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
|
||||
return groups;
|
||||
},
|
||||
[homeDirectory, worktreeMetadata]
|
||||
[homeDirectory, worktreeMetadata, pinnedSessionIds]
|
||||
);
|
||||
|
||||
const toggleGroupSessionLimit = React.useCallback((groupId: string) => {
|
||||
@@ -1390,16 +1468,25 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
[availableWorktreesByProject, getSessionsByDirectory, sessionsByDirectory, isVSCode],
|
||||
);
|
||||
|
||||
// Keep last-known repo status to avoid UI jiggling during project switch
|
||||
const lastRepoStatusRef = React.useRef(false);
|
||||
if (activeProjectId && projectRepoStatus.has(activeProjectId)) {
|
||||
lastRepoStatusRef.current = Boolean(projectRepoStatus.get(activeProjectId));
|
||||
}
|
||||
|
||||
const projectSections = React.useMemo(() => {
|
||||
return normalizedProjects.map((project) => {
|
||||
const projectSessions = getSessionsForProject(project);
|
||||
const worktreesForProject = availableWorktreesByProject.get(project.normalizedPath) ?? [];
|
||||
const isRepo = projectRepoStatus.has(project.id)
|
||||
? Boolean(projectRepoStatus.get(project.id))
|
||||
: lastRepoStatusRef.current;
|
||||
const groups = buildGroupedSessions(
|
||||
projectSessions,
|
||||
project.normalizedPath,
|
||||
worktreesForProject,
|
||||
projectRootBranches.get(project.id) ?? null,
|
||||
Boolean(projectRepoStatus.get(project.id)),
|
||||
isRepo,
|
||||
);
|
||||
return {
|
||||
project,
|
||||
@@ -1429,11 +1516,26 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
: null),
|
||||
[activeProjectForHeader],
|
||||
);
|
||||
const branchPickerProject = React.useMemo(() => {
|
||||
if (!activeProjectForHeader) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id: activeProjectForHeader.id,
|
||||
path: activeProjectForHeader.path,
|
||||
normalizedPath: activeProjectForHeader.normalizedPath,
|
||||
label: activeProjectForHeader.label,
|
||||
};
|
||||
}, [activeProjectForHeader]);
|
||||
|
||||
const activeProjectIsRepo = React.useMemo(
|
||||
() => (activeProjectForHeader ? Boolean(projectRepoStatus.get(activeProjectForHeader.id)) : false),
|
||||
[activeProjectForHeader, projectRepoStatus],
|
||||
);
|
||||
// Only flip to false once the new project's status is actually resolved (present in map)
|
||||
const stableActiveProjectIsRepo = activeProjectForHeader && projectRepoStatus.has(activeProjectForHeader.id)
|
||||
? activeProjectIsRepo
|
||||
: lastRepoStatusRef.current;
|
||||
const reserveHeaderActionsSpace = Boolean(activeProjectForHeader);
|
||||
const useMobileNotesPanel = mobileVariant || deviceInfo.isMobile;
|
||||
|
||||
@@ -1690,6 +1792,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
const isActive = currentSessionId === session.id;
|
||||
const sessionTitle = session.title || 'Untitled Session';
|
||||
const hasChildren = node.children.length > 0;
|
||||
const isPinnedSession = pinnedSessionIds.has(session.id);
|
||||
const isExpanded = expandedParents.has(session.id);
|
||||
const needsAttention = sessionAttentionStates.get(session.id)?.needsAttention === true;
|
||||
const sessionSummary = session.summary as
|
||||
@@ -1834,8 +1937,8 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
)}
|
||||
>
|
||||
{}
|
||||
<div className="flex w-full items-center gap-2 min-w-0 flex-1 overflow-hidden">
|
||||
{showStatusMarker ? (
|
||||
<div className="flex w-full items-center gap-2 min-w-0 flex-1 overflow-hidden">
|
||||
{showStatusMarker ? (
|
||||
<span className="inline-flex h-3.5 w-3.5 flex-shrink-0 items-center justify-center">
|
||||
{isStreaming ? (
|
||||
<GridLoader size="xs" className="text-primary" />
|
||||
@@ -1856,6 +1959,9 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
{isPinnedSession ? (
|
||||
<RiPushpinLine className="h-3 w-3 flex-shrink-0 text-primary" aria-label="Pinned session" />
|
||||
) : null}
|
||||
<div className="block min-w-0 flex-1 truncate typography-ui-label font-normal text-foreground">
|
||||
{sessionTitle}
|
||||
</div>
|
||||
@@ -1955,6 +2061,14 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
<RiPencilAiLine className="mr-1 h-4 w-4" />
|
||||
Rename
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => togglePinnedSession(session.id)} className="[&>svg]:mr-1">
|
||||
{isPinnedSession ? (
|
||||
<RiUnpinLine className="mr-1 h-4 w-4" />
|
||||
) : (
|
||||
<RiPushpinLine className="mr-1 h-4 w-4" />
|
||||
)}
|
||||
{isPinnedSession ? 'Unpin session' : 'Pin session'}
|
||||
</DropdownMenuItem>
|
||||
{!session.share ? (
|
||||
<DropdownMenuItem onClick={() => handleShareSession(session)} className="[&>svg]:mr-1">
|
||||
<RiShare2Line className="mr-1 h-4 w-4" />
|
||||
@@ -2023,6 +2137,8 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
toggleParent,
|
||||
handleSessionSelect,
|
||||
handleSessionDoubleClick,
|
||||
pinnedSessionIds,
|
||||
togglePinnedSession,
|
||||
handleShareSession,
|
||||
handleCopyShareUrl,
|
||||
handleUnshareSession,
|
||||
@@ -2056,7 +2172,9 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
};
|
||||
const allGroupSessions = collectGroupSessions(group.sessions);
|
||||
const normalizedGroupDirectory = normalizePath(group.directory ?? null);
|
||||
const isGitProject = Boolean(projectId && projectRepoStatus.get(projectId));
|
||||
const isGitProject = projectId && projectRepoStatus.has(projectId)
|
||||
? Boolean(projectRepoStatus.get(projectId))
|
||||
: lastRepoStatusRef.current;
|
||||
const showBranchSubtitle = !group.isMain && isBranchDifferentFromLabel(group.branch, group.label);
|
||||
const isActiveGroup = Boolean(
|
||||
normalizedGroupDirectory
|
||||
@@ -2136,12 +2254,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
aria-label={!hideGroupLabel ? (isCollapsed ? `Expand ${group.label}` : `Collapse ${group.label}`) : undefined}
|
||||
>
|
||||
{!hideGroupLabel ? (
|
||||
<div className="min-w-0 flex items-center gap-1.5 px-0">
|
||||
{isCollapsed ? (
|
||||
<RiArrowRightSLine className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" />
|
||||
) : (
|
||||
<RiArrowDownSLine className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" />
|
||||
)}
|
||||
<div className="min-w-0 flex items-center gap-1.5 pl-1.5">
|
||||
{!group.isMain || isGitProject ? (
|
||||
<RiGitBranchLine className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" />
|
||||
) : null}
|
||||
@@ -2155,6 +2268,11 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{isCollapsed ? (
|
||||
<RiArrowRightSLine className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" />
|
||||
) : (
|
||||
<RiArrowDownSLine className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
) : <div />}
|
||||
{group.directory ? (
|
||||
@@ -2282,7 +2400,8 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
)}
|
||||
>
|
||||
{!hideDirectoryControls && (
|
||||
<div className="select-none pl-3.5 pr-2 py-1.5 flex-shrink-0 border-b border-border/60">
|
||||
<div className={cn('select-none pl-3.5 pr-2 flex-shrink-0 border-b border-border/60', hideProjectSelector ? 'py-1' : 'py-1.5')}>
|
||||
{!hideProjectSelector && (
|
||||
<div className="flex h-8 items-center justify-between gap-2">
|
||||
<DropdownMenu
|
||||
onOpenChange={(open) => {
|
||||
@@ -2403,11 +2522,12 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
<RiFolderAddLine className="h-4.5 w-4.5" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{reserveHeaderActionsSpace ? (
|
||||
<div className="mt-1 h-8 pl-1">
|
||||
<div className="mt-1 -ml-1 flex h-8 items-center">
|
||||
{activeProjectForHeader ? (
|
||||
<div className="inline-flex h-8 items-center gap-1.5 rounded-md pl-0 pr-1">
|
||||
{activeProjectIsRepo ? (
|
||||
<div className="flex h-full items-center gap-1.5 rounded-md pl-0 pr-1">
|
||||
{stableActiveProjectIsRepo ? (
|
||||
<>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -2479,6 +2599,21 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
</Tooltip>
|
||||
</>
|
||||
) : null}
|
||||
{stableActiveProjectIsRepo && branchPickerProject ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsBranchPickerOpen(true)}
|
||||
className={headerActionButtonClass}
|
||||
aria-label="Manage branches"
|
||||
>
|
||||
<RiGitRepositoryLine className="h-4.5 w-4.5" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={4}><p>Manage branches</p></TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
{useMobileNotesPanel ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -2512,7 +2647,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
<DropdownMenuContent align="start" className="w-[340px] p-0">
|
||||
<ProjectNotesTodoPanel
|
||||
projectRef={activeProjectRefForHeader}
|
||||
canCreateWorktree={activeProjectIsRepo}
|
||||
canCreateWorktree={stableActiveProjectIsRepo}
|
||||
onActionComplete={() => setProjectNotesPanelOpen(false)}
|
||||
/>
|
||||
</DropdownMenuContent>
|
||||
@@ -2741,6 +2876,12 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
<BranchPickerDialog
|
||||
open={isBranchPickerOpen}
|
||||
onOpenChange={setIsBranchPickerOpen}
|
||||
project={branchPickerProject}
|
||||
/>
|
||||
|
||||
{useMobileNotesPanel ? (
|
||||
<MobileOverlayPanel
|
||||
open={projectNotesPanelOpen}
|
||||
@@ -2749,7 +2890,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
>
|
||||
<ProjectNotesTodoPanel
|
||||
projectRef={activeProjectRefForHeader}
|
||||
canCreateWorktree={activeProjectIsRepo}
|
||||
canCreateWorktree={stableActiveProjectIsRepo}
|
||||
onActionComplete={() => setProjectNotesPanelOpen(false)}
|
||||
className="p-0"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user