From d4776405bab1fba4ef802c5820675599b66f8b51 Mon Sep 17 00:00:00 2001 From: duanfuxiang Date: Sun, 15 Jun 2025 15:48:10 +0800 Subject: [PATCH] update history --- src/components/chat-view/ChatHistoryView.tsx | 321 +++++++++++++++++-- 1 file changed, 290 insertions(+), 31 deletions(-) diff --git a/src/components/chat-view/ChatHistoryView.tsx b/src/components/chat-view/ChatHistoryView.tsx index daf3785..3fa63cf 100644 --- a/src/components/chat-view/ChatHistoryView.tsx +++ b/src/components/chat-view/ChatHistoryView.tsx @@ -1,4 +1,4 @@ -import { Clock, MessageSquare, Pencil, Search, Trash2 } from 'lucide-react' +import { CheckSquare, Clock, Edit3, MessageSquare, Pencil, Search, Square, Trash2, CopyPlus } from 'lucide-react' import { Notice } from 'obsidian' import React, { useMemo, useRef, useState } from 'react' @@ -31,6 +31,10 @@ const ChatHistoryView = ({ // editing conversation id const [editingConversationId, setEditingConversationId] = useState(null) + // selection mode and selected conversations + const [selectionMode, setSelectionMode] = useState(false) + const [selectedConversations, setSelectedConversations] = useState>(new Set()) + const titleInputRefs = useRef>(new Map()) // handle search @@ -49,6 +53,38 @@ const ChatHistoryView = ({ ) }, [chatList, searchTerm]) + // toggle selection mode + const toggleSelectionMode = () => { + setSelectionMode(!selectionMode) + setSelectedConversations(new Set()) // clear selections when toggling + } + + // toggle conversation selection + const toggleConversationSelection = (conversationId: string) => { + const newSelected = new Set(selectedConversations) + if (newSelected.has(conversationId)) { + newSelected.delete(conversationId) + } else { + newSelected.add(conversationId) + } + setSelectedConversations(newSelected) + } + + // select all conversations + const selectAllConversations = () => { + const allIds = new Set(filteredConversations.map(conv => conv.id)) + setSelectedConversations(allIds) + } + + // clear all selections + const clearAllSelections = () => { + setSelectedConversations(new Set()) + } + + // check if all conversations are selected + const isAllSelected = filteredConversations.length > 0 && + filteredConversations.every(conv => selectedConversations.has(conv.id)) + // delete conversation const handleDeleteConversation = async (id: string) => { try { @@ -60,6 +96,46 @@ const ChatHistoryView = ({ } } + // batch delete selected conversations + const handleBatchDelete = async () => { + if (selectedConversations.size === 0) { + new Notice('请先选择要删除的对话') + return + } + + // show confirmation + const confirmed = confirm(`确定要删除选中的 ${selectedConversations.size} 个对话吗?此操作不可撤销。`) + if (!confirmed) { + return + } + + const deletedIds: string[] = [] + const errors: string[] = [] + + // delete conversations one by one + for (const id of selectedConversations) { + try { + await deleteConversation(id) + deletedIds.push(id) + onDelete?.(id) + } catch (error) { + errors.push(id) + console.error('Failed to delete conversation', id, error) + } + } + + // show results + if (deletedIds.length > 0) { + new Notice(`成功删除 ${deletedIds.length} 个对话`) + } + if (errors.length > 0) { + new Notice(`${errors.length} 个对话删除失败`) + } + + // clear selections + setSelectedConversations(new Set()) + } + // edit conversation title const handleEditConversation = (conversation: ChatConversationMeta) => { setEditingConversationId(conversation.id) @@ -85,7 +161,11 @@ const ChatHistoryView = ({ // select conversation const handleSelectConversation = (conversationId: string) => { - onSelect?.(conversationId) + if (selectionMode) { + toggleConversationSelection(conversationId) + } else { + onSelect?.(conversationId) + } } // format date @@ -98,7 +178,7 @@ const ChatHistoryView = ({ if (date >= today) { return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } else if (date >= yesterday) { - return t('chat.history.yesterday') + return String(t('chat.history.yesterday')) } else { return date.toLocaleDateString() } @@ -111,19 +191,66 @@ const ChatHistoryView = ({

{t('chat.history.title')}

+
+ +
{/* description */}
- {t('chat.history.description')} + {selectionMode + ? `选择模式 - 已选择 ${selectedConversations.size} 个对话` + : String(t('chat.history.description')) + }
+ {/* batch operations bar */} + {selectionMode && ( +
+
+ +
+
+ +
+
+ )} + {/* search bar */}
-

{searchTerm ? t('chat.history.noMatchingChats') : t('chat.history.noChats')}

+

{searchTerm ? String(t('chat.history.noMatchingChats')) : String(t('chat.history.noChats'))}

) : ( filteredConversations.map(conversation => (
{editingConversationId === conversation.id ? ( // edit mode @@ -167,13 +294,13 @@ const ChatHistoryView = ({ onClick={() => handleSaveEdit(conversation.id)} className="infio-chat-history-save-btn" > - {t('chat.history.save')} + {String(t('chat.history.save'))}
@@ -183,6 +310,15 @@ const ChatHistoryView = ({ className="infio-chat-history-view-mode" onClick={() => handleSelectConversation(conversation.id)} > + {selectionMode && ( +
+ {selectedConversations.has(conversation.id) ? ( + + ) : ( + + )} +
+ )}
@@ -190,28 +326,30 @@ const ChatHistoryView = ({
{conversation.title}
-
- - -
+ {!selectionMode && ( +
+ + +
+ )} )} @@ -243,11 +381,113 @@ const ChatHistoryView = ({ display: flex; justify-content: space-between; align-items: center; + width: 100%; + min-height: 40px; + margin-bottom: 8px; } .infio-chat-history-title h2 { margin: 0; font-size: 24px; + flex: 1; + } + + .infio-chat-history-header-actions { + display: flex; + gap: 8px; + flex-shrink: 0; + } + + .infio-chat-history-selection-btn { + display: flex !important; + align-items: center; + gap: 6px; + background-color: var(--background-primary, #ffffff); + border: 1px solid var(--background-modifier-border, #e0e0e0); + color: var(--text-normal, #333333); + padding: 6px 12px; + border-radius: var(--radius-s, 4px); + cursor: pointer; + font-size: var(--font-ui-small, 14px); + transition: all 0.2s ease; + min-width: 60px; + height: 32px; + box-sizing: border-box; + } + + .infio-chat-history-selection-btn:hover { + background-color: var(--background-modifier-hover, #f5f5f5); + border-color: var(--background-modifier-border-hover, #d0d0d0); + } + + .infio-chat-history-selection-btn.active { + background-color: var(--interactive-accent, #007acc); + color: var(--text-on-accent, #ffffff); + border-color: var(--interactive-accent, #007acc); + } + + .infio-chat-history-batch-actions { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px; + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-s); + gap: 12px; + } + + .infio-chat-history-select-actions { + display: flex; + gap: 8px; + } + + .infio-chat-history-select-all-btn { + display: flex; + align-items: center; + gap: 6px; + background-color: transparent; + border: 1px solid var(--background-modifier-border); + color: var(--text-normal); + padding: 6px 12px; + border-radius: var(--radius-s); + cursor: pointer; + font-size: var(--font-ui-small); + transition: all 0.2s ease; + } + + .infio-chat-history-select-all-btn:hover { + background-color: var(--background-modifier-hover); + } + + .infio-chat-history-batch-delete { + display: flex; + gap: 8px; + } + + .infio-chat-history-batch-delete-btn { + display: flex; + align-items: center; + gap: 6px; + background-color: var(--background-modifier-error); + border: 1px solid var(--background-modifier-error); + color: var(--text-error); + padding: 6px 12px; + border-radius: var(--radius-s); + cursor: pointer; + font-size: var(--font-ui-small); + transition: all 0.2s ease; + } + + .infio-chat-history-batch-delete-btn:hover:not(:disabled) { + background-color: var(--background-modifier-error-hover); + } + + .infio-chat-history-batch-delete-btn:disabled { + background-color: var(--background-modifier-form-field); + color: var(--text-faint); + border-color: var(--background-modifier-border); + cursor: not-allowed; } .infio-chat-history-tip { @@ -353,6 +593,11 @@ const ChatHistoryView = ({ border-color: var(--text-accent); } + .infio-chat-history-item.selected { + background-color: var(--background-modifier-active); + border-color: var(--interactive-accent); + } + .infio-chat-history-view-mode { display: flex; align-items: center; @@ -361,6 +606,20 @@ const ChatHistoryView = ({ cursor: pointer; } + .infio-chat-history-checkbox { + margin-right: 12px; + display: flex; + align-items: center; + } + + .infio-chat-history-checkbox-checked { + color: var(--interactive-accent); + } + + .infio-chat-history-checkbox-unchecked { + color: var(--text-muted); + } + .infio-chat-history-content { flex: 1; min-width: 0;