From 3c562c1398d158e73c81183b5084bedaa3a79d8c Mon Sep 17 00:00:00 2001 From: jeffvli Date: Tue, 17 Mar 2026 22:28:07 -0700 Subject: [PATCH] redesign command palette - add collapsible search groups - reduce modal padding - reduce command item padding - use breadcrumbs for pagination - move page breadcrumbs to the bottom --- .../collapsible-command-group.module.css | 42 +++++++++ .../components/collapsible-command-group.tsx | 52 +++++++++++ .../search/components/command-palette.tsx | 92 ++++++++++--------- .../features/search/components/command.css | 2 +- .../components/library-command-item.tsx | 2 +- 5 files changed, 146 insertions(+), 44 deletions(-) create mode 100644 src/renderer/features/search/components/collapsible-command-group.module.css create mode 100644 src/renderer/features/search/components/collapsible-command-group.tsx diff --git a/src/renderer/features/search/components/collapsible-command-group.module.css b/src/renderer/features/search/components/collapsible-command-group.module.css new file mode 100644 index 000000000..73e4122f4 --- /dev/null +++ b/src/renderer/features/search/components/collapsible-command-group.module.css @@ -0,0 +1,42 @@ +.root { + display: flex; + flex-direction: column; + gap: var(--theme-spacing-xs); + + &:not(:last-child) { + margin-bottom: var(--theme-spacing-xs); + } +} + + +.heading { + display: flex; + gap: var(--theme-spacing-xs); + align-items: center; + font-size: var(--theme-font-size-sm); + cursor: pointer; + user-select: none; + opacity: 0.8; +} + +.heading:hover { + opacity: 1; +} + +.heading:focus-visible { + outline: 2px solid var(--theme-colors-primary); + outline-offset: 2px; +} + +.chevron { + flex-shrink: 0; + width: 1rem; + height: 1rem; + opacity: 0.9; +} + +.items { + display: flex; + flex-direction: column; + gap: 0; +} diff --git a/src/renderer/features/search/components/collapsible-command-group.tsx b/src/renderer/features/search/components/collapsible-command-group.tsx new file mode 100644 index 000000000..e74bb4a99 --- /dev/null +++ b/src/renderer/features/search/components/collapsible-command-group.tsx @@ -0,0 +1,52 @@ +import { ReactNode, useCallback, useState } from 'react'; + +import styles from './collapsible-command-group.module.css'; + +import { Icon } from '/@/shared/components/icon/icon'; +import { Paper } from '/@/shared/components/paper/paper'; + +interface CollapsibleCommandGroupProps { + children: ReactNode; + defaultExpanded?: boolean; + heading: string; +} + +export function CollapsibleCommandGroup({ + children, + defaultExpanded = true, + heading, +}: CollapsibleCommandGroupProps) { + const [expanded, setExpanded] = useState(defaultExpanded); + + const toggle = useCallback(() => { + setExpanded((prev) => !prev); + }, []); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + toggle(); + } + }, + [toggle], + ); + + return ( +
+ +
+ + {heading} +
+
+ {expanded &&
{children}
} +
+ ); +} diff --git a/src/renderer/features/search/components/command-palette.tsx b/src/renderer/features/search/components/command-palette.tsx index f7784e67e..a2faaf77d 100644 --- a/src/renderer/features/search/components/command-palette.tsx +++ b/src/renderer/features/search/components/command-palette.tsx @@ -1,9 +1,10 @@ import { useQuery } from '@tanstack/react-query'; -import { Fragment, useCallback, useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { generatePath, useNavigate } from 'react-router'; import { searchQueries } from '/@/renderer/features/search/api/search-api'; +import { CollapsibleCommandGroup } from '/@/renderer/features/search/components/collapsible-command-group'; import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command'; import { CommandItemSelectable } from '/@/renderer/features/search/components/command-item-selectable'; import { GoToCommands } from '/@/renderer/features/search/components/go-to-commands'; @@ -13,8 +14,9 @@ import { ServerCommands } from '/@/renderer/features/search/components/server-co import { AppRoute } from '/@/renderer/router/routes'; import { useCurrentServer } from '/@/renderer/store'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; -import { Box } from '/@/shared/components/box/box'; +import { Breadcrumb } from '/@/shared/components/breadcrumb/breadcrumb'; import { Button } from '/@/shared/components/button/button'; +import { Divider } from '/@/shared/components/divider/divider'; import { Group } from '/@/shared/components/group/group'; import { Icon } from '/@/shared/components/icon/icon'; import { Kbd } from '/@/shared/components/kbd/kbd'; @@ -94,19 +96,10 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => { }} size="lg" styles={{ + body: { padding: '0' }, header: { display: 'none' }, }} > - - {pages.map((page, index) => ( - - {index > 0 && ' > '} - - - ))} - { if (value.includes(search)) return 1; @@ -130,26 +123,32 @@ export const CommandPalette = ({ modalProps }: CommandPaletteProps) => { onChange={(e) => setQuery(e.currentTarget.value)} ref={searchInputRef} rightSection={ - query && ( - { - setQuery(''); - searchInputRef.current?.focus(); - }} - variant="transparent" - > - - + isLoading ? ( + + ) : ( + query && ( + { + setQuery(''); + searchInputRef.current?.focus(); + }} + variant="transparent" + > + + + ) ) } size="sm" value={query} /> - + - No results found. + + {t('common.noResultsFromQuery', { postProcess: 'sentenceCase' })} + {showAlbumGroup && ( - + {data?.albums?.map((album) => ( { )} ))} - + )} {showArtistGroup && ( - + {data?.albumArtists.map((artist) => ( { )} ))} - + )} {showTrackGroup && ( - + {data?.songs.map((song) => ( { )} ))} - + )} {activePage === CommandPalettePages.HOME && ( { )} - - - - {isHome && isLoading && query !== '' && } - - - ESC - - - - + + + }> + {pages.map((page, index) => ( + + ))} + + + + ESC + + + - + ); }; diff --git a/src/renderer/features/search/components/command.css b/src/renderer/features/search/components/command.css index 8a0cd1620..204257a6c 100644 --- a/src/renderer/features/search/components/command.css +++ b/src/renderer/features/search/components/command.css @@ -14,7 +14,7 @@ input[cmdk-input] { [cmdk-group-items] { display: flex; flex-direction: column; - gap: var(--theme-spacing-xs); + gap: 0; } [cmdk-item] { diff --git a/src/renderer/features/search/components/library-command-item.tsx b/src/renderer/features/search/components/library-command-item.tsx index ac1cf84b1..a02dcddd6 100644 --- a/src/renderer/features/search/components/library-command-item.tsx +++ b/src/renderer/features/search/components/library-command-item.tsx @@ -113,7 +113,7 @@ export const LibraryCommandItem = ({
{title} - + {subtitle}