refactor: rebase from hard fork into soft fork that follows upstream
This commit is contained in:
parent
021accda4e
commit
8560239425
59 changed files with 2419 additions and 11935 deletions
|
@ -1,70 +0,0 @@
|
|||
---
|
||||
import Link from '@/components/Link.astro'
|
||||
import AvatarComponent from '@/components/ui/avatar'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { SocialLink } from '@/types'
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import SocialIcons from './SocialIcons.astro'
|
||||
|
||||
type Props = {
|
||||
author: CollectionEntry<'authors'>
|
||||
linkDisabled?: boolean
|
||||
}
|
||||
const { author, linkDisabled = false } = Astro.props
|
||||
const {
|
||||
name,
|
||||
avatar,
|
||||
bio,
|
||||
pronouns,
|
||||
github,
|
||||
twitter,
|
||||
linkedin,
|
||||
website,
|
||||
mail,
|
||||
} = author.data
|
||||
|
||||
const socialLinks: SocialLink[] = [
|
||||
website && { href: website, label: 'Website' },
|
||||
github && { href: github, label: 'GitHub' },
|
||||
twitter && { href: twitter, label: 'Twitter' },
|
||||
linkedin && { href: linkedin, label: 'LinkedIn' },
|
||||
mail && { href: `mailto:${mail}`, label: 'Email' },
|
||||
].filter(Boolean) as SocialLink[]
|
||||
---
|
||||
|
||||
<div
|
||||
class="has-[a:hover]:bg-secondary/50 overflow-hidden rounded-xl border p-4 transition-colors duration-300 ease-in-out"
|
||||
>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href={`/authors/${author.id}`}
|
||||
class={cn('block', linkDisabled && 'pointer-events-none')}
|
||||
>
|
||||
<AvatarComponent
|
||||
client:load
|
||||
src={avatar}
|
||||
alt={`Avatar of ${name}`}
|
||||
fallback={name[0]}
|
||||
className={cn(
|
||||
'size-32 rounded-md',
|
||||
!linkDisabled &&
|
||||
'hover:ring-primary transition-shadow duration-300 hover:cursor-pointer hover:ring-2',
|
||||
)}
|
||||
/>
|
||||
</Link>
|
||||
<div class="flex grow flex-col justify-between gap-y-4">
|
||||
<div>
|
||||
<div class="flex flex-wrap items-center gap-x-2">
|
||||
<h3 class="text-lg font-medium">{name}</h3>
|
||||
{
|
||||
pronouns && (
|
||||
<span class="text-muted-foreground text-sm">({pronouns})</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<p class="text-muted-foreground text-sm">{bio}</p>
|
||||
</div>
|
||||
<SocialIcons links={socialLinks} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,10 +1,6 @@
|
|||
---
|
||||
import AvatarComponent from '@/components/ui/avatar'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { parseAuthors } from '@/lib/server-utils'
|
||||
import { formatDate, readingTime } from '@/lib/utils'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { Image } from 'astro:assets'
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
import Link from './Link.astro'
|
||||
|
@ -19,19 +15,15 @@ const { entry } = Astro.props as {
|
|||
|
||||
const formattedDate = formatDate(entry.data.date)
|
||||
const readTime = readingTime(entry.body!)
|
||||
const authors = await parseAuthors(entry.data.authors ?? [])
|
||||
---
|
||||
|
||||
<div
|
||||
class="hover:bg-secondary/50 rounded-xl border p-4 transition-colors duration-300 ease-in-out"
|
||||
class="not-prose hover:bg-secondary/50 rounded-xl border p-4 transition-colors duration-300 ease-in-out"
|
||||
>
|
||||
<Link
|
||||
href={`/${entry.collection}/${entry.id}`}
|
||||
class="flex flex-col gap-4 sm:flex-row"
|
||||
>
|
||||
<Link href={`/blog/${entry.id}`} class="flex flex-col gap-4 sm:flex-row">
|
||||
{
|
||||
entry.data.image && (
|
||||
<div class="max-w-[200px] sm:shrink-0">
|
||||
<div class="max-w-[200px] sm:flex-shrink-0">
|
||||
<Image
|
||||
src={entry.data.image}
|
||||
alt={entry.data.title}
|
||||
|
@ -42,8 +34,8 @@ const authors = await parseAuthors(entry.data.authors ?? [])
|
|||
</div>
|
||||
)
|
||||
}
|
||||
<div class="grow">
|
||||
<h3 class="mb-1 text-lg font-medium">
|
||||
<div class="flex-grow">
|
||||
<h3 class="mb-1 text-lg font-semibold">
|
||||
{entry.data.title}
|
||||
</h3>
|
||||
<p class="text-muted-foreground mb-2 text-sm">
|
||||
|
@ -52,41 +44,10 @@ const authors = await parseAuthors(entry.data.authors ?? [])
|
|||
<div
|
||||
class="text-muted-foreground mb-2 flex flex-wrap items-center gap-x-2 text-xs"
|
||||
>
|
||||
{
|
||||
authors.length > 0 && (
|
||||
<>
|
||||
{authors.map((author) => (
|
||||
<div class="flex items-center gap-x-1.5">
|
||||
<AvatarComponent
|
||||
client:load
|
||||
src={author.avatar}
|
||||
alt={author.name}
|
||||
fallback={author.name[0]}
|
||||
className="size-5 rounded-full"
|
||||
/>
|
||||
<span>{author.name}</span>
|
||||
</div>
|
||||
))}
|
||||
<Separator orientation="vertical" className="h-4!" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
<span>{formattedDate}</span>
|
||||
<Separator orientation="vertical" className="h-4!" />
|
||||
<Separator orientation="vertical" className="h-4" />
|
||||
<span>{readTime}</span>
|
||||
</div>
|
||||
{
|
||||
entry.data.tags && (
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{entry.data.tags.map((tag) => (
|
||||
<Badge variant="secondary" className="flex items-center gap-x-1">
|
||||
<Icon name="lucide:hash" class="size-3" />
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import Container from '@/components/Container.astro'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { SOCIAL_LINKS } from '@/consts'
|
||||
import Link from './Link.astro'
|
||||
import SocialIcons from './SocialIcons.astro'
|
||||
---
|
||||
|
||||
|
@ -13,16 +12,11 @@ import SocialIcons from './SocialIcons.astro'
|
|||
>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<span class="text-muted-foreground text-center text-sm">
|
||||
© {new Date().getFullYear()} All rights reserved.
|
||||
© {new Date().getFullYear()} • z0x
|
||||
</span>
|
||||
<Separator orientation="vertical" className="h-4!" />
|
||||
<p class="text-muted-foreground text-center text-sm">
|
||||
Made with 🤍 by <Link
|
||||
href="https://github.com/jktrn"
|
||||
class="text-foreground"
|
||||
external
|
||||
underline>enscribe</Link
|
||||
>!
|
||||
All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
<SocialIcons links={SOCIAL_LINKS} />
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
import '@fontsource-variable/geist'
|
||||
import '@fontsource-variable/geist-mono'
|
||||
import '../styles/global.css'
|
||||
import '../styles/typography.css'
|
||||
|
||||
|
@ -22,38 +24,48 @@ const { title, description, image = '/static/twitter-card.png' } = Astro.props
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no" />
|
||||
<meta
|
||||
name="format-detection"
|
||||
content="telephone=no,date=no,address=no,email=no,url=no"
|
||||
/>
|
||||
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
|
||||
<title>{title}</title>
|
||||
<meta name="title" content={title} />
|
||||
<meta name="apple-mobile-web-app-title" content={title} />
|
||||
|
||||
<meta name="description" content={description} />
|
||||
<meta name="author" content={SITE.title} />
|
||||
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<meta name="apple-mobile-web-app-title" content="astro-erudite" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
|
||||
<meta name="theme-color" content="#121212" media="(prefers-color-scheme: dark)" />
|
||||
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#121212"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
/>
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#ffffff"
|
||||
media="(prefers-color-scheme: light)"
|
||||
/>
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
<meta property="og:site_name" content={SITE.title} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={new URL(image, Astro.url)} />
|
||||
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={Astro.url} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={new URL(image, Astro.url)} />
|
||||
|
||||
<ClientRouter />
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
---
|
||||
import Container from '@/components/Container.astro'
|
||||
import Link from '@/components/Link.astro'
|
||||
import MobileMenu from '@/components/ui/mobile-menu'
|
||||
import { ModeToggle } from '@/components/ui/mode-toggle'
|
||||
import { NAV_LINKS, SITE } from '@/consts'
|
||||
import { Image } from 'astro:assets'
|
||||
import logo from '../../public/static/logo.svg'
|
||||
import { SITE } from '@/consts'
|
||||
---
|
||||
|
||||
<header
|
||||
|
@ -15,26 +12,12 @@ import logo from '../../public/static/logo.svg'
|
|||
<Container>
|
||||
<div class="flex flex-wrap items-center justify-between gap-4 py-4">
|
||||
<Link
|
||||
href="/"
|
||||
href="https://z0x.ca"
|
||||
class="hover:text-primary flex shrink-0 items-center gap-2 text-xl font-medium transition-colors duration-300"
|
||||
>
|
||||
<Image src={logo} alt="Logo" class="size-8" />
|
||||
{SITE.title}
|
||||
</Link>
|
||||
<div class="flex items-center gap-2 md:gap-4">
|
||||
<nav class="hidden items-center gap-4 text-sm sm:gap-6 md:flex">
|
||||
{
|
||||
NAV_LINKS.map((item) => (
|
||||
<Link
|
||||
href={item.href}
|
||||
class="text-foreground/60 hover:text-foreground/80 capitalize transition-colors"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))
|
||||
}
|
||||
</nav>
|
||||
<MobileMenu client:load transition:persist />
|
||||
<ModeToggle client:load transition:persist />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
---
|
||||
import Link from '@/components/Link.astro'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Image } from 'astro:assets'
|
||||
import type { CollectionEntry } from 'astro:content'
|
||||
|
||||
type Props = {
|
||||
project: CollectionEntry<'projects'>
|
||||
}
|
||||
|
||||
const { project } = Astro.props
|
||||
---
|
||||
|
||||
<div
|
||||
class="hover:bg-secondary/50 rounded-xl border p-4 transition-colors duration-300 ease-in-out"
|
||||
>
|
||||
<Link
|
||||
href={project.data.link}
|
||||
class="flex flex-col gap-4 sm:flex-row"
|
||||
external
|
||||
>
|
||||
{
|
||||
project.data.image && (
|
||||
<div class="max-w-[200px] sm:shrink-0">
|
||||
<Image
|
||||
src={project.data.image}
|
||||
alt={project.data.name}
|
||||
width={1200}
|
||||
height={630}
|
||||
class="object-cover"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div class="grow">
|
||||
<h3 class="mb-1 text-lg font-medium">
|
||||
{project.data.name}
|
||||
</h3>
|
||||
<p class="text-muted-foreground mb-2 text-sm">
|
||||
{project.data.description}
|
||||
</p>
|
||||
{
|
||||
project.data.tags && (
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{project.data.tags.map((tag: string) => (
|
||||
<Badge variant="secondary">{tag}</Badge>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
|
@ -17,6 +17,7 @@ function buildToc(headings: Heading[]): Heading[] {
|
|||
const toc: Heading[] = []
|
||||
const stack: Heading[] = []
|
||||
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
headings.forEach((h) => {
|
||||
const heading = { ...h, subheadings: [] }
|
||||
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { NAV_LINKS } from '@/consts'
|
||||
import { Menu } from 'lucide-react'
|
||||
|
||||
const MobileMenu = () => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleViewTransitionStart = () => {
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
document.addEventListener('astro:before-swap', handleViewTransitionStart)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener(
|
||||
'astro:before-swap',
|
||||
handleViewTransitionStart,
|
||||
)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<DropdownMenu open={isOpen} onOpenChange={setIsOpen} modal={false}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="md:hidden"
|
||||
title="Menu"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
<span className="sr-only">Toggle menu</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="bg-background">
|
||||
{NAV_LINKS.map((item) => (
|
||||
<DropdownMenuItem key={item.href} asChild>
|
||||
<a
|
||||
href={item.href}
|
||||
className="w-full text-lg font-medium capitalize"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
export default MobileMenu
|
Loading…
Add table
Add a link
Reference in a new issue