mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-27 15:13:37 +08:00
feat(sidebar): add batch export from project context menu (#81)
- Project context menu gains 'Export sessions…' entry that enters batch mode with all project sessions pre-selected. - Batch toolbar adds 'Export selected (N)' button alongside the existing select-all and delete buttons (grid now 3-col). - handleBatchExport loops over selected sessions, calling the existing per-session JSONL export, then exits batch mode.
This commit is contained in:
parent
0f2729adcd
commit
5a5997e50a
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState, useCallback, useMemo, useRef } from 'react'
|
||||
import { Folder, FolderOpen, MoreHorizontal, Pin, PinOff, RefreshCw, RotateCcw, SquarePen, Trash2, Upload, X } from 'lucide-react'
|
||||
import { Download, Folder, FolderOpen, MoreHorizontal, Pin, PinOff, RefreshCw, RotateCcw, SquarePen, Trash2, Upload, X } from 'lucide-react'
|
||||
import { useSessionStore } from '../../stores/sessionStore'
|
||||
import { useUIStore } from '../../stores/uiStore'
|
||||
import { useTranslation } from '../../i18n'
|
||||
@ -631,6 +631,19 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
|
||||
setPendingBatchDeleteSessionIds(null)
|
||||
}, [exitBatchMode])
|
||||
|
||||
const handleBatchExport = useCallback(async () => {
|
||||
const ids = [...selectedSessionIds]
|
||||
if (ids.length === 0) return
|
||||
let successCount = 0
|
||||
for (const sessionId of ids) {
|
||||
await handleExportSession(sessionId)
|
||||
successCount++
|
||||
}
|
||||
if (successCount > 0) {
|
||||
handleExitBatchMode()
|
||||
}
|
||||
}, [handleExportSession, handleExitBatchMode, selectedSessionIds])
|
||||
|
||||
const requestBatchDelete = useCallback((ids: string[]) => {
|
||||
if (ids.length === 0) return
|
||||
setPendingBatchDeleteSessionIds([...new Set(ids)])
|
||||
@ -920,7 +933,7 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
|
||||
<span className="material-symbols-outlined text-[17px]">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-2 grid grid-cols-2 gap-1.5">
|
||||
<div className="mt-2 grid grid-cols-3 gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@ -937,6 +950,14 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
|
||||
? t('sidebar.batchDeselectAll')
|
||||
: t('sidebar.batchSelectAll')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void handleBatchExport()}
|
||||
disabled={selectedCount === 0}
|
||||
className="rounded-md border border-[var(--color-border)] px-2 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)] disabled:opacity-50"
|
||||
>
|
||||
{t('sidebar.batchExportSelected', { count: selectedCount })}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => requestBatchDelete([...selectedSessionIds])}
|
||||
@ -1292,6 +1313,19 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
|
||||
>
|
||||
{t('sidebar.importSession')}
|
||||
</ProjectMenuItem>
|
||||
<ProjectMenuItem
|
||||
icon={<Download size={18} aria-hidden="true" />}
|
||||
onClick={() => {
|
||||
setProjectContextMenu(null)
|
||||
enterBatchMode()
|
||||
const projectSessionIds = sessions
|
||||
.filter((s) => (s.workDir || s.projectRoot || s.projectPath) === project.workDir)
|
||||
.map((s) => s.id)
|
||||
selectSessions(projectSessionIds)
|
||||
}}
|
||||
>
|
||||
{t('sidebar.exportProjectSessions')}
|
||||
</ProjectMenuItem>
|
||||
<ProjectMenuItem
|
||||
icon={hidden ? <RotateCcw size={18} aria-hidden="true" /> : <X size={18} aria-hidden="true" />}
|
||||
onClick={() => toggleHiddenProject(project)}
|
||||
|
||||
@ -92,6 +92,8 @@ export const en = {
|
||||
'sidebar.batchDeselectAll': 'Deselect all',
|
||||
'sidebar.batchSelectGroup': 'Select {group}',
|
||||
'sidebar.batchDeleteSelected': 'Delete selected ({count})',
|
||||
'sidebar.batchExportSelected': 'Export selected ({count})',
|
||||
'sidebar.exportProjectSessions': 'Export sessions…',
|
||||
'sidebar.batchDeleteConfirm': 'Delete {count} sessions? This cannot be undone.',
|
||||
'sidebar.batchDeleteConfirmBody': 'The following sessions will be deleted:',
|
||||
'sidebar.batchDeleteMore': '...and {count} more',
|
||||
|
||||
@ -94,6 +94,8 @@ export const jp: Record<TranslationKey, string> = {
|
||||
'sidebar.batchDeselectAll': 'すべて選択解除',
|
||||
'sidebar.batchSelectGroup': '{group} を選択',
|
||||
'sidebar.batchDeleteSelected': '選択項目を削除 ({count})',
|
||||
'sidebar.batchExportSelected': '選択項目をエクスポート ({count})',
|
||||
'sidebar.exportProjectSessions': 'セッションをエクスポート…',
|
||||
'sidebar.batchDeleteConfirm': '{count} 件のセッションを削除しますか?この操作は取り消せません。',
|
||||
'sidebar.batchDeleteConfirmBody': '以下のセッションが削除されます:',
|
||||
'sidebar.batchDeleteMore': '...他 {count} 件',
|
||||
|
||||
@ -94,6 +94,8 @@ export const kr: Record<TranslationKey, string> = {
|
||||
'sidebar.batchDeselectAll': '모두 선택 해제',
|
||||
'sidebar.batchSelectGroup': '{group} 선택',
|
||||
'sidebar.batchDeleteSelected': '선택 항목 삭제 ({count})',
|
||||
'sidebar.batchExportSelected': '선택 항목 내보내기 ({count})',
|
||||
'sidebar.exportProjectSessions': '세션 내보내기…',
|
||||
'sidebar.batchDeleteConfirm': '{count}개의 세션을 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.',
|
||||
'sidebar.batchDeleteConfirmBody': '다음 세션이 삭제됩니다:',
|
||||
'sidebar.batchDeleteMore': '...외 {count}개',
|
||||
|
||||
@ -94,6 +94,8 @@ export const zh: Record<TranslationKey, string> = {
|
||||
'sidebar.batchDeselectAll': '取消全選',
|
||||
'sidebar.batchSelectGroup': '選擇{group}',
|
||||
'sidebar.batchDeleteSelected': '刪除已選 ({count})',
|
||||
'sidebar.batchExportSelected': '匯出已選 ({count})',
|
||||
'sidebar.exportProjectSessions': '匯出會話…',
|
||||
'sidebar.batchDeleteConfirm': '確定要刪除 {count} 個會話嗎?此操作不可撤銷。',
|
||||
'sidebar.batchDeleteConfirmBody': '以下會話將被刪除:',
|
||||
'sidebar.batchDeleteMore': '...還有 {count} 條',
|
||||
|
||||
@ -94,6 +94,8 @@ export const zh: Record<TranslationKey, string> = {
|
||||
'sidebar.batchDeselectAll': '取消全选',
|
||||
'sidebar.batchSelectGroup': '选择{group}',
|
||||
'sidebar.batchDeleteSelected': '删除已选 ({count})',
|
||||
'sidebar.batchExportSelected': '导出已选 ({count})',
|
||||
'sidebar.exportProjectSessions': '导出会话…',
|
||||
'sidebar.batchDeleteConfirm': '确定要删除 {count} 个会话吗?此操作不可撤销。',
|
||||
'sidebar.batchDeleteConfirmBody': '以下会话将被删除:',
|
||||
'sidebar.batchDeleteMore': '...还有 {count} 条',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user