chat: put the session list cwd in the url, not a panel channel
Which project group you are looking at is addressable state, so /chat?cwd=<dir> has to be a
link anyone can hand out — it survives a refresh and an agent run can point straight at its
own runs directory. Was usePanelChannel('chat:active-cwd'), which per the navigation audit is
for signals and refresh buses only. Row links and New Chat now carry the query string along.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router';
|
||||
import { useParams, useNavigate, useSearchParams } from 'react-router';
|
||||
import type { LayoutNode, SelectedSession } from 'officerdev';
|
||||
import { WorkspaceView } from 'officerdev';
|
||||
import { useIsMobile } from 'hooks/useIsMobile';
|
||||
@@ -38,7 +38,7 @@ type SessionListPageProps = {
|
||||
export const SessionListPage = ({ isNew }: SessionListPageProps) => {
|
||||
const { sessionId } = useParams<{ sessionId: string }>();
|
||||
const [selected, setSelected] = usePanelChannel<SelectedSession>('chat:selected-session', null);
|
||||
const [, setActiveCwd] = usePanelChannel<string | null>('chat:active-cwd', null);
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
const client = useClient();
|
||||
const selectedRef = useRef(selected);
|
||||
selectedRef.current = selected;
|
||||
@@ -78,7 +78,16 @@ export const SessionListPage = ({ isNew }: SessionListPageProps) => {
|
||||
try {
|
||||
const detail = await client.get<ClaudeSessionDetail>(`/chat/sessions/${sessionId}?limit=${CHAT_TAIL}`);
|
||||
if (cancelled) return;
|
||||
setActiveCwd(detail.cwd || null);
|
||||
// replace: resolving a deep link is a canonicalisation, not a navigation the back button owes you.
|
||||
setSearchParams(
|
||||
(prev) => {
|
||||
const next = new URLSearchParams(prev);
|
||||
if (detail.cwd) next.set('cwd', detail.cwd);
|
||||
else next.delete('cwd');
|
||||
return next;
|
||||
},
|
||||
{ replace: true },
|
||||
);
|
||||
setSelected({
|
||||
id: sessionId,
|
||||
model: detail.model,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import { useLocation, useSearchParams } from 'react-router';
|
||||
import { Unplug } from 'lucide-react';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { useAuth } from 'hooks/useAuth';
|
||||
@@ -98,8 +98,9 @@ function NewChat({ resumeSummary, resumeSessionId, initialMessages, total, initi
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// Run the session in the pwd chosen in the Sessions panel; null → backend default (general_chat_sessions).
|
||||
const [activeCwd] = usePanelChannel<string | null>('chat:active-cwd', null);
|
||||
// Run the session in the pwd the URL names; absent → backend default (general_chat_sessions).
|
||||
const [searchParams] = useSearchParams();
|
||||
const activeCwd = searchParams.get('cwd');
|
||||
const cwd = activeCwd ? { path: activeCwd } : locationState?.cwd;
|
||||
|
||||
const initialMessage = locationState?.initialMessage
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRef, useState, useCallback } from 'react';
|
||||
import { Link, useNavigate } from 'react-router';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router';
|
||||
import { Plus, MessageSquare, RefreshCw, Trash2, Pencil, Check, X } from 'lucide-react';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { useClaudeSessions } from 'state/useClaudeSessions';
|
||||
@@ -10,8 +10,12 @@ import { PwdSelector } from './PwdSelector';
|
||||
// Clicking a session loads its transcript and continues the real Claude session via --resume.
|
||||
export const SessionList = () => {
|
||||
const navigate = useNavigate();
|
||||
// The working directory the list operates on (null = the default general_chat_sessions dir).
|
||||
const [activeCwd, setActiveCwd] = usePanelChannel<string | null>('chat:active-cwd', null);
|
||||
// The working directory the list operates on (absent = the default general_chat_sessions dir).
|
||||
// In the URL, not a channel: which project group you're looking at is addressable state, so
|
||||
// /chat?cwd=<dir> has to be a link anyone can hand out — an agent run points at its own group
|
||||
// this way, and it survives a refresh.
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const activeCwd = searchParams.get('cwd');
|
||||
const { sessions, isLoading, refetch, deleteSession, renameSession } = useClaudeSessions(activeCwd);
|
||||
const [selected, setSelected] = usePanelChannel<SelectedSession>('chat:selected-session', null);
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
@@ -52,7 +56,15 @@ export const SessionList = () => {
|
||||
<PwdSelector
|
||||
value={activeCwd}
|
||||
onChange={(cwd) => {
|
||||
setActiveCwd(cwd);
|
||||
setSearchParams(
|
||||
(prev) => {
|
||||
const next = new URLSearchParams(prev);
|
||||
if (cwd) next.set('cwd', cwd);
|
||||
else next.delete('cwd');
|
||||
return next;
|
||||
},
|
||||
{ replace: true },
|
||||
);
|
||||
setSelected(null); // sessions belong to a cwd — clear the open one when switching
|
||||
}}
|
||||
/>
|
||||
@@ -67,7 +79,8 @@ export const SessionList = () => {
|
||||
<button
|
||||
onClick={() => {
|
||||
setSelected({ id: `new:${Date.now()}` });
|
||||
navigate('/chat/new', { replace: true });
|
||||
// Carry the cwd: a new chat starts in the pwd the list is showing, and that now lives in the URL.
|
||||
navigate({ pathname: '/chat/new', search: searchParams.toString() }, { replace: true });
|
||||
}}
|
||||
className="flex items-center gap-1.5 rounded-md bg-duck-teal hover:bg-duck-teal/90 text-duck-yellow cursor-pointer h-7 px-3 text-xs font-medium"
|
||||
>
|
||||
@@ -132,7 +145,7 @@ export const SessionList = () => {
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
to={`/chat/${session.id}`}
|
||||
to={{ pathname: `/chat/${session.id}`, search: searchParams.toString() }}
|
||||
className="flex flex-1 items-center gap-3 px-4 py-3 min-w-0 text-left cursor-pointer"
|
||||
>
|
||||
<MessageSquare className="h-4 w-4 shrink-0 text-duck-teal/60" />
|
||||
|
||||
Reference in New Issue
Block a user