src
components
content
embeds
layouts
···
20
20
- ✅ Tags for posts
21
21
- ✅ Super easy to deploy as a static site
22
22
- ✅ Includes some prebuilt components for you to use
23
23
-
- ✅ Easy to edit using [pagecms](https://pagescms.org) or by editing the markdown directly
23
23
+
- ✅ Easy to edit by editing the markdown directly
24
24
25
25
## tutorials
26
26
···
38
38
39
39
2. In your repository settings, set up github pages to deploy using github actions (*SETTINGS* -> *PAGES* -> *SOURCE*: **Github Actions**)
40
40
41
41
-
3. Your blog should be live in about 1 minute at `https://<your-github-username>.github.io/<your-repo-name>`
42
42
-
43
43
-
For editing the blog you can either edit the code directly or use the preconfigured [pagescms](https://next.pagescms.org).
44
44
-
45
45
-
### Editing with pagescms
46
46
-
47
47
-
4. Go to [pagescms](https://next.pagescms.org) and log in with your github account and authorize the app.
48
48
-
49
49
-
5. Add your repository to pagescms and open it.
50
50
-
51
51
-
6. Now you can edit your website configuration, about page, description and blog posts directly in the browser.
52
52
-
53
53
-
7. Your changes will be automatically pushed to your repository and deployed to github pages in about 1 minute everytime you hit save.
41
41
+
3. Set up your blog info in `src/config.json` (most importantly change `SITE` to your deployment url, e.g. for github pages `https://<your-github-username>.github.io/` and `BASE` to your base path, e.g. for github pages `/<your-repo-name>`)
54
42
55
55
-
### Editing the code
56
56
-
57
57
-
4. Set up your blog info in `src/config.json` (leaving SITE, BASE, and MANUAL_SITE_BASE, as they are).
43
43
+
4. Your blog should be live in about 1 minute at `https://<your-github-username>.github.io/<your-repo-name>`
58
44
59
45
5. Add your blog posts in `src/content/blog/`
60
46
···
10
10
11
11
import { BASE, SITE } from "./src/config.json";
12
12
13
13
+
import customEmbeds from 'astro-custom-embeds';
14
14
+
15
15
+
import { transformerMetaHighlight, transformerNotationHighlight } from '@shikijs/transformers'
16
16
+
17
17
+
import LinkCardEmbed from './src/embeds/link-card/embed'
18
18
+
import YoutubeEmbed from './src/embeds/youtube/embed'
19
19
+
13
20
// https://astro.build/config
14
21
export default defineConfig({
15
22
vite: {
···
33
40
},
34
41
},
35
42
36
36
-
integrations: [mdx(), sitemap(), tailwind(), svelte()],
43
43
+
integrations: [customEmbeds({
44
44
+
embeds: [
45
45
+
YoutubeEmbed,
46
46
+
LinkCardEmbed,
47
47
+
48
48
+
]
49
49
+
}), mdx(), sitemap(), tailwind(), svelte()],
37
50
38
51
markdown: {
39
52
shikiConfig: {
···
45
58
light: "github-light",
46
59
dark: "github-dark",
47
60
},
61
61
+
defaultColor: false,
62
62
+
transformers: [transformerMetaHighlight(), transformerNotationHighlight()],
63
63
+
wrap: true
48
64
},
49
65
},
50
66
···
10
10
"license": "MIT",
11
11
"dependencies": {
12
12
"@ascorbic/bluesky-loader": "^0.0.3",
13
13
+
"@astro-community/astro-embed-utils": "^0.1.3",
13
14
"@astrojs/check": "^0.9.4",
14
15
"@astrojs/mdx": "^3.1.9",
15
16
"@astrojs/rss": "^4.0.9",
16
17
"@astrojs/sitemap": "^3.2.1",
17
18
"@astrojs/svelte": "^6.0.2",
18
19
"@astrojs/tailwind": "^5.1.2",
20
20
+
"@shikijs/transformers": "^1.24.2",
19
21
"@tailwindcss/forms": "^0.5.9",
20
22
"astro": "^4.16.14",
23
23
+
"astro-custom-embeds": "^0.0.1",
21
24
"astro-og-canvas": "^0.5.4",
25
25
+
"lite-youtube-embed": "^0.3.3",
22
26
"svelte": "^5.1.1",
23
27
"tailwind-merge": "^2.5.4",
24
28
"tailwindcss": "^3.4.14",
···
72
76
"integrity": "sha512-pg43g83gojVtEsAkXfjWuzJhuXneJp4wM/leBftGkCPV3yxKgB92EWA+nWu735BgbBMph3P7DrVqVc3ikt+dJA==",
73
77
"peerDependencies": {
74
78
"astro": "^4.14.0 || ^5.0.0-beta.0"
79
79
+
}
80
80
+
},
81
81
+
"node_modules/@astro-community/astro-embed-utils": {
82
82
+
"version": "0.1.3",
83
83
+
"resolved": "https://registry.npmjs.org/@astro-community/astro-embed-utils/-/astro-embed-utils-0.1.3.tgz",
84
84
+
"integrity": "sha512-eiMO+vfCdE9GtW6qE7X5Xl6YCKZDCoXJEWqRofQcoC3GHjqN2/WhJlnaxNVRq3demSO03UNtho57Em5p7o7AOA==",
85
85
+
"dependencies": {
86
86
+
"linkedom": "^0.14.26"
75
87
}
76
88
},
77
89
"node_modules/@astrojs/check": {
···
1815
1827
]
1816
1828
},
1817
1829
"node_modules/@shikijs/core": {
1818
1818
-
"version": "1.23.1",
1819
1819
-
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.23.1.tgz",
1820
1820
-
"integrity": "sha512-NuOVgwcHgVC6jBVH5V7iblziw6iQbWWHrj5IlZI3Fqu2yx9awH7OIQkXIcsHsUmY19ckwSgUMgrqExEyP5A0TA==",
1830
1830
+
"version": "1.24.2",
1831
1831
+
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.24.2.tgz",
1832
1832
+
"integrity": "sha512-BpbNUSKIwbKrRRA+BQj0BEWSw+8kOPKDJevWeSE/xIqGX7K0xrCZQ9kK0nnEQyrzsUoka1l81ZtJ2mGaCA32HQ==",
1821
1833
"dependencies": {
1822
1822
-
"@shikijs/engine-javascript": "1.23.1",
1823
1823
-
"@shikijs/engine-oniguruma": "1.23.1",
1824
1824
-
"@shikijs/types": "1.23.1",
1834
1834
+
"@shikijs/engine-javascript": "1.24.2",
1835
1835
+
"@shikijs/engine-oniguruma": "1.24.2",
1836
1836
+
"@shikijs/types": "1.24.2",
1825
1837
"@shikijs/vscode-textmate": "^9.3.0",
1826
1838
"@types/hast": "^3.0.4",
1827
1839
"hast-util-to-html": "^9.0.3"
1828
1840
}
1829
1841
},
1830
1842
"node_modules/@shikijs/engine-javascript": {
1831
1831
-
"version": "1.23.1",
1832
1832
-
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.23.1.tgz",
1833
1833
-
"integrity": "sha512-i/LdEwT5k3FVu07SiApRFwRcSJs5QM9+tod5vYCPig1Ywi8GR30zcujbxGQFJHwYD7A5BUqagi8o5KS+LEVgBg==",
1843
1843
+
"version": "1.24.2",
1844
1844
+
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.24.2.tgz",
1845
1845
+
"integrity": "sha512-EqsmYBJdLEwEiO4H+oExz34a5GhhnVp+jH9Q/XjPjmBPc6TE/x4/gD0X3i0EbkKKNqXYHHJTJUpOLRQNkEzS9Q==",
1834
1846
"dependencies": {
1835
1835
-
"@shikijs/types": "1.23.1",
1847
1847
+
"@shikijs/types": "1.24.2",
1836
1848
"@shikijs/vscode-textmate": "^9.3.0",
1837
1837
-
"oniguruma-to-es": "0.4.1"
1849
1849
+
"oniguruma-to-es": "0.7.0"
1838
1850
}
1839
1851
},
1840
1852
"node_modules/@shikijs/engine-oniguruma": {
1841
1841
-
"version": "1.23.1",
1842
1842
-
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.23.1.tgz",
1843
1843
-
"integrity": "sha512-KQ+lgeJJ5m2ISbUZudLR1qHeH3MnSs2mjFg7bnencgs5jDVPeJ2NVDJ3N5ZHbcTsOIh0qIueyAJnwg7lg7kwXQ==",
1853
1853
+
"version": "1.24.2",
1854
1854
+
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.2.tgz",
1855
1855
+
"integrity": "sha512-ZN6k//aDNWRJs1uKB12pturKHh7GejKugowOFGAuG7TxDRLod1Bd5JhpOikOiFqPmKjKEPtEA6mRCf7q3ulDyQ==",
1844
1856
"dependencies": {
1845
1845
-
"@shikijs/types": "1.23.1",
1857
1857
+
"@shikijs/types": "1.24.2",
1846
1858
"@shikijs/vscode-textmate": "^9.3.0"
1847
1859
}
1848
1860
},
1861
1861
+
"node_modules/@shikijs/transformers": {
1862
1862
+
"version": "1.24.2",
1863
1863
+
"resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.24.2.tgz",
1864
1864
+
"integrity": "sha512-cIwn8YSwO3bsWKJ+pezcXY1Vq0BVwvuLes1TZSC5+Awi6Tsfqhf3vBahOIqZK1rraMKOti2VEAEF/95oXMig1w==",
1865
1865
+
"dependencies": {
1866
1866
+
"shiki": "1.24.2"
1867
1867
+
}
1868
1868
+
},
1849
1869
"node_modules/@shikijs/types": {
1850
1850
-
"version": "1.23.1",
1851
1851
-
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.23.1.tgz",
1852
1852
-
"integrity": "sha512-98A5hGyEhzzAgQh2dAeHKrWW4HfCMeoFER2z16p5eJ+vmPeF6lZ/elEne6/UCU551F/WqkopqRsr1l2Yu6+A0g==",
1870
1870
+
"version": "1.24.2",
1871
1871
+
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.2.tgz",
1872
1872
+
"integrity": "sha512-bdeWZiDtajGLG9BudI0AHet0b6e7FbR0EsE4jpGaI0YwHm/XJunI9+3uZnzFtX65gsyJ6ngCIWUfA4NWRPnBkQ==",
1853
1873
"dependencies": {
1854
1874
"@shikijs/vscode-textmate": "^9.3.0",
1855
1875
"@types/hast": "^3.0.4"
···
2399
2419
"sharp": "^0.33.3"
2400
2420
}
2401
2421
},
2422
2422
+
"node_modules/astro-auto-import": {
2423
2423
+
"version": "0.4.4",
2424
2424
+
"resolved": "https://registry.npmjs.org/astro-auto-import/-/astro-auto-import-0.4.4.tgz",
2425
2425
+
"integrity": "sha512-tiYe1hp+VusdiyaD3INgZgbvXEPamDFiURnQR5Niz+E9fWa6IHYjJ99TwGlHh/evfaXE/U/86jp9MRKWTuJU1A==",
2426
2426
+
"dependencies": {
2427
2427
+
"@types/node": "^18.0.0",
2428
2428
+
"acorn": "^8.8.0"
2429
2429
+
},
2430
2430
+
"engines": {
2431
2431
+
"node": ">=16.0.0"
2432
2432
+
},
2433
2433
+
"peerDependencies": {
2434
2434
+
"astro": "^2.0.0 || ^3.0.0-beta || ^4.0.0-beta || ^5.0.0-beta"
2435
2435
+
}
2436
2436
+
},
2437
2437
+
"node_modules/astro-auto-import/node_modules/@types/node": {
2438
2438
+
"version": "18.19.67",
2439
2439
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.67.tgz",
2440
2440
+
"integrity": "sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==",
2441
2441
+
"dependencies": {
2442
2442
+
"undici-types": "~5.26.4"
2443
2443
+
}
2444
2444
+
},
2445
2445
+
"node_modules/astro-auto-import/node_modules/undici-types": {
2446
2446
+
"version": "5.26.5",
2447
2447
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
2448
2448
+
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
2449
2449
+
},
2450
2450
+
"node_modules/astro-custom-embeds": {
2451
2451
+
"version": "0.0.1",
2452
2452
+
"resolved": "https://registry.npmjs.org/astro-custom-embeds/-/astro-custom-embeds-0.0.1.tgz",
2453
2453
+
"integrity": "sha512-8f5lYAImE38VC1ZAAQ8S2BCEa6vSBIeph+aPEW+uX10FIfvgAJ7Na+Q2cmMGoMG+p0dQ+1mEDtzvzlIAO04MSg==",
2454
2454
+
"dependencies": {
2455
2455
+
"@types/unist": "^2.0.0",
2456
2456
+
"astro-auto-import": "^0.4.2",
2457
2457
+
"remark-directive": "^3.0.0",
2458
2458
+
"unist-util-select": "^4.0.1",
2459
2459
+
"unist-util-visit": "^5.0.0"
2460
2460
+
},
2461
2461
+
"peerDependencies": {
2462
2462
+
"astro": "^4.0.0 || ^5.0.0-beta"
2463
2463
+
}
2464
2464
+
},
2465
2465
+
"node_modules/astro-custom-embeds/node_modules/@types/unist": {
2466
2466
+
"version": "2.0.11",
2467
2467
+
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
2468
2468
+
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="
2469
2469
+
},
2402
2470
"node_modules/astro-og-canvas": {
2403
2471
"version": "0.5.4",
2404
2472
"resolved": "https://registry.npmjs.org/astro-og-canvas/-/astro-og-canvas-0.5.4.tgz",
···
2493
2561
"funding": {
2494
2562
"url": "https://github.com/sponsors/sindresorhus"
2495
2563
}
2564
2564
+
},
2565
2565
+
"node_modules/boolbase": {
2566
2566
+
"version": "1.0.0",
2567
2567
+
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
2568
2568
+
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
2496
2569
},
2497
2570
"node_modules/boxen": {
2498
2571
"version": "8.0.1",
···
2945
3018
"node": ">= 8"
2946
3019
}
2947
3020
},
3021
3021
+
"node_modules/css-select": {
3022
3022
+
"version": "5.1.0",
3023
3023
+
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
3024
3024
+
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
3025
3025
+
"dependencies": {
3026
3026
+
"boolbase": "^1.0.0",
3027
3027
+
"css-what": "^6.1.0",
3028
3028
+
"domhandler": "^5.0.2",
3029
3029
+
"domutils": "^3.0.1",
3030
3030
+
"nth-check": "^2.0.1"
3031
3031
+
},
3032
3032
+
"funding": {
3033
3033
+
"url": "https://github.com/sponsors/fb55"
3034
3034
+
}
3035
3035
+
},
3036
3036
+
"node_modules/css-selector-parser": {
3037
3037
+
"version": "1.4.1",
3038
3038
+
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz",
3039
3039
+
"integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g=="
3040
3040
+
},
3041
3041
+
"node_modules/css-what": {
3042
3042
+
"version": "6.1.0",
3043
3043
+
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
3044
3044
+
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
3045
3045
+
"engines": {
3046
3046
+
"node": ">= 6"
3047
3047
+
},
3048
3048
+
"funding": {
3049
3049
+
"url": "https://github.com/sponsors/fb55"
3050
3050
+
}
3051
3051
+
},
2948
3052
"node_modules/cssesc": {
2949
3053
"version": "3.0.0",
2950
3054
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
···
2955
3059
"engines": {
2956
3060
"node": ">=4"
2957
3061
}
3062
3062
+
},
3063
3063
+
"node_modules/cssom": {
3064
3064
+
"version": "0.5.0",
3065
3065
+
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
3066
3066
+
"integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="
2958
3067
},
2959
3068
"node_modules/debug": {
2960
3069
"version": "4.3.7",
···
3060
3169
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
3061
3170
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
3062
3171
},
3172
3172
+
"node_modules/dom-serializer": {
3173
3173
+
"version": "2.0.0",
3174
3174
+
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
3175
3175
+
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
3176
3176
+
"dependencies": {
3177
3177
+
"domelementtype": "^2.3.0",
3178
3178
+
"domhandler": "^5.0.2",
3179
3179
+
"entities": "^4.2.0"
3180
3180
+
},
3181
3181
+
"funding": {
3182
3182
+
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
3183
3183
+
}
3184
3184
+
},
3185
3185
+
"node_modules/domelementtype": {
3186
3186
+
"version": "2.3.0",
3187
3187
+
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
3188
3188
+
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
3189
3189
+
"funding": [
3190
3190
+
{
3191
3191
+
"type": "github",
3192
3192
+
"url": "https://github.com/sponsors/fb55"
3193
3193
+
}
3194
3194
+
]
3195
3195
+
},
3196
3196
+
"node_modules/domhandler": {
3197
3197
+
"version": "5.0.3",
3198
3198
+
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
3199
3199
+
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
3200
3200
+
"dependencies": {
3201
3201
+
"domelementtype": "^2.3.0"
3202
3202
+
},
3203
3203
+
"engines": {
3204
3204
+
"node": ">= 4"
3205
3205
+
},
3206
3206
+
"funding": {
3207
3207
+
"url": "https://github.com/fb55/domhandler?sponsor=1"
3208
3208
+
}
3209
3209
+
},
3210
3210
+
"node_modules/domutils": {
3211
3211
+
"version": "3.1.0",
3212
3212
+
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
3213
3213
+
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
3214
3214
+
"dependencies": {
3215
3215
+
"dom-serializer": "^2.0.0",
3216
3216
+
"domelementtype": "^2.3.0",
3217
3217
+
"domhandler": "^5.0.3"
3218
3218
+
},
3219
3219
+
"funding": {
3220
3220
+
"url": "https://github.com/fb55/domutils?sponsor=1"
3221
3221
+
}
3222
3222
+
},
3063
3223
"node_modules/dset": {
3064
3224
"version": "3.1.4",
3065
3225
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
···
3844
4004
"url": "https://github.com/sponsors/wooorm"
3845
4005
}
3846
4006
},
4007
4007
+
"node_modules/htmlparser2": {
4008
4008
+
"version": "8.0.2",
4009
4009
+
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
4010
4010
+
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
4011
4011
+
"funding": [
4012
4012
+
"https://github.com/fb55/htmlparser2?sponsor=1",
4013
4013
+
{
4014
4014
+
"type": "github",
4015
4015
+
"url": "https://github.com/sponsors/fb55"
4016
4016
+
}
4017
4017
+
],
4018
4018
+
"dependencies": {
4019
4019
+
"domelementtype": "^2.3.0",
4020
4020
+
"domhandler": "^5.0.3",
4021
4021
+
"domutils": "^3.0.1",
4022
4022
+
"entities": "^4.4.0"
4023
4023
+
}
4024
4024
+
},
3847
4025
"node_modules/http-cache-semantics": {
3848
4026
"version": "4.1.1",
3849
4027
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
···
4175
4353
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
4176
4354
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
4177
4355
},
4356
4356
+
"node_modules/linkedom": {
4357
4357
+
"version": "0.14.26",
4358
4358
+
"resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.14.26.tgz",
4359
4359
+
"integrity": "sha512-mK6TrydfFA7phrnp+1j57ycBwFI5bGSW6YXlw9acHoqF+mP/y+FooEYYyniOt5Ot57FSKB3iwmnuQ1UUyNLm5A==",
4360
4360
+
"dependencies": {
4361
4361
+
"css-select": "^5.1.0",
4362
4362
+
"cssom": "^0.5.0",
4363
4363
+
"html-escaper": "^3.0.3",
4364
4364
+
"htmlparser2": "^8.0.1",
4365
4365
+
"uhyphen": "^0.2.0"
4366
4366
+
}
4367
4367
+
},
4368
4368
+
"node_modules/lite-youtube-embed": {
4369
4369
+
"version": "0.3.3",
4370
4370
+
"resolved": "https://registry.npmjs.org/lite-youtube-embed/-/lite-youtube-embed-0.3.3.tgz",
4371
4371
+
"integrity": "sha512-gFfVVnj6NRjxVfJKo3qoLtpi0v5mn3AcR4eKD45wrxQuxzveFJUb+7Cr6uV6n+DjO8X3p0UzPPquhGt0H/y+NA=="
4372
4372
+
},
4178
4373
"node_modules/load-yaml-file": {
4179
4374
"version": "0.2.0",
4180
4375
"resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz",
···
4345
4540
"@types/mdast": "^4.0.0",
4346
4541
"@types/unist": "^3.0.0",
4347
4542
"unist-util-visit": "^5.0.0"
4543
4543
+
},
4544
4544
+
"funding": {
4545
4545
+
"type": "opencollective",
4546
4546
+
"url": "https://opencollective.com/unified"
4547
4547
+
}
4548
4548
+
},
4549
4549
+
"node_modules/mdast-util-directive": {
4550
4550
+
"version": "3.0.0",
4551
4551
+
"resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz",
4552
4552
+
"integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==",
4553
4553
+
"dependencies": {
4554
4554
+
"@types/mdast": "^4.0.0",
4555
4555
+
"@types/unist": "^3.0.0",
4556
4556
+
"devlop": "^1.0.0",
4557
4557
+
"mdast-util-from-markdown": "^2.0.0",
4558
4558
+
"mdast-util-to-markdown": "^2.0.0",
4559
4559
+
"parse-entities": "^4.0.0",
4560
4560
+
"stringify-entities": "^4.0.0",
4561
4561
+
"unist-util-visit-parents": "^6.0.0"
4348
4562
},
4349
4563
"funding": {
4350
4564
"type": "opencollective",
···
4707
4921
"micromark-util-types": "^2.0.0"
4708
4922
}
4709
4923
},
4924
4924
+
"node_modules/micromark-extension-directive": {
4925
4925
+
"version": "3.0.2",
4926
4926
+
"resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz",
4927
4927
+
"integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==",
4928
4928
+
"dependencies": {
4929
4929
+
"devlop": "^1.0.0",
4930
4930
+
"micromark-factory-space": "^2.0.0",
4931
4931
+
"micromark-factory-whitespace": "^2.0.0",
4932
4932
+
"micromark-util-character": "^2.0.0",
4933
4933
+
"micromark-util-symbol": "^2.0.0",
4934
4934
+
"micromark-util-types": "^2.0.0",
4935
4935
+
"parse-entities": "^4.0.0"
4936
4936
+
},
4937
4937
+
"funding": {
4938
4938
+
"type": "opencollective",
4939
4939
+
"url": "https://opencollective.com/unified"
4940
4940
+
}
4941
4941
+
},
4710
4942
"node_modules/micromark-extension-gfm": {
4711
4943
"version": "3.0.0",
4712
4944
"resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
···
5488
5720
"node": ">=0.10.0"
5489
5721
}
5490
5722
},
5723
5723
+
"node_modules/nth-check": {
5724
5724
+
"version": "2.1.1",
5725
5725
+
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
5726
5726
+
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
5727
5727
+
"dependencies": {
5728
5728
+
"boolbase": "^1.0.0"
5729
5729
+
},
5730
5730
+
"funding": {
5731
5731
+
"url": "https://github.com/fb55/nth-check?sponsor=1"
5732
5732
+
}
5733
5733
+
},
5491
5734
"node_modules/object-assign": {
5492
5735
"version": "4.1.1",
5493
5736
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
···
5519
5762
}
5520
5763
},
5521
5764
"node_modules/oniguruma-to-es": {
5522
5522
-
"version": "0.4.1",
5523
5523
-
"resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.4.1.tgz",
5524
5524
-
"integrity": "sha512-rNcEohFz095QKGRovP/yqPIKc+nP+Sjs4YTHMv33nMePGKrq/r2eu9Yh4646M5XluGJsUnmwoXuiXE69KDs+fQ==",
5765
5765
+
"version": "0.7.0",
5766
5766
+
"resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.7.0.tgz",
5767
5767
+
"integrity": "sha512-HRaRh09cE0gRS3+wi2zxekB+I5L8C/gN60S+vb11eADHUaB/q4u8wGGOX3GvwvitG8ixaeycZfeoyruKQzUgNg==",
5525
5768
"dependencies": {
5526
5769
"emoji-regex-xs": "^1.0.0",
5527
5527
-
"regex": "^5.0.0",
5528
5528
-
"regex-recursion": "^4.2.1"
5770
5770
+
"regex": "^5.0.2",
5771
5771
+
"regex-recursion": "^4.3.0"
5529
5772
}
5530
5773
},
5531
5774
"node_modules/ora": {
···
6112
6355
}
6113
6356
},
6114
6357
"node_modules/regex-recursion": {
6115
6115
-
"version": "4.2.1",
6116
6116
-
"resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-4.2.1.tgz",
6117
6117
-
"integrity": "sha512-QHNZyZAeKdndD1G3bKAbBEKOSSK4KOHQrAJ01N1LJeb0SoH4DJIeFhp0uUpETgONifS4+P3sOgoA1dhzgrQvhA==",
6358
6358
+
"version": "4.3.0",
6359
6359
+
"resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-4.3.0.tgz",
6360
6360
+
"integrity": "sha512-5LcLnizwjcQ2ALfOj95MjcatxyqF5RPySx9yT+PaXu3Gox2vyAtLDjHB8NTJLtMGkvyau6nI3CfpwFCjPUIs/A==",
6118
6361
"dependencies": {
6119
6362
"regex-utilities": "^2.3.0"
6120
6363
}
···
6195
6438
"url": "https://opencollective.com/unified"
6196
6439
}
6197
6440
},
6441
6441
+
"node_modules/remark-directive": {
6442
6442
+
"version": "3.0.0",
6443
6443
+
"resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.0.tgz",
6444
6444
+
"integrity": "sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==",
6445
6445
+
"dependencies": {
6446
6446
+
"@types/mdast": "^4.0.0",
6447
6447
+
"mdast-util-directive": "^3.0.0",
6448
6448
+
"micromark-extension-directive": "^3.0.0",
6449
6449
+
"unified": "^11.0.0"
6450
6450
+
},
6451
6451
+
"funding": {
6452
6452
+
"type": "opencollective",
6453
6453
+
"url": "https://opencollective.com/unified"
6454
6454
+
}
6455
6455
+
},
6198
6456
"node_modules/remark-gfm": {
6199
6457
"version": "4.0.0",
6200
6458
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz",
···
6545
6803
}
6546
6804
},
6547
6805
"node_modules/shiki": {
6548
6548
-
"version": "1.23.1",
6549
6549
-
"resolved": "https://registry.npmjs.org/shiki/-/shiki-1.23.1.tgz",
6550
6550
-
"integrity": "sha512-8kxV9TH4pXgdKGxNOkrSMydn1Xf6It8lsle0fiqxf7a1149K1WGtdOu3Zb91T5r1JpvRPxqxU3C2XdZZXQnrig==",
6806
6806
+
"version": "1.24.2",
6807
6807
+
"resolved": "https://registry.npmjs.org/shiki/-/shiki-1.24.2.tgz",
6808
6808
+
"integrity": "sha512-TR1fi6mkRrzW+SKT5G6uKuc32Dj2EEa7Kj0k8kGqiBINb+C1TiflVOiT9ta6GqOJtC4fraxO5SLUaKBcSY38Fg==",
6551
6809
"dependencies": {
6552
6552
-
"@shikijs/core": "1.23.1",
6553
6553
-
"@shikijs/engine-javascript": "1.23.1",
6554
6554
-
"@shikijs/engine-oniguruma": "1.23.1",
6555
6555
-
"@shikijs/types": "1.23.1",
6810
6810
+
"@shikijs/core": "1.24.2",
6811
6811
+
"@shikijs/engine-javascript": "1.24.2",
6812
6812
+
"@shikijs/engine-oniguruma": "1.24.2",
6813
6813
+
"@shikijs/types": "1.24.2",
6556
6814
"@shikijs/vscode-textmate": "^9.3.0",
6557
6815
"@types/hast": "^3.0.4"
6558
6816
}
···
7095
7353
"semver": "^7.3.8"
7096
7354
}
7097
7355
},
7356
7356
+
"node_modules/uhyphen": {
7357
7357
+
"version": "0.2.0",
7358
7358
+
"resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz",
7359
7359
+
"integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="
7360
7360
+
},
7098
7361
"node_modules/uint8arrays": {
7099
7362
"version": "3.0.0",
7100
7363
"resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz",
···
7200
7463
"type": "opencollective",
7201
7464
"url": "https://opencollective.com/unified"
7202
7465
}
7466
7466
+
},
7467
7467
+
"node_modules/unist-util-select": {
7468
7468
+
"version": "4.0.3",
7469
7469
+
"resolved": "https://registry.npmjs.org/unist-util-select/-/unist-util-select-4.0.3.tgz",
7470
7470
+
"integrity": "sha512-1074+K9VyR3NyUz3lgNtHKm7ln+jSZXtLJM4E22uVuoFn88a/Go2pX8dusrt/W+KWH1ncn8jcd8uCQuvXb/fXA==",
7471
7471
+
"dependencies": {
7472
7472
+
"@types/unist": "^2.0.0",
7473
7473
+
"css-selector-parser": "^1.0.0",
7474
7474
+
"nth-check": "^2.0.0",
7475
7475
+
"zwitch": "^2.0.0"
7476
7476
+
},
7477
7477
+
"funding": {
7478
7478
+
"type": "opencollective",
7479
7479
+
"url": "https://opencollective.com/unified"
7480
7480
+
}
7481
7481
+
},
7482
7482
+
"node_modules/unist-util-select/node_modules/@types/unist": {
7483
7483
+
"version": "2.0.11",
7484
7484
+
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
7485
7485
+
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="
7203
7486
},
7204
7487
"node_modules/unist-util-stringify-position": {
7205
7488
"version": "4.0.0",
···
12
12
},
13
13
"dependencies": {
14
14
"@ascorbic/bluesky-loader": "^0.0.3",
15
15
+
"@astro-community/astro-embed-utils": "^0.1.3",
15
16
"@astrojs/check": "^0.9.4",
16
17
"@astrojs/mdx": "^3.1.9",
17
18
"@astrojs/rss": "^4.0.9",
18
19
"@astrojs/sitemap": "^3.2.1",
19
20
"@astrojs/svelte": "^6.0.2",
20
21
"@astrojs/tailwind": "^5.1.2",
22
22
+
"@shikijs/transformers": "^1.24.2",
21
23
"@tailwindcss/forms": "^0.5.9",
22
24
"astro": "^4.16.14",
25
25
+
"astro-custom-embeds": "^0.0.1",
23
26
"astro-og-canvas": "^0.5.4",
27
27
+
"lite-youtube-embed": "^0.3.3",
24
28
"svelte": "^5.1.1",
25
29
"tailwind-merge": "^2.5.4",
26
30
"tailwindcss": "^3.4.14",
···
1
1
{
2
2
+
"SITE": "https://flo-bit.dev",
3
3
+
"BASE": "/blog-template",
2
4
"SITE_TITLE": "Blog template",
3
5
"SITE_NAME": "blog template",
4
6
"SITE_DESCRIPTION": "Welcome to my website!",
···
20
22
"EMAIL": "flo.bit.dev@gmail.com",
21
23
"BLUESKY_URL": "https://bsky.app/profile/flo-bit.dev"
22
24
},
23
23
-
"BLUESKY_IDENTIFIER": "flo-bit.dev",
24
24
-
"SITE": "https://flo-bit.dev",
25
25
-
"BASE": "/blog-template"
25
25
+
"BLUESKY_IDENTIFIER": "flo-bit.dev"
26
26
}
···
1
1
---
2
2
title: "Adding blog posts directly"
3
3
-
description: "Learn how to add content to your blog directly in the code (without using pagescms)"
3
3
+
description: "Learn how to add content to your blog directly in the code"
4
4
pubDate: "Oct 24 2024"
5
5
published: true
6
6
heroImage: "/src/assets/blog-placeholder-4.jpg"
···
4
4
pubDate: "Nov 22 2024"
5
5
published: true
6
6
heroImage: "/src/assets/blog-placeholder-3.jpg"
7
7
-
tags: ["setup"]
7
7
+
tags: ["setup", "bluesky"]
8
8
disableComments: false
9
9
---
10
10
···
1
1
+
---
2
2
+
title: "Showing code"
3
3
+
description: "Learn how to show code in your blog posts"
4
4
+
pubDate: "Dec 10 2024"
5
5
+
published: false
6
6
+
heroImage: "/src/assets/blog-placeholder-4.jpg"
7
7
+
tags: ["code", "markdown"]
8
8
+
---
9
9
+
10
10
+
11
11
+
this is some `inline code`
12
12
+
13
13
+
this is some multiline code:
14
14
+
15
15
+
```js {1,2}
16
16
+
const a = 1;
17
17
+
18
18
+
console.log(a);
19
19
+
20
20
+
const b = "this is a string";
21
21
+
22
22
+
console.log(b);// [!code highlight]
23
23
+
24
24
+
function test() {
25
25
+
const test = "this is a very long string, that should hopefully wrap onto multiple lines"; // [!code highlight]
26
26
+
27
27
+
console.log(test);
28
28
+
}
29
29
+
```
···
1
1
+
---
2
2
+
title: "Showing embeds"
3
3
+
description: "Learn how to show embeds in your blog posts"
4
4
+
pubDate: "Dec 11 2024"
5
5
+
published: true
6
6
+
heroImage: "/src/assets/blog-placeholder-3.jpg"
7
7
+
tags: ["embeds"]
8
8
+
---
9
9
+
10
10
+
you can show embeds in your blog posts for youtube and link cards.
11
11
+
12
12
+
simply write a youtube url in your post like this (with nothing else on the same line):
13
13
+
14
14
+
```mdx
15
15
+
https://www.youtube.com/watch?v=dQw4w9WgXcQ
16
16
+
```
17
17
+
18
18
+
https://www.youtube.com/watch?v=dQw4w9WgXcQ
19
19
+
20
20
+
or any other url for a link card like this (again with nothing else on the same line):
21
21
+
22
22
+
```mdx
23
23
+
https://flo-bit.dev
24
24
+
```
25
25
+
26
26
+
https://flo-bit.dev
···
1
1
+
2
2
+
import LinkCard from './link-card/LinkCard.astro';
3
3
+
import YouTube from './youtube/Youtube.astro';
4
4
+
5
5
+
export { LinkCard, YouTube };
···
1
1
+
---
2
2
+
import { cn } from "../../utils";
3
3
+
import { parseOpenGraph } from "./lib";
4
4
+
import type { HTMLAttributes } from "astro/types";
5
5
+
6
6
+
export type Props = HTMLAttributes<"a"> & {
7
7
+
showMedia?: boolean;
8
8
+
showDescription?: boolean;
9
9
+
showTitle?: boolean;
10
10
+
showDomain?: boolean;
11
11
+
12
12
+
showGradient?: boolean;
13
13
+
14
14
+
blurMedia?: boolean;
15
15
+
16
16
+
id?: string;
17
17
+
18
18
+
title?: string;
19
19
+
description?: string;
20
20
+
};
21
21
+
22
22
+
const {
23
23
+
href,
24
24
+
id,
25
25
+
class: className,
26
26
+
showDescription = false,
27
27
+
showTitle = true,
28
28
+
showDomain = true,
29
29
+
showMedia = true,
30
30
+
showGradient = true,
31
31
+
target = "_blank",
32
32
+
title,
33
33
+
description,
34
34
+
...props
35
35
+
} = Astro.props;
36
36
+
37
37
+
const meta = await parseOpenGraph((href ?? id)?.toString() ?? "");
38
38
+
39
39
+
if (title && meta) meta.title = title;
40
40
+
if (description && meta) meta.description = description;
41
41
+
42
42
+
const domain = meta?.url ? new URL(meta.url).hostname.replace("www.", "") : "";
43
43
+
---
44
44
+
45
45
+
<article
46
46
+
class={cn(
47
47
+
"group max-w-lg my-6 relative isolate flex flex-col justify-end overflow-hidden not-prose aspect-[1200/630] rounded-xl border border-base-300 dark:border-base-700 bg-base-200/50 dark:bg-base-950/50 p-4",
48
48
+
className
49
49
+
)}
50
50
+
{...Astro.props}
51
51
+
>
52
52
+
{
53
53
+
!showMedia ? null : meta?.video && meta.videoType ? (
54
54
+
<video
55
55
+
autoplay
56
56
+
loop
57
57
+
muted
58
58
+
preload="metadata"
59
59
+
width="1200"
60
60
+
height="630"
61
61
+
class="absolute inset-0 w-full h-full object-cover -z-10 rounded-xl"
62
62
+
>
63
63
+
<source src={meta.video} type={meta.videoType} />
64
64
+
</video>
65
65
+
) : meta?.image ? (
66
66
+
<img
67
67
+
src={meta.image}
68
68
+
alt={meta.imageAlt || ""}
69
69
+
width="1200"
70
70
+
height="630"
71
71
+
class="absolute inset-0 w-full h-full object-cover -z-10 rounded-xl"
72
72
+
/>
73
73
+
) : null
74
74
+
}
75
75
+
76
76
+
{
77
77
+
showGradient ? (
78
78
+
<div class="absolute inset-0 -z-10 bg-gradient-to-t from-base-200/90 rounded-xl to-base-200/40 dark:from-base-950 dark:to-base-950/60" />
79
79
+
) : null
80
80
+
}
81
81
+
82
82
+
{
83
83
+
showDomain ? (
84
84
+
<div class="text-xs dark:text-accent-500 text-accent-600 font-bold">
85
85
+
{domain ?? ""}
86
86
+
</div>
87
87
+
) : null
88
88
+
}
89
89
+
<div class="text-lg font-semibold text-base-950 dark:text-base-50">
90
90
+
<a href={href ?? id} {...props} {target}>
91
91
+
<span class="absolute inset-0"></span>
92
92
+
<div class={showTitle ? "" : "sr-only"}>{meta?.title ?? href}</div>
93
93
+
</a>
94
94
+
</div>
95
95
+
{
96
96
+
showDescription ? (
97
97
+
<div class="text-xs dark:text-base-300 text-base-700 line-clamp-2">
98
98
+
{meta?.description ?? ""}
99
99
+
</div>
100
100
+
) : null
101
101
+
}
102
102
+
</article>
···
1
1
+
import type { EmbedsOption } from "astro-custom-embeds";
2
2
+
import LinkMatcher from "./matcher";
3
3
+
4
4
+
const LinkCardEmbed: EmbedsOption = {
5
5
+
componentName: 'LinkCard',
6
6
+
urlMatcher: LinkMatcher,
7
7
+
directiveName: 'link',
8
8
+
importPath: 'src/embeds',
9
9
+
}
10
10
+
11
11
+
export default LinkCardEmbed;
···
1
1
+
import { safeGetDOM } from '@astro-community/astro-embed-utils';
2
2
+
3
3
+
/** Helper to get the `content` attribute of an element. */
4
4
+
const getContent = (el: Element | null) => el?.getAttribute('content');
5
5
+
/** Helper to filter out insecure or non-absolute URLs. */
6
6
+
const urlOrNull = (url: string | null | undefined) =>
7
7
+
url?.slice(0, 8) === 'https://' ? url : null;
8
8
+
9
9
+
/**
10
10
+
* Loads and parses an HTML page to return Open Graph metadata.
11
11
+
* @param pageUrl URL to parse
12
12
+
*/
13
13
+
export async function parseOpenGraph(pageUrl: string) {
14
14
+
const html = await safeGetDOM(pageUrl);
15
15
+
if (!html) return;
16
16
+
17
17
+
const getMetaProperty = (prop: string) =>
18
18
+
getContent(html.querySelector(`meta[property=${JSON.stringify(prop)}]`));
19
19
+
const getMetaName = (name: string) =>
20
20
+
getContent(html.querySelector(`meta[name=${JSON.stringify(name)}]`));
21
21
+
22
22
+
const title =
23
23
+
getMetaProperty('og:title') || html.querySelector('title')?.textContent;
24
24
+
const description =
25
25
+
getMetaProperty('og:description') || getMetaName('description');
26
26
+
const image = urlOrNull(
27
27
+
getMetaProperty('og:image:secure_url') ||
28
28
+
getMetaProperty('og:image:url') ||
29
29
+
getMetaProperty('og:image')
30
30
+
);
31
31
+
const imageAlt = getMetaProperty('og:image:alt');
32
32
+
const video = urlOrNull(
33
33
+
getMetaProperty('og:video:secure_url') ||
34
34
+
getMetaProperty('og:video:url') ||
35
35
+
getMetaProperty('og:video')
36
36
+
);
37
37
+
const videoType = getMetaProperty('og:video:type');
38
38
+
const url =
39
39
+
urlOrNull(
40
40
+
getMetaProperty('og:url') ||
41
41
+
html.querySelector("link[rel='canonical']")?.getAttribute('href')
42
42
+
) || pageUrl;
43
43
+
44
44
+
return { title, description, image, imageAlt, url, video, videoType };
45
45
+
}
···
1
1
+
// Matches any HTTPS URL
2
2
+
const urlPattern = /(https:\/\/[^\s\\]+)/;
3
3
+
4
4
+
// Function to return only the URL part
5
5
+
export default function urlMatcher(url: string): string | undefined {
6
6
+
const match = url.match(urlPattern);
7
7
+
return match?.[0];
8
8
+
}
···
1
1
+
import YoutubeMatcher from './youtube/matcher.ts';
2
2
+
import LinkMatcher from './link-card/matcher.ts';
3
3
+
export { YoutubeMatcher, LinkMatcher };
···
1
1
+
---
2
2
+
import "lite-youtube-embed/src/lite-yt-embed.css";
3
3
+
import urlMatcher from "./matcher";
4
4
+
5
5
+
export interface Props extends astroHTML.JSX.HTMLAttributes {
6
6
+
id: string;
7
7
+
poster?: string;
8
8
+
posterQuality?: "max" | "high" | "default" | "low";
9
9
+
params?: string;
10
10
+
playlabel?: string;
11
11
+
}
12
12
+
13
13
+
const {
14
14
+
id,
15
15
+
poster,
16
16
+
posterQuality = "default",
17
17
+
title,
18
18
+
...attrs
19
19
+
} = Astro.props as Props;
20
20
+
21
21
+
const idRegExp = /^[A-Za-z0-9-_]+$/;
22
22
+
23
23
+
function extractID(idOrUrl: string) {
24
24
+
if (idRegExp.test(idOrUrl)) return idOrUrl;
25
25
+
return urlMatcher(idOrUrl);
26
26
+
}
27
27
+
28
28
+
const videoid = extractID(id);
29
29
+
const posterFile =
30
30
+
{
31
31
+
max: "maxresdefault",
32
32
+
high: "sddefault",
33
33
+
default: "hqdefault",
34
34
+
low: "default",
35
35
+
}[posterQuality] || "hqdefault";
36
36
+
const posterURL =
37
37
+
poster || `https://i.ytimg.com/vi/${videoid}/${posterFile}.jpg`;
38
38
+
const href = `https://youtube.com/watch?v=${videoid}`;
39
39
+
---
40
40
+
41
41
+
<lite-youtube
42
42
+
{videoid}
43
43
+
{title}
44
44
+
data-title={title}
45
45
+
{...attrs}
46
46
+
style={`background-image: url('${posterURL}');`}
47
47
+
>
48
48
+
<a {href} class="lty-playbtn">
49
49
+
<span class="lyt-visually-hidden">{attrs.playlabel}</span>
50
50
+
</a>
51
51
+
</lite-youtube>
52
52
+
53
53
+
<script src="lite-youtube-embed"></script>
54
54
+
55
55
+
<style is:global>
56
56
+
lite-youtube > iframe {
57
57
+
all: unset !important;
58
58
+
width: 100% !important;
59
59
+
height: 100% !important;
60
60
+
position: absolute !important;
61
61
+
inset: 0 !important;
62
62
+
border: 0 !important;
63
63
+
}
64
64
+
65
65
+
lite-youtube > .lty-playbtn {
66
66
+
@apply fill-amber-500;
67
67
+
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 48"><path d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z" fill="red"/><path d="M45 24 27 14v20" fill="white"/></svg>');
68
68
+
}
69
69
+
</style>
···
1
1
+
import type { EmbedsOption } from "astro-custom-embeds";
2
2
+
import YoutubeMatcher from "./matcher";
3
3
+
4
4
+
const YoutubeEmbed: EmbedsOption = {
5
5
+
componentName: 'YouTube',
6
6
+
urlArgument: 'id',
7
7
+
urlMatcher: YoutubeMatcher,
8
8
+
directiveName: 'youtube',
9
9
+
importPath: 'src/embeds',
10
10
+
}
11
11
+
12
12
+
export default YoutubeEmbed;
···
1
1
+
// Thanks to eleventy-plugin-youtube-embed
2
2
+
// https://github.com/gfscott/eleventy-plugin-youtube-embed/blob/main/lib/extractMatches.js
3
3
+
const urlPattern =
4
4
+
/(?=(\s*))\1(?:<a [^>]*?>)??(?=(\s*))\2(?:https?:\/\/)??(?:w{3}\.)??(?:youtube\.com|youtu\.be)\/(?:watch\?v=|embed\/|shorts\/)??([A-Za-z0-9-_]{11})(?:[^\s<>]*)(?=(\s*))\4(?:<\/a>)??(?=(\s*))\5/;
5
5
+
6
6
+
/**
7
7
+
* Extract a YouTube ID from a URL if it matches the pattern.
8
8
+
* @param url URL to test
9
9
+
* @returns A YouTube video ID or undefined if none matched
10
10
+
*/
11
11
+
export default function matcher(url: string): string | undefined {
12
12
+
const match = url.match(urlPattern);
13
13
+
return match?.[3];
14
14
+
}
···
27
27
<style is:global>
28
28
html.dark .astro-code,
29
29
html.dark .astro-code span {
30
30
-
color: var(--shiki-dark) !important;
31
31
-
/* Optional, if you also want font styles */
32
32
-
font-style: var(--shiki-dark-font-style) !important;
33
33
-
font-weight: var(--shiki-dark-font-weight) !important;
34
34
-
text-decoration: var(--shiki-dark-text-decoration) !important;
30
30
+
color: var(--shiki-dark);
31
31
+
}
32
32
+
33
33
+
html:not(dark) .astro-code,
34
34
+
html:not(dark) .astro-code span {
35
35
+
color: var(--shiki-light);
35
36
}
36
37
37
38
pre {
38
38
-
border: 1px solid rgba(0, 0, 0, 0.1);
39
39
-
background-color: rgba(0, 0, 0, 0.03) !important;
40
40
-
border-radius: 0.75rem !important;
39
39
+
@apply overflow-hidden !important;
40
40
+
@apply border border-base-300 dark:border-base-700 bg-base-200/50 dark:bg-base-950/30 rounded-xl !important;
41
41
}
42
42
43
43
-
html.dark pre {
44
44
-
border: 1px solid rgba(255, 255, 255, 0.1);
45
45
-
background-color: rgba(255, 255, 255, 0.05) !important;
46
46
-
border-radius: 0.75rem !important;
43
43
+
.line.highlighted {
44
44
+
margin: 0 -24px;
45
45
+
padding: 0 24px;
46
46
+
position: relative;
47
47
+
48
48
+
display: inline-block;
49
49
+
width: calc(100% + 48px);
50
50
+
}
51
51
+
.line.highlighted {
52
52
+
@apply bg-accent-500/10 dark:bg-accent-700/10;
53
53
+
}
54
54
+
55
55
+
::selection {
56
56
+
@apply bg-accent-300/50 dark:bg-accent-900/40;
57
57
+
}
58
58
+
59
59
+
lite-youtube {
60
60
+
@apply rounded-xl border border-base-300 dark:border-base-700;
61
61
+
}
62
62
+
63
63
+
.prose mark {
64
64
+
@apply bg-accent-200 dark:bg-accent-900 text-black dark:text-white;
47
65
}
48
66
</style>
49
67
</head>