diff --git a/eslint.config.mjs b/eslint.config.mjs
index fa04862ad..45d4cb650 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -22,6 +22,14 @@ export default defineConfig(
"unicorn/prefer-module": "off",
"@typescript-eslint/no-floating-promises": "off",
},
+ settings: {
+ tailwindcss: {
+ callees: ["cn"],
+ whitelist: [
+ "center",
+ ],
+ },
+ },
},
{
files: GLOB_TS_SRC,
diff --git a/postcss.config.cjs b/postcss.config.cjs
index 08a01d4d1..fe5e4a5c7 100644
--- a/postcss.config.cjs
+++ b/postcss.config.cjs
@@ -1,5 +1,6 @@
module.exports = {
plugins: {
- tailwindcss: {},
+ "tailwindcss": {},
+ "tailwindcss/nesting": {},
},
}
diff --git a/src/main/tipc.ts b/src/main/tipc.ts
index 956cbb114..9278618cb 100644
--- a/src/main/tipc.ts
+++ b/src/main/tipc.ts
@@ -106,6 +106,9 @@ export const router = {
handlers.invalidateQuery.send(input)
}),
+ /**
+ * @deprecated
+ */
previewImage: t.procedure
.input<{
realUrl: string
diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index 4f18c5fa3..dc8af5633 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -3,6 +3,7 @@ import { useEffect } from "react"
import { Outlet } from "react-router-dom"
import { useDark } from "./hooks/useDark"
+import { RootProviders } from "./providers/root-providers"
import { handlers } from "./tipc"
function App() {
@@ -25,8 +26,9 @@ function App() {
aria-hidden
/>
)}
-
-
+
+
+
>
)
}
diff --git a/src/renderer/src/components/discover/form.tsx b/src/renderer/src/components/discover/form.tsx
index 57e7c9bcb..095acd813 100644
--- a/src/renderer/src/components/discover/form.tsx
+++ b/src/renderer/src/components/discover/form.tsx
@@ -17,6 +17,8 @@ import {
import { Image } from "@renderer/components/ui/image"
import { Input } from "@renderer/components/ui/input"
import { apiClient } from "@renderer/lib/api-fetch"
+import { FeedViewType } from "@renderer/lib/enum"
+import { useFeedStore } from "@renderer/store"
import { DEEPLINK_SCHEME } from "@shared/constants"
import { openElectronWindow } from "@shared/electron"
import { useMutation } from "@tanstack/react-query"
@@ -183,20 +185,28 @@ export function DiscoverForm({ type }: { type: string }) {
) : (
diff --git a/src/renderer/src/components/entry-column/index.tsx b/src/renderer/src/components/entry-column/index.tsx
index 223cd45ea..5d55c1b03 100644
--- a/src/renderer/src/components/entry-column/index.tsx
+++ b/src/renderer/src/components/entry-column/index.tsx
@@ -1,4 +1,4 @@
-import { Button, HeaderActionButton } from "@renderer/components/ui/button"
+import { ActionButton, Button } from "@renderer/components/ui/button"
import {
Popover,
PopoverClose,
@@ -29,6 +29,7 @@ import { Virtuoso, VirtuosoGrid } from "react-virtuoso"
import { useEventCallback } from "usehooks-ts"
import { useShallow } from "zustand/react/shallow"
+import { EmptyIcon } from "../icons/empty"
import { ArticleItem } from "./article-item"
import { AudioItem } from "./audio-item"
import { EntryItemWrapper } from "./item-wrapper"
@@ -149,17 +150,21 @@ export function EntryColumn() {
return (
setActiveEntry?.(null)}
+ onClick={() => setActiveEntry(null)}
>
- {activeList?.view && views[activeList.view].gridMode ? (
-
- ) : (
-
- )}
+ {virtuosoOptions.totalCount === 0 ? (
+
+ ) : activeList?.view && views[activeList.view].gridMode ?
+ (
+
+ ) :
+ (
+
+ )}
)
}
@@ -208,7 +213,7 @@ const ListHeader: FC = () => {
-
setUnreadOnly(!unreadOnly)}
>
@@ -217,12 +222,12 @@ const ListHeader: FC = () => {
) : (
)}
-
+
- {}} tooltip="Mark All as Read">
+ {}} tooltip="Mark All as Read">
-
+
Mark all as read?
@@ -274,15 +279,20 @@ const EmptyList = (props, ref) => {
initial={{ opacity: 0.01, y: 100 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0.01, y: -100 }}
- className="-mt-10 flex h-full flex-col items-center justify-center gap-2 text-zinc-400"
+ className="-mt-20 flex h-full flex-col items-center justify-center gap-2 text-zinc-400"
{...props}
ref={ref}
>
- {unreadOnly && (
+ {unreadOnly ? (
<>
-
+
Zero Unread
>
+ ) : (
+
+
+ Zero Items
+
)}
)
diff --git a/src/renderer/src/components/entry-column/picture-item.tsx b/src/renderer/src/components/entry-column/picture-item.tsx
index b60876b4c..e92da5bce 100644
--- a/src/renderer/src/components/entry-column/picture-item.tsx
+++ b/src/renderer/src/components/entry-column/picture-item.tsx
@@ -1,28 +1,42 @@
import { FeedIcon } from "@renderer/components/feed-icon"
-import { SwipeImages } from "@renderer/components/swipe-images"
+import { SwipeImages } from "@renderer/components/ui/image/swipe-images"
import { ReactVirtuosoItemPlaceholder } from "@renderer/components/ui/placeholder"
import dayjs from "@renderer/lib/dayjs"
import { cn } from "@renderer/lib/utils"
import { useEntry } from "@renderer/store/entry"
+import { usePreviewImages } from "../ui/image/hooks"
import type { UniversalItemProps } from "./types"
export function PictureItem({ entryId, entryPreview }: UniversalItemProps) {
const entry = useEntry(entryId) || entryPreview
+ const previewImage = usePreviewImages()
if (!entry) return
return (
-
)
}
diff --git a/src/renderer/src/components/entry-column/social-media-item.tsx b/src/renderer/src/components/entry-column/social-media-item.tsx
index 556617a9d..77228d31c 100644
--- a/src/renderer/src/components/entry-column/social-media-item.tsx
+++ b/src/renderer/src/components/entry-column/social-media-item.tsx
@@ -39,6 +39,7 @@ export function SocialMediaItem({ entryId, entryPreview }: UniversalItemProps) {
{entry.entries.images?.map((image) => (
diff --git a/src/renderer/src/components/entry-content/share.tsx b/src/renderer/src/components/entry-content/share.tsx
index 7ec64dfe7..357388013 100644
--- a/src/renderer/src/components/entry-content/share.tsx
+++ b/src/renderer/src/components/entry-content/share.tsx
@@ -1,4 +1,4 @@
-import { HeaderActionButton } from "@renderer/components/ui/button"
+import { ActionButton } from "@renderer/components/ui/button"
import {
TooltipProvider,
} from "@renderer/components/ui/tooltip"
@@ -26,7 +26,7 @@ export function EntryShare({
{items
.filter((item) => !item.disabled)
.map((item) => (
-
setActiveList({
level: levels.view,
id: active,
name: views[active].name,
view: active,
- preventNavigate: true,
})}
>
{views.map((item, index) => (
-
-
-
-
- {item.name}
-
+
{
+ setActive(index)
+ setActiveList?.({
+ level: "view",
+ id: index,
+ name: views[index].name,
+ view: index,
+ })
+ e.stopPropagation()
+ }}
+ >
+ {item.icon}
+
))}
@@ -154,6 +142,6 @@ export function FeedColumn() {
))}
-
+
)
}
diff --git a/src/renderer/src/components/icons/empty.tsx b/src/renderer/src/components/icons/empty.tsx
new file mode 100644
index 000000000..8bfe0be4e
--- /dev/null
+++ b/src/renderer/src/components/icons/empty.tsx
@@ -0,0 +1,34 @@
+/* eslint-disable @stylistic/no-tabs */
+import { cn } from "@renderer/lib/utils"
+
+export const EmptyIcon: Component = ({ className }) => (
+
+)
diff --git a/src/renderer/src/components/swipe-images.tsx b/src/renderer/src/components/swipe-images.tsx
deleted file mode 100644
index 93a44dc0d..000000000
--- a/src/renderer/src/components/swipe-images.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import "swiper/css"
-import "swiper/css/navigation"
-import "swiper/css/scrollbar"
-
-import { Image } from "@renderer/components/ui/image"
-import { cn } from "@renderer/lib/utils"
-import { Mousewheel, Navigation, Scrollbar } from "swiper/modules"
-import { Swiper, SwiperSlide } from "swiper/react"
-
-export function SwipeImages({
- images,
- uniqueKey,
- className,
- imgClassName,
-}: {
- images?: string[] | null
- uniqueKey?: string
- className?: string
- imgClassName?: string
-}) {
- return (
-
- {(images?.length || 0) > 1 ?
- (
- <>
-
- {images?.slice(0, 5).map((image) => (
-
-
-
- ))}
-
-
e.stopPropagation()}
- >
-
-
-
e.stopPropagation()}
- >
-
-
- >
- ) :
- images?.length === 1 ?
- (
-
- ) :
- (
-
- )}
-
- )
-}
diff --git a/src/renderer/src/components/ui/background/index.ts b/src/renderer/src/components/ui/background/index.ts
new file mode 100644
index 000000000..11474f523
--- /dev/null
+++ b/src/renderer/src/components/ui/background/index.ts
@@ -0,0 +1 @@
+export * from "./vibrancy"
diff --git a/src/renderer/src/components/ui/background/vibrancy.tsx b/src/renderer/src/components/ui/background/vibrancy.tsx
new file mode 100644
index 000000000..2ae19980a
--- /dev/null
+++ b/src/renderer/src/components/ui/background/vibrancy.tsx
@@ -0,0 +1,25 @@
+import { useDark } from "@renderer/hooks/useDark"
+import { cn } from "@renderer/lib/utils"
+import { useMediaQuery } from "usehooks-ts"
+
+export const Vibrancy: Component<
+ React.DetailedHTMLProps, HTMLDivElement>
+> = ({ className, children, ...rest }) => {
+ const canVibrancy =
+ window.electron && window.electron.process.platform === "darwin"
+
+ const systemDark = useMediaQuery("(prefers-color-scheme: dark)")
+ const { isDark } = useDark()
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/renderer/src/components/ui/button/index.tsx b/src/renderer/src/components/ui/button/index.tsx
index 6ffbe7546..4b10d5ca2 100644
--- a/src/renderer/src/components/ui/button/index.tsx
+++ b/src/renderer/src/components/ui/button/index.tsx
@@ -1,4 +1,5 @@
import { Slot } from "@radix-ui/react-slot"
+import { stopPropagation } from "@renderer/lib/dom"
import { cn } from "@renderer/lib/utils"
import type { VariantProps } from "class-variance-authority"
import type { HTMLMotionProps } from "framer-motion"
@@ -42,24 +43,26 @@ Button.displayName = "Button"
// BIZ buttons
-interface HeaderActionButtonProps {
- onClick?: () => void
+interface ActionButtonProps {
+ onClick?: React.MouseEventHandler
icon?: React.ReactNode | React.FC
tooltip: string
tooltipSide?: "top" | "bottom"
active?: boolean
}
-export const HeaderActionButton = React.forwardRef<
+export const ActionButton = React.forwardRef<
HTMLButtonElement,
- ComponentType
+ ComponentType
>(
(
{ icon, onClick, tooltip, className, tooltipSide, children, active },
ref,
) => (
-
+