refactor: drop type-to-confirm gate on chat delete

The chat-delete dialog required typing the chat name to confirm
deletion. Single-user app — typing friction is annoying, not safety.
Match the archive dialog pattern in SettingsPane.tsx: title +
description naming the chat in mono font, plain Cancel + destructive
Delete button.

Removes the deleteInput state, deleteExpected / deleteEnabled
deriveds, the <Input> field, and its lone <Input> import.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 20:50:30 +00:00
parent 1ecccc112f
commit 98b432ebce

View File

@@ -3,7 +3,6 @@ import { Archive, MessageSquare, Send, ChevronDown, ChevronRight, RotateCcw, Tra
import type { Chat } from '@/api/types'; import type { Chat } from '@/api/types';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { Input } from '@/components/ui/input';
import { import {
ContextMenu, ContextMenu,
ContextMenuContent, ContextMenuContent,
@@ -165,7 +164,6 @@ export function SessionLandingPage({
const [renameValue, setRenameValue] = useState(''); const [renameValue, setRenameValue] = useState('');
const [archiveConfirm, setArchiveConfirm] = useState<Chat | null>(null); const [archiveConfirm, setArchiveConfirm] = useState<Chat | null>(null);
const [deleteConfirm, setDeleteConfirm] = useState<Chat | null>(null); const [deleteConfirm, setDeleteConfirm] = useState<Chat | null>(null);
const [deleteInput, setDeleteInput] = useState('');
const openChats = chats const openChats = chats
.filter((c) => c.status === 'open') .filter((c) => c.status === 'open')
@@ -193,9 +191,6 @@ export function SessionLandingPage({
setRenamingId(null); setRenamingId(null);
} }
const deleteExpected = deleteConfirm?.name ?? '';
const deleteEnabled = deleteConfirm !== null && deleteInput === deleteExpected && deleteExpected.length > 0;
// TODO: Landing page chat counts are a snapshot at mount. New messages in // TODO: Landing page chat counts are a snapshot at mount. New messages in
// visible chats won't update the per-row stats until next mount/navigation. // visible chats won't update the per-row stats until next mount/navigation.
return ( return (
@@ -217,7 +212,7 @@ export function SessionLandingPage({
onCancelRename={() => setRenamingId(null)} onCancelRename={() => setRenamingId(null)}
onContextStartRename={() => startRename(chat)} onContextStartRename={() => startRename(chat)}
onContextArchive={() => setArchiveConfirm(chat)} onContextArchive={() => setArchiveConfirm(chat)}
onContextDelete={() => { setDeleteConfirm(chat); setDeleteInput(''); }} onContextDelete={() => setDeleteConfirm(chat)}
showContextMenu showContextMenu
actions={ actions={
<> <>
@@ -242,7 +237,6 @@ export function SessionLandingPage({
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setDeleteConfirm(chat); setDeleteConfirm(chat);
setDeleteInput('');
}} }}
> >
<Trash2 size={14} /> <Trash2 size={14} />
@@ -352,36 +346,25 @@ export function SessionLandingPage({
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<Dialog open={deleteConfirm !== null} onOpenChange={(open) => { if (!open) { setDeleteConfirm(null); setDeleteInput(''); } }}> <Dialog open={deleteConfirm !== null} onOpenChange={(open) => { if (!open) setDeleteConfirm(null); }}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Delete chat?</DialogTitle> <DialogTitle>Delete chat?</DialogTitle>
<DialogDescription> <DialogDescription>
Type the chat name to confirm: Permanently delete{' '}
{' '} <span className="font-mono font-medium text-foreground">{deleteConfirm?.name || '(unnamed)'}</span>
<span className="font-mono font-medium text-foreground">{deleteExpected || '(unnamed — cannot type-confirm)'}</span> {' '}and all its messages. This cannot be undone.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<Input
value={deleteInput}
onChange={(e) => setDeleteInput(e.target.value)}
placeholder={deleteExpected}
disabled={!deleteExpected}
/>
<div className="text-xs text-muted-foreground">
This will permanently delete this chat and all its messages. This cannot be undone.
</div>
<div className="flex gap-2 justify-end pt-2"> <div className="flex gap-2 justify-end pt-2">
<Button variant="outline" onClick={() => { setDeleteConfirm(null); setDeleteInput(''); }}> <Button variant="outline" onClick={() => setDeleteConfirm(null)}>
Cancel Cancel
</Button> </Button>
<Button <Button
variant="destructive" variant="destructive"
disabled={!deleteEnabled}
onClick={() => { onClick={() => {
if (deleteConfirm && deleteEnabled) void onDeleteChat(deleteConfirm.id); if (deleteConfirm) void onDeleteChat(deleteConfirm.id);
setDeleteConfirm(null); setDeleteConfirm(null);
setDeleteInput('');
}} }}
> >
Delete Delete