From af926b033ecfd39a069ac8d1ad13f29c85b26d38 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Mon, 10 Aug 2026 00:19:57 -0400 Subject: [PATCH] improve keyboard shortcuts handling --- src/search/GlobalSearchPalette.jsx | 93 ++++++++++--------- src/search/searchProviders.js | 46 ++++----- .../Chores/hooks/useKeyboardShortcuts.js | 18 ++-- 3 files changed, 81 insertions(+), 76 deletions(-) diff --git a/src/search/GlobalSearchPalette.jsx b/src/search/GlobalSearchPalette.jsx index d903b0b..ec2b520 100644 --- a/src/search/GlobalSearchPalette.jsx +++ b/src/search/GlobalSearchPalette.jsx @@ -40,7 +40,7 @@ const GROUPS = [ ] const GROUP_LABELS = { tasks: 'Tasks', - history: 'Notes & activity', + history: 'Notes', projects: 'Projects', labels: 'Labels', people: 'People', @@ -164,6 +164,7 @@ const SearchContainer = ({ children, onClose, presentation }) => { - new Fuse(documents, { - threshold: 0.38, - distance: 120, - ignoreLocation: true, - includeScore: true, - keys: [ - { name: 'title', weight: 0.5 }, - { name: 'keywords', weight: 0.25 }, - { name: 'body', weight: 0.17 }, - { name: 'subtitle', weight: 0.08 }, - ], - }), + new Map( + GROUPS.filter(group => group !== 'actions').map(group => [ + group, + new Fuse( + documents.filter(item => item.provider === group), + { + threshold: 0.38, + distance: 120, + ignoreLocation: true, + includeScore: true, + keys: + group === 'history' + ? [{ name: 'body', weight: 1 }] + : [ + { name: 'title', weight: 0.5 }, + { name: 'keywords', weight: 0.25 }, + { name: 'body', weight: 0.17 }, + { name: 'subtitle', weight: 0.08 }, + ], + }, + ), + ]), + ), [documents], ) @@ -217,39 +229,32 @@ const GlobalSearchPalette = ({ const normalized = query.trim().toLocaleLowerCase() if (!normalized) { const currentById = new Map(documents.map(item => [item.id, item])) - const recentResults = recents.map( - item => currentById.get(item.id) || item, - ) + const recentResults = recents + .map(item => currentById.get(item.id) || item) + .filter(item => item.provider !== 'history' || currentById.has(item.id)) return [...recentResults, ...QUICK_ACTIONS] } - const matches = fuse - .search(normalized, { limit: 60 }) - .map(match => { - const title = match.item.title?.toLocaleLowerCase() ?? '' - let score = match.score ?? 1 + const grouped = GROUPS.filter(group => group !== 'actions').flatMap(group => + (searchIndexes.get(group)?.search(normalized, { limit: 7 }) || []) + .map(match => { + const title = match.item.title?.toLocaleLowerCase() ?? '' + let score = match.score ?? 1 - if (title === normalized) { - score -= 1 - } else if (title.startsWith(normalized)) { - score -= 0.15 - } else if (title.includes(normalized)) { - score -= 0.08 - } + if (group !== 'history') { + if (title === normalized) { + score -= 1 + } else if (title.startsWith(normalized)) { + score -= 0.15 + } else if (title.includes(normalized)) { + score -= 0.08 + } + } - return { - ...match.item, - score, - } - }) - .sort((a, b) => a.score - b.score) - - const byGroup = new Map(GROUPS.map(group => [group, []])) - matches.forEach(item => { - const group = byGroup.get(item.provider) - if (group && group.length < 7) group.push(item) - }) - const grouped = GROUPS.flatMap(group => byGroup.get(group)) + return { ...match.item, score } + }) + .sort((a, b) => a.score - b.score), + ) grouped.push({ id: 'action:filter-tasks', provider: 'actions', @@ -258,7 +263,7 @@ const GlobalSearchPalette = ({ route: `/chores?search=${encodeURIComponent(query.trim())}`, }) return grouped - }, [documents, fuse, query, recents]) + }, [documents, query, recents, searchIndexes]) useEffect(() => { selectedResultRef.current?.scrollIntoView({ @@ -383,7 +388,7 @@ const GlobalSearchPalette = ({ setSelectedIndex(index)} + onMouseMove={() => setSelectedIndex(index)} onClick={() => selectResult(result)} sx={{ borderRadius: 'md', diff --git a/src/search/searchProviders.js b/src/search/searchProviders.js index 0eb20f6..7b61a54 100644 --- a/src/search/searchProviders.js +++ b/src/search/searchProviders.js @@ -86,29 +86,33 @@ registerSearchProvider({ registerSearchProvider({ id: 'history', getDocuments: ({ choresById, history, membersById }) => - history.map(entry => { + history.flatMap(entry => { + const note = stripHtml(entry.notes).trim() + if (!note) return [] + const chore = choresById.get(String(entry.choreId)) const member = membersById.get(String(entry.completedBy)) - const note = stripHtml(entry.notes) - return document('history', { - id: `history:${entry.id}`, - entityId: entry.id, - title: chore?.name || entry.choreName || 'Task activity', - subtitle: [ - member?.displayName, - entry.performedAt - ? new Date(entry.performedAt).toLocaleDateString() - : null, - ] - .filter(Boolean) - .join(' · '), - body: note, - keywords: `${HISTORY_STATUS[entry.status] || 'activity'} ${member?.displayName || ''}`, - route: entry.choreId - ? `/chores/${entry.choreId}/history` - : '/activities', - updatedAt: entry.performedAt || entry.updatedAt, - }) + return [ + document('history', { + id: `history:${entry.id}`, + entityId: entry.id, + title: chore?.name || entry.choreName || 'Task note', + subtitle: [ + member?.displayName, + entry.performedAt + ? new Date(entry.performedAt).toLocaleDateString() + : null, + ] + .filter(Boolean) + .join(' · '), + body: note, + keywords: `${HISTORY_STATUS[entry.status] || 'activity'} ${member?.displayName || ''}`, + route: entry.choreId + ? `/chores/${entry.choreId}/history` + : '/activities', + updatedAt: entry.performedAt || entry.updatedAt, + }), + ] }), }) diff --git a/src/views/Chores/hooks/useKeyboardShortcuts.js b/src/views/Chores/hooks/useKeyboardShortcuts.js index 7e4de87..ecb0cd6 100644 --- a/src/views/Chores/hooks/useKeyboardShortcuts.js +++ b/src/views/Chores/hooks/useKeyboardShortcuts.js @@ -1,15 +1,15 @@ -import { useState, useEffect } from 'react' +import { useEffect, useState } from 'react' export const useKeyboardShortcuts = ({ - isMultiSelectMode, - selectedChores, addTaskModalOpen, - searchTerm, - searchFilter, - filteredChores, choreSections, - openChoreSections, + filteredChores, handlers, + isMultiSelectMode, + openChoreSections, + searchFilter, + searchTerm, + selectedChores, }) => { const [showKeyboardShortcuts, setShowKeyboardShortcuts] = useState(false) @@ -35,10 +35,6 @@ export const useKeyboardShortcuts = ({ event.preventDefault() handlers.onNavigateToCreate() return - } else if (isHoldingCmdOrCtrl && event.key === 'f') { - event.preventDefault() - handlers.onFocusSearch() - return } else if (isHoldingCmdOrCtrl && event.key === 'x') { event.preventDefault() if (searchTerm?.length > 0) {