import Workspace from "@/models/workspace"; import paths from "@/utils/paths"; import showToast from "@/utils/toast"; import { ArrowCounterClockwise, DotsThree, PencilSimple, Trash, X, } from "@phosphor-icons/react"; import { useEffect, useRef, useState } from "react"; import { useParams } from "react-router-dom"; const THREAD_CALLOUT_DETAIL_WIDTH = 26; export default function ThreadItem({ idx, activeIdx, isActive, workspace, thread, onRemove, toggleMarkForDeletion, hasNext, ctrlPressed = false, }) { const { slug, threadSlug = null } = useParams(); const optionsContainer = useRef(null); const [showOptions, setShowOptions] = useState(false); const linkTo = !thread.slug ? paths.workspace.chat(slug) : paths.workspace.thread(slug, thread.slug); return (
{/* Curved line Element and leader if required */}
{/* Downstroke border for next item */} {hasNext && (
)} {/* Curved line inline placeholder for spacing - not visible */}
{thread.deleted ? (

deleted thread

{ctrlPressed && ( )}
) : (

{thread.name}

)} {!!thread.slug && !thread.deleted && (
{" "} {/* Added flex and items-center */} {ctrlPressed ? ( ) : (
)} {showOptions && ( setShowOptions(false)} currentThreadSlug={threadSlug} /> )}
)}
); } function OptionsMenu({ containerRef, workspace, thread, onRemove, close, currentThreadSlug, }) { const menuRef = useRef(null); // Ref menu options const outsideClick = (e) => { if (!menuRef.current) return false; if ( !menuRef.current?.contains(e.target) && !containerRef.current?.contains(e.target) ) close(); return false; }; const isEsc = (e) => { if (e.key === "Escape" || e.key === "Esc") close(); }; function cleanupListeners() { window.removeEventListener("click", outsideClick); window.removeEventListener("keyup", isEsc); } // end Ref menu options useEffect(() => { function setListeners() { if (!menuRef?.current || !containerRef.current) return false; window.document.addEventListener("click", outsideClick); window.document.addEventListener("keyup", isEsc); } setListeners(); return cleanupListeners; }, [menuRef.current, containerRef.current]); const renameThread = async () => { const name = window .prompt("What would you like to rename this thread to?") ?.trim(); if (!name || name.length === 0) { close(); return; } const { message } = await Workspace.threads.update( workspace.slug, thread.slug, { name } ); if (!!message) { showToast(`Thread could not be updated! ${message}`, "error", { clear: true, }); close(); return; } thread.name = name; close(); }; const handleDelete = async () => { if ( !window.confirm( "Are you sure you want to delete this thread? All of its chats will be deleted. You cannot undo this." ) ) return; const success = await Workspace.threads.delete(workspace.slug, thread.slug); if (!success) { showToast("Thread could not be deleted!", "error", { clear: true }); return; } if (success) { showToast("Thread deleted successfully!", "success", { clear: true }); onRemove(thread.id); // Redirect if deleting the active thread if (currentThreadSlug === thread.slug) { window.location.href = paths.workspace.chat(workspace.slug); } return; } }; return (
); }