Compare commits
3 Commits
187821c88e
...
e230e431cf
| Author | SHA1 | Date | |
|---|---|---|---|
| e230e431cf | |||
|
|
13eca9025d | ||
|
|
8cf29fc0b5 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,6 +11,8 @@ node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-store
|
||||
.hist
|
||||
pnpm-debug.log*
|
||||
|
||||
# environment variables
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
pubDatetime: 2022-09-23T15:22:00Z
|
||||
modDatetime: 2025-06-13T16:52:45.934Z
|
||||
title: Adding new posts in AstroPaper theme
|
||||
slug: adding-new-posts-in-astropaper-theme
|
||||
featured: true
|
||||
draft: false
|
||||
tags:
|
||||
- docs
|
||||
description:
|
||||
Some rules & recommendations for creating or adding new posts using AstroPaperr
|
||||
theme.
|
||||
---
|
||||
|
||||
Here are some rules/recommendations, tips & ticks for creating new posts in AstroPaper blog theme.
|
||||
|
||||
<figure>
|
||||
<img
|
||||
src="https://images.pexels.com/photos/159618/still-life-school-retro-ink-159618.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"
|
||||
alt="Free Classic wooden desk with writing materials, vintage clock, and a leather bag. Stock Photo"
|
||||
/>
|
||||
<figcaption class="text-center">
|
||||
Photo by <a href="https://www.pexels.com/photo/brown-wooden-desk-159618/">Pixabay</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Creating a Blog Post
|
||||
|
||||
To write a new blog post, create a markdown file inside the `src/data/blog/` directory.
|
||||
|
||||
> Prior to AstroPaper v5.1.0, all blog posts had to be in `src/data/blog/`, meaning you couldn't organize them into subdirectories.
|
||||
|
||||
Starting from AstroPaper v5.1.0, you can now organize blog posts into subdirectories, making it easier to manage your content.
|
||||
|
||||
For example, if you want to group posts under `2025`, you can place them in `src/data/blog/2025/`. This also affects the post URL, so `src/data/blog/2025/example-post.md` will be available at `/posts/2025/example-post`.
|
||||
|
||||
If you don’t want subdirectories to affect the post URL, just prefix the folder name with an underscore `_`.
|
||||
|
||||
```bash
|
||||
# Example: blog post structure and URLs
|
||||
src/data/blog/very-first-post.md -> mysite.com/posts/very-first-post
|
||||
src/data/blog/2025/example-post.md -> mysite.com/posts/2025/example-post
|
||||
src/data/blog/_2026/another-post.md -> mysite.com/posts/another-post
|
||||
src/data/blog/docs/_legacy/how-to.md -> mysite.com/posts/docs/how-to
|
||||
src/data/blog/Example Dir/Dummy Post.md -> mysite.com/posts/example-dir/dummy-post
|
||||
```
|
||||
|
||||
> 💡 Tip: You can override a blog post’s slug in the frontmatter as well. See the next section for more details.
|
||||
|
||||
If the subdirectory URL doesn’t appear in the build output, remove node_modules, reinstall packages, and then rebuild.
|
||||
|
||||
## Frontmatter
|
||||
|
||||
Frontmatter is the main place to store some important information about the blog post (article). Frontmatter lies at the top of the article and is written in YAML format. Read more about frontmatter and its usage in [astro documentation](https://docs.astro.build/en/guides/markdown-content/).
|
||||
|
||||
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. | required<sup>\*</sup> |
|
||||
| **_pubDatetime_** | Published datetime in ISO 8601 format. | required<sup>\*</sup> |
|
||||
| **_modDatetime_** | Modified datetime in ISO 8601 format. (only add this property when a blog post is modified) | optional |
|
||||
| **_author_** | Author of the post. | default = SITE.author |
|
||||
| **_slug_** | Slug for the post. This field is optional. | default = slugified file name |
|
||||
| **_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. This can be a remote URL or an image path relative to current folder. | default = `SITE.ogImage` or generated OG image |
|
||||
| **_canonicalURL_** | Canonical URL (absolute), in case the article already exists on other source. | default = `Astro.site` + `Astro.url.pathname` |
|
||||
| **_hideEditPost_** | Hide editPost button under blog title. This applies only to the current blog post. | default = false |
|
||||
| **_timezone_** | Specify a timezone in IANA format for the current blog post. This will override the `SITE.timezone` config for the current blog post. | default = `SITE.timezone` |
|
||||
|
||||
> Tip! You can get ISO 8601 datetime by running `new Date().toISOString()` in the console. Make sure you remove quotes though.
|
||||
|
||||
Only `title`, `description` and `pubDatetime` fields in frontmatter must be specified.
|
||||
|
||||
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` should to be separated with `-` or `_` but `-` is recommended. Slug is automatically generated using the blog post file name. However, you can define your `slug` as a frontmatter in your blog post.
|
||||
|
||||
For example, if the blog file name is `adding-new-post.md` and you don't specify the slug in your frontmatter, Astro will automatically create a slug for the blog post using the file name. Thus, the slug will be `adding-new-post`. But if you specify the `slug` in the frontmatter, this will override the default slug. You can read more about this in [Astro Docs](https://docs.astro.build/en/guides/content-collections/#defining-custom-slugs).
|
||||
|
||||
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 `content.config.ts` file.
|
||||
|
||||
```ts file="src/content.config.ts"
|
||||
export const blogSchema = z.object({
|
||||
// ...
|
||||
draft: z.boolean().optional(),
|
||||
// [!code highlight:1]
|
||||
tags: z.array(z.string()).default(["others"]), // replace "others" with whatever you want
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Sample Frontmatter
|
||||
|
||||
Here is the sample frontmatter for a post.
|
||||
|
||||
```yaml file="src/data/blog/sample-post.md"
|
||||
---
|
||||
title: The title of the post
|
||||
author: your name
|
||||
pubDatetime: 2022-09-21T05:17:19Z
|
||||
slug: the-title-of-the-post
|
||||
featured: true
|
||||
draft: false
|
||||
tags:
|
||||
- some
|
||||
- example
|
||||
- tags
|
||||
ogImage: ../../assets/images/example.png # src/assets/images/example.png
|
||||
# ogImage: "https://example.org/remote-image.png" # remote URL
|
||||
description: This is the example description of the example post.
|
||||
canonicalURL: https://example.org/my-article-was-already-posted-here
|
||||
---
|
||||
```
|
||||
|
||||
## Adding table of contents
|
||||
|
||||
By default, a post (article) does not include any table of contents (toc). To include toc, you have to specify it in a specific way.
|
||||
|
||||
Write `Table of contents` in h2 format (## in markdown) and place it where you want it to be appeared on the post.
|
||||
|
||||
For instance, if you want to place your table of contents just under the intro paragraph (like I usually do), you can do that in the following way.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
```md
|
||||
---
|
||||
# frontmatter
|
||||
---
|
||||
|
||||
Here are some recommendations, tips & ticks for creating new posts in AstroPaper blog theme.
|
||||
|
||||
<!-- [!code ++] -->
|
||||
## Table of contents
|
||||
|
||||
<!-- the rest of the post -->
|
||||
```
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## Headings
|
||||
|
||||
There's one thing to note about headings. The AstroPaper blog posts use title (title in the frontmatter) as the main heading of the post. Therefore, the rest of the heading in the post should be using h2 \~ h6.
|
||||
|
||||
This rule is not mandatory, but highly recommended for visual, accessibility and SEO purposes.
|
||||
|
||||
## Syntax Highlighting
|
||||
|
||||
AstroPaper uses [Shiki](https://shiki.style/) as the default syntax highlighting. Starting from AstroPaper v5.4, [@shikijs/transformers](https://shiki.style/packages/transformers) is used to enhance better fenced code blocks. If you don't want to use it, you can simply remove it like this
|
||||
|
||||
```bash
|
||||
pnpm remove @shikijs/transformers
|
||||
```
|
||||
|
||||
```js file="astro.config.ts"
|
||||
// ...
|
||||
// [!code --:5]
|
||||
import {
|
||||
transformerNotationDiff,
|
||||
transformerNotationHighlight,
|
||||
transformerNotationWordHighlight,
|
||||
} from "@shikijs/transformers";
|
||||
|
||||
export default defineConfig({
|
||||
// ...
|
||||
markdown: {
|
||||
remarkPlugins: [remarkToc, [remarkCollapse, { test: "Table of contents" }]],
|
||||
shikiConfig: {
|
||||
// For more themes, visit https://shiki.style/themes
|
||||
themes: { light: "min-light", dark: "night-owl" },
|
||||
defaultColor: false,
|
||||
wrap: false,
|
||||
transformers: [
|
||||
transformerFileName(),
|
||||
// [!code --:3]
|
||||
transformerNotationHighlight(),
|
||||
transformerNotationWordHighlight(),
|
||||
transformerNotationDiff({ matchAlgorithm: "v3" }),
|
||||
],
|
||||
},
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Storing Images for Blog Content
|
||||
|
||||
Here are two methods for storing images and displaying them inside a markdown file.
|
||||
|
||||
> Note! If it's a requirement to style optimized images in markdown you should [use MDX](https://docs.astro.build/en/guides/images/#images-in-mdx-files).
|
||||
|
||||
### Inside `src/assets/` directory (recommended)
|
||||
|
||||
You can store images inside `src/assets/` directory. These images will be automatically optimized by Astro through [Image Service API](https://docs.astro.build/en/reference/image-service-reference/).
|
||||
|
||||
You can use relative path or alias path (`@/assets/`) to serve these images.
|
||||
|
||||
Example: Suppose you want to display `example.jpg` whose path is `/src/assets/images/example.jpg`.
|
||||
|
||||
```md
|
||||

|
||||
|
||||
<!-- OR -->
|
||||
|
||||

|
||||
|
||||
<!-- Using img tag or Image component won't work ❌ -->
|
||||
<img src="@/assets/images/example.jpg" alt="something">
|
||||
<!-- ^^ This is wrong -->
|
||||
```
|
||||
|
||||
> Technically, you can store images inside any directory under `src`. In here, `src/assets` is just a recommendation.
|
||||
|
||||
### Inside `public` directory
|
||||
|
||||
You can store images inside the `public` directory. Keep in mind that images stored in the `public` directory remain untouched by Astro, meaning they will be unoptimized and you need to handle image optimization by yourself.
|
||||
|
||||
For these images, you should use an absolute path; and these images can be displayed using [markdown annotation](https://www.markdownguide.org/basic-syntax/#images-1) or [HTML img tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img).
|
||||
|
||||
Example: Assume `example.jpg` is located at `/public/assets/images/example.jpg`.
|
||||
|
||||
```md
|
||||

|
||||
|
||||
<!-- OR -->
|
||||
|
||||
<img src="/assets/images/example.jpg" alt="something">
|
||||
```
|
||||
|
||||
## Bonus
|
||||
|
||||
### Image compression
|
||||
|
||||
When you put images in the blog post (especially for images under `public` directory), it is recommended that the image is compressed. This will affect the overall performance of the website.
|
||||
|
||||
My recommendation for image compression sites.
|
||||
|
||||
- [TinyPng](https://tinypng.com/)
|
||||
- [TinyJPG](https://tinyjpg.com/)
|
||||
|
||||
### 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/).
|
||||
@@ -1,148 +0,0 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
pubDatetime: 2022-09-25T15:20:35Z
|
||||
modDatetime: 2026-01-09T15:00:15.170Z
|
||||
title: Customizing AstroPaper theme color schemes
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
- color-schemes
|
||||
- docs
|
||||
description:
|
||||
How you can enable/disable light & dark mode; and customize color schemes
|
||||
of AstroPaper theme.
|
||||
---
|
||||
|
||||
This post will explain how you can enable/disable light & dark mode for the website. Moreover, you'll learn how you can customize color schemes of the entire website.
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Enable/disable light & dark mode
|
||||
|
||||
AstroPaper theme will include light and dark mode by default. In other words, there will be two color schemes\_ one for light mode and another for dark mode. This default behavior can be disabled in `SITE` configuration object.
|
||||
|
||||
```js file="src/config.ts"
|
||||
export const SITE = {
|
||||
website: "https://astro-paper.pages.dev/", // replace this with your deployed domain
|
||||
author: "Sat Naing",
|
||||
profile: "https://satnaing.dev/",
|
||||
desc: "A minimal, responsive and SEO-friendly Astro blog theme.",
|
||||
title: "AstroPaper",
|
||||
ogImage: "astropaper-og.jpg",
|
||||
lightAndDarkMode: true, // [!code highlight]
|
||||
postPerIndex: 4,
|
||||
postPerPage: 4,
|
||||
scheduledPostMargin: 15 * 60 * 1000, // 15 minutes
|
||||
showArchives: true,
|
||||
showBackButton: true, // show back button in post detail
|
||||
editPost: {
|
||||
enabled: true,
|
||||
text: "Suggest Changes",
|
||||
url: "https://github.com/satnaing/astro-paper/edit/main/",
|
||||
},
|
||||
dynamicOgImage: true,
|
||||
lang: "en", // html lang code. Set this empty and default will be "en"
|
||||
timezone: "Asia/Bangkok", // Default global timezone (IANA format) https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
} as const;
|
||||
```
|
||||
|
||||
To disable `light & dark mode` set `SITE.lightAndDarkMode` to `false`.
|
||||
|
||||
## Choose initial color scheme
|
||||
|
||||
By default, if we disable `SITE.lightAndDarkMode`, we will only get system's prefers-color-scheme.
|
||||
|
||||
Thus, to choose an initial color scheme instead of prefers-color-scheme, we have to set color scheme in the `initialColorScheme` variable inside `theme.ts`.
|
||||
|
||||
```ts file="src/scripts/theme.ts"
|
||||
// Initial color scheme
|
||||
// Can be "light", "dark", or empty string for system's prefers-color-scheme
|
||||
const initialColorScheme = ""; // "light" | "dark" // [!code hl]
|
||||
|
||||
function getPreferTheme(): string {
|
||||
// get theme data from local storage (user's explicit choice)
|
||||
const currentTheme = localStorage.getItem("theme");
|
||||
if (currentTheme) return currentTheme;
|
||||
|
||||
// return initial color scheme if it is set (site default)
|
||||
if (initialColorScheme) return initialColorScheme;
|
||||
|
||||
// return user device's prefer color scheme (system fallback)
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
}
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
The **initialColorScheme** variable can hold two values\_ `"light"`, `"dark"`. You can leave the empty string (default) if you don't want to specify an initial color scheme.
|
||||
|
||||
- `""` - system's prefers-color-scheme. (default)
|
||||
- `"light"` - use light mode as initial color scheme.
|
||||
- `"dark"` - use dark mode as initial color scheme.
|
||||
|
||||
<details>
|
||||
<summary>Why initialColorScheme is not inside config.ts?</summary>
|
||||
To avoid color flickering on page reload, we have to place the theme initialization JavaScript code as early as possible when the page loads. The theme script is split into two parts: a minimal inline script in the `<head>` that sets the theme immediately, and the full script that loads asynchronously. This approach prevents FOUC (Flash of Unstyled Content) while maintaining optimal performance.
|
||||
</details>
|
||||
|
||||
## Customize color schemes
|
||||
|
||||
Both light & dark color schemes of AstroPaper theme can be customized in the `global.css` file.
|
||||
|
||||
```css file="src/styles/global.css"
|
||||
@import "tailwindcss";
|
||||
@import "./typography.css";
|
||||
|
||||
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
||||
|
||||
:root,
|
||||
html[data-theme="light"] {
|
||||
--background: #fdfdfd;
|
||||
--foreground: #282728;
|
||||
--accent: #006cac;
|
||||
--muted: #e6e6e6;
|
||||
--border: #ece9e9;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] {
|
||||
--background: #212737;
|
||||
--foreground: #eaedf3;
|
||||
--accent: #ff6b01;
|
||||
--muted: #343f60bf;
|
||||
--border: #ab4b08;
|
||||
}
|
||||
/* ... */
|
||||
```
|
||||
|
||||
In the AstroPaper theme, the `:root` and `html[data-theme="light"]` selectors define the light color scheme, while `html[data-theme="dark"]` defines the dark color scheme.
|
||||
|
||||
To customize your own color scheme, specify your light colors inside `:root, html[data-theme="light"]`, and your dark colors inside `html[data-theme="dark"]`.
|
||||
|
||||
Here is the detail explanation of color properties.
|
||||
|
||||
| Color Property | Definition & Usage |
|
||||
| -------------- | ------------------------------------------------------------- |
|
||||
| `--background` | Primary color of the website. Usually the main background. |
|
||||
| `--foreground` | Secondary color of the website. Usually the text color. |
|
||||
| `--accent` | Accent color of the website. Link color, hover color etc. |
|
||||
| `--muted` | Card and scrollbar background color for hover state etc. |
|
||||
| `--border` | Border color. Used for border utilities and visual separation |
|
||||
|
||||
Here is an example of changing the light color scheme.
|
||||
|
||||
```css file="src/styles/global.css"
|
||||
/* ... */
|
||||
:root,
|
||||
html[data-theme="light"] {
|
||||
--background: #f6eee1;
|
||||
--foreground: #012c56;
|
||||
--accent: #e14a39;
|
||||
--muted: #efd8b0;
|
||||
--border: #dc9891;
|
||||
}
|
||||
/* ... */
|
||||
```
|
||||
|
||||
> Check out some [predefined color schemes](https://astro-paper.pages.dev/posts/predefined-color-schemes/) AstroPaper has already crafted for you.
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
pubDatetime: 2022-12-28T04:59:04.866Z
|
||||
modDatetime: 2025-03-12T13:39:20.763Z
|
||||
title: Dynamic OG image generation in AstroPaper blog posts
|
||||
slug: dynamic-og-image-generation-in-astropaper-blog-posts
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
- docs
|
||||
- release
|
||||
description: New feature in AstroPaper v1.4.0, introducing dynamic OG image generation for blog posts.
|
||||
---
|
||||
|
||||
New feature in AstroPaper v1.4.0, introducing dynamic OG image generation for blog posts.
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Intro
|
||||
|
||||
OG images (aka Social Images) play an important role in social media engagements. In case you don't know what OG image means, it is an image displayed whenever we share our website URL on social media such as Facebook, Discord etc.
|
||||
|
||||
> The Social Image used for Twitter is technically not called OG image. However, in this post, I'll be using the term OG image for all types of Social Images.
|
||||
|
||||
## Default/Static OG image (the old way)
|
||||
|
||||
AstroPaper already provided a way to add an OG image to a blog post. The author can specify the OG image in the frontmatter `ogImage`. Even when the author doesn't define the OG image in the frontmatter, the default OG image will be used as a fallback (in this case `public/astropaper-og.jpg`). But the problem is that the default OG image is static, which means every blog post that does not include an OG image in the frontmatter will always use the same default OG image despite each post title/content being different from others.
|
||||
|
||||
## Dynamic OG Image
|
||||
|
||||
Generating a dynamic OG image for each post allows the author to avoid specifying an OG image for every single blog post. Besides, this will prevent the fallback OG image from being identical to all blog posts.
|
||||
|
||||
In AstroPaper v1.4.0, Vercel's [Satori](https://github.com/vercel/satori) package is used for dynamic OG image generation.
|
||||
|
||||
Dynamic OG images will be generated at build time for blog posts that
|
||||
|
||||
- don't include OG image in the frontmatter
|
||||
- are not marked as draft.
|
||||
|
||||
## Anatomy of AstroPaper dynamic OG image
|
||||
|
||||
Dynamic OG image of AstroPaper includes _the blog post title_, _author name_ and _site title_. Author name and site title will be retrieved via `SITE.author` and `SITE.title` of **"src/config.ts"** file. The title is generated from the blog post frontmatter `title`.
|
||||

|
||||
|
||||
### Issue Non-Latin Characters
|
||||
|
||||
Titles with non-latin characters won't display properly out of the box. To resolve this, we have to replace `fontsConfig` inside `loadGoogleFont.ts` with your preferred font.
|
||||
|
||||
```ts file=src/utils/loadGoogleFont.ts
|
||||
async function loadGoogleFonts(
|
||||
text: string
|
||||
): Promise<
|
||||
Array<{ name: string; data: ArrayBuffer; weight: number; style: string }>
|
||||
> {
|
||||
const fontsConfig = [
|
||||
{
|
||||
name: "Noto Sans JP",
|
||||
font: "Noto+Sans+JP",
|
||||
weight: 400,
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
name: "Noto Sans JP",
|
||||
font: "Noto+Sans+JP:wght@700",
|
||||
weight: 700,
|
||||
style: "normal",
|
||||
},
|
||||
{ name: "Noto Sans", font: "Noto+Sans", weight: 400, style: "normal" },
|
||||
{
|
||||
name: "Noto Sans",
|
||||
font: "Noto+Sans:wght@700",
|
||||
weight: 700,
|
||||
style: "normal",
|
||||
},
|
||||
];
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
> Check out [this PR](https://github.com/satnaing/astro-paper/pull/318) for more info.
|
||||
|
||||
## Trade-off
|
||||
|
||||
While this is a nice feature to have, there's a trade-off. Each OG image takes roughly one second to generate. This might not be noticeable at first, but as the number of blog posts grows, you might want to disable this feature. Since every OG image takes time to generate, having many of them will increase the build time linearly.
|
||||
|
||||
For example: If one OG image takes one second to generate, then 60 images will take around one minute, and 600 images will take approximately 10 minutes. This can significantly impact build times as your content scales.
|
||||
|
||||
Related issue: [#428](https://github.com/satnaing/astro-paper/issues/428)
|
||||
|
||||
## Limitations
|
||||
|
||||
At the time of writing this, [Satori](https://github.com/vercel/satori) is fairly new and has not reached major release yet. So, there are still some limitations to this dynamic OG image feature.
|
||||
|
||||
- Besides, RTL languages are not supported yet.
|
||||
- [Using emoji](https://github.com/vercel/satori#emojis) in the title might be a little bit tricky.
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
author: Alberto Perdomo
|
||||
pubDatetime: 2024-09-08T20:58:52.737Z
|
||||
modDatetime: 2025-03-22T09:25:46.734Z
|
||||
title: How to add LaTeX Equations in Astro blog posts
|
||||
tags:
|
||||
- docs
|
||||
description: Learn how to add LaTeX equations in Astro blog posts using Markdown, KaTeX, and remark/rehype plugins.
|
||||
---
|
||||
|
||||
This document demonstrates how to use LaTeX equations in your Markdown files for AstroPaper. LaTeX is a powerful typesetting system often used for mathematical and scientific documents.
|
||||
|
||||
<figure>
|
||||
<img
|
||||
src="https://images.pexels.com/photos/22690748/pexels-photo-22690748/free-photo-of-close-up-of-complicated-equations-written-on-a-blackboard.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
|
||||
alt="Free Close-up of complex equations on a chalkboard, showcasing chemistry and math symbols. Stock Photo"
|
||||
/>
|
||||
<figcaption class="text-center">
|
||||
Photo by <a href="https://www.pexels.com/photo/close-up-of-complicated-equations-written-on-a-blackboard-22690748/">Vitaly Gariev</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Instructions
|
||||
|
||||
In this section, you will find instructions on how to add support for LaTeX in your Markdown files for AstroPaper.
|
||||
|
||||
1. Install the necessary remark and rehype plugins by running:
|
||||
|
||||
```bash
|
||||
pnpm install rehype-katex remark-math katex
|
||||
```
|
||||
|
||||
2. Update the Astro configuration to use the these plugins:
|
||||
|
||||
```ts file=astro.config.ts
|
||||
// ...
|
||||
import remarkMath from "remark-math";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
|
||||
export default defineConfig({
|
||||
// ...
|
||||
markdown: {
|
||||
remarkPlugins: [
|
||||
remarkMath, // [!code ++]
|
||||
remarkToc,
|
||||
[remarkCollapse, { test: "Table of contents" }],
|
||||
],
|
||||
rehypePlugins: [rehypeKatex], // [!code ++]
|
||||
shikiConfig: {
|
||||
// For more themes, visit https://shiki.style/themes
|
||||
themes: { light: "min-light", dark: "night-owl" },
|
||||
wrap: false,
|
||||
},
|
||||
},
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
3. Import KaTeX CSS in the main layout file
|
||||
|
||||
```astro file=src/layouts/Layout.astro
|
||||
---
|
||||
import { SITE } from "@config";
|
||||
|
||||
// astro code
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<!-- Other elements -->
|
||||
<meta property="og:image" content={socialImageURL} />
|
||||
|
||||
<!-- [!code highlight:4] -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css"
|
||||
/>
|
||||
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
```
|
||||
|
||||
4. As the last step, add a text-color for `katex` in `typography.css`.
|
||||
|
||||
```css file=src/styles/typography.css
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@layer base {
|
||||
/* other classes */
|
||||
|
||||
/* Katex text color */
|
||||
/* [!code highlight:3] */
|
||||
.prose .katex-display {
|
||||
@apply text-foreground;
|
||||
}
|
||||
|
||||
/* ===== Code Blocks & Syntax Highlighting ===== */
|
||||
/* other classes */
|
||||
}
|
||||
```
|
||||
|
||||
And _voilà_, this setup allows you to write LaTeX equations in your Markdown files, which will be rendered properly when the site is built. Once you do it, the rest of the document will appear rendered correctly.
|
||||
|
||||
---
|
||||
|
||||
## Inline Equations
|
||||
|
||||
Inline equations are written between single dollar signs `$...$`. Here are some examples:
|
||||
|
||||
1. The famous mass-energy equivalence formula: `$E = mc^2$`
|
||||
2. The quadratic formula: `$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$`
|
||||
3. Euler's identity: `$e^{i\pi} + 1 = 0$`
|
||||
|
||||
---
|
||||
|
||||
## Block Equations
|
||||
|
||||
For more complex equations or when you want the equation to be displayed on its own line, use double dollar signs `$$...$$`:
|
||||
|
||||
The Gaussian integral:
|
||||
|
||||
```bash
|
||||
$$ \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi} $$
|
||||
```
|
||||
|
||||
The definition of the Riemann zeta function:
|
||||
|
||||
```bash
|
||||
$$ \zeta(s) = \sum_{n=1}^{\infty} \frac{1}{n^s} $$
|
||||
```
|
||||
|
||||
Maxwell's equations in differential form:
|
||||
|
||||
```bash
|
||||
$$
|
||||
\begin{aligned}
|
||||
\nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0} \\
|
||||
\nabla \cdot \mathbf{B} &= 0 \\
|
||||
\nabla \times \mathbf{E} &= -\frac{\partial \mathbf{B}}{\partial t} \\
|
||||
\nabla \times \mathbf{B} &= \mu_0\left(\mathbf{J} + \varepsilon_0 \frac{\partial \mathbf{E}}{\partial t}\right)
|
||||
\end{aligned}
|
||||
$$
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using Mathematical Symbols
|
||||
|
||||
LaTeX provides a wide range of mathematical symbols:
|
||||
|
||||
- Greek letters: `$\alpha$`, `$\beta$`, `$\gamma$`, `$\delta$`, `$\epsilon$`, `$\pi$`
|
||||
- Operators: `$\sum$`, `$\prod$`, `$\int$`, `$\partial$`, `$\nabla$`
|
||||
- Relations: `$\leq$`, `$\geq$`, `$\approx$`, `$\sim$`, `$\propto$`
|
||||
- Logical symbols: `$\forall$`, `$\exists$`, `$\neg$`, `$\wedge$`, `$\vee$`
|
||||
@@ -1,271 +0,0 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
pubDatetime: 2022-09-23T04:58:53Z
|
||||
modDatetime: 2026-01-10T13:04:53.851Z
|
||||
title: How to configure AstroPaper theme
|
||||
slug: how-to-configure-astropaper-theme
|
||||
featured: true
|
||||
draft: false
|
||||
tags:
|
||||
- configuration
|
||||
- docs
|
||||
description: How you can make AstroPaper theme absolutely yours.
|
||||
---
|
||||
|
||||
AstroPaper is a highly customizable Astro blog theme. With AstroPaper, you can customize everything according to your personal taste. This article will explain how you can make some customizations easily in the config file.
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Configuring SITE
|
||||
|
||||
The important configurations resides in `src/config.ts` file. Within that file, you'll see the `SITE` object where you can specify your website's main configurations.
|
||||
|
||||
During development, it's okay to leave `SITE.website` empty. But in production mode, you should specify your deployed url in `SITE.website` option since this will be used for canonical URL, social card URL etc.. which are important for SEO.
|
||||
|
||||
```js file=src/config.ts
|
||||
export const SITE = {
|
||||
website: "https://astro-paper.pages.dev/", // replace this with your deployed domain
|
||||
author: "Sat Naing",
|
||||
profile: "https://satnaing.dev/",
|
||||
desc: "A minimal, responsive and SEO-friendly Astro blog theme.",
|
||||
title: "AstroPaper",
|
||||
ogImage: "astropaper-og.jpg",
|
||||
lightAndDarkMode: true,
|
||||
postPerIndex: 4,
|
||||
postPerPage: 4,
|
||||
scheduledPostMargin: 15 * 60 * 1000, // 15 minutes
|
||||
showArchives: true,
|
||||
showBackButton: true, // show back button in post detail
|
||||
editPost: {
|
||||
enabled: true,
|
||||
text: "Suggest Changes",
|
||||
url: "https://github.com/satnaing/astro-paper/edit/main/",
|
||||
},
|
||||
dynamicOgImage: true, // enable automatic dynamic og-image generation
|
||||
dir: "ltr", // "rtl" | "auto"
|
||||
lang: "en", // html lang code. Set this empty and default will be "en"
|
||||
timezone: "Asia/Bangkok", // Default global timezone (IANA format) https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
} as const;
|
||||
```
|
||||
|
||||
Here are SITE configuration options
|
||||
|
||||
| Options | Description |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `website` | Your deployed website URL |
|
||||
| `author` | Your name |
|
||||
| `profile` | Your personal/portfolio website URL which is used for better SEO. Put `null` or empty string `""` if you don't have any. |
|
||||
| `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. |
|
||||
| `postPerIndex` | The number of posts to be displayed at the home page under `Recent` section. |
|
||||
| `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) |
|
||||
| `scheduledPostMargin` | In Production mode, posts with a future `pubDatetime` will not be visible. However, if a post's `pubDatetime` is within the next 15 minutes, it will be visible. You can set `scheduledPostMargin` if you don't like the default 15 minutes margin. |
|
||||
| `showArchives` | Determines whether to display the `Archives` menu (positioned between the `About` and `Search` menus) and its corresponding page on the site. This option is set to `true` by default. |
|
||||
| `showBackButton` | Determines whether to display the `Go back` button in each blog post. |
|
||||
| `editPost` | This option allows users to suggest changes to a blog post by providing an edit link under blog post titles. This feature can be disabled by setting `SITE.editPost.enabled` to `false`. |
|
||||
| `dynamicOgImage` | This option controls whether to [generate dynamic og-image](https://astro-paper.pages.dev/posts/dynamic-og-image-generation-in-astropaper-blog-posts/) if no `ogImage` is specified in the blog post frontmatter. If you have many blog posts, you might want to disable this feature. See the [trade-off](https://astro-paper.pages.dev/posts/dynamic-og-image-generation-in-astropaper-blog-posts/#trade-off) for more details. |
|
||||
| `dir` | Specifies the text direction of the entire blog. Used as [HTML dir attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir) in `<html dir="ltr">`. Supported values: `ltr` \| `rtl` \| `auto` |
|
||||
| `lang` | Used as HTML ISO Language code in `<html lang"en">`. Default is `en`. |
|
||||
| `timezone` | This option allows you to specify your timezone using the [IANA format](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Setting this ensures consistent timestamps across your localhost and deployed site, eliminating time differences. |
|
||||
|
||||
## Update layout width
|
||||
|
||||
The default `max-width` for the entire blog is `768px` (`max-w-3xl`). If you'd like to change it, you can easily update the `max-w-app` utility in your `global.css`. For instance:
|
||||
|
||||
```css file=src/styles/global.css
|
||||
@utility max-w-app {
|
||||
/* [!code --:1] */
|
||||
@apply max-w-3xl;
|
||||
/* [!code ++:1] */
|
||||
@apply max-w-4xl xl:max-w-5xl;
|
||||
}
|
||||
```
|
||||
|
||||
You can explore more `max-width` values in the [Tailwind CSS docs](https://tailwindcss.com/docs/max-width).
|
||||
|
||||
## Configuring logo or title
|
||||
|
||||
Prior to AstroPaper v5, you can update your site name/logo in `LOGO_IMAGE` object inside `src/config.ts` file. However, in AstroPaper v5, this option has been removed in favor of Astro's built-in SVG and Image components.
|
||||
|
||||

|
||||
|
||||
There are 3 options you can do:
|
||||
|
||||
### Option 1: SITE title text
|
||||
|
||||
This is the easiest option. You just have to update `SITE.title` in `src/config.ts` file.
|
||||
|
||||
### Option 2: Astro's SVG component
|
||||
|
||||
You might want to use this option if you want to use an SVG logo.
|
||||
|
||||
- First add an SVG inside `src/assets` directory. (eg: `src/assets/dummy-logo.svg`)
|
||||
- Then import that SVG inside `Header.astro`
|
||||
|
||||
```astro file=src/components/Header.astro
|
||||
---
|
||||
// ...
|
||||
import DummyLogo from "@/assets/dummy-logo.svg";
|
||||
---
|
||||
```
|
||||
|
||||
- Finally, replace `{SITE.title}` with imported logo.
|
||||
|
||||
```html
|
||||
<a
|
||||
href="/"
|
||||
class="absolute py-1 text-left text-2xl leading-7 font-semibold whitespace-nowrap sm:static"
|
||||
>
|
||||
<DummyLogo class="scale-75 dark:invert" />
|
||||
<!-- {SITE.title} -->
|
||||
</a>
|
||||
```
|
||||
|
||||
The best part of this approach is that you can customize your SVG styles as needed. In the example above, you can see how the SVG logo color can be inverted in dark mode.
|
||||
|
||||
### Option 3: Astro's Image component
|
||||
|
||||
If your logo is an image but not SVG, you can use Astro's Image component.
|
||||
|
||||
- Add your logo inside `src/assets` directory. (eg: `src/assets/dummy-logo.png`)
|
||||
- Import `Image` and your logo in `Header.astro`
|
||||
|
||||
```astro file=src/components/Header.astro
|
||||
---
|
||||
// ...
|
||||
import { Image } from "astro:assets";
|
||||
import dummyLogo from "@/assets/dummy-logo.png";
|
||||
---
|
||||
```
|
||||
|
||||
- Then, replace `{SITE.title}` with imported logo.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```html
|
||||
<a
|
||||
href="/"
|
||||
class="absolute py-1 text-left text-2xl leading-7 font-semibold whitespace-nowrap sm:static"
|
||||
>
|
||||
<Image src="{dummyLogo}" alt="Dummy Blog" class="dark:invert" />
|
||||
<!-- {SITE.title} -->
|
||||
</a>
|
||||
```
|
||||
|
||||
With this approach, you can still adjust your image's appearance using CSS classes. However, this might not always fit what you want. If you need to display different logo images based on light or dark mode, check how light/dark icons are handled inside the `Header.astro` component.
|
||||
|
||||
## Configuring social links
|
||||
|
||||

|
||||
|
||||
You can configure social links in `SOCIALS` object inside `constants.ts`.
|
||||
|
||||
```ts file=src/constants.ts
|
||||
export const SOCIALS = [
|
||||
{
|
||||
name: "GitHub",
|
||||
href: "https://github.com/satnaing/astro-paper",
|
||||
linkTitle: ` ${SITE.title} on GitHub`,
|
||||
icon: IconGitHub,
|
||||
},
|
||||
{
|
||||
name: "X",
|
||||
href: "https://x.com/username",
|
||||
linkTitle: `${SITE.title} on X`,
|
||||
icon: IconBrandX,
|
||||
},
|
||||
{
|
||||
name: "LinkedIn",
|
||||
href: "https://www.linkedin.com/in/username/",
|
||||
linkTitle: `${SITE.title} on LinkedIn`,
|
||||
icon: IconLinkedin,
|
||||
},
|
||||
{
|
||||
name: "Mail",
|
||||
href: "mailto:yourmail@gmail.com",
|
||||
linkTitle: `Send an email to ${SITE.title}`,
|
||||
icon: IconMail,
|
||||
},
|
||||
] as const;
|
||||
```
|
||||
|
||||
## Configuring share links
|
||||
|
||||
You can configure share links in `SHARE_LINKS` object inside `src/constants.ts`.
|
||||
|
||||

|
||||
|
||||
## Configuring fonts
|
||||
|
||||
AstroPaper uses Astro's [experimental fonts API](https://docs.astro.build/en/reference/experimental-flags/fonts/) with [Google Sans Code](https://fonts.google.com/specimen/Google+Sans+Code) as the default font. This provides consistent typography across all platforms with automatic font optimizations including preloading and caching.
|
||||
|
||||
### Using the default font
|
||||
|
||||
The font is automatically configured in `astro.config.ts` and loaded in `Layout.astro`. No additional configuration is needed to use the default Google Sans Code font.
|
||||
|
||||
### Customizing the font
|
||||
|
||||
To use a different font, you need to update three places:
|
||||
|
||||
1. **Update the font configuration in `astro.config.ts`:**
|
||||
|
||||
```ts file=astro.config.ts
|
||||
import { defineConfig, fontProviders } from "astro/config";
|
||||
|
||||
export default defineConfig({
|
||||
// ...
|
||||
experimental: {
|
||||
fonts: [
|
||||
{
|
||||
name: "Your Font Name", // [!code highlight]
|
||||
cssVariable: "--font-your-font", // [!code highlight]
|
||||
provider: fontProviders.google(),
|
||||
fallbacks: ["monospace"],
|
||||
weights: [300, 400, 500, 600, 700],
|
||||
styles: ["normal", "italic"],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
1. **Update the Font component in `Layout.astro`:**
|
||||
|
||||
```astro file=src/layouts/Layout.astro
|
||||
---
|
||||
import { Font } from "astro:assets";
|
||||
// ...
|
||||
---
|
||||
|
||||
<head>
|
||||
<!-- ... -->
|
||||
// [!code highlight:4]
|
||||
<Font
|
||||
cssVariable="--font-your-font"
|
||||
preload={[{ subset: "latin", weight: 400, style: "normal" }]}
|
||||
/>
|
||||
<!-- ... -->
|
||||
</head>
|
||||
```
|
||||
|
||||
1. **Update the CSS variable mapping in `global.css`:**
|
||||
|
||||
```css file=src/styles/global.css
|
||||
@theme inline {
|
||||
--font-app: var(--font-your-font); /* [!code highlight] */
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted: var(--muted);
|
||||
--color-border: var(--border);
|
||||
}
|
||||
```
|
||||
|
||||
The `--font-app` variable is used throughout the theme via the `font-app` Tailwind utility class, so updating this single variable will apply your custom font everywhere.
|
||||
|
||||
> **Note**: Make sure the font name matches exactly as it appears on [Google Fonts](https://fonts.google.com). For other font providers or local fonts, refer to the [Astro Experimental Fonts API documentation](https://docs.astro.build/en/reference/experimental-flags/fonts/).
|
||||
|
||||
## Conclusion
|
||||
|
||||
This is the brief specification of how you can customize this theme. You can customize more if you know some coding. For customizing styles, please read [this article](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/). Thanks for reading.✌🏻
|
||||
@@ -1,206 +0,0 @@
|
||||
---
|
||||
author: FjellOverflow
|
||||
pubDatetime: 2024-07-25T11:11:53Z
|
||||
modDatetime: 2025-03-12T12:28:53Z
|
||||
title: How to integrate Giscus comments into AstroPaper
|
||||
slug: how-to-integrate-giscus-comments
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
- astro
|
||||
- blog
|
||||
- docs
|
||||
description: Comment function on a static blog hosted on GitHub Pages with Giscus.
|
||||
---
|
||||
|
||||
Hosting a thin static blog on a platform like [GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site) has numerous advantages, but also takes away some interactivity. Fortunately, [Giscus](https://giscus.app/) exists and offers a way to embed user comments on static sites.
|
||||
|
||||
## Table of contents
|
||||
|
||||
## How _Giscus_ works
|
||||
|
||||
[Giscus uses the GitHub API](https://github.com/giscus/giscus?tab=readme-ov-file#how-it-works) to read and store comments made by _GitHub_ users in the `Discussions` associated with a repository.
|
||||
|
||||
Embed the _Giscus_ client-side script bundle on your site, configure it with the correct repository URL, and users can view and write comments (when logged into _GitHub_).
|
||||
|
||||
The approach is serverless, as the comments are stored on _GitHub_ and dynamically loaded from there on client side, hence perfect for a static blog, like _AstroPaper_.
|
||||
|
||||
## Setting up _Giscus_
|
||||
|
||||
_Giscus_ can be set up easily on [giscus.app](https://giscus.app/), but I will outline the process shortly still.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Prerequisites to get _Giscus_ working are
|
||||
|
||||
- the repository is [public](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/setting-repository-visibility#making-a-repository-public)
|
||||
- the [Giscus app](https://github.com/apps/giscus) is installed
|
||||
- the [Discussions](https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/enabling-or-disabling-github-discussions-for-a-repository) feature is turned on for your repository
|
||||
|
||||
If any of these conditions cannot be fulfilled for any reason, unfortunately, _Giscus_ cannot be integrated.
|
||||
|
||||
### Configuring _Giscus_
|
||||
|
||||
Next, configuring _Giscus_ is necessary. In most cases, the preselected defaults are suitable, and you should only modify them if you have a specific reason and know what you are doing. Don't worry too much about making the wrong choices; you can always adjust the configuration later on.
|
||||
|
||||
However you need to
|
||||
|
||||
- select the right language for the UI
|
||||
- specify the _GitHub_ repository you want to connect, typically the repository containing your statically hosted _AstroPaper_ blog on _GitHub Pages_
|
||||
- create and set an `Announcement` type discussion on _GitHub_ if you want to ensure nobody can create random comments directly on _GitHub_
|
||||
- define the color scheme
|
||||
|
||||
After configuring the settings, _Giscus_ provides you with a generated `<script>` tag, which you will need in the next steps.
|
||||
|
||||
## Simple script tag
|
||||
|
||||
You should now have a script tag that looks like this:
|
||||
|
||||
```html
|
||||
<script
|
||||
src="https://giscus.app/client.js"
|
||||
data-repo="[ENTER REPO HERE]"
|
||||
data-repo-id="[ENTER REPO ID HERE]"
|
||||
data-category="[ENTER CATEGORY NAME HERE]"
|
||||
data-category-id="[ENTER CATEGORY ID HERE]"
|
||||
data-mapping="pathname"
|
||||
data-strict="0"
|
||||
data-reactions-enabled="1"
|
||||
data-emit-metadata="0"
|
||||
data-input-position="bottom"
|
||||
data-theme="preferred_color_scheme"
|
||||
data-lang="en"
|
||||
crossorigin="anonymous"
|
||||
async
|
||||
></script>
|
||||
```
|
||||
|
||||
Simply add that to the source code of the site. Most likely, if you're using _AstroPaper_ and want to enable comments on posts, navigate to `PostDetails.astro` and paste it into the desired location where you want the comments to appear, perhaps underneath the `Share this post on:` buttons.
|
||||
|
||||
```astro file=src/layouts/PostDetails.astro
|
||||
<Layout {...layoutProps}>
|
||||
<main>
|
||||
<ShareLinks />
|
||||
|
||||
<!-- [!code ++:6] -->
|
||||
<script
|
||||
src="https://giscus.app/client.js"
|
||||
data-repo="[ENTER REPO HERE]"
|
||||
data-repo-id="[ENTER REPO ID HERE]"
|
||||
data-category="[ENTER CATEGORY NAME HERE]"
|
||||
data-category-id="[ENTER CATEGORY ID HERE]"></script>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
```
|
||||
|
||||
And it's done! You have successfully integrated comments in _AstroPaper_!
|
||||
|
||||
## React component with light/dark theme
|
||||
|
||||
The embedded script tag in the layout is quite static, with the _Giscus_ configuration, including `theme`, hardcoded into the layout. Given that _AstroPaper_ features a light/dark theme toggle, it would be nice for the comments to seamlessly transition between light and dark themes along with the rest of the site. To achieve this, a more sophisticated approach to embedding _Giscus_ is required.
|
||||
|
||||
Firstly, we are going to install the [React component](https://www.npmjs.com/package/@giscus/react) for _Giscus_:
|
||||
|
||||
```bash
|
||||
npm i @giscus/react && npx astro add react
|
||||
```
|
||||
|
||||
Then we create a new `Comments.tsx` React component in `src/components`:
|
||||
|
||||
```tsx file=src/components/Comments.tsx
|
||||
import Giscus, { type Theme } from "@giscus/react";
|
||||
import { GISCUS } from "@/constants";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface CommentsProps {
|
||||
lightTheme?: Theme;
|
||||
darkTheme?: Theme;
|
||||
}
|
||||
|
||||
export default function Comments({
|
||||
lightTheme = "light",
|
||||
darkTheme = "dark",
|
||||
}: CommentsProps) {
|
||||
const [theme, setTheme] = useState(() => {
|
||||
const currentTheme = localStorage.getItem("theme");
|
||||
const browserTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
||||
.matches
|
||||
? "dark"
|
||||
: "light";
|
||||
|
||||
return currentTheme || browserTheme;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
const handleChange = ({ matches }: MediaQueryListEvent) => {
|
||||
setTheme(matches ? "dark" : "light");
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener("change", handleChange);
|
||||
|
||||
return () => mediaQuery.removeEventListener("change", handleChange);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const themeButton = document.querySelector("#theme-btn");
|
||||
const handleClick = () => {
|
||||
setTheme(prevTheme => (prevTheme === "dark" ? "light" : "dark"));
|
||||
};
|
||||
|
||||
themeButton?.addEventListener("click", handleClick);
|
||||
|
||||
return () => themeButton?.removeEventListener("click", handleClick);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<Giscus theme={theme === "light" ? lightTheme : darkTheme} {...GISCUS} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
This _React_ component not only wraps the native _Giscus_ component, but also introduces additional props, namely `lightTheme` and `darkTheme`. Leveraging two event listeners, the _Giscus_ comments will align with the site's theme, dynamically switching between dark and light themes whenever the site or browser theme is changed.
|
||||
|
||||
We also need to define the `GISCUS` config, for which the optimal location is in `constants.ts`:
|
||||
|
||||
```ts file=src/constants.ts
|
||||
import type { GiscusProps } from "@giscus/react";
|
||||
|
||||
...
|
||||
|
||||
export const GISCUS: GiscusProps = {
|
||||
repo: "[ENTER REPO HERE]",
|
||||
repoId: "[ENTER REPO ID HERE]",
|
||||
category: "[ENTER CATEGORY NAME HERE]",
|
||||
categoryId: "[ENTER CATEGORY ID HERE]",
|
||||
mapping: "pathname",
|
||||
reactionsEnabled: "0",
|
||||
emitMetadata: "0",
|
||||
inputPosition: "bottom",
|
||||
lang: "en",
|
||||
loading: "lazy",
|
||||
};
|
||||
```
|
||||
|
||||
Note that specifying a `theme` here will override the `lightTheme` and `darkTheme` props, resulting in a static theme setting, similar to the previous approach of embedding _Giscus_ with the `<script>` tag.
|
||||
|
||||
To complete the process, add the new Comments component to `PostDetails.astro` (replacing the `script` tag from the previous step).
|
||||
|
||||
```jsx file=src/layouts/PostDetails.astro
|
||||
// [!code ++:1]
|
||||
import Comments from "@/components/Comments";
|
||||
|
||||
<ShareLinks />
|
||||
|
||||
// [!code ++:1]
|
||||
<Comments client:only="react" />
|
||||
|
||||
<hr class="my-6 border-dashed" />
|
||||
|
||||
<Footer />
|
||||
```
|
||||
|
||||
And that's it!
|
||||
@@ -1,114 +0,0 @@
|
||||
---
|
||||
title: How to update dependencies of AstroPaper
|
||||
author: Sat Naing
|
||||
pubDatetime: 2023-07-20T15:33:05.569Z
|
||||
slug: how-to-update-dependencies
|
||||
featured: false
|
||||
draft: false
|
||||
ogImage: ../../assets/images/forrest-gump-quote.png
|
||||
tags:
|
||||
- FAQ
|
||||
description: How to update project dependencies and AstroPaper template.
|
||||
---
|
||||
|
||||
Updating the dependencies of a project can be tedious. However, neglecting to update project dependencies is not a good idea either 😬. In this post, I will share how I usually update my projects, focusing on AstroPaper as an example. Nonetheless, these steps can be applied to other js/node projects as well.
|
||||
|
||||

|
||||
|
||||
## Table of contents
|
||||
|
||||
## Updating Package Dependencies
|
||||
|
||||
There are several ways to update dependencies, and I've tried various methods to find the easiest path. One way to do it is by manually updating each package using `npm install package-name@latest`. This method is the most straightforward way of updating. However, it may not be the most efficient option.
|
||||
|
||||
My recommended way of updating dependencies is by using the [npm-check-updates package](https://www.npmjs.com/package/npm-check-updates). There's a good [article](https://www.freecodecamp.org/news/how-to-update-npm-dependencies/) from freeCodeCamp about that, so I won't be explaining the details of what it is and how to use that package. Instead, I'll show you my typical approach.
|
||||
|
||||
First, install `npm-check-updates` package globally.
|
||||
|
||||
```bash
|
||||
npm install -g npm-check-updates
|
||||
```
|
||||
|
||||
Before making any updates, it’s a good idea to check all new dependencies that can be updated.
|
||||
|
||||
```bash
|
||||
ncu
|
||||
```
|
||||
|
||||
Most of the time, patch dependencies can be updated without affecting the project at all. So, I usually update patch dependencies by running either `ncu -i --target patch` or `ncu -u --target patch`. The difference is that `ncu -u --target patch` will update all the patches, while `ncu -i --target patch` will give an option to toggle which package to update. It’s up to you to decide which approach to take.
|
||||
|
||||
The next part involves updating minor dependencies. Minor package updates usually won't break the project, but it is always good to check the release notes of the respective packages. These minor updates often include some cool features that can be applied to our projects.
|
||||
|
||||
```bash
|
||||
ncu -i --target minor
|
||||
```
|
||||
|
||||
Last but not least, there might be some major package updates in the dependencies. So, check the rest of the dependency updates by running
|
||||
|
||||
```bash
|
||||
ncu -i
|
||||
```
|
||||
|
||||
If there are any major updates (or some updates you still have to make), the above command will output those remaining packages. If the package is a major version update, you have to be very careful since this will likely break the whole project. Therefore, please read the respective release note (or) docs very carefully and make changes accordingly.
|
||||
|
||||
If you run `ncu -i` and found no more packages to be updated, _**Congrats!!!**_ you have successfully updated all the dependencies in your project.
|
||||
|
||||
## Updating AstroPaper template
|
||||
|
||||
Like other open-source projects, AstroPaper is evolving with bug fixes, feature updates, and so on. So if you’re someone who is using AstroPaper as a template, you might also want to update the template when there’s a new release.
|
||||
|
||||
The thing is, you might already have updated the template according to your flavor. Therefore, I can’t exactly show **"the one-size-fits-all perfect way"** to update the template to the most recent release. However, here are some tips to update the template without breaking your repo. Keep in mind that, most of the time, updating the package dependencies might be sufficient for you.
|
||||
|
||||
### Files and Directories to keep in mind
|
||||
|
||||
In most cases, the files and directories you might not want to override (as you've likely updated those files) are `src/content/blog/`, `src/config.ts`, `src/pages/about.md`, and other assets & styles like `public/` and `src/styles/base.css`.
|
||||
|
||||
If you’re someone who only updates the bare minimum of the template, it should be okay to replace everything with the latest AstroPaper except the above files and directories. It’s like pure Android OS and other vendor-specific OSes like OneUI. The less you modify the base, the less you have to update.
|
||||
|
||||
You can manually replace every file one by one, or you can use the magic of git to update everything. I won’t show you the manual replacement process since it is very straightforward. If you’re not interested in that straightforward and inefficient method, bear with me 🐻.
|
||||
|
||||
### Updating AstroPaper using Git
|
||||
|
||||
**IMPORTANT!!!**
|
||||
|
||||
> Only do the following if you know how to resolve merge conflicts. Otherwise, you’d better replace files manually or update dependencies only.
|
||||
|
||||
First, add astro-paper as the remote in your project.
|
||||
|
||||
```bash
|
||||
git remote add astro-paper https://github.com/satnaing/astro-paper.git
|
||||
```
|
||||
|
||||
Checkout to a new branch in order to update the template. If you know what you’re doing and you’re confident with your git skill, you can omit this step.
|
||||
|
||||
```bash
|
||||
git checkout -b build/update-astro-paper
|
||||
```
|
||||
|
||||
Then, pull the changes from astro-paper by running
|
||||
|
||||
```bash
|
||||
git pull astro-paper main
|
||||
```
|
||||
|
||||
If you face `fatal: refusing to merge unrelated histories` error, you can resolve that by running the following command
|
||||
|
||||
```bash
|
||||
git pull astro-paper main --allow-unrelated-histories
|
||||
```
|
||||
|
||||
After running the above command, you’re likely to encounter conflicts in your project. You'll need to resolve these conflicts manually and make the necessary adjustments according to your needs.
|
||||
|
||||
After resolving the conflicts, test your blog thoroughly to ensure everything is working as expected. Check your articles, components, and any customizations you made.
|
||||
|
||||
Once you're satisfied with the result, it's time to merge the update branch into your main branch (only if you are updating the template in another branch). Congratulations! You've successfully updated your template to the latest version. Your blog is now up-to-date and ready to shine! 🎉
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this article, I've shared some of my insights and processes for updating dependencies and the AstroPaper template. I genuinely hope this article proves valuable and assists you in managing your projects more efficiently.
|
||||
|
||||
If you have any alternative or improved approaches for updating dependencies/AstroPaper, I would love to hear from you. Thus, don't hesitate to start a discussion in the repository, email me, or open an issue. Your input and ideas are highly appreciated!
|
||||
|
||||
Please understand that my schedule is quite busy these days, and I may not be able to respond quickly. However, I promise to get back to you as soon as possible. 😬
|
||||
|
||||
Thank you for taking the time to read this article, and I wish you all the best with your projects!
|
||||
@@ -1,171 +0,0 @@
|
||||
---
|
||||
author: Sat Naing
|
||||
pubDatetime: 2022-09-26T12:13:24Z
|
||||
modDatetime: 2024-01-04T09:09:06Z
|
||||
title: Predefined color schemes
|
||||
slug: predefined-color-schemes
|
||||
featured: false
|
||||
draft: false
|
||||
tags:
|
||||
- color-schemes
|
||||
description:
|
||||
Some of the well-crafted, predefined color schemes for AstroPaper blog
|
||||
theme.
|
||||
---
|
||||
|
||||
I've crafted some predefined color schemes for this AstroPaper blog theme. You can replace these color schemes with the original ones.
|
||||
|
||||
If you don't know how you can configure color schemes, check [this blog post](https://astro-paper.pages.dev/posts/customizing-astropaper-theme-color-schemes/).
|
||||
|
||||
## Table of contents
|
||||
|
||||
## Light color schemes
|
||||
|
||||
Light color scheme has to be defined using the css selector `:root` and `html[data-theme="light"]`.
|
||||
|
||||
### Lobster
|
||||
|
||||

|
||||
|
||||
```css
|
||||
:root,
|
||||
html[data-theme="light"] {
|
||||
--background: #f6eee1;
|
||||
--foreground: #012c56;
|
||||
--accent: #e14a39;
|
||||
--muted: #efd8b0;
|
||||
--border: #dc9891;
|
||||
}
|
||||
```
|
||||
|
||||
### Leaf Blue
|
||||
|
||||

|
||||
|
||||
```css
|
||||
:root,
|
||||
html[data-theme="light"] {
|
||||
--background: #f2f5ec;
|
||||
--foreground: #353538;
|
||||
--accent: #1158d1;
|
||||
--muted: #bbc789;
|
||||
--border: #7cadff;
|
||||
}
|
||||
```
|
||||
|
||||
### Pinky light
|
||||
|
||||

|
||||
|
||||
```css
|
||||
:root,
|
||||
html[data-theme="light"] {
|
||||
--background: #fafcfc;
|
||||
--foreground: #222e36;
|
||||
--accent: #d3006a;
|
||||
--muted: #f1bad4;
|
||||
--border: #e3a9c6;
|
||||
}
|
||||
```
|
||||
|
||||
## Dark color schemes
|
||||
|
||||
Dark color scheme has to be defined as `html[data-theme="dark"]`.
|
||||
|
||||
### AstroPaper 1 original Dark Theme
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--background: #2f3741;
|
||||
--foreground: #e6e6e6;
|
||||
--accent: #1ad9d9;
|
||||
--muted: #596b81;
|
||||
--border: #3b4655;
|
||||
}
|
||||
```
|
||||
|
||||
### Deep Oyster
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--background: #21233d;
|
||||
--foreground: #f4f7f5;
|
||||
--accent: #ff5256;
|
||||
--muted: #4a4e86;
|
||||
--border: #b12f32;
|
||||
}
|
||||
```
|
||||
|
||||
### Pikky dark
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--background: #353640;
|
||||
--foreground: #e9edf1;
|
||||
--accent: #ff78c8;
|
||||
--muted: #715566;
|
||||
--border: #86436b;
|
||||
}
|
||||
```
|
||||
|
||||
### Astro dark (High Contrast)
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--background: #212737;
|
||||
--foreground: #eaedf3;
|
||||
--accent: #ff6b01;
|
||||
--muted: #8a3302;
|
||||
--border: #ab4b08;
|
||||
}
|
||||
```
|
||||
|
||||
### Astro dark (New default dark theme in AstroPaper 2)
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--background: #212737; /* lower contrast background */
|
||||
--foreground: #eaedf3;
|
||||
--accent: #ff6b01;
|
||||
--muted: #8a3302;
|
||||
--border: #ab4b08;
|
||||
}
|
||||
```
|
||||
|
||||
### Astro Deep Purple (New dark theme in AstroPaper 3)
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--background: #212737;
|
||||
--foreground: #eaedf3;
|
||||
--accent: #eb3fd3;
|
||||
--muted: #7d4f7c;
|
||||
--border: #642451;
|
||||
}
|
||||
```
|
||||
|
||||
### AstroPaper v4 Special (New dark theme in AstroPaper 4)
|
||||
|
||||

|
||||
|
||||
```css
|
||||
html[data-theme="dark"] {
|
||||
--background: #000123;
|
||||
--accent: #617bff;
|
||||
--foreground: #eaedf3;
|
||||
--muted: #0c0e4f;
|
||||
--border: #303f8a;
|
||||
}
|
||||
```
|
||||
@@ -1,189 +0,0 @@
|
||||
---
|
||||
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;
|
||||
}
|
||||
```
|
||||
49
src/data/events/annual-summit-2026.md
Normal file
49
src/data/events/annual-summit-2026.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: "HEC IA Annual AI Summit 2026"
|
||||
description: "Join us for our biggest event of the year featuring industry leaders, researchers, and AI practitioners."
|
||||
author: "HEC IA"
|
||||
pubDatetime: 2026-01-20T09:00:00Z
|
||||
eventDate: 2026-03-15T09:00:00Z
|
||||
eventEndDate: 2026-03-15T18:00:00Z
|
||||
location: "HEC Paris Campus, Jouy-en-Josas"
|
||||
tags: ["summit", "conference", "networking"]
|
||||
featured: true
|
||||
registrationLink: "https://hec-ia.com/summit-2026"
|
||||
---
|
||||
|
||||
## About the Summit
|
||||
|
||||
The HEC IA Annual AI Summit brings together students, professionals, and researchers to explore the latest developments in artificial intelligence and its impact on business and society.
|
||||
|
||||
## Keynote Speakers
|
||||
|
||||
- **Dr. Sarah Chen** - Director of AI Research at Google DeepMind
|
||||
- **Prof. Jean Dupont** - AI Ethics Researcher, Sorbonne University
|
||||
- **Marie Laurent** - Head of AI Strategy, Total Energies
|
||||
|
||||
## Event Schedule
|
||||
|
||||
### Morning Session (9:00 - 12:30)
|
||||
|
||||
- Opening remarks
|
||||
- Keynote: "The Future of AI in Business"
|
||||
- Panel discussion: "AI Ethics and Governance"
|
||||
|
||||
### Afternoon Session (14:00 - 18:00)
|
||||
|
||||
- Technical workshops
|
||||
- Startup showcase
|
||||
- Networking reception
|
||||
|
||||
## Who Should Attend
|
||||
|
||||
- Students interested in AI careers
|
||||
- Professionals working with AI
|
||||
- Researchers and academics
|
||||
- Entrepreneurs in the AI space
|
||||
|
||||
## Registration
|
||||
|
||||
Early bird tickets available until February 15th. Student discounts available with valid ID.
|
||||
|
||||
**Price**: €50 (Students: €25)
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: 'HEC IA x HEC for Women : international digital women day '
|
||||
description: HEC IA commitment for gender equality in tech
|
||||
author: HEC IA
|
||||
pubDatetime: 2026-02-05T01:45:23.962Z
|
||||
draft: false
|
||||
featured: true
|
||||
eventDate: 2026-02-14T01:45:26.266Z
|
||||
location: HEC Paris
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet est incididunt officia ea. Irure ex consectetur, id esse consectetur incididunt id non culpa est enim et reprehenderit non nulla fugiat. Ex consequat qui minim magna. Dolor eiusmod ullamco reprehenderit consequat, duis dolore aliquip ut laborum ullamco nulla cillum. Excepteur consequat tempor veniam in irure cupidatat veniam sit cupidatat sint occaecat do cillum occaecat reprehenderit adipisicing. Velit sunt excepteur officia occaecat aliquip incididunt aliquip adipisicing amet commodo.
|
||||
36
src/data/news/best-association-award.md
Normal file
36
src/data/news/best-association-award.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: "HEC IA Wins Best Student Association Award"
|
||||
description: "Our association has been recognized for excellence in promoting AI education and innovation."
|
||||
author: "HEC IA"
|
||||
pubDatetime: 2026-01-25T10:00:00Z
|
||||
tags: ["awards", "recognition", "community"]
|
||||
featured: true
|
||||
---
|
||||
|
||||
## Award Recognition
|
||||
|
||||
We're thrilled to announce that HEC IA has been awarded the "Best Student Association" prize at the 2026 French Higher Education Excellence Awards!
|
||||
|
||||
## What This Means
|
||||
|
||||
This recognition validates our commitment to:
|
||||
|
||||
- Providing high-quality AI education to students
|
||||
- Creating opportunities for hands-on learning
|
||||
- Building bridges between academia and industry
|
||||
- Fostering an inclusive AI community
|
||||
|
||||
## Looking Forward
|
||||
|
||||
This award motivates us to continue our mission of making AI education accessible and engaging. We have exciting plans for 2026, including:
|
||||
|
||||
- Expanded workshop series
|
||||
- New partnership announcements
|
||||
- Launch of our mentorship program
|
||||
- International collaboration initiatives
|
||||
|
||||
## Thank You
|
||||
|
||||
We couldn't have achieved this without our dedicated team, supportive faculty, industry partners, and engaged community members. Thank you all!
|
||||
|
||||
Stay tuned for more updates and upcoming events.
|
||||
192
src/data/technical/transformer-architecture-deep-dive.md
Normal file
192
src/data/technical/transformer-architecture-deep-dive.md
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
title: "Understanding Transformer Architecture: A Deep Dive"
|
||||
description: "An in-depth technical exploration of transformer models, the architecture behind modern NLP breakthroughs."
|
||||
author: "HEC IA Technical Team"
|
||||
pubDatetime: 2026-01-28T10:00:00Z
|
||||
tags: ["transformers", "nlp", "deep-learning", "architecture"]
|
||||
difficulty: "advanced"
|
||||
readingTime: "25 min"
|
||||
featured: true
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Transformers have revolutionized natural language processing since their introduction in the landmark paper "Attention is All You Need" (Vaswani et al., 2017). This deep dive explores the architecture, mechanisms, and implementations that make transformers so powerful.
|
||||
|
||||
## The Attention Mechanism
|
||||
|
||||
At the heart of transformers lies the self-attention mechanism, which allows the model to weigh the importance of different words in a sequence.
|
||||
|
||||
### Self-Attention Formula
|
||||
|
||||
The self-attention mechanism computes:
|
||||
|
||||
```
|
||||
Attention(Q, K, V) = softmax(QK^T / √d_k)V
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- Q (Query): What we're looking for
|
||||
- K (Key): What we're matching against
|
||||
- V (Value): The actual content we want to retrieve
|
||||
- d_k: Dimension of the key vectors (for scaling)
|
||||
|
||||
## Multi-Head Attention
|
||||
|
||||
Instead of performing a single attention function, multi-head attention runs multiple attention operations in parallel:
|
||||
|
||||
```python
|
||||
class MultiHeadAttention(nn.Module):
|
||||
def __init__(self, d_model, num_heads):
|
||||
super().__init__()
|
||||
self.d_model = d_model
|
||||
self.num_heads = num_heads
|
||||
self.head_dim = d_model // num_heads
|
||||
|
||||
self.qkv_proj = nn.Linear(d_model, 3 * d_model)
|
||||
self.out_proj = nn.Linear(d_model, d_model)
|
||||
|
||||
def forward(self, x):
|
||||
batch_size, seq_len, _ = x.shape
|
||||
|
||||
# Project and split into Q, K, V
|
||||
qkv = self.qkv_proj(x)
|
||||
qkv = qkv.reshape(batch_size, seq_len, 3, self.num_heads, self.head_dim)
|
||||
q, k, v = qkv.unbind(2)
|
||||
|
||||
# Compute attention
|
||||
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
|
||||
attn = F.softmax(scores, dim=-1)
|
||||
|
||||
# Apply attention to values
|
||||
out = torch.matmul(attn, v)
|
||||
out = out.transpose(1, 2).reshape(batch_size, seq_len, self.d_model)
|
||||
|
||||
return self.out_proj(out)
|
||||
```
|
||||
|
||||
## Positional Encoding
|
||||
|
||||
Since transformers don't have inherent sequence order, we add positional encodings:
|
||||
|
||||
```python
|
||||
def positional_encoding(seq_len, d_model):
|
||||
position = torch.arange(seq_len).unsqueeze(1)
|
||||
div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))
|
||||
|
||||
pe = torch.zeros(seq_len, d_model)
|
||||
pe[:, 0::2] = torch.sin(position * div_term)
|
||||
pe[:, 1::2] = torch.cos(position * div_term)
|
||||
|
||||
return pe
|
||||
```
|
||||
|
||||
## Feed-Forward Networks
|
||||
|
||||
Each transformer layer includes a position-wise feed-forward network:
|
||||
|
||||
```python
|
||||
class FeedForward(nn.Module):
|
||||
def __init__(self, d_model, d_ff, dropout=0.1):
|
||||
super().__init__()
|
||||
self.linear1 = nn.Linear(d_model, d_ff)
|
||||
self.linear2 = nn.Linear(d_ff, d_model)
|
||||
self.dropout = nn.Dropout(dropout)
|
||||
|
||||
def forward(self, x):
|
||||
return self.linear2(self.dropout(F.relu(self.linear1(x))))
|
||||
```
|
||||
|
||||
## Complete Transformer Block
|
||||
|
||||
Putting it all together:
|
||||
|
||||
```python
|
||||
class TransformerBlock(nn.Module):
|
||||
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
|
||||
super().__init__()
|
||||
self.attention = MultiHeadAttention(d_model, num_heads)
|
||||
self.feed_forward = FeedForward(d_model, d_ff, dropout)
|
||||
|
||||
self.norm1 = nn.LayerNorm(d_model)
|
||||
self.norm2 = nn.LayerNorm(d_model)
|
||||
|
||||
self.dropout1 = nn.Dropout(dropout)
|
||||
self.dropout2 = nn.Dropout(dropout)
|
||||
|
||||
def forward(self, x):
|
||||
# Self-attention with residual connection
|
||||
attn_out = self.attention(x)
|
||||
x = self.norm1(x + self.dropout1(attn_out))
|
||||
|
||||
# Feed-forward with residual connection
|
||||
ff_out = self.feed_forward(x)
|
||||
x = self.norm2(x + self.dropout2(ff_out))
|
||||
|
||||
return x
|
||||
```
|
||||
|
||||
## Training Considerations
|
||||
|
||||
### Learning Rate Scheduling
|
||||
|
||||
Transformers typically use warm-up scheduling:
|
||||
|
||||
```python
|
||||
def lr_schedule(step, d_model, warmup_steps=4000):
|
||||
arg1 = step ** -0.5
|
||||
arg2 = step * (warmup_steps ** -1.5)
|
||||
return d_model ** -0.5 * min(arg1, arg2)
|
||||
```
|
||||
|
||||
### Label Smoothing
|
||||
|
||||
To prevent overconfidence:
|
||||
|
||||
```python
|
||||
class LabelSmoothingLoss(nn.Module):
|
||||
def __init__(self, smoothing=0.1):
|
||||
super().__init__()
|
||||
self.smoothing = smoothing
|
||||
|
||||
def forward(self, pred, target):
|
||||
n_class = pred.size(1)
|
||||
one_hot = torch.zeros_like(pred).scatter(1, target.unsqueeze(1), 1)
|
||||
one_hot = one_hot * (1 - self.smoothing) + self.smoothing / n_class
|
||||
log_prob = F.log_softmax(pred, dim=1)
|
||||
return -(one_hot * log_prob).sum(dim=1).mean()
|
||||
```
|
||||
|
||||
## Practical Applications
|
||||
|
||||
Transformers excel at:
|
||||
|
||||
1. **Machine Translation**: Translating between languages
|
||||
2. **Text Summarization**: Generating concise summaries
|
||||
3. **Question Answering**: Understanding context to answer queries
|
||||
4. **Text Generation**: Creating coherent text (GPT models)
|
||||
5. **Sentiment Analysis**: Understanding emotional tone
|
||||
|
||||
## Optimization Tips
|
||||
|
||||
1. **Gradient Checkpointing**: Save memory during training
|
||||
2. **Mixed Precision Training**: Use FP16 for faster computation
|
||||
3. **Efficient Attention**: Implement sparse or local attention for long sequences
|
||||
4. **Model Parallelism**: Distribute layers across GPUs
|
||||
|
||||
## Conclusion
|
||||
|
||||
Transformers represent a paradigm shift in sequence modeling. Understanding their architecture is crucial for anyone working in modern NLP and beyond.
|
||||
|
||||
## References
|
||||
|
||||
- Vaswani et al. (2017) - "Attention is All You Need"
|
||||
- Devlin et al. (2018) - "BERT: Pre-training of Deep Bidirectional Transformers"
|
||||
- Brown et al. (2020) - "Language Models are Few-Shot Learners" (GPT-3)
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [The Illustrated Transformer](http://jalammar.github.io/illustrated-transformer/)
|
||||
- [Annotated Transformer](http://nlp.seas.harvard.edu/2018/04/03/attention.html)
|
||||
- [Transformers from Scratch](https://peterbloem.nl/blog/transformers)
|
||||
46
src/data/workshops/intro-to-ml.md
Normal file
46
src/data/workshops/intro-to-ml.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "AI Workshop: Introduction to Machine Learning"
|
||||
description: "Join us for a hands-on workshop introducing the fundamentals of machine learning with practical Python examples."
|
||||
author: "HEC IA"
|
||||
pubDatetime: 2026-01-15T10:00:00Z
|
||||
workshopDate: 2026-02-05T14:00:00Z
|
||||
duration: "3 hours"
|
||||
level: "beginner"
|
||||
tags: ["machine-learning", "python", "workshop", "beginner"]
|
||||
featured: true
|
||||
materials: "https://github.com/hec-ia/ml-workshop-intro"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This workshop will introduce you to the fundamentals of machine learning using Python and scikit-learn. Perfect for beginners with basic Python knowledge!
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
- Understanding machine learning concepts
|
||||
- Data preprocessing techniques
|
||||
- Building your first ML model
|
||||
- Model evaluation and validation
|
||||
- Hands-on coding exercises
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Basic Python programming knowledge
|
||||
- Laptop with Python 3.8+ installed
|
||||
- Jupyter Notebook or Google Colab access
|
||||
|
||||
## Schedule
|
||||
|
||||
- **14:00-14:30**: Introduction to ML concepts
|
||||
- **14:30-15:30**: Data preprocessing workshop
|
||||
- **15:30-15:45**: Break
|
||||
- **15:45-16:45**: Building and training models
|
||||
- **16:45-17:00**: Q&A and wrap-up
|
||||
|
||||
## Materials
|
||||
|
||||
All workshop materials will be available on our GitHub repository. Please clone the repo before the workshop begins.
|
||||
|
||||
## Registration
|
||||
|
||||
Limited seats available. Register early to secure your spot!
|
||||
@@ -33,7 +33,23 @@ const {
|
||||
timezone,
|
||||
tags,
|
||||
hideEditPost,
|
||||
} = post.data;
|
||||
// Champs spécifiques events
|
||||
eventDate,
|
||||
eventEndDate,
|
||||
location,
|
||||
registrationLink,
|
||||
// Champs spécifiques workshops
|
||||
workshopDate,
|
||||
duration,
|
||||
level,
|
||||
materials,
|
||||
// Champs spécifiques technical
|
||||
difficulty,
|
||||
readingTime,
|
||||
} = post.data as any;
|
||||
|
||||
// Détecter le type de collection
|
||||
const collection = post.collection;
|
||||
|
||||
const { Content } = await render(post);
|
||||
|
||||
@@ -103,10 +119,130 @@ const nextPost =
|
||||
class:list={[
|
||||
"max-sm:hidden",
|
||||
{ hidden: !SITE.editPost.enabled || hideEditPost },
|
||||
]}>|</span
|
||||
>
|
||||
]}>|
|
||||
</span>
|
||||
<EditPost {hideEditPost} {post} class="max-sm:hidden" />
|
||||
</div>
|
||||
|
||||
{/* Informations spécifiques selon la collection */}
|
||||
{collection === "events" && (
|
||||
<div class="my-4 p-4 bg-muted rounded-lg border border-border">
|
||||
<h3 class="text-sm font-semibold text-muted-foreground mb-2">📅 Détails de l'événement</h3>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 text-sm">
|
||||
{eventDate && (
|
||||
<div>
|
||||
<span class="font-medium">Date :</span>{" "}
|
||||
{new Date(eventDate).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{eventEndDate && (
|
||||
<div>
|
||||
<span class="font-medium">Fin :</span>{" "}
|
||||
{new Date(eventEndDate).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{location && (
|
||||
<div>
|
||||
<span class="font-medium">📍 Lieu :</span> {location}
|
||||
</div>
|
||||
)}
|
||||
{registrationLink && (
|
||||
<div>
|
||||
<a
|
||||
href={registrationLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-accent hover:underline"
|
||||
>
|
||||
📝 S'inscrire →
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{collection === "workshops" && (
|
||||
<div class="my-4 p-4 bg-muted rounded-lg border border-border">
|
||||
<h3 class="text-sm font-semibold text-muted-foreground mb-2">🎓 Détails de l'atelier</h3>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 text-sm">
|
||||
{workshopDate && (
|
||||
<div>
|
||||
<span class="font-medium">Date :</span>{" "}
|
||||
{new Date(workshopDate).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{duration && (
|
||||
<div>
|
||||
<span class="font-medium">⏱️ Durée :</span> {duration}
|
||||
</div>
|
||||
)}
|
||||
{level && (
|
||||
<div>
|
||||
<span class="font-medium">📊 Niveau :</span>{" "}
|
||||
<span class:list={[
|
||||
"px-2 py-0.5 rounded text-xs font-medium",
|
||||
level === "beginner" && "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200",
|
||||
level === "intermediate" && "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200",
|
||||
level === "advanced" && "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200",
|
||||
]}>
|
||||
{level === "beginner" && "Débutant"}
|
||||
{level === "intermediate" && "Intermédiaire"}
|
||||
{level === "advanced" && "Avancé"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{materials && (
|
||||
<div>
|
||||
<span class="font-medium">📚 Matériaux :</span> {materials}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{collection === "technical" && (
|
||||
<div class="my-4 p-4 bg-muted rounded-lg border border-border">
|
||||
<h3 class="text-sm font-semibold text-muted-foreground mb-2">🔧 Informations techniques</h3>
|
||||
<div class="flex flex-wrap gap-4 text-sm">
|
||||
{difficulty && (
|
||||
<div>
|
||||
<span class="font-medium">Niveau :</span>{" "}
|
||||
<span class:list={[
|
||||
"px-2 py-0.5 rounded text-xs font-medium",
|
||||
difficulty === "beginner" && "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200",
|
||||
difficulty === "intermediate" && "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200",
|
||||
difficulty === "advanced" && "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200",
|
||||
]}>
|
||||
{difficulty === "beginner" && "Débutant"}
|
||||
{difficulty === "intermediate" && "Intermédiaire"}
|
||||
{difficulty === "advanced" && "Avancé"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{readingTime && (
|
||||
<div>
|
||||
<span class="font-medium">⏱️ Temps de lecture :</span> {readingTime}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<article
|
||||
id="article"
|
||||
class="app-prose mt-8 w-full max-w-app prose-pre:bg-(--shiki-light-bg) dark:prose-pre:bg-(--shiki-dark-bg)"
|
||||
@@ -119,7 +255,7 @@ const nextPost =
|
||||
<EditPost class="sm:hidden" {hideEditPost} {post} />
|
||||
|
||||
<ul class="mt-4 mb-8 flex flex-wrap gap-4 sm:my-8">
|
||||
{tags.map(tag => <Tag tag={slugifyStr(tag)} tagName={tag} size="sm" />)}
|
||||
{tags.map((tag: string) => <Tag tag={slugifyStr(tag)} tagName={tag} size="sm" />)}
|
||||
</ul>
|
||||
|
||||
<BackToTopButton />
|
||||
|
||||
@@ -3,35 +3,46 @@ layout: ../layouts/AboutLayout.astro
|
||||
title: "About"
|
||||
---
|
||||
|
||||
AstroPaper is a minimal, accessible and SEO-friendly blog theme built with [Astro](https://astro.build/) and [Tailwind CSS](https://tailwindcss.com/).
|
||||
HEC IA is the artificial intelligence student association at HEC Paris, dedicated to promoting AI education, research, and innovation among students.
|
||||
|
||||

|
||||
## Our Mission
|
||||
|
||||
AstroPaper provides a solid foundation for blogs, or even portfolios\_ with full markdown support, built-in dark mode, and a clean layout that works out-of-the-box.
|
||||
We aim to:
|
||||
|
||||
The blog posts in this theme also serve as guides, docs or example articles\_ making AstroPaper a flexible starting point for your next content-driven site.
|
||||
- **Educate**: Provide accessible AI education through workshops, tutorials, and technical resources
|
||||
- **Connect**: Build a community of AI enthusiasts, students, and professionals
|
||||
- **Innovate**: Foster innovation through hackathons, projects, and research initiatives
|
||||
- **Bridge**: Connect students with industry leaders and academic researchers in AI
|
||||
|
||||
## Features
|
||||
## What We Offer
|
||||
|
||||
AstroPaper comes with a set of useful features that make content publishing easy and effective:
|
||||
### Events
|
||||
|
||||
- SEO-friendly
|
||||
- Fast performance
|
||||
- Light & dark mode
|
||||
- Highly customizable
|
||||
- Organizable blog posts
|
||||
- Responsive & accessible
|
||||
- Static search with [PageFind](https://pagefind.app/)
|
||||
- Automatic social image generation
|
||||
We organize regular events including conferences, speaker sessions, and networking opportunities with AI professionals and researchers.
|
||||
|
||||
and so much more.
|
||||
### Workshops
|
||||
|
||||
## Show your support
|
||||
Hands-on technical workshops covering topics from machine learning basics to advanced deep learning architectures.
|
||||
|
||||
If you like [AstroPaper](https://github.com/satnaing/astro-paper), consider giving it a star ⭐️.
|
||||
### News & Updates
|
||||
|
||||
Found a bug 🐛 or have an improvement ✨ in mind? Feel free to open an [issue](https://github.com/satnaing/astro-paper/issues), submit a [pull request](https://github.com/satnaing/astro-paper/pulls) or start a [discussion](https://github.com/satnaing/astro-paper/discussions).
|
||||
Stay informed about the latest developments in AI, both within our community and in the broader field.
|
||||
|
||||
If you find this theme helpful, you can also [sponsor me on GitHub](https://github.com/sponsors/satnaing) or [buy me a coffee](https://buymeacoffee.com/satnaing) to show your support — every penny counts.
|
||||
### Technical Deep Dives
|
||||
|
||||
Kyay zuu! 🙏🏼
|
||||
In-depth technical articles exploring AI concepts, algorithms, and implementations.
|
||||
|
||||
## Get Involved
|
||||
|
||||
Whether you're a beginner curious about AI or an experienced practitioner, there's a place for you in HEC IA. Join us for our events, contribute to our wiki, or reach out to learn more about getting involved.
|
||||
|
||||
## Contact Us
|
||||
|
||||
For questions, collaboration opportunities, or to get involved:
|
||||
|
||||
- Email: contact@hec-ia.com
|
||||
- Follow us on our social media channels
|
||||
|
||||
---
|
||||
|
||||
_Building the future of AI, one student at a time._
|
||||
|
||||
@@ -27,10 +27,19 @@ const featuredWorkshops = sortedWorkshops.filter(({ data }) => data.featured);
|
||||
const featuredNews = sortedNews.filter(({ data }) => data.featured);
|
||||
const featuredTechnical = sortedTechnical.filter(({ data }) => data.featured);
|
||||
|
||||
const recentEvents = sortedEvents.slice(0, SITE.postPerIndex);
|
||||
const recentWorkshops = sortedWorkshops.slice(0, SITE.postPerIndex);
|
||||
const recentNews = sortedNews.slice(0, SITE.postPerIndex);
|
||||
const recentTechnical = sortedTechnical.slice(0, SITE.postPerIndex);
|
||||
// Exclure les featured des recent pour éviter les doublons
|
||||
const recentEvents = sortedEvents
|
||||
.filter(event => !featuredEvents.find(f => f.id === event.id))
|
||||
.slice(0, SITE.postPerIndex);
|
||||
const recentWorkshops = sortedWorkshops
|
||||
.filter(workshop => !featuredWorkshops.find(f => f.id === workshop.id))
|
||||
.slice(0, SITE.postPerIndex);
|
||||
const recentNews = sortedNews
|
||||
.filter(newsItem => !featuredNews.find(f => f.id === newsItem.id))
|
||||
.slice(0, SITE.postPerIndex);
|
||||
const recentTechnical = sortedTechnical
|
||||
.filter(tech => !featuredTechnical.find(f => f.id === tech.id))
|
||||
.slice(0, SITE.postPerIndex);
|
||||
---
|
||||
|
||||
<Layout>
|
||||
|
||||
@@ -1,36 +1,71 @@
|
||||
import { BLOG_PATH } from "@/content.config";
|
||||
import {
|
||||
BLOG_PATH,
|
||||
EVENTS_PATH,
|
||||
WORKSHOPS_PATH,
|
||||
NEWS_PATH,
|
||||
TECHNICAL_PATH,
|
||||
} from "@/content.config";
|
||||
import { slugifyStr } from "./slugify";
|
||||
|
||||
const PATHS = [
|
||||
BLOG_PATH,
|
||||
EVENTS_PATH,
|
||||
WORKSHOPS_PATH,
|
||||
NEWS_PATH,
|
||||
TECHNICAL_PATH,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get full path of a blog post
|
||||
* @param id - id of the blog post (aka slug)
|
||||
* @param filePath - the blog post full file location
|
||||
* @param includeBase - whether to include `/posts` in return value
|
||||
* @returns blog post path
|
||||
* Get full path of a content entry
|
||||
* @param id - id of the entry (aka slug)
|
||||
* @param filePath - the entry full file location
|
||||
* @param includeBase - whether to include base path in return value
|
||||
* @returns entry path
|
||||
*/
|
||||
export function getPath(
|
||||
id: string,
|
||||
filePath: string | undefined,
|
||||
includeBase = true
|
||||
) {
|
||||
// Determine which collection path to remove
|
||||
let basePathToRemove = BLOG_PATH;
|
||||
for (const path of PATHS) {
|
||||
if (filePath?.includes(path)) {
|
||||
basePathToRemove = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const pathSegments = filePath
|
||||
?.replace(BLOG_PATH, "")
|
||||
?.replace(basePathToRemove, "")
|
||||
.split("/")
|
||||
.filter(path => path !== "") // remove empty string in the segments ["", "other-path"] <- empty string will be removed
|
||||
.filter(path => !path.startsWith("_")) // exclude directories start with underscore "_"
|
||||
.slice(0, -1) // remove the last segment_ file name_ since it's unnecessary
|
||||
.map(segment => slugifyStr(segment)); // slugify each segment path
|
||||
|
||||
const basePath = includeBase ? "/posts" : "";
|
||||
// Determine base path based on collection
|
||||
let basePath = "/posts";
|
||||
if (!includeBase) {
|
||||
basePath = "";
|
||||
} else if (filePath?.includes(EVENTS_PATH)) {
|
||||
basePath = "/events";
|
||||
} else if (filePath?.includes(WORKSHOPS_PATH)) {
|
||||
basePath = "/workshops";
|
||||
} else if (filePath?.includes(NEWS_PATH)) {
|
||||
basePath = "/news";
|
||||
} else if (filePath?.includes(TECHNICAL_PATH)) {
|
||||
basePath = "/technical";
|
||||
}
|
||||
|
||||
// Making sure `id` does not contain the directory
|
||||
const blogId = id.split("/");
|
||||
const slug = blogId.length > 0 ? blogId.slice(-1) : blogId;
|
||||
const entryId = id.split("/");
|
||||
const slug = entryId.length > 0 ? entryId.slice(-1) : entryId;
|
||||
|
||||
// If not inside the sub-dir, simply return the file path
|
||||
if (!pathSegments || pathSegments.length < 1) {
|
||||
return [basePath, slug].join("/");
|
||||
return [basePath, slug].filter(Boolean).join("/");
|
||||
}
|
||||
|
||||
return [basePath, ...pathSegments, slug].join("/");
|
||||
return [basePath, ...pathSegments, slug].filter(Boolean).join("/");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user