chat: pwd selector — switch the /chat working directory
The Sessions panel header gets a pwd selector: the default claude_sessions dir, plus every directory that already has Claude sessions (auto-discovered by reading the real cwd back from ~/.claude/projects), plus free-text entry. Switching pwd refetches the list for that cwd's Claude project group and runs New Chat / resume in it. Backend: GET /chat/pwds + a ?cwd= param on the session ops; the WS handler honors a chosen cwd for /chat (default claude_sessions). Browse-modal picker next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,8 @@ export { useDashboardState } from './useDashboardState';
|
||||
export { usePiModels, useVisiblePiModels, useUserVisibleModels, useEnabledPiModels, modelKey } from './useModels';
|
||||
export type { ModelOption } from './useModels';
|
||||
export { useAccessPolicy } from './useAccessPolicy';
|
||||
export { useClaudeSessions } from './useClaudeSessions';
|
||||
export type { ClaudeSessionSummary } from './useClaudeSessions';
|
||||
export { useClaudeSessions, useChatPwds } from './useClaudeSessions';
|
||||
export type { ClaudeSessionSummary, ClaudePwd } from './useClaudeSessions';
|
||||
export { useRecentModels } from './useRecentModels';
|
||||
export { usePlans } from './usePlans';
|
||||
export { useLandingPage } from './useLandingPage';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useCallback } from 'react';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { useAuth } from 'hooks/useAuth';
|
||||
|
||||
const QUERY_KEY = ['CLAUDE_SESSIONS'];
|
||||
const SESSIONS_KEY = 'CLAUDE_SESSIONS';
|
||||
|
||||
export type ClaudeSessionSummary = {
|
||||
id: string; // Claude session uuid (= transcript filename)
|
||||
@@ -22,37 +22,60 @@ export type ClaudeSessionMessage =
|
||||
|
||||
export type ClaudeSessionDetail = { id: string; model: string; cwd: string; messages: ClaudeSessionMessage[] };
|
||||
|
||||
/** The /chat route's sessions, read straight from Claude's own transcript store (source of truth). */
|
||||
export function useClaudeSessions() {
|
||||
export type ClaudePwd = { cwd: string; sessionCount: number; updatedAt: string; isDefault: boolean };
|
||||
|
||||
// Query string for the selected working directory (null/undefined = the default claude_sessions dir).
|
||||
const cwdQuery = (cwd?: string | null) => (cwd ? `?cwd=${encodeURIComponent(cwd)}` : '');
|
||||
|
||||
/** The default /chat dir plus every directory that already has Claude sessions. */
|
||||
export function useChatPwds() {
|
||||
const client = useClient();
|
||||
const { isAuthenticated } = useAuth();
|
||||
const { data } = useQuery<{ pwds: ClaudePwd[]; default: string }>({
|
||||
queryKey: ['CHAT_PWDS'],
|
||||
enabled: isAuthenticated,
|
||||
queryFn: () => client.get<{ pwds: ClaudePwd[]; default: string }>('/chat/pwds'),
|
||||
staleTime: 30 * 1000,
|
||||
});
|
||||
return { pwds: data?.pwds ?? [], defaultCwd: data?.default ?? null };
|
||||
}
|
||||
|
||||
/** Sessions for a working directory, read from Claude's own transcript store (source of truth). */
|
||||
export function useClaudeSessions(cwd?: string | null) {
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
const { isAuthenticated } = useAuth();
|
||||
const q = cwdQuery(cwd);
|
||||
|
||||
const { data, isLoading, refetch } = useQuery<{ sessions: ClaudeSessionSummary[] }>({
|
||||
queryKey: QUERY_KEY,
|
||||
queryKey: [SESSIONS_KEY, cwd ?? 'default'],
|
||||
enabled: isAuthenticated,
|
||||
queryFn: () => client.get<{ sessions: ClaudeSessionSummary[] }>('/chat/sessions'),
|
||||
queryFn: () => client.get<{ sessions: ClaudeSessionSummary[] }>(`/chat/sessions${q}`),
|
||||
staleTime: 30 * 1000,
|
||||
});
|
||||
|
||||
const loadSession = (id: string) => client.get<ClaudeSessionDetail>(`/chat/sessions/${id}`);
|
||||
const loadSession = (id: string) => client.get<ClaudeSessionDetail>(`/chat/sessions/${id}${q}`);
|
||||
|
||||
const invalidate = useCallback(() => queryClient.invalidateQueries({ queryKey: QUERY_KEY }), [queryClient]);
|
||||
// Invalidate every cwd's list (also refreshes pwd counts) — cheap and avoids stale lists.
|
||||
const invalidate = useCallback(() => {
|
||||
queryClient.invalidateQueries({ queryKey: [SESSIONS_KEY] });
|
||||
queryClient.invalidateQueries({ queryKey: ['CHAT_PWDS'] });
|
||||
}, [queryClient]);
|
||||
|
||||
const deleteSession = useCallback(
|
||||
async (id: string) => {
|
||||
await client.delete(`/chat/sessions/${id}`);
|
||||
await client.delete(`/chat/sessions/${id}${q}`);
|
||||
invalidate();
|
||||
},
|
||||
[client, invalidate],
|
||||
[client, q, invalidate],
|
||||
);
|
||||
|
||||
const renameSession = useCallback(
|
||||
async (id: string, title: string) => {
|
||||
await client.patch(`/chat/sessions/${id}/title`, { title });
|
||||
await client.patch(`/chat/sessions/${id}/title${q}`, { title });
|
||||
invalidate();
|
||||
},
|
||||
[client, invalidate],
|
||||
[client, q, invalidate],
|
||||
);
|
||||
|
||||
return { sessions: data?.sessions ?? [], isLoading, refetch, loadSession, deleteSession, renameSession, invalidate };
|
||||
|
||||
Reference in New Issue
Block a user