fix(spotlight): add media mode to Modes list and remove from search results

Media request search now appears in the Modes section (with no keyboard
shortcut) instead of being mixed into search results when typing. Changed
keyPath from 'character' to 'modeKey' to support modes without a trigger
character. Removed the mediaRequestSearchEntry from home search results
since it is now accessible via the Modes list.
This commit is contained in:
Thomas Camlong committed 2026-05-30 00:56:24 +02:00
1 parent 5a697f911a
commit 8a0c438a7d
2 files changed
+16 -52

No files matched your search

@@ -19,6 +19,7 @@ import type { SearchMode } from "../../lib/mode";
import { appIntegrationBoardMode } from "../app-integration-board";
import { commandMode } from "../command";
import { externalMode } from "../external";
import { mediaMode } from "../media";
import { pageMode } from "../page";
import { userGroupMode } from "../user-group";
@@ -33,7 +34,7 @@ type QuickLinkOption = {
export const useHomeEmptyGroups = () => {
const { data: session } = useSession();
const tPages = useScopedI18n("search.mode.page.group.page.option");
const visibleSearchModes: SearchMode[] = [appIntegrationBoardMode, externalMode, commandMode, pageMode];
const visibleSearchModes: SearchMode[] = [appIntegrationBoardMode, externalMode, mediaMode, commandMode, pageMode];
if (session?.user.permissions.includes("admin")) {
visibleSearchModes.unshift(userGroupMode);
@@ -87,7 +88,7 @@ export const useHomeEmptyGroups = () => {
useInteraction: interaction.link(({ path }) => ({ href: path })),
}),
createGroup({
keyPath: "character",
keyPath: "modeKey",
title: (t) => t("search.mode.help.group.mode.title"),
options: visibleSearchModes.map(({ character, modeKey }) => ({ character, modeKey })),
Component: ({ modeKey, character }) => {
@@ -96,7 +97,7 @@ export const useHomeEmptyGroups = () => {
return (
<Group px="md" py="xs" w="100%" wrap="nowrap" align="center" justify="space-between">
<Text>{t("help")}</Text>
<Kbd size="sm">{character}</Kbd>
{character && <Kbd size="sm">{character}</Kbd>}
</Group>
);
},
@@ -1,5 +1,5 @@
import { Box, Group, Stack, Text } from "@mantine/core";
import { IconMovie, IconSearch, IconSearchOff } from "@tabler/icons-react";
import { IconSearch, IconSearchOff } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
@@ -64,10 +64,6 @@ export const homeSearchEngineGroup = createGroup<GroupItem>({
clientApi.searchEngine.getDefaultSearchEngine.useQuery(undefined, {
enabled: status !== "loading",
});
const { data: mediaRequestSearchTargets, ...mediaRequestSearchTargetsQuery } =
clientApi.integration.mediaRequestSearchTargets.useQuery(undefined, {
enabled: status !== "loading" && session?.user !== undefined,
});
const fromIntegrationEnabled = defaultSearchEngine?.type === "fromIntegration" && query.length > 0;
const { data: results, ...resultQuery } = clientApi.integration.searchInIntegration.useQuery(
{
@@ -83,14 +79,12 @@ export const homeSearchEngineGroup = createGroup<GroupItem>({
return {
isLoading:
defaultSearchEngineQuery.isLoading ||
mediaRequestSearchTargetsQuery.isLoading ||
(resultQuery.isLoading && fromIntegrationEnabled) ||
status === "loading",
isError:
defaultSearchEngineQuery.isError ||
mediaRequestSearchTargetsQuery.isError ||
(resultQuery.isError && fromIntegrationEnabled),
data: createDefaultSearchEntries(defaultSearchEngine, results, mediaRequestSearchTargets, session, query, t),
data: createDefaultSearchEntries(defaultSearchEngine, results, session, query, t),
};
},
});
@@ -98,40 +92,14 @@ export const homeSearchEngineGroup = createGroup<GroupItem>({
const createDefaultSearchEntries = (
defaultSearchEngine: RouterOutputs["searchEngine"]["getDefaultSearchEngine"] | null,
results: RouterOutputs["integration"]["searchInIntegration"] | undefined,
mediaRequestSearchTargets: RouterOutputs["integration"]["mediaRequestSearchTargets"] | undefined,
session: Session | null,
query: string,
t: TranslationFunction,
): GroupItem[] => {
const mediaRequestSearchEntry: GroupItem = {
id: "media-request-search",
name: t("search.mode.media.action.search.label"),
description:
mediaRequestSearchTargets && mediaRequestSearchTargets.length === 0
? t("search.mode.media.action.search.disabled.noIntegration")
: t("search.mode.media.action.search.description"),
icon: IconMovie,
disabled: !mediaRequestSearchTargets || mediaRequestSearchTargets.length === 0,
useInteraction(query) {
if (!mediaRequestSearchTargets || mediaRequestSearchTargets.length === 0) {
return {
type: "none",
};
}
return {
type: "mode",
mode: "media",
query,
};
},
};
if (!session?.user && !defaultSearchEngine) return [mediaRequestSearchEntry];
if (!session?.user && !defaultSearchEngine) return [];
if (!defaultSearchEngine) {
return [
mediaRequestSearchEntry,
{
id: "no-default",
name: t("search.mode.home.group.search.option.no-default.label"),
@@ -150,7 +118,6 @@ const createDefaultSearchEntries = (
if (defaultSearchEngine.type === "generic") {
return [
mediaRequestSearchEntry,
{
id: "search",
name: t("search.mode.home.group.search.option.search.label", {
@@ -173,7 +140,6 @@ const createDefaultSearchEntries = (
if (!results) {
return [
mediaRequestSearchEntry,
{
id: "from-integration",
name: defaultSearchEngine.name,
@@ -188,16 +154,13 @@ const createDefaultSearchEntries = (
];
}
return [
mediaRequestSearchEntry,
...results.map((result) => ({
id: `search-${result.id}`,
name: result.name,
description: result.text,
icon: result.image ?? IconSearch,
useInteraction() {
return useFromIntegrationSearchInteraction(defaultSearchEngine, result);
},
})),
];
return results.map((result) => ({
id: `search-${result.id}`,
name: result.name,
description: result.text,
icon: result.image ?? IconSearch,
useInteraction() {
return useFromIntegrationSearchInteraction(defaultSearchEngine, result);
},
}));
};