mirror of
https://github.com/ChenQihan666/MyBlog-Next.git
synced 2026-08-14 11:29:33 +08:00
feat!: AstroPaper v6 (#631)
* feat: add initial configuration and social icons for AstroPaper theme * feat: add theme styles and design tokens * chore: configure Astro with sitemap, i18n, fonts, and tooling - Set site URL, i18n (single locale, no prefix), sitemap integration - Add Google Sans Code font via stable fonts API - Configure Shiki dual-theme markdown (min-light / night-owl) - Fix astro-paper.config.ts to use relative import (alias not available when Vite loads root-level config files) - Add @astrojs/sitemap and @typescript-eslint/parser dependencies - Wire @typescript-eslint/parser into ESLint so TypeScript syntax in Astro frontmatter (type Props, etc.) parses correctly * feat: add base styles, dark variant, and typography - global.css: dark variant via [data-theme], base layer for * / a / button selectors, scrollbar tokens, layout utilities (max-w-app, app-layout), active-nav as @utility for Tailwind variant support - html and body classes moved directly onto elements in BaseLayout (Tailwind-native approach) so only truly global selectors remain here - typography.css: prose overrides, Shiki light/dark code block switching, diff/highlight/word-highlight transformer styles * feat: add i18n types, English strings, and useTranslations helper Plain typed object approach — no third-party i18n library needed. Paraglide.js was considered but requires SSR output (hard blocker for a static Astro site). - UIStrings interface with nav, post, pagination, and a11y groups - en.ts with `satisfies UIStrings` for literal-type preservation - useTranslations(locale) helper falls back to English automatically Adding a new language is a one-file addition; full routing stays as a registry add-on. * feat: add client-side theme toggle script - Reads window.__theme.value set by the inline FOUC-prevention script to avoid re-detecting the theme on first load - Toggles data-theme on <html> and persists to localStorage - Fills <meta name="theme-color"> with the computed background colour so Android's browser chrome matches the page - Re-runs setup on astro:after-swap for View Transitions navigation - Carries theme-color across astro:before-swap to prevent navbar flicker on Android during page transitions - Syncs with OS prefers-color-scheme changes at runtime * feat: add BaseLayout and PostLayout BaseLayout (replaces scaffold Layout.astro): - Font, canonical URL via Astro.site, RSS autodiscovery - FOUC-prevention inline script sets data-theme before first paint and exposes window.__theme.value for theme.ts to reuse - html/body classes on elements directly (Tailwind-native) - <slot name="head"> for child layout injection - ClientRouter for View Transitions (respects lightAndDarkMode flag) - googleVerification rendered from config instead of .env — it is a public string, not a secret, and not environment-specific PostLayout (new): - Wraps BaseLayout; injects article-specific head content via slot - Overrides og:type to "article" - Adds article:published_time / article:modified_time conditionally - JSON-LD BlogPosting with conditional datePublished/dateModified (fixes old bug where absent dates produced "undefined" string) - Structured data only on actual post pages (not home/archives/tags) SiteConfig: add optional googleVerification field * chore: replace scaffold index page with BaseLayout placeholder * feat: add Header component * feat: add Footer and Socials component * chore: install dayjs, slugify, zod, @astrojs/mdx and add MDX integration * feat: add resolved config that applies defaults in one place Defaults for posts, features, socials, and shareLinks are merged in src/config.ts so all consuming code gets non-optional types with no scattered ?? fallbacks. * feat: add content schema, blog path, and post utilities Renames collection blog→posts, moves content to src/content/posts/, adds .mdx support, and adds a typed pages collection. Includes getSortedPosts, postFilter, getPath, and slugify utilities. * feat: add Datetime and Card components * refactor: switch layouts and components to resolved config Replaces @/astro-paper.config imports with @/config and removes optional chaining on feature flags now that the resolved config guarantees non-optional values. * feat: add home and footer i18n strings * feat: implement home page with hero, featured, and recent posts Replaces the BaseLayout placeholder with the full index page: hero section with RSS link and social links, featured posts section, recent posts capped at posts.perIndex, and an All Posts link. All section labels come from i18n. * feat: add pagination component * feat: add Breadcrumb component with locale support * feat: add Main component * refactor: update code formatting in Card.astro * feat: add posts page * fix: add missing @astrojs/mdx dependency * refactor: rename meta to pages in i18n * refactor: improve code formatting in Breadcrumb * refactor: destructure props in Main component for clarity * feat: add tag pages * feat: add About page * chore: update i18n configuration to use site language from config * feat: add Archives page * feat: integrate Pagefind search functionality * feat: add dynamic Open Graph image generation for posts * feat: add post detail page * feat: add Shiki transformers * chore: remove unused edit link text * chore: update .gitignore to include generated Pagefind directory * chore: update site URL in configuration to production link * chore: replace favicon and remove unused SVG assets, add new Open Graph image * feat: update site config with default values * feat: add default OG image path resolution * fix: ensure edit post feature checks for enabled status correctly * feat: implement dynamic Open Graph image generation * feat: add RSS feed generation functionality * feat: add 404 Not Found page * feat: add robots.txt generation for SEO optimization * refactor: change getSortedPosts to a named export * refactor: change postFilter to a named export * refactor: rename BaseLayout to Layout * refactor: standardize locale handling across components * refactor: update getPostsByGroupCondition location * chore: remove unused Welcome component * refactor(i18n): update links using getRelativeLocaleUrl * refactor(i18n): add dynamic locale loading * fix: update language attribute to use current locale * fix: correct wording for updatedAt in post translations * fix: update layout titles to use correct translation keys for tags pages * chore: use new colors for better DX and a11y * feat: add underline icon for active icon nav * feat: add toc for markdown contents * refactor: remove unused feature flags in config * refactor(i18n): enhance locale handling and path management * fix: ts unused variable hint * refactor: extract locale stripping logic into a util func * refactor(i18n): improve locale handling in tag pages * refactor: update post URL helper functions * refactor(i18n): replace pathWithBase with getRelativeLocaleUrl for locale handling * refactor: rename fileWithBase to getAssetPath * refactor: use runtime agnostic APIs for file paths * refactor: use transition directive for heading transition * refactor: remove unused title element from tag page * fix: decouple ClientRouter from lightAndDarkMode flag * refactor: rename `postPaths.ts` to `getPostPaths.ts` * refactor: remove unused `minutesRead` string from post translations * refactor: simplify AdjacentPostNav component by passing prevPost and nextPost directly * refactor: remove unused code declaration * docs: add existing blog posts * chore: update astro and @astrojs/check dependencies to latest versions * build: use experimental svg optimizer * chore(repo): restore project meta files and Docker configs * refactor: update code formatting with Prettier * docs: update README and docs * docs: add JSDoc comments to utility functions * docs: add JSDoc comments to resolved config file * fix(og): avoid double slashes when appending index.png * chore(ci): update Node.js version to 22.12.0 in CI workflow * feat: improve og image generation with Astro's getFontFileURL API (#632) * feat: improve og image generation with Astro's getFontFileURL API - remove @resvg/resvg-js and use sharp to convert SVG to PNG - update og image generation to use Astro's getFontFileURL API for better font handling * docs: update README and dynamic OG images documentation - remove outdated content and clarify dynamic OG image generation process in README. - add new image example and update instructions for handling non-Latin characters in dynamic OG images. - adjust copyright year in README to 2026. * fix(header): adjust icon button alignment in mobile * feat(docs): add ResponsiveTable to use it in MDX posts (#634) * feat(docs): add ResponsiveTable to use it in MDX posts Introduce ResponsiveTable with variant-based styling. Rename docs posts to .mdx, wrap tables in the component, and remove redundant table `code` rules from prose typography. * chore(prettier): ignore .mdx files when checking format * chore(ci): update Node.js version to 24 in CI workflow * bump: upgrade Astro and dependencies * fix: remove redundant backUrl script bug * fix(header): adjust button and icon alignment * fix(a11y): improve muted-foreground color contrast * docs: update predefined color schemes * chore: support site verification env as fallback for googleVerification * docs: add astro paper v6 updates in docs * docs: add AstroPaper-v6.png in astro-paper-6 blog post * fix: update back-to-top button DOM placement (#641) Closes #626, #131, #493
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
---
|
||||
author: Simon Smale
|
||||
pubDatetime: 2024-01-03T20:40:08Z
|
||||
modDatetime: 2024-01-08T18:59:05Z
|
||||
title: How to use Git Hooks to set Created and Modified Dates
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
- docs
|
||||
- FAQ
|
||||
canonicalURL: https://smale.codes/posts/setting-dates-via-git-hooks/
|
||||
description: How to use Git Hooks to set your Created and Modified Dates on AstroPaper
|
||||
---
|
||||
|
||||
In this post I will explain how to use the pre-commit Git hook to automate the input of the created (`pubDatetime`) and modified (`modDatetime`) in the AstroPaper blog theme frontmatter
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Have them Everywhere
|
||||
|
||||
[Git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) are great for automating tasks like [adding](https://gist.github.com/SSmale/3b380e5bbed3233159fb7031451726ea) or [checking](https://itnext.io/using-git-hooks-to-enforce-branch-naming-policy-ffd81fa01e5e) the branch name to your commit messages or [stopping you committing plain text secrets](https://gist.github.com/SSmale/367deee757a9b2e119d241e120249000). Their biggest flaw is that client-side hooks are per machine.
|
||||
|
||||
You can get around this by having a `hooks` directory and manually copy them to the `.git/hooks` directory or set up a symlink, but this all requires you to remember to set it up, and that is not something I am good at doing.
|
||||
|
||||
As this project uses npm, we are able to make use of a package called [Husky](https://typicode.github.io/husky/) (this is already installed in AstroPaper) to automatically install the hooks for us.
|
||||
|
||||
> Update! In AstroPaper [v4.3.0](https://github.com/satnaing/astro-paper/releases/tag/v4.3.0), the pre-commit hook has been removed in favor of GitHub Actions. However, you can easily [install Husky](https://typicode.github.io/husky/get-started.html) yourself.
|
||||
|
||||
## The Hook
|
||||
|
||||
As we want this hook to run as we commit the code to update the dates and then have that as part of our change we are going to use the `pre-commit` hook. This has already been set up by this AstroPaper project, but if it hadn't, you would run `npx husky add .husky/pre-commit 'echo "This is our new pre-commit hook"'`.
|
||||
|
||||
Navigating to the `hooks/pre-commit` file, we are going to add one or both of the following snippets.
|
||||
|
||||
### Updating the modified date when a file is edited
|
||||
|
||||
---
|
||||
|
||||
UPDATE:
|
||||
|
||||
This section has been updated with a new version of the hook that is smarter. It will now not increment the `modDatetime` until the post is published. On the first publish, set the draft status to `first` and watch the magic happen.
|
||||
|
||||
---
|
||||
|
||||
```shell
|
||||
# Modified files, update the modDatetime
|
||||
git diff --cached --name-status |
|
||||
grep -i '^M.*\.md$' |
|
||||
while read _ file; do
|
||||
filecontent=$(cat "$file")
|
||||
frontmatter=$(echo "$filecontent" | awk -v RS='---' 'NR==2{print}')
|
||||
draft=$(echo "$frontmatter" | awk '/^draft: /{print $2}')
|
||||
if [ "$draft" = "false" ]; then
|
||||
echo "$file modDateTime updated"
|
||||
cat $file | sed "/---.*/,/---.*/s/^modDatetime:.*$/modDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
|
||||
mv tmp $file
|
||||
git add $file
|
||||
fi
|
||||
if [ "$draft" = "first" ]; then
|
||||
echo "First release of $file, draft set to false and modDateTime removed"
|
||||
cat $file | sed "/---.*/,/---.*/s/^modDatetime:.*$/modDatetime:/" | sed "/---.*/,/---.*/s/^draft:.*$/draft: false/" > tmp
|
||||
mv tmp $file
|
||||
git add $file
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
`git diff --cached --name-status` gets the files from git that have been staged for committing. The output looks like:
|
||||
|
||||
```shell
|
||||
A src/content/blog/setting-dates-via-git-hooks.md
|
||||
```
|
||||
|
||||
The letter at the start denotes what action has been taken, in the above example the file has been added. Modified files have `M`
|
||||
|
||||
We pipe that output into the grep command where we are looking at each line to find that have been modified. The line needs to start with `M` (`^(M)`), have any number of characters after that (`.*`) and end with the `.md` file extension (`.(md)$`).This is going to filter out the lines that are not modified markdown files `egrep -i "^(M).*\.(md)$"`.
|
||||
|
||||
---
|
||||
|
||||
#### Improvement - More Explicit
|
||||
|
||||
This could be added to only look for files that we markdown files in the `blog` directory, as these are the only ones that will have the right frontmatter
|
||||
|
||||
---
|
||||
|
||||
The regex will capture the two parts, the letter and the file path. We are going to pipe this list into a while loop to iterate over the matching lines and assign the letter to `a` and the path to `b`. We are going to ignore `a` for now.
|
||||
|
||||
To know the draft status of the file, we need its frontmatter. In the following code we are using `cat` to get the content of the file, then using `awk` to split the file on the frontmatter separator (`---`) and taking the second block (the fonmtmatter, the bit between the `---`). From here we are using `awk` again to find the draft key and print is value.
|
||||
|
||||
```shell
|
||||
filecontent=$(cat "$file")
|
||||
frontmatter=$(echo "$filecontent" | awk -v RS='---' 'NR==2{print}')
|
||||
draft=$(echo "$frontmatter" | awk '/^draft: /{print $2}')
|
||||
```
|
||||
|
||||
Now we have the value for `draft` we are going to do 1 of 3 things, set the modDatetime to now (when draft is false `if [ "$draft" = "false" ]; then`), clear the modDatetime and set draft to false (when draft is set to first `if [ "$draft" = "first" ]; then`), or nothing (in any other case).
|
||||
|
||||
The next part with the sed command is a bit magical to me as I don't often use it, it was copied from [another blog post on doing something similar](https://mademistakes.com/notes/adding-last-modified-timestamps-with-git/). In essence, it is looking inside the frontmatter tags (`---`) of the file to find the `pubDatetime:` key, getting the full line and replacing it with the `pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/"` same key again and the current datetime formatted correctly.
|
||||
|
||||
This replacement is in the context of the whole file so we put that into a temporary file (`> tmp`), then we move (`mv`) the new file into the location of the old file, overwriting it. This is then added to git ready to be committed as if we made the change ourselves.
|
||||
|
||||
---
|
||||
|
||||
#### NOTE
|
||||
|
||||
For the `sed` to work the frontmatter needs to already have the `modDatetime` key in the frontmatter. There are some other changes you will need to make for the app to build with a blank date, see [further down](#empty-moddatetime-changes)
|
||||
|
||||
---
|
||||
|
||||
### Adding the Date for new files
|
||||
|
||||
Adding the date for a new file is the same process as above, but this time we are looking for lines that have been added (`A`) and we are going to replace the `pubDatetime` value.
|
||||
|
||||
```shell
|
||||
# New files, add/update the pubDatetime
|
||||
git diff --cached --name-status | egrep -i "^(A).*\.(md)$" | while read a b; do
|
||||
cat $b | sed "/---.*/,/---.*/s/^pubDatetime:.*$/pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
|
||||
mv tmp $b
|
||||
git add $b
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Improvement - Only Loop Once
|
||||
|
||||
We could use the `a` variable to switch inside the loop and either update the `modDatetime` or add the `pubDatetime` in one loop.
|
||||
|
||||
---
|
||||
|
||||
## Populating the frontmatter
|
||||
|
||||
If your IDE supports snippets then there is the option to create a custom snippet to populate the frontmatter.[In AstroPaper v4 will come with one for VSCode by default.](https://github.com/satnaing/astro-paper/pull/206)
|
||||
|
||||
<video autoplay muted="muted" controls plays-inline="true" class="border border-skin-line">
|
||||
<source src="https://github.com/satnaing/astro-paper/assets/17761689/e13babbc-2d78-405d-8758-ca31915e41b0" type="video/mp4">
|
||||
</video>
|
||||
|
||||
## Empty `modDatetime` changes
|
||||
|
||||
To allow Astro to compile the markdown and do its thing, it needs to know what is expected in the frontmatter. It does this via the config in `src/content/config.ts`
|
||||
|
||||
To allow the key to be there with no value we need to edit line 10 to add the `.nullable()` function.
|
||||
|
||||
```ts
|
||||
const blog = defineCollection({
|
||||
type: "content",
|
||||
schema: ({ image }) =>
|
||||
z.object({
|
||||
author: z.string().default(SITE.author),
|
||||
pubDatetime: z.date(),
|
||||
modDatetime: z.date().optional(), // [!code --]
|
||||
modDatetime: z.date().optional().nullable(), // [!code ++]
|
||||
title: z.string(),
|
||||
featured: z.boolean().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
tags: z.array(z.string()).default(["others"]),
|
||||
ogImage: image().or(z.string()).optional(),
|
||||
description: z.string(),
|
||||
canonicalURL: z.string().optional(),
|
||||
readingTime: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
To stop the IDE complaining in the blog engine files I have also done the following:
|
||||
|
||||
1. added `| null` to line 15 in `src/layouts/Layout.astro` so that it looks like
|
||||
|
||||
```typescript
|
||||
export interface Props {
|
||||
title?: string;
|
||||
author?: string;
|
||||
description?: string;
|
||||
ogImage?: string;
|
||||
canonicalURL?: string;
|
||||
pubDatetime?: Date;
|
||||
modDatetime?: Date | null;
|
||||
}
|
||||
```
|
||||
|
||||
2. added `| null` to line 5 in `src/components/Datetime.tsx` so that it looks like
|
||||
|
||||
```typescript
|
||||
interface DatetimesProps {
|
||||
pubDatetime: string | Date;
|
||||
modDatetime: string | Date | undefined | null;
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user