···2020- ✅ Tags for posts
2121- ✅ Super easy to deploy as a static site
2222- ✅ Includes some prebuilt components for you to use
2323-- ✅ Easy to edit using [pagecms](https://pagescms.org) or by editing the markdown directly
2323+- ✅ Easy to edit by editing the markdown directly
24242525## tutorials
2626···383839392. In your repository settings, set up github pages to deploy using github actions (*SETTINGS* -> *PAGES* -> *SOURCE*: **Github Actions**)
40404141-3. Your blog should be live in about 1 minute at `https://<your-github-username>.github.io/<your-repo-name>`
4141+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>`)
42424343-For editing the blog you can either edit the code directly or use the preconfigured [pagescms](https://next.pagescms.org).
4444-4545-### Editing with pagescms
4646-4747-4. Go to [pagescms](https://next.pagescms.org) and log in with your github account and authorize the app.
4848-4949-5. Add your repository to pagescms and open it.
5050-5151-6. Now you can edit your website configuration, about page, description and blog posts directly in the browser.
5252-5353-7. Your changes will be automatically pushed to your repository and deployed to github pages in about 1 minute everytime you hit save.
5454-5555-### Editing the code
5656-5757-4. Set up your blog info in `src/config.json` (leaving SITE, BASE, and MANUAL_SITE_BASE, as they are).
4343+4. Your blog should be live in about 1 minute at `https://<your-github-username>.github.io/<your-repo-name>`
584459455. Add your blog posts in `src/content/blog/`
6046
···11---
22title: "Adding blog posts directly"
33-description: "Learn how to add content to your blog directly in the code (without using pagescms)"
33+description: "Learn how to add content to your blog directly in the code"
44pubDate: "Oct 24 2024"
55published: true
66heroImage: "/src/assets/blog-placeholder-4.jpg"
+1-1
src/content/blog/comments-via-bluesky.mdx
···44pubDate: "Dec 02 2024"
55published: true
66heroImage: "/src/assets/blog-placeholder-1.jpg"
77-tags: ["setup"]
77+tags: ["setup", "bluesky"]
88---
991010You can also show comments on your blog posts using bluesky.
···11+---
22+title: "Showing code"
33+description: "Learn how to show code in your blog posts"
44+pubDate: "Dec 10 2024"
55+published: false
66+heroImage: "/src/assets/blog-placeholder-4.jpg"
77+tags: ["code", "markdown"]
88+---
99+1010+1111+this is some `inline code`
1212+1313+this is some multiline code:
1414+1515+```js {1,2}
1616+const a = 1;
1717+1818+console.log(a);
1919+2020+const b = "this is a string";
2121+2222+console.log(b);// [!code highlight]
2323+2424+function test() {
2525+ const test = "this is a very long string, that should hopefully wrap onto multiple lines"; // [!code highlight]
2626+2727+ console.log(test);
2828+}
2929+```
+26
src/content/blog/showing-embeds.mdx
···11+---
22+title: "Showing embeds"
33+description: "Learn how to show embeds in your blog posts"
44+pubDate: "Dec 11 2024"
55+published: true
66+heroImage: "/src/assets/blog-placeholder-3.jpg"
77+tags: ["embeds"]
88+---
99+1010+you can show embeds in your blog posts for youtube and link cards.
1111+1212+simply write a youtube url in your post like this (with nothing else on the same line):
1313+1414+```mdx
1515+https://www.youtube.com/watch?v=dQw4w9WgXcQ
1616+```
1717+1818+https://www.youtube.com/watch?v=dQw4w9WgXcQ
1919+2020+or any other url for a link card like this (again with nothing else on the same line):
2121+2222+```mdx
2323+https://flo-bit.dev
2424+```
2525+2626+https://flo-bit.dev
···11+import { safeGetDOM } from '@astro-community/astro-embed-utils';
22+33+/** Helper to get the `content` attribute of an element. */
44+const getContent = (el: Element | null) => el?.getAttribute('content');
55+/** Helper to filter out insecure or non-absolute URLs. */
66+const urlOrNull = (url: string | null | undefined) =>
77+ url?.slice(0, 8) === 'https://' ? url : null;
88+99+/**
1010+ * Loads and parses an HTML page to return Open Graph metadata.
1111+ * @param pageUrl URL to parse
1212+ */
1313+export async function parseOpenGraph(pageUrl: string) {
1414+ const html = await safeGetDOM(pageUrl);
1515+ if (!html) return;
1616+1717+ const getMetaProperty = (prop: string) =>
1818+ getContent(html.querySelector(`meta[property=${JSON.stringify(prop)}]`));
1919+ const getMetaName = (name: string) =>
2020+ getContent(html.querySelector(`meta[name=${JSON.stringify(name)}]`));
2121+2222+ const title =
2323+ getMetaProperty('og:title') || html.querySelector('title')?.textContent;
2424+ const description =
2525+ getMetaProperty('og:description') || getMetaName('description');
2626+ const image = urlOrNull(
2727+ getMetaProperty('og:image:secure_url') ||
2828+ getMetaProperty('og:image:url') ||
2929+ getMetaProperty('og:image')
3030+ );
3131+ const imageAlt = getMetaProperty('og:image:alt');
3232+ const video = urlOrNull(
3333+ getMetaProperty('og:video:secure_url') ||
3434+ getMetaProperty('og:video:url') ||
3535+ getMetaProperty('og:video')
3636+ );
3737+ const videoType = getMetaProperty('og:video:type');
3838+ const url =
3939+ urlOrNull(
4040+ getMetaProperty('og:url') ||
4141+ html.querySelector("link[rel='canonical']")?.getAttribute('href')
4242+ ) || pageUrl;
4343+4444+ return { title, description, image, imageAlt, url, video, videoType };
4545+}
+8
src/embeds/link-card/matcher.ts
···11+// Matches any HTTPS URL
22+const urlPattern = /(https:\/\/[^\s\\]+)/;
33+44+// Function to return only the URL part
55+export default function urlMatcher(url: string): string | undefined {
66+ const match = url.match(urlPattern);
77+ return match?.[0];
88+}
···11+// Thanks to eleventy-plugin-youtube-embed
22+// https://github.com/gfscott/eleventy-plugin-youtube-embed/blob/main/lib/extractMatches.js
33+const urlPattern =
44+ /(?=(\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/;
55+66+/**
77+ * Extract a YouTube ID from a URL if it matches the pattern.
88+ * @param url URL to test
99+ * @returns A YouTube video ID or undefined if none matched
1010+ */
1111+export default function matcher(url: string): string | undefined {
1212+ const match = url.match(urlPattern);
1313+ return match?.[3];
1414+}