From 42d16dcd0de142269c92a044259ca76b38f6d9ec Mon Sep 17 00:00:00 2001 From: finxol Date: Tue, 25 Nov 2025 16:27:44 +0100 Subject: [PATCH] chore: improve README, with detailed atproto integration explanation --- README.md | 129 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 2036b15..59f68d8 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,116 @@ -# Nuxt Minimal Starter +# finxol blog -Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. +This is the repo for finxol's blog. -## Setup +All posts are in `content/`. +Configuration is in `blog.config.ts`. -Make sure to install dependencies: +## Technology stack -```bash -# pnpm -pnpm install -``` +- Nuxt v4 +- Nuxt Content +- TailwindCSS +- Deno (Deploy EA) -## Development Server +## Bluesky integration -Start the development server on `http://localhost:3000`: +Tracking PR: [#1](https://tangled.org/finxol.io/blog/pulls/1/) -```bash -# pnpm -pnpm dev -``` +Comments on this blog are directly integrated with Bluesky, the atproto-based micro-blogging social network. -## Production +This integration relies on the `@atcute/` library collection for interaction with Bluesky/atproto. -Build the application for production: +The idea was originally inspired from [natalie's blog](https://natalie.sh/posts/bluesky-comments/). +Although I ended up using mostly the same tools and strategies, I didn't follow her post to build it here. -```bash -# pnpm -pnpm build -``` +### How it works in practice -Locally preview production build: +The author of the blog writes a post and publishes it. +They can then post about it on Bluesky, find the post id, and add it to the `bskyCid` field in the post frontmatter. +Any Bluesky post below the one identified will now be displayed at the bottom of the blog post, allowing for integrated conversation about the post. -```bash -# pnpm -pnpm preview -``` +### How it works technically + +The [AT Protocol](https://atproto.com/) is an open internet protocol for social applications. +All the data is decentralised and public ([for now](https://pfrazee.leaflet.pub/3lzhui2zbxk2b)). +This openness allows us to reuse and build things based on that data very easily, in a built-in way, without hacky workarounds. + +This integration works in several parts: + +#### `app/util/atproto.ts` + +Contains the utility functions for retrieving all replies to a post, and extracting a post id from an atproto uri. + +Uses `@atcute/client` to fetch using the `app.bsky.feed.getPostThread` RPC on the Bluesky public API. +Everything is strongly typed, although fetch errors are handled as `post not found` to make handling simpler in the Vue component. + +#### `blog.config.ts` + +The author DID is set blog-wide in the config file through `authorDid`, as it is primarily intended as a personal blog. +If need be, I can always move the DID parameter to the post frontmatter, allowing for guest authors or secondary accounts too. + +#### `content.config.ts` + +Since the Bluesky post CID needs to be set for each blog post independently, +I added a `bskyCid` field in the post frontmatter. + +#### `app/components/BskyComments.vue` + +This is the core component to display the replies. + +The component simply fetches the replies by calling `getBskyReplies`, passing in the post CID passed as prop, +and displays the content using the `BskyPost` component. + +The reply, like, repost, and bookmark counts of the original Bluesky post are also displayed. + + +#### `app/components/BskyPost.vue` -Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. +This component displays the post author, their avatar, the post content, and its stats beautifully. + +Replies to replies are indented accordingly to visually thread replies together, using `BskyPost` recursively, +with a `MAX_DEPTH` to set a limit to the number of replies to show. + + +#### `app/pages/posts/[...slug].vue` + +The actual post page only had some minor adjustments to integrate the `BskyComments` component, +using a `Suspense` boundary with a fallback to avoid blocking the rendering of the actual content. + +#### Others + +Some other files saw modifications, to adapt to this integration addition, allowing for visual consistency. + +### Advantages of the approach + +Since this blog is built with Nuxt, everything is SSRed. +This makes the Bluesky integration a wonderful progressive enhancement. +The comments will still display and show up as intended if the client has Javascript disabled, +without blocking rendering of the actual content through the use of a `Suspense` boundary. + +Using Bluesky as a comment platform allows me to integrate conversations about my posts directly alongside them, +without bearing the load of moderation and user accounts. + +### Limitations + +As briefly mentioned, fetch errors are normalised to `#notFoundPost`, +this could be refined for better reporting in the UI. + +This integration also only handles plain text content. +All embedded and rich media is effectively ignored for now. + +## Install locally + +```sh +# Install dependencies +pnpm i + +# Run the development server +deno task dev + +# Build for production +deno task build + +# Deploy to Deno Deploy EA. Add `--prod` to deploy to production +deno deploy +``` -- 2.51.2 From fc3b875353ab70d539bfa285926fbbff26a0d939 Mon Sep 17 00:00:00 2001 From: finxol Date: Tue, 25 Nov 2025 16:28:07 +0100 Subject: [PATCH] feat: display repost count --- app/components/BskyComments.vue | 8 +++++--- package.json | 1 + pnpm-lock.yaml | 10 ++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/components/BskyComments.vue b/app/components/BskyComments.vue index 4a514e5..1dd0b28 100644 --- a/app/components/BskyComments.vue +++ b/app/components/BskyComments.vue @@ -38,9 +38,11 @@ if (data.value.$type === "app.bsky.feed.defs#threadViewPost") {

- - {{post.post.likeCount}} - + {{post.post.likeCount}} +

+

+ + {{post.post.repostCount}}

diff --git a/package.json b/package.json index 0495147..bb695a6 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@atcute/bluesky": "^3.2.10", "@atcute/client": "^4.0.5", "@atcute/lexicons": "^1.2.4", + "@iconify-json/bx": "^1.2.2", "@nuxt/content": "^3.8.0", "@nuxt/icon": "1.11.0", "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5767345..087b4a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@atcute/lexicons': specifier: ^1.2.4 version: 1.2.4 + '@iconify-json/bx': + specifier: ^1.2.2 + version: 1.2.2 '@nuxt/content': specifier: ^3.8.0 version: 3.8.0(@libsql/client@0.15.15)(better-sqlite3@12.4.1)(magicast@0.5.1) @@ -459,6 +462,9 @@ packages: '@iconify-json/ant-design@1.2.5': resolution: {integrity: sha512-SYxhrx1AFq2MBcXk77AERYz2mPhLQes1F0vtvG64+dJZWyge9studXo7MiR8PPeLjRjZdWRrReRbxiwdRMf70Q==} + '@iconify-json/bx@1.2.2': + resolution: {integrity: sha512-hZVx6LMEkYckScdRdUuQWcmv8Lm2au6Cnf799TLoR6YgiAfFvaJ4M5ElwcnExvCu8ntsS7jW89r0W5LwBAfZXQ==} + '@iconify-json/ri@1.2.6': resolution: {integrity: sha512-tGXRmXtb8oFu8DNg9MsS1pywKFgs9QZ4U6LBzUamBHaw3ePSiPd7ouE64gzHzfEcR16hgVaXoUa+XxD3BB0XOg==} @@ -5183,6 +5189,10 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + '@iconify-json/bx@1.2.2': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/ri@1.2.6': dependencies: '@iconify/types': 2.0.0 -- 2.51.2 From e63e45fcfcdc4b2fbd853ee2e65e91fc6013d641 Mon Sep 17 00:00:00 2001 From: finxol Date: Tue, 25 Nov 2025 16:28:26 +0100 Subject: [PATCH] chore: improve typing and documentation --- app/components/BskyComments.vue | 22 +++++++++++++--------- app/util/atproto.ts | 14 +++++++++++++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/app/components/BskyComments.vue b/app/components/BskyComments.vue index 1dd0b28..30441aa 100644 --- a/app/components/BskyComments.vue +++ b/app/components/BskyComments.vue @@ -1,5 +1,6 @@ @@ -31,7 +32,7 @@ if (data.value.$type === "app.bsky.feed.defs#threadViewPost") {

Join the conversation!

-
+

{{post.post.replyCount}} @@ -51,13 +52,16 @@ if (data.value.$type === "app.bsky.feed.defs#threadViewPost") {

-

+

+

+ {{ err }} +

+
+ +

Reply on Bluesky to take part in the discussion.

-
-
{{ err }}
-
@@ -66,7 +70,7 @@ if (data.value.$type === "app.bsky.feed.defs#threadViewPost") {