batch3 T8: chat->file click, Session.tsx rewires to Workspace, sidebar polish
- MessageBubble & ToolCallCard: detect path-like strings in rendered text via regex requiring slash+extension; clicks dispatch open_file_in_browser - Session.tsx: now renders <Workspace sessionId projectId>; on mount, emits session_loaded so sidebar can highlight even deep-linked sessions not in the recent_sessions cache - ProjectSidebar: active project's chevron visually disabled (50% opacity, cursor-not-allowed) and click no-op; activeSession from useSidebar used as fallback when active session isn't in cache Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { api } from '@/api/client';
|
||||
import { sessionEvents } from '@/hooks/sessionEvents';
|
||||
import { useSidebar } from '@/hooks/useSidebar';
|
||||
import type { SidebarProject } from '@/api/types';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const EXPANDED_KEY = 'boocode.sidebar.expanded';
|
||||
const MAX_VISIBLE_SESSIONS = 5;
|
||||
@@ -55,13 +56,29 @@ function relTime(iso: string): string {
|
||||
return `${Math.floor(mo / 12)}y`;
|
||||
}
|
||||
|
||||
function activeProjectId(pathname: string, projects: SidebarProject[]): string | null {
|
||||
function activeProjectId(
|
||||
pathname: string,
|
||||
projects: SidebarProject[],
|
||||
activeSession: { session_id: string; project_id: string } | null
|
||||
): string | null {
|
||||
const pm = pathname.match(/^\/project\/([^/]+)/);
|
||||
if (pm?.[1]) return pm[1];
|
||||
const sm = pathname.match(/^\/session\/([^/]+)/);
|
||||
const sid = sm?.[1];
|
||||
if (!sid) return null;
|
||||
return projects.find((p) => p.recent_sessions.some((s) => s.id === sid))?.id ?? null;
|
||||
// Prefer the cache lookup so we resolve correctly even when an older
|
||||
// activeSession (from a prior route) hasn't been cleared yet.
|
||||
const fromCache = projects.find((p) =>
|
||||
p.recent_sessions.some((s) => s.id === sid)
|
||||
)?.id;
|
||||
if (fromCache) return fromCache;
|
||||
// Fallback: the session was loaded via deep link (not in cache) and
|
||||
// emitted session_loaded — use that. Guard against stale values by
|
||||
// matching the current URL sid.
|
||||
if (activeSession && activeSession.session_id === sid) {
|
||||
return activeSession.project_id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function activeSessionId(pathname: string): string | null {
|
||||
@@ -70,7 +87,8 @@ function activeSessionId(pathname: string): string | null {
|
||||
}
|
||||
|
||||
export function ProjectSidebar() {
|
||||
const { data, error, loading, retry } = useSidebar();
|
||||
const { data, error, loading, retry, activeSession: loadedActiveSession } =
|
||||
useSidebar();
|
||||
const [addOpen, setAddOpen] = useState(false);
|
||||
const [expanded, setExpanded] = useState<Set<string>>(() => readExpanded());
|
||||
const navigate = useNavigate();
|
||||
@@ -87,8 +105,8 @@ export function ProjectSidebar() {
|
||||
|
||||
const projects = data?.projects ?? [];
|
||||
const activeProject = useMemo(
|
||||
() => activeProjectId(location.pathname, projects),
|
||||
[location.pathname, projects]
|
||||
() => activeProjectId(location.pathname, projects, loadedActiveSession),
|
||||
[location.pathname, projects, loadedActiveSession]
|
||||
);
|
||||
const activeSession = useMemo(
|
||||
() => activeSessionId(location.pathname),
|
||||
@@ -173,11 +191,17 @@ export function ProjectSidebar() {
|
||||
type="button"
|
||||
aria-label={isExpanded ? 'Collapse' : 'Expand'}
|
||||
aria-expanded={isExpanded}
|
||||
disabled={isActiveProject}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (isActiveProject) return;
|
||||
toggle(p.id);
|
||||
}}
|
||||
className="flex items-center justify-center size-4 shrink-0 opacity-70 hover:opacity-100"
|
||||
className={cn(
|
||||
'flex items-center justify-center size-4 shrink-0 opacity-70 hover:opacity-100',
|
||||
isActiveProject &&
|
||||
'opacity-50 cursor-not-allowed hover:opacity-50'
|
||||
)}
|
||||
>
|
||||
<ChevronRight
|
||||
className={`size-3.5 transition-transform ${isExpanded ? 'rotate-90' : ''}`}
|
||||
|
||||
Reference in New Issue
Block a user