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") {
-
+
+
+
Reply on Bluesky to take part in the discussion.
-
@@ -66,7 +70,7 @@ if (data.value.$type === "app.bsky.feed.defs#threadViewPost") {