Rename chat title
This commit is contained in:
parent
f960ed3030
commit
5da7d46a49
@ -64,6 +64,30 @@ export async function GET(
|
||||
return Response.json({ id, messages });
|
||||
}
|
||||
|
||||
/** PATCH /api/web-sessions/[id] — update session metadata (e.g. rename). */
|
||||
export async function PATCH(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const { id } = await params;
|
||||
let body: { title?: string };
|
||||
try {
|
||||
body = await request.json();
|
||||
} catch {
|
||||
return Response.json({ error: "Invalid JSON" }, { status: 400 });
|
||||
}
|
||||
const sessions = readIndex();
|
||||
const session = sessions.find((s) => s.id === id);
|
||||
if (!session) {
|
||||
return Response.json({ error: "Session not found" }, { status: 404 });
|
||||
}
|
||||
if (typeof body.title === "string") {
|
||||
session.title = body.title;
|
||||
}
|
||||
writeIndex(sessions);
|
||||
return Response.json({ ok: true, session });
|
||||
}
|
||||
|
||||
/** DELETE /api/web-sessions/[id] — remove a web chat session and its messages. */
|
||||
export async function DELETE(
|
||||
_request: Request,
|
||||
|
||||
@ -49,6 +49,8 @@ type ChatSessionsSidebarProps = {
|
||||
width?: number;
|
||||
/** Called when the user deletes a session from the sidebar menu. */
|
||||
onDeleteSession?: (sessionId: string) => void;
|
||||
/** Called when the user renames a session from the sidebar menu. */
|
||||
onRenameSession?: (sessionId: string, newTitle: string) => void;
|
||||
/** When true, show a loader instead of empty state (e.g. initial sessions fetch). */
|
||||
loading?: boolean;
|
||||
};
|
||||
@ -154,12 +156,15 @@ export function ChatSessionsSidebar({
|
||||
onNewSession,
|
||||
onSelectSubagent,
|
||||
onDeleteSession,
|
||||
onRenameSession,
|
||||
mobile,
|
||||
onClose,
|
||||
width: widthProp,
|
||||
loading = false,
|
||||
}: ChatSessionsSidebarProps) {
|
||||
const [hoveredId, setHoveredId] = useState<string | null>(null);
|
||||
const [renamingId, setRenamingId] = useState<string | null>(null);
|
||||
const [renameValue, setRenameValue] = useState("");
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(id: string) => {
|
||||
@ -184,6 +189,19 @@ export function ChatSessionsSidebar({
|
||||
[onDeleteSession],
|
||||
);
|
||||
|
||||
const handleStartRename = useCallback((sessionId: string, currentTitle: string) => {
|
||||
setRenamingId(sessionId);
|
||||
setRenameValue(currentTitle || "");
|
||||
}, []);
|
||||
|
||||
const handleCommitRename = useCallback(() => {
|
||||
if (renamingId && renameValue.trim()) {
|
||||
onRenameSession?.(renamingId, renameValue.trim());
|
||||
}
|
||||
setRenamingId(null);
|
||||
setRenameValue("");
|
||||
}, [renamingId, renameValue, onRenameSession]);
|
||||
|
||||
// Index subagents by parent session ID
|
||||
const subagentsByParent = useMemo(() => {
|
||||
const map = new Map<string, SidebarSubagentInfo[]>();
|
||||
@ -288,6 +306,23 @@ export function ChatSessionsSidebar({
|
||||
: "transparent",
|
||||
}}
|
||||
>
|
||||
{renamingId === session.id ? (
|
||||
<form
|
||||
className="flex-1 min-w-0 px-2 py-1.5"
|
||||
onSubmit={(e) => { e.preventDefault(); handleCommitRename(); }}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
onBlur={handleCommitRename}
|
||||
onKeyDown={(e) => { if (e.key === "Escape") { setRenamingId(null); setRenameValue(""); } }}
|
||||
autoFocus
|
||||
className="w-full text-xs font-medium px-1 py-0.5 rounded outline-none border"
|
||||
style={{ color: "var(--color-text)", background: "var(--color-surface)", borderColor: "var(--color-border)" }}
|
||||
/>
|
||||
</form>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleSelect(session.id)}
|
||||
@ -313,7 +348,7 @@ export function ChatSessionsSidebar({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-0.5" style={{ paddingLeft: isStreamingSession ? "calc(0.375rem + 6px)" : undefined }}>
|
||||
|
||||
|
||||
<span
|
||||
className="text-[10px]"
|
||||
style={{ color: "var(--color-text-muted)" }}
|
||||
@ -330,6 +365,7 @@ export function ChatSessionsSidebar({
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
{onDeleteSession && (
|
||||
<div className={`shrink-0 flex items-center pr-1 transition-opacity ${showMore ? "opacity-100" : "opacity-0"}`}>
|
||||
<DropdownMenu>
|
||||
@ -343,6 +379,12 @@ export function ChatSessionsSidebar({
|
||||
<MoreHorizontalIcon />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" side="bottom">
|
||||
<DropdownMenuItem
|
||||
onSelect={() => handleStartRename(session.id, session.title)}
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /></svg>
|
||||
Rename
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
variant="destructive"
|
||||
onSelect={() => handleDeleteSession(session.id)}
|
||||
|
||||
@ -533,6 +533,18 @@ function WorkspacePageInner() {
|
||||
[activeSessionId, sessions, fetchSessions],
|
||||
);
|
||||
|
||||
const handleRenameSession = useCallback(
|
||||
async (sessionId: string, newTitle: string) => {
|
||||
await fetch(`/api/web-sessions/${sessionId}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ title: newTitle }),
|
||||
});
|
||||
void fetchSessions();
|
||||
},
|
||||
[fetchSessions],
|
||||
);
|
||||
|
||||
// Poll for active (streaming) agent runs so the sidebar can show indicators.
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@ -1448,6 +1460,7 @@ function WorkspacePageInner() {
|
||||
onSubagentClick={handleSubagentClickFromChat}
|
||||
onFilePathClick={handleFilePathClickFromChat}
|
||||
onDeleteSession={handleDeleteSession}
|
||||
onRenameSession={handleRenameSession}
|
||||
compact={isMobile}
|
||||
/>
|
||||
)}
|
||||
@ -1477,6 +1490,7 @@ function WorkspacePageInner() {
|
||||
}}
|
||||
onSelectSubagent={handleSelectSubagent}
|
||||
onDeleteSession={handleDeleteSession}
|
||||
onRenameSession={handleRenameSession}
|
||||
mobile
|
||||
onClose={() => setChatSessionsOpen(false)}
|
||||
/>
|
||||
@ -1521,6 +1535,7 @@ function WorkspacePageInner() {
|
||||
}}
|
||||
onSelectSubagent={handleSelectSubagent}
|
||||
onDeleteSession={handleDeleteSession}
|
||||
onRenameSession={handleRenameSession}
|
||||
width={rightSidebarWidth}
|
||||
/>
|
||||
)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user