adds deleting
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
import { Globe2, LogOut, MessageSquare, Plus, Search, SendHorizontal } from "lucide-preact";
|
||||
import { Globe2, LogOut, MessageSquare, Plus, Search, SendHorizontal, Trash2 } from "lucide-preact";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
@@ -10,6 +10,8 @@ import { SearchResultsPanel } from "@/components/search/search-results-panel";
|
||||
import {
|
||||
createChat,
|
||||
createSearch,
|
||||
deleteChat,
|
||||
deleteSearch,
|
||||
getChat,
|
||||
getSearch,
|
||||
listChats,
|
||||
@@ -33,6 +35,11 @@ type SidebarItem = SidebarSelection & {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
};
|
||||
type ContextMenuState = {
|
||||
item: SidebarSelection;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
const PROVIDER_DEFAULT_MODELS: Record<Provider, string> = {
|
||||
openai: "gpt-4.1-mini",
|
||||
@@ -111,6 +118,8 @@ export default function App() {
|
||||
const [model, setModel] = useState(PROVIDER_DEFAULT_MODELS.openai);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const transcriptEndRef = useRef<HTMLDivElement>(null);
|
||||
const contextMenuRef = useRef<HTMLDivElement>(null);
|
||||
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
|
||||
|
||||
const sidebarItems = useMemo(() => buildSidebarItems(chats, searches), [chats, searches]);
|
||||
|
||||
@@ -263,6 +272,7 @@ export default function App() {
|
||||
|
||||
const handleCreateChat = () => {
|
||||
setError(null);
|
||||
setContextMenu(null);
|
||||
setDraftKind("chat");
|
||||
setSelectedItem(null);
|
||||
setSelectedChat(null);
|
||||
@@ -271,12 +281,62 @@ export default function App() {
|
||||
|
||||
const handleCreateSearch = () => {
|
||||
setError(null);
|
||||
setContextMenu(null);
|
||||
setDraftKind("search");
|
||||
setSelectedItem(null);
|
||||
setSelectedChat(null);
|
||||
setSelectedSearch(null);
|
||||
};
|
||||
|
||||
const openContextMenu = (event: MouseEvent, item: SidebarSelection) => {
|
||||
event.preventDefault();
|
||||
const menuWidth = 160;
|
||||
const menuHeight = 40;
|
||||
const padding = 8;
|
||||
const x = Math.min(event.clientX, window.innerWidth - menuWidth - padding);
|
||||
const y = Math.min(event.clientY, window.innerHeight - menuHeight - padding);
|
||||
setContextMenu({ item, x: Math.max(padding, x), y: Math.max(padding, y) });
|
||||
};
|
||||
|
||||
const handleDeleteFromContextMenu = async () => {
|
||||
if (!contextMenu || isSending) return;
|
||||
const target = contextMenu.item;
|
||||
setContextMenu(null);
|
||||
setError(null);
|
||||
try {
|
||||
if (target.kind === "chat") {
|
||||
await deleteChat(target.id);
|
||||
} else {
|
||||
await deleteSearch(target.id);
|
||||
}
|
||||
await refreshCollections();
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (message.includes("bearer token")) {
|
||||
handleAuthFailure(message);
|
||||
} else {
|
||||
setError(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!contextMenu) return;
|
||||
const handlePointerDown = (event: PointerEvent) => {
|
||||
if (contextMenuRef.current?.contains(event.target as Node)) return;
|
||||
setContextMenu(null);
|
||||
};
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") setContextMenu(null);
|
||||
};
|
||||
window.addEventListener("pointerdown", handlePointerDown);
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener("pointerdown", handlePointerDown);
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
}, [contextMenu]);
|
||||
|
||||
const handleSendChat = async (content: string) => {
|
||||
let chatId = draftKind === "chat" ? null : selectedItem?.kind === "chat" ? selectedItem.id : null;
|
||||
|
||||
@@ -446,6 +506,7 @@ export default function App() {
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
setContextMenu(null);
|
||||
logout();
|
||||
resetWorkspaceState();
|
||||
};
|
||||
@@ -503,9 +564,11 @@ export default function App() {
|
||||
active ? "bg-slate-700 text-slate-50" : "text-slate-200 hover:bg-slate-800"
|
||||
)}
|
||||
onClick={() => {
|
||||
setContextMenu(null);
|
||||
setDraftKind(null);
|
||||
setSelectedItem({ kind: item.kind, id: item.id });
|
||||
}}
|
||||
onContextMenu={(event) => openContextMenu(event, { kind: item.kind, id: item.id })}
|
||||
type="button"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -595,6 +658,24 @@ export default function App() {
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
{contextMenu ? (
|
||||
<div
|
||||
ref={contextMenuRef}
|
||||
className="fixed z-50 min-w-40 rounded-md border border-border bg-background p-1 shadow-md"
|
||||
style={{ left: contextMenu.x, top: contextMenu.y }}
|
||||
onContextMenu={(event) => event.preventDefault()}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-red-600 transition hover:bg-muted disabled:text-muted-foreground"
|
||||
onClick={() => void handleDeleteFromContextMenu()}
|
||||
disabled={isSending}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user