diff --git a/backend/chainlit/server.py b/backend/chainlit/server.py index 6accf1ccc4..e34d98ee8e 100644 --- a/backend/chainlit/server.py +++ b/backend/chainlit/server.py @@ -66,6 +66,7 @@ ConnectMCPRequest, DeleteFeedbackRequest, DeleteThreadRequest, + DeleteThreadsRequest, DisconnectMCPRequest, ElementRequest, GetThreadsRequest, @@ -1241,6 +1242,31 @@ async def delete_thread( return JSONResponse(content={"success": True}) +@router.delete("/project/threads") +async def delete_threads( + request: Request, + payload: DeleteThreadsRequest, + current_user: UserParam, +): + """Delete multiple threads.""" + + data_layer = get_data_layer() + + if not data_layer: + raise HTTPException(status_code=400, detail="Data persistence is not enabled") + + if not current_user: + raise HTTPException(status_code=401, detail="Unauthorized") + + thread_ids = payload.threadIds + + for thread_id in thread_ids: + await is_thread_author(current_user.identifier, thread_id) + await data_layer.delete_thread(thread_id) + + return JSONResponse(content={"success": True}) + + @router.post("/project/action") async def call_action( payload: CallActionRequest, diff --git a/backend/chainlit/translations/bn.json b/backend/chainlit/translations/bn.json index 17bc629842..4dec40a63b 100644 --- a/backend/chainlit/translations/bn.json +++ b/backend/chainlit/translations/bn.json @@ -6,6 +6,7 @@ "continue": "চালিয়ে যান", "goBack": "পিছনে যান", "reset": "রিসেট করুন", + "select": "নির্বাচন করুন", "submit": "জমা দিন" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "মুছে ফেলা নিশ্চিত করুন", "description": "এটি থ্রেড এবং এর বার্তা ও উপাদানগুলি মুছে ফেলবে। এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না", + "description_plural": "এটি {{count}}টি থ্রেড এবং তাদের বার্তা এবং উপাদানগুলি মুছে ফেলবে। এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না", "success": "চ্যাট মুছে ফেলা হয়েছে", "inProgress": "চ্যাট মুছে ফেলা হচ্ছে" }, diff --git a/backend/chainlit/translations/de-DE.json b/backend/chainlit/translations/de-DE.json index d626294400..7affc97297 100644 --- a/backend/chainlit/translations/de-DE.json +++ b/backend/chainlit/translations/de-DE.json @@ -6,6 +6,7 @@ "continue": "Fortfahren", "goBack": "Zurück", "reset": "Zurücksetzen", + "select": "Auswählen", "submit": "Absenden" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "Löschen bestätigen", "description": "Dies wird den Thread sowie seine Nachrichten und Elemente löschen. Dies kann nicht rückgängig gemacht werden", + "description_plural": "Dies wird {{count}} Threads sowie deren Nachrichten und Elemente löschen. Dies kann nicht rückgängig gemacht werden", "success": "Chat gelöscht", "inProgress": "Chat wird gelöscht" }, diff --git a/backend/chainlit/translations/el-GR.json b/backend/chainlit/translations/el-GR.json index bfcbc05883..761041ab50 100644 --- a/backend/chainlit/translations/el-GR.json +++ b/backend/chainlit/translations/el-GR.json @@ -6,6 +6,7 @@ "continue": "Συνέχεια", "goBack": "Επιστροφή", "reset": "Επαναφορά", + "select": "Επιλογή", "submit": "Υποβολή" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "Επιβεβαίωση διαγραφής", "description": "Αυτό θα διαγράψει το νήμα καθώς και τα μηνύματα και τα στοιχεία του. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", + "description_plural": "Αυτό θα διαγράψει {{count}} νήματα καθώς και τα μηνύματα και τα στοιχεία τους. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.", "success": "Η συνομιλία διαγράφηκε", "inProgress": "Διαγραφή συνομιλίας" }, diff --git a/backend/chainlit/translations/en-US.json b/backend/chainlit/translations/en-US.json index 028b750a0a..64c631ae80 100644 --- a/backend/chainlit/translations/en-US.json +++ b/backend/chainlit/translations/en-US.json @@ -6,6 +6,7 @@ "continue": "Continue", "goBack": "Go Back", "reset": "Reset", + "select": "Select", "submit": "Submit" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "Confirm deletion", "description": "This will delete the thread as well as its messages and elements. This action cannot be undone", + "description_plural": "This will delete {{count}} threads as well as their messages and elements. This action cannot be undone", "success": "Chat deleted", "inProgress": "Deleting chat" }, diff --git a/backend/chainlit/translations/es.json b/backend/chainlit/translations/es.json index 0527fe7429..03fddfb494 100644 --- a/backend/chainlit/translations/es.json +++ b/backend/chainlit/translations/es.json @@ -6,6 +6,7 @@ "continue": "Continuar", "goBack": "Volver", "reset": "Restablecer", + "select": "Seleccionar", "submit": "Enviar" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "Confirmar eliminación", "description": "Esto eliminará la conversación, sus mensajes y elementos. Esta acción no se puede deshacer", + "description_plural": "Esto eliminará {{count}} conversaciones, sus mensajes y elementos. Esta acción no se puede deshacer", "success": "Chat eliminado", "inProgress": "Eliminando chat" }, @@ -256,4 +258,4 @@ } } } -} \ No newline at end of file +} diff --git a/backend/chainlit/translations/fr-FR.json b/backend/chainlit/translations/fr-FR.json index 915ecefc82..584fbf27d4 100644 --- a/backend/chainlit/translations/fr-FR.json +++ b/backend/chainlit/translations/fr-FR.json @@ -6,6 +6,7 @@ "continue": "Continuer", "goBack": "Retour", "reset": "Réinitialiser", + "select": "Sélectionner", "submit": "Envoyer" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "Confirmer la suppression", "description": "Cela supprimera le fil de discussion ainsi que ses messages et éléments. Cette action ne peut pas être annulée", + "description_plural": "Cela supprimera {{count}} fils de discussion ainsi que leurs messages et éléments. Cette action ne peut pas être annulée", "success": "Discussion supprimée", "inProgress": "Suppression de la discussion" }, diff --git a/backend/chainlit/translations/gu.json b/backend/chainlit/translations/gu.json index 905612645a..21f1763330 100644 --- a/backend/chainlit/translations/gu.json +++ b/backend/chainlit/translations/gu.json @@ -6,6 +6,7 @@ "continue": "ચાલુ રાખો", "goBack": "પાછા જાઓ", "reset": "રીસેટ કરો", + "select": "પસંદ કરો", "submit": "સબમિટ કરો" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "કાઢી નાખવાની પુષ્ટિ કરો", "description": "આ થ્રેડ અને તેના સંદેશાઓ અને તત્વોને કાઢી નાખશે. આ ક્રિયા પાછી ફેરવી શકાશે નહીં", + "description_plural": "આ {{count}} થ્રેડો તેમજ તેમના સંદેશાઓ અને તત્વોને કાઢી નાખશે. આ ક્રિયા પાછી ફેરવી શકાશે નહીં", "success": "ચેટ કાઢી નાખી", "inProgress": "ચેટ કાઢી નાખી રહ્યા છીએ" }, diff --git a/backend/chainlit/translations/he-IL.json b/backend/chainlit/translations/he-IL.json index db787c49e3..2adf36d75b 100644 --- a/backend/chainlit/translations/he-IL.json +++ b/backend/chainlit/translations/he-IL.json @@ -6,6 +6,7 @@ "continue": "המשך", "goBack": "חזור", "reset": "איפוס", + "select": "בחר", "submit": "שלח" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "אשר מחיקה", "description": "פעולה זו תמחק את השיחה וכן את ההודעות והאלמנטים שלה. לא ניתן לבטל פעולה זו", + "description_plural": "פעולה זו תמחק {{count}} שיחות וכן את ההודעות והאלמנטים שלהן. לא ניתן לבטל פעולה זו", "success": "הצ'אט נמחק", "inProgress": "מוחק צ'אט" }, diff --git a/backend/chainlit/translations/hi.json b/backend/chainlit/translations/hi.json index 73cff709ee..c592ac3453 100644 --- a/backend/chainlit/translations/hi.json +++ b/backend/chainlit/translations/hi.json @@ -6,6 +6,7 @@ "continue": "जारी रखें", "goBack": "वापस जाएं", "reset": "रीसेट करें", + "select": "चुनें", "submit": "जमा करें" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "हटाने की पुष्टि करें", "description": "यह थ्रेड और इसके संदेशों और तत्वों को हटा देगा। यह क्रिया वापस नहीं की जा सकती", + "description_plural": "यह {{count}} थ्रेड्स के साथ-साथ उनके संदेशों और तत्वों को हटा देगा। यह क्रिया वापस नहीं की जा सकती", "success": "चैट हटा दी गई", "inProgress": "चैट हटाई जा रही है" }, diff --git a/backend/chainlit/translations/it.json b/backend/chainlit/translations/it.json index cbc74b9f3a..1f553cfce7 100644 --- a/backend/chainlit/translations/it.json +++ b/backend/chainlit/translations/it.json @@ -6,6 +6,7 @@ "continue": "Continua", "goBack": "Ritorna", "reset": "Reset", + "select": "Seleziona", "submit": "Invia" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "Conferma eliminazione", "description": "Stai per eliminare la chat insieme ai suoi messaggi ed elementi. Questa azione non può essere annullata", + "description_plural": "Stai per eliminare {{count}} chat insieme ai loro messaggi ed elementi. Questa azione non può essere annullata", "success": "Chat eliminata", "inProgress": "Eliminazione chat" }, diff --git a/backend/chainlit/translations/ja.json b/backend/chainlit/translations/ja.json index ed7ab6f9d4..d8768aeed7 100644 --- a/backend/chainlit/translations/ja.json +++ b/backend/chainlit/translations/ja.json @@ -6,6 +6,7 @@ "continue": "続ける", "goBack": "戻る", "reset": "リセット", + "select": "選択", "submit": "送信" }, "status": { @@ -175,6 +176,7 @@ "delete": { "title": "削除の確認", "description": "このスレッドとそのメッセージ、要素が削除されます。この操作は取り消せません", + "description_plural": "これにより {{count}} 個のスレッドと、そのメッセージおよび要素が削除されます。この操作は取り消せません", "success": "チャットを削除しました", "inProgress": "チャットを削除中" }, diff --git a/backend/chainlit/translations/kn.json b/backend/chainlit/translations/kn.json index 0ad4b05f04..db2828007f 100644 --- a/backend/chainlit/translations/kn.json +++ b/backend/chainlit/translations/kn.json @@ -5,8 +5,9 @@ "confirm": "ದೃಢೀಕರಿಸಿ", "continue": "ಮುಂದುವರಿಸಿ", "goBack": "ಹಿಂದೆ ಹೋಗಿ", - "reset": "ಮರುಹೊಂದಿಸಿ", - "submit": "ಸಲ್ಲಿಸಿ" + "reset": "ಮರುಹೊಂದಿಸಿ", + "select": "ಆರಿಸಿ", + "submit": "ಸಲ್ಲಿಸಿ" }, "status": { "loading": "ಲೋಡ್ ಆಗುತ್ತಿದೆ...", @@ -176,6 +177,7 @@ "delete": { "title": "ಅಳಿಸುವಿಕೆಯನ್ನು ದೃಢೀಕರಿಸಿ", "description": "ಇದು ಸಂಭಾಷಣೆಯನ್ನು ಹಾಗೂ ಅದರ ಸಂದೇಶಗಳು ಮತ್ತು ಅಂಶಗಳನ್ನು ಅಳಿಸುತ್ತದೆ. ಈ ಕ್ರಿಯೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", + "description_plural": "ಇದು {{count}} ಸಂಭಾಷಣೆಗಳನ್ನು ಹಾಗೂ ಅವುಗಳ ಸಂದೇಶಗಳು ಮತ್ತು ಅಂಶಗಳನ್ನು ಅಳಿಸುತ್ತದೆ. ಈ ಕ್ರಿಯೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", "success": "ಸಂಭಾಷಣೆ ಅಳಿಸಲಾಗಿದೆ", "inProgress": "ಸಂಭಾಷಣೆ ಅಳಿಸಲಾಗುತ್ತಿದೆ" }, diff --git a/backend/chainlit/translations/ko.json b/backend/chainlit/translations/ko.json index 2d286036a5..0ef1424409 100644 --- a/backend/chainlit/translations/ko.json +++ b/backend/chainlit/translations/ko.json @@ -6,6 +6,7 @@ "continue": "계속", "goBack": "뒤로 가기", "reset": "초기화", + "select": "선택", "submit": "제출" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "삭제 확인", "description": "이렇게 하면 스레드와 그 메시지 및 요소가 삭제됩니다. 이 작업은 취소할 수 없습니다", + "description_plural": "이렇게 하면 {{count}}개의 스레드와 해당 메시지 및 요소가 삭제됩니다. 이 작업은 취소할 수 없습니다", "success": "채팅이 삭제되었습니다", "inProgress": "채팅 삭제 중" }, diff --git a/backend/chainlit/translations/ml.json b/backend/chainlit/translations/ml.json index 70eb7ea1f8..6577ad40b5 100644 --- a/backend/chainlit/translations/ml.json +++ b/backend/chainlit/translations/ml.json @@ -6,6 +6,7 @@ "continue": "തുടരുക", "goBack": "തിരികെ പോകുക", "reset": "പുനഃസജ്ജമാക്കുക", + "select": "തിരഞ്ഞെടുക്കുക", "submit": "സമർപ്പിക്കുക" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "ഡിലീറ്റ് സ്ഥിരീകരിക്കുക", "description": "ഇത് ത്രെഡും അതിന്റെ സന്ദേശങ്ങളും ഘടകങ്ങളും ഡിലീറ്റ് ചെയ്യും. ഈ പ്രവർത്തി പഴയപടിയാക്കാൻ കഴിയില്ല", + "description_plural": "ഇത് {{count}} ത്രെഡുകളും അവയുടെ സന്ദേശങ്ങളും ഘടകങ്ങളും ഡിലീറ്റ് ചെയ്യും. ഈ പ്രവർത്തി പഴയപടിയാക്കാൻ കഴിയില്ല", "success": "ചാറ്റ് ഡിലീറ്റ് ചെയ്തു", "inProgress": "ചാറ്റ് ഡിലീറ്റ് ചെയ്യുന്നു" }, diff --git a/backend/chainlit/translations/mr.json b/backend/chainlit/translations/mr.json index 2d232c4a71..321e9636ca 100644 --- a/backend/chainlit/translations/mr.json +++ b/backend/chainlit/translations/mr.json @@ -6,6 +6,7 @@ "continue": "पुढे जा", "goBack": "मागे जा", "reset": "रीसेट करा", + "select": "निवडा", "submit": "सबमिट करा" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "हटविण्याची पुष्टी करा", "description": "हे थ्रेड आणि त्याचे संदेश व घटक हटवेल. ही क्रिया पूर्ववत केली जाऊ शकत नाही", + "description_plural": "हे {{count}} थ्रेड्स तसेच त्यांचे संदेश आणि घटक हटवेल. ही क्रिया पूर्ववत केली जाऊ शकत नाही", "success": "चॅट हटवला", "inProgress": "चॅट हटवत आहे" }, diff --git a/backend/chainlit/translations/nl.json b/backend/chainlit/translations/nl.json index 43a5e522ba..6d4be0d037 100644 --- a/backend/chainlit/translations/nl.json +++ b/backend/chainlit/translations/nl.json @@ -6,6 +6,7 @@ "continue": "Doorgaan", "goBack": "Terug", "reset": "Herstellen", + "select": "Selecteer", "submit": "Versturen" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "Verwijdering bevestigen", "description": "Dit zal het gesprek en bijbehorende berichten en elementen verwijderen. Deze actie kan niet ongedaan worden gemaakt", + "description_plural": "Dit zal {{count}} gesprekken en bijbehorende berichten en elementen verwijderen. Deze actie kan niet ongedaan worden gemaakt", "success": "Chat verwijderd", "inProgress": "Chat verwijderen" }, diff --git a/backend/chainlit/translations/ta.json b/backend/chainlit/translations/ta.json index 9324ff3479..dba7552020 100644 --- a/backend/chainlit/translations/ta.json +++ b/backend/chainlit/translations/ta.json @@ -6,6 +6,7 @@ "continue": "தொடர்க", "goBack": "திரும்பிச் செல்", "reset": "மீட்டமை", + "select": "தேர்ந்தெடு", "submit": "சமர்ப்பி" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "நீக்குவதை உறுதிப்படுத்து", "description": "இது உரையாடல் மற்றும் அதன் செய்திகள், உறுப்புகளை நீக்கும். இந்த செயலை மீட்டெடுக்க முடியாது", + "description_plural": "இது {{count}} உரையாடல்கள் மற்றும் அவற்றின் செய்திகள், உறுப்புகளை நீக்கும். இந்தச் செயலை மீட்டெடுக்க முடியாது", "success": "உரையாடல் நீக்கப்பட்டது", "inProgress": "உரையாடலை நீக்குகிறது" }, diff --git a/backend/chainlit/translations/te.json b/backend/chainlit/translations/te.json index 8eb7d40db3..9333936d6f 100644 --- a/backend/chainlit/translations/te.json +++ b/backend/chainlit/translations/te.json @@ -6,6 +6,7 @@ "continue": "కొనసాగించండి", "goBack": "వెనక్కి వెళ్ళండి", "reset": "రీసెట్ చేయండి", + "select": "ఎంచుకోండి", "submit": "సమర్పించండి" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "తొలగింపును నిర్ధారించండి", "description": "ఇది థ్రెడ్‌తో పాటు దాని సందేశాలను మరియు అంశాలను తొలగిస్తుంది. ఈ చర్యను రద్దు చేయలేరు", + "description_plural": "ఇది {{count}} థ్రెడ్‌లతో పాటు వాటి సందేశాలను మరియు అంశాలను తొలగిస్తుంది. ఈ చర్యను రద్దు చేయలేరు", "success": "చాట్ తొలగించబడింది", "inProgress": "చాట్‌ని తొలగిస్తోంది" }, diff --git a/backend/chainlit/translations/zh-CN.json b/backend/chainlit/translations/zh-CN.json index 5a6348f1f7..18a05b4336 100644 --- a/backend/chainlit/translations/zh-CN.json +++ b/backend/chainlit/translations/zh-CN.json @@ -6,6 +6,7 @@ "continue": "继续", "goBack": "返回", "reset": "重置", + "select": "选择", "submit": "提交" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "确认删除", "description": "这将删除该对话及其所有消息和元素。此操作无法撤销", + "description_plural": "这将删除 {{count}} 个对话及其所有消息和元素。此操作无法撤销", "success": "对话已删除", "inProgress": "正在删除对话" }, diff --git a/backend/chainlit/translations/zh-TW.json b/backend/chainlit/translations/zh-TW.json index 5e2e1421aa..6e0ce0eaa6 100644 --- a/backend/chainlit/translations/zh-TW.json +++ b/backend/chainlit/translations/zh-TW.json @@ -6,6 +6,7 @@ "continue": "繼續", "goBack": "返回", "reset": "重設", + "select": "選擇", "submit": "送出" }, "status": { @@ -176,6 +177,7 @@ "delete": { "title": "確認刪除", "description": "這將刪除該對話及其所有訊息和元件。此操作無法復原。", + "description_plural": "這將刪除 {{count}} 個對話及其所有訊息和元件。此操作無法復原。", "success": "對話已刪除", "inProgress": "正在刪除對話" }, diff --git a/backend/chainlit/types.py b/backend/chainlit/types.py index 43d8e7cefa..8e69912ab4 100644 --- a/backend/chainlit/types.py +++ b/backend/chainlit/types.py @@ -231,6 +231,10 @@ class DeleteThreadRequest(BaseModel): threadId: str +class DeleteThreadsRequest(BaseModel): + threadIds: List[str] + + class DeleteFeedbackRequest(BaseModel): feedbackId: str diff --git a/frontend/src/components/LeftSidebar/ThreadList.tsx b/frontend/src/components/LeftSidebar/ThreadList.tsx index 42d52706da..be0426727f 100644 --- a/frontend/src/components/LeftSidebar/ThreadList.tsx +++ b/frontend/src/components/LeftSidebar/ThreadList.tsx @@ -1,6 +1,6 @@ import { cn } from '@/lib/utils'; import { size } from 'lodash'; -import { Share2 } from 'lucide-react'; +import { Share2, Trash2 } from 'lucide-react'; import { useContext, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useNavigate } from 'react-router-dom'; @@ -32,6 +32,7 @@ import { AlertDialogTitle } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; import { Dialog, DialogContent, @@ -79,6 +80,9 @@ export function ThreadList({ const { clear } = useChatInteract(); const { threadId: currentThreadId } = useChatMessages(); const [threadIdToDelete, setThreadIdToDelete] = useState(); + const [isDeletingSelected, setIsDeletingSelected] = useState(false); + const [selectedThreadIds, setSelectedThreadIds] = useState([]); + const [isSelectionMode, setIsSelectionMode] = useState(false); const [threadIdToRename, setThreadIdToRename] = useState(); const [threadNewName, setThreadNewName] = useState(); const setThreadHistory = useSetRecoilState(threadHistoryState); @@ -176,6 +180,45 @@ export function ThreadList({ }); }; + const handleDeleteSelected = async () => { + if (selectedThreadIds.length === 0) return; + + if ( + selectedThreadIds.includes(idToResume || '') || + selectedThreadIds.includes(currentThreadId || '') + ) { + clear(); + await new Promise((resolve) => setTimeout(resolve, 300)); + } + + toast.promise(apiClient.deleteThreads(selectedThreadIds), { + loading: ( + + ), + success: () => { + setThreadHistory((prev) => ({ + ...prev, + threads: prev?.threads?.filter( + (t) => !selectedThreadIds.includes(t.id) + ) + })); + setSelectedThreadIds([]); + setIsSelectionMode(false); + navigate('/'); + return ( + + ); + }, + error: (err) => { + if (err instanceof ClientError) { + return {err.message}; + } else { + return ; + } + } + }); + }; + const handleRenameThread = () => { if (!threadIdToRename || !threadNewName) return; @@ -259,6 +302,31 @@ export function ThreadList({ + setIsDeletingSelected(open)} + > + + + + + + + {t('threadHistory.thread.actions.delete.description_plural', { + count: selectedThreadIds.length + })} + + + + + + + + + + + + setThreadIdToRename(undefined)} @@ -312,6 +380,39 @@ export function ThreadList({ threadId={threadIdToShare || null} /> +
+ + {isSelectionMode && ( +
+ {selectedThreadIds.length > 0 && ( + + )} +
+ )} +
{sortedTimeGroupKeys.map((group) => { const items = threadHistory!.timeGroupedThreads![group]; return ( @@ -329,51 +430,104 @@ export function ThreadList({ + {isSelectionMode && ( + { + if (checked) { + setSelectedThreadIds([ + ...selectedThreadIds, + thread.id + ]); + } else { + setSelectedThreadIds( + selectedThreadIds.filter( + (id) => id !== thread.id + ) + ); + } + }} + className="ml-2 shrink-0 transition-opacity" + /> + )} - - + + { + if (isSelectionMode) { + e.preventDefault(); + e.stopPropagation(); + const isSelected = selectedThreadIds.includes( + thread.id + ); + if (isSelected) { + setSelectedThreadIds( + selectedThreadIds.filter( + (id) => id !== thread.id + ) + ); + } else { + setSelectedThreadIds([ + ...selectedThreadIds, + thread.id + ]); + } + } + }} + > - + {thread.metadata?.is_shared ? (