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.

I feel like I'm getting sooo close but I still gotta start most of the UI...


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

+415 -293
+3
.gitignore
··· 5 5 6 6 # Ignore direnv cache 7 7 .direnv/ 8 + 8 9 .vscode 10 + examples/lustre_chilp_app/node_modules 11 + examples/lustre_chilp_app/assets/styled.css
+2 -266
README.md
··· 14 14 15 15 Further documentation can be found at <https://hexdocs.pm/chilp>. 16 16 17 - ## Styling 18 - 19 - When using `chilp.widget()`, CSS to style it is automatically embedded. To say no to that use `widget.element` or create your own widget from `chilp/widget/base`! 20 - 21 - That said, 22 - ```gleam 23 - io.print(widget.css) 24 - ``` 25 - 26 - Yields you: 27 - 28 - ```css 29 - .chilp-widget { 30 - --highlight: #595aff; 31 - transition: all 0.5s ease; 32 - overflow: hidden; 33 - ::selection { 34 - background-color: rgba(89, 90, 255, 0.2); 35 - color: var(--highlight); 36 - } 37 - 38 - .widget::-webkit-scrollbar { 39 - width: 6px; 40 - } 41 - .widget::-webkit-scrollbar-thumb { 42 - background-color: #e2e8f0; 43 - border-radius: 10px; 44 - } 45 - 46 - .widget { 47 - max-width: 600px; 48 - margin: 2rem auto; 49 - background-color: floralwhite; 50 - font-family: sans-serif; 51 - padding: 2rem; 52 - border-radius: 12px; 53 - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); 54 - } 55 - 56 - .widget > .btn-get-comments { 57 - display: block; 58 - width: 100%; 59 - max-width: 200px; 60 - margin: 2rem auto 0 auto; 61 - padding: 12px 24px; 62 - background-color: transparent; 63 - color: #595aff; 64 - border: 2px solid #595aff; 65 - border-radius: 8px; 66 - font-weight: 600; 67 - cursor: pointer; 68 - transition: all 0.2s ease-in-out; 69 - } 70 - 71 - .widget > .btn-get-comments:hover { 72 - background-color: #595aff; 73 - color: white; 74 - box-shadow: 0 4px 12px rgba(89, 90, 255, 0.3); 75 - transform: translateY(-1px); 76 - } 77 - 78 - h1.widget-header { 79 - font-size: 1.75rem; 80 - font-weight: 800; 81 - color: #1a202c; 82 - margin: 0 0 0.5rem 0; 83 - letter-spacing: -0.025em; 84 - } 85 - 86 - .subheader { 87 - font-size: 0.95rem; 88 - color: #718096; 89 - margin-bottom: 1.5rem; 90 - display: flex; 91 - align-items: center; 92 - gap: 6px; 93 - } 94 - 95 - .post-link { 96 - color: #595aff; 97 - text-decoration: none; 98 - font-weight: 500; 99 - border-bottom: 1px solid transparent; 100 - transition: border-color 0.2s; 101 - } 102 - 103 - .post-link:hover { 104 - border-bottom-color: #595aff; 105 - } 106 - 107 - .or-create-an-account-disclaimer { 108 - font-size: 0.73rem; 109 - color: #a0aec0; 110 - margin: -4px 0 12px 2px; 111 - font-style: italic; 112 - margin-bottom: 1.5rem; 113 - display: flex; 114 - align-items: center; 115 - gap: 6px; 116 - } 117 - 118 - .go-reply-form { 119 - display: flex; 120 - gap: 0; 121 - border-bottom: 1px solid #edf2f7; 122 - padding-bottom: 1.5rem; 123 - margin-bottom: 2rem; 124 - max-width: 500px; 125 - filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1)); 126 - } 127 - 128 - .go-reply-form-input { 129 - flex: 1; 130 - padding: 10px 15px; 131 - border: 2px solid #e2e8f0; 132 - flex: 1; 133 - border-right: none; 134 - border-radius: 8px 0 0 8px; 135 - font-size: 0.95rem; 136 - outline: none; 137 - transition: border-color 0.2s; 138 - } 139 - 140 - .go-reply-form-input:focus { 141 - border-color: var(--highlight); 142 - } 143 - 144 - .go-reply-form-input::placeholder { 145 - color: #a0aec0; 146 - } 147 - .input-group { 148 - display: flex; 149 - flex-direction: column; 150 - width: 100%; 151 - gap: 6px; 152 - } 153 - 154 - .go-reply-label { 155 - font-size: 0.85rem; 156 - font-weight: 600; 157 - color: #4a5568; 158 - letter-spacing: 0.05em; 159 - margin-left: 2px; 160 - } 161 - 162 - .form-controls { 163 - display: flex; 164 - width: 100%; 165 - flex-wrap: wrap; 166 - } 167 - 168 - .go-reply-form-button { 169 - padding: 10px 20px; 170 - border: 2px solid var(--highlight); 171 - border-radius: 0 8px 8px 0; 172 - background-color: var(--highlight); 173 - color: white; 174 - font-weight: 600; 175 - cursor: pointer; 176 - transition: all 0.2s; 177 - white-space: nowrap; 178 - } 179 - 180 - .go-reply-form-button:hover { 181 - background-color: var(--highlight); 182 - border-color: var(--highlight); 183 - } 184 - 185 - @media (max-width: 480px) { 186 - .go-reply-form { 187 - flex-direction: column; 188 - gap: 8px; 189 - } 190 - .go-reply-form-input, 191 - .go-reply-form-button { 192 - border-radius: 8px; 193 - border: 2px solid #e2e8f0; 194 - } 195 - } 196 - 197 - .comment-widget form { 198 - display: flex; 199 - gap: 10px; 200 - margin-bottom: 1.5rem; 201 - } 202 - 203 - .comment-widget input[name="userinstance"] { 204 - flex-grow: 1; 205 - padding: 8px 12px; 206 - border: 1px solid #ccc; 207 - border-radius: 4px; 208 - } 209 - 210 - .comment { 211 - border-left: 2px solid #eee; 212 - padding-left: 1rem; 213 - margin-top: 1.5rem; 214 - display: flex; 215 - flex-direction: column; 216 - } 217 - 218 - .comment header { 219 - display: flex; 220 - align-items: center; 221 - gap: 12px; 222 - margin-bottom: 8px; 223 - } 224 - 225 - .comment .avatar { 226 - width: 36px; 227 - height: 36px; 228 - border-radius: 4px; 229 - object-fit: cover; 230 - background: #eee; 231 - border: 1px solid rgba(0, 0, 0, 0.05); 232 - } 233 - 234 - .comment .display-name { 235 - font-weight: bold; 236 - display: block; 237 - } 238 - 239 - .comment time { 240 - font-size: 0.85rem; 241 - color: #666; 242 - } 243 - 244 - .comment .content { 245 - line-height: 1.5; 246 - } 247 - 248 - .comment .content p { 249 - margin: 0.5rem 0; 250 - } 251 - 252 - .comment .mention { 253 - color: var(--highlight); 254 - text-decoration: none; 255 - } 256 - 257 - .comment footer { 258 - margin-top: 8px; 259 - } 260 - 261 - .comment footer a { 262 - font-size: 0.8rem; 263 - color: #888; 264 - text-decoration: none; 265 - } 266 - 267 - .comment footer a:hover { 268 - text-decoration: underline; 269 - } 270 - 271 - .comment .comment { 272 - margin-left: 10px; 273 - border-left: 2px solid #ddd; 274 - } 275 - 276 - .error { 277 - color: red; 278 - font-size: smaller; 279 - } 280 - } 281 - ``` 282 - 283 17 ## Development 284 18 285 19 ```sh 286 20 cd examples/lustre_chilp_app 21 + bun install 22 + tailwindcss -i ./src/input.css -o ./assets/styled.css 287 23 gleam run -m lustre/dev start 288 24 ```
examples/lustre_chilp_app/assets/.gitkeep

This is a binary file and will not be displayed.

+150
examples/lustre_chilp_app/bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "configVersion": 1, 4 + "workspaces": { 5 + "": { 6 + "dependencies": { 7 + "@tailwindcss/cli": "^4.2.2", 8 + "tailwindcss": "^4.2.2", 9 + }, 10 + "devDependencies": { 11 + "daisyui": "^5.5.19", 12 + }, 13 + }, 14 + }, 15 + "packages": { 16 + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 17 + 18 + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 19 + 20 + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 21 + 22 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 23 + 24 + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 25 + 26 + "@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="], 27 + 28 + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="], 29 + 30 + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="], 31 + 32 + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="], 33 + 34 + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="], 35 + 36 + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="], 37 + 38 + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="], 39 + 40 + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="], 41 + 42 + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="], 43 + 44 + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="], 45 + 46 + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="], 47 + 48 + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="], 49 + 50 + "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="], 51 + 52 + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="], 53 + 54 + "@tailwindcss/cli": ["@tailwindcss/cli@4.2.2", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "enhanced-resolve": "^5.19.0", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.2.2" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-iJS+8kAFZ8HPqnh0O5DHCLjo4L6dD97DBQEkrhfSO4V96xeefUus2jqsBs1dUMt3OU9Ks4qIkiY0mpL5UW+4LQ=="], 55 + 56 + "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], 57 + 58 + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], 59 + 60 + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="], 61 + 62 + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="], 63 + 64 + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="], 65 + 66 + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="], 67 + 68 + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="], 69 + 70 + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="], 71 + 72 + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="], 73 + 74 + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="], 75 + 76 + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="], 77 + 78 + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="], 79 + 80 + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="], 81 + 82 + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="], 83 + 84 + "daisyui": ["daisyui@5.5.19", "", {}, "sha512-pbFAkl1VCEh/MPCeclKL61I/MqRIFFhNU7yiXoDDRapXN4/qNCoMxeCCswyxEEhqL5eiTTfwHvucFtOE71C9sA=="], 85 + 86 + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 87 + 88 + "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], 89 + 90 + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 91 + 92 + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 93 + 94 + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 95 + 96 + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 97 + 98 + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], 99 + 100 + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], 101 + 102 + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], 103 + 104 + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], 105 + 106 + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], 107 + 108 + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], 109 + 110 + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], 111 + 112 + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], 113 + 114 + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], 115 + 116 + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], 117 + 118 + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], 119 + 120 + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], 121 + 122 + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 123 + 124 + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], 125 + 126 + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], 127 + 128 + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 129 + 130 + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], 131 + 132 + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 133 + 134 + "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], 135 + 136 + "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], 137 + 138 + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], 139 + 140 + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], 141 + 142 + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], 143 + 144 + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], 145 + 146 + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 147 + 148 + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 149 + } 150 + }
+6
examples/lustre_chilp_app/gleam.toml
··· 21 21 [dev-dependencies] 22 22 gleeunit = ">= 1.0.0 and < 2.0.0" 23 23 lustre_dev_tools = ">= 2.3.4 and < 3.0.0" 24 + 25 + [tools.lustre.build] 26 + no_tailwind = true 27 + 28 + [tools.lustre.html] 29 + stylesheets = [{ href = "/styled.css" }]
+2 -2
examples/lustre_chilp_app/manifest.toml
··· 4 4 packages = [ 5 5 { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, 6 6 { name = "booklet", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "booklet", source = "hex", outer_checksum = "08E0FDB78DC4D8A5D3C80295B021505C7D2A2E7B6C6D5EAB7286C36F4A53C851" }, 7 - { name = "chilp", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "gleam_time", "glentities", "lustre", "rsvp"], source = "local", path = "../.." }, 7 + { name = "chilp", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "gleam_time", "html_parser", "lustre", "rsvp"], source = "local", path = "../.." }, 8 8 { name = "directories", version = "1.2.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "D13090CFCDF6759B87217E8DDD73A75903A700148A82C1D33799F333E249BF9E" }, 9 9 { name = "envoy", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "850DA9D29D2E5987735872A2B5C81035146D7FE19EFC486129E44440D03FD832" }, 10 10 { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, ··· 31 31 { name = "group_registry", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "group_registry", source = "hex", outer_checksum = "BC798A53D6F2406DB94E27CB45C57052CB56B32ACF7CC16EA20F6BAEC7E36B90" }, 32 32 { name = "houdini", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "houdini", source = "hex", outer_checksum = "5DB1053F1AF828049C2B206D4403C18970ABEF5C18671CA3C2D2ED0DD64F6385" }, 33 33 { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 34 + { name = "html_parser", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "html_parser", source = "hex", outer_checksum = "EEC0A3891CE99A49A8BB99086A06F56441D2ACF9436CE33ADBE51CE277D2D607" }, 34 35 { name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" }, 35 36 { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, 36 37 { name = "lustre", version = "5.5.2", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib", "houdini"], otp_app = "lustre", source = "hex", outer_checksum = "2DC2973D81C12E63251B636773217B8E09C5C84590A729750F6BCF009420B38E" }, 37 38 { name = "lustre_dev_tools", version = "2.3.4", build_tools = ["gleam"], requirements = ["argv", "booklet", "filepath", "gleam_community_ansi", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_httpc", "gleam_json", "gleam_otp", "gleam_regexp", "gleam_stdlib", "glint", "group_registry", "justin", "lustre", "mist", "polly", "simplifile", "tom", "wisp"], otp_app = "lustre_dev_tools", source = "hex", outer_checksum = "5D5C479E465A3EA018205EFCD2F2FE430A9B9783CAC21670E6CB25703069407D" }, 38 39 { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" }, 39 40 { name = "mist", version = "5.0.4", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "gleam_yielder", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "7CED4B2D81FD547ADB093D97B9928B9419A7F58B8562A30A6CC17A252B31AD05" }, 40 - { name = "odysseus", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "odysseus", source = "hex", outer_checksum = "6A97DA1075BDDEA8B60F47B1DFFAD49309FA27E73843F13A0AF32EA7087BA11C" }, 41 41 { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" }, 42 42 { name = "polly", version = "3.0.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_erlang", "gleam_otp", "gleam_stdlib", "simplifile"], otp_app = "polly", source = "hex", outer_checksum = "35B11497B998618CEE216415A7853C3FED3F0F2148DC86BD8FC86B95D67F6DD8" }, 43 43 { name = "rsvp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_fetch", "gleam_http", "gleam_httpc", "gleam_javascript", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "rsvp", source = "hex", outer_checksum = "40F9E0E662FF258E10C7041A9591261FE802D56625FB444B91510969644F7722" },
+9
examples/lustre_chilp_app/package.json
··· 1 + { 2 + "dependencies": { 3 + "@tailwindcss/cli": "^4.2.2", 4 + "tailwindcss": "^4.2.2" 5 + }, 6 + "devDependencies": { 7 + "daisyui": "^5.5.19" 8 + } 9 + }
+3
examples/lustre_chilp_app/src/input.css
··· 1 + @import "tailwindcss"; 2 + @plugin "daisyui"; 3 + @source "../build/dev/javascript";
+12 -1
examples/lustre_chilp_app/src/lustre_chilp_app.gleam
··· 2 2 3 3 import chilp 4 4 import chilp/widget 5 + import chilp/widget/anchors 6 + import gleam/option 5 7 import lustre 6 8 import lustre/effect.{type Effect} 7 9 import lustre/element.{type Element} ··· 55 57 html.div([], [ 56 58 element.text(model.string), 57 59 // Let's render comments under https://pony.social/@strawmelonjuice/115911235653686237 58 - chilp.widget(instance: "pony.social", post_id: "115911235653686237"), 60 + chilp.widget( 61 + option.Some(anchors.Mastodon( 62 + instance: "pony.social", 63 + postid: "115911235653686237", 64 + )), 65 + option.Some(anchors.Bluesky( 66 + did: "did:plc:jgtfsmv25thfs4zmydtbccnn", 67 + postid: "3mgrbiiadws2k", 68 + )), 69 + ), 59 70 ]) 60 71 }
+11 -3
flake.nix
··· 4 4 utils.url = "github:numtide/flake-utils"; 5 5 }; 6 6 7 - outputs = { self, nixpkgs, utils }: 8 - utils.lib.eachDefaultSystem (system: 7 + outputs = 8 + { 9 + self, 10 + nixpkgs, 11 + utils, 12 + }: 13 + utils.lib.eachDefaultSystem ( 14 + system: 9 15 let 10 16 pkgs = import nixpkgs { inherit system; }; 11 17 in ··· 16 22 erlang_28 17 23 rebar3 18 24 bun 25 + tailwindcss_4 19 26 ]; 20 27 21 28 shellHook = '' ··· 24 31 # echo "Use just to run them." 25 32 ''; 26 33 }; 27 - }); 34 + } 35 + ); 28 36 }
+3 -1
gleam.toml
··· 1 1 name = "chilp" 2 2 description = "Allows you to use Mastodon comments on your Lustre blog." 3 3 version = "1.0.0" 4 + gleam = ">= 1.15.0" 4 5 licences = ["Apache-2.0"] 5 6 repository = { type = "tangled", user = "did:plc:jgtfsmv25thfs4zmydtbccnn", repo = "chilp" } 6 - # links = [{ title = "Website", href = "" }] 7 + documentation.pages = [{title = "Changelog", path = "changelog.html",source = "./changelog.md" }] 8 + 7 9 8 10 [dependencies] 9 11 gleam_stdlib = ">= 0.44.0 and < 2.0.0"
+1 -5
src/chilp.gleam
··· 3 3 /// Widget component! 4 4 /// Before adding this component make sure to call `widget.register()` to register it! 5 5 /// 6 - /// This component adds in inline CSS, to not do this, use `widget.element("bla", "111", False)` instead. 7 - pub fn widget(instance instance: String, post_id post: String) { 8 - todo 9 - // widget.element(instance:, post_id: post, with_styles: True) 10 - } 6 + pub const widget = widget.element
+213 -15
src/chilp/widget.gleam
··· 3 3 import chilp/api_typing/new.{type BskyThreadReply, type MastodonDescendant} 4 4 import chilp/widget/anchors 5 5 import gleam/bool 6 + import gleam/dynamic/decode 6 7 import gleam/option.{type Option, None, Some} 7 8 import gleam/result 8 9 import gleam/string ··· 11 12 import lustre/component 12 13 import lustre/effect.{type Effect} 13 14 import lustre/element.{type Element} 15 + import lustre/element/html 14 16 import rsvp 15 17 16 18 // MAIN ------------------------------------------------------------------------ ··· 23 25 component.on_attribute_change("mastodon-anchor", fn(value) { 24 26 use <- bool.guard(when: value == "", return: Ok(MastodonUnAnchored)) 25 27 value 26 - |> string.split_once(":") 28 + |> string.split_once("\\") 27 29 |> result.map(fn(a) { MastodonAnchored(a.0, a.1) }) 28 30 }), 29 31 component.on_attribute_change("bluesky-anchor", fn(value) { 30 32 use <- bool.guard(when: value == "", return: Ok(BskyUnAnchored)) 31 33 value 32 - |> string.split_once(":") 34 + |> string.split_once("\\") 33 35 |> result.map(fn(a) { BskyAnchored(a.0, a.1) }) 34 36 }), 35 37 ]) ··· 45 47 "comment-widget", 46 48 [ 47 49 attribute.attribute("mastodon-anchor", case mastodon_anchor { 48 - Some(anchor) -> anchor.instance <> ":" <> anchor.postid 50 + Some(anchor) -> anchor.instance <> "\\" <> anchor.postid 49 51 None -> "" 50 52 }), 51 53 attribute.attribute("bluesky-anchor", case bsky_anchor { 52 - Some(anchor) -> anchor.did <> ":" <> anchor.postid 54 + Some(anchor) -> anchor.did <> "\\" <> anchor.postid 53 55 None -> "" 54 56 }), 55 57 ], ··· 63 65 Model( 64 66 mastodon_anchor: Option(anchors.Mastodon), 65 67 cached_mastodon_descendants: List(MastodonDescendant), 68 + mastodon_op_username: String, 66 69 bluesky_anchor: Option(anchors.Bluesky), 67 70 cached_bluesky_replies: List(BskyThreadReply), 71 + bsky_op_handle: String, 68 72 all_stopping_error: Option(String), 69 73 cached_coalesced_view: List(new.CoalescedView), 70 74 /// For those not using DaisyUI, using it's hacky way of creating tabs is... hacky. ··· 79 83 Model( 80 84 mastodon_anchor: None, 81 85 cached_mastodon_descendants: [], 86 + mastodon_op_username: "username", 82 87 bluesky_anchor: None, 83 88 cached_bluesky_replies: [], 89 + bsky_op_handle: "", 84 90 all_stopping_error: None, 85 91 cached_coalesced_view: [], 86 92 open_tab: 1, ··· 100 106 BskyIncomingThreadView(new.BskyThreadView) 101 107 IncomingCoalescedView(List(new.CoalescedView)) 102 108 MastodonIncomingStatus(new.MastodonStatusContext) 109 + MastodonIncomingOpUsername(String) 103 110 } 104 111 105 112 fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { ··· 112 119 Model(..model, mastodon_anchor: None), 113 120 effect.none(), 114 121 ) 115 - MastodonAnchored(instance:, post:) -> #( 116 - Model( 117 - ..model, 118 - mastodon_anchor: Some(anchors.Mastodon(instance:, postid: post)), 119 - ), 120 - refresh_mastodon(model), 122 + MastodonAnchored(instance:, post:) -> { 123 + let model = 124 + Model( 125 + ..model, 126 + mastodon_anchor: Some(anchors.Mastodon(instance:, postid: post)), 127 + ) 128 + #( 129 + model, 130 + effect.batch([get_username_mastodon(model), refresh_mastodon(model)]), 131 + ) 132 + } 133 + MastodonIncomingOpUsername(mastodon_op_username) -> #( 134 + Model(..model, mastodon_op_username: echo mastodon_op_username), 135 + effect.none(), 121 136 ) 122 137 MastodonIncomingStatus(status) -> { 123 138 #( ··· 133 148 ..model, 134 149 bluesky_anchor: Some(anchors.Bluesky(did:, postid: post)), 135 150 ) 136 - #(model, refresh_bsky(model)) 151 + #(model, effect.batch([refresh_bsky(model)])) 137 152 } 138 153 BskyIncomingThreadView(threadview) -> { 139 154 #( ··· 150 165 effect.none(), 151 166 ) 152 167 } 168 + |> echo 153 169 } 154 170 155 171 // VIEW ------------------------------------------------------------------------ 156 172 157 173 fn view(model: Model) -> Element(Msg) { 158 - todo 174 + case model.all_stopping_error { 175 + None -> view_normal(model) 176 + Some(error) -> error_view(error) 177 + } 178 + } 179 + 180 + fn view_normal(model: Model) -> Element(Msg) { 181 + html.div([attribute.class("chilp-widget")], [ 182 + html.div([attribute.class("widget ")], [ 183 + html.h1([attribute.class("widget-header h1 ")], [html.text("Comments")]), 184 + html.p([attribute.class("subheader ")], [ 185 + html.text("Linked to "), 186 + case model.bluesky_anchor, model.mastodon_anchor { 187 + Some(bsky), Some(masto) -> { 188 + [ 189 + html.a( 190 + [ 191 + attribute.class("text-purple"), 192 + // https://pony.social/@strawmelonjuice/115911235653686237 193 + attribute.href( 194 + "https://" 195 + <> masto.instance 196 + <> "/@" 197 + <> model.mastodon_op_username 198 + <> "/" 199 + <> masto.postid, 200 + ), 201 + attribute.class("post-link "), 202 + ], 203 + [html.text("this post")], 204 + ), 205 + html.text(" on Mastodon, and to "), 206 + html.a( 207 + [ 208 + attribute.href( 209 + "https://bsky.app/profile/" 210 + <> bsky.did 211 + <> "/post/" 212 + <> bsky.postid, 213 + ), 214 + attribute.class("post-link "), 215 + ], 216 + [html.text("this post")], 217 + ), 218 + html.text(" on Bluesky."), 219 + ] 220 + } 221 + None, Some(masto) -> { 222 + [ 223 + html.a( 224 + [ 225 + attribute.href( 226 + "https://" 227 + <> masto.instance 228 + <> "/" 229 + <> model.mastodon_op_username 230 + <> "/" 231 + <> masto.postid, 232 + ), 233 + attribute.class("post-link "), 234 + ], 235 + [html.text("this post")], 236 + ), 237 + html.text(" on Mastodon."), 238 + ] 239 + } 240 + Some(bsky), None -> { 241 + [ 242 + html.a( 243 + [ 244 + attribute.href( 245 + "https://bsky.app/profile/" 246 + <> bsky.did 247 + <> "/post/" 248 + <> bsky.postid, 249 + ), 250 + attribute.class("post-link "), 251 + ], 252 + [html.text("this post")], 253 + ), 254 + html.text(" on Bluesky."), 255 + ] 256 + } 257 + None, None -> [ 258 + error_view("No comment backends are configured for this widget."), 259 + ] 260 + } 261 + |> element.fragment, 262 + ]), 263 + html.form([attribute.class("go-reply-form ")], [ 264 + html.div([attribute.class("input-group")], [ 265 + html.label( 266 + [ 267 + attribute.for("userinstance"), 268 + attribute.class("go-reply-label"), 269 + ], 270 + [ 271 + html.text("Enter your instance adress to reply or "), 272 + html.a( 273 + [ 274 + attribute.href("https://mastodon.social/auth/sign_up"), 275 + attribute.class("post-link "), 276 + ], 277 + [html.text("create an account")], 278 + ), 279 + html.text("!"), 280 + ], 281 + ), 282 + html.p( 283 + [ 284 + attribute.for("userinstance"), 285 + attribute.class("or-create-an-account-disclaimer"), 286 + ], 287 + [ 288 + html.text( 289 + "on an instance reccommended by this site... or one you pick yourself!", 290 + ), 291 + ], 292 + ), 293 + html.div([attribute.class("form-controls")], [ 294 + html.input([ 295 + attribute.type_("text"), 296 + attribute.required(True), 297 + attribute.placeholder("todon.nl"), 298 + attribute.pattern("^([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,}$"), 299 + attribute.name("userinstance"), 300 + attribute.class("go-reply-form-input "), 301 + ]), 302 + html.button( 303 + [ 304 + attribute.type_("submit"), 305 + attribute.class("go-reply-form-button "), 306 + ], 307 + [html.text("Go reply")], 308 + ), 309 + ]), 310 + ]), 311 + ]), 312 + html.section([], []), 313 + ]), 314 + ]) 315 + } 316 + 317 + fn error_view(error: String) { 318 + // todo: Style dis. 319 + element.text("AN ERROR OCCURED:" <> error) 159 320 } 160 321 161 322 // EFFECTS --------------------------------------------------------------------- ··· 204 365 } 205 366 } 206 367 368 + fn get_username_mastodon(model: Model) -> Effect(Msg) { 369 + case model.mastodon_anchor { 370 + Some(anchor) -> { 371 + let url = 372 + "https://" <> anchor.instance <> "/api/v1/statuses/" <> anchor.postid 373 + 374 + let handler = 375 + rsvp.expect_json( 376 + { 377 + use user <- decode.subfield(["account", "username"], decode.string) 378 + decode.success(user) 379 + }, 380 + fn(response) { 381 + case response { 382 + Ok(username) -> MastodonIncomingOpUsername(username) 383 + Error(rsvperror) -> 384 + case rsvperror { 385 + rsvp.UnhandledResponse(_) | rsvp.JsonError(_) | rsvp.BadBody -> 386 + AllStoppingError( 387 + "The response body we got back from Mastodon was misformed.", 388 + ) 389 + rsvp.BadUrl(_) -> 390 + AllStoppingError( 391 + "The API call to Mastodon failed. Did you enter the instance and post id correctly?", 392 + ) 393 + rsvp.HttpError(_) | rsvp.NetworkError -> 394 + AllStoppingError("Could not fetch comments from Mastodon.") 395 + } 396 + } 397 + }, 398 + ) 399 + rsvp.get(url, handler) 400 + } 401 + None -> effect.none() 402 + } 403 + } 404 + 207 405 fn refresh_mastodon(model: Model) -> Effect(Msg) { 208 406 case model.mastodon_anchor { 209 407 Some(anchor) -> { ··· 224 422 case rsvperror { 225 423 rsvp.UnhandledResponse(_) | rsvp.JsonError(_) | rsvp.BadBody -> 226 424 AllStoppingError( 227 - "The response body we got back from Bluesky was misformed.", 425 + "The response body we got back from Mastodon was misformed.", 228 426 ) 229 427 rsvp.BadUrl(_) -> 230 428 AllStoppingError( 231 - "The API call to Bluesky failed. Did you enter the DID and post id correctly?", 429 + "The API call to Mastodon failed. Did you enter the instance and post id correctly?", 232 430 ) 233 431 rsvp.HttpError(_) | rsvp.NetworkError -> 234 - AllStoppingError("Could not fetch comments from Bluesky.") 432 + AllStoppingError("Could not fetch comments from Mastodon.") 235 433 } 236 434 } 237 435 },