my website at ewancroft.uk
6
fork

Configure Feed

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

docs: expand README with updated features, music integration, and configuration changes

+307 -226
+307 -226
README.md
··· 1 1 # AT Protocol Personal Website 2 2 3 - A modern, AT Protocol-powered personal website template built with SvelteKit 2 and Tailwind CSS 4. 3 + A modern, feature-rich personal website powered by AT Protocol, built with SvelteKit 2 and Tailwind CSS 4. 4 4 5 5 > **Note**: This repository contains the source code for [Ewan's Corner](https://ewancroft.uk). The current configuration (environment variables, slug mappings, static files) is specific to that website, but the codebase is designed to be easily adapted for your own AT Protocol-powered site. See the [Configuration](#configuration) section below for details on personalising it for your use. 6 6 7 7 ## 🌟 Features 8 8 9 - - **AT Protocol Integration**: Fetch and display content from your AT Protocol repository 10 - - **Multi-Publication Support**: Map friendly URL slugs to Leaflet publications with a simple config 11 - - **Multi-Platform Blog**: Seamlessly aggregate blog posts from WhiteWind and/or Leaflet (configurable) 12 - - **Dynamic Profile**: Automatically display your Bluesky profile information 13 - - **Custom Status**: Show real-time status updates using custom AT Protocol lexicons 14 - - **Link Board**: Display a Linkat board with emoji-styled link cards 15 - - **Bluesky Posts**: Showcase your latest non-reply Bluesky posts with rich media support 16 - - **Smart Redirects**: Intelligent redirection system for publication URLs with platform prioritisation 17 - - **Responsive Design**: Mobile-first layout with dark-mode support 18 - - **RSS Feed**: Intelligent RSS-feed handling for WhiteWind and/or Leaflet posts 19 - - **Type-Safe**: Full TypeScript support throughout the application 9 + ### Core AT Protocol Integration 20 10 21 - ## Configuration 11 + - **Dynamic Profile Display**: Automatically fetch and display your Bluesky profile information with avatar, banner, follower counts, and bio 12 + - **Site Metadata**: Store and display comprehensive site information using the `uk.ewancroft.site.info` lexicon (credits, tech stack, privacy statement, licenses) 13 + - **Smart Caching**: Intelligent 5-minute in-memory cache with TTL support for all AT Protocol data 14 + - **PDS Resolution**: Automatic PDS discovery with fallback to Bluesky public API for maximum reliability 22 15 23 - Before using this template, you'll need to update several configuration files with your own information: 16 + ### Content & Publishing 24 17 25 - ### Environment Variables (`.env`) 18 + - **Multi-Platform Blog System**: 19 + - **Leaflet** (`pub.leaflet.document`) - Primary platform with custom domain support 20 + - **WhiteWind** (`com.whtwnd.blog.entry`) - Optional secondary platform (disabled by default) 21 + - Intelligent RSS feed generation with full content support 22 + - Automatic draft filtering and non-public post handling 23 + - Multi-publication support via slug mapping 26 24 27 - Update these with your own values: 25 + - **Flexible Publication Management**: 26 + - Map friendly URL slugs to AT Protocol publications 27 + - Support for unlimited publications with individual configurations 28 + - Custom base paths for each publication 29 + - Smart redirects with platform prioritization 30 + - Intelligent fallback handling for missing content 28 31 29 - ```ini 30 - PUBLIC_ATPROTO_DID=did:plc:your-did-here # Your AT Protocol DID 31 - PUBLIC_SITE_TITLE="Your Site Title" # Your site name 32 - PUBLIC_SITE_DESCRIPTION="..." # Your description 33 - PUBLIC_SITE_URL="https://example.com" # Your domain 34 - ```` 35 - 36 - ### Publication Slug Mappings (`src/lib/data/slug-mappings.ts`) 37 - 38 - Replace the example mappings with your own Leaflet publication rkeys: 39 - 40 - ```typescript 41 - export const slugMappings: SlugMapping[] = [ 42 - { slug: 'blog', publicationRkey: 'your-rkey-here' } 43 - ]; 44 - ``` 45 - 46 - ### Static Files 47 - 48 - Update or remove these files that are specific to the example site: 49 - 50 - - `static/robots.txt` - Update the sitemap URL 51 - - `static/sitemap.xml` - Update with your domain and pages 52 - - `static/.well-known/*` - These files are specific to ewancroft.uk and should be replaced with your own 53 - 54 - ### Favicon and Assets 32 + - **Bluesky Post Display**: 33 + - Showcase latest non-reply posts with rich media support 34 + - Full thread context with recursive parent fetching 35 + - Quoted post embedding with media preservation 36 + - Image galleries with alt text support 37 + - External link cards with preview generation 38 + - Video embed support 55 39 56 - Replace the favicons in `static/favicon/` with your own branding. 40 + - **Engagement Tracking**: 41 + - Real-time like and repost counts via Constellation API 42 + - Paginated engagement data fetching 43 + - Cached engagement metrics for performance 57 44 58 - ## 🚀 Getting Started 45 + ### Music Integration (via teal.fm) 59 46 60 - ### Prerequisites 47 + - **Now Playing Display**: Show currently playing or recently played tracks via `fm.teal.alpha.actor.status` 48 + - **Play History**: Display listening history via `fm.teal.alpha.feed.play` 49 + - **Album Artwork**: 50 + - **Primary**: MusicBrainz Cover Art Archive integration (no API key required!) 51 + - **Automatic Search**: Searches MusicBrainz when release IDs are missing 52 + - **Smart Caching**: Caches MusicBrainz lookups to avoid repeated searches 53 + - **Fallback**: AT Protocol blob storage for custom artwork 54 + - **Rich Metadata**: Artist names, album info, duration, and relative timestamps 55 + - **Multi-Service Support**: Works with Last.fm, Spotify, and other scrobbling services 56 + - **Intelligent Expiry**: Automatically handles expired "now playing" status 61 57 62 - - Node.js 18+ and npm 63 - - An AT Protocol DID (Decentralised Identifier) from Bluesky 58 + ### Developer Tools 64 59 65 - ### Installation 60 + - **Tangled Repository Display**: Showcase your code repositories using the `sh.tangled.repo` lexicon 61 + - **Repository Cards**: Display with descriptions, creation dates, labels, and source links 62 + - **Automatic Sorting**: Repos sorted by creation date (newest first) 66 63 67 - 1. Clone the repository: 64 + ### User Experience 68 65 69 - ```bash 70 - git clone git@github.com:ewanc26/website.git 71 - cd website-redesign 72 - ``` 66 + - **Link Board**: Display curated link collections from Linkat (`blue.linkat.board`) with emoji icons 67 + - **Dark Mode**: Seamless light/dark theme switching with system preference detection 68 + - **Wolf Mode**: Fun "wolf speak" text transformation toggle that converts text to wolf sounds while preserving: 69 + - Numbers and abbreviations (1K, 2M, 30s, etc.) 70 + - Capitalization patterns (UPPERCASE → AWOO, Capitalized → Awoo) 71 + - Punctuation and formatting 72 + - Navigation and interactive elements 73 + - **Scroll to Top**: Smooth scroll-to-top button for long pages 74 + - **Responsive Design**: Mobile-first layout that adapts to all screen sizes 75 + - **SEO Optimization**: Comprehensive meta tags, Open Graph, and Twitter Card support 76 + - **RSS/Atom Feeds**: Multiple feed endpoints for blog posts and status updates 73 77 74 - 1. Install dependencies: 78 + ### Technical Features 75 79 76 - ```bash 77 - npm install 78 - ``` 80 + - **Type-Safe Development**: Full TypeScript support with comprehensive type definitions 81 + - **Smart Error Handling**: Graceful degradation with informative error states 82 + - **Loading States**: Skeleton loaders for all async content 83 + - **Image Optimization**: Lazy loading and responsive image handling 84 + - **Blob URL Construction**: Proper PDS blob URL generation for media assets 85 + - **Media Extraction**: Automatic CID extraction from various image object formats 86 + - **Facet Processing**: Rich text with link detection and mention highlighting 79 87 80 - 1. Configure environment variables: 88 + ## 📋 Configuration 81 89 82 - Copy the example environment file and update it with your own information: 90 + Before using this template, you'll need to update several configuration files with your own information: 83 91 84 - ```bash 85 - cp .env .env.local 86 - ``` 92 + ### Environment Variables (`.env`) 87 93 88 - Edit `.env.local` with your settings: 94 + Create a `.env.local` file with your configuration: 89 95 90 96 ```ini 91 97 # Required: Your AT Protocol DID ··· 94 100 # Optional: Enable WhiteWind blog support (default: false) 95 101 PUBLIC_ENABLE_WHITEWIND=false 96 102 103 + # Optional: Custom domain for Leaflet publications 104 + PUBLIC_LEAFLET_BASE_PATH=https://blog.example.com 105 + 97 106 # Optional: Fallback URL for missing blog posts 98 - PUBLIC_BLOG_FALLBACK_URL= 107 + PUBLIC_BLOG_FALLBACK_URL=https://archive.example.com/blog 99 108 100 109 # Site metadata 101 110 PUBLIC_SITE_TITLE="Your Site Title" 102 111 PUBLIC_SITE_DESCRIPTION="Your site description" 103 - PUBLIC_SITE_KEYWORDS="keywords, here" 112 + PUBLIC_SITE_KEYWORDS="keywords, separated, by, commas" 104 113 PUBLIC_SITE_URL="https://example.com" 105 114 ``` 106 115 107 - 1. Configure your publication slugs in `src/lib/config/slugs.ts`: 116 + ### Publication Slug Mappings (`src/lib/config/slugs.ts`) 117 + 118 + Map friendly URLs to your Leaflet publications: 108 119 109 120 ```typescript 110 121 export const slugMappings: SlugMapping[] = [ 111 - { 112 - slug: 'blog', 113 - publicationRkey: '3m3x4bgbsh22k' // Your publication rkey 114 - } 115 - // Add more mappings as needed: 116 - // { slug: 'notes', publicationRkey: 'xyz123abc' }, 117 - // { slug: 'essays', publicationRkey: 'def456ghi' }, 122 + { slug: 'blog', publicationRkey: '3m3x4bgbsh22k' }, 123 + { slug: 'essays', publicationRkey: 'abc123xyz' }, 124 + { slug: 'notes', publicationRkey: 'def456uvw' } 118 125 ]; 119 126 ``` 120 127 121 - 1. Start the development server: 128 + ### Static Files 129 + 130 + Update or remove these files that are specific to the example site: 131 + 132 + - `static/robots.txt` - Update the sitemap URL 133 + - `static/sitemap.xml` - Update with your domain and pages 134 + - `static/.well-known/*` - Replace with your own well-known files 135 + - `static/favicon/` - Replace with your branding 136 + 137 + ## 🚀 Getting Started 138 + 139 + ### Prerequisites 140 + 141 + - Node.js 18+ and npm 142 + - An AT Protocol DID (Decentralized Identifier) from Bluesky 143 + 144 + ### Installation 145 + 146 + 1. **Clone the repository**: 147 + 148 + ```bash 149 + git clone git@github.com:ewanc26/website.git 150 + cd website 151 + ``` 152 + 153 + 2. **Install dependencies**: 154 + 155 + ```bash 156 + npm install 157 + ``` 158 + 159 + 3. **Configure environment variables**: 122 160 123 - ```bash 124 - npm run dev 125 - ``` 161 + ```bash 162 + cp .env .env.local 163 + ``` 164 + 165 + Edit `.env.local` with your settings (see Configuration section above) 166 + 167 + 4. **Configure publication slugs** in `src/lib/config/slugs.ts` 168 + 169 + 5. **Start the development server**: 170 + 171 + ```bash 172 + npm run dev 173 + ``` 126 174 127 - Visit `http://localhost:5173` to view your site. 175 + Visit `http://localhost:5173` to view your site 128 176 129 177 ## 📁 Project Structure 130 178 131 179 ```text 132 - website-redesign/ 180 + website/ 133 181 ├── src/ 134 182 │ ├── lib/ 135 - │ │ ├── assets/ # Static assets (images, icons) 136 - │ │ ├── components/ # Reusable Svelte components 137 - │ │ │ ├── layout/ # Header, Footer, Navigation 138 - │ │ │ └── ui/ # UI components (Card, etc.) 139 - │ │ ├── config/ # Configuration files 140 - │ │ │ └── slugs.ts # Slug to publication mapping 141 - │ │ ├── data/ # Static data (navigation items) 142 - │ │ ├── helper/ # Helper functions (meta tags, OG images) 143 - │ │ ├── services/ # External service integrations 144 - │ │ │ └── atproto/ # AT Protocol service layer 145 - │ │ └── utils/ # Utility functions 146 - │ ├── routes/ # SvelteKit routes 147 - │ │ ├── [slug]/ # Dynamic slug-based publication routes 148 - │ │ ├── now/ # Status-feed endpoints 149 - │ │ └── site/ # Site-metadata pages 150 - │ ├── app.css # Global styles 151 - │ └── app.html # HTML template 152 - ├── static/ # Static files (favicon, etc.) 183 + │ │ ├── assets/ # Static assets (images, icons) 184 + │ │ ├── components/ # Reusable Svelte components 185 + │ │ │ ├── layout/ # Header, Footer, Navigation, ThemeToggle, WolfToggle 186 + │ │ │ │ └── main/ 187 + │ │ │ │ ├── card/ # ProfileCard, MusicStatusCard, etc. 188 + │ │ │ │ ├── DynamicLinks.svelte 189 + │ │ │ │ ├── ScrollToTop.svelte 190 + │ │ │ │ └── TangledRepos.svelte 191 + │ │ │ ├── seo/ # MetaTags component 192 + │ │ │ └── ui/ # Reusable UI components (Card, etc.) 193 + │ │ ├── config/ # Configuration files 194 + │ │ │ └── slugs.ts # Slug to publication mapping 195 + │ │ ├── data/ # Static data (navigation items) 196 + │ │ ├── helper/ # Helper functions (meta tags, OG images) 197 + │ │ ├── services/ # External service integrations 198 + │ │ │ └── atproto/ # AT Protocol service layer 199 + │ │ │ ├── agents.ts # Agent management & PDS resolution 200 + │ │ │ ├── cache.ts # In-memory caching 201 + │ │ │ ├── engagement.ts # Post engagement (likes/reposts) 202 + │ │ │ ├── fetch.ts # Profile, status, site info, music status 203 + │ │ │ ├── media.ts # Blob URL & image handling 204 + │ │ │ ├── musicbrainz.ts # MusicBrainz API integration 205 + │ │ │ ├── posts.ts # Blog posts, Bluesky posts, publications 206 + │ │ │ ├── tangled.ts # Tangled repository fetching 207 + │ │ │ └── types.ts # TypeScript type definitions 208 + │ │ ├── stores/ # Svelte stores 209 + │ │ │ └── wolfMode.ts # Wolf mode text transformation 210 + │ │ └── utils/ # Utility functions (date formatting, etc.) 211 + │ ├── routes/ # SvelteKit routes 212 + │ │ ├── [slug=slug]/ # Dynamic slug-based publication routes 213 + │ │ │ ├── [rkey]/ # Individual document redirects 214 + │ │ │ ├── atom/ # Deprecated Atom feeds (410 Gone) 215 + │ │ │ └── rss/ # RSS feed endpoints 216 + │ │ ├── favicon.ico/ # Favicon endpoint 217 + │ │ ├── now/ # Status feed endpoints 218 + │ │ │ ├── atom/ # Deprecated Atom feeds 219 + │ │ │ └── rss/ # RSS feeds 220 + │ │ └── site/ 221 + │ │ └── meta/ # Site metadata page 222 + │ ├── app.css # Global styles 223 + │ └── app.html # HTML template 224 + ├── static/ # Static files (favicon, robots.txt, etc.) 153 225 └── package.json 154 226 ``` 155 227 ··· 160 232 ### Core Services 161 233 162 234 - **agents.ts**: Agent management with automatic PDS resolution and fallback to the Bluesky public API 163 - - **fetch.ts**: Profile, status, site info, and links fetching 164 - - **posts.ts**: Blog posts, Leaflet publications, and Bluesky posts 165 - - **media.ts**: Image and blob-URL handling 166 - - **cache.ts**: In-memory caching with TTL support 167 - - **types.ts**: TypeScript definitions for all data structures 235 + - **fetch.ts**: Profile, status, site info, links, and music status fetching 236 + - **posts.ts**: Blog posts (WhiteWind & Leaflet), Bluesky posts, and publications 237 + - **tangled.ts**: Repository information from Tangled lexicon 238 + - **engagement.ts**: Post engagement data (likes/reposts) via Constellation API 239 + - **media.ts**: Image and blob URL handling with CID extraction 240 + - **musicbrainz.ts**: MusicBrainz API integration for album artwork 241 + - **cache.ts**: In-memory caching with configurable TTL support 242 + - **types.ts**: Comprehensive TypeScript definitions for all data structures 168 243 169 - ### Usage Example 244 + ### Usage Examples 170 245 171 246 ```typescript 172 - import { fetchProfile, fetchBlogPosts, fetchLatestBlueskyPost } from '$lib/services/atproto'; 247 + import { 248 + fetchProfile, 249 + fetchBlogPosts, 250 + fetchLatestBlueskyPost, 251 + fetchMusicStatus, 252 + fetchTangledRepos 253 + } from '$lib/services/atproto'; 173 254 174 255 // Fetch profile data 175 256 const profile = await fetchProfile(); 176 257 177 - // Fetch blog posts from WhiteWind and Leaflet 258 + // Fetch blog posts from WhiteWind and/or Leaflet 178 259 const { posts } = await fetchBlogPosts(); 179 260 180 261 // Fetch latest Bluesky post 181 262 const post = await fetchLatestBlueskyPost(); 263 + 264 + // Fetch current or last played music 265 + const musicStatus = await fetchMusicStatus(); 266 + 267 + // Fetch code repositories 268 + const repos = await fetchTangledRepos(); 182 269 ``` 183 270 184 271 ## 📝 Publication System ··· 192 279 ```typescript 193 280 export const slugMappings: SlugMapping[] = [ 194 281 { 195 - slug: 'blog', // Access via /blog 282 + slug: 'blog', // Access via /blog 196 283 publicationRkey: '3m3x4bgbsh22k' // Leaflet publication rkey 197 284 }, 198 285 { 199 - slug: 'notes', // Access via /notes 286 + slug: 'notes', // Access via /notes 200 287 publicationRkey: 'xyz123abc' 201 288 } 202 289 ]; ··· 204 291 205 292 ### Supported Platforms 206 293 207 - 1. **Leaflet*- (`pub.leaflet.document`) – **Prioritised by default** 208 - 294 + 1. **Leaflet** (`pub.leaflet.document`) – **Prioritized by default** 209 295 - Format: Custom domain or `https://leaflet.pub/lish/{did}/{publication}/{rkey}` 210 296 - Supports multiple publications via slug mapping 211 297 - Respects `base_path` configuration 212 298 - Always checked first 213 299 214 - 2. **WhiteWind*- (`com.whtwnd.blog.entry`) – **Optional, disabled by default** 215 - 300 + 2. **WhiteWind** (`com.whtwnd.blog.entry`) – **Optional, disabled by default** 216 301 - Format: `https://whtwnd.com/{did}/{rkey}` 217 302 - Automatically filters out drafts and non-public posts 218 303 - Only checked if `PUBLIC_ENABLE_WHITEWIND=true` ··· 222 307 - `/{slug}` – Redirects to your publication homepage (configured in slugs.ts) 223 308 - `/{slug}/{rkey}` – Smart redirect to the correct platform (checks Leaflet first, then WhiteWind if enabled) 224 309 - `/{slug}/rss` – Intelligent RSS feed (redirects to Leaflet RSS by default, or generates WhiteWind RSS if enabled) 225 - - `/{slug}/atom` – Deprecated (returns *410 Gone*, use RSS instead) 310 + - `/{slug}/atom` – Deprecated (returns 410 Gone, use RSS instead) 226 311 227 - ### How It Works 312 + ### Priority Order 228 313 229 - **Priority Order:** 230 - 231 - 1. **Leaflet*- is always checked first for publications and documents 314 + 1. **Leaflet** is always checked first for publications and documents 232 315 2. The slug mapping determines which publication to check 233 - 3. **WhiteWind*- is only checked if `PUBLIC_ENABLE_WHITEWIND=true` 316 + 3. **WhiteWind** is only checked if `PUBLIC_ENABLE_WHITEWIND=true` 234 317 4. If neither platform has the document, it falls back to `PUBLIC_BLOG_FALLBACK_URL` if configured 235 - 5. Returns *404- if the document isn't found and no fallback is set 318 + 5. Returns 404 if the document isn't found and no fallback is set 236 319 237 - **When a user visits `/{slug}/{rkey}`:** 238 - 239 - 1. The system looks up the publication rkey from the slug configuration 240 - 2. It checks Leaflet for the document in that specific publication 241 - 3. If not found and WhiteWind is enabled, it checks WhiteWind 242 - 4. Redirects to the appropriate platform URL 243 - 5. Falls back to `PUBLIC_BLOG_FALLBACK_URL` if configured 244 - 6. Returns 404 if no document is found and no fallback exists 245 - 246 - **RSS Feed Behaviour:** 320 + ### RSS Feed Behavior 247 321 248 - - **WhiteWind disabled*- (default): Redirects to Leaflet's native RSS feed (includes full content) 322 + - **WhiteWind disabled** (default): Redirects to Leaflet's native RSS feed (includes full content) 249 323 - **WhiteWind enabled with posts**: Generates an RSS feed with WhiteWind post links 250 324 - **No posts found**: Returns 404 251 325 252 - ### Platform Configuration (Leaflet / WhiteWind) 326 + ### Finding Your Publication Rkey 253 327 254 - Control publication behaviour with environment variables: 328 + 1. Visit your Leaflet publication page 329 + 2. The URL will be in the format: `https://leaflet.pub/lish/{did}/{rkey}` 330 + 3. Copy the `{rkey}` part (e.g., `3m3x4bgbsh22k`) 331 + 4. Add it to your slug mapping in `src/lib/config/slugs.ts` 255 332 256 - ```ini 257 - # Use a custom domain for Leaflet posts (recommended) 258 - PUBLIC_LEAFLET_BASE_PATH=https://blog.example.com 333 + ## 🎵 Music Integration 259 334 260 - # Enable WhiteWind support (set to "true" to enable, default: "false") 261 - PUBLIC_ENABLE_WHITEWIND=false 335 + The site displays your music listening activity via teal.fm integration: 262 336 263 - # Fallback for missing posts 264 - PUBLIC_BLOG_FALLBACK_URL=https://archive.example.com/blog 265 - ``` 337 + ### Supported Record Types 266 338 267 - And configure your slug mappings in `src/lib/config/slugs.ts`: 339 + - **`fm.teal.alpha.actor.status`**: Current "Now Playing" status with expiry 340 + - **`fm.teal.alpha.feed.play`**: Historical play records 268 341 269 - ```typescript 270 - export const slugMappings: SlugMapping[] = [ 271 - { slug: 'blog', publicationRkey: '3m3x4bgbsh22k' }, 272 - { slug: 'essays', publicationRkey: 'abc123xyz' }, 273 - { slug: 'notes', publicationRkey: 'def456uvw' } 274 - ]; 275 - ``` 342 + ### Album Artwork System 276 343 277 - ### Why Leaflet is Prioritised 344 + The music card uses a sophisticated artwork retrieval system: 278 345 279 - - **Better Performance**: Leaflet's RSS feeds include full post content 280 - - **Custom Domains**: Each publication can have its own `base_path` configured in Leaflet 281 - - **Rich Features**: Better media handling and publication management 282 - - **Multiple Publications**: Easy management of multiple publications with slug mapping 283 - - **Active Development**: Leaflet is actively maintained and improved 346 + 1. **MusicBrainz Cover Art Archive** (Primary) 347 + - Uses `releaseMbId` from music records 348 + - Free, no API key required 349 + - Automatic search fallback when IDs are missing 350 + - Caches search results to avoid repeated lookups 284 351 285 - ### Enabling WhiteWind 352 + 2. **AT Protocol Blob Storage** (Fallback) 353 + - Uses `artwork` field from records 354 + - Proper PDS blob URL construction 286 355 287 - If you still use WhiteWind or want to support both platforms: 356 + ### Features 288 357 289 - ```ini 290 - PUBLIC_ENABLE_WHITEWIND=true 291 - ``` 358 + - Displays track name, artists, album, and duration 359 + - Shows relative timestamps ("2 minutes ago") 360 + - Links to origin URLs (Last.fm, Spotify, etc.) 361 + - Responsive artwork display with fallback icons 362 + - Smart caching with 5-minute TTL 363 + - Automatic status expiry handling 292 364 293 - With WhiteWind enabled: 365 + ### Configuration 294 366 295 - - Documents are checked on both platforms (Leaflet first, then WhiteWind) 296 - - RSS feed includes WhiteWind posts if they exist 297 - - `/{slug}` redirects to WhiteWind if no Leaflet configuration is set 367 + Set your DID in `.env.local` to fetch your music status: 298 368 299 - ### Finding Your Publication Rkey 369 + ```ini 370 + PUBLIC_ATPROTO_DID=did:plc:your-did-here 371 + ``` 300 372 301 - 1. Visit your Leaflet publication page 302 - 2. The URL will be in the format: `https://leaflet.pub/lish/{did}/{rkey}` 303 - 3. Copy the `{rkey}` part (e.g., `3m3x4bgbsh22k`) 304 - 4. Add it to your slug mapping in `src/lib/config/slugs.ts` 373 + The card will automatically display your current or last played track. 305 374 306 375 ## 🎨 Styling 307 376 308 377 The project uses: 309 378 310 - - **Tailwind CSS 4**: Latest Tailwind with new features 311 - - **@tailwindcss/typography**: Beautiful prose styling 312 - - **@tailwindcss/vite**: Vite plugin for Tailwind 313 - - **Custom Components**: Pre-built UI components with consistent styling 379 + - **Tailwind CSS 4**: Latest Tailwind with new features and improved performance 380 + - **@tailwindcss/typography**: Beautiful prose styling for blog content 381 + - **@tailwindcss/vite**: Vite plugin for optimal Tailwind integration 382 + - **Custom Color Palette**: Semantic color tokens (canvas, ink, primary) for consistent theming 383 + - **Dark Mode**: System preference detection with manual override 384 + - **Responsive Design**: Mobile-first approach with breakpoint utilities 314 385 315 386 ## 🏗️ Building for Production 316 387 ··· 328 399 329 400 This project uses `@sveltejs/adapter-auto`, which automatically selects the best adapter for your deployment platform: 330 401 331 - - **Vercel**: Automatic detection and optimisation 332 - - **Netlify**: Automatic detection and optimisation 333 - - **Cloudflare Pages**: Automatic detection and optimisation 402 + - **Vercel**: Automatic detection and optimization 403 + - **Netlify**: Automatic detection and optimization 404 + - **Cloudflare Pages**: Automatic detection and optimization 334 405 - **Node.js**: Fallback option 335 406 336 407 For other platforms, see the [SvelteKit adapters documentation](https://kit.svelte.dev/docs/adapters). ··· 339 410 340 411 The site supports several custom AT Protocol lexicons: 341 412 342 - ### Status Updates (`uk.ewancroft.now`) 343 - 344 - Display real-time status messages on your site. 345 - 346 413 ### Site Information (`uk.ewancroft.site.info`) 347 414 348 - Store detailed site metadata including: 415 + Store comprehensive site metadata: 349 416 350 417 - Technology stack 351 418 - Privacy statement 352 419 - Open-source information 353 - - Credits and licences 420 + - Credits and licenses 421 + - Related services 354 422 355 423 ### Link Board (`blue.linkat.board`) 356 424 357 425 Display a collection of links with emoji icons. 358 426 427 + ### Music Status (`fm.teal.alpha.actor.status` & `fm.teal.alpha.feed.play`) 428 + 429 + Show music listening activity via teal.fm integration. 430 + 431 + ### Tangled Repositories (`sh.tangled.repo`) 432 + 433 + Display code repositories with descriptions, labels, and metadata. 434 + 359 435 ## 🛠️ Development 360 436 361 437 ### Available Scripts ··· 372 448 373 449 The project uses: 374 450 375 - - **TypeScript*- – Full type safety 376 - - **Prettier*- – Consistent code formatting 377 - - **svelte-check*- – Svelte-specific linting 378 - - **ESLint*- – (can be added if needed) 379 - 380 - ## 📚 Reference Implementation 381 - 382 - The `other/leaflet-main` directory contains the reference Leaflet implementation, which was used as inspiration for: 383 - 384 - - Feed generation 385 - - Publication handling 386 - - Document rendering 387 - - Bluesky integration 451 + - **TypeScript** – Full type safety throughout 452 + - **Prettier** – Consistent code formatting 453 + - **svelte-check** – Svelte-specific linting 454 + - **Svelte 5 Runes** – Modern reactivity with better performance 388 455 389 456 ## 🤝 Contributing 390 457 391 458 Contributions are welcome! Please feel free to submit a pull request. 392 459 393 - ## 📄 Licence 460 + 1. Fork the repository 461 + 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 462 + 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 463 + 4. Push to the branch (`git push origin feature/amazing-feature`) 464 + 5. Open a Pull Request 394 465 395 - This project is open-source. See the [licence](./LICENCE) file for more details on the website source code specifically and the [third party licences](./THIRD-PARTY-LICENSES.txt) file for the rest. 466 + ## 📄 License 467 + 468 + This project is open-source. See the [LICENSE](./LICENSE) file for more details on the website source code specifically and the [THIRD-PARTY-LICENSES.txt](./THIRD-PARTY-LICENSES.txt) file for third-party dependencies. 396 469 397 470 ## 🔗 Links 398 471 ··· 402 475 - [Bluesky](https://bsky.app/) 403 476 - [WhiteWind](https://whtwnd.com/) 404 477 - [Leaflet](https://leaflet.pub/) 478 + - [teal.fm](https://teal.fm/) 479 + - [MusicBrainz](https://musicbrainz.org/) 480 + - [Tangled](https://tangled.sh/) 481 + - [Linkat](https://linkat.blue/) 405 482 406 - ## 💡 Tips 483 + ## 💡 Tips & Troubleshooting 407 484 408 485 ### Finding Your DID 409 486 410 - 1. visit [PDSls](https://pdsls.dev/) 411 - 1. enter your handle (i.e. `ewancroft.uk`) 412 - 1. you will see a `did:plc` (or `did:web`) in the Repository field, that is your DID 413 - 414 - > if not, select the arrow to the right of the text, it might show your handle instead. 487 + 1. Visit [PDSls](https://pdsls.dev/) 488 + 2. Enter your handle (e.g., `ewancroft.uk`) 489 + 3. Look for the `did:plc` (or `did:web`) in the Repository field 490 + 4. If not visible, click the arrow to the right of the text 415 491 416 492 ### Cache Management 417 493 ··· 425 501 426 502 // Clear a specific entry 427 503 cache.delete('profile:did:plc:...'); 504 + 505 + // Get cache statistics 506 + const profile = cache.get<ProfileData>('profile:did:plc:...'); 428 507 ``` 429 508 430 - ### Custom Components 509 + ### Music Status Not Showing Artwork 431 510 432 - All components are built with Svelte 5 runes for better reactivity and performance. See the components directory for examples. 511 + If your music status doesn't show album artwork: 433 512 434 - ## 🐛 Troubleshooting 513 + 1. Ensure your scrobbler (e.g., piper) is including `releaseMbId` in records 514 + 2. The system will automatically search MusicBrainz if IDs are missing 515 + 3. Check browser console for MusicBrainz search results 516 + 4. Fallback to blob storage if available 517 + 5. Icon placeholder displays if no artwork is found 435 518 436 519 ### Documents Not Found 437 520 438 - 1. Check your `PUBLIC_ATPROTO_DID` is correct 439 - 2. Verify the slug mapping in `src/lib/config/slugs.ts` is correct 440 - 3. Ensure the publication rkey matches your Leaflet publication 441 - 4. Verify documents are not drafts (WhiteWind) or unpublished (Leaflet) 442 - 5. If using WhiteWind, ensure `PUBLIC_ENABLE_WHITEWIND=true` is set 443 - 6. Check the browser console for AT Protocol service errors 444 - 445 - ### Slug Not Found 446 - 447 - 1. Add your slug mapping to `src/lib/config/slugs.ts` 448 - 2. Ensure the format is: `{ slug: 'your-slug', publicationRkey: 'your-rkey' }` 449 - 3. Restart the development server after changes 521 + 1. Verify `PUBLIC_ATPROTO_DID` is correct 522 + 2. Check slug mapping in `src/lib/config/slugs.ts` 523 + 3. Ensure publication rkey matches your Leaflet publication 524 + 4. Verify documents are published (not drafts) 525 + 5. If using WhiteWind, ensure `PUBLIC_ENABLE_WHITEWIND=true` 526 + 6. Check browser console for AT Protocol service errors 450 527 451 - ### Profile Data Not Loading 528 + ### Wolf Mode Not Working 452 529 453 - 1. Ensure your DID is publicly accessible 454 - 2. Check the browser console for errors 455 - 3. Verify AT Protocol services are reachable 530 + 1. Ensure JavaScript is enabled 531 + 2. Check browser console for errors 532 + 3. Wolf mode preserves navigation and interactive elements 533 + 4. Numbers and abbreviations are preserved intentionally 456 534 457 535 ### Build Errors 458 536 459 - 1. Clear the `.svelte-kit` directory: `rm -rf .svelte-kit` 537 + 1. Clear `.svelte-kit` directory: `rm -rf .svelte-kit` 460 538 2. Remove `node_modules`: `rm -rf node_modules` 461 - 3. Reinstall dependencies: `npm install` 462 - 4. Try building again: `npm run build` 539 + 3. Clear package lock: `rm package-lock.json` 540 + 4. Reinstall: `npm install` 541 + 5. Try building: `npm run build` 463 542 464 543 ## 🙏 Acknowledgements 465 544 466 - - Thanks to the AT Protocol team for creating an open protocol 467 - - Thanks to the Bluesky, WhiteWind, and Leaflet teams 545 + - Thanks to the AT Protocol team for creating an open, decentralized protocol 546 + - Thanks to the Bluesky, WhiteWind, Leaflet, teal.fm, Tangled, and Linkat teams 547 + - Thanks to MusicBrainz for providing free album artwork via the Cover Art Archive 468 548 - Inspired by the personal-web movement and IndieWeb principles 549 + - Built with love using modern web technologies 469 550 470 551 --- 471 552 472 - Built with ❤️ using SvelteKit and AT Protocol 553 + Built with ❤️ using SvelteKit, AT Protocol, and open-source tools