feat: breadcrumb trash/restore buttons and cmd+del shortcut
- Added trash icon button in breadcrumb bar for non-trashed notes - Added restore (arrow counter-clockwise) button for trashed notes - Trash button sets trashed=true and trashed_at in frontmatter - Restore button removes trashed/trashed_at from frontmatter - Added Cmd+Delete keyboard shortcut to trash active note - Wired handlers through Editor -> App with vault entry updates - Toast notifications for trash/restore actions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
18
src/App.tsx
18
src/App.tsx
@@ -112,10 +112,26 @@ function App() {
|
||||
vault.updateEntry(typeEntry.path, { icon, color })
|
||||
}, [vault, notes])
|
||||
|
||||
const handleTrashNote = useCallback(async (path: string) => {
|
||||
const now = new Date().toISOString().slice(0, 10)
|
||||
await notes.handleUpdateFrontmatter(path, 'trashed', true)
|
||||
await notes.handleUpdateFrontmatter(path, 'trashed_at', now)
|
||||
vault.updateEntry(path, { trashed: true, trashedAt: Date.now() / 1000 })
|
||||
setToastMessage('Note moved to trash')
|
||||
}, [notes, vault, setToastMessage])
|
||||
|
||||
const handleRestoreNote = useCallback(async (path: string) => {
|
||||
await notes.handleUpdateFrontmatter(path, 'trashed', false)
|
||||
await notes.handleDeleteProperty(path, 'trashed_at')
|
||||
vault.updateEntry(path, { trashed: false, trashedAt: null })
|
||||
setToastMessage('Note restored from trash')
|
||||
}, [notes, vault, setToastMessage])
|
||||
|
||||
useAppKeyboard({
|
||||
onQuickOpen: () => setShowQuickOpen(true),
|
||||
onCreateNote: openCreateDialog,
|
||||
onSave: () => setToastMessage('Saved'),
|
||||
onTrashNote: handleTrashNote,
|
||||
activeTabPathRef: notes.activeTabPathRef,
|
||||
handleCloseTabRef: notes.handleCloseTabRef,
|
||||
})
|
||||
@@ -193,6 +209,8 @@ function App() {
|
||||
showAIChat={showAIChat}
|
||||
onToggleAIChat={() => setShowAIChat(c => !c)}
|
||||
vaultPath={vaultPath}
|
||||
onTrashNote={handleTrashNote}
|
||||
onRestoreNote={handleRestoreNote}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
Sparkle,
|
||||
SlidersHorizontal,
|
||||
DotsThree,
|
||||
Trash,
|
||||
ArrowCounterClockwise,
|
||||
} from '@phosphor-icons/react'
|
||||
|
||||
interface BreadcrumbBarProps {
|
||||
@@ -22,6 +24,8 @@ interface BreadcrumbBarProps {
|
||||
onToggleAIChat?: () => void
|
||||
inspectorCollapsed?: boolean
|
||||
onToggleInspector?: () => void
|
||||
onTrash?: () => void
|
||||
onRestore?: () => void
|
||||
}
|
||||
|
||||
const DISABLED_ICON_STYLE = { opacity: 0.4, cursor: 'not-allowed' } as const
|
||||
@@ -29,6 +33,7 @@ const DISABLED_ICON_STYLE = { opacity: 0.4, cursor: 'not-allowed' } as const
|
||||
export const BreadcrumbBar = memo(function BreadcrumbBar({
|
||||
entry, wordCount, isModified, showDiffToggle, diffMode, diffLoading,
|
||||
onToggleDiff, showAIChat, onToggleAIChat, inspectorCollapsed, onToggleInspector,
|
||||
onTrash, onRestore,
|
||||
}: BreadcrumbBarProps) {
|
||||
return (
|
||||
<div
|
||||
@@ -104,6 +109,23 @@ export const BreadcrumbBar = memo(function BreadcrumbBar({
|
||||
>
|
||||
<Sparkle size={16} weight={showAIChat ? 'fill' : 'regular'} />
|
||||
</button>
|
||||
{entry.trashed ? (
|
||||
<button
|
||||
className="flex items-center justify-center border-none bg-transparent p-0 cursor-pointer transition-colors text-muted-foreground hover:text-foreground"
|
||||
onClick={onRestore}
|
||||
title="Restore from trash"
|
||||
>
|
||||
<ArrowCounterClockwise size={16} />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="flex items-center justify-center border-none bg-transparent p-0 cursor-pointer transition-colors text-muted-foreground hover:text-destructive"
|
||||
onClick={onTrash}
|
||||
title="Move to trash (Cmd+Delete)"
|
||||
>
|
||||
<Trash size={16} />
|
||||
</button>
|
||||
)}
|
||||
{inspectorCollapsed && (
|
||||
<button
|
||||
className="flex items-center justify-center border-none bg-transparent p-0 text-muted-foreground cursor-pointer hover:text-foreground transition-colors"
|
||||
|
||||
@@ -48,6 +48,8 @@ interface EditorProps {
|
||||
showAIChat?: boolean
|
||||
onToggleAIChat?: () => void
|
||||
vaultPath?: string
|
||||
onTrashNote?: (path: string) => void
|
||||
onRestoreNote?: (path: string) => void
|
||||
}
|
||||
|
||||
// --- Custom Inline Content: WikiLink ---
|
||||
@@ -151,6 +153,7 @@ export const Editor = memo(function Editor({
|
||||
onUpdateFrontmatter, onDeleteProperty, onAddProperty,
|
||||
showAIChat, onToggleAIChat,
|
||||
vaultPath,
|
||||
onTrashNote, onRestoreNote,
|
||||
}: EditorProps) {
|
||||
const [diffMode, setDiffMode] = useState(false)
|
||||
const [diffContent, setDiffContent] = useState<string | null>(null)
|
||||
@@ -351,6 +354,8 @@ export const Editor = memo(function Editor({
|
||||
onToggleAIChat={onToggleAIChat}
|
||||
inspectorCollapsed={inspectorCollapsed}
|
||||
onToggleInspector={onToggleInspector}
|
||||
onTrash={onTrashNote ? () => onTrashNote(activeTab.entry.path) : undefined}
|
||||
onRestore={onRestoreNote ? () => onRestoreNote(activeTab.entry.path) : undefined}
|
||||
/>
|
||||
) : null
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ interface KeyboardActions {
|
||||
onQuickOpen: () => void
|
||||
onCreateNote: () => void
|
||||
onSave: () => void
|
||||
onTrashNote: (path: string) => void
|
||||
activeTabPathRef: React.MutableRefObject<string | null>
|
||||
handleCloseTabRef: React.MutableRefObject<(path: string) => void>
|
||||
}
|
||||
|
||||
export function useAppKeyboard({
|
||||
onQuickOpen, onCreateNote, onSave,
|
||||
onQuickOpen, onCreateNote, onSave, onTrashNote,
|
||||
activeTabPathRef, handleCloseTabRef,
|
||||
}: KeyboardActions) {
|
||||
useEffect(() => {
|
||||
@@ -36,9 +37,16 @@ export function useAppKeyboard({
|
||||
if (path) handleCloseTabRef.current(path)
|
||||
break
|
||||
}
|
||||
case 'Backspace':
|
||||
case 'Delete': {
|
||||
e.preventDefault()
|
||||
const path = activeTabPathRef.current
|
||||
if (path) onTrashNote(path)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||
}, [onQuickOpen, onCreateNote, onSave, activeTabPathRef, handleCloseTabRef])
|
||||
}, [onQuickOpen, onCreateNote, onSave, onTrashNote, activeTabPathRef, handleCloseTabRef])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user