feat: harden local shell, SFTP, and desktop session reliability
Finder-launched terminals now inherit a login PATH so tools like ollama resolve, destructive SFTP/editor actions require confirmation, and terminal search plus window dragging work again. SSH forwards and RDP sessions keep listeners and survive reconnect without FastPath crashes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
188f3556ed
commit
08696896f9
223 files changed
+13493
-3423
No files matched your search
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* TransferPanel - 传输队列面板
|
||||
*
|
||||
* 显示文件上传/下载的传输队列
|
||||
*
|
||||
* 显示文件上传/下载的传输队列;进行中任务置顶。
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { TransferItem } from './TransferItem'
|
||||
@@ -15,16 +16,47 @@ interface TransferPanelProps {
|
||||
onToggle: () => void
|
||||
onCancel?: (id: string) => void
|
||||
onRemove: (id: string) => void
|
||||
onRetry?: (id: string) => void
|
||||
}
|
||||
|
||||
export function TransferPanel({ tasks, isOpen, onToggle, onCancel, onRemove }: TransferPanelProps) {
|
||||
/** transferring → pending → failed/canceled → completed(组内保持原相对顺序) */
|
||||
// eslint-disable-next-line react-refresh/only-export-components -- pure helper used by tests and panel
|
||||
export function sortTransferTasks(tasks: TransferTask[]): TransferTask[] {
|
||||
const rank = (status: TransferTask['status']): number => {
|
||||
switch (status) {
|
||||
case 'transferring':
|
||||
return 0
|
||||
case 'pending':
|
||||
return 1
|
||||
case 'failed':
|
||||
case 'canceled':
|
||||
return 2
|
||||
case 'completed':
|
||||
return 3
|
||||
default:
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
return tasks
|
||||
.map((task, index) => ({ task, index }))
|
||||
.sort((a, b) => {
|
||||
const byStatus = rank(a.task.status) - rank(b.task.status)
|
||||
return byStatus !== 0 ? byStatus : a.index - b.index
|
||||
})
|
||||
.map(({ task }) => task)
|
||||
}
|
||||
|
||||
export function TransferPanel({ tasks, isOpen, onToggle, onCancel, onRemove, onRetry }: TransferPanelProps) {
|
||||
const { t } = useTranslation('sftp')
|
||||
const activeCount = tasks.filter(t => t.status === 'pending' || t.status === 'transferring').length
|
||||
const completedCount = tasks.filter(t => t.status === 'completed').length
|
||||
const orderedTasks = useMemo(() => sortTransferTasks(tasks), [tasks])
|
||||
const activeCount = tasks.filter((task) => task.status === 'pending' || task.status === 'transferring').length
|
||||
const completedCount = tasks.filter((task) => task.status === 'completed').length
|
||||
|
||||
return (
|
||||
<div className="bg-dark-800 border-t border-dark-700/30">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
className="w-full flex items-center justify-between px-4 py-2 hover:bg-dark-700 transition-colors"
|
||||
>
|
||||
@@ -36,7 +68,7 @@ export function TransferPanel({ tasks, isOpen, onToggle, onCancel, onRemove }: T
|
||||
</span>
|
||||
)}
|
||||
{completedCount > 0 && (
|
||||
<span className="px-2 py-0.5 bg-green-500/20 text-green-400 text-xs rounded-full">
|
||||
<span className="px-2 py-0.5 bg-feedback-success/15 text-feedback-success text-xs rounded-full">
|
||||
{t('transfer.completed', { count: completedCount })}
|
||||
</span>
|
||||
)}
|
||||
@@ -47,14 +79,15 @@ export function TransferPanel({ tasks, isOpen, onToggle, onCancel, onRemove }: T
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{isOpen && tasks.length > 0 && (
|
||||
{isOpen && orderedTasks.length > 0 && (
|
||||
<div className="max-h-48 overflow-y-auto border-t border-dark-700/30">
|
||||
{tasks.map((task) => (
|
||||
{orderedTasks.map((task) => (
|
||||
<TransferItem
|
||||
key={task.id}
|
||||
task={task}
|
||||
onCancel={onCancel ? () => onCancel(task.id) : undefined}
|
||||
onRemove={() => onRemove(task.id)}
|
||||
onRetry={onRetry ? () => onRetry(task.id) : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user