Compare commits
5 commits
8b06e2772a
...
9a62f7bd1b
Author | SHA1 | Date | |
---|---|---|---|
9a62f7bd1b | |||
987dff1dce | |||
5e4649d03f | |||
73a5211f4e | |||
a5bccdf490 |
10 changed files with 301 additions and 249 deletions
|
@ -5,7 +5,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>Systems Obscure</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -13,7 +13,7 @@ const renderer = {
|
|||
}
|
||||
|
||||
const highlighter = await createHighlighter({
|
||||
themes: ["github-dark-dimmed"],
|
||||
themes: ["dark-plus"],
|
||||
langs: [
|
||||
"javascript",
|
||||
"typescript",
|
||||
|
@ -47,7 +47,7 @@ const posts = files.map((file) => {
|
|||
(match, lang, code) => {
|
||||
return highlighter.codeToHtml(code.trim(), {
|
||||
lang: lang || "text",
|
||||
theme: "github-dark-dimmed",
|
||||
theme: "dark-plus",
|
||||
transformers: [transformerColorizedBrackets()],
|
||||
})
|
||||
}
|
||||
|
|
|
@ -42,14 +42,15 @@ const Menu = () => {
|
|||
{/* Mobile dropdown - visible only on small screens */}
|
||||
<NavigationMenuItem className="md:hidden">
|
||||
<NavigationMenuTrigger>Menu</NavigationMenuTrigger>
|
||||
|
||||
<NavigationMenuContent>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link href="/docs" className="block px-2 py-1.5">
|
||||
<Link to="/posts/page/1" className="block px-2 py-1.5">
|
||||
Posts
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link href="/docs" className="block px-2 py-1.5">
|
||||
<Link to="/about" className="block px-2 py-1.5">
|
||||
About
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
|
@ -65,7 +66,7 @@ const Header = () => {
|
|||
const { theme, setTheme } = useTheme()
|
||||
return (
|
||||
<header className="w-full h-12 flex items-center justify-center border-b fixed top-0 z-20 bg-background">
|
||||
<div className="w-full px-2 md:px-4 flex items-center justify-between">
|
||||
<div className="w-full px-0 md:px-4 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" asChild>
|
||||
<Link to="/">
|
||||
|
@ -76,16 +77,19 @@ const Header = () => {
|
|||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<Menu />
|
||||
<Toggle
|
||||
pressed={theme === "dark"}
|
||||
onPressedChange={() =>
|
||||
setTheme(theme === "dark" ? "light" : "dark")
|
||||
}
|
||||
>
|
||||
{/*
|
||||
|
||||
<MoonStar />
|
||||
<span className="hidden sm:block">Dark theme</span>
|
||||
*/}
|
||||
<span className="">Dark theme</span>
|
||||
</Toggle>
|
||||
<Menu />
|
||||
|
||||
{/*
|
||||
<div className="hidden md:block">
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// @ts-nocheck
|
||||
import {
|
||||
Card,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Card,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Link } from "react-router"
|
||||
|
@ -11,45 +11,45 @@ import { Link } from "react-router"
|
|||
import { convertDate } from "@/utils/convertDate"
|
||||
|
||||
const PostListing = ({ posts, title, showAllButton }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-5 ">
|
||||
<h2 className="scroll-m-20 text-2xl font-semibold lg:text-2xl border-b pb-3">
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 gap-3">
|
||||
{posts.map((post) => (
|
||||
<Link
|
||||
to={`/posts/${post.slug}`}
|
||||
key={post.slug}
|
||||
className="block no-underline"
|
||||
>
|
||||
<Card
|
||||
key={post.slug}
|
||||
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
|
||||
>
|
||||
<CardHeader className="">
|
||||
<CardTitle className="leading-snug font-semibold ">
|
||||
{post.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm text-muted-foreground">
|
||||
<div className="flex justify-between gap-2">
|
||||
<span className="text-sm">{convertDate(post.date)}</span>
|
||||
</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
{showAllButton && (
|
||||
<Button asChild variant="secondary" className="w-full mt-4">
|
||||
<Link to="/posts/page/1">View all</Link>
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<>
|
||||
<div className="mb-5 ">
|
||||
<h2 className="scroll-m-20 text-2xl font-bold lg:text-2xl border-b pb-3">
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 gap-3">
|
||||
{posts.map((post) => (
|
||||
<Link
|
||||
to={`/posts/${post.slug}`}
|
||||
key={post.slug}
|
||||
className="block no-underline"
|
||||
>
|
||||
<Card
|
||||
key={post.slug}
|
||||
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
|
||||
>
|
||||
<CardHeader className="px-4 md:px-6">
|
||||
<CardTitle className="leading-snug font-semibold ">
|
||||
{post.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm text-muted-foreground">
|
||||
<div className="flex justify-between gap-2">
|
||||
<span className="text-sm">{convertDate(post.date)}</span>
|
||||
</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
{showAllButton && (
|
||||
<Button asChild variant="" className="w-full mt-4">
|
||||
<Link to="/posts/page/1">View all</Link>
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostListing
|
||||
|
|
|
@ -2,89 +2,114 @@ import MainTemplate from "@/templates/MainTemplate"
|
|||
import portrait from "../images/portrait-compressed.jpg"
|
||||
|
||||
const AboutPage = () => {
|
||||
return (
|
||||
<MainTemplate>
|
||||
<div className="mb-5 ">
|
||||
<h2 className="scroll-m-20 text-2xl font-semibold lg:text-2xl border-b pb-3">
|
||||
About
|
||||
</h2>
|
||||
</div>
|
||||
return (
|
||||
<MainTemplate>
|
||||
<div className="mb-5 ">
|
||||
<h2 className="scroll-m-20 text-2xl font-bold lg:text-2xl border-b pb-3">
|
||||
About
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<figure className="w-full flex flex-col items-center mb-6">
|
||||
<img
|
||||
alt="A portrait of the blog author"
|
||||
src={portrait}
|
||||
className="w-0 flex"
|
||||
/>
|
||||
<figcaption className="text-sm text-muted-foreground mt-3 text-center">
|
||||
Pictured with the WITCH computer at the{" "}
|
||||
<a
|
||||
href="https://www.tnmoc.org/"
|
||||
target="_blank"
|
||||
className="text-primary hover:text-primary/80"
|
||||
>
|
||||
National Museum of Computing
|
||||
</a>
|
||||
, Bletchley Park.
|
||||
</figcaption>
|
||||
</figure>
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
Another software engineer with a blog!{" "}
|
||||
</p>
|
||||
<figure className="w-full flex flex-col items-center mb-6">
|
||||
<img
|
||||
alt="A portrait of the blog author"
|
||||
src={portrait}
|
||||
className="w-0 flex"
|
||||
/>
|
||||
<figcaption className="text-sm text-muted-foreground mt-3 text-center">
|
||||
Pictured with the WITCH computer at the{" "}
|
||||
<a
|
||||
href="https://www.tnmoc.org/"
|
||||
target="_blank"
|
||||
className="text-primary hover:text-primary/80"
|
||||
>
|
||||
National Museum of Computing
|
||||
</a>
|
||||
, Bletchley Park.
|
||||
</figcaption>
|
||||
</figure>
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
Husband. Dad. Christian.
|
||||
</p>
|
||||
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
I am a self-taught engineer from London. This blog is a technical
|
||||
scrapbook. I document the details of my technical life so I can have a
|
||||
record of progress when I look back. Doing this publicly motivates me to
|
||||
take care with my writing and to be as clear as possible.{" "}
|
||||
</p>
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
Currently I work for{" "}
|
||||
<a
|
||||
href="https://en.wikipedia.org/wiki/ITV_(TV_network)"
|
||||
target="_blank"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
ITV
|
||||
</a>{" "}
|
||||
{""}
|
||||
as a backend software engineer. Before that, I worked as a full-stack
|
||||
engineer at the{" "}
|
||||
<a
|
||||
href="https://en.wikipedia.org/wiki/BBC"
|
||||
target="_blank"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
BBC
|
||||
</a>{" "}
|
||||
and as a frontend engineer at{" "}
|
||||
<a
|
||||
href="https://www.arria.com/"
|
||||
target="_blank"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
Arria NLG
|
||||
</a>{" "}
|
||||
and in several web developer roles.{" "}
|
||||
</p>
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
No, not really. I am a Londoner living on the south coast of the UK. I
|
||||
am a self-taught software engineer and this blog is my technical
|
||||
scrapbook. I document the details of my technical life so I can have a
|
||||
record of progress when I look back.{" "}
|
||||
</p>
|
||||
|
||||
<h3 className="mt-5 mb-0 scroll-m-20 text-xl font-semibold tracking-tight">
|
||||
Code
|
||||
</h3>
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
I completed a degree in Philosophy at the University of Warwick and hold
|
||||
a Postgraduate Certificate of Education.
|
||||
</p>
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
Currently I work for{" "}
|
||||
<a
|
||||
href="https://en.wikipedia.org/wiki/ITV_(TV_network)"
|
||||
target="_blank"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
ITV
|
||||
</a>{" "}
|
||||
{""}
|
||||
as a backend software engineer. Before that, I worked as a full-stack
|
||||
engineer at the{" "}
|
||||
<a
|
||||
href="https://en.wikipedia.org/wiki/BBC"
|
||||
target="_blank"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
BBC
|
||||
</a>{" "}
|
||||
and as a frontend engineer at{" "}
|
||||
<a
|
||||
href="https://www.arria.com/"
|
||||
target="_blank"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
Arria NLG
|
||||
</a>{" "}
|
||||
and in several web developer roles. Before software I was a
|
||||
teacher.{" "}
|
||||
</p>
|
||||
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
I self-host my own Git forge at{" "}
|
||||
<a
|
||||
href="https://forgejo.systemsobscure.net/thomasabishop"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
forgejo.systemsobscure.net
|
||||
</a>{" "}
|
||||
rather than use Microsoft GitHub. You can view my personal projects
|
||||
there.
|
||||
</p>
|
||||
</MainTemplate>
|
||||
)
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
Some things I like:
|
||||
<ul className="pt-2">
|
||||
<li className="mb-1">🐶 Staffies and bull-breeds</li>
|
||||
<li className="mb-1">🎼 Classical music (Haydn, Mozart, JSB)</li>
|
||||
<li className="mb-1">🛸 Science fiction </li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
Some things I'm interested in:
|
||||
<ul className="pt-2">
|
||||
<li className="mb-1">🧑💻 Self-hosting and digital resiliance</li>
|
||||
<li className="mb-1">🖳 The history of computing and networks</li>
|
||||
<li className="mb-1">🇮🇪 Irish history and culture</li>
|
||||
<li className="mb-1">☸️ Buddhism</li>
|
||||
{/*
|
||||
|
||||
<li className="mb-1">📡 Civil communications infrastructure</li>
|
||||
*/}
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
I self-host my own Git forge at{" "}
|
||||
<a
|
||||
href="https://forgejo.systemsobscure.net/thomasabishop"
|
||||
className="underline decoration-1 hover:text-primary/80 underline-offset-2"
|
||||
>
|
||||
forgejo.systemsobscure.net
|
||||
</a>{" "}
|
||||
rather than use Microsoft GitHub. You can view my personal projects
|
||||
there.
|
||||
</p>
|
||||
</MainTemplate>
|
||||
)
|
||||
}
|
||||
|
||||
export { AboutPage }
|
||||
|
|
|
@ -1,18 +1,41 @@
|
|||
import MainTemplate from "@/templates/MainTemplate"
|
||||
import PostListing from "@/containers/PostListing"
|
||||
import { usePosts } from "@/hooks/usePosts"
|
||||
import { Card, CardHeader, CardContent, CardFooter } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Link } from "react-router"
|
||||
|
||||
const HomePage = () => {
|
||||
const { posts } = usePosts()
|
||||
return (
|
||||
<MainTemplate>
|
||||
<PostListing
|
||||
title="Recent posts"
|
||||
posts={posts.slice(0, 5)}
|
||||
showAllButton
|
||||
/>
|
||||
</MainTemplate>
|
||||
)
|
||||
const { posts } = usePosts()
|
||||
return (
|
||||
<MainTemplate>
|
||||
<Card className="mb-8">
|
||||
<CardHeader>
|
||||
<h1 className="scroll-m-20 text-left text-3xl font-bold text-balance">
|
||||
Another software engineer with a blog 🥱
|
||||
</h1>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="leading-[1.5] [&:not(:first-child)]:mt-6">
|
||||
I'm a self-taught software engineer currently working at ITV,
|
||||
previously at the BBC. This blog is a technical scrapbook and
|
||||
digital garden.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button asChild>
|
||||
<Link to="/about">About</Link>
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<PostListing
|
||||
title="Recent posts"
|
||||
posts={posts.slice(0, 5)}
|
||||
showAllButton
|
||||
/>
|
||||
</MainTemplate>
|
||||
)
|
||||
}
|
||||
|
||||
export { HomePage }
|
||||
|
|
|
@ -4,128 +4,128 @@ import MainTemplate from "@/templates/MainTemplate"
|
|||
import { useParams, useNavigate } from "react-router"
|
||||
import { convertDate } from "@/utils/convertDate"
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import {
|
||||
Card,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Card,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Link } from "react-router"
|
||||
import { usePosts } from "@/hooks/usePosts"
|
||||
|
||||
const PostsPage = () => {
|
||||
const { page } = useParams()
|
||||
const { page } = useParams()
|
||||
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const { posts } = usePosts()
|
||||
const postsPerPage = 10
|
||||
const { posts } = usePosts()
|
||||
const postsPerPage = 10
|
||||
|
||||
const currentPage = Number(page) || 1
|
||||
const totalPages = Math.ceil(posts.length / postsPerPage)
|
||||
const currentPage = Number(page) || 1
|
||||
const totalPages = Math.ceil(posts.length / postsPerPage)
|
||||
|
||||
useEffect(() => {
|
||||
if (currentPage < 1 || currentPage > totalPages) {
|
||||
navigate(`/posts/page/1`, { replace: true })
|
||||
}
|
||||
}, [currentPage, totalPages, navigate])
|
||||
useEffect(() => {
|
||||
if (currentPage < 1 || currentPage > totalPages) {
|
||||
navigate(`/posts/page/1`, { replace: true })
|
||||
}
|
||||
}, [currentPage, totalPages, navigate])
|
||||
|
||||
const currentPosts = useMemo(() => {
|
||||
const startIndex = (currentPage - 1) * postsPerPage
|
||||
const endIndex = startIndex + postsPerPage
|
||||
return posts.slice(startIndex, endIndex)
|
||||
}, [posts, currentPage, postsPerPage])
|
||||
const currentPosts = useMemo(() => {
|
||||
const startIndex = (currentPage - 1) * postsPerPage
|
||||
const endIndex = startIndex + postsPerPage
|
||||
return posts.slice(startIndex, endIndex)
|
||||
}, [posts, currentPage, postsPerPage])
|
||||
|
||||
const hasNextPage = currentPage < totalPages
|
||||
const hasPrevPage = currentPage > 1
|
||||
const hasNextPage = currentPage < totalPages
|
||||
const hasPrevPage = currentPage > 1
|
||||
|
||||
const goToNextPage = () => {
|
||||
if (hasNextPage) {
|
||||
navigate(`/posts/page/${currentPage + 1}`)
|
||||
}
|
||||
}
|
||||
const goToNextPage = () => {
|
||||
if (hasNextPage) {
|
||||
navigate(`/posts/page/${currentPage + 1}`)
|
||||
}
|
||||
}
|
||||
|
||||
const goToPrevPage = () => {
|
||||
if (hasPrevPage) {
|
||||
navigate(`/posts/page/${currentPage - 1}`)
|
||||
}
|
||||
}
|
||||
const goToPrevPage = () => {
|
||||
if (hasPrevPage) {
|
||||
navigate(`/posts/page/${currentPage - 1}`)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MainTemplate>
|
||||
<div className="mb-5 ">
|
||||
<h2 className="scroll-m-20 text-2xl font-semibold lg:text-2xl border-b pb-3">
|
||||
All posts
|
||||
</h2>
|
||||
</div>
|
||||
<div className="min-h-[calc(100vh-200px)] flex flex-col">
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 gap-3 flex-grow">
|
||||
{currentPosts.map((post) => (
|
||||
<Link
|
||||
to={`/posts/${post.slug}`}
|
||||
key={post.slug}
|
||||
className="block no-underline"
|
||||
>
|
||||
<Card
|
||||
key={post.slug}
|
||||
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="leading-snug font-semibold">
|
||||
{post.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm text-muted-foreground">
|
||||
<div className="flex justify-between gap-2">
|
||||
<span className="text-sm">{convertDate(post.date)}</span>
|
||||
<div>
|
||||
{post.tags.map((tag, i) => (
|
||||
<Badge
|
||||
className="ml-2 cursor-pointer"
|
||||
key={i}
|
||||
variant="secondary"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
navigate(`/tags/${tag}`)
|
||||
}}
|
||||
>
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<Pagination className="mt-4">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
className={`select-none ${hasPrevPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
|
||||
onClick={goToPrevPage}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
className={`select-none ${hasNextPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
|
||||
onClick={goToNextPage}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
</MainTemplate>
|
||||
)
|
||||
return (
|
||||
<MainTemplate>
|
||||
<div className="mb-5 ">
|
||||
<h2 className="scroll-m-20 text-2xl font-bold lg:text-2xl border-b pb-3">
|
||||
All posts
|
||||
</h2>
|
||||
</div>
|
||||
<div className="min-h-[calc(100vh-200px)] flex flex-col">
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 gap-3 flex-grow">
|
||||
{currentPosts.map((post) => (
|
||||
<Link
|
||||
to={`/posts/${post.slug}`}
|
||||
key={post.slug}
|
||||
className="block no-underline"
|
||||
>
|
||||
<Card
|
||||
key={post.slug}
|
||||
className="flex flex-col h-full hover:bg-primary/5 py-4 px-0"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="leading-snug font-semibold">
|
||||
{post.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm text-muted-foreground">
|
||||
<div className="flex justify-between gap-2">
|
||||
<span className="text-sm">{convertDate(post.date)}</span>
|
||||
<div className="hidden md:block">
|
||||
{post.tags.map((tag, i) => (
|
||||
<Badge
|
||||
className="ml-2 cursor-pointer"
|
||||
key={i}
|
||||
variant="secondary"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
navigate(`/tags/${tag}`)
|
||||
}}
|
||||
>
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<Pagination className="mt-4">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
className={`select-none ${hasPrevPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
|
||||
onClick={goToPrevPage}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
className={`select-none ${hasNextPage ? "cursor-pointer" : "cursor-not-allowed opacity-50"}`}
|
||||
onClick={goToNextPage}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
</MainTemplate>
|
||||
)
|
||||
}
|
||||
|
||||
export { PostsPage }
|
||||
|
|
|
@ -15,7 +15,7 @@ p code {
|
|||
border-radius: var(--radius);
|
||||
overflow-x: auto;
|
||||
margin: 1.5rem 0;
|
||||
/* line-height: 1.3; */
|
||||
line-height: 1.3;
|
||||
/* counter-reset: line; */
|
||||
font-family: var(--font-monospaced) !important;
|
||||
font-size: 14px !important;
|
||||
|
|
|
@ -17,7 +17,7 @@ const BlogTemplate = () => {
|
|||
) : (
|
||||
<>
|
||||
<div className="mb-5">
|
||||
<h2 className="text-2xl font-semibold lg:text-2xl border-b pb-3">
|
||||
<h2 className="text-2xl font-bold lg:text-2xl border-b pb-3">
|
||||
{post?.title}
|
||||
</h2>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useTheme } from "@/context/ThemeProvider"
|
|||
|
||||
const MainTemplate = (props) => {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
||||
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
|
||||
<MainContent>{props.children}</MainContent>
|
||||
</ThemeProvider>
|
||||
)
|
||||
|
@ -22,7 +22,7 @@ const MainContent = ({ children }) => {
|
|||
<div className={classes}>
|
||||
<Header />
|
||||
<main className="flex-1 w-full px-2 md:px-4 flex justify-center pt-16">
|
||||
<div className="w-full max-w-3xl lg:max-w-3xl xl:max-w-3xl px-4 py-3">
|
||||
<div className="w-full max-w-3xl lg:max-w-3xl xl:max-w-3xl px-2 md:px-4 md:py-3 py-0">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
|
|
Loading…
Add table
Reference in a new issue