From b5c2206c8858fa2349ccf9544a964e616f378e09 Mon Sep 17 00:00:00 2001 From: RememBerBer Date: Thu, 27 Aug 2026 10:42:03 +0800 Subject: [PATCH] =?UTF-8?q?[next]next=E7=89=88=EF=BC=8C=E9=9A=8F=E6=89=8B?= =?UTF-8?q?=E8=AE=B0=EF=BC=8C=E5=88=9B=E5=BB=BA=E5=89=AF=E6=9C=AC=E3=80=81?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E3=80=81=E6=96=87=E6=A1=A3=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E8=BD=AC=E7=A7=BB=E5=88=B0=E5=B7=A6=E4=BE=A7?= =?UTF-8?q?=E5=8F=B3=E9=94=AE=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next/src/features/quickNote/QuickNoteTool.tsx | 47 +++++++++++-------- next/src/features/quickNote/QuickNoteTree.tsx | 24 ++++++++-- next/src/shared/styles/global.css | 6 +++ 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/next/src/features/quickNote/QuickNoteTool.tsx b/next/src/features/quickNote/QuickNoteTool.tsx index 32e63c48..9643fd72 100644 --- a/next/src/features/quickNote/QuickNoteTool.tsx +++ b/next/src/features/quickNote/QuickNoteTool.tsx @@ -3,8 +3,6 @@ import { Check, ChevronDown, Columns2, - CopyPlus, - Download, Eye, FilePenLine, FilePlus2, @@ -13,7 +11,6 @@ import { FoldVertical, GitBranch, ImagePlus, - Info, List, ListOrdered, PanelLeftClose, @@ -551,21 +548,23 @@ export function QuickNoteTool() { await Promise.all([loadTree(), reloadCurrentNoteFromDisk(), refreshGitChangeCount()]) } - async function selectNode(node: QuickNoteNode): Promise { + async function selectNode(node: QuickNoteNode): Promise { if (node.kind === 'directory') { if (dirty) await saveCurrent(false) update({ selectedPath: node.relativePath, selectedKind: 'directory', note: null, content: '', metadataDirty: false }) - return + return null } - if (node.relativePath === state.note?.relativePath) return - if (dirty && !await saveCurrent(false)) return + if (node.relativePath === state.note?.relativePath) return state.note + if (dirty && !await saveCurrent(false)) return null update({ busy: true }) try { const note = await window.mootool.readQuickNote(node.relativePath) update({ selectedPath: note.relativePath, selectedKind: 'file', note, content: note.content, busy: false, metadataDirty: false }) + return note } catch (error) { update({ busy: false }) toast.error(errorMessage(error)) + return null } } @@ -594,6 +593,7 @@ export function QuickNoteTool() { async function openTreeAction(node: QuickNoteNode, mode: 'rename' | 'move' | 'delete'): Promise { if (node.kind === 'directory') { + if (dirty && !await saveCurrent(false)) return update({ selectedPath: node.relativePath, selectedKind: 'directory', @@ -684,13 +684,15 @@ export function QuickNoteTool() { } } - async function duplicateNote(): Promise { - if (!state.note) return - if (dirty && !await saveCurrent(false)) return + async function duplicateNote(node: QuickNoteNode): Promise { + if (node.kind !== 'file') return + const note = await selectNode(node) + if (!note) return + if (node.relativePath === state.note?.relativePath && dirty && !await saveCurrent(false)) return try { - const note = await window.mootool.duplicateQuickNote(state.note.relativePath) + const duplicate = await window.mootool.duplicateQuickNote(node.relativePath) await loadTree() - update({ selectedPath: note.relativePath, selectedKind: 'file', note, content: note.content, metadataDirty: false }) + update({ selectedPath: duplicate.relativePath, selectedKind: 'file', note: duplicate, content: duplicate.content, metadataDirty: false }) } catch (error) { toast.error(errorMessage(error)) } @@ -772,16 +774,24 @@ export function QuickNoteTool() { attachmentInsertionQueueRef.current = operation } - async function exportNote(): Promise { - if (!state.note) return + async function exportNote(node: QuickNoteNode): Promise { + if (node.kind !== 'file') return + const note = await selectNode(node) + if (!note) return + const content = node.relativePath === state.note?.relativePath ? state.content : note.content const path = await window.mootool.saveTextFile({ kind: 'text', - defaultName: `${state.note.metadata.title}.txt`, - content: state.content + defaultName: `${note.metadata.title}.txt`, + content }) if (path) toast.success(t('quickNote.export')) } + async function showDocumentInfo(node: QuickNoteNode): Promise { + if (node.kind !== 'file') return + if (await selectNode(node)) update({ infoOpen: true }) + } + function insertAttachment( attachment: QuickNoteAttachment, requestedSelection?: TextSelection @@ -967,7 +977,7 @@ export function QuickNoteTool() {
{ quickNoteTreeScrollTop = event.currentTarget.scrollTop }}> {state.nodes.length - ? { void selectNode(node) }} onToggle={toggleDirectory} onMove={(node, targetDirectory) => { void moveTreeEntry(node, targetDirectory) }} onRenameRequest={(node) => { void openTreeAction(node, 'rename') }} onMoveRequest={(node) => { void openTreeAction(node, 'move') }} onDeleteRequest={(node) => { void openTreeAction(node, 'delete') }} onRevealRequest={(node) => { void window.mootool.revealQuickNoteEntry(node.relativePath).catch((error) => toast.error(errorMessage(error))) }} renameLabel={t('quickNote.rename')} moveLabel={t('quickNote.move')} deleteLabel={t('quickNote.delete')} revealLabel={t('quickNote.revealInFinder')} /> + ? { void selectNode(node) }} onToggle={toggleDirectory} onMove={(node, targetDirectory) => { void moveTreeEntry(node, targetDirectory) }} onRenameRequest={(node) => { void openTreeAction(node, 'rename') }} onMoveRequest={(node) => { void openTreeAction(node, 'move') }} onDuplicateRequest={(node) => { void duplicateNote(node) }} onExportRequest={(node) => { void exportNote(node) }} onInfoRequest={(node) => { void showDocumentInfo(node) }} onDeleteRequest={(node) => { void openTreeAction(node, 'delete') }} onRevealRequest={(node) => { void window.mootool.revealQuickNoteEntry(node.relativePath).catch((error) => toast.error(errorMessage(error))) }} renameLabel={t('quickNote.rename')} moveLabel={t('quickNote.move')} duplicateLabel={t('quickNote.duplicate')} exportLabel={t('quickNote.export')} infoLabel={t('quickNote.info')} deleteLabel={t('quickNote.delete')} revealLabel={t('quickNote.revealInFinder')} /> :
{t('quickNote.empty')}
}
@@ -1021,11 +1031,8 @@ export function QuickNoteTool() { { if (state.findOpen) update({ findOpen: false }); else openFindReplace() }} /> { void saveCurrent() }} /> - { void duplicateNote() }} /> { void importAttachment() }} /> - { void exportNote() }} /> update({ quickReplaceOpen: !state.quickReplaceOpen })} /> - update({ infoOpen: true })} /> update({ gitOpen: true })} /> { void window.mootool.openQuickNoteVault() }} /> openAction('delete')} /> diff --git a/next/src/features/quickNote/QuickNoteTree.tsx b/next/src/features/quickNote/QuickNoteTree.tsx index 146c8a49..77a105af 100644 --- a/next/src/features/quickNote/QuickNoteTree.tsx +++ b/next/src/features/quickNote/QuickNoteTree.tsx @@ -13,10 +13,16 @@ type QuickNoteTreeProps = { onMove: (node: Pick, targetDirectory: string) => void onRenameRequest: (node: QuickNoteNode) => void onMoveRequest: (node: QuickNoteNode) => void + onDuplicateRequest: (node: QuickNoteNode) => void + onExportRequest: (node: QuickNoteNode) => void + onInfoRequest: (node: QuickNoteNode) => void onDeleteRequest: (node: QuickNoteNode) => void onRevealRequest: (node: QuickNoteNode) => void renameLabel: string moveLabel: string + duplicateLabel: string + exportLabel: string + infoLabel: string deleteLabel: string revealLabel: string } @@ -24,7 +30,7 @@ type QuickNoteTreeProps = { const quickNotePathType = 'application/x-mootool-quick-note-path' const quickNoteKindType = 'application/x-mootool-quick-note-kind' -export function QuickNoteTree({ nodes, selectedPath, expanded, onSelect, onToggle, onMove, onRenameRequest, onMoveRequest, onDeleteRequest, onRevealRequest, renameLabel, moveLabel, deleteLabel, revealLabel }: QuickNoteTreeProps) { +export function QuickNoteTree({ nodes, selectedPath, expanded, onSelect, onToggle, onMove, onRenameRequest, onMoveRequest, onDuplicateRequest, onExportRequest, onInfoRequest, onDeleteRequest, onRevealRequest, renameLabel, moveLabel, duplicateLabel, exportLabel, infoLabel, deleteLabel, revealLabel }: QuickNoteTreeProps) { const toolActive = useToolActivity() const menuRef = useRef(null) const [contextMenu, setContextMenu] = useState<{ node: QuickNoteNode; left: number; top: number } | null>(null) @@ -69,7 +75,6 @@ export function QuickNoteTree({ nodes, selectedPath, expanded, onSelect, onToggl onToggle={onToggle} onMove={onMove} onOpenContextMenu={(menuNode, left, top) => { - onSelect(menuNode) setContextMenu({ node: menuNode, left, top }) }} /> @@ -85,6 +90,14 @@ export function QuickNoteTree({ nodes, selectedPath, expanded, onSelect, onToggl > + {contextMenu.node.kind === 'file' && ( + <> + + + +
+ + )}
, @@ -120,7 +133,12 @@ function QuickNoteTreeNode({ node, depth, selectedPath, expanded, onSelect, onTo }} onContextMenu={(event) => { event.preventDefault() - onOpenContextMenu(node, Math.min(event.clientX, window.innerWidth - 164), Math.min(event.clientY, window.innerHeight - 138)) + const menuHeight = node.kind === 'file' ? 238 : 140 + onOpenContextMenu( + node, + Math.max(8, Math.min(event.clientX, window.innerWidth - 176)), + Math.max(8, Math.min(event.clientY, window.innerHeight - menuHeight)) + ) }} onDragStart={(event) => { event.dataTransfer.effectAllowed = 'move' diff --git a/next/src/shared/styles/global.css b/next/src/shared/styles/global.css index 0323efce..e8c30dae 100644 --- a/next/src/shared/styles/global.css +++ b/next/src/shared/styles/global.css @@ -901,6 +901,12 @@ text-align: left; } +.quick-note-tree-menu__separator { + height: 1px; + margin: 4px 7px; + background: var(--border-control); +} + .quick-note-tree-menu button:hover, .quick-note-tree-menu button:focus-visible, .vault-tree-menu button:hover,