Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { headers } from "next/headers";
import Link from "next/link";
import { redirect } from "next/navigation";
import { ProductDescription } from "@/components/partials/product-description";
import { Button } from "@/components/ui/button";
import { auth } from "@/lib/auth";

Expand All @@ -14,15 +15,18 @@ export default async function Home() {
}

return (
<div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<h1 className="text-4xl font-bold animate-pulse">📡 rundown</h1>
<Button asChild>
<Link href="/sign-in" className="text-sm">
Sign in
</Link>
</Button>
</main>
</div>
<main className="mx-auto p-4 pt-24 flex flex-col gap-[32px] row-start-2 items-center my-auto max-w-4xl">
<h1 className="text-4xl font-bold animate-pulse">📡 </h1>
<h1 className="text-4xl font-bold ">rundown</h1>
<Button asChild>
<Link href="/sign-in" className="text-sm">
Sign in
</Link>
</Button>
<p className="text-lg font-black">
AI-powered RSS reader that helps you read less and learn more
</p>
<ProductDescription />
</main>
);
}
51 changes: 37 additions & 14 deletions components/partials/app-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Home, Rss, Settings2 } from "lucide-react";
import { Heart, Home, Rss, Settings2 } from "lucide-react";
import Link from "next/link";
import {
Sidebar,
Expand All @@ -10,23 +10,45 @@ import {
SidebarMenuItem,
} from "@/components/ui/sidebar";
import { getUserId } from "@/lib/auth";
import { cn } from "@/lib/utils";
import { ListUserFeed } from "@/server/queries/list-user-feed";
import { Button } from "../ui/button";
import { Separator } from "../ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
import { SidebarFoldButton } from "./sidebar-fold-button";
import { SignOutButton } from "./sign-out-button";

export async function AppSidebar() {
return (
<Sidebar>
<SidebarHeader className="flex flex-row">
<SidebarFoldButton />
<Button asChild className="flex-1" size={"sm"}>
<Link href="/timeline">
<Home />
Timeline
</Link>
</Button>
<SidebarHeader className="flex flex-col">
{process.env.NEXT_PUBLIC_SPONSOR && (
<>
<Button
asChild
className="border-[#C96198] text-[#C96198] hover:bg-[#C96198]/40 hover:text-foreground"
>
<a
href="https://github.com/sponsors/howyi"
target="_blank"
rel="noopener noreferrer"
>
<Heart />
Support this project
</a>
</Button>
<Separator />
</>
)}
<div className="flex-1 flex flex-row gap-2">
<SidebarFoldButton />
<Button asChild className="flex-1" size={"sm"}>
<Link href="/timeline">
<Home />
Timeline
</Link>
</Button>
</div>
</SidebarHeader>
<SidebarContent>
<FeedItems />
Expand Down Expand Up @@ -63,10 +85,6 @@ async function FeedItems() {

const feeds = await ListUserFeed({ userId });

if (feeds.length === 0) {
return <div>No feeds available</div>;
}

return (
<SidebarMenu>
{feeds.map((feed) => (
Expand All @@ -76,7 +94,12 @@ async function FeedItems() {
</SidebarMenuButton>
</SidebarMenuItem>
))}
<div className="p-2 text-xs text-muted-foreground mx-auto hover:underline">
<div
className={cn(
"p-2 text-xs text-muted-foreground mx-auto hover:underline",
feeds.length === 0 && "animate-pulse text-foreground font-bold",
)}
>
<Link href="/feeds">+ Add RSS URL</Link>
</div>
</SidebarMenu>
Expand Down
2 changes: 1 addition & 1 deletion components/partials/auth-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function AuthForm() {
setIsLoading(true);
},
onSuccess: () => {
router.push("/");
router.push("/settings/summarize");
},
onError: (ctx) => {
toast.error(ctx.error.message);
Expand Down
51 changes: 51 additions & 0 deletions components/partials/product-description.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { RenderedMarkdown } from "../shared/rendered-markdown";

const DESCRIPTION = `
rundown crawls your subscribed RSS feeds every 15 minutes, detects new or updated articles, and generates AI-powered summaries.
You can customize the summary language and length, and receive notifications via Discord Webhook.

[GitHub](https://github.com/howyi/rundown)

---

## Key Features

### RSS Feed Registration & Management

* Register and manage multiple RSS feeds in one place.
* Simple UI for adding, editing, and deleting feeds.

![RSS Feed Registration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fd4crqjtcgnflzv6o8o.png)

### Update Detection + AI Summarization

* Checks feeds every 15 minutes for new articles.
* Summarizes using **gpt-5-nano** with multi-language and adjustable length options.

![AI Summarization](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ogghc2n78ife7k0pdon.gif)

### Timeline View

* Browse summarized articles in chronological order.
* Easily access past articles.

![Timeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ra9g6ldpiq3dd7d6wtbq.png)

### Discord Webhook Notifications

* Get instant updates in your Discord channels.
* Ideal for teams and communities.

![Discord Webhook Notifications](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1aae1g7ytkh5556mtoz9.png)

### MCP Integration

* Access feed and article data programmatically via the MCP server.
* Connect to \`rundown.sbox.studio/mcp\` using an API key generated from the settings page.

![MCP Integration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17450f6wwhtndbzfjs6s.png)
`;

export function ProductDescription() {
return <RenderedMarkdown>{DESCRIPTION}</RenderedMarkdown>;
}
67 changes: 22 additions & 45 deletions components/partials/summertize-setting-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
DialogTrigger,
} from "@/components/ui/dialog";
import { SummaryLengthOptions } from "@/lib/const";
import type { Article } from "@/lib/types";
import type { ArticleWithFeed } from "@/lib/types";
import {
PreviewSummarizeAction,
SaveSummarySettingAction,
Expand All @@ -35,7 +35,7 @@ export function SummarizeSettingForm({
language: string;
length: string;
customInstructions: string;
articles: Article[];
articles: ArticleWithFeed[];
}) {
const [saved, setSaved] = useState({
language: initialLanguage,
Expand Down Expand Up @@ -156,40 +156,36 @@ export function SummarizeSettingForm({
<DialogTrigger asChild>
<Button>Select Preview Article</Button>
</DialogTrigger>
<DialogContent className="overflow-cl">
<DialogContent className="overflow-hidden">
<DialogHeader>
<DialogTitle>Select Preview Article</DialogTitle>
<DialogDescription>
Select an article from{" "}
<a
className="font-medium underline"
href="https://thisweekinreact.com/newsletter"
target="_blank"
rel="noopener noreferrer"
>
the week in react
</a>{" "}
RSS
</DialogDescription>
<DialogDescription>Select an example article</DialogDescription>
</DialogHeader>
<ScrollArea className="max-h-96 px-4">
<ScrollBar orientation="horizontal" />
<ScrollBar orientation="vertical" />
<div className="flex flex-col gap-2">
{articles.map((article) => (
<div
className="justify-start text-left whitespace-pre-wrap border-2 border-l-4 p-2"
className="flex flex-col gap-1 text-left whitespace-pre-wrap border-2 border-l-4 p-2"
key={article.id}
>
<Button
onClick={() => handleArticleSelect(article.id)}
className="w-full text-left"
>
Select Article
</Button>
{article.title}
<br />
<p className="text-xs text-muted-foreground line-clamp-1 break-all whitespace-pre-wrap">
{article.feed.title}
</p>
<div className="flex flex-row gap-2">
<Button
onClick={() => handleArticleSelect(article.id)}
className="text-left"
>
Select
</Button>
<p className="flex-1 line-clamp-2 text-wrap whitespace-pre-wrap">
{article.title}
</p>
</div>
<a
className="text-xs text-muted-foreground underline"
className="text-xs text-muted-foreground underline line-clamp-1 break-all whitespace-pre-wrap"
href={article.url}
target="_blank"
rel="noopener noreferrer"
Expand All @@ -202,27 +198,8 @@ export function SummarizeSettingForm({
</ScrollArea>
</DialogContent>
</Dialog>
{/* <Select value={previewArticleId} onValueChange={setPreviewArticleId}>
<SelectTrigger className="w-full">
<SelectValue
placeholder="Select an article"
className="truncate"
/>
</SelectTrigger>
<SelectContent className="w-full max-w-200">
{articles.map((article) => (
<SelectItem
key={article.id}
value={article.id}
onClick={() => setPreviewArticleId(article.id)}
>
{article.title}
</SelectItem>
))}
</SelectContent>
</Select> */}
</div>
<div className="whitespace-pre-wrap font-light text-sm">
<div className="font-light text-sm line-clamp-1 break-all whitespace-pre-wrap">
Preview Article:{" "}
<a
className="font-medium underline"
Expand Down
22 changes: 22 additions & 0 deletions components/shared/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ export function SettingsHeader() {
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<a
href="https://github.com/howyi/rundown"
className="flex flex-row"
target="_blank"
rel="noopener noreferrer"
>
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="my-auto text-none"
>
<title>GitHub</title>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>
Source
</a>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</header>
Expand Down
Loading