Skip to content

Commit ab9bc1e

Browse files
authored
Merge pull request #27 from pheuberger/claude/add-bookmark-shortcuts-sSe5s
Add quick tag modal and keyboard shortcuts for bookmark management
2 parents 86bb334 + 4fa59b9 commit ab9bc1e

3 files changed

Lines changed: 376 additions & 0 deletions

File tree

src/components/bookmarks/BookmarkList.jsx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import { FilterBar } from './FilterBar'
1111
import { SelectionActionBar } from './SelectionActionBar'
1212
import { SettingsView } from '../ui/SettingsView'
1313
import { HelpModal } from '../ui/HelpModal'
14+
import { QuickTagModal } from '../ui/QuickTagModal'
1415
import { ToastContainer } from '../ui/Toast'
1516
import { PackageOpen } from '../ui/Icons'
1617
import {
1718
getAllBookmarks,
1819
deleteBookmark,
1920
bulkDeleteBookmarks,
21+
toggleReadLater,
2022
} from '../../services/bookmarks'
2123

2224
export function BookmarkList() {
@@ -52,6 +54,8 @@ export function BookmarkList() {
5254
const [searchQuery, setSearchQuery] = useState('')
5355
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
5456
const [isHelpOpen, setIsHelpOpen] = useState(false)
57+
const [isTagModalOpen, setIsTagModalOpen] = useState(false)
58+
const [tagModalBookmark, setTagModalBookmark] = useState(null)
5559
const [selectedIndex, setSelectedIndex] = useState(-1)
5660
const [selectionMode, setSelectionMode] = useState(false)
5761
const [selectedIds, setSelectedIds] = useState(new Set())
@@ -335,6 +339,63 @@ export function BookmarkList() {
335339
}
336340
}, [selectedIds, addToast, exitSelectionMode])
337341

342+
// Get the currently selected bookmark
343+
const getSelectedBookmark = useCallback(() => {
344+
if (selectedIndex >= 0 && selectedIndex < filteredBookmarks.length) {
345+
return filteredBookmarks[selectedIndex]
346+
}
347+
return null
348+
}, [selectedIndex, filteredBookmarks])
349+
350+
// Shift+T: Open tag edit modal for selected bookmark
351+
const openTagModal = useCallback(() => {
352+
if (filterView === 'inbox') return
353+
if (isAddingNew || editingBookmarkId) return
354+
const bookmark = getSelectedBookmark()
355+
if (bookmark) {
356+
setTagModalBookmark(bookmark)
357+
setIsTagModalOpen(true)
358+
}
359+
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark])
360+
361+
const closeTagModal = useCallback(() => {
362+
setIsTagModalOpen(false)
363+
setTagModalBookmark(null)
364+
}, [])
365+
366+
// Shift+L: Toggle read later for selected bookmark
367+
const toggleReadLaterSelected = useCallback(() => {
368+
if (filterView === 'inbox') return
369+
if (isAddingNew || editingBookmarkId) return
370+
const bookmark = getSelectedBookmark()
371+
if (bookmark) {
372+
try {
373+
const newValue = toggleReadLater(bookmark._id)
374+
addToast({
375+
message: newValue ? 'Added to Read Later' : 'Removed from Read Later',
376+
duration: 2000,
377+
})
378+
} catch (error) {
379+
console.error('Failed to toggle read later:', error)
380+
}
381+
}
382+
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])
383+
384+
// c: Copy URL to clipboard
385+
const copySelectedUrl = useCallback(() => {
386+
if (filterView === 'inbox') return
387+
if (isAddingNew || editingBookmarkId) return
388+
const bookmark = getSelectedBookmark()
389+
if (bookmark) {
390+
navigator.clipboard.writeText(bookmark.url).then(() => {
391+
addToast({ message: 'URL copied to clipboard', duration: 2000 })
392+
}).catch((error) => {
393+
console.error('Failed to copy URL:', error)
394+
addToast({ message: 'Failed to copy URL', duration: 2000 })
395+
})
396+
}
397+
}, [filterView, isAddingNew, editingBookmarkId, getSelectedBookmark, addToast])
398+
338399
useEffect(() => {
339400
setSelectedIndex(-1)
340401
}, [filteredBookmarks.length, filterView, selectedTag, debouncedSearchQuery])
@@ -364,8 +425,12 @@ export function BookmarkList() {
364425
'shift+j': selectNextWithShift,
365426
'shift+k': selectPrevWithShift,
366427
'enter': openSelected,
428+
'o': openSelected,
367429
'e': editSelected,
368430
'd': selectionMode && selectedIds.size > 0 ? handleBulkDelete : deleteSelected,
431+
't': openTagModal,
432+
'l': toggleReadLaterSelected,
433+
'c': copySelectedUrl,
369434
'mod+k': focusSearch,
370435
'q': exitInbox,
371436
'mod+z': handleUndo,
@@ -547,6 +612,12 @@ export function BookmarkList() {
547612

548613
<HelpModal isOpen={isHelpOpen} onClose={() => setIsHelpOpen(false)} />
549614

615+
<QuickTagModal
616+
isOpen={isTagModalOpen}
617+
onClose={closeTagModal}
618+
bookmark={tagModalBookmark}
619+
/>
620+
550621
<SelectionActionBar
551622
selectedCount={selectedIds.size}
552623
onDelete={handleBulkDelete}

src/components/ui/HelpModal.jsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,35 @@ const HOTKEY_GROUPS = [
1212
{ keys: ['j'], description: 'Select next bookmark' },
1313
{ keys: ['k'], description: 'Select previous bookmark' },
1414
{ keys: ['Enter'], description: 'Open selected bookmark' },
15+
{ keys: ['o'], description: 'Open selected bookmark' },
1516
{ keys: ['e'], description: 'Edit selected bookmark' },
17+
{ keys: ['d'], description: 'Delete selected bookmark' },
1618
{ keys: ['Ctrl', 'k'], description: 'Focus search bar', modifier: true },
1719
],
1820
},
21+
{
22+
title: 'Quick Actions',
23+
hotkeys: [
24+
{ keys: ['t'], description: 'Edit tags' },
25+
{ keys: ['l'], description: 'Toggle read later' },
26+
{ keys: ['c'], description: 'Copy URL' },
27+
],
28+
},
1929
{
2030
title: 'Go To',
2131
hotkeys: [
2232
{ keys: ['g', 'a'], description: 'All bookmarks' },
2333
{ keys: ['g', 'l'], description: 'Read later' },
34+
{ keys: ['g', 'i'], description: 'Inbox' },
2435
{ keys: ['g', 's'], description: 'Settings' },
2536
{ keys: ['g', 'n'], description: 'New bookmark' },
2637
],
2738
},
2839
{
2940
title: 'General',
3041
hotkeys: [
42+
{ keys: ['Ctrl', 'z'], description: 'Undo', modifier: true },
43+
{ keys: ['Ctrl', 'Shift', 'z'], description: 'Redo', modifier: true },
3144
{ keys: ['Ctrl', 'Enter'], description: 'Submit form', modifier: true },
3245
{ keys: ['?'], description: 'Show this help' },
3346
{ keys: ['Esc'], description: 'Close modal / Cancel' },

0 commit comments

Comments
 (0)