feat: copy button
This commit is contained in:
parent
1012f562ff
commit
4382f7165c
22 changed files with 197 additions and 752 deletions
|
@ -3,6 +3,7 @@ import mdx from '@astrojs/mdx'
|
|||
import react from '@astrojs/react'
|
||||
import sitemap from '@astrojs/sitemap'
|
||||
import tailwind from '@astrojs/tailwind'
|
||||
import { transformerCopyButton } from '@rehype-pretty/transformers'
|
||||
import {
|
||||
transformerMetaHighlight,
|
||||
transformerNotationDiff,
|
||||
|
@ -26,13 +27,6 @@ export default defineConfig({
|
|||
react(),
|
||||
],
|
||||
markdown: {
|
||||
// shikiConfig: {
|
||||
// transformers: [
|
||||
// transformerNotationDiff(),
|
||||
// transformerNotationFocus(),
|
||||
// transformerMetaHighlight(),
|
||||
// ],
|
||||
// },
|
||||
syntaxHighlight: false,
|
||||
rehypePlugins: [
|
||||
rehypeHeadingIds,
|
||||
|
@ -44,7 +38,14 @@ export default defineConfig({
|
|||
light: 'vitesse-light',
|
||||
dark: 'vitesse-dark',
|
||||
},
|
||||
transformers: [transformerNotationDiff(), transformerMetaHighlight()],
|
||||
transformers: [
|
||||
transformerNotationDiff(),
|
||||
transformerMetaHighlight(),
|
||||
transformerCopyButton({
|
||||
visibility: 'hover',
|
||||
feedbackDuration: 1000,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
|
|
9
package-lock.json
generated
9
package-lock.json
generated
|
@ -22,6 +22,7 @@
|
|||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@rehype-pretty/transformers": "^0.13.2",
|
||||
"@shikijs/transformers": "^1.16.3",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
|
@ -2344,6 +2345,14 @@
|
|||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
|
||||
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="
|
||||
},
|
||||
"node_modules/@rehype-pretty/transformers": {
|
||||
"version": "0.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@rehype-pretty/transformers/-/transformers-0.13.2.tgz",
|
||||
"integrity": "sha512-p2ciQSwqy5Ip8aNUa9q6rdS/hJZXrxHYYfDVOHvKOsBu3t9HDmQ65YX6r9Qbl19vi160OAxmGF7MIoCRDJrRhg==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz",
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@rehype-pretty/transformers": "^0.13.2",
|
||||
"@shikijs/transformers": "^1.16.3",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
|
|
|
@ -40,11 +40,11 @@ if (
|
|||
>
|
||||
<Link
|
||||
href={`/${entry.collection}/${entry.slug}`}
|
||||
class="flex flex-wrap gap-4"
|
||||
class="flex flex-col gap-4 sm:flex-row"
|
||||
>
|
||||
{
|
||||
entry.data.image && (
|
||||
<div class="flex-shrink-0">
|
||||
<div class="sm:flex-shrink-0">
|
||||
<Image
|
||||
src={entry.data.image}
|
||||
alt={entry.data.title}
|
||||
|
@ -63,7 +63,7 @@ if (
|
|||
{entry.data.description}
|
||||
</p>
|
||||
<div
|
||||
class="mb-2 flex items-center space-x-2 text-xs text-muted-foreground"
|
||||
class="mb-2 flex flex-wrap items-center gap-x-2 text-xs text-muted-foreground"
|
||||
>
|
||||
{
|
||||
author && (
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
---
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface Props {
|
||||
class?: string
|
||||
}
|
||||
|
||||
const { class: className } = Astro.props
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="mx-auto max-w-screen-md"><slot /></div>
|
||||
<div class={cn('mx-auto max-w-screen-md', className)}><slot /></div>
|
||||
|
|
|
@ -24,7 +24,7 @@ const {
|
|||
target={external ? '_blank' : '_self'}
|
||||
class={cn(
|
||||
'inline-block transition-colors duration-300 ease-in-out',
|
||||
underline && 'hover:underline hover:underline-offset-[3px]',
|
||||
underline && 'underline underline-offset-[3px]',
|
||||
className,
|
||||
)}
|
||||
data-heading={dataHeading}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
import { Hash } from 'lucide-react'
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
@ -29,7 +30,10 @@ export interface BadgeProps
|
|||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props}>
|
||||
<Hash className="mr-0.5 h-3 w-3" />
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
10
src/content/blog/2022.mdx
Normal file
10
src/content/blog/2022.mdx
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: '2022 Post'
|
||||
description: 'This a dummy post written in the year 2022.'
|
||||
date: '2022-01-01'
|
||||
tags: ['dummy', 'placeholder']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
This is a dummy post written in the year 2022.
|
10
src/content/blog/2023.mdx
Normal file
10
src/content/blog/2023.mdx
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: '2023 Post'
|
||||
description: 'This a dummy post written in the year 2023.'
|
||||
date: '2023-01-01'
|
||||
tags: ['dummy', 'placeholder']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
This is a dummy post written in the year 2023.
|
10
src/content/blog/2024.mdx
Normal file
10
src/content/blog/2024.mdx
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: '2024 Post'
|
||||
description: 'This a dummy post written in the year 2024.'
|
||||
date: '2024-01-01'
|
||||
tags: ['dummy', 'placeholder']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
This is a dummy post written in the year 2024.
|
|
@ -1,113 +0,0 @@
|
|||
---
|
||||
title: 'Blog Post'
|
||||
description: 'Features, enhancements, and changes.'
|
||||
date: '2022-07-25'
|
||||
tags: ['log', 'rss-feed']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
## h2 Heading
|
||||
|
||||
### h3 Heading
|
||||
|
||||
#### h4 Heading
|
||||
|
||||
##### h5 Heading
|
||||
|
||||
###### h6 Heading
|
||||
|
||||
## Emphasis
|
||||
|
||||
**This is bold text**
|
||||
|
||||
**This is bold text**
|
||||
|
||||
_This is italic text_
|
||||
|
||||
_This is italic text_
|
||||
|
||||
~~Strikethrough~~
|
||||
|
||||
## Blockquotes
|
||||
|
||||
> Blockquotes can also be nested...
|
||||
>
|
||||
> > ...by using additional greater-than signs right next to each other...
|
||||
> >
|
||||
> > > ...or with spaces between arrows.
|
||||
|
||||
## Lists
|
||||
|
||||
Unordered
|
||||
|
||||
- Create a list by starting a line with `+`, `-`, or `*`
|
||||
- Sub-lists are made by indenting 2 spaces:
|
||||
- Marker character change forces new list start:
|
||||
- Ac tristique libero volutpat at
|
||||
* Facilisis in pretium nisl aliquet
|
||||
- Nulla volutpat aliquam velit
|
||||
- Very easy!
|
||||
|
||||
Ordered
|
||||
|
||||
1. Lorem ipsum dolor sit amet
|
||||
2. Consectetur adipiscing elit
|
||||
3. Integer molestie lorem at massa
|
||||
|
||||
4. You can use sequential numbers...
|
||||
5. ...or keep all the numbers as `1.`
|
||||
|
||||
Start numbering with offset:
|
||||
|
||||
57. foo
|
||||
1. bar
|
||||
|
||||
## Code
|
||||
|
||||
Inline `code`
|
||||
|
||||
Indented code
|
||||
|
||||
// Some comments
|
||||
line 1 of code
|
||||
line 2 of code
|
||||
line 3 of code
|
||||
|
||||
Block code "fences"
|
||||
|
||||
```
|
||||
Sample text here...
|
||||
```
|
||||
|
||||
Syntax highlighting
|
||||
|
||||
```js
|
||||
var foo = function (bar) {
|
||||
return bar++
|
||||
}
|
||||
|
||||
console.log(foo(5))
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ------------------------------------------------------------------------- |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
Right aligned columns
|
||||
|
||||
| Option | Description |
|
||||
| -----: | ------------------------------------------------------------------------: |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
## Links
|
||||
|
||||
[link text](http://dev.nodeca.com)
|
||||
|
||||
[link with title](http://nodeca.github.io/pica/demo/ 'title text!')
|
|
@ -1,113 +0,0 @@
|
|||
---
|
||||
title: 'Blog Post'
|
||||
description: 'Features, enhancements, and changes.'
|
||||
date: '2023-07-25'
|
||||
tags: ['log', 'rss-feed']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
## h2 Heading
|
||||
|
||||
### h3 Heading
|
||||
|
||||
#### h4 Heading
|
||||
|
||||
##### h5 Heading
|
||||
|
||||
###### h6 Heading
|
||||
|
||||
## Emphasis
|
||||
|
||||
**This is bold text**
|
||||
|
||||
**This is bold text**
|
||||
|
||||
_This is italic text_
|
||||
|
||||
_This is italic text_
|
||||
|
||||
~~Strikethrough~~
|
||||
|
||||
## Blockquotes
|
||||
|
||||
> Blockquotes can also be nested...
|
||||
>
|
||||
> > ...by using additional greater-than signs right next to each other...
|
||||
> >
|
||||
> > > ...or with spaces between arrows.
|
||||
|
||||
## Lists
|
||||
|
||||
Unordered
|
||||
|
||||
- Create a list by starting a line with `+`, `-`, or `*`
|
||||
- Sub-lists are made by indenting 2 spaces:
|
||||
- Marker character change forces new list start:
|
||||
- Ac tristique libero volutpat at
|
||||
* Facilisis in pretium nisl aliquet
|
||||
- Nulla volutpat aliquam velit
|
||||
- Very easy!
|
||||
|
||||
Ordered
|
||||
|
||||
1. Lorem ipsum dolor sit amet
|
||||
2. Consectetur adipiscing elit
|
||||
3. Integer molestie lorem at massa
|
||||
|
||||
4. You can use sequential numbers...
|
||||
5. ...or keep all the numbers as `1.`
|
||||
|
||||
Start numbering with offset:
|
||||
|
||||
57. foo
|
||||
1. bar
|
||||
|
||||
## Code
|
||||
|
||||
Inline `code`
|
||||
|
||||
Indented code
|
||||
|
||||
// Some comments
|
||||
line 1 of code
|
||||
line 2 of code
|
||||
line 3 of code
|
||||
|
||||
Block code "fences"
|
||||
|
||||
```
|
||||
Sample text here...
|
||||
```
|
||||
|
||||
Syntax highlighting
|
||||
|
||||
```js
|
||||
var foo = function (bar) {
|
||||
return bar++
|
||||
}
|
||||
|
||||
console.log(foo(5))
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ------------------------------------------------------------------------- |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
Right aligned columns
|
||||
|
||||
| Option | Description |
|
||||
| -----: | ------------------------------------------------------------------------: |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
## Links
|
||||
|
||||
[link text](http://dev.nodeca.com)
|
||||
|
||||
[link with title](http://nodeca.github.io/pica/demo/ 'title text!')
|
|
@ -1,113 +0,0 @@
|
|||
---
|
||||
title: 'Blog Post'
|
||||
description: 'Features, enhancements, and changes.'
|
||||
date: '2024-07-25'
|
||||
tags: ['log', 'rss-feed']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
## h2 Heading
|
||||
|
||||
### h3 Heading
|
||||
|
||||
#### h4 Heading
|
||||
|
||||
##### h5 Heading
|
||||
|
||||
###### h6 Heading
|
||||
|
||||
## Emphasis
|
||||
|
||||
**This is bold text**
|
||||
|
||||
**This is bold text**
|
||||
|
||||
_This is italic text_
|
||||
|
||||
_This is italic text_
|
||||
|
||||
~~Strikethrough~~
|
||||
|
||||
## Blockquotes
|
||||
|
||||
> Blockquotes can also be nested...
|
||||
>
|
||||
> > ...by using additional greater-than signs right next to each other...
|
||||
> >
|
||||
> > > ...or with spaces between arrows.
|
||||
|
||||
## Lists
|
||||
|
||||
Unordered
|
||||
|
||||
- Create a list by starting a line with `+`, `-`, or `*`
|
||||
- Sub-lists are made by indenting 2 spaces:
|
||||
- Marker character change forces new list start:
|
||||
- Ac tristique libero volutpat at
|
||||
* Facilisis in pretium nisl aliquet
|
||||
- Nulla volutpat aliquam velit
|
||||
- Very easy!
|
||||
|
||||
Ordered
|
||||
|
||||
1. Lorem ipsum dolor sit amet
|
||||
2. Consectetur adipiscing elit
|
||||
3. Integer molestie lorem at massa
|
||||
|
||||
4. You can use sequential numbers...
|
||||
5. ...or keep all the numbers as `1.`
|
||||
|
||||
Start numbering with offset:
|
||||
|
||||
57. foo
|
||||
1. bar
|
||||
|
||||
## Code
|
||||
|
||||
Inline `code`
|
||||
|
||||
Indented code
|
||||
|
||||
// Some comments
|
||||
line 1 of code
|
||||
line 2 of code
|
||||
line 3 of code
|
||||
|
||||
Block code "fences"
|
||||
|
||||
```
|
||||
Sample text here...
|
||||
```
|
||||
|
||||
Syntax highlighting
|
||||
|
||||
```js
|
||||
var foo = function (bar) {
|
||||
return bar++
|
||||
}
|
||||
|
||||
console.log(foo(5))
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ------------------------------------------------------------------------- |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
Right aligned columns
|
||||
|
||||
| Option | Description |
|
||||
| -----: | ------------------------------------------------------------------------: |
|
||||
| data | path to data files to supply the data that will be passed into templates. |
|
||||
| engine | engine to be used for processing templates. Handlebars is the default. |
|
||||
| ext | extension to be used for dest files. |
|
||||
|
||||
## Links
|
||||
|
||||
[link text](http://dev.nodeca.com)
|
||||
|
||||
[link with title](http://nodeca.github.io/pica/demo/ 'title text!')
|
|
@ -1,312 +0,0 @@
|
|||
---
|
||||
title: 'Blog Post'
|
||||
description: 'Features, enhancements, and changes.'
|
||||
date: '2024-07-25'
|
||||
tags: ['log', 'rss-feed']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
```elixir title="fib.ex" showLineNumbers /palindrome/ {1-2, 5-8}
|
||||
defmodule Solution do
|
||||
@spec is_palindrome(x :: integer) :: boolean
|
||||
def is_palindrome(x) when x < 0, do: false
|
||||
def is_palindrome(x), do: do_is_palindrome(x, get_base_10(x, 1))
|
||||
|
||||
defp do_is_palindrome(x, b10) when b10 > 1,
|
||||
do: get_first_digit(x, b10) == rem(x, 10) and do_is_palindrome(div(x, 10), div(b10, 100))
|
||||
|
||||
defp do_is_palindrome(_, _), do: true
|
||||
|
||||
defp get_first_digit(n, b10), do: div(n, b10) |> rem(10)
|
||||
|
||||
defp get_base_10(n, b10) when n >= b10, do: get_base_10(n, b10 * 10)
|
||||
defp get_base_10(n, b10), do: div(b10, 10)
|
||||
end
|
||||
```
|
||||
|
||||
```tsx showLineNumbers
|
||||
console.log('Introduction.')
|
||||
console.log('Subtraction.') // [!code --]
|
||||
console.log('Addition.') // [!code ++]
|
||||
console.log('In focus.')
|
||||
console.log('End.')
|
||||
```
|
||||
|
||||
Here is a sample of some basic Markdown syntax that can be used when writing Markdown content in Astro.
|
||||
|
||||
## Headings
|
||||
|
||||
The following HTML `<h1>`—`<h6>` elements represent six levels of section headings. `<h1>` is the highest section level while `<h6>` is the lowest.
|
||||
|
||||
## H2
|
||||
|
||||
### H3
|
||||
|
||||
#### H4
|
||||
|
||||
##### H5
|
||||
|
||||
###### H6
|
||||
|
||||
## Paragraph
|
||||
|
||||
Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.
|
||||
|
||||
Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.
|
||||
|
||||
## Images
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||

|
||||
|
||||
## Blockquotes
|
||||
|
||||
The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
|
||||
|
||||
### Blockquote without attribution
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
|
||||
> **Note** that you can use _Markdown syntax_ within a blockquote.
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
|
||||
> **Note** that you can use _Markdown syntax_ within a blockquote.
|
||||
|
||||
### Blockquote with attribution
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
> Don't communicate by sharing memory, share memory by communicating.
|
||||
> — <cite>Rob Pike[^1]</cite>
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
> Don't communicate by sharing memory, share memory by communicating.
|
||||
> — <cite>Rob Pike[^1]</cite>
|
||||
|
||||
[^1]: The above quote is excerpted from Rob Pike's [talk](https://www.youtube.com/watch?v=PAAkCSZUG1c) during Gopherfest, November 18, 2015.
|
||||
|
||||
## Tables
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
| Italics | Bold | Code |
|
||||
| --------- | -------- | ------ |
|
||||
| _italics_ | **bold** | `code` |
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
| Italics | Bold | Code |
|
||||
| --------- | -------- | ------ |
|
||||
| _italics_ | **bold** | `code` |
|
||||
|
||||
## Code Blocks
|
||||
|
||||
#### Syntax
|
||||
|
||||
we can use 3 backticks ``` in new line and write snippet and close with 3 backticks on new line and to highlight language specific syntac, write one word of language name after first 3 backticks, for eg. html, javascript, css, markdown, typescript, txt, bash
|
||||
|
||||
````markdown
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
````
|
||||
|
||||
Output
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## List Types
|
||||
|
||||
### Ordered List
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
1. First item
|
||||
2. Second item
|
||||
3. Third item
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
1. First item
|
||||
2. Second item
|
||||
3. Third item
|
||||
|
||||
### Unordered List
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
- List item
|
||||
- Another item
|
||||
- And another item
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
- List item
|
||||
- Another item
|
||||
- And another item
|
||||
|
||||
### Nested list
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
- Fruit
|
||||
- Apple
|
||||
- Orange
|
||||
- Banana
|
||||
- Dairy
|
||||
- Milk
|
||||
- Cheese
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
- Fruit
|
||||
- Apple
|
||||
- Orange
|
||||
- Banana
|
||||
- Dairy
|
||||
- Milk
|
||||
- Cheese
|
||||
|
||||
## Other Elements — abbr, sub, sup, kbd, mark
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
|
||||
|
||||
H<sub>2</sub>O
|
||||
|
||||
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
|
||||
|
||||
Press <kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd> to end the session.
|
||||
|
||||
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
|
||||
|
||||
H<sub>2</sub>O
|
||||
|
||||
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
|
||||
|
||||
Press <kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd> to end the session.
|
||||
|
||||
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
|
||||
|
||||
```js title="/src/app/fetchMovie.js"
|
||||
const movies = await fetchMovies()
|
||||
```
|
||||
|
||||
```js caption="Fetch movies from our server"
|
||||
export async function fetchMovies() {
|
||||
const response = await fetch('http://example.com/movies.json')
|
||||
return await response.json()
|
||||
}
|
||||
```
|
||||
|
||||
```js {2-4}
|
||||
const movies = await fetchMovies()
|
||||
movies.map((movie, i) => {
|
||||
console.log(movie.title)
|
||||
})
|
||||
```
|
||||
|
||||
```js /movie.title/
|
||||
const movies = await fetchMovies()
|
||||
movies.map((movie, i) => {
|
||||
console.log(movie.title)
|
||||
})
|
||||
```
|
||||
|
||||
```js showLineNumbers
|
||||
const movies = await fetchMovies()
|
||||
movies.map((movie, i) => {
|
||||
console.log(movie.title)
|
||||
})
|
||||
```
|
58
src/content/blog/the-state-of-static-blogs.mdx
Normal file
58
src/content/blog/the-state-of-static-blogs.mdx
Normal file
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
title: 'The State of Static Blogs in 2024'
|
||||
description: 'There should not be a single reason why you would need a command palette search bar to find a blog post on your own site.'
|
||||
date: '2024-07-25'
|
||||
tags: ['webdev', 'opinion']
|
||||
image: '/1200x630.png'
|
||||
author: 'enscribe'
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Hello! My name is enscribe, and I'm a fullstack web developer who has been fiddling with blogging platforms for a couple of years now. I run a blog at [enscribe.dev](https://enscribe.dev), where I write about cybersecurity and the capture-the-flag (CTF) scene.
|
||||
|
||||
I have a lot of opinions about what makes a great blogging template. As a cumulative result of all the slop, bullshit, and outright terrible design decisions I've had to deal with working with various templates and frameworks, I bring you [astro-erudite](https://github.com/jktrn/astro-erudite), which should hopefully bring a better developer and user experience in terms of ease of use, customization, and performance.
|
||||
|
||||
astro-erudite is written in Astro, a framework hyperoptimized for static content such as blogs. Within the codebase of this template I've included many nuances that, in my opinion (and there will be many, many opinions here), make the developer experience significantly more pleasant. I've also _excluded_ many features that, frankly, you don't need.
|
||||
|
||||
### Welcoming some features
|
||||
|
||||
- [shadcn/ui](https://ui.shadcn.com) is a pretty controversial component library. I love it. The best part is arguably its take on [theming](https://ui.shadcn.com/docs/theming), which introduces a convention involving CSS colors such as `background` and `foreground` into your Tailwind configuration so that styling is a breeze. These classes also automatically adapt to the user's selected theme, and as such you don't need to worry about adding an equivalent `dark:` style to all of your theming. shadcn/ui turns `"bg-neutral-50 text-neutral-900 dark:bg-neutral-900 dark:text-neutral-50"` into `"bg-background text-foreground"`, both more semantic and easier to blanket edit (if you wanted to change all your blues in your site to indigos, you would need to go around every single class and change it rather than editing a single CSS variable). Other utiliy colors such as `secondary`, `muted`, `accent`, and `destructive` also exist and are very self-explanatory in name (and also have an equivalent `-foreground` class, e.g. `secondary-foreground`, which you can apply to text on top of these colors).
|
||||
- [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography) is a plugin that automatically styles any content surrounded by an `<article>{:html}` tag in a way which makes it readable and blog-post-friendly. It does this via a `prose` class which you can wrap anything with to style the interior content. This is especially useful for HTML you don't control, e.g. a post rendered from Markdown. Although your control over the rendering is a bit less fine-grained, you're also already using Tailwind so this right has long been forsaken.
|
||||
- [Shiki](https://github.com/shikijs/shiki) is a syntax highlighter for code blocks. Although Astro code blocks utilize Shiki under the hood, I've actually disabled the default code blocks in this template so that they don't collide with my preferred library [rehype-pretty-code](https://rehype-pretty.pages.dev), which is _also_ powered by Shiki but allows for line numbers, line highlighting, inline code snytax highlighting, and a transformers API for advanced customization such as manual `diff` visualization and line blurring. This library does not ship with any CSS, and it's up to you to style the code blocks and code block titles as you see fit. I've provided styles in `src/styles/global.css` within the `@layer components{:css}` directive if you wish to fiddle with them:
|
||||
|
||||
```css title="src/styles/global.css" caption="Styling code blocks using rehype-pretty-code (with a caption down here)" showLineNumbers{80} {10-12} /pre/
|
||||
@layer components {
|
||||
code[data-theme*=' '] span { /* [!code ++] */
|
||||
color: var(--shiki-light); /* [!code ++] */
|
||||
} /* [!code ++] */
|
||||
|
||||
.dark code[data-theme*=' '] span { /* [!code --] */
|
||||
color: var(--shiki-dark); /* [!code --] */
|
||||
} /* [!code --] */
|
||||
|
||||
pre {
|
||||
@apply max-h-[600px] overflow-auto rounded-xl border bg-secondary/20 p-4 text-sm leading-loose;
|
||||
}
|
||||
```
|
||||
|
||||
The following snippet was generated with this Markdown code:
|
||||
|
||||
````mdx
|
||||
```css title="src/styles/global.css" caption="Styling code blocks using rehype-pretty-code (with a caption down here)" showLineNumbers{80} {10-12} /pre/
|
||||
|
||||
```
|
||||
````
|
||||
|
||||
When I added those two diff additions and deletions, I simply added `/* [!code ++] */` and `/* [!code --] */` to the lines I wanted to highlight. Just use the comment syntax of whatever language you're attempting to highlight.
|
||||
|
||||
### Foregoing some slop
|
||||
|
||||
- Goodbye, [ESLint](https://eslint.org/)! There have been so many occasions where I've had to deal with blogging templates with in-built pre-commit hooks which enforce contrived and arbitrary linting rules that, frankly, I couldn't be bothered with. Obviously, linting is awesome for ensuring consistency and best practice, but that's with shared and large codebases. You're dealing with, at most, your mediocre MDX blog posts and some interior fetching. It's just not worth the hypertension.
|
||||
|
||||
- You really don't need analytics via [Umami](https://umami.is) or [Plausible](https://plausible.io). Let's be realistic: for many personal blogs, unless you're an anime profile picture Twitter microcelebrity and/or the daughter of Taylor Swift you don't need to know how many of your avid fans click Big Button A versus how many click Big Button B.
|
||||
- You really don't need a comments section via [Giscus](https://giscus.app). This opens up a can of worms involving the ability to spam comments and the necessity to moderate them. If you want organic discussion about your blog posts to happen, then share on social media and let people discuss there.
|
||||
- Speaking of sharing on social media, let's get rid of the share buttons. Please inform me of a single time you have used a share button on a blog post.
|
||||
- You really don't need a <abbr title="Content Management System">CMS</abbr> unless you have thousands of posts and/or are willing to navigate through a clunky management interface. Markdown and folders is really all you need, which you can organize to your preference via folder or file naming conventions.
|
||||
- If you have literally anything involving an `.env` file in a blog post, please reconsider what you are doing.
|
||||
- Please do not override the browser's <kbd>Ctrl</kbd> + <kbd>K</kbd> functionality to open up a command palette. There should not be a single reason why a user would use a small context menu to browse your blog over the `/blog` route. Most of the time, command palettes on sites do nothing more than regurgitate shortcuts that are already on the same page you're hiding with the palette's modal.
|
|
@ -8,8 +8,8 @@ export function cn(...inputs: ClassValue[]) {
|
|||
export function formatDate(date: Date) {
|
||||
return Intl.DateTimeFormat('en-US', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
}).format(date)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,11 +13,12 @@ import {
|
|||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb'
|
||||
import { HomeIcon } from 'lucide-react'
|
||||
import { cn } from '@lib/utils'
|
||||
---
|
||||
|
||||
<Layout title="404" description={SITE.DESCRIPTION}>
|
||||
<Container>
|
||||
<Breadcrumb className="mb-6">
|
||||
<Container class="flex grow flex-col gap-y-6">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/"
|
||||
|
@ -32,7 +33,7 @@ import { HomeIcon } from 'lucide-react'
|
|||
</Breadcrumb>
|
||||
|
||||
<section
|
||||
class="flex min-h-[50vh] flex-col items-center justify-center space-y-6 text-center"
|
||||
class="flex flex-col items-center justify-center gap-y-4 text-center"
|
||||
>
|
||||
<div class="max-w-md">
|
||||
<h1 class="mb-4 text-3xl font-bold">404: Page not found</h1>
|
||||
|
@ -40,16 +41,17 @@ import { HomeIcon } from 'lucide-react'
|
|||
Oops! The page you're looking for doesn't exist.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<Link
|
||||
href="/"
|
||||
class={buttonVariants({ variant: 'outline' }) + ' group'}
|
||||
>
|
||||
<span class="mr-1.5 transition-transform group-hover:-translate-x-1"
|
||||
>←</span
|
||||
> Go to home page
|
||||
</Link>
|
||||
</div>
|
||||
<Link
|
||||
href="/"
|
||||
class={cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'flex gap-x-1.5 group',
|
||||
)}
|
||||
>
|
||||
<span class="transition-transform group-hover:-translate-x-1"
|
||||
>←</span
|
||||
> Go to home page
|
||||
</Link>
|
||||
</section>
|
||||
</Container>
|
||||
</Layout>
|
||||
|
|
|
@ -15,8 +15,8 @@ import { HomeIcon } from 'lucide-react'
|
|||
---
|
||||
|
||||
<Layout title="About" description={SITE.DESCRIPTION}>
|
||||
<Container>
|
||||
<Breadcrumb className="mb-6">
|
||||
<Container class="flex flex-col gap-y-6">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/"
|
||||
|
@ -30,7 +30,7 @@ import { HomeIcon } from 'lucide-react'
|
|||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
|
||||
<section class="space-y-6">
|
||||
<section>
|
||||
<div class="min-w-full">
|
||||
<h1 class="mb-4 text-3xl font-bold">Some more about us</h1>
|
||||
<p class="prose dark:prose-invert">
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb'
|
||||
import { HomeIcon } from 'lucide-react'
|
||||
import { Hash, HomeIcon } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import Link from '@components/Link.astro'
|
||||
|
@ -156,6 +156,7 @@ if (
|
|||
href={`/tags/${tag}`}
|
||||
class={badgeVariants({ variant: 'secondary' })}
|
||||
>
|
||||
<Hash className="mr-0.5 h-3 w-3" />
|
||||
{tag}
|
||||
</a>
|
||||
))
|
||||
|
|
|
@ -34,8 +34,8 @@ const years = Object.keys(posts).sort((a, b) => parseInt(b) - parseInt(a))
|
|||
---
|
||||
|
||||
<Layout title="Blog" description="Blog">
|
||||
<Container>
|
||||
<Breadcrumb className="mb-6">
|
||||
<Container class="flex flex-col gap-y-6">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="/"
|
||||
|
@ -49,25 +49,23 @@ const years = Object.keys(posts).sort((a, b) => parseInt(b) - parseInt(a))
|
|||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
|
||||
<div class="space-y-10">
|
||||
<div class="space-y-4">
|
||||
{
|
||||
years.map((year) => (
|
||||
<section class="space-y-4">
|
||||
<div class="font-semibold">{year}</div>
|
||||
<div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
{posts[year].map((post) => (
|
||||
<li>
|
||||
<BlogCard entry={post} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-8">
|
||||
{
|
||||
years.map((year) => (
|
||||
<section class="flex flex-col gap-y-4">
|
||||
<div class="font-semibold">{year}</div>
|
||||
<div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
{posts[year].map((post) => (
|
||||
<li>
|
||||
<BlogCard entry={post} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Container>
|
||||
</Layout>
|
||||
|
|
|
@ -21,31 +21,32 @@ const blog = (await getCollection('blog'))
|
|||
---
|
||||
|
||||
<Layout title="Home" description="Home">
|
||||
<Container>
|
||||
<section class="space-y-6">
|
||||
<Container class="flex flex-col gap-y-6">
|
||||
<section>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-3xl">er·u·dite</CardTitle>
|
||||
<CardDescription
|
||||
>/ˈer(y)əˌdīt/ • <span
|
||||
class="font-semibold text-muted-foreground">adjective</span
|
||||
>/ˈer(y)əˌdīt/ • <span class="font-semibold">adjective</span
|
||||
></CardDescription
|
||||
>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>having or showing great knowledge or learning.</p>
|
||||
<p class="text-sm">
|
||||
astro-erudite is an opinionated, no-frills static blogging template
|
||||
built with <Link href="https://astro.build" external underline
|
||||
>Astro</Link
|
||||
>. To learn more about why this template exists, read <Link
|
||||
href="/blog/the-state-of-static-blogs"
|
||||
underline>The State of Static Blogs in 2024</Link
|
||||
>.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div class="mt-8">
|
||||
<p class="prose min-w-full dark:prose-invert">
|
||||
{SITE.TITLE} is an opinionated, no-frills static blogging template built
|
||||
with Astro.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<h2 class="text-2xl font-bold">Latest posts</h2>
|
||||
</div>
|
||||
<ul class="not-prose flex flex-col gap-4">
|
||||
</section>
|
||||
<section class="flex flex-col gap-y-4">
|
||||
<h2 class="text-2xl font-bold">Latest posts</h2>
|
||||
<ul class="not-prose flex flex-col gap-y-4">
|
||||
{
|
||||
blog.map((post) => (
|
||||
<li>
|
||||
|
|
|
@ -2,34 +2,6 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--astro-code-color-text: #09090b;
|
||||
--astro-code-color-background: #fafafa;
|
||||
--astro-code-token-comment: #a19595;
|
||||
--astro-code-token-keyword: #f47067;
|
||||
--astro-code-token-string: #00a99a;
|
||||
--astro-code-token-function: #429996;
|
||||
--astro-code-token-constant: #2b70c5;
|
||||
--astro-code-token-parameter: #4e8fdf;
|
||||
--astro-code-token-string-expression: #ae42a0;
|
||||
--astro-code-token-punctuation: #8996a3;
|
||||
--astro-code-token-link: #8d85ff;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--astro-code-color-text: #fafafa;
|
||||
--astro-code-color-background: #09090b;
|
||||
--astro-code-token-comment: #a19595;
|
||||
--astro-code-token-keyword: #f47067;
|
||||
--astro-code-token-string: #00a99a;
|
||||
--astro-code-token-function: #6eafad;
|
||||
--astro-code-token-constant: #b3cceb;
|
||||
--astro-code-token-parameter: #4e8fdf;
|
||||
--astro-code-token-string-expression: #bf7db6;
|
||||
--astro-code-token-punctuation: #8996a3;
|
||||
--astro-code-token-link: #8d85ff;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
|
@ -103,6 +75,10 @@
|
|||
html.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
@ -115,11 +91,12 @@
|
|||
}
|
||||
|
||||
pre {
|
||||
@apply max-h-[600px] overflow-auto rounded-xl border bg-secondary/20 p-4 text-sm leading-loose;
|
||||
@apply max-h-[600px] overflow-auto rounded-xl border bg-secondary/20 py-4 text-sm leading-loose;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
counter-reset: line;
|
||||
@apply whitespace-pre-wrap;
|
||||
}
|
||||
|
||||
code[data-line-numbers] {
|
||||
|
@ -132,6 +109,10 @@
|
|||
@apply mr-4 inline-block w-4 text-right text-muted-foreground;
|
||||
}
|
||||
|
||||
pre > code > span[data-line] {
|
||||
@apply px-4;
|
||||
}
|
||||
|
||||
[data-highlighted-line] {
|
||||
@apply !bg-foreground/10;
|
||||
}
|
||||
|
@ -155,4 +136,8 @@
|
|||
.diff.remove {
|
||||
@apply bg-destructive/15;
|
||||
}
|
||||
|
||||
article :not(pre) > code {
|
||||
@apply relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-medium;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue