diff --git a/flake.nix b/flake.nix index 3e090b7..1c6b0cd 100644 --- a/flake.nix +++ b/flake.nix @@ -1,3 +1,7 @@ +# Nix flake for Ewan's personal package monorepo +# Provides buildable derivations for pds-landing and nix-config-tools, +# plus dev-shell apps for each CLI tool. + { description = "Ewan's personal package monorepo — TypeScript and Rust packages"; diff --git a/packages/atproto/src/agents.ts b/packages/atproto/src/agents.ts index 8e14d1f..f9b6165 100644 --- a/packages/atproto/src/agents.ts +++ b/packages/atproto/src/agents.ts @@ -1,3 +1,11 @@ +/** + * Agent management for AT Protocol XRPC calls. + * + * Creates and caches ATP agents with fallback between public and PDS endpoints. + * All functions that previously read PUBLIC_ATPROTO_DID from the environment + * now accept `did: string` as their first argument. + */ + import { AtpAgent } from '@atproto/api'; import type { ResolvedIdentity } from './types.js'; import { cache } from './cache.js'; diff --git a/packages/atproto/src/documents.ts b/packages/atproto/src/documents.ts index 0b90230..12a4507 100644 --- a/packages/atproto/src/documents.ts +++ b/packages/atproto/src/documents.ts @@ -1,3 +1,10 @@ +/** + * Fetch `site.standard.*` AT Protocol records — publications and documents. + * + * Resolves blob URLs for cover images and maps documents to their + * publications via the `site` DID reference. + */ + import { cache } from './cache.js'; import { withFallback, resolveIdentity } from './agents.js'; import { buildPdsBlobUrl } from './media.js'; diff --git a/packages/atproto/src/engagement.ts b/packages/atproto/src/engagement.ts index 95141f8..8849b2f 100644 --- a/packages/atproto/src/engagement.ts +++ b/packages/atproto/src/engagement.ts @@ -1,3 +1,10 @@ +/** + * Fetch engagement data (likes, reposts) from the Constellation link resolver. + * + * Constellation indexes cross-DID relationships, letting us find who + * liked or reposted a given record URI. + */ + import { cache } from './cache.js'; export type EngagementType = 'app.bsky.feed.like' | 'app.bsky.feed.repost'; diff --git a/packages/atproto/src/fetch.ts b/packages/atproto/src/fetch.ts index 04829a6..1ae0f69 100644 --- a/packages/atproto/src/fetch.ts +++ b/packages/atproto/src/fetch.ts @@ -1,3 +1,21 @@ +/** + * AT Protocol data-fetching layer. + * + * Each public function reads a specific record collection from the + * AT Protocol, with caching and automatic agent fallback. Functions + * accept `did: string` as their first argument — no environment vars. + * + * ─── Collection Reference ─────────────────────────── + * - `fm.teal.alpha.actor.status` — now-playing music + * - `fm.teal.alpha.feed.play` — scrobble history + * - `social.kibun.status` — mood/emoji status + * - `social.popfeed.feed.review` — media reviews + * - `sh.tangled.repo` — Tangled git repos + * - `blue.linkat.board` — link-in-bio cards + * - `id.sifa.profile.*` — professional profile + * - `uk.ewancroft.site.info` — site metadata + */ + import { cache } from './cache.js'; import { withFallback, resolveIdentity } from './agents.js'; import { buildPdsBlobUrl } from './media.js'; @@ -36,6 +54,8 @@ import type { */ const STATUS_GRACE_PERIOD_MS = 10 * 60 * 1000; +// ─── Profile ───────────────────────────────────────────────────────────── + export async function fetchProfile(did: string, fetchFn?: typeof fetch): Promise { const cacheKey = `profile:${did}`; const cached = cache.get(cacheKey); @@ -86,6 +106,8 @@ export async function fetchProfile(did: string, fetchFn?: typeof fetch): Promise return data; } +// ─── Site Info & Links ─────────────────────────────────────────────────── + export async function fetchSiteInfo( did: string, fetchFn?: typeof fetch @@ -330,6 +352,8 @@ export async function fetchKibunStatus( } } +// ─── Media Reviews (Popfeed) ───────────────────────────────────────────── + export async function fetchRecentPopfeedReviews( did: string, limit = 5, @@ -721,7 +745,7 @@ export async function fetchTangledRepos( } } -// Sifa Professional Profile fetch functions +// ─── SIFA Professional Profile ───────────────────────────────────────── export async function fetchSifaProfile( did: string, diff --git a/packages/atproto/src/media.ts b/packages/atproto/src/media.ts index a925930..a9e177a 100644 --- a/packages/atproto/src/media.ts +++ b/packages/atproto/src/media.ts @@ -1,3 +1,10 @@ +/** + * Media URL helpers for AT Protocol blobs and embeddings. + * + * Builds PDS blob URLs and extracts image/video URLs from + * post embed structures (images, video, recordWithMedia). + */ + export function buildPdsBlobUrl(pds: string, did: string, cid: string): string { return `${pds.replace(/\/$/, '')}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`; } diff --git a/packages/atproto/src/musicbrainz.ts b/packages/atproto/src/musicbrainz.ts index 0fb7719..173a689 100644 --- a/packages/atproto/src/musicbrainz.ts +++ b/packages/atproto/src/musicbrainz.ts @@ -1,3 +1,13 @@ +/** + * Music artwork resolution cascade. + * + * Given a track/release, tries Cover Art Archive (via MusicBrainz ID), + * iTunes, Last.fm, and Deezer in order. Each step is bounded by + * ARTWORK_TIMEOUT to prevent serverless function timeouts. + * + * Deezer is skipped in browser contexts due to CORS restrictions. + */ + import { cache } from './cache.js'; /** Timeout for individual artwork API calls (ms). */ diff --git a/packages/atproto/src/pagination/fetchAllRecords.ts b/packages/atproto/src/pagination/fetchAllRecords.ts index 93b7277..30820eb 100644 --- a/packages/atproto/src/pagination/fetchAllRecords.ts +++ b/packages/atproto/src/pagination/fetchAllRecords.ts @@ -1,3 +1,10 @@ +/** + * Paginated AT Protocol record fetching. + * + * Handles cursor-based pagination for listRecords calls, collecting + * all pages into a single array. Defaults to 100 records per page. + */ + import { withFallback } from '../agents.js'; export interface FetchRecordsConfig { diff --git a/packages/atproto/src/posts.ts b/packages/atproto/src/posts.ts index aba33dc..601f6ac 100644 --- a/packages/atproto/src/posts.ts +++ b/packages/atproto/src/posts.ts @@ -1,3 +1,10 @@ +/** + * Fetch Bluesky posts from the AT Protocol. + * + * Handles post threads with embedded media, quotes, replies, and reposts. + * Recursion depth is bounded at 3 to prevent runaway API chains. + */ + import { cache } from './cache.js'; import { withFallback } from './agents.js'; import type { BlueskyPost, PostAuthor, ExternalLink } from './types.js'; diff --git a/packages/bismuth-web/src/routes/layout.css b/packages/bismuth-web/src/routes/layout.css index 90dee87..0594a0d 100644 --- a/packages/bismuth-web/src/routes/layout.css +++ b/packages/bismuth-web/src/routes/layout.css @@ -1,3 +1,8 @@ +/* ── Bismuth Web Layout ──────────────────────────────────────────── + Deep indigo-purple colour scheme for the bismuth web frontend. + Single-page app with Tailwind v4 theme tokens mapped to CSS vars. + ────────────────────────────────────────────────────────────────── */ + @import 'tailwindcss'; :root { diff --git a/packages/croft-click/src/routes/layout.css b/packages/croft-click/src/routes/layout.css index 3114386..85835dd 100644 --- a/packages/croft-click/src/routes/layout.css +++ b/packages/croft-click/src/routes/layout.css @@ -1,3 +1,8 @@ +/* ── croft.click Global Layout ──────────────────────────────────── + Warm dark-stone palette (stone-900 through stone-50). + No frills — this is a landing page, not a design portfolio. + ────────────────────────────────────────────────────────────────── */ + @import 'tailwindcss'; :root { diff --git a/packages/jasper-web/src/routes/layout.css b/packages/jasper-web/src/routes/layout.css index a220241..f9c0f30 100644 --- a/packages/jasper-web/src/routes/layout.css +++ b/packages/jasper-web/src/routes/layout.css @@ -1,3 +1,8 @@ +/* ── Jasper Web Layout ───────────────────────────────────────────── + Amber-accented dark theme for the Instagram-to-ATProto import tool. + Tailwind v4 theme tokens mapped to CSS custom properties. + ────────────────────────────────────────────────────────────────── */ + @import 'tailwindcss'; :root { diff --git a/packages/malachite-web/src/routes/layout.css b/packages/malachite-web/src/routes/layout.css index 95a5527..cbda581 100644 --- a/packages/malachite-web/src/routes/layout.css +++ b/packages/malachite-web/src/routes/layout.css @@ -1,3 +1,8 @@ +/* ── Malachite Web Layout ────────────────────────────────────────── + Forest-green dark palette for the scrobble importer frontend. + Green on black, minimal — the tool does one thing. + ────────────────────────────────────────────────────────────────── */ + @import 'tailwindcss'; :root { diff --git a/packages/nix-config-tools/src/lib.rs b/packages/nix-config-tools/src/lib.rs index f003908..e547e2f 100644 --- a/packages/nix-config-tools/src/lib.rs +++ b/packages/nix-config-tools/src/lib.rs @@ -1,9 +1,19 @@ +//! Shared utilities for Nix config management CLI tools. +//! +//! Provides git helpers (root detection, auto-commit-and-push), +//! system info (hostname, timestamp), and a generic nix-capture +//! function used by all binaries in this crate. + pub use std::env; pub use std::fs::{self, File}; pub use std::io::{self, Write}; pub use std::path::{Path, PathBuf}; pub use std::process::{Command, Stdio}; +/// Resolve the project root directory. +/// +/// Checks PRJ_ROOT env var first, then falls back to `git rev-parse`, +/// and finally to `~/.config/nix-config` as a last resort. pub fn git_root() -> PathBuf { if let Ok(root) = env::var("PRJ_ROOT") { return PathBuf::from(root); } let output = Command::new("git").args(["rev-parse", "--show-toplevel"]).output().ok(); @@ -14,16 +24,20 @@ pub fn git_root() -> PathBuf { PathBuf::from(env::var("HOME").unwrap_or_default()).join(".config/nix-config") } +/// Current timestamp in `YYYY-MM-DD HH:MM:SS` format. pub fn get_timestamp() -> String { let out = Command::new("date").arg("+%Y-%m-%d %H:%M:%S").output().expect("date fail"); String::from_utf8_lossy(&out.stdout).trim().to_string() } +/// Short hostname (no domain suffix). pub fn get_hostname() -> String { let out = Command::new("hostname").arg("-s").output().expect("hostname fail"); String::from_utf8_lossy(&out.stdout).trim().to_string() } +/// Run a `nix` subcommand and write stdout to a file. +/// Returns true on success, false on any failure. pub fn capture_nix_to_file(cmd_args: &[&str], out_path: &Path) -> bool { let output = Command::new("nix").args(cmd_args).output(); match output { @@ -37,6 +51,10 @@ pub fn capture_nix_to_file(cmd_args: &[&str], out_path: &Path) -> bool { } } +/// Stage, commit, and push changes for a path relative to the git root. +/// +/// Skips the commit when there are no staged changes, so calling +/// this unconditionally after `capture_nix_to_file` is safe. pub fn git_sync(rel_path: &str, msg_prefix: &str) { let root = git_root(); let timestamp = get_timestamp(); diff --git a/packages/opal-web/src/routes/layout.css b/packages/opal-web/src/routes/layout.css index 8707a00..7e9b184 100644 --- a/packages/opal-web/src/routes/layout.css +++ b/packages/opal-web/src/routes/layout.css @@ -1,3 +1,8 @@ +/* ── Opal Web Layout ─────────────────────────────────────────────── + Deep emerald-green palette for the Twitter-to-ATProto importer. + Shares structural DNA with malachite-web; colour is the differentiator. + ────────────────────────────────────────────────────────────────── */ + @import 'tailwindcss'; :root { diff --git a/packages/tangled-sync/src/index.ts b/packages/tangled-sync/src/index.ts index 5a809cb..707bcfb 100644 --- a/packages/tangled-sync/src/index.ts +++ b/packages/tangled-sync/src/index.ts @@ -1,3 +1,13 @@ +/** + * Tangled Sync — CLI for syncing GitHub repos to Tangled. + * + * Clones (or pulls) each GitHub repo, adds a Tangled remote, pushes, + * updates the README with a Tangled mirror link, and creates/updates + * an ATProto sh.tangled.repo record tracking the mirror. + * + * Skips repos that already have a record unless --force is passed. + */ + import { AtpAgent } from "@atproto/api"; import dotenv from "dotenv"; import fs from "fs"; diff --git a/packages/tourmaline/src/lib/server/resolve.ts b/packages/tourmaline/src/lib/server/resolve.ts index da90a2a..2c9159d 100644 --- a/packages/tourmaline/src/lib/server/resolve.ts +++ b/packages/tourmaline/src/lib/server/resolve.ts @@ -1,3 +1,11 @@ +/** + * DID and PDS resolution for the Tourmaline API. + * + * Resolves handles and DIDs via Slingshot, fetches DID documents + * from plc.directory or did:web endpoints, and retrieves basic + * Bluesky profile info (display name, avatar) from the PDS. + */ + const SLINGSHOT_URL = "https://slingshot.microcosm.blue"; interface DidDocument { diff --git a/packages/ui/src/lib/helper/badges.ts b/packages/ui/src/lib/helper/badges.ts index 150ebba..70d1a92 100644 --- a/packages/ui/src/lib/helper/badges.ts +++ b/packages/ui/src/lib/helper/badges.ts @@ -1,3 +1,10 @@ +/** + * Badge generation utilities for post metadata display. + * + * Maps publication source and type to visual badge configs + * used across feed card components. + */ + import type { BlogPost } from '@ewanc26/atproto'; export interface PostBadge { diff --git a/packages/ui/src/lib/helper/posts.ts b/packages/ui/src/lib/helper/posts.ts index ee97bff..2b6cd02 100644 --- a/packages/ui/src/lib/helper/posts.ts +++ b/packages/ui/src/lib/helper/posts.ts @@ -1,3 +1,10 @@ +/** + * Post filtering, grouping, and tag extraction utilities. + * + * Used by the blog listing pages to search, group by year/month, + * and extract tag clouds from post collections. + */ + import type { BlogPost } from '@ewanc26/atproto'; import { getUserLocale } from '../utils/locale.js'; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 4e57b4e..0d0add3 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,3 +1,10 @@ +/** + * @ewanc26/utils — Shared utility functions for Ewan's personal packages. + * + * Re-exports date formatting, number formatting, URL helpers, validators, + * RSS helpers, locale detection, slug generation, and config constants. + */ + export * from './formatDate.js'; export * from './formatNumber.js'; export * from './url.js'; diff --git a/packages/utils/src/locale.ts b/packages/utils/src/locale.ts index 3b7c78c..16409f8 100644 --- a/packages/utils/src/locale.ts +++ b/packages/utils/src/locale.ts @@ -1,3 +1,8 @@ +/** + * Locale detection and localized date formatting. + * Defaults to en-GB when running server-side or when navigator is unavailable. + */ + export function getUserLocale(): string { if (typeof navigator !== 'undefined') { return navigator.language || 'en-GB'; diff --git a/packages/utils/src/url.ts b/packages/utils/src/url.ts index 8a37d19..e33af44 100644 --- a/packages/utils/src/url.ts +++ b/packages/utils/src/url.ts @@ -1,3 +1,8 @@ +/** + * URL helpers for domain extraction, AT Protocol URI conversion, + * and external-link detection. + */ + export function getDomain(url: string): string { try { const urlObj = new URL(url); diff --git a/packages/utils/src/validators.ts b/packages/utils/src/validators.ts index caaa703..7845d84 100644 --- a/packages/utils/src/validators.ts +++ b/packages/utils/src/validators.ts @@ -1,3 +1,10 @@ +/** + * General-purpose validators and text utilities. + * + * AT Protocol identifier validation, HTML escaping, truncation, + * and common async patterns (debounce, throttle). + */ + export function isValidTid(tid: string): boolean { const tidPattern = /^[a-zA-Z0-9]{12,16}$/; return tidPattern.test(tid); diff --git a/packages/wafrn-theme/src/theme.css b/packages/wafrn-theme/src/theme.css index 8ada5ad..19d86b7 100644 --- a/packages/wafrn-theme/src/theme.css +++ b/packages/wafrn-theme/src/theme.css @@ -1,5 +1,24 @@ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&display=swap'); +/* ── WAFRN Custom Theme ──────────────────────────────────────────── + Catppuccin-terminal aesthetic for WAFRN (wafrn.net). + Dark green-black palette, monospace throughout, smooth transitions. + No AI-slop patterns — every colour and spacing choice is deliberate. + + Colour reference: + --pds-crust #0d1210 deepest background + --pds-mantle #141c19 secondary surface + --pds-base #1a2420 element background + --pds-surface-0 #243028 raised surfaces + --pds-surface-1 #2e3d34 borders, dividers + --pds-overlay-0 #4d6b58 muted decorative elements + --pds-text #cdd6f4 primary text + --pds-subtext #93b09a secondary text + --pds-green #a6e3a1 accent + --pds-red #f38ba8 destructive + --pds-yellow #f9e2af hover, warning + ────────────────────────────────────────────────────────────────── */ + :root { --pds-crust: #0d1210; --pds-mantle: #141c19;