Allows you to use Mastodon and Bluesky comments on your Lustre blog hexdocs.pm/chilp/
blog gleam lustre indieweb mastodon bluesky comments
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Am I rushing this?


Signed-off-by: MLC Bloeiman <mar@strawmelonjuice.com>

+7 -276
-271
NOTES.md
··· 1 - # Notes for 1.1.0: 2 - 3 - To do for this release: 4 - - [x] [feat] Adding multiple back-ends 5 - - [x] Bluesky, including a "[Comment via Bluesky]" button, and fetching comments from Bluesky posts in Bluesky-only mode or in a mixed mode with Mastodon. 6 - - [x] ~~GitHub issues, including a "[Comment via GitHub]" button.~~ For a later release, as this will require a lot of work to implement the GraphQL queries and mutations. 7 - - [ ] Other platforms? Maybe? Depends on how much time I have, and how much demand there is for other platforms. 8 - - [x] Remove dependency on DOMPurifier 9 - - [x] [fix] Adding `flex-wrap: wrap;` to `.widget .form-controls` to prevent the buttons from overflowing on smaller screens. 10 - - [x] Remove the builtin CSS in favour of [DaisyUI](https://daisyui.com) classes which means standardized class names for self-stylers, and site-themed widgets for everyone else! 11 - 12 - ## [feat] Adding multiple back-ends 13 - 14 - ### Fetching a post from Bluesky 15 - This seems a little more complicated than fetching from Mastodon, but seems doable. 16 - 17 - Where for a mastodon post we need `instance` and `postid`, we now need `did` and `postid`. 18 - 19 - Let's say they have been supplied as `did:plc:jgtfsmv25thfs4zmydtbccnn` (strawmelonjuice.com) and `3mgt3lymlak2c` 20 - 21 - Fetch the thread: 22 - 23 - https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgrbiiadws2k 24 - 25 - That'd be [this post](https://witchsky.app/profile/did:plc:jgtfsmv25thfs4zmydtbccnn/post/3mgt3lymlak2c), shaped like this: 26 - 27 - ```json 28 - { 29 - "thread": { 30 - "$type": "app.bsky.feed.defs#threadViewPost", 31 - "post": { 32 - "uri": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgt3lymlak2c", 33 - "cid": "bafyreib7cnklabvss4jpi6j2dplahndlb4vpvwpmacqfbu6227wap4xs6i", 34 - "author": { 35 - "did": "did:plc:jgtfsmv25thfs4zmydtbccnn", 36 - "handle": "strawmelonjuice.com", 37 - "displayName": "Mar !!", 38 - "pronouns": "she/her", 39 - "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:jgtfsmv25thfs4zmydtbccnn/bafkreia45evsjv2oae3bceh6jezy7am7uobmjhstvmwgdb5fekvs6q5wry", 40 - "associated": { 41 - "chat": { 42 - "allowIncoming": "following" 43 - }, 44 - "activitySubscription": { 45 - "allowSubscriptions": "followers" 46 - } 47 - }, 48 - "labels": [], 49 - "createdAt": "2023-12-03T09:45:34.997Z" 50 - }, 51 - "record": { 52 - "$type": "app.bsky.feed.post", 53 - "createdAt": "2026-03-11T23:56:53.666Z", 54 - "embed": { 55 - "$type": "app.bsky.embed.external", 56 - "external": { 57 - "description": "", 58 - "title": "Mar's site", 59 - "uri": "https://strawmelonjuice.com/post/0" 60 - } 61 - }, 62 - "facets": [ 63 - { 64 - "features": [ 65 - { 66 - "$type": "app.bsky.richtext.facet#link", 67 - "uri": "https://strawmelonjuice.com/post/0" 68 - } 69 - ], 70 - "index": { 71 - "byteEnd": 55, 72 - "byteStart": 29 73 - } 74 - } 75 - ], 76 - "langs": [ 77 - "en" 78 - ], 79 - "text": "NEW SITE WHO DIS?!\n\nhttps://\nstrawmelonjuice.com/post/0 :3" 80 - }, 81 - "embed": { 82 - "$type": "app.bsky.embed.external#view", 83 - "external": { 84 - "uri": "https://strawmelonjuice.com/post/0", 85 - "title": "Mar's site", 86 - "description": "" 87 - } 88 - }, 89 - "bookmarkCount": 1, 90 - "replyCount": 3, 91 - "repostCount": 1, 92 - "likeCount": 4, 93 - "quoteCount": 0, 94 - "indexedAt": "2026-03-11T23:56:53.973Z", 95 - "labels": [] 96 - }, 97 - "replies": [ 98 - { 99 - "$type": "app.bsky.feed.defs#threadViewPost", 100 - "post": { 101 - "uri": "at://did:plc:qcwhrvzx6wmi5hz775uyi6fh/app.bsky.feed.post/3mgt3lz2dar2k", 102 - "cid": "bafyreidipzjm3znrh27cj2267t5g2tm75jxq4bvgwsyrgn3f4odqzptsq4", 103 - "author": { 104 - "did": "did:plc:qcwhrvzx6wmi5hz775uyi6fh", 105 - "handle": "bot-tan.suibari.com", 106 - "displayName": "全肯定botたん", 107 - "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:qcwhrvzx6wmi5hz775uyi6fh/bafkreiafvcttyogza65axx4kknnbgkeflgcz4abz6bcxqsbumtiyzygzjq", 108 - "associated": { 109 - "chat": { 110 - "allowIncoming": "following" 111 - }, 112 - "activitySubscription": { 113 - "allowSubscriptions": "followers" 114 - } 115 - }, 116 - "labels": [], 117 - "createdAt": "2024-02-19T04:43:58.159Z" 118 - }, 119 - "record": { 120 - "$type": "app.bsky.feed.post", 121 - "createdAt": "2026-03-11T23:56:54.045Z", 122 - "reply": { 123 - "parent": { 124 - "cid": "bafyreib7cnklabvss4jpi6j2dplahndlb4vpvwpmacqfbu6227wap4xs6i", 125 - "uri": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgt3lymlak2c" 126 - }, 127 - "root": { 128 - "cid": "bafyreib7cnklabvss4jpi6j2dplahndlb4vpvwpmacqfbu6227wap4xs6i", 129 - "uri": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgt3lymlak2c" 130 - } 131 - }, 132 - "text": "A quiet storm of excellence." 133 - }, 134 - "bookmarkCount": 0, 135 - "replyCount": 0, 136 - "repostCount": 0, 137 - "likeCount": 1, 138 - "quoteCount": 0, 139 - "indexedAt": "2026-03-11T23:56:54.249Z", 140 - "labels": [] 141 - }, 142 - "replies": [], 143 - "threadContext": { 144 - "rootAuthorLike": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.like/3mgtql33lob2w" 145 - } 146 - }, 147 - { 148 - "$type": "app.bsky.feed.defs#threadViewPost", 149 - "post": { 150 - "uri": "at://did:plc:fz7okbhusu5f2gbzx5tyncgf/app.bsky.feed.post/3mgtk3ajb622a", 151 - "cid": "bafyreien7vm2l2e2q6nwnsabvzleuluikrgzgalj4dnj6q7xkdjsrj67na", 152 - "author": { 153 - "did": "did:plc:fz7okbhusu5f2gbzx5tyncgf", 154 - "handle": "cool-handle.ebil.club", 155 - "displayName": "coil door habdle", 156 - "pronouns": "they/she", 157 - "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:fz7okbhusu5f2gbzx5tyncgf/bafkreif5r27afvsjy5tgcvwj52jw7tnpxmko5rior4wz6ktkdygyumosdy", 158 - "associated": { 159 - "chat": { 160 - "allowIncoming": "all" 161 - }, 162 - "activitySubscription": { 163 - "allowSubscriptions": "mutuals" 164 - } 165 - }, 166 - "labels": [], 167 - "createdAt": "2025-08-11T04:50:51.801Z" 168 - }, 169 - "record": { 170 - "$type": "app.bsky.feed.post", 171 - "createdAt": "2026-03-12T04:15:57.648Z", 172 - "langs": [ 173 - "en" 174 - ], 175 - "reply": { 176 - "parent": { 177 - "cid": "bafyreib7cnklabvss4jpi6j2dplahndlb4vpvwpmacqfbu6227wap4xs6i", 178 - "uri": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgt3lymlak2c" 179 - }, 180 - "root": { 181 - "cid": "bafyreib7cnklabvss4jpi6j2dplahndlb4vpvwpmacqfbu6227wap4xs6i", 182 - "uri": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgt3lymlak2c" 183 - } 184 - }, 185 - "text": "woag !!" 186 - }, 187 - "bookmarkCount": 0, 188 - "replyCount": 0, 189 - "repostCount": 0, 190 - "likeCount": 1, 191 - "quoteCount": 0, 192 - "indexedAt": "2026-03-12T04:15:58.953Z", 193 - "labels": [] 194 - }, 195 - "replies": [], 196 - "threadContext": { 197 - "rootAuthorLike": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.like/3mgtpt6x5722m" 198 - } 199 - }, 200 - { 201 - "$type": "app.bsky.feed.defs#threadViewPost", 202 - "post": { 203 - "uri": "at://did:plc:4vsww6rhxujyrsd3h7z24xj7/app.bsky.feed.post/3mgt3lzkaw72e", 204 - "cid": "bafyreidwpcqfprkf7qhy4s6czekq25zraoasqs25f4t2f6uebb3ujqsrhm", 205 - "author": { 206 - "did": "did:plc:4vsww6rhxujyrsd3h7z24xj7", 207 - "handle": "positivitybot.bsky.social", 208 - "displayName": "Whimsy Miku", 209 - "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:4vsww6rhxujyrsd3h7z24xj7/bafkreieh4gyxzq4okicnox22ptf3dhnpptpo2tty72m4ejaqsd7bcwvg4a", 210 - "associated": { 211 - "chat": { 212 - "allowIncoming": "all" 213 - }, 214 - "activitySubscription": { 215 - "allowSubscriptions": "followers" 216 - } 217 - }, 218 - "labels": [], 219 - "createdAt": "2025-10-24T22:52:38.552Z" 220 - }, 221 - "record": { 222 - "$type": "app.bsky.feed.post", 223 - "createdAt": "2026-03-11T23:56:54.549441+00:00", 224 - "facets": [], 225 - "langs": [ 226 - "en" 227 - ], 228 - "reply": { 229 - "$type": "app.bsky.feed.post#replyRef", 230 - "parent": { 231 - "$type": "com.atproto.repo.strongRef", 232 - "cid": "bafyreib7cnklabvss4jpi6j2dplahndlb4vpvwpmacqfbu6227wap4xs6i", 233 - "uri": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgt3lymlak2c" 234 - }, 235 - "root": { 236 - "$type": "com.atproto.repo.strongRef", 237 - "cid": "bafyreib7cnklabvss4jpi6j2dplahndlb4vpvwpmacqfbu6227wap4xs6i", 238 - "uri": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.post/3mgt3lymlak2c" 239 - } 240 - }, 241 - "text": "Yap more often!" 242 - }, 243 - "bookmarkCount": 0, 244 - "replyCount": 0, 245 - "repostCount": 0, 246 - "likeCount": 1, 247 - "quoteCount": 0, 248 - "indexedAt": "2026-03-11T23:56:55.550Z", 249 - "labels": [] 250 - }, 251 - "replies": [], 252 - "threadContext": { 253 - "rootAuthorLike": "at://did:plc:jgtfsmv25thfs4zmydtbccnn/app.bsky.feed.like/3mgtql45dip2k" 254 - } 255 - } 256 - ], 257 - "threadContext": { 258 - 259 - } 260 - } 261 - } 262 - ``` 263 - 264 - 265 - This is a big chunk of data, but it contains all the information about the post, including the author, the text, the embed (if any), and the counts for bookmarks, replies, reposts, likes, and quotes. It also includes information about the reply (if it's a reply), including the root and parent posts. 266 - 267 - Should be enough to mix it into our Mastodon based comments! 268 - 269 - ### GitHub issues for comments 270 - 271 - GitHub uses GraphQL for this, I do not currently have the capacity to work on that.
+4 -2
gleam.toml
··· 1 1 name = "chilp" 2 2 description = "Allows you to use Mastodon comments on your Lustre blog." 3 - version = "1.0.0" 3 + version = "2.0.0-rc" 4 4 gleam = ">= 1.15.0" 5 5 licences = ["Apache-2.0"] 6 6 repository = { type = "tangled", user = "did:plc:jgtfsmv25thfs4zmydtbccnn", repo = "chilp" } 7 - documentation.pages = [{title = "Changelog", path = "changelog.html",source = "./changelog.md" }] 7 + documentation.pages = [ 8 + { title = "Changelog", path = "changelog.html", source = "./changelog.md" }, 9 + ] 8 10 9 11 10 12 [dependencies]
+3 -3
src/chilp/widget.gleam
··· 441 441 html.a( 442 442 [ 443 443 attribute.href(comment.author_profile_link), 444 - attribute.class("link link-secondary link-sm"), 444 + attribute.class("link link-secondary-content link-sm"), 445 445 ], 446 446 [element.text(comment.author_username)], 447 447 ), ··· 531 531 html.a( 532 532 [ 533 533 attribute.href("https://strawmelonjuice.com/post/chilpv2"), 534 - attribute.class("link link-primary"), 534 + attribute.class("link link-primary-content"), 535 535 ], 536 536 [ 537 537 element.text("more about Chilp"), ··· 618 618 <> placeholder_instance(mastodon_anchor) 619 619 <> "/auth/sign_up", 620 620 ), 621 - attribute.class("link link-primary"), 621 + attribute.class("link link-primary-content"), 622 622 ], 623 623 [html.text("create an account")], 624 624 ),