diff --git a/icons/mgc/search_2_cute_re.svg b/icons/mgc/search_2_cute_re.svg
new file mode 100644
index 000000000..7c5e90731
--- /dev/null
+++ b/icons/mgc/search_2_cute_re.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/src/atoms/app.ts b/src/renderer/src/atoms/app.ts
index 57bab10d0..97894dca9 100644
--- a/src/renderer/src/atoms/app.ts
+++ b/src/renderer/src/atoms/app.ts
@@ -1,6 +1,10 @@
import { createAtomHooks } from "@renderer/lib/jotai"
import { atom } from "jotai"
-export const [, ,useAppIsReady, , , setAppIsReady] = createAtomHooks(
+export const [, , useAppIsReady, , , setAppIsReady] = createAtomHooks(
+ atom(false),
+)
+
+export const [, , useAppSearchOpen, , , setAppSearchOpen] = createAtomHooks(
atom(false),
)
diff --git a/src/renderer/src/modules/feed-column/index.tsx b/src/renderer/src/modules/feed-column/index.tsx
index e0c3db9bb..48c29f66a 100644
--- a/src/renderer/src/modules/feed-column/index.tsx
+++ b/src/renderer/src/modules/feed-column/index.tsx
@@ -1,4 +1,6 @@
+import { setAppSearchOpen } from "@renderer/atoms/app"
import { getReadonlyRoute } from "@renderer/atoms/route"
+import { useGeneralSettingKey } from "@renderer/atoms/settings/general"
import { useSidebarActiveView } from "@renderer/atoms/sidebar"
import { Logo } from "@renderer/components/icons/logo"
import { ActionButton } from "@renderer/components/ui/button"
@@ -141,9 +143,11 @@ export function FeedColumn({ children }: PropsWithChildren) {
)}
+
+
@@ -196,7 +200,6 @@ export function FeedColumn({ children }: PropsWithChildren) {
{children}
-
)
}
@@ -230,3 +233,17 @@ const SwipeWrapper: Component<{
)
}
+
+const SearchActionButton = () => {
+ const canSearch = useGeneralSettingKey("dataPersist")
+ if (!canSearch) return null
+ return (
+ setAppSearchOpen(true)}
+ >
+
+
+ )
+}
diff --git a/src/renderer/src/modules/search/cmdk.module.css b/src/renderer/src/modules/search/cmdk.module.css
new file mode 100644
index 000000000..517d1e935
--- /dev/null
+++ b/src/renderer/src/modules/search/cmdk.module.css
@@ -0,0 +1,12 @@
+.status-bar {
+ @apply scale-y-75 z-10 relative h-px w-full shrink-0 transform;
+
+ &.loading::before {
+ @apply scale-y-75 z-10 h-px absolute bottom-0 w-full left-0 top-0 transform;
+ @apply bg-repeat;
+
+ content: "";
+ background: linear-gradient(90deg, transparent, #bbb, transparent);
+ animation: move 2s steps(60) infinite;
+ }
+}
diff --git a/src/renderer/src/modules/search/cmdk.tsx b/src/renderer/src/modules/search/cmdk.tsx
index 199d6b187..98e6130f4 100644
--- a/src/renderer/src/modules/search/cmdk.tsx
+++ b/src/renderer/src/modules/search/cmdk.tsx
@@ -1,3 +1,4 @@
+import { setAppSearchOpen, useAppSearchOpen } from "@renderer/atoms/app"
import { EmptyIcon } from "@renderer/components/icons/empty"
import { Logo } from "@renderer/components/icons/logo"
import { SiteIcon } from "@renderer/components/site-icon"
@@ -22,16 +23,15 @@ import { Command } from "cmdk"
import type { FC } from "react"
import * as React from "react"
import { memo, useMemo } from "react"
-import { useHotkeys } from "react-hotkeys-hook"
+
+import styles from "./cmdk.module.css"
const SearchCmdKContext = React.createContext | null>(
null,
)
export const SearchCmdK: React.FC = () => {
- const [open, setOpen] = React.useState(false)
- useHotkeys("meta+k,ctrl+k", () => {
- setOpen((o) => !o)
- })
+ const open = useAppSearchOpen()
+
const searchInstance = useMemo(() => searchActions.createLocalDbSearch(), [])
const entries = useSearchStore((s) => s.entries)
@@ -51,7 +51,7 @@ export const SearchCmdK: React.FC = () => {
React.useCallback((e) => {
const $input = inputRef.current
if (e.key === "Escape") {
- setOpen(false)
+ setAppSearchOpen(false)
return
}
@@ -61,6 +61,20 @@ export const SearchCmdK: React.FC = () => {
$input?.focus()
}
}, [])
+ const [isPending, startTransition] = React.useTransition()
+ const handleSearch = React.useCallback(
+ async (value: string) => {
+ const { search } = await searchInstance
+ startTransition(() => {
+ search(value)
+ const $scrollView = scrollViewRef.current
+ if ($scrollView) {
+ $scrollView.scrollTop = 0
+ }
+ })
+ },
+ [searchInstance],
+ )
return (
{
shouldFilter={false}
open={open}
onKeyDown={handleKeyDownToFocusInput}
- onOpenChange={setOpen}
+ onOpenChange={setAppSearchOpen}
className={cn(
"h-[600px] max-h-[80vh] w-[800px] max-w-[100vw] rounded-none md:h-screen md:max-h-[60vh] md:max-w-[80vw]",
"flex min-h-[50vh] flex-col bg-zinc-50/85 shadow-2xl backdrop-blur-md dark:bg-neutral-900/80 md:rounded-xl",
@@ -79,15 +93,11 @@ export const SearchCmdK: React.FC = () => {
{
- const { search } = await searchInstance
- search(value)
- const $scrollView = scrollViewRef.current
- if ($scrollView) {
- $scrollView.scrollTop = 0
- }
- }}
+ placeholder={searchActions.getCurrentKeyword() || "Search..."}
+ onValueChange={handleSearch}
+ />
+
{
id={feed.item.id!}
index={entries.length + index}
icon={feed.item.siteUrl}
- subtitle={useFeedUnreadStore.getState().data[feed.item.id!]?.toString()}
+ subtitle={useFeedUnreadStore
+ .getState()
+ .data[feed.item.id!]?.toString()}
/>
))}
@@ -221,7 +233,7 @@ const SearchOptions = () => {
const searchInstance = React.useContext(SearchCmdKContext)
const hasKeyword = useSearchStore((s) => !!s.keyword)
return (
-