Compare commits
2 Commits
f9fcae6c7c
...
86bb1d349a
Author | SHA1 | Date | |
---|---|---|---|
86bb1d349a | |||
ea50f739b3 |
@ -11,6 +11,8 @@ export interface RelatedAccount {
|
||||
}
|
||||
|
||||
export const SITE_NAME = "Goatshed";
|
||||
export const SITE_DESCRIPTION = "山羊寒舍,在这里最终智羊工作室的最新动态。";
|
||||
export const SITE_URL = "https://smartsheep.studio"
|
||||
|
||||
export const RELATED_ACCOUNTS: RelatedAccount[] = [
|
||||
{ icon: <GitHubIcon />, platform: "GitHub", accountName: "@smartsheep-hq", link: "https://github.com/smartsheep-hq" },
|
||||
|
28
app/feed/route.ts
Normal file
28
app/feed/route.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import RSS from "rss";
|
||||
import { SITE_DESCRIPTION, SITE_NAME, SITE_URL } from "@/app/consts";
|
||||
import { getSortedPosts } from "@/content/posts";
|
||||
|
||||
export async function GET() {
|
||||
const feed = new RSS({
|
||||
title: SITE_NAME,
|
||||
description: SITE_DESCRIPTION,
|
||||
site_url: SITE_URL,
|
||||
feed_url: `${SITE_URL}/feed`,
|
||||
language: "zh-CN"
|
||||
});
|
||||
|
||||
getSortedPosts().forEach((item) => {
|
||||
feed.item({
|
||||
url: `${SITE_URL}/p/${item.id}`,
|
||||
title: item.title,
|
||||
description: item.description ?? "No description yet.",
|
||||
date: item.date,
|
||||
});
|
||||
});
|
||||
|
||||
return new Response(feed.xml(), {
|
||||
headers: {
|
||||
"content-type": "application/xml"
|
||||
}
|
||||
});
|
||||
}
|
@ -3,7 +3,7 @@ import { ReactNode } from "react";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import { AppRouterCacheProvider } from "@mui/material-nextjs/v13-appRouter";
|
||||
import { CssBaseline } from "@mui/material";
|
||||
import { SITE_NAME } from "@/app/consts";
|
||||
import { SITE_DESCRIPTION, SITE_NAME } from "@/app/consts";
|
||||
import { theme } from "@/app/theme";
|
||||
|
||||
import "@fontsource/roboto/300.css";
|
||||
@ -16,15 +16,18 @@ import "./globals.css";
|
||||
import AppShell from "@/components/AppShell";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: SITE_NAME,
|
||||
description: "山羊寒舍,在这里最终智羊工作室的最新动态。"
|
||||
title: {
|
||||
default: SITE_NAME,
|
||||
template: `${SITE_NAME} | %s`
|
||||
},
|
||||
description: SITE_DESCRIPTION,
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: Readonly<{
|
||||
children: ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<html lang="zh-CN">
|
||||
<body>
|
||||
<AppRouterCacheProvider>
|
||||
<CssBaseline />
|
||||
|
16
app/page.tsx
16
app/page.tsx
@ -24,8 +24,8 @@ export default function Home() {
|
||||
alignItems="center"
|
||||
sx={{ height: "calc(100vh - 64px)" }}
|
||||
>
|
||||
<Grid item xs={12} md={6} sx={{ textAlign: { xs: "center", md: "initial" } }}>
|
||||
<Typography variant="h3" component="h1" gutterBottom>你好呀 👋</Typography>
|
||||
<Grid item xs={12} sm={6} sx={{ textAlign: { xs: "center", sm: "initial" } }}>
|
||||
<Typography variant="h1" gutterBottom>你好呀 👋</Typography>
|
||||
<Typography paragraph>
|
||||
欢迎来到 SmartSheep Studio 的官方网站!在这里了解,订阅,跟踪我们的最新消息。
|
||||
接触我们最大的官方社区,并且尝试最新产品,参与各种活动,提供反馈,让我们更好的服务您。
|
||||
@ -35,8 +35,8 @@ export default function Home() {
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
md={6}
|
||||
sx={{ display: "flex", justifyContent: { xs: "center", lg: "end" }, order: { xs: -100, lg: 0 } }}
|
||||
sm={6}
|
||||
sx={{ display: "flex", justifyContent: { xs: "center", sm: "end" }, order: { xs: -100, sm: 0 } }}
|
||||
>
|
||||
<Box>
|
||||
<Image src="/smartsheep.svg" alt="Logo" width={256} height={256} />
|
||||
@ -50,8 +50,8 @@ export default function Home() {
|
||||
alignItems="center"
|
||||
sx={{ height: "calc(100vh - 64px)" }}
|
||||
>
|
||||
<Grid item xs={12} md={6} sx={{ display: "flex", justifyContent: { xs: "center", lg: "end" } }}>
|
||||
<Card sx={{ flexGrow: 1, mr: { xs: 0, md: 8 } }}>
|
||||
<Grid item xs={12} sm={6} sx={{ display: "flex", justifyContent: { xs: "center", sm: "end" } }}>
|
||||
<Card sx={{ flexGrow: 1, mr: { xs: 0, sm: 4, md: 8 } }}>
|
||||
<List sx={{ width: "100%", bgcolor: "background.paper" }}>
|
||||
{RELATED_ACCOUNTS.map((item, idx) => (
|
||||
<Link key={idx} href={item.link} target="_blank" passHref>
|
||||
@ -66,8 +66,8 @@ export default function Home() {
|
||||
</List>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} sx={{ textAlign: { xs: "center", md: "initial" } }}>
|
||||
<Typography variant="h3" component="h1" gutterBottom>关于我们</Typography>
|
||||
<Grid item xs={12} sm={6} sx={{ textAlign: { xs: "center", sm: "initial" } }}>
|
||||
<Typography variant="h1" gutterBottom>关于我们</Typography>
|
||||
<Typography paragraph>
|
||||
我们是一群充满活力、对开源充满热情的开发者。成立于 2019 年。自那年起我们一直在开发让人喜欢的开源软件。
|
||||
在我们这里,“取之于开源,用之于开源” 不仅是原则,更是我们信仰的座右铭。
|
||||
|
@ -22,7 +22,7 @@ export default function PostDetailPage({ params }: { params: { id: string } }) {
|
||||
|
||||
<CardContent sx={{ paddingX: 5, paddingY: 3 }}>
|
||||
<Box>
|
||||
<Typography gutterBottom variant="h5" component="h1">
|
||||
<Typography gutterBottom variant="h2">
|
||||
{post.title}
|
||||
</Typography>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
@ -30,7 +30,7 @@ export default function PostDetailPage({ params }: { params: { id: string } }) {
|
||||
</Typography>
|
||||
</Box>
|
||||
<Divider sx={{ my: 5 }} />
|
||||
<Box component="article" sx={{ minWidth: 0 }}>
|
||||
<Box component="article" className="prose max-w-none" sx={{ minWidth: 0 }}>
|
||||
<PostContent content={post.content ?? ""} />
|
||||
</Box>
|
||||
</CardContent>
|
||||
|
@ -5,7 +5,7 @@ export default function PostLayout({children}: Readonly<{
|
||||
children: ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<Container sx={{ display: "flex", justifyContent: "center", gap: 4, py: 4 }}>
|
||||
<Container sx={{ display: "flex", justifyContent: "center", gap: 4, py: 2 }}>
|
||||
<Box sx={{ flexGrow: 1, maxWidth: 720 }}>
|
||||
{children}
|
||||
</Box>
|
||||
|
@ -22,7 +22,7 @@ export default function PostList() {
|
||||
}
|
||||
|
||||
<CardContent sx={{ paddingX: 5, paddingY: 3 }}>
|
||||
<Typography gutterBottom variant="h5" component="h2">
|
||||
<Typography gutterBottom variant="h3">
|
||||
{post.title}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
@ -30,7 +30,7 @@ export default function PostList() {
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions sx={{ paddingX: 4, paddingBottom: 2 }}>
|
||||
<Link href={`/posts/${post.id}`} passHref>
|
||||
<Link href={`/p/${post.id}`} passHref>
|
||||
<Button>Read more</Button>
|
||||
</Link>
|
||||
</CardActions>
|
||||
|
29
app/sitemap.ts
Normal file
29
app/sitemap.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { MetadataRoute } from "next";
|
||||
import { getSortedPosts, Post } from "@/content/posts";
|
||||
import { SITE_URL } from "@/app/consts";
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const posts = getSortedPosts();
|
||||
|
||||
return [
|
||||
{
|
||||
url: `${SITE_URL}/`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 1
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/posts`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8
|
||||
},
|
||||
|
||||
...posts.map((item: Post) => ({
|
||||
url: `${SITE_URL}/posts/${item.id}`,
|
||||
lastModified: item.date,
|
||||
changeFrequency: "daily" as any,
|
||||
priority: 0.75
|
||||
}))
|
||||
];
|
||||
}
|
12
app/theme.ts
12
app/theme.ts
@ -1,4 +1,4 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import { createTheme } from "@mui/material/styles";
|
||||
|
||||
@ -10,5 +10,13 @@ export const theme = createTheme({
|
||||
secondary: {
|
||||
main: "#d43630"
|
||||
}
|
||||
}
|
||||
},
|
||||
typography: {
|
||||
h1: { fontSize: "2.5rem" },
|
||||
h2: { fontSize: "2rem" },
|
||||
h3: { fontSize: "1.75rem" },
|
||||
h4: { fontSize: "1.5rem" },
|
||||
h5: { fontSize: "1.25rem" },
|
||||
h6: { fontSize: "1.15rem" },
|
||||
},
|
||||
});
|
||||
|
@ -109,7 +109,7 @@ export default function AppShell({ children }: {
|
||||
<Image src="/smartsheep.svg" alt="Logo" width={32} height={32} />
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1, fontSize: "1.2rem" }}>
|
||||
<Link href="/">
|
||||
{SITE_NAME}
|
||||
</Link>
|
||||
|
@ -18,18 +18,22 @@ import { theme } from "@/app/theme";
|
||||
import { ReactNode } from "react";
|
||||
import HomeIcon from "@mui/icons-material/Home";
|
||||
import ArticleIcon from "@mui/icons-material/Article";
|
||||
import FeedIcon from "@mui/icons-material/RssFeed";
|
||||
import Link from "next/link";
|
||||
|
||||
export interface NavigationItem {
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
link: string;
|
||||
icon?: ReactNode;
|
||||
title?: string;
|
||||
link?: string;
|
||||
divider?: boolean;
|
||||
}
|
||||
|
||||
export const DRAWER_WIDTH = 320;
|
||||
export const NAVIGATION_ITEMS: NavigationItem[] = [
|
||||
{ icon: <HomeIcon />, title: "首页", link: "/" },
|
||||
{ icon: <ArticleIcon />, title: "新闻", link: "/posts" },
|
||||
{ divider: true },
|
||||
{ icon: <FeedIcon />, title: "订阅源", link: "/feed" },
|
||||
];
|
||||
|
||||
export const AppNavigationHeader = styled("div")(({ theme }) => ({
|
||||
@ -57,14 +61,16 @@ export function AppNavigation({ showClose, onClose }: {
|
||||
</AppNavigationHeader>
|
||||
<Divider />
|
||||
<List>
|
||||
{NAVIGATION_ITEMS.map((item, idx) => (
|
||||
<Link key={idx} href={item.link} passHref>
|
||||
<ListItemButton>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.title} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
))}
|
||||
{NAVIGATION_ITEMS.map((item, idx) => {
|
||||
return item.divider ? <Divider /> : (
|
||||
<Link key={idx} href={item.link ?? "/"} passHref>
|
||||
<ListItemButton>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.title} />
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import MuiMarkdown from "mui-markdown";
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
export default function PostContent({ content }: { content: string }) {
|
||||
return <MuiMarkdown>{content}</MuiMarkdown>;
|
||||
return <Markdown>{content}</Markdown>;
|
||||
}
|
@ -5,9 +5,18 @@ const nextConfig = {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "**",
|
||||
},
|
||||
],
|
||||
hostname: "**"
|
||||
}
|
||||
]
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{ source: "/rss", destination: "/feed" },
|
||||
{ source: "/rss.xml", destination: "/feed.xml" },
|
||||
{ source: "/feed.xml", destination: "/feed" },
|
||||
|
||||
{ source: "/p/:id", destination: "/posts/:id" }
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -21,18 +21,18 @@
|
||||
"@next/mdx": "^14.1.0",
|
||||
"@types/mdx": "^2.0.11",
|
||||
"gray-matter": "^4.0.3",
|
||||
"html-react-parser": "^5.1.7",
|
||||
"marked": "^12.0.0",
|
||||
"mui-markdown": "^1.1.13",
|
||||
"next": "14.1.0",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"react-dom": "^18",
|
||||
"react-markdown": "^9.0.1",
|
||||
"rss": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/rss": "^0.0.32",
|
||||
"autoprefixer": "^10.0.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.1.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user