mirror of
https://github.com/ChenQihan666/MyBlog-Next.git
synced 2026-08-14 07:33:07 +08:00
Merge branch 'astro-v2'
This commit is contained in:
@@ -18,3 +18,6 @@ pnpm-debug.log*
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# ignore .astro directory
|
||||
.astro
|
||||
|
||||
+2
-1
@@ -9,4 +9,5 @@
|
||||
!astro.config.mjs
|
||||
!package.json
|
||||
!.prettierrc
|
||||
!.eslintrc.json
|
||||
!.eslintrc.json
|
||||
!README.md
|
||||
@@ -14,6 +14,7 @@ This theme is self-documented \_ which means articles/posts in this theme can al
|
||||
|
||||
## 🔥 Features
|
||||
|
||||
- [x] type-safe markdown
|
||||
- [x] super fast performance
|
||||
- [x] accessible (Keyboard/VoiceOver)
|
||||
- [x] responsive (mobile ~ desktops)
|
||||
@@ -54,8 +55,11 @@ Inside of AstroPaper, you'll see the following folders and files:
|
||||
│ ├── assets/
|
||||
│ │ └── socialIcons.ts
|
||||
│ ├── components/
|
||||
│ ├── contents/
|
||||
│ │ └── some-blog-posts.md
|
||||
│ ├── content/
|
||||
│ │ | blog/
|
||||
│ │ | └── some-blog-posts.md
|
||||
│ │ └── _schemas.ts
|
||||
│ │ └── config.ts
|
||||
│ ├── layouts/
|
||||
│ └── pages/
|
||||
│ └── styles/
|
||||
@@ -69,16 +73,18 @@ Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page
|
||||
|
||||
Any static assets, like images, can be placed in the `public/` directory.
|
||||
|
||||
All blog posts are stored in `src/contents/` directory.
|
||||
All blog posts are stored in `src/content/blog` directory.
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
Documentation can be read in two formats\_ _markdown_ & _blog post_.
|
||||
|
||||
- Configuration - [markdown](src/contents/how-to-configure-astropaper-theme.md) | [blog post](https://astro-paper.pages.dev/posts/how-to-configure-astropaper-theme/)
|
||||
- Add Posts - [markdown](src/contents/adding-new-post.md) | [blog post](https://astro-paper.pages.dev/posts/adding-new-posts-in-astropaper-theme/)
|
||||
- Customize Color Schemes - [markdown](src/contents/customizing-astropaper-theme-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/)
|
||||
- Predefined Color Schemes - [markdown](src/contents/predefined-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/predefined-color-schemes/)
|
||||
- Configuration - [markdown](src/content/blog/how-to-configure-astropaper-theme.md) | [blog post](https://astro-paper.pages.dev/posts/how-to-configure-astropaper-theme/)
|
||||
- Add Posts - [markdown](src/content/blog/adding-new-post.md) | [blog post](https://astro-paper.pages.dev/posts/adding-new-posts-in-astropaper-theme/)
|
||||
- Customize Color Schemes - [markdown](src/content/blog/customizing-astropaper-theme-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/)
|
||||
- Predefined Color Schemes - [markdown](src/content/blog/predefined-color-schemes.md) | [blog post](https://astro-paper.pages.dev/posts/predefined-color-schemes/)
|
||||
|
||||
> For AstroPaper v1, check out [this branch](https://github.com/satnaing/astro-paper/tree/astro-paper-v1) and this [live URL](https://astro-paper-v1.astro-paper.pages.dev/)
|
||||
|
||||
## 💻 Tech Stack
|
||||
|
||||
@@ -121,17 +127,16 @@ PUBLIC_GOOGLE_SITE_VERIFICATION=your-google-site-verification-value
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :--------------------- | :------------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:3000` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run format:check` | Check code format with Prettier |
|
||||
| `npm run format` | Format codes with Prettier |
|
||||
| `npm run cz` | Commit code changes with commitizen |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
|
||||
| `npm run astro --help` | Get help using the Astro CLI |
|
||||
| Command | Action |
|
||||
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:3000` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run format:check` | Check code format with Prettier |
|
||||
| `npm run format` | Format codes with Prettier |
|
||||
| `npm run sync` | Generates TypeScript types for all Astro modules. [Learn more](https://docs.astro.build/en/reference/cli-reference/#astro-sync). |
|
||||
| `npm run cz` | Commit code changes with commitizen |
|
||||
|
||||
## ✨ Feedback & Suggestions
|
||||
|
||||
|
||||
Generated
+1171
-2721
File diff suppressed because it is too large
Load Diff
+8
-6
@@ -7,6 +7,7 @@
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"sync": "astro sync",
|
||||
"astro": "astro",
|
||||
"format:check": "prettier --check .",
|
||||
"format": "prettier --write .",
|
||||
@@ -14,19 +15,19 @@
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/rss": "^1.0.3",
|
||||
"astro": "^1.9.2",
|
||||
"@astrojs/rss": "^2.1.0",
|
||||
"astro": "^2.0.4",
|
||||
"fuse.js": "^6.6.2",
|
||||
"github-slugger": "^2.0.0",
|
||||
"remark-collapse": "^0.1.2",
|
||||
"remark-toc": "^8.0.1",
|
||||
"satori": "^0.1.1",
|
||||
"satori": "^0.1.2",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/react": "^1.2.2",
|
||||
"@astrojs/sitemap": "^1.0.0",
|
||||
"@astrojs/tailwind": "^2.1.3",
|
||||
"@astrojs/react": "^2.0.2",
|
||||
"@astrojs/sitemap": "^1.0.1",
|
||||
"@astrojs/tailwind": "^3.0.1",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/github-slugger": "^1.3.0",
|
||||
"@types/react": "^18.0.27",
|
||||
@@ -35,6 +36,7 @@
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.1.0",
|
||||
"prettier": "^2.8.3",
|
||||
"prettier-plugin-tailwindcss": "^0.2.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SocialIcons } from "src/types";
|
||||
import type { SocialIcons } from "@types";
|
||||
|
||||
const socialIcons: SocialIcons = {
|
||||
Github: `<svg
|
||||
|
||||
@@ -22,7 +22,11 @@ breadcrumbList[0] === "posts" &&
|
||||
breadcrumbList.map((breadcrumb, index) =>
|
||||
index + 1 === breadcrumbList.length ? (
|
||||
<li>
|
||||
<span class={`${index > 0 ? "lowercase" : ""}`} aria-current="page">
|
||||
<span
|
||||
class={`${index > 0 ? "lowercase" : "capitalize"}`}
|
||||
aria-current="page"
|
||||
>
|
||||
{/* make the last part lowercase in Home > Tags > some-tag */}
|
||||
{breadcrumb}
|
||||
</span>
|
||||
</li>
|
||||
@@ -39,14 +43,16 @@ breadcrumbList[0] === "posts" &&
|
||||
|
||||
<style>
|
||||
.breadcrumb {
|
||||
@apply max-w-3xl mx-auto px-4 w-full mt-8 mb-1;
|
||||
@apply mx-auto mt-8 mb-1 w-full max-w-3xl px-4;
|
||||
}
|
||||
.breadcrumb ul li {
|
||||
@apply inline;
|
||||
}
|
||||
.breadcrumb ul li a,
|
||||
.breadcrumb ul li a {
|
||||
@apply capitalize opacity-70;
|
||||
}
|
||||
.breadcrumb ul li span {
|
||||
@apply opacity-70 capitalize;
|
||||
@apply opacity-70;
|
||||
}
|
||||
.breadcrumb ul li:not(:last-child) a {
|
||||
@apply hover:opacity-100;
|
||||
|
||||
+17
-16
@@ -1,31 +1,32 @@
|
||||
import Datetime from "./Datetime";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import type { BlogFrontmatter } from "@content/_schemas";
|
||||
|
||||
export interface Props {
|
||||
href?: string;
|
||||
post: Frontmatter;
|
||||
frontmatter: BlogFrontmatter;
|
||||
secHeading?: boolean;
|
||||
}
|
||||
|
||||
const styles = {
|
||||
cardContainer: "my-6",
|
||||
titleLink:
|
||||
"text-skin-accent font-medium text-lg underline-offset-4 decoration-dashed focus-visible:no-underline focus-visible:underline-offset-0 inline-block",
|
||||
titleHeading: "font-medium text-lg decoration-dashed hover:underline",
|
||||
};
|
||||
|
||||
export default function Card({ href, post, secHeading = true }: Props) {
|
||||
export default function Card({ href, frontmatter, secHeading = true }: Props) {
|
||||
const { title, pubDatetime, description } = frontmatter;
|
||||
return (
|
||||
<li className={styles.cardContainer}>
|
||||
<a href={href} className={styles.titleLink}>
|
||||
<li className="my-6">
|
||||
<a
|
||||
href={href}
|
||||
className="inline-block text-lg font-medium text-skin-accent decoration-dashed underline-offset-4 focus-visible:no-underline focus-visible:underline-offset-0"
|
||||
>
|
||||
{secHeading ? (
|
||||
<h2 className={styles.titleHeading}>{post.title}</h2>
|
||||
<h2 className="text-lg font-medium decoration-dashed hover:underline">
|
||||
{title}
|
||||
</h2>
|
||||
) : (
|
||||
<h3 className={styles.titleHeading}>{post.title}</h3>
|
||||
<h3 className="text-lg font-medium decoration-dashed hover:underline">
|
||||
{title}
|
||||
</h3>
|
||||
)}
|
||||
</a>
|
||||
<Datetime datetime={post.datetime} />
|
||||
<p>{post.description}</p>
|
||||
<Datetime datetime={pubDatetime} />
|
||||
<p>{description}</p>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
export interface Props {
|
||||
datetime: string;
|
||||
datetime: string | Date;
|
||||
size?: "sm" | "lg";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Datetime({ datetime, size = "sm", className }: Props) {
|
||||
return (
|
||||
<div className={`opacity-80 flex items-center space-x-2 ${className}`}>
|
||||
<div className={`flex items-center space-x-2 opacity-80 ${className}`}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`${
|
||||
size === "sm" ? "scale-90" : "scale-100"
|
||||
} w-6 h-6 inline-block fill-skin-base`}
|
||||
} inline-block h-6 w-6 fill-skin-base`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"></path>
|
||||
@@ -25,7 +25,7 @@ export default function Datetime({ datetime, size = "sm", className }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
const FormattedDatetime = ({ datetime }: { datetime: string }) => {
|
||||
const FormattedDatetime = ({ datetime }: { datetime: string | Date }) => {
|
||||
const myDatetime = new Date(datetime);
|
||||
|
||||
const date = myDatetime.toLocaleDateString([], {
|
||||
|
||||
@@ -28,16 +28,16 @@ const { noMarginTop = false } = Astro.props;
|
||||
@apply w-full;
|
||||
}
|
||||
.footer-wrapper {
|
||||
@apply py-6 sm:py-4 flex flex-col sm:flex-row-reverse justify-between items-center;
|
||||
@apply flex flex-col items-center justify-between py-6 sm:flex-row-reverse sm:py-4;
|
||||
}
|
||||
.link-button {
|
||||
@apply p-2 my-1 hover:rotate-6;
|
||||
@apply my-1 p-2 hover:rotate-6;
|
||||
}
|
||||
.link-button svg {
|
||||
@apply scale-125;
|
||||
}
|
||||
.copyright-wrapper {
|
||||
@apply my-2 flex flex-col sm:flex-row items-center whitespace-nowrap;
|
||||
@apply my-2 flex flex-col items-center whitespace-nowrap sm:flex-row;
|
||||
}
|
||||
.separator {
|
||||
@apply hidden sm:inline;
|
||||
|
||||
+15
-15
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { LOGO_IMAGE, SITE } from "src/config";
|
||||
import { LOGO_IMAGE, SITE } from "@config";
|
||||
import Hr from "./Hr.astro";
|
||||
import LinkButton from "./LinkButton.astro";
|
||||
|
||||
@@ -107,35 +107,35 @@ const { activeNav } = Astro.props;
|
||||
|
||||
<style>
|
||||
#skip-to-content {
|
||||
@apply bg-skin-accent text-skin-inverted py-2 px-3 absolute -top-full focus:top-4 left-16 z-50 transition-all;
|
||||
@apply absolute -top-full left-16 z-50 bg-skin-accent py-2 px-3 text-skin-inverted transition-all focus:top-4;
|
||||
}
|
||||
.nav-container {
|
||||
@apply max-w-3xl mx-auto flex flex-col sm:flex-row justify-between items-center;
|
||||
@apply mx-auto flex max-w-3xl flex-col items-center justify-between sm:flex-row;
|
||||
}
|
||||
.top-nav-wrap {
|
||||
@apply relative w-full p-4 sm:py-8 flex justify-between items-start sm:items-center;
|
||||
@apply relative flex w-full items-start justify-between p-4 sm:items-center sm:py-8;
|
||||
}
|
||||
.logo {
|
||||
@apply absolute sm:static font-semibold text-xl sm:text-2xl py-1;
|
||||
@apply absolute py-1 text-xl font-semibold sm:static sm:text-2xl;
|
||||
}
|
||||
.hamburger-menu {
|
||||
@apply self-end p-2 sm:hidden;
|
||||
}
|
||||
.hamburger-menu svg {
|
||||
@apply fill-skin-base w-6 h-6 scale-125;
|
||||
@apply h-6 w-6 scale-125 fill-skin-base;
|
||||
}
|
||||
|
||||
nav {
|
||||
@apply w-full sm:py-0 sm:ml-2 flex flex-col items-center sm:justify-end sm:flex-row sm:space-x-4 bg-skin-fill;
|
||||
@apply flex w-full flex-col items-center bg-skin-fill sm:ml-2 sm:flex-row sm:justify-end sm:space-x-4 sm:py-0;
|
||||
}
|
||||
nav ul {
|
||||
@apply mt-4 w-44 grid grid-rows-4 grid-cols-2 gap-y-2 gap-x-2 sm:gap-y-0 sm:mt-0 sm:ml-0 sm:w-auto sm:gap-x-5;
|
||||
@apply mt-4 grid w-44 grid-cols-2 grid-rows-4 gap-y-2 gap-x-2 sm:mt-0 sm:ml-0 sm:w-auto sm:gap-y-0 sm:gap-x-5;
|
||||
}
|
||||
nav ul li {
|
||||
@apply col-span-2 flex justify-center items-center;
|
||||
@apply col-span-2 flex items-center justify-center;
|
||||
}
|
||||
nav ul li a {
|
||||
@apply px-4 py-3 w-full text-center sm:px-2 sm:py-1 sm:my-0 font-medium hover:text-skin-accent;
|
||||
@apply w-full px-4 py-3 text-center font-medium hover:text-skin-accent sm:my-0 sm:px-2 sm:py-1;
|
||||
}
|
||||
nav ul li:nth-child(4) a {
|
||||
@apply w-auto;
|
||||
@@ -145,7 +145,7 @@ const { activeNav } = Astro.props;
|
||||
@apply col-span-1;
|
||||
}
|
||||
nav a.active {
|
||||
@apply underline underline-offset-4 decoration-wavy decoration-2;
|
||||
@apply underline decoration-wavy decoration-2 underline-offset-4;
|
||||
}
|
||||
nav a.active svg {
|
||||
@apply fill-skin-accent;
|
||||
@@ -155,20 +155,20 @@ const { activeNav } = Astro.props;
|
||||
@apply p-1;
|
||||
}
|
||||
nav button svg {
|
||||
@apply w-6 h-6 fill-skin-base hover:fill-skin-accent;
|
||||
@apply h-6 w-6 fill-skin-base hover:fill-skin-accent;
|
||||
}
|
||||
#theme-btn {
|
||||
@apply p-3 sm:p-1;
|
||||
}
|
||||
#theme-btn svg {
|
||||
@apply hover:rotate-12 scale-125 sm:scale-100;
|
||||
@apply scale-125 hover:rotate-12 sm:scale-100;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
@apply w-6 h-5 flex-col justify-between items-end;
|
||||
@apply h-5 w-6 flex-col items-end justify-between;
|
||||
}
|
||||
.icon-container div {
|
||||
@apply bg-skin-inverted h-0.5 transition-all;
|
||||
@apply h-0.5 bg-skin-inverted transition-all;
|
||||
}
|
||||
#first-line {
|
||||
@apply w-full;
|
||||
|
||||
+12
-13
@@ -2,13 +2,12 @@ import Fuse from "fuse.js";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Card from "@components/Card";
|
||||
import slugify from "@utils/slugify";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import type { BlogFrontmatter } from "@content/_schemas";
|
||||
|
||||
type SearchItem = {
|
||||
export type SearchItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
headings: string[];
|
||||
frontmatter: Frontmatter;
|
||||
data: BlogFrontmatter;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
@@ -32,7 +31,7 @@ export default function SearchBar({ searchList }: Props) {
|
||||
};
|
||||
|
||||
const fuse = new Fuse(searchList, {
|
||||
keys: ["title", "description", "headings"],
|
||||
keys: ["title", "description"],
|
||||
includeMatches: true,
|
||||
minMatchCharLength: 2,
|
||||
threshold: 0.5,
|
||||
@@ -79,14 +78,14 @@ export default function SearchBar({ searchList }: Props) {
|
||||
</svg>
|
||||
</span>
|
||||
<input
|
||||
className="placeholder:italic placeholder:text-opacity-75 py-3 pl-10 pr-3
|
||||
block bg-skin-fill w-full rounded
|
||||
border border-skin-fill border-opacity-40
|
||||
focus:outline-none focus:border-skin-accent"
|
||||
className="block w-full rounded border border-skin-fill
|
||||
border-opacity-40 bg-skin-fill py-3 pl-10
|
||||
pr-3 placeholder:italic placeholder:text-opacity-75
|
||||
focus:border-skin-accent focus:outline-none"
|
||||
placeholder="Search for anything..."
|
||||
type="text"
|
||||
name="search"
|
||||
defaultValue={inputVal}
|
||||
value={inputVal}
|
||||
onChange={handleChange}
|
||||
autoComplete="off"
|
||||
autoFocus
|
||||
@@ -108,9 +107,9 @@ export default function SearchBar({ searchList }: Props) {
|
||||
{searchResults &&
|
||||
searchResults.map(({ item, refIndex }) => (
|
||||
<Card
|
||||
post={item.frontmatter}
|
||||
href={`/posts/${slugify(item.frontmatter)}`}
|
||||
key={`${refIndex}-${slugify(item.frontmatter)}`}
|
||||
href={`/posts/${slugify(item.data)}`}
|
||||
frontmatter={item.data}
|
||||
key={`${refIndex}-${slugify(item.data)}`}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { SOCIALS } from "src/config";
|
||||
import { SOCIALS } from "@config";
|
||||
import LinkButton from "./LinkButton.astro";
|
||||
import socialIcons from "@assets/socialIcons";
|
||||
|
||||
@@ -29,6 +29,6 @@ const { centered = false } = Astro.props;
|
||||
@apply flex-wrap justify-center gap-1;
|
||||
}
|
||||
.link-button {
|
||||
@apply p-2 sm:p-1 hover:rotate-6;
|
||||
@apply p-2 hover:rotate-6 sm:p-1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,8 +13,8 @@ const { name, size = "sm" } = Astro.props;
|
||||
}`}
|
||||
>
|
||||
<a
|
||||
href={`/tags/${name}`}
|
||||
class={`${size === "sm" ? "pr-1 text-sm" : "pr-2 text-lg"} group`}
|
||||
href={`/tags/${name.toLowerCase()}`}
|
||||
class={`${size === "sm" ? "text-sm" : "text-lg"} pr-2 group`}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -23,15 +23,15 @@ const { name, size = "sm" } = Astro.props;
|
||||
d="M16.018 3.815 15.232 8h-4.966l.716-3.815-1.964-.37L8.232 8H4v2h3.857l-.751 4H3v2h3.731l-.714 3.805 1.965.369L8.766 16h4.966l-.714 3.805 1.965.369.783-4.174H20v-2h-3.859l.751-4H21V8h-3.733l.716-3.815-1.965-.37zM14.106 14H9.141l.751-4h4.966l-.752 4z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>{name}</span>
|
||||
<span>{name.toLowerCase()}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<style>
|
||||
a {
|
||||
@apply underline decoration-dashed hover:text-skin-accent relative hover:-top-0.5 focus-visible:p-1;
|
||||
@apply relative underline decoration-dashed hover:-top-0.5 hover:text-skin-accent focus-visible:p-1;
|
||||
}
|
||||
a svg {
|
||||
@apply w-6 h-6 text-skin-base scale-95 opacity-80 -mr-5 group-hover:fill-skin-accent;
|
||||
@apply -mr-5 h-6 w-6 scale-95 text-skin-base opacity-80 group-hover:fill-skin-accent;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { z } from "astro:content";
|
||||
|
||||
export const blogSchema = z.object({
|
||||
author: z.string().optional(),
|
||||
pubDatetime: z.date(),
|
||||
title: z.string(),
|
||||
postSlug: z.string().optional(),
|
||||
featured: z.boolean().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
tags: z.array(z.string()).default(["others"]),
|
||||
ogImage: z.string().optional(),
|
||||
description: z.string(),
|
||||
});
|
||||
|
||||
export type BlogFrontmatter = z.infer<typeof blogSchema>;
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
datetime: 2022-09-23T15:22:00Z
|
||||
pubDatetime: 2022-09-23T15:22:00Z
|
||||
title: Adding new posts in AstroPaper theme
|
||||
slug: adding-new-posts-in-astropaper-theme
|
||||
postSlug: adding-new-posts-in-astropaper-theme
|
||||
featured: true
|
||||
draft: false
|
||||
tags:
|
||||
@@ -23,33 +23,48 @@ Frontmatter is the main place to store some important information about the post
|
||||
|
||||
Here is the list of frontmatter property for each post.
|
||||
|
||||
| Property | Description | Remark |
|
||||
| ----------------- | ----------------------------------------------------------------------------------------- | ------------------------- |
|
||||
| **_title_** | Title of the post. (h1) | required<sup>\*</sup> |
|
||||
| **_description_** | Description of the post. Used in post excerpt and site description of the post. | default = SITE.desc |
|
||||
| **_author_** | Author of the post. | default = SITE.author |
|
||||
| **_datetime_** | Published datetime in ISO 8601 format. | |
|
||||
| **_slug_** | Slug for the post. Usually the all lowercase title seperated in `-` instead of whtiespace | default = slugified title |
|
||||
| **_featured_** | Whether or not display this post in featured section of home page | default = false |
|
||||
| **_draft_** | Mark this post 'unpublished'. | default = false |
|
||||
| **_tags_** | Related keywords for this post. Written in array yaml format. | |
|
||||
| **_ogImage_** | OG image of the post. Useful for social media sharing and SEO. | default = SITE.ogImage |
|
||||
| Property | Description | Remark |
|
||||
| ----------------- | ------------------------------------------------------------------------------- | --------------------------------------------- |
|
||||
| **_title_** | Title of the post. (h1) | required<sup>\*</sup> |
|
||||
| **_description_** | Description of the post. Used in post excerpt and site description of the post. | required<sup>\*</sup> |
|
||||
| **_pubDatetime_** | Published datetime in ISO 8601 format. | required<sup>\*</sup> |
|
||||
| **_author_** | Author of the post. | default = SITE.author |
|
||||
| **_postSlug_** | Slug for the post. Will automatically be slugified. | default = slugified title |
|
||||
| **_featured_** | Whether or not display this post in featured section of home page | default = false |
|
||||
| **_draft_** | Mark this post 'unpublished'. | default = false |
|
||||
| **_tags_** | Related keywords for this post. Written in array yaml format. | default = others |
|
||||
| **_ogImage_** | OG image of the post. Useful for social media sharing and SEO. | default = SITE.ogImage or generated SVG image |
|
||||
|
||||
`title` and `slug` fields in frontmatter must be specified.
|
||||
Only `title`, `description` and `pubDatetime` fields in frontmatter must be specified.
|
||||
|
||||
Title is the title of the post and it is very important for search engine optimization (SEO).
|
||||
Title and description (excerpt) are important for search engine optimization (SEO) and thus AstroPaper encourages to include these in blog posts.
|
||||
|
||||
`slug` is the unique identifier of the url. Thus, `slug` must be unique and different from other posts. The whitespace of `slug` needs to be separated with `-` or `_` but `-` is recommended. If slug is not specified, the slugified title of the post will be used as slug.
|
||||
`slug` is the unique identifier of the url. Thus, `slug` must be unique and different from other posts. The whitespace of `slug` needs to be separated with `-` or `_` but `-` is recommended. However, even if you don't write the correct slug, AstroPaper will automatically slugify your incorrect slug. If slug is not specified, the slugified title of the post will be used as slug.
|
||||
|
||||
Here is the sample frontmatter for the post.
|
||||
If you omit `tags` in a blog post (in other words, if no tag is specified), the default tag `others` will be used as a tag for that post. You can set the default tag in the `/src/content/_schemas.ts` file.
|
||||
|
||||
```ts
|
||||
// src/contents/_schemas.ts
|
||||
export const blogSchema = z.object({
|
||||
// ---
|
||||
// replace "others" with whatever you want
|
||||
tags: z.array(z.string()).default(["others"]),
|
||||
ogImage: z.string().optional(),
|
||||
description: z.string(),
|
||||
});
|
||||
```
|
||||
|
||||
### Sample Frontmatter
|
||||
|
||||
Here is the sample frontmatter for a post.
|
||||
|
||||
```yaml
|
||||
# src/contents/sample-post.md
|
||||
---
|
||||
title: The title of the post
|
||||
author: your name
|
||||
datetime: 2022-09-21T05:17:19Z
|
||||
slug: the-title-of-the-post
|
||||
pubDatetime: 2022-09-21T05:17:19Z
|
||||
postSlug: the-title-of-the-post
|
||||
featured: true
|
||||
draft: false
|
||||
tags:
|
||||
@@ -101,3 +116,5 @@ My recommendation for image compression sites.
|
||||
### OG Image
|
||||
|
||||
The default OG image will be placed if a post does not specify the OG image. Though not required, OG image related to the post should be specify in the frontmatter. The recommended size for OG image is **_1200 X 640_** px.
|
||||
|
||||
> Since AstroPaper v1.4.0, OG images will be generated automatically if not specified. Check out [the announcement](https://astro-paper.pages.dev/posts/dynamic-og-image-generation-in-astropaper-blog-posts/).
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
pubDatetime: 2023-01-30T15:57:52.737Z
|
||||
title: AstroPaper 2.0
|
||||
postSlug: astro-paper-2
|
||||
featured: true
|
||||
ogImage: https://user-images.githubusercontent.com/53733092/215771435-25408246-2309-4f8b-a781-1f3d93bdf0ec.png
|
||||
tags:
|
||||
- release
|
||||
description: AstroPaper with the enhancements of Astro v2. Type-safe markdown contents, bug fixes and better dev experience etc.
|
||||
---
|
||||
|
||||
Astro 2.0 has been released with some cool features, breaking changes, DX improvements, better error overlay and so on. AstroPaper takes advantage of those cool features, especially Content Collections API.
|
||||
|
||||
<!--  -->
|
||||
|
||||

|
||||
|
||||
## Table of contents
|
||||
|
||||
## Features & Changes
|
||||
|
||||
### Type-safe Frontmatters and Redefined Blog Schema
|
||||
|
||||
Frontmatter of AstroPaper 2.0 markdown contents are now type-safe thanks to Astro’s Content Collections. Blog schema is defined inside the `src/content/_schemas.ts` file.
|
||||
|
||||
### New Home for Blog contents
|
||||
|
||||
All the blog posts were moved from `src/contents` to `src/content/blog` directory.
|
||||
|
||||
### New Fetch API
|
||||
|
||||
Contents are now fetched with `getCollection` function. No relative path to the content needs to be specified anymore.
|
||||
|
||||
```ts
|
||||
// old content fetching method
|
||||
- const postImportResult = import.meta.glob<MarkdownInstance<Frontmatter>>(
|
||||
"../contents/**/**/*.md",);
|
||||
|
||||
// new content fetching method
|
||||
+ const postImportResult = await getCollection("blog");
|
||||
```
|
||||
|
||||
### Modified Search Logic for better Search Result
|
||||
|
||||
In the older version of AstroPaper, when someone search some article, the search cirtea keys that will be searched are `title`, `description` and `headings` (heading means all the headings h1 ~ h6 of the blog post). In AstroPaper v2, only `title` and `description` will be searched as the user types.
|
||||
|
||||
### Renamed Frontmatter Properties
|
||||
|
||||
The following frontmatter properties are renamed.
|
||||
|
||||
| Old Names | New Names |
|
||||
| --------- | ----------- |
|
||||
| datetime | pubDatetime |
|
||||
| slug | postSlug |
|
||||
|
||||
### Default Tag for blog post
|
||||
|
||||
If a blog post doesn't have any tag (in other words, frontmatter property `tags` is not specified), the default tag `others` will be used for that blog post. But you can set the default tag in the `/src/content/_schemas.ts` file.
|
||||
|
||||
```ts
|
||||
// src/contents/_schemas.ts
|
||||
export const blogSchema = z.object({
|
||||
// ---
|
||||
// replace "others" with whatever you want
|
||||
tags: z.array(z.string()).default(["others"]),
|
||||
ogImage: z.string().optional(),
|
||||
description: z.string(),
|
||||
});
|
||||
```
|
||||
|
||||
### New Predefined Dark Color Scheme
|
||||
|
||||
AstroPaper v2 has a new dark color scheme (high contrast & low contrast) which is based on Astro's dark logo. Check out [this link](https://astro-paper.pages.dev/posts/predefined-color-schemes#astro-dark) for more info.
|
||||
|
||||

|
||||
|
||||
### Automatic Class Sorting
|
||||
|
||||
AstroPaper 2.0 includes automatic class sorting with [TailwindCSS Prettier plugin](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier)
|
||||
|
||||
### Updated Docs & README
|
||||
|
||||
All the [#docs](https://astro-paper.pages.dev/tags/docs/) blog posts and [README](https://github.com/satnaing/astro-paper#readme) are updated for this AstroPaper v2.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- fix broken tags in the Blog Post page
|
||||
- in a tag page, the last part of the breadcrumb is now updated to lower-case for consistency
|
||||
- exclude draft posts in a tag page
|
||||
- fix 'onChange value not updating issue' after a page reload
|
||||
+4
-2
@@ -1,8 +1,8 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
datetime: 2022-09-25T15:20:35Z
|
||||
pubDatetime: 2022-09-25T15:20:35Z
|
||||
title: Customizing AstroPaper theme color schemes
|
||||
slug: ""
|
||||
postSlug: ""
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
@@ -130,3 +130,5 @@ Here is an example of changing the light color scheme.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Check out some [predefined color schemes](https://astro-paper.pages.dev/posts/predefined-color-schemes/) AstroPaper has already crafted for you.
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
datetime: 2022-12-28T04:59:04.866Z
|
||||
pubDatetime: 2022-12-28T04:59:04.866Z
|
||||
title: Dynamic OG image generation in AstroPaper blog posts
|
||||
slug: dynamic-og-image-generation-in-astropaper-blog-posts
|
||||
postSlug: dynamic-og-image-generation-in-astropaper-blog-posts
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
+11
-11
@@ -1,8 +1,8 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
datetime: 2022-09-23T04:58:53Z
|
||||
pubDatetime: 2022-09-23T04:58:53Z
|
||||
title: How to configure AstroPaper theme
|
||||
slug: how-to-configure-astropaper-theme
|
||||
postSlug: how-to-configure-astropaper-theme
|
||||
featured: true
|
||||
draft: false
|
||||
tags:
|
||||
@@ -37,15 +37,15 @@ export const SITE = {
|
||||
|
||||
Here are SITE configuration options
|
||||
|
||||
| Options | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `website` | Your deployed website url |
|
||||
| `author` | Your name |
|
||||
| `desc` | Your site description. Useful for SEO and social media sharing. |
|
||||
| `title` | Your site name |
|
||||
| `ogImage` | Your default OG image for the site. Useful for social media sharing. OG image can be updated under `/public` directory. |
|
||||
| `lightAndDarkMode` | Enable or disable `light & dark mode` for the website. If disabled, primary color scheme will be used. This option is enabled by default. |
|
||||
| `postPerPage` | You can specify how many posts will be displayed in each posts page. (eg: if you set SITE.postPerPage to 3, each page will only show 3 posts per page) |
|
||||
| Options | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `website` | Your deployed website url |
|
||||
| `author` | Your name |
|
||||
| `desc` | Your site description. Useful for SEO and social media sharing. |
|
||||
| `title` | Your site name |
|
||||
| `ogImage` | Your default OG image for the site. Useful for social media sharing. OG images can be an external image url or they can be placed under `/public` directory. |
|
||||
| `lightAndDarkMode` | Enable or disable `light & dark mode` for the website. If disabled, primary color scheme will be used. This option is enabled by default. |
|
||||
| `postPerPage` | You can specify how many posts will be displayed in each posts page. (eg: if you set SITE.postPerPage to 3, each page will only show 3 posts per page) |
|
||||
|
||||
## Configuring logo or title
|
||||
|
||||
+5
-3
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: How to connect AstroPaper blog with Forestry CMS
|
||||
author: Sat Naing
|
||||
datetime: 2022-09-21T05:17:19Z
|
||||
slug: how-to-connect-astro-paper-blog-with-forestry-cms
|
||||
featured: true
|
||||
pubDatetime: 2022-09-21T05:17:19Z
|
||||
postSlug: how-to-connect-astro-paper-blog-with-forestry-cms
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
- docs
|
||||
@@ -15,6 +15,8 @@ description:
|
||||
Headless CMS.
|
||||
---
|
||||
|
||||
> Important!!! Forestry is going to be discontinued on April 22nd, 2023. You can [read their announcement](https://forestry.io/blog/forestry.io-end-of-life/) for more info.
|
||||
|
||||
In this article, I will explain step by step process of connecting AstroPaper theme with the Forestry headless CMS. So, let's get started 🎉
|
||||
|
||||
## Table of contents
|
||||
+3
-2
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: How Do I Develop My Portfolio Website & Blog
|
||||
author: Sat Naing
|
||||
datetime: 2022-03-25T16:55:12.000+00:00
|
||||
slug: how-do-i-develop-my-portfolio-and-blog
|
||||
pubDatetime: 2022-03-25T16:55:12.000+00:00
|
||||
postSlug: how-do-i-develop-my-portfolio-and-blog
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
@@ -10,6 +10,7 @@ tags:
|
||||
- TailwindCSS
|
||||
- HeadlessCMS
|
||||
- Blog
|
||||
ogImage: ""
|
||||
description:
|
||||
"EXAMPLE POST: My experience about developing my first portfolio website and a blog
|
||||
using NextJS and a headless CMS."
|
||||
+48
-2
@@ -1,8 +1,8 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
datetime: 2022-09-26T12:13:24Z
|
||||
pubDatetime: 2022-09-26T12:13:24Z
|
||||
title: Predefined color schemes
|
||||
slug: predefined-color-schemes
|
||||
postSlug: predefined-color-schemes
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
@@ -75,6 +75,21 @@ html[data-theme="light"] {
|
||||
|
||||
Light color scheme has to be defined as `html[data-theme="dark"]`.
|
||||
|
||||
### AstroPaper 1 original Dark Theme
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--color-fill: 47, 55, 65;
|
||||
--color-text-base: 230, 230, 230;
|
||||
--color-accent: 26, 217, 217;
|
||||
--color-card: 63, 75, 90;
|
||||
--color-card-muted: 89, 107, 129;
|
||||
--color-border: 59, 70, 85;
|
||||
}
|
||||
```
|
||||
|
||||
### Deep Oyster
|
||||
|
||||

|
||||
@@ -104,3 +119,34 @@ html[data-theme="dark"] {
|
||||
--color-border: 134, 67, 107;
|
||||
}
|
||||
```
|
||||
|
||||
### Astro dark (High Contrast)
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--color-fill: 16, 23, 42; /* higher contrast bgColor */
|
||||
--color-fill: 33, 39, 55;
|
||||
--color-text-base: 234, 237, 243;
|
||||
--color-accent: 255, 107, 1;
|
||||
--color-card: 27, 39, 70;
|
||||
--color-card-muted: 138, 51, 2;
|
||||
--color-border: 171, 75, 8;
|
||||
}
|
||||
```
|
||||
|
||||
### Astro dark (New default dark theme in AstroPaper 2)
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--color-fill: 33, 39, 55; /* lower contrast bgColor */
|
||||
--color-text-base: 234, 237, 243;
|
||||
--color-accent: 255, 107, 1;
|
||||
--color-card: 52, 63, 96;
|
||||
--color-card-muted: 138, 51, 2;
|
||||
--color-border: 171, 75, 8;
|
||||
}
|
||||
```
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: My recent article 2
|
||||
author: Sat Naing
|
||||
datetime: 2022-06-06T04:06:31Z
|
||||
slug: my-recent-2
|
||||
pubDatetime: 2022-06-06T04:06:31Z
|
||||
postSlug: my-recent-2
|
||||
featured: false
|
||||
draft: true
|
||||
tags:
|
||||
@@ -1,12 +1,13 @@
|
||||
---
|
||||
title: Tailwind Typography Plugin
|
||||
author: Sat Naing
|
||||
datetime: 2022-07-05T02:05:51Z
|
||||
pubDatetime: 2022-07-05T02:05:51Z
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
- TypeScript
|
||||
- Astro
|
||||
ogImage: ""
|
||||
description: "EXAMPLE POST: About Tailwind Typography Plugin and how you can use it effectively."
|
||||
---
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: How Do I Develop My Terminal Portfolio Website with React
|
||||
author: Sat Naing
|
||||
datetime: 2022-06-09T03:42:51Z
|
||||
slug: how-do-i-develop-my-terminal-portfolio-website-with-react
|
||||
pubDatetime: 2022-06-09T03:42:51Z
|
||||
postSlug: how-do-i-develop-my-terminal-portfolio-website-with-react
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
@@ -0,0 +1,8 @@
|
||||
import { defineCollection } from "astro:content";
|
||||
import { blogSchema } from "./_schemas";
|
||||
|
||||
const blog = defineCollection({
|
||||
schema: blogSchema,
|
||||
});
|
||||
|
||||
export const collections = { blog };
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { SITE } from "@config";
|
||||
import Breadcrumbs from "@components/Breadcrumbs.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
@@ -19,8 +19,8 @@ const { frontmatter } = Astro.props;
|
||||
<Header activeNav="about" />
|
||||
<Breadcrumbs />
|
||||
<main id="main-content">
|
||||
<section id="about" class="mb-28 prose max-w-3xl prose-img:border-0">
|
||||
<h1 class="text-2xl sm:text-3xl tracking-wider">{frontmatter.title}</h1>
|
||||
<section id="about" class="prose mb-28 max-w-3xl prose-img:border-0">
|
||||
<h1 class="text-2xl tracking-wider sm:text-3xl">{frontmatter.title}</h1>
|
||||
<slot />
|
||||
</section>
|
||||
</main>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { SITE } from "@config";
|
||||
import "../styles/base.css";
|
||||
|
||||
const googleSiteVerification = import.meta.env.PUBLIC_GOOGLE_SITE_VERIFICATION;
|
||||
|
||||
@@ -18,10 +18,10 @@ const { pageTitle, pageDesc } = Astro.props;
|
||||
|
||||
<style>
|
||||
#main-content {
|
||||
@apply max-w-3xl mx-auto px-4 pb-12 w-full;
|
||||
@apply mx-auto w-full max-w-3xl px-4 pb-12;
|
||||
}
|
||||
#main-content h1 {
|
||||
@apply font-semibold text-2xl sm:text-3xl;
|
||||
@apply text-2xl font-semibold sm:text-3xl;
|
||||
}
|
||||
#main-content p {
|
||||
@apply mt-2 mb-6 italic;
|
||||
|
||||
@@ -4,16 +4,17 @@ import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Tag from "@components/Tag.astro";
|
||||
import Datetime from "@components/Datetime";
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export interface Props {
|
||||
post: MarkdownInstance<Frontmatter>;
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
|
||||
const { frontmatter, Content } = Astro.props.post;
|
||||
const { post } = Astro.props;
|
||||
|
||||
const { title, author, description, ogImage, datetime, tags } = frontmatter;
|
||||
const { title, author, description, ogImage, pubDatetime, tags } = post.data;
|
||||
|
||||
const { Content } = await post.render();
|
||||
|
||||
const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
|
||||
.href;
|
||||
@@ -21,9 +22,9 @@ const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
|
||||
|
||||
<Layout title={title} author={author} description={description} ogImage={ogUrl}>
|
||||
<Header />
|
||||
<div class="max-w-3xl mx-auto w-full px-2 flex justify-start">
|
||||
<div class="mx-auto flex w-full max-w-3xl justify-start px-2">
|
||||
<button
|
||||
class="mt-8 mb-2 hover:opacity-75 flex focus-outline"
|
||||
class="focus-outline mt-8 mb-2 flex hover:opacity-75"
|
||||
onclick="history.back()"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -35,8 +36,8 @@ const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
|
||||
</div>
|
||||
<main id="main-content">
|
||||
<h1 class="post-title">{title}</h1>
|
||||
<Datetime datetime={datetime} size="lg" className="my-2" />
|
||||
<article id="article" role="article" class="mx-auto max-w-3xl mt-8 prose">
|
||||
<Datetime datetime={pubDatetime} size="lg" className="my-2" />
|
||||
<article id="article" role="article" class="prose mx-auto mt-8 max-w-3xl">
|
||||
<Content />
|
||||
</article>
|
||||
|
||||
@@ -49,10 +50,10 @@ const ogUrl = new URL(ogImage ? ogImage : `${title}.svg`, Astro.url.origin)
|
||||
|
||||
<style>
|
||||
main {
|
||||
@apply max-w-3xl mx-auto px-4 pb-12 w-full;
|
||||
@apply mx-auto w-full max-w-3xl px-4 pb-12;
|
||||
}
|
||||
.post-title {
|
||||
@apply font-semibold text-2xl text-skin-accent;
|
||||
@apply text-2xl font-semibold text-skin-accent;
|
||||
}
|
||||
.tags-container {
|
||||
@apply my-8;
|
||||
|
||||
+8
-11
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { SITE } from "@config";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
@@ -7,13 +7,12 @@ import Footer from "@components/Footer.astro";
|
||||
import Card from "@components/Card";
|
||||
import LinkButton from "@components/LinkButton.astro";
|
||||
import slugify from "@utils/slugify";
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export interface Props {
|
||||
pageNum: number;
|
||||
totalPages: number;
|
||||
posts: MarkdownInstance<Frontmatter>[];
|
||||
posts: CollectionEntry<"blog">[];
|
||||
}
|
||||
|
||||
const { pageNum, totalPages, posts } = Astro.props;
|
||||
@@ -27,11 +26,9 @@ const next = pageNum < totalPages ? "" : "disabled";
|
||||
<Main pageTitle="Posts" pageDesc="All the articles I've posted.">
|
||||
<ul>
|
||||
{
|
||||
posts.map(({ frontmatter }) => {
|
||||
return (
|
||||
<Card href={`/posts/${slugify(frontmatter)}`} post={frontmatter} />
|
||||
);
|
||||
})
|
||||
posts.map(({ data }) => (
|
||||
<Card href={`/posts/${slugify(data)}`} frontmatter={data} />
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</Main>
|
||||
@@ -69,10 +66,10 @@ const next = pageNum < totalPages ? "" : "disabled";
|
||||
|
||||
<style>
|
||||
.pagination-wrapper {
|
||||
@apply flex justify-center mt-auto mb-8;
|
||||
@apply mt-auto mb-8 flex justify-center;
|
||||
}
|
||||
.disabled {
|
||||
@apply opacity-50 hover:text-skin-base group-hover:fill-skin-base pointer-events-none select-none;
|
||||
@apply pointer-events-none select-none opacity-50 hover:text-skin-base group-hover:fill-skin-base;
|
||||
}
|
||||
.disabled-svg {
|
||||
@apply group-hover:!fill-skin-base;
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { SITE } from "@config";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
@@ -28,15 +28,15 @@ import LinkButton from "@components/LinkButton.astro";
|
||||
|
||||
<style>
|
||||
#main-content {
|
||||
@apply max-w-3xl mx-auto flex-1 justify-center items-center flex;
|
||||
@apply mx-auto flex max-w-3xl flex-1 items-center justify-center;
|
||||
}
|
||||
.not-found-wrapper {
|
||||
@apply flex flex-col items-center justify-center mb-14;
|
||||
@apply mb-14 flex flex-col items-center justify-center;
|
||||
}
|
||||
.not-found-wrapper h1 {
|
||||
@apply font-bold text-9xl text-skin-accent;
|
||||
@apply text-9xl font-bold text-skin-accent;
|
||||
}
|
||||
.not-found-wrapper p {
|
||||
@apply text-2xl sm:text-3xl mt-4;
|
||||
@apply mt-4 text-2xl sm:text-3xl;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
import type { APIRoute, MarkdownInstance } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import generateOgImage from "@utils/generateOgImage";
|
||||
import type { Frontmatter } from "@types";
|
||||
import type { APIRoute } from "astro";
|
||||
|
||||
export const get: APIRoute = async ({ params }) => ({
|
||||
body: await generateOgImage(params.ogTitle),
|
||||
});
|
||||
|
||||
const postImportResult = import.meta.glob<MarkdownInstance<Frontmatter>>(
|
||||
"../contents/**/**/*.md",
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
);
|
||||
const postImportResult = await getCollection("blog", ({ data }) => !data.draft);
|
||||
const posts = Object.values(postImportResult);
|
||||
|
||||
export function getStaticPaths() {
|
||||
return posts
|
||||
.filter(({ frontmatter }) => !frontmatter.draft)
|
||||
.filter(({ frontmatter }) => !frontmatter.ogImage)
|
||||
.map(({ frontmatter }) => ({
|
||||
params: { ogTitle: frontmatter.title },
|
||||
.filter(({ data }) => !data.ogImage)
|
||||
.map(({ data }) => ({
|
||||
params: { ogTitle: data.title },
|
||||
}));
|
||||
}
|
||||
|
||||
+16
-18
@@ -1,22 +1,20 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import LinkButton from "@components/LinkButton.astro";
|
||||
import Hr from "@components/Hr.astro";
|
||||
import Card from "@components/Card";
|
||||
import Socials from "@components/Socials.astro";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
import slugify from "@utils/slugify";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import Socials from "@components/Socials.astro";
|
||||
import { SOCIALS } from "@config";
|
||||
|
||||
const posts = await Astro.glob<Frontmatter>("../contents/**/*.md");
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
const featuredPosts = sortedPosts.filter(
|
||||
({ frontmatter }) => frontmatter.featured
|
||||
);
|
||||
const featuredPosts = sortedPosts.filter(({ data }) => data.featured);
|
||||
|
||||
const socialCount = SOCIALS.filter(social => social.active).length;
|
||||
---
|
||||
@@ -76,10 +74,10 @@ const socialCount = SOCIALS.filter(social => social.active).length;
|
||||
<section id="featured">
|
||||
<h2>Featured</h2>
|
||||
<ul>
|
||||
{featuredPosts.map(({ frontmatter }) => (
|
||||
{featuredPosts.map(({ data }) => (
|
||||
<Card
|
||||
href={`/posts/${slugify(frontmatter)}`}
|
||||
post={frontmatter}
|
||||
href={`/posts/${slugify(data)}`}
|
||||
frontmatter={data}
|
||||
secHeading={false}
|
||||
/>
|
||||
))}
|
||||
@@ -95,11 +93,11 @@ const socialCount = SOCIALS.filter(social => social.active).length;
|
||||
<ul>
|
||||
{
|
||||
sortedPosts.map(
|
||||
({ frontmatter }, index) =>
|
||||
({ data }, index) =>
|
||||
index < 4 && (
|
||||
<Card
|
||||
href={`/posts/${slugify(frontmatter)}`}
|
||||
post={frontmatter}
|
||||
href={`/posts/${slugify(data)}`}
|
||||
frontmatter={data}
|
||||
secHeading={false}
|
||||
/>
|
||||
)
|
||||
@@ -128,22 +126,22 @@ const socialCount = SOCIALS.filter(social => social.active).length;
|
||||
@apply pt-8 pb-6;
|
||||
}
|
||||
#hero h1 {
|
||||
@apply inline-block font-bold my-4 sm:my-8 text-3xl sm:text-5xl;
|
||||
@apply my-4 inline-block text-3xl font-bold sm:my-8 sm:text-5xl;
|
||||
}
|
||||
#hero .rss-link {
|
||||
@apply mb-6;
|
||||
}
|
||||
#hero .rss-icon {
|
||||
@apply h-6 w-6 scale-110 sm:scale-125 mb-2 sm:mb-3 fill-skin-accent;
|
||||
@apply mb-2 h-6 w-6 scale-110 fill-skin-accent sm:mb-3 sm:scale-125;
|
||||
}
|
||||
#hero p {
|
||||
@apply my-2;
|
||||
}
|
||||
.social-wrapper {
|
||||
@apply flex flex-col sm:flex-row sm:items-center mt-4;
|
||||
@apply mt-4 flex flex-col sm:flex-row sm:items-center;
|
||||
}
|
||||
.social-links {
|
||||
@apply whitespace-nowrap mr-2 sm:mb-0 mb-1;
|
||||
@apply mr-2 mb-1 whitespace-nowrap sm:mb-0;
|
||||
}
|
||||
|
||||
/* ===== Featured & Recent Posts Sections ===== */
|
||||
@@ -153,9 +151,9 @@ const socialCount = SOCIALS.filter(social => social.active).length;
|
||||
}
|
||||
#featured h2,
|
||||
#recent-posts h2 {
|
||||
@apply font-semibold text-2xl tracking-wide;
|
||||
@apply text-2xl font-semibold tracking-wide;
|
||||
}
|
||||
.all-posts-btn-wrapper {
|
||||
@apply text-center my-8;
|
||||
@apply my-8 text-center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,53 +1,27 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { CollectionEntry, getCollection } from "astro:content";
|
||||
import Posts from "@layouts/Posts.astro";
|
||||
import PostDetails from "@layouts/PostDetails.astro";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
import getPageNumbers from "@utils/getPageNumbers";
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import slugify from "@utils/slugify";
|
||||
import { SITE } from "@config";
|
||||
|
||||
export interface Props {
|
||||
post: MarkdownInstance<Frontmatter>;
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
|
||||
type PostResult = {
|
||||
params: {
|
||||
slug: string | number;
|
||||
};
|
||||
props?: {
|
||||
post: MarkdownInstance<Record<string, any>>;
|
||||
};
|
||||
}[];
|
||||
|
||||
type PagePaths = {
|
||||
params: {
|
||||
slug: string;
|
||||
};
|
||||
}[];
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
|
||||
const filteredPosts = posts.filter(({ frontmatter }) => !frontmatter.draft);
|
||||
const postResult = posts.map(post => ({
|
||||
params: { slug: slugify(post.data) },
|
||||
props: { post },
|
||||
}));
|
||||
|
||||
let postResult: PostResult = filteredPosts.map(post => {
|
||||
return {
|
||||
params: {
|
||||
slug: slugify(post.frontmatter),
|
||||
},
|
||||
props: {
|
||||
post,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const pagePaths: PagePaths = getPageNumbers(filteredPosts.length).map(
|
||||
pageNum => ({
|
||||
params: { slug: String(pageNum) },
|
||||
})
|
||||
);
|
||||
const pagePaths = getPageNumbers(posts.length).map(pageNum => ({
|
||||
params: { slug: String(pageNum) },
|
||||
}));
|
||||
|
||||
return [...postResult, ...pagePaths];
|
||||
}
|
||||
@@ -55,7 +29,7 @@ export async function getStaticPaths() {
|
||||
const { slug } = Astro.params;
|
||||
const { post } = Astro.props;
|
||||
|
||||
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { SITE } from "@config";
|
||||
import Posts from "@layouts/Posts.astro";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
import getPageNumbers from "@utils/getPageNumbers";
|
||||
import type { Frontmatter } from "src/types";
|
||||
|
||||
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
|
||||
|
||||
+12
-21
@@ -1,28 +1,19 @@
|
||||
import { SITE } from "src/config";
|
||||
import rss from "@astrojs/rss";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import { SITE } from "@config";
|
||||
import slugify from "@utils/slugify";
|
||||
|
||||
const postImportResult = import.meta.glob<MarkdownInstance<Frontmatter>>(
|
||||
"../contents/**/**/*.md",
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
);
|
||||
const posts = Object.values(postImportResult);
|
||||
|
||||
export const get = () =>
|
||||
rss({
|
||||
export async function get() {
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
return rss({
|
||||
title: SITE.title,
|
||||
description: SITE.desc,
|
||||
site: SITE.website,
|
||||
items: posts
|
||||
.filter(({ frontmatter }) => !frontmatter.draft)
|
||||
.map(({ frontmatter }) => ({
|
||||
link: `posts/${slugify(frontmatter)}`,
|
||||
title: frontmatter.title,
|
||||
description: frontmatter.description,
|
||||
pubDate: new Date(frontmatter.datetime),
|
||||
})),
|
||||
items: posts.map(({ data }) => ({
|
||||
link: `posts/${slugify(data)}`,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
pubDate: new Date(data.pubDatetime),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
+8
-11
@@ -1,24 +1,21 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { getCollection } from "astro:content";
|
||||
import { SITE } from "@config";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Search from "@components/Search";
|
||||
import type { Frontmatter } from "src/types";
|
||||
|
||||
// Retrieve all articles
|
||||
const posts = await Astro.glob<Frontmatter>("../contents/**/*.md");
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
|
||||
// List of items to search in
|
||||
const searchList = posts
|
||||
.filter(({ frontmatter }) => !frontmatter.draft)
|
||||
.map(post => ({
|
||||
title: post.frontmatter.title,
|
||||
description: post.frontmatter.description,
|
||||
headings: post.getHeadings().map(h => h.text),
|
||||
frontmatter: post.frontmatter,
|
||||
}));
|
||||
const searchList = posts.map(({ data }) => ({
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
data,
|
||||
}));
|
||||
---
|
||||
|
||||
<Layout title={`Search | ${SITE.title}`}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { CollectionEntry, getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
@@ -7,39 +7,30 @@ import Footer from "@components/Footer.astro";
|
||||
import Card from "@components/Card";
|
||||
import getUniqueTags from "@utils/getUniqueTags";
|
||||
import getPostsByTag from "@utils/getPostsByTag";
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import slugify from "@utils/slugify";
|
||||
import { SITE } from "@config";
|
||||
|
||||
export interface Props {
|
||||
post: MarkdownInstance<Frontmatter>;
|
||||
post: CollectionEntry<"blog">;
|
||||
tag: string;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts: MarkdownInstance<Frontmatter>[] = await Astro.glob(
|
||||
"../../contents/**/*.md"
|
||||
);
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const tags = getUniqueTags(posts);
|
||||
|
||||
return tags.map(tag => {
|
||||
return {
|
||||
params: {
|
||||
tag,
|
||||
},
|
||||
props: {
|
||||
tag,
|
||||
},
|
||||
params: { tag },
|
||||
props: { tag },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const { tag } = Astro.props;
|
||||
|
||||
const posts: MarkdownInstance<Frontmatter>[] = await Astro.glob(
|
||||
"../../contents/**/*.md"
|
||||
);
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
|
||||
const tagPosts = getPostsByTag(posts, tag);
|
||||
---
|
||||
@@ -52,8 +43,8 @@ const tagPosts = getPostsByTag(posts, tag);
|
||||
>
|
||||
<ul>
|
||||
{
|
||||
tagPosts.map(({ frontmatter }) => (
|
||||
<Card href={`/posts/${slugify(frontmatter)}`} post={frontmatter} />
|
||||
tagPosts.map(({ data }) => (
|
||||
<Card href={`/posts/${slugify(data)}`} frontmatter={data} />
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
import { SITE } from "src/config";
|
||||
import { getCollection } from "astro:content";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Tag from "@components/Tag.astro";
|
||||
import getUniqueTags from "@utils/getUniqueTags";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import { SITE } from "@config";
|
||||
|
||||
const posts = await Astro.glob<Frontmatter>("../../contents/**/*.md");
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
let tags = getUniqueTags(posts);
|
||||
---
|
||||
|
||||
+34
-34
@@ -13,12 +13,12 @@
|
||||
--color-border: 236, 233, 233;
|
||||
}
|
||||
html[data-theme="dark"] {
|
||||
--color-fill: 47, 55, 65;
|
||||
--color-text-base: 230, 230, 230;
|
||||
--color-accent: 26, 217, 217;
|
||||
--color-card: 63, 75, 90;
|
||||
--color-card-muted: 89, 107, 129;
|
||||
--color-border: 59, 70, 85;
|
||||
--color-fill: 33, 39, 55;
|
||||
--color-text-base: 234, 237, 243;
|
||||
--color-accent: 255, 107, 1;
|
||||
--color-card: 52, 63, 96;
|
||||
--color-card-muted: 138, 51, 2;
|
||||
--color-border: 171, 75, 8;
|
||||
}
|
||||
#sun-svg,
|
||||
html[data-theme="dark"] #moon-svg {
|
||||
@@ -29,45 +29,45 @@
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
@apply font-mono bg-skin-fill text-skin-base min-h-screen flex flex-col
|
||||
selection:bg-skin-accent selection:text-skin-inverted selection:bg-opacity-70;
|
||||
@apply flex min-h-screen flex-col bg-skin-fill font-mono text-skin-base
|
||||
selection:bg-skin-accent selection:bg-opacity-70 selection:text-skin-inverted;
|
||||
}
|
||||
section,
|
||||
footer {
|
||||
@apply max-w-3xl mx-auto px-4;
|
||||
@apply mx-auto max-w-3xl px-4;
|
||||
}
|
||||
a {
|
||||
@apply outline-offset-1 outline-skin-fill outline-2
|
||||
focus-visible:outline-dashed focus-visible:no-underline;
|
||||
@apply outline-2 outline-offset-1 outline-skin-fill
|
||||
focus-visible:no-underline focus-visible:outline-dashed;
|
||||
}
|
||||
svg {
|
||||
@apply w-6 h-6 inline-block fill-skin-base group-hover:fill-skin-accent;
|
||||
@apply inline-block h-6 w-6 fill-skin-base group-hover:fill-skin-accent;
|
||||
}
|
||||
svg.icon-tabler {
|
||||
@apply w-6 h-6 scale-125 sm:scale-110 opacity-90
|
||||
inline-block fill-transparent stroke-2 stroke-current
|
||||
group-hover:fill-transparent;
|
||||
@apply inline-block h-6 w-6 scale-125 fill-transparent
|
||||
stroke-current stroke-2 opacity-90 group-hover:fill-transparent
|
||||
sm:scale-110;
|
||||
}
|
||||
.prose {
|
||||
@apply prose-a:underline-offset-8 prose-ul:overflow-x-clip
|
||||
prose-a:!decoration-dashed hover:prose-a:text-skin-accent
|
||||
prose-img:mx-auto prose-img:border-2 prose-img:border-skin-line
|
||||
prose-img:!mt-2 prose-headings:!mb-3
|
||||
prose-th:border prose-th:border-skin-line
|
||||
prose-td:border prose-td:border-skin-line
|
||||
prose-h3:italic
|
||||
@apply prose-headings:!mb-3 prose-headings:!text-skin-base
|
||||
prose-h3:italic prose-p:!text-skin-base
|
||||
prose-a:!text-skin-base prose-a:!decoration-dashed prose-a:underline-offset-8
|
||||
hover:prose-a:text-skin-accent prose-blockquote:!border-l-skin-accent
|
||||
prose-blockquote:border-opacity-50 prose-blockquote:opacity-80
|
||||
prose-figcaption:!text-skin-base prose-figcaption:opacity-70
|
||||
prose-strong:!text-skin-base
|
||||
|
||||
|
||||
prose-p:!text-skin-base prose-headings:!text-skin-base
|
||||
prose-ol:!text-skin-base prose-ul:!text-skin-base prose-blockquote:opacity-80
|
||||
prose-figcaption:!text-skin-base prose-figcaption:opacity-70
|
||||
prose-code:bg-skin-card prose-code:bg-opacity-75
|
||||
prose-code:!text-skin-base prose-code:p-1 prose-code:rounded
|
||||
prose-code:after:!content-[''] prose-code:before:!content-['']
|
||||
prose-strong:!text-skin-base prose-table:text-skin-base
|
||||
prose-pre:!text-skin-base prose-a:!text-skin-base
|
||||
prose-hr:!border-skin-line prose-li:marker:!text-skin-accent
|
||||
prose-blockquote:!border-l-skin-accent prose-blockquote:border-opacity-50;
|
||||
prose-code:rounded prose-code:bg-skin-card
|
||||
prose-code:bg-opacity-75 prose-code:p-1 prose-code:!text-skin-base
|
||||
prose-code:before:!content-[''] prose-code:after:!content-['']
|
||||
prose-pre:!text-skin-base prose-ol:!text-skin-base
|
||||
prose-ul:overflow-x-clip prose-ul:!text-skin-base prose-li:marker:!text-skin-accent
|
||||
prose-table:text-skin-base prose-th:border
|
||||
prose-th:border-skin-line prose-td:border
|
||||
prose-td:border-skin-line prose-img:mx-auto
|
||||
prose-img:!mt-2 prose-img:border-2
|
||||
prose-img:border-skin-line prose-hr:!border-skin-line;
|
||||
}
|
||||
.prose a {
|
||||
@apply hover:!text-skin-accent;
|
||||
@@ -81,7 +81,7 @@
|
||||
@apply mb-2;
|
||||
}
|
||||
.prose details {
|
||||
@apply text-skin-base cursor-pointer inline-block select-none;
|
||||
@apply inline-block cursor-pointer select-none text-skin-base;
|
||||
}
|
||||
.prose summary {
|
||||
@apply focus-outline;
|
||||
@@ -121,6 +121,6 @@
|
||||
@apply hidden;
|
||||
}
|
||||
.focus-outline {
|
||||
@apply outline-offset-1 outline-skin-fill outline-2 focus-visible:outline-dashed focus-visible:no-underline;
|
||||
@apply outline-2 outline-offset-1 outline-skin-fill focus-visible:no-underline focus-visible:outline-dashed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
export interface Frontmatter {
|
||||
title: string;
|
||||
ogImage?: string;
|
||||
description: string;
|
||||
author: string;
|
||||
datetime: string;
|
||||
slug: string;
|
||||
featured: boolean;
|
||||
draft: boolean;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export type SocialObjects = {
|
||||
name: SocialMedia;
|
||||
href: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SITE } from "src/config";
|
||||
import { SITE } from "@config";
|
||||
|
||||
const getPageNumbers = (numberOfPosts: number) => {
|
||||
const numberOfPages = numberOfPosts / Number(SITE.postPerPage);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { slufigyAll } from "./slugify";
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import type { Frontmatter } from "../types";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
const getPostsByTag = (posts: MarkdownInstance<Frontmatter>[], tag: string) =>
|
||||
posts.filter(post => slufigyAll(post.frontmatter.tags).includes(tag));
|
||||
const getPostsByTag = (posts: CollectionEntry<"blog">[], tag: string) =>
|
||||
posts.filter(post => slufigyAll(post.data.tags).includes(tag));
|
||||
|
||||
export default getPostsByTag;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import type { Frontmatter } from "../types";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
const getSortedPosts = (posts: MarkdownInstance<Frontmatter>[]) =>
|
||||
const getSortedPosts = (posts: CollectionEntry<"blog">[]) =>
|
||||
posts
|
||||
.filter(({ frontmatter }) => !frontmatter.draft)
|
||||
.filter(({ data }) => !data.draft)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
Math.floor(new Date(b.frontmatter.datetime).getTime() / 1000) -
|
||||
Math.floor(new Date(a.frontmatter.datetime).getTime() / 1000)
|
||||
Math.floor(new Date(b.data.pubDatetime).getTime() / 1000) -
|
||||
Math.floor(new Date(a.data.pubDatetime).getTime() / 1000)
|
||||
);
|
||||
|
||||
export default getSortedPosts;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { slugifyStr } from "./slugify";
|
||||
import type { MarkdownInstance } from "astro";
|
||||
import type { Frontmatter } from "../types";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
const getUniqueTags = (posts: MarkdownInstance<Frontmatter>[]) => {
|
||||
const getUniqueTags = (posts: CollectionEntry<"blog">[]) => {
|
||||
let tags: string[] = [];
|
||||
const filteredPosts = posts.filter(({ frontmatter }) => !frontmatter.draft);
|
||||
const filteredPosts = posts.filter(({ data }) => !data.draft);
|
||||
filteredPosts.forEach(post => {
|
||||
tags = [...tags, ...post.frontmatter.tags]
|
||||
tags = [...tags, ...post.data.tags]
|
||||
.map(tag => slugifyStr(tag))
|
||||
.filter(
|
||||
(value: string, index: number, self: string[]) =>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { slug as slugger } from "github-slugger";
|
||||
import type { Frontmatter } from "src/types";
|
||||
import type { BlogFrontmatter } from "@content/_schemas";
|
||||
|
||||
export const slugifyStr = (str: string) => slugger(str);
|
||||
|
||||
const slugify = (frontmatter: Frontmatter) =>
|
||||
frontmatter.slug ? slugger(frontmatter.slug) : slugger(frontmatter.title);
|
||||
const slugify = (post: BlogFrontmatter) =>
|
||||
post.postSlug ? slugger(post.postSlug) : slugger(post.title);
|
||||
|
||||
export const slufigyAll = (arr: string[]) => arr.map(str => slugifyStr(str));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user