feat: add new view options and update keyboard shortcuts for improved navigation

This commit is contained in:
Mo Tarbin
2026-08-16 02:53:53 -04:00
parent 1c99bd1886
commit 97453803d6
7 changed files with 99 additions and 42 deletions

View File

@@ -61,6 +61,12 @@
"createFilter": "Create a filter", "createFilter": "Create a filter",
"viewAllTasks": "View all tasks", "viewAllTasks": "View all tasks",
"viewArchivedTasks": "View archived tasks", "viewArchivedTasks": "View archived tasks",
"viewThings": "View things",
"viewLabels": "View labels",
"viewProjects": "View projects",
"viewFilters": "View filters",
"viewActivities": "View activities",
"viewPoints": "View points",
"openSettings": "Open settings", "openSettings": "Open settings",
"filterTasks": "Show tasks matching “{{query}}”", "filterTasks": "Show tasks matching “{{query}}”",
"filterTasksSubtitle": "Filter the task list" "filterTasksSubtitle": "Filter the task list"

View File

@@ -152,7 +152,7 @@ export const GlobalSearchProvider = ({ children }) => {
useEffect(() => { useEffect(() => {
const onKeyDown = event => { const onKeyDown = event => {
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'f') { if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {
event.preventDefault() event.preventDefault()
isOpen ? closeSearch() : openSearch() isOpen ? closeSearch() : openSearch()
} }

View File

@@ -1,6 +1,8 @@
import { import {
AddRounded, AddRounded,
ArchiveOutlined,
CheckCircleOutline, CheckCircleOutline,
FilterAltOutlined,
FolderOutlined, FolderOutlined,
HistoryRounded, HistoryRounded,
InboxOutlined, InboxOutlined,
@@ -8,6 +10,8 @@ import {
PersonOutline, PersonOutline,
SearchRounded, SearchRounded,
SettingsOutlined, SettingsOutlined,
TollOutlined,
WidgetsOutlined,
} from '@mui/icons-material' } from '@mui/icons-material'
import { import {
Box, Box,
@@ -85,30 +89,77 @@ const buildQuickActions = t => [
keywords: 'new filter view saved search add create', keywords: 'new filter view saved search add create',
route: '/filters?create=1', route: '/filters?create=1',
}, },
{ // Every destination in the nav drawer is reachable from here, so the palette
id: 'action:tasks', // is a complete way to move around the app without opening the drawer.
...[
{
id: 'action:tasks',
title: t('search.actions.viewAllTasks'),
keywords: 'tasks chores list all open',
route: '/chores',
icon: <InboxOutlined />,
},
{
id: 'action:archived',
title: t('search.actions.viewArchivedTasks'),
keywords: 'archive archived tasks completed open',
route: '/archived',
icon: <ArchiveOutlined />,
},
{
id: 'action:things',
title: t('search.actions.viewThings'),
keywords: 'things devices sensors trackers state open',
route: '/things',
icon: <WidgetsOutlined />,
},
{
id: 'action:labels',
title: t('search.actions.viewLabels'),
keywords: 'labels tags open',
route: '/labels',
icon: <LabelOutlined />,
},
{
id: 'action:projects',
title: t('search.actions.viewProjects'),
keywords: 'projects folders groups open',
route: '/projects',
icon: <FolderOutlined />,
},
{
id: 'action:filters',
title: t('search.actions.viewFilters'),
keywords: 'filters saved views open',
route: '/filters',
icon: <FilterAltOutlined />,
},
{
id: 'action:activities',
title: t('search.actions.viewActivities'),
keywords: 'activities history timeline log open',
route: '/activities',
icon: <HistoryRounded />,
},
{
id: 'action:points',
title: t('search.actions.viewPoints'),
keywords: 'points rewards score leaderboard open',
route: '/points',
icon: <TollOutlined />,
},
{
id: 'action:settings',
title: t('search.actions.openSettings'),
keywords: 'settings preferences configuration open',
route: '/settings',
icon: <SettingsOutlined />,
},
].map(action => ({
...action,
provider: 'actions', provider: 'actions',
title: t('search.actions.viewAllTasks'),
subtitle: t('search.actions.navigation'), subtitle: t('search.actions.navigation'),
keywords: 'tasks chores list open', })),
route: '/chores',
},
{
id: 'action:archived',
provider: 'actions',
title: t('search.actions.viewArchivedTasks'),
subtitle: t('search.actions.navigation'),
keywords: 'archive archived tasks open',
route: '/archived',
},
{
id: 'action:settings',
provider: 'actions',
title: t('search.actions.openSettings'),
subtitle: t('search.actions.navigation'),
keywords: 'settings preferences configuration open',
route: '/settings',
},
] ]
const readRecents = () => { const readRecents = () => {
@@ -464,7 +515,7 @@ const GlobalSearchPalette = ({
<ListItemDecorator <ListItemDecorator
sx={{ mt: 0.25, color: result.color || 'text.secondary' }} sx={{ mt: 0.25, color: result.color || 'text.secondary' }}
> >
{ICONS[result.provider]} {result.icon || ICONS[result.provider]}
</ListItemDecorator> </ListItemDecorator>
<ListItemContent> <ListItemContent>
<Typography <Typography

View File

@@ -752,9 +752,6 @@ const ArchivedTasks = () => {
padding: 1, padding: 1,
}} }}
onChange={handleSearchChange} onChange={handleSearchChange}
startDecorator={
showKeyboardShortcuts ? <KeyboardShortcutHint shortcut='F' /> : null
}
endDecorator={ endDecorator={
searchTerm && ( searchTerm && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>

View File

@@ -1543,18 +1543,22 @@ const MyChores = () => {
onClick={() => { onClick={() => {
Navigate(`/chores/create`) Navigate(`/chores/create`)
}} }}
title='Create new chore (Cmd+C)' title='Create new chore'
> >
<Add /> <Add />
<KeyboardShortcutHint <KeyboardShortcutHint
sx={{ sx={{
position: 'absolute', position: 'absolute',
top: -8, top: -12,
right: -8, // Anchored left so the wider "⌘ + Shift + J" label grows to the
// right instead of off the left edge of the viewport.
left: 2,
whiteSpace: 'nowrap',
zIndex: 1000, zIndex: 1000,
}} }}
show={showKeyboardShortcuts} show={showKeyboardShortcuts}
shortcut='J' shortcut='J'
withShift
/> />
</IconButton> </IconButton>
<IconButton <IconButton
@@ -1583,7 +1587,7 @@ const MyChores = () => {
<KeyboardShortcutHint <KeyboardShortcutHint
sx={{ position: 'relative', left: -40, top: 30 }} sx={{ position: 'relative', left: -40, top: 30 }}
show={showKeyboardShortcuts} show={showKeyboardShortcuts}
shortcut='K' shortcut='J'
/> />
</Box> </Box>
<NotificationAccessSnackbar /> <NotificationAccessSnackbar />

View File

@@ -43,7 +43,7 @@ const SearchBar = ({
startDecorator={ startDecorator={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
<SearchRounded sx={{ fontSize: 18, color: 'text.secondary' }} /> <SearchRounded sx={{ fontSize: 18, color: 'text.secondary' }} />
<KeyboardShortcutHint shortcut='F' show={showKeyboardShortcuts} /> <KeyboardShortcutHint shortcut='K' show={showKeyboardShortcuts} />
</Box> </Box>
} }
endDecorator={ endDecorator={

View File

@@ -23,17 +23,16 @@ export const useKeyboardShortcuts = ({
const isHoldingCmdOrCtrl = event.ctrlKey || event.metaKey const isHoldingCmdOrCtrl = event.ctrlKey || event.metaKey
if (isHoldingCmdOrCtrl && event.key === 'k') { // Cmd/Ctrl + J opens the quick-add modal, + Shift opens the full create
// page. Cmd/Ctrl + K is reserved for global search and handled by
// GlobalSearchContext.
if (isHoldingCmdOrCtrl && event.key.toLowerCase() === 'j') {
event.preventDefault() event.preventDefault()
handlers.onOpenTaskModal() if (event.shiftKey) {
return handlers.onNavigateToCreate()
} } else {
handlers.onOpenTaskModal()
if (addTaskModalOpen) return }
if (isHoldingCmdOrCtrl && event.key === 'j') {
event.preventDefault()
handlers.onNavigateToCreate()
return return
} else if (isHoldingCmdOrCtrl && event.key === 'x') { } else if (isHoldingCmdOrCtrl && event.key === 'x') {
event.preventDefault() event.preventDefault()