Tap-to-edit session titles, PATCH proxy, model display on replay
This commit is contained in:
@@ -16,6 +16,25 @@ export async function GET(_req: NextRequest, { params }: { params: Promise<{ id:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||||
|
const { id } = await params;
|
||||||
|
const body = await req.text();
|
||||||
|
try {
|
||||||
|
const res = await fetch(BACKEND + "/api/sessions/" + id, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
const data = await res.text();
|
||||||
|
return new NextResponse(data, {
|
||||||
|
status: res.status,
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ error: "backend unreachable" }, { status: 502 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function DELETE(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
export async function DELETE(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export default function SessionReplayPage({ params }: { params: Promise<{ id: st
|
|||||||
const qc = useQueryClient();
|
const qc = useQueryClient();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||||
|
const [editingTitle, setEditingTitle] = useState(false);
|
||||||
|
const [titleDraft, setTitleDraft] = useState("");
|
||||||
const { data: sessionData } = useSession(id);
|
const { data: sessionData } = useSession(id);
|
||||||
const { data: messagesData, isLoading } = useMessages(id);
|
const { data: messagesData, isLoading } = useMessages(id);
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
@@ -88,15 +90,44 @@ export default function SessionReplayPage({ params }: { params: Promise<{ id: st
|
|||||||
<ArrowLeft size={20} style={{ color: "var(--text-2)" }} />
|
<ArrowLeft size={20} style={{ color: "var(--text-2)" }} />
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="text-[15px] font-medium truncate" style={{ color: "var(--text-1)" }}>
|
{editingTitle ? (
|
||||||
{session?.title || "Session " + id.slice(0, 8)}
|
<input
|
||||||
</div>
|
type="text"
|
||||||
|
value={titleDraft}
|
||||||
|
onChange={(e) => setTitleDraft(e.target.value)}
|
||||||
|
onBlur={async () => {
|
||||||
|
if (titleDraft.trim() && titleDraft !== session?.title) {
|
||||||
|
await fetch("/api/sessions/" + id, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ title: titleDraft.trim() }),
|
||||||
|
});
|
||||||
|
qc.invalidateQueries({ queryKey: ["session", id] });
|
||||||
|
}
|
||||||
|
setEditingTitle(false);
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => { if (e.key === "Enter") (e.target as HTMLInputElement).blur(); }}
|
||||||
|
className="text-[15px] font-medium w-full bg-transparent outline-none"
|
||||||
|
style={{ color: "var(--text-1)", borderBottom: "1px solid var(--accent)" }}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="text-[15px] font-medium truncate"
|
||||||
|
style={{ color: "var(--text-1)" }}
|
||||||
|
onClick={() => { setTitleDraft(session?.title || ""); setEditingTitle(true); }}
|
||||||
|
>
|
||||||
|
{session?.title || "Session " + id.slice(0, 8)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{session && (
|
{session && (
|
||||||
<div className="flex items-center gap-2 mt-0.5">
|
<div className="flex items-center gap-2 mt-0.5">
|
||||||
<span className={`badge ${platformBadgeClass(session.source)}`}>
|
<span className={`badge ${platformBadgeClass(session.source)}`}>
|
||||||
{session.source}
|
{session.source}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[11px]" style={{ color: "var(--text-3)" }}>
|
<span className="text-[11px]" style={{ color: "var(--text-3)" }}>
|
||||||
|
{session.model && <span className="font-mono">{session.model.split("/").pop()}</span>}
|
||||||
|
{session.model && " · "}
|
||||||
{session.message_count} msgs · {formatTokens(session.input_tokens + session.output_tokens)} · {timeAgo(session.started_at)}
|
{session.message_count} msgs · {formatTokens(session.input_tokens + session.output_tokens)} · {timeAgo(session.started_at)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user