From 977c91db1a627e2499a9a96263890b3bd24007fc Mon Sep 17 00:00:00 2001 From: cye2 Date: Thu, 9 Apr 2026 18:02:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(editor):=20=E6=94=AF=E6=8C=81=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E5=85=B3=E9=97=AD=E5=B7=B2=E4=BF=9D=E5=AD=98=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在编辑区标签动作菜单与标签页右键菜单中新增「关闭已保存」入口,并补充相关中英文文案。 --- .../components/files/CurrentFilePanel.tsx | 8 +++ src/renderer/components/files/EditorArea.tsx | 56 ++++++++++++++++++- src/renderer/components/files/EditorTabs.tsx | 15 ++++- src/renderer/components/files/FilePanel.tsx | 8 +++ src/shared/i18n.ts | 2 + 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/renderer/components/files/CurrentFilePanel.tsx b/src/renderer/components/files/CurrentFilePanel.tsx index c910c8f7..81d6a9fb 100644 --- a/src/renderer/components/files/CurrentFilePanel.tsx +++ b/src/renderer/components/files/CurrentFilePanel.tsx @@ -200,6 +200,13 @@ export function CurrentFilePanel({ rootPath, isActive = false }: CurrentFilePane [tabs, promptUnsaved, saveFile, closeFile, t] ); + const closeSavedTabs = useCallback(async () => { + const paths = tabs.filter((tab) => !tab.isDirty).map((tab) => tab.path); + for (const path of paths) { + await closeFile(path); + } + }, [tabs, closeFile]); + // Handle tab click const handleTabClick = useCallback( (path: string) => { @@ -266,6 +273,7 @@ export function CurrentFilePanel({ rootPath, isActive = false }: CurrentFilePane const paths = tabs.map((t) => t.path); await requestCloseTabs(paths); }} + onCloseSaved={closeSavedTabs} onCloseLeft={async (path) => { const index = tabs.findIndex((t) => t.path === path); if (index <= 0) return; diff --git a/src/renderer/components/files/EditorArea.tsx b/src/renderer/components/files/EditorArea.tsx index 68b4e27d..65920ff2 100644 --- a/src/renderer/components/files/EditorArea.tsx +++ b/src/renderer/components/files/EditorArea.tsx @@ -1,5 +1,14 @@ import Editor, { type OnMount } from '@monaco-editor/react'; -import { ChevronRight, Eye, EyeOff, FileCode, FileX, Maximize2, MessageSquare } from 'lucide-react'; +import { + ChevronRight, + Eye, + EyeOff, + FileCode, + FileX, + Maximize2, + MessageSquare, + MoreVertical, +} from 'lucide-react'; import type * as monaco from 'monaco-editor'; import { forwardRef, @@ -18,6 +27,7 @@ import { BreadcrumbList, BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; +import { Button } from '@/components/ui/button'; import { Empty, EmptyDescription, @@ -25,6 +35,7 @@ import { EmptyMedia, EmptyTitle, } from '@/components/ui/empty'; +import { Menu, MenuItem, MenuPopup, MenuTrigger } from '@/components/ui/menu'; import { addToast } from '@/components/ui/toast'; import { useDebouncedSave } from '@/hooks/useDebouncedSave'; import { useI18n } from '@/i18n'; @@ -139,6 +150,7 @@ interface EditorAreaProps { onTabClose: (path: string) => void | Promise; onCloseOthers?: (keepPath: string) => void | Promise; onCloseAll?: () => void | Promise; + onCloseSaved?: () => void | Promise; onCloseLeft?: (path: string) => void | Promise; onCloseRight?: (path: string) => void | Promise; onTabReorder: (fromIndex: number, toIndex: number) => void; @@ -163,6 +175,7 @@ export const EditorArea = forwardRef(function Ed onTabClose, onCloseOthers, onCloseAll, + onCloseSaved, onCloseLeft, onCloseRight, onTabReorder, @@ -1287,7 +1300,7 @@ export const EditorArea = forwardRef(function Ed return (
{/* Tabs */} -
+
{isFileTreeCollapsed && onToggleFileTree && (
+ {tabs.length > 0 && (onCloseSaved || onCloseAll) && ( + + + + + } + /> + + !tab.isDirty)} + onClick={async () => { + if (onCloseSaved) await onCloseSaved(); + }} + > + {t('Close Saved Tabs')} + + { + if (!onCloseAll) return; + await onCloseAll(); + }} + > + {t('Close All Tabs')} + + + + )} {/* Markdown Preview Toggle */} {isMarkdown && (