refactor(sidebar): scope folder-header menu to per-folder new/import, drop import from card and blank-area menus
This commit is contained in:
@@ -9,7 +9,6 @@ import {
|
||||
CircleCheck,
|
||||
CircleDashed,
|
||||
CircleX,
|
||||
Download,
|
||||
Plus,
|
||||
type LucideIcon,
|
||||
} from "lucide-react"
|
||||
@@ -75,8 +74,6 @@ interface SidebarConversationCardProps {
|
||||
onDelete: (id: number, agentType: string) => Promise<void>
|
||||
onStatusChange: (id: number, status: ConversationStatus) => Promise<void>
|
||||
onNewConversation?: () => void
|
||||
onImport?: () => void
|
||||
importing?: boolean
|
||||
}
|
||||
|
||||
export const SidebarConversationCard = memo(function SidebarConversationCard({
|
||||
@@ -89,8 +86,6 @@ export const SidebarConversationCard = memo(function SidebarConversationCard({
|
||||
onDelete,
|
||||
onStatusChange,
|
||||
onNewConversation,
|
||||
onImport,
|
||||
importing,
|
||||
}: SidebarConversationCardProps) {
|
||||
const t = useTranslations("Folder.conversationCard")
|
||||
const tSidebar = useTranslations("Folder.sidebar")
|
||||
@@ -254,15 +249,6 @@ export const SidebarConversationCard = memo(function SidebarConversationCard({
|
||||
<Trash2 className="h-4 w-4" />
|
||||
{t("delete")}
|
||||
</ContextMenuItem>
|
||||
{onImport && (
|
||||
<>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem disabled={importing} onSelect={onImport}>
|
||||
<Download className="h-4 w-4" />
|
||||
{importing ? t("importing") : t("importLocalSessions")}
|
||||
</ContextMenuItem>
|
||||
</>
|
||||
)}
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
|
||||
|
||||
@@ -107,22 +107,22 @@ const FolderHeader = memo(function FolderHeader({
|
||||
folderName,
|
||||
count,
|
||||
expanded,
|
||||
importing,
|
||||
onToggle,
|
||||
onFocus,
|
||||
onCloseFolderTabs,
|
||||
onRemoveFromWorkspace,
|
||||
onNewConversation,
|
||||
onImport,
|
||||
t,
|
||||
}: {
|
||||
folderId: number
|
||||
folderName: string
|
||||
count: number
|
||||
expanded: boolean
|
||||
importing: boolean
|
||||
onToggle: (folderId: number) => void
|
||||
onFocus: (folderId: number) => void
|
||||
onCloseFolderTabs: (folderId: number) => void
|
||||
onRemoveFromWorkspace: (folderId: number) => void
|
||||
onNewConversation: (folderId: number) => void
|
||||
onImport: (folderId: number) => void
|
||||
t: ReturnType<typeof useTranslations>
|
||||
}) {
|
||||
return (
|
||||
@@ -204,11 +204,16 @@ const FolderHeader = memo(function FolderHeader({
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem onSelect={() => onFocus(folderId)}>
|
||||
{t("folderHeaderMenu.focus")}
|
||||
<ContextMenuItem onSelect={() => onNewConversation(folderId)}>
|
||||
<Plus className="h-4 w-4" />
|
||||
{t("newConversation")}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onSelect={() => onCloseFolderTabs(folderId)}>
|
||||
{t("folderHeaderMenu.closeFolderTabs")}
|
||||
<ContextMenuItem
|
||||
disabled={importing}
|
||||
onSelect={() => onImport(folderId)}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
{importing ? t("importing") : t("importLocalSessions")}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
@@ -470,28 +475,6 @@ export function SidebarConversationList({
|
||||
})
|
||||
}, [])
|
||||
|
||||
const focusFolder = useCallback(
|
||||
(folderId: number) => {
|
||||
const idx = flatItems.findIndex(
|
||||
(item) => item.type === "folder_header" && item.folderId === folderId
|
||||
)
|
||||
if (idx >= 0) {
|
||||
virtualizerRef.current?.scrollToIndex(idx, {
|
||||
align: "start",
|
||||
smooth: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
[flatItems]
|
||||
)
|
||||
|
||||
const handleCloseFolderTabs = useCallback(
|
||||
(folderId: number) => {
|
||||
closeTabsByFolder(folderId)
|
||||
},
|
||||
[closeTabsByFolder]
|
||||
)
|
||||
|
||||
const handleRemoveFolder = useCallback(
|
||||
(folderId: number) => {
|
||||
const name = folderIndex.get(folderId)?.name ?? String(folderId)
|
||||
@@ -595,35 +578,44 @@ export function SidebarConversationList({
|
||||
[folderIndex, openNewConversationTab]
|
||||
)
|
||||
|
||||
const handleImport = useCallback(async () => {
|
||||
if (importing) return
|
||||
if (!activeFolder) return
|
||||
setImporting(true)
|
||||
const taskId = `import-${activeFolder.id}-${Date.now()}`
|
||||
addTask(taskId, t("importLocalSessions"))
|
||||
updateTask(taskId, { status: "running" })
|
||||
try {
|
||||
const result = await importLocalConversations(activeFolder.id)
|
||||
updateTask(taskId, { status: "completed" })
|
||||
refreshConversations()
|
||||
if (result.imported > 0) {
|
||||
toast.success(
|
||||
t("toasts.importedSessions", {
|
||||
imported: result.imported,
|
||||
skipped: result.skipped,
|
||||
})
|
||||
)
|
||||
} else {
|
||||
toast.info(t("toasts.noNewSessionsFound", { skipped: result.skipped }))
|
||||
const handleImportForFolder = useCallback(
|
||||
async (folderId: number) => {
|
||||
if (importing) return
|
||||
setImporting(true)
|
||||
const taskId = `import-${folderId}-${Date.now()}`
|
||||
addTask(taskId, t("importLocalSessions"))
|
||||
updateTask(taskId, { status: "running" })
|
||||
try {
|
||||
const result = await importLocalConversations(folderId)
|
||||
updateTask(taskId, { status: "completed" })
|
||||
refreshConversations()
|
||||
if (result.imported > 0) {
|
||||
toast.success(
|
||||
t("toasts.importedSessions", {
|
||||
imported: result.imported,
|
||||
skipped: result.skipped,
|
||||
})
|
||||
)
|
||||
} else {
|
||||
toast.info(
|
||||
t("toasts.noNewSessionsFound", { skipped: result.skipped })
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
updateTask(taskId, { status: "failed", error: msg })
|
||||
toast.error(t("toasts.importFailed", { message: msg }))
|
||||
} finally {
|
||||
setImporting(false)
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
updateTask(taskId, { status: "failed", error: msg })
|
||||
toast.error(t("toasts.importFailed", { message: msg }))
|
||||
} finally {
|
||||
setImporting(false)
|
||||
}
|
||||
}, [importing, activeFolder, addTask, updateTask, refreshConversations, t])
|
||||
},
|
||||
[importing, addTask, updateTask, refreshConversations, t]
|
||||
)
|
||||
|
||||
const handleImport = useCallback(async () => {
|
||||
if (!activeFolder) return
|
||||
await handleImportForFolder(activeFolder.id)
|
||||
}, [activeFolder, handleImportForFolder])
|
||||
|
||||
const emptyAfterFilter =
|
||||
filteredConversations.length === 0 && conversations.length > 0
|
||||
@@ -678,14 +670,6 @@ export function SidebarConversationList({
|
||||
<Plus className="h-4 w-4" />
|
||||
{t("newConversation")}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
disabled={importing || !activeFolder}
|
||||
onSelect={handleImport}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
{importing ? t("importing") : t("importLocalSessions")}
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
) : emptyAfterFilter ? (
|
||||
@@ -716,11 +700,11 @@ export function SidebarConversationList({
|
||||
folderName={stickyFolderItem.folderName}
|
||||
count={stickyFolderItem.count}
|
||||
expanded={stickyFolderItem.expanded}
|
||||
importing={importing}
|
||||
onToggle={toggleFolder}
|
||||
onFocus={focusFolder}
|
||||
onCloseFolderTabs={handleCloseFolderTabs}
|
||||
onRemoveFromWorkspace={handleRemoveFolder}
|
||||
onNewConversation={handleNewConversationForFolder}
|
||||
onImport={handleImportForFolder}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
@@ -746,11 +730,11 @@ export function SidebarConversationList({
|
||||
folderName={item.folderName}
|
||||
count={item.count}
|
||||
expanded={item.expanded}
|
||||
importing={importing}
|
||||
onToggle={toggleFolder}
|
||||
onFocus={focusFolder}
|
||||
onCloseFolderTabs={handleCloseFolderTabs}
|
||||
onRemoveFromWorkspace={handleRemoveFolder}
|
||||
onNewConversation={handleNewConversationForFolder}
|
||||
onImport={handleImportForFolder}
|
||||
t={t}
|
||||
/>
|
||||
)
|
||||
@@ -771,8 +755,6 @@ export function SidebarConversationList({
|
||||
onDelete={handleDelete}
|
||||
onStatusChange={handleStatusChange}
|
||||
onNewConversation={handleNewConversation}
|
||||
onImport={handleImport}
|
||||
importing={importing}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
@@ -788,14 +770,6 @@ export function SidebarConversationList({
|
||||
<Plus className="h-4 w-4" />
|
||||
{t("newConversation")}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
disabled={importing || !activeFolder}
|
||||
onSelect={handleImport}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
{importing ? t("importing") : t("importLocalSessions")}
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
)}
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "إزالة المجلد من مساحة العمل؟",
|
||||
"removeFolderConfirmDescription": "إزالة \"{name}\" من مساحة العمل؟ سيتم إغلاق علامات التبويب والمحطات المرتبطة.",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "تركيز",
|
||||
"closeFolderTabs": "إغلاق جميع علامات تبويب هذا المجلد",
|
||||
"removeFromWorkspace": "إزالة من مساحة العمل"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "Ordner aus Arbeitsbereich entfernen?",
|
||||
"removeFolderConfirmDescription": "\"{name}\" aus dem Arbeitsbereich entfernen? Zugehörige Tabs und Terminals werden geschlossen.",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "Fokussieren",
|
||||
"closeFolderTabs": "Alle Tabs dieses Ordners schließen",
|
||||
"removeFromWorkspace": "Aus Arbeitsbereich entfernen"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "Remove folder from workspace?",
|
||||
"removeFolderConfirmDescription": "Remove \"{name}\" from the workspace? Its tabs and terminals will close.",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "Focus",
|
||||
"closeFolderTabs": "Close all tabs of this folder",
|
||||
"removeFromWorkspace": "Remove from workspace"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "¿Eliminar carpeta del espacio de trabajo?",
|
||||
"removeFolderConfirmDescription": "¿Eliminar \"{name}\" del espacio de trabajo? Sus pestañas y terminales se cerrarán.",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "Enfocar",
|
||||
"closeFolderTabs": "Cerrar todas las pestañas de esta carpeta",
|
||||
"removeFromWorkspace": "Quitar del espacio de trabajo"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "Retirer le dossier de l'espace de travail ?",
|
||||
"removeFolderConfirmDescription": "Retirer \"{name}\" de l'espace de travail ? Les onglets et terminaux associés seront fermés.",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "Focaliser",
|
||||
"closeFolderTabs": "Fermer tous les onglets de ce dossier",
|
||||
"removeFromWorkspace": "Retirer de l'espace de travail"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "このフォルダをワークスペースから削除しますか?",
|
||||
"removeFolderConfirmDescription": "\"{name}\" をワークスペースから削除しますか?関連するタブとターミナルが閉じられます。",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "フォーカス",
|
||||
"closeFolderTabs": "このフォルダのすべてのタブを閉じる",
|
||||
"removeFromWorkspace": "ワークスペースから削除"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "이 폴더를 워크스페이스에서 제거하시겠습니까?",
|
||||
"removeFolderConfirmDescription": "워크스페이스에서 \"{name}\"을(를) 제거하시겠습니까? 관련 탭과 터미널이 닫힙니다.",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "포커스",
|
||||
"closeFolderTabs": "이 폴더의 모든 탭 닫기",
|
||||
"removeFromWorkspace": "워크스페이스에서 제거"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "Remover pasta do espaço de trabalho?",
|
||||
"removeFolderConfirmDescription": "Remover \"{name}\" do espaço de trabalho? As abas e terminais relacionados serão fechados.",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "Focar",
|
||||
"closeFolderTabs": "Fechar todas as abas desta pasta",
|
||||
"removeFromWorkspace": "Remover do espaço de trabalho"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "从工作区移除该文件夹?",
|
||||
"removeFolderConfirmDescription": "从工作区移除 \"{name}\"?其相关 Tab 与终端将会关闭。",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "聚焦到此",
|
||||
"closeFolderTabs": "关闭此文件夹的所有 Tab",
|
||||
"removeFromWorkspace": "从工作区移除"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -796,8 +796,6 @@
|
||||
"removeFolderConfirmTitle": "從工作區移除此資料夾?",
|
||||
"removeFolderConfirmDescription": "從工作區移除 \"{name}\"?相關分頁與終端機將會關閉。",
|
||||
"folderHeaderMenu": {
|
||||
"focus": "聚焦至此",
|
||||
"closeFolderTabs": "關閉此資料夾的所有分頁",
|
||||
"removeFromWorkspace": "從工作區移除"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user