feat: search in library

This commit is contained in:
5rahim
2026-03-03 15:23:07 +01:00
parent a537c8c82b
commit eb66dafcd8
4 changed files with 46 additions and 2 deletions

View File

@@ -101,7 +101,7 @@ export function EpisodeCard(props: EpisodeCardProps) {
const Meta = () => (
<div data-episode-card-info-container className="relative z-[3] w-full space-y-0">
{topTitle !== title && <p
{(topTitle !== title || showTotalEpisodes) && <p
data-episode-card-title
className={cn(
"w-[80%] line-clamp-1 text-md md:text-lg transition-colors duration-200 text-[--foreground] font-semibold",

View File

@@ -11,6 +11,7 @@ import { HomeSettingsButton } from "@/app/(main)/_features/home/home-settings-bu
import { libraryExplorer_drawerOpenAtom } from "@/app/(main)/_features/library-explorer/library-explorer.atoms"
import { useNakamaStatus } from "@/app/(main)/_features/nakama/nakama-manager"
import { usePlaylistEditorManager } from "@/app/(main)/_features/playlists/lib/playlist-editor-manager"
import { useSeaCommand } from "@/app/(main)/_features/sea-command/sea-command.tsx"
import { useServerStatus } from "@/app/(main)/_hooks/use-server-status"
import { SeaLink } from "@/components/shared/sea-link"
import { Button, IconButton } from "@/components/ui/button"
@@ -24,6 +25,7 @@ import React from "react"
import { BiCollection, BiDotsVerticalRounded, BiFolder } from "react-icons/bi"
import { HiExclamation } from "react-icons/hi"
import { IoHome, IoLibraryOutline, IoLibrarySharp } from "react-icons/io5"
import { LuSearch } from "react-icons/lu"
import { LuFolderSearch, LuFolderSync, LuFolderTree } from "react-icons/lu"
import { MdOutlineConnectWithoutContact, MdOutlineVideoLibrary } from "react-icons/md"
import { TbFileSad, TbReportSearch } from "react-icons/tb"
@@ -73,6 +75,8 @@ export function HomeToolbar(props: HomeToolbarProps) {
const { mutate: openInExplorer } = useOpenInExplorer()
const { setSeaCommandInput, setSeaCommandOpen } = useSeaCommand()
const hasLibraryPath = !!status?.settings?.library?.libraryPath
return (
@@ -197,6 +201,23 @@ export function HomeToolbar(props: HomeToolbarProps) {
{/* <span>Library explorer</span>*/}
{/*</DropdownMenuItem>*/}
<DropdownMenuItem
data-home-toolbar-search-library
disabled={!hasLibraryPath}
className={cn("cursor-pointer", { "!text-[--muted]": !hasLibraryPath })}
onClick={() => {
setSeaCommandOpen(true)
React.startTransition(() => {
setTimeout(() => {
setSeaCommandInput("/library ")
}, 300)
})
}}
>
<LuSearch />
<span>Search in library</span>
</DropdownMenuItem>
<DropdownMenuItem
data-home-toolbar-open-directory-button
disabled={!hasLibraryPath}

View File

@@ -1,5 +1,6 @@
import { useGetAnimeCollection } from "@/api/hooks/anilist.hooks"
import { useGetMangaCollection } from "@/api/hooks/manga.hooks"
import { useLibraryCollection } from "@/app/(main)/_hooks/anime-library-collection-loader.ts"
import { useServerStatus } from "@/app/(main)/_hooks/use-server-status"
import { CommandGroup, CommandItem, CommandShortcut } from "@/components/ui/command"
import { useRouter } from "@/lib/navigation"
@@ -15,6 +16,7 @@ export function SeaCommandUserMediaNavigation() {
const { input, select, command: { isCommand, command, args }, scrollToTop } = useSeaCommandContext()
const { data: animeCollection, isLoading: isAnimeLoading } = useGetAnimeCollection() // should be available instantly
const { data: mangaCollection, isLoading: isMangaLoading } = useGetMangaCollection()
const animeLibraryCollection = useLibraryCollection()
const anime = animeCollection?.MediaListCollection?.lists?.flatMap(n => n?.entries)?.filter(Boolean)?.map(n => n.media)?.filter(Boolean) ?? []
const manga = mangaCollection?.lists?.flatMap(n => n?.entries)?.filter(Boolean)?.map(n => n.media)?.filter(Boolean) ?? []
@@ -24,6 +26,10 @@ export function SeaCommandUserMediaNavigation() {
const query = args.join(" ")
const filteredAnime = (command === "anime" && query.length > 0) ? anime.filter(n => seaCommand_compareMediaTitles(n.title, query)) : []
const filteredManga = (command === "manga" && query.length > 0) ? manga.filter(n => seaCommand_compareMediaTitles(n.title, query)) : []
const filteredAnimeLibrary = (command === "library" && query.length > 0) ? animeLibraryCollection?.lists?.flatMap(l => l.entries)
?.filter(n => seaCommand_compareMediaTitles(n?.media?.title, query))
?.map(n => n?.media)
?.filter(Boolean) ?? [] : []
return (
<>
@@ -63,6 +69,23 @@ export function SeaCommandUserMediaNavigation() {
))}
</CommandGroup>
)}
{command === "library" && filteredAnimeLibrary.length > 0 && (
<CommandGroup heading="Library anime">
{filteredAnimeLibrary.map(n => (
<CommandItem
key={n.id}
onSelect={() => {
select(() => {
router.push(`/entry?id=${n.id}`)
})
}}
>
<CommandItemMedia media={n} type="anime" />
</CommandItem>
))}
</CommandGroup>
)}
{command === "manga" && filteredManga.length > 0 && (
<CommandGroup heading="My manga">
{filteredManga.map(n => (

View File

@@ -181,7 +181,7 @@ export function SeaCommand() {
shouldShow={ctx => (
ctx.command.command === "anime"
|| ctx.command.command === "manga"
// || ctx.command.command === "library"
|| ctx.command.command === "library"
)}
render={() => <SeaCommandUserMediaNavigation />}
/>