Upload starter template

This commit is contained in:
Timothy Lin
2021-01-09 17:50:45 +08:00
parent e332766d0f
commit 9a6f4efbb8
72 changed files with 11703 additions and 0 deletions

68
pages/_app.js Normal file
View File

@ -0,0 +1,68 @@
import '@/css/tailwind.css'
import { MDXProvider } from '@mdx-js/react'
import { ThemeProvider } from 'next-themes'
import { DefaultSeo } from 'next-seo'
import Head from 'next/head'
import SEO from '@/components/SEO'
import LayoutWrapper from '@/components/LayoutWrapper'
import MDXComponents from '@/components/MDXComponents'
export default function App({ Component, pageProps }) {
return (
<ThemeProvider attribute="class">
<MDXProvider components={MDXComponents}>
<div className="antialiased">
<Head>
<meta content="width=device-width, initial-scale=1" name="viewport" />
</Head>
<DefaultSeo {...SEO} />
<Head>
<link
rel="apple-touch-icon-precomposed"
sizes="57x57"
href="apple-touch-icon-57x57.png"
/>
<link
rel="apple-touch-icon-precomposed"
sizes="114x114"
href="apple-touch-icon-114x114.png"
/>
<link
rel="apple-touch-icon-precomposed"
sizes="72x72"
href="apple-touch-icon-72x72.png"
/>
<link
rel="apple-touch-icon-precomposed"
sizes="144x144"
href="apple-touch-icon-144x144.png"
/>
<link
rel="apple-touch-icon-precomposed"
sizes="120x120"
href="apple-touch-icon-120x120.png"
/>
<link
rel="apple-touch-icon-precomposed"
sizes="152x152"
href="apple-touch-icon-152x152.png"
/>
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
<link rel="manifest" href="/manifest.json" />
<meta name="application-name" content="&nbsp;" />
<meta name="msapplication-TileColor" content="#FFFFFF" />
<meta name="msapplication-TileImage" content="mstile-144x144.png" />
<meta name="theme-color" content="#ffffff" />
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
</Head>
<LayoutWrapper>
<Component {...pageProps} />
</LayoutWrapper>
</div>
</MDXProvider>
</ThemeProvider>
)
}

52
pages/_document.js Normal file
View File

@ -0,0 +1,52 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'
import GoogleFonts from 'next-google-fonts'
class MyDocument extends Document {
render() {
return (
<Html lang="en">
<GoogleFonts href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" />
<Head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css"
integrity="sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X"
crossOrigin="anonymous"
/>
<link href="/static/favicons/favicon.ico" rel="shortcut icon" />
<link href="/static/favicons/site.webmanifest" rel="manifest" />
<link rel="preconnect" href="https://fonts.gstatic.com/" crossOrigin="" />
<link
href="/static/favicons/apple-touch-icon.png"
rel="apple-touch-icon"
sizes="180x180"
/>
<link
href="/static/favicons/favicon-32x32.png"
rel="icon"
sizes="32x32"
type="image/png"
/>
<link
href="/static/favicons/favicon-16x16.png"
rel="icon"
sizes="16x16"
type="image/png"
/>
<link color="#00aba9" href="/static/favicons/safari-pinned-tab.svg" rel="mask-icon" />
<link rel="alternate" type="application/rss+xml" href="/index.xml" />
<meta content="IE=edge" httpEquiv="X-UA-Compatible" />
<meta content="#ffffff" name="theme-color" />
<meta content="#00aba9" name="msapplication-TileColor" />
<meta content="/static/favicons/browserconfig.xml" name="msapplication-config" />
</Head>
<body className="bg-white dark:bg-gray-900 text-black dark:text-white">
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

60
pages/about.js Normal file
View File

@ -0,0 +1,60 @@
import { NextSeo } from 'next-seo'
import siteMetadata from '@/data/siteMetadata'
import SocialIcon from '@/components/social-icons'
export default function About() {
return (
<>
<NextSeo
title={`About - ${siteMetadata.author}`}
canonical={`${siteMetadata.siteUrl}/about`}
openGraph={{
url: `${siteMetadata.siteUrl}/about`,
title: `About - ${siteMetadata.author}`,
}}
/>
<div className="divide-y">
<div className="pt-6 pb-8 space-y-2 md:space-y-5">
<h1 className="text-3xl leading-9 font-extrabold text-gray-900 dark:text-gray-100 tracking-tight sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
About
</h1>
</div>
<div className="space-y-2 xl:grid xl:grid-cols-3 xl:gap-x-8 xl:space-y-0 items-start">
<div className="flex flex-col items-center space-x-2 pt-8">
<img src={siteMetadata.image} alt="avatar" className="w-48 h-48 rounded-full" />
<h3 className="text-2xl leading-8 font-bold tracking-tight pt-4 pb-2">
{siteMetadata.author}
</h3>
<div className="text-gray-500 dark:text-gray-400">Professor of Atmospheric Science</div>
<div className="text-gray-500 dark:text-gray-400">Stanford University</div>
<div className="flex pt-6 space-x-3">
<SocialIcon kind="mail" href={`mailto:${siteMetadata.email}`} />
<SocialIcon kind="github" href={siteMetadata.github} />
<SocialIcon kind="facebook" href={siteMetadata.facebook} />
<SocialIcon kind="youtube" href={siteMetadata.youtube} />
<SocialIcon kind="linkedin" href={siteMetadata.linkedin} />
<SocialIcon kind="twitter" href={siteMetadata.twitter} />
</div>
</div>
<div className="prose dark:prose-dark max-w-none pt-8 pb-8 xl:col-span-2">
<p>
Tails Azimuth is a professor of atmospheric sciences at the Stanford AI Lab. His
research interests includes complexity modelling of tailwinds, headwinds and
crosswinds.
</p>
<p>
He leads the clean energy group which develops 3D air pollution-climate models, writes
differential equation solvers, and manufactures titanium plated air ballons. In his
free time he bakes raspberry pi.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed neque elit, tristique
placerat feugiat ac, facilisis vitae arcu. Proin eget egestas augue. Praesent ut sem
nec arcu pellentesque aliquet. Duis dapibus diam vel metus tempus vulputate.
</p>
</div>
</div>
</div>
</>
)
}

30
pages/blog.js Normal file
View File

@ -0,0 +1,30 @@
import { NextSeo } from 'next-seo'
import { getAllFilesFrontMatter } from '@/lib/mdx'
import siteMetadata from '@/data/siteMetadata'
import ListLayout from '@/layouts/ListLayout'
export async function getStaticProps() {
const posts = await getAllFilesFrontMatter('blog')
return { props: { posts } }
}
export default function Blog({ posts }) {
return (
<>
<NextSeo
title={`Blog - ${siteMetadata.name}`}
description={siteMetadata.description}
canonical={`${siteMetadata.siteUrl}/blog`}
openGraph={{
url: `${siteMetadata.siteUrl}/blog`,
title: `Blog - ${siteMetadata.name}`,
description: siteMetadata.description,
}}
/>
<ListLayout posts={posts} title="All Posts"/>
</>
)
}

50
pages/blog/[slug].js Normal file
View File

@ -0,0 +1,50 @@
import fs from 'fs'
import hydrate from 'next-mdx-remote/hydrate'
import { getFiles, getFileBySlug, getAllFilesFrontMatter } from '@/lib/mdx'
import PostLayout from '@/layouts/PostLayout'
import MDXComponents from '@/components/MDXComponents'
import generateRss from '@/lib/generate-rss'
export async function getStaticPaths() {
const posts = await getFiles('blog')
return {
paths: posts.map((p) => ({
params: {
slug: p.replace(/\.(mdx|md)/, ''),
},
})),
fallback: false,
}
}
export async function getStaticProps({ params }) {
const allPosts = await getAllFilesFrontMatter('blog')
const postIndex = allPosts.findIndex((post) => post.slug === params.slug)
const prev = allPosts[postIndex + 1] || null
const next = allPosts[postIndex - 1] || null
const post = await getFileBySlug('blog', params.slug)
// rss
const rss = generateRss(allPosts)
fs.writeFileSync('./public/index.xml', rss)
return { props: { post, prev, next } }
}
export default function Blog({ post, prev, next }) {
const { mdxSource, frontMatter } = post
const content = hydrate(mdxSource, {
components: MDXComponents,
})
return (
<>
{frontMatter.draft !== true && (
<PostLayout frontMatter={frontMatter} prev={prev} next={next}>
{content}
</PostLayout>
)}
</>
)
}

90
pages/index.js Normal file
View File

@ -0,0 +1,90 @@
import tinytime from 'tinytime'
import Link from 'next/link'
import { getAllFilesFrontMatter } from '@/lib/mdx'
import siteMetadata from '@/data/siteMetadata'
import Tag from '@/components/Tag'
const MAX_DISPLAY = 5
const postDateTemplate = tinytime('{MMMM} {DD}, {YYYY}')
export async function getStaticProps() {
const posts = await getAllFilesFrontMatter('blog')
return { props: { posts } }
}
export default function Home({ posts }) {
return (
<div>
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="pt-6 pb-8 space-y-2 md:space-y-5">
<h1 className="text-3xl leading-9 font-extrabold text-gray-900 dark:text-gray-100 tracking-tight sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
Latest
</h1>
<p className="text-lg leading-7 text-gray-500 dark:text-gray-400">
{siteMetadata.description}
</p>
</div>
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
{!posts.length && 'No posts found.'}
{posts.slice(0, MAX_DISPLAY).map((frontMatter) => {
const { slug, date, title, summary, tags } = frontMatter
return (
<li key={slug} className="py-12">
<article className="space-y-2 xl:grid xl:grid-cols-4 xl:space-y-0 xl:items-baseline">
<dl>
<dt className="sr-only">Published on</dt>
<dd className="text-base leading-6 font-medium text-gray-500 dark:text-gray-400">
<time dateTime={date}>{postDateTemplate.render(new Date(date))}</time>
</dd>
</dl>
<div className="space-y-5 xl:col-span-3">
<div className="space-y-6">
<div>
<h2 className="text-2xl leading-8 font-bold tracking-tight">
<Link href={`/blog/${slug}`}>
<a className="text-gray-900 dark:text-gray-100">{title}</a>
</Link>
</h2>
<div className="space-x-2">
{tags.map((tag) => (
<Tag key={tag} text={tag} />
))}
</div>
</div>
<div className="prose max-w-none text-gray-500 dark:text-gray-400">
{summary}
</div>
</div>
<div className="text-base leading-6 font-medium">
<Link href={`/blog/${slug}`}>
<a
className="text-blue-500 hover:text-blue-600 dark:hover:text-blue-400"
aria-label={`Read "${title}"`}
>
Read more &rarr;
</a>
</Link>
</div>
</div>
</article>
</li>
)
})}
</ul>
</div>
{posts.length > MAX_DISPLAY && (
<div className="flex justify-end text-base leading-6 font-medium">
<Link href="/blog">
<a
className="text-blue-500 hover:text-blue-600 dark:hover:text-blue-400"
aria-label="all posts"
>
All Posts &rarr;
</a>
</Link>
</div>
)}
</div>
)
}

48
pages/tags.js Normal file
View File

@ -0,0 +1,48 @@
import Link from 'next/link'
import kebabCase from 'just-kebab-case'
import { NextSeo } from 'next-seo'
import siteMetadata from '@/data/siteMetadata'
import { getAllTags } from '@/lib/tags'
import Tag from '@/components/Tag'
export async function getStaticProps() {
const tags = await getAllTags('blog')
return { props: { tags } }
}
export default function Tags({ tags }) {
const sortedTags = Object.keys(tags).sort((a, b) => tags[b] - tags[a])
return (
<>
<NextSeo
title={`Tags - ${siteMetadata.title}`}
canonical={`${siteMetadata.siteUrl}/tags`}
openGraph={{
url: `${siteMetadata.siteUrl}/tags`,
title: `Tags - ${siteMetadata.title}`,
}}
/>
<div className="flex items-start justify-start flex-col divide-y divide-gray-200 dark:divide-gray-700 md:justify-center md:items-center md:divide-y-0 md:flex-row md:space-x-6 md:mt-24">
<div className="pt-6 pb-8 space-x-2 md:space-y-5">
<h1 className="text-3xl leading-9 font-extrabold text-gray-900 dark:text-gray-100 tracking-tight sm:text-4xl sm:leading-10 md:text-6xl md:leading-14 md:border-r-2 md:px-6">
Tags
</h1>
</div>
<div className="max-w-lg flex flex-wrap">
{Object.keys(tags).length === 0 && 'No tags found.'}
{sortedTags.map((t) => {
return (
<div key={t} className="m-2">
<Tag text={t} />
<Link href={`/tags/${kebabCase(t)}`}>
<a className="uppercase font-semibold text-sm mx-1 text-gray-600 dark:text-gray-300">{` (${tags[t]})`}</a>
</Link>
</div>
)
})}
</div>
</div>
</>
)
}

47
pages/tags/[tag].js Normal file
View File

@ -0,0 +1,47 @@
import { NextSeo } from 'next-seo'
import { getAllFilesFrontMatter } from '@/lib/mdx'
import { getAllTags } from '@/lib/tags'
import siteMetadata from '@/data/siteMetadata'
import ListLayout from '@/layouts/ListLayout'
import kebabCase from 'just-kebab-case'
export async function getStaticPaths() {
const tags = await getAllTags('blog')
return {
paths: Object.keys(tags).map((tag) => ({
params: {
tag,
},
})),
fallback: false,
}
}
export async function getStaticProps({ params }) {
const allPosts = await getAllFilesFrontMatter('blog')
const filteredPosts = allPosts.filter(
(post) => post.draft !== true && post.tags.map((t) => kebabCase(t)).includes(params.tag)
)
return { props: { posts: filteredPosts, tag: params.tag } }
}
export default function Blog({ posts, tag }) {
// Capitalize first letter and convert space to dash
const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1)
return (
<>
<NextSeo
title={`${tag} - ${siteMetadata.title}`}
description={siteMetadata.description}
canonical={`${siteMetadata.siteUrl}/tags/${tag}`}
openGraph={{
url: `${siteMetadata.siteUrl}/tags/${tag}`,
title: `${tag} - ${siteMetadata.title}`,
}}
/>
<ListLayout posts={posts} title={title} />
</>
)
}