Skip to content
Open
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
6 changes: 6 additions & 0 deletions public/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ span.katex-display {
.astro-code pre span {
color: var(--shiki-dark) !important;
}
:not(pre) > code {
color: hsl(var(--foreground));
}
:not(.astro-code) > code span {
color: var(--shiki-dark, hsl(var(--foreground))) !important;
}
}

/* Scroll bar */
Expand Down
22 changes: 18 additions & 4 deletions src/components/BaseHead.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,26 @@

import { prod } from 'astro-pure/server'
import type { SiteMeta } from 'astro-pure/types'
import { LOCALE_META, type Locale } from '@/i18n/locales'
import { toChinesePath, toEnglishPath } from '@/i18n/routing'
import config from '@/site-config'

type Props = SiteMeta
type Props = SiteMeta & {
locale?: Locale
alternatePath?: string
}

const { articleDate, description, ogImage, title } = Astro.props
const { articleDate, description, ogImage, title, locale = 'zh', alternatePath } = Astro.props

const siteTitle = `${title} ${config.titleDelimiter} ${config.title}`
const canonicalURL = new URL(Astro.url.pathname, Astro.site)
const socialImageURL = new URL(ogImage ? ogImage : '/images/social-card.png', Astro.url).href
const localeMeta = LOCALE_META[locale]
const zhPath = toChinesePath(Astro.url.pathname)
const enPath = alternatePath && locale === 'zh' ? alternatePath : toEnglishPath(Astro.url.pathname)
const alternateZhURL = new URL(locale === 'en' && alternatePath ? alternatePath : zhPath, Astro.site)
const alternateEnURL = new URL(locale === 'en' && alternatePath ? alternatePath : enPath, Astro.site)
const rssPath = locale === 'en' ? '/en/rss.xml' : '/rss.xml'
---

<meta charset='utf-8' />
Expand Down Expand Up @@ -52,7 +63,7 @@ const socialImageURL = new URL(ogImage ? ogImage : '/images/social-card.png', As
<meta content={description} property='og:description' />
<meta content={canonicalURL} property='og:url' />
<meta content={config.title} property='og:site_name' />
<meta content={config.locale.attrs} property='og:locale' />
<meta content={localeMeta.ogLocale} property='og:locale' />
<meta content={socialImageURL} property='og:image' />
<meta content='1200' property='og:image:width' />
<meta content='630' property='og:image:height' />
Expand All @@ -74,13 +85,16 @@ const socialImageURL = new URL(ogImage ? ogImage : '/images/social-card.png', As

{/* Sitemap */}
<link href='/sitemap-index.xml' rel='sitemap' />
<link rel='alternate' hreflang='zh-CN' href={alternateZhURL} />
<link rel='alternate' hreflang='en' href={alternateEnURL} />
<link rel='alternate' hreflang='x-default' href={new URL('/', Astro.site)} />

{/* RSS auto-discovery */}
<link
rel='alternate'
type='application/rss+xml'
title={config.title}
href={`${Astro.site}rss.xml`}
href={new URL(rssPath, Astro.site).href}
/>

<meta content={Astro.generator} name='generator' />
Expand Down
133 changes: 133 additions & 0 deletions src/components/papers/PaperHero.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
import { Image } from 'astro:assets'
import type { InferEntrySchema } from 'astro:content'

import { FormattedDate } from 'astro-pure/user'
import { cn } from 'astro-pure/utils'
import type { Locale } from '@/i18n/locales'

type PaperSchema = InferEntrySchema<'papers'> | InferEntrySchema<'papersEn'>

interface Props {
data: PaperSchema
remarkPluginFrontmatter: Record<string, unknown>
locale?: Locale
tagBasePath?: string
}

const {
data: {
title,
description,
draft,
heroImage,
publishDate,
updatedDate,
status,
tags,
language,
comment: enableComment
},
remarkPluginFrontmatter,
locale = 'zh',
tagBasePath = '/papers'
} = Astro.props

const dateTimeOptions: Intl.DateTimeFormatOptions = {
month: 'short'
}

const statusText =
status === 'reading' ? 'Reading' : status === 'revisit' ? 'Need Revisit' : 'Completed'

const statusClass =
status === 'reading'
? 'border-amber-300/70 bg-amber-100/60 text-amber-700 dark:border-amber-700/60 dark:bg-amber-900/30 dark:text-amber-300'
: status === 'revisit'
? 'border-sky-300/70 bg-sky-100/60 text-sky-700 dark:border-sky-700/60 dark:bg-sky-900/30 dark:text-sky-300'
: 'border-emerald-300/70 bg-emerald-100/60 text-emerald-700 dark:border-emerald-700/60 dark:bg-emerald-900/30 dark:text-emerald-300'
---

{
heroImage && (
<div class='mb-6'>
<Image
src={heroImage.src}
alt={heroImage.alt || title}
inferSize={heroImage.inferSize}
width={heroImage.width}
height={heroImage.height}
class='h-auto w-full max-w-[65ch] rounded-2xl object-contain'
fetchpriority='high'
loading='eager'
/>
</div>
)
}

{draft && <span class='text-red-500'>(Draft)</span>}

<div class='max-lg:mx-auto'>
<div class='flex flex-wrap items-center gap-2 text-xs leading-6 text-muted-foreground'>
<span class='inline-flex items-center gap-1'>
<FormattedDate class='font-sans' date={publishDate} dateTimeOptions={dateTimeOptions} />
{
updatedDate && (
<>
<span>/</span>
<span>
Update
<FormattedDate
class='font-sans'
date={updatedDate}
dateTimeOptions={dateTimeOptions}
/>
</span>
</>
)
}
</span>
<span class='hidden sm:inline'>•</span>
<span>{remarkPluginFrontmatter.minutesRead}</span>
{
language && (
<>
<span class='hidden sm:inline'>•</span>
<span>{language}</span>
</>
)
}
<span class='hidden sm:inline'>•</span>
<span class={cn('rounded-full border px-2 py-0.5 font-medium', statusClass)}>{statusText}</span>
</div>

<h1 class='mt-4 text-2xl font-medium sm:mb-2 sm:mt-6 sm:text-3xl'>{title}</h1>

<blockquote class='mt-3 text-sm italic text-muted-foreground'>
<q>{description}</q>
</blockquote>

{!!tags.length && (
<div class='mt-3 flex flex-wrap gap-2 text-xs'>
{tags.map((tag) => (
<a
aria-label={`View more papers with the tag ${tag}`}
class='rounded-full border bg-muted px-2 py-0.5 text-muted-foreground transition-colors hover:text-primary'
href={`${tagBasePath}/tags/${tag}`}
>
#{tag}
</a>
))}
</div>
)}

{!draft && enableComment && (
<p class='mt-3 text-xs text-muted-foreground'>
{locale === 'en'
? "After reading, feel free to share your thoughts and disagreements in the comments."
: '阅读后欢迎在评论区讨论你对这篇论文的理解和分歧。'}
</p>
)}
</div>

<div class='mt-4 w-1/2 border-t max-lg:mx-auto sm:mt-6 sm:w-1/3'></div>
68 changes: 68 additions & 0 deletions src/components/papers/PaperMeta.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
import type { InferEntrySchema } from 'astro:content'
import { cn } from 'astro-pure/utils'

type PaperSchema = InferEntrySchema<'papers'> | InferEntrySchema<'papersEn'>

interface Props {
data: PaperSchema
class?: string
}

const {
data: { paperLink, pdfLink, codeLink, authors, venue, year },
class: className
} = Astro.props
---

<div class={cn('rounded-xl border bg-muted/20 p-4', className)}>
<h2 class='text-sm font-semibold uppercase tracking-wide text-muted-foreground'>Paper Reference</h2>

<div class='mt-2 flex flex-wrap items-center gap-2 text-sm'>
<a
href={paperLink}
target='_blank'
rel='noopener noreferrer'
class='rounded-full border bg-background px-3 py-1 transition-colors hover:text-primary'
>
Original Link ↗
</a>
{
pdfLink && (
<a
href={pdfLink}
target='_blank'
rel='noopener noreferrer'
class='rounded-full border bg-background px-3 py-1 transition-colors hover:text-primary'
>
PDF ↗
</a>
)
}
{
codeLink && (
<a
href={codeLink}
target='_blank'
rel='noopener noreferrer'
class='rounded-full border bg-background px-3 py-1 transition-colors hover:text-primary'
>
Code ↗
</a>
)
}
</div>

<div class='mt-3 space-y-1 text-sm text-muted-foreground'>
<p>
<span class='font-medium text-foreground'>Authors:</span>
{authors.join(', ')}
</p>
<p>
<span class='font-medium text-foreground'>Venue:</span>
{venue || 'N/A'}
{' · '}
{year}
</p>
</div>
</div>
111 changes: 111 additions & 0 deletions src/components/papers/PaperPreview.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
import type { CollectionEntry } from 'astro:content'
import { FormattedDate } from 'astro-pure/user'
import { cn } from 'astro-pure/utils'

type PaperEntry = CollectionEntry<'papers'> | CollectionEntry<'papersEn'>

interface Props {
paper: PaperEntry
detailed?: boolean
class?: string
basePath?: string
}

const { paper, detailed = false, class: className, basePath = '/papers' } = Astro.props

const {
id,
data: { title, description, publishDate, updatedDate, status, tags, venue, year, paperLink, draft }
} = paper

const postDate = updatedDate ?? publishDate

const statusText =
status === 'reading' ? 'Reading' : status === 'revisit' ? 'Need Revisit' : 'Completed'

const statusClass =
status === 'reading'
? 'border-amber-300/70 bg-amber-100/60 text-amber-700 dark:border-amber-700/60 dark:bg-amber-900/30 dark:text-amber-300'
: status === 'revisit'
? 'border-sky-300/70 bg-sky-100/60 text-sky-700 dark:border-sky-700/60 dark:bg-sky-900/30 dark:text-sky-300'
: 'border-emerald-300/70 bg-emerald-100/60 text-emerald-700 dark:border-emerald-700/60 dark:bg-emerald-900/30 dark:text-emerald-300'
---

<li
class={cn(
'group rounded-2xl border bg-background px-5 py-3 transition-colors duration-200 hover:bg-muted max-sm:px-4',
detailed ? 'sm:py-4' : 'py-2.5',
className
)}
>
<a href={`${basePath}/${id}`} class='block no-underline'>
<div class='flex flex-wrap items-center gap-2 text-xs text-muted-foreground'>
<FormattedDate class='font-sans' date={postDate} />
<span class='hidden sm:inline'>•</span>
<span>{year}</span>
{venue && (
<>
<span class='hidden sm:inline'>•</span>
<span>{venue}</span>
</>
)}
<span class='hidden sm:inline'>•</span>
<span
class={cn(
'rounded-full border px-2 py-0.5 font-medium',
statusClass
)}
>
{statusText}
</span>
</div>

<div class='mt-1.5 flex items-start justify-between gap-3'>
<h3 class={cn('font-medium leading-6 group-hover:text-primary', detailed ? 'text-lg' : 'text-base')}>
{draft && <span class='text-red-500'>(Draft) </span>}
{title}
</h3>
<svg
xmlns='http://www.w3.org/2000/svg'
width='16'
height='16'
viewBox='0 0 24 24'
fill='none'
stroke-width='2.5'
stroke-linecap='round'
stroke-linejoin='round'
class='mt-1 shrink-0 stroke-muted-foreground transition-colors group-hover:stroke-primary'
>
<line x1='5' y1='12' x2='19' y2='12'></line>
<polyline points='12 5 19 12 12 19'></polyline>
</svg>
</div>

<p class={cn('mt-1 line-clamp-2 text-sm text-muted-foreground', detailed && 'line-clamp-3')}>
{description}
</p>
</a>

<div class='mt-2.5 flex flex-wrap items-center gap-2 text-xs'>
<a
href={paperLink}
target='_blank'
rel='noopener noreferrer'
class='rounded-full border px-2 py-0.5 text-muted-foreground transition-colors hover:text-primary'
>
Original Paper ↗
</a>
{
tags.map((tag) => (
<a
href={`${basePath}/tags/${tag}`}
class='rounded-full border bg-muted px-2 py-0.5 text-muted-foreground transition-colors hover:text-primary'
data-astro-prefetch
>
#{tag}
</a>
))
}
</div>
</li>
Loading