From 8bd4763dc8da92324b0296e25620ed864edd8fb5 Mon Sep 17 00:00:00 2001 From: Ewan Croft Date: Sat, 14 Mar 2026 19:23:01 +0000 Subject: [PATCH] docs: replace verbose READMEs with docsite funnels, bump patch versions --- README.md | 77 +- packages/atproto/README.md | 74 +- packages/atproto/package.json | 2 +- packages/llm-analyser/README.md | 75 +- packages/malachite/README.md | 828 +-------------------- packages/nix-config-tools/README.md | 127 +--- packages/noise-avatar/README.md | 56 +- packages/noise-avatar/package.json | 2 +- packages/noise/README.md | 117 +-- packages/noise/package.json | 2 +- packages/pds-landing/README.md | 125 +--- packages/pds-landing/package.json | 2 +- packages/supporters/README.md | 109 +-- packages/supporters/package.json | 2 +- packages/svelte-standard-site/README.md | 101 +-- packages/svelte-standard-site/package.json | 2 +- packages/tangled-sync/README.md | 67 +- packages/tangled-sync/package.json | 2 +- packages/tid/README.md | 134 +--- packages/tid/package.json | 2 +- packages/ui/README.md | 56 +- packages/ui/package.json | 2 +- packages/utils/README.md | 35 +- packages/utils/package.json | 2 +- 24 files changed, 71 insertions(+), 1930 deletions(-) diff --git a/README.md b/README.md index 8269431..b169c9c 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,27 @@ # pkgs -Ewan's personal package monorepo — managed with [pnpm workspaces](https://pnpm.io/workspaces) (TypeScript/Svelte) and [Cargo workspaces](https://doc.rust-lang.org/cargo/reference/workspaces.html) + [Nix flake](https://nixos.wiki/wiki/Flakes) (Rust). +Ewan's personal package monorepo — TypeScript/Svelte packages managed with [pnpm workspaces](https://pnpm.io/workspaces), Rust tools via [Cargo](https://doc.rust-lang.org/cargo/reference/workspaces.html) + [Nix flake](https://nixos.wiki/wiki/Flakes). -## Packages - -| Package | Lang | Description | -|---|---|---| -| [`@ewanc26/tid`](./packages/tid) | TypeScript | Zero-dependency AT Protocol TID generation | -| [`@ewanc26/atproto`](./packages/atproto) | TypeScript | AT Protocol service layer | -| [`@ewanc26/ui`](./packages/ui) | Svelte | Svelte UI component library | -| [`@ewanc26/utils`](./packages/utils) | TypeScript | Shared utility functions | -| [`@ewanc26/svelte-standard-site`](./packages/svelte-standard-site) | Svelte | SvelteKit library for site.standard.* AT Protocol records | -| [`@ewanc26/tangled-sync`](./packages/tangled-sync) | TypeScript | CLI tool for syncing GitHub repos to Tangled with ATProto record publishing | -| [`nix-config-tools`](./packages/nix-config-tools) | Rust | Nix config management tools (flake-bump, gen-diff, health-check, server-config) | - -## Setup - -```bash -# TypeScript/Svelte packages -pnpm install - -# Rust/Nix packages (no install needed — run directly) -nix run github:ewanc26/pkgs#flake-bump -nix run github:ewanc26/pkgs#gen-diff -nix run github:ewanc26/pkgs#health-check -nix run github:ewanc26/pkgs#server-config -``` - -## Common commands - -### TypeScript/Svelte - -```bash -# Build all packages -pnpm build +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/pkgs)**. -# Type-check all packages -pnpm check - -# Test all packages -pnpm test - -# Work on a single package -pnpm --filter @ewanc26/tid build -pnpm --filter @ewanc26/svelte-standard-site dev -``` - -### Rust/Nix - -```bash -# Build (via Nix) -nix build .#nix-config-tools - -# Build (via Cargo, for development) -cargo build --workspace - -# Local run (uncommitted changes) -nix run ./packages/nix-config-tools#flake-bump -``` +## Packages -## License +| Package | Description | +|---|---| +| [`@ewanc26/tid`](./packages/tid) | Zero-dependency AT Protocol TID generation | +| [`@ewanc26/atproto`](./packages/atproto) | AT Protocol service layer | +| [`@ewanc26/ui`](./packages/ui) | Svelte UI component library | +| [`@ewanc26/utils`](./packages/utils) | Shared utility functions | +| [`@ewanc26/noise`](./packages/noise) | Deterministic value-noise generation | +| [`@ewanc26/noise-avatar`](./packages/noise-avatar) | Noise-based avatar generation | +| [`@ewanc26/svelte-standard-site`](./packages/svelte-standard-site) | SvelteKit library for `site.standard.*` ATProto records | +| [`@ewanc26/pds-landing`](./packages/pds-landing) | Svelte components for an ATProto PDS landing page | +| [`@ewanc26/supporters`](./packages/supporters) | Ko-fi supporter display backed by ATProto | +| [`@ewanc26/tangled-sync`](./packages/tangled-sync) | CLI for syncing GitHub repos to Tangled | +| [`malachite`](./packages/malachite) | Last.fm/Spotify → ATProto scrobble importer | +| [`nix-config-tools`](./packages/nix-config-tools) | Nix config management tools (Rust) | +| [`llm-analyser`](./packages/llm-analyser) | `.docx` analysis with Ollama (Python) | + +## Licence AGPL-3.0-only (TypeScript/Svelte) · MIT (Rust) diff --git a/packages/atproto/README.md b/packages/atproto/README.md index f5a9c10..1dc258c 100644 --- a/packages/atproto/README.md +++ b/packages/atproto/README.md @@ -1,82 +1,12 @@ # @ewanc26/atproto -AT Protocol service layer extracted from [ewancroft.uk](https://ewancroft.uk). Handles identity resolution, record fetching, Bluesky posts, Standard.site documents, music/mood status, and more — with a built-in in-memory cache. - -**Key difference from the app's internal service layer:** all functions accept `did: string` as their first argument rather than reading `PUBLIC_ATPROTO_DID` from the environment. - -## Installation +AT Protocol service layer — identity resolution, record fetching, Bluesky posts, Standard.site documents, music/mood status, and more. ```bash pnpm add @ewanc26/atproto ``` -Requires `@atproto/api >= 0.13.0` as a peer dependency. - -## What's Exported - -### Fetch Functions - -| Function | Description | -|----------|-------------| -| `fetchProfile(did)` | Bluesky profile including pronouns | -| `fetchSiteInfo(did)` | `uk.ewancroft.site.info` record | -| `fetchLinks(did)` | `blue.linkat.board` link cards | -| `fetchMusicStatus(did)` | teal.fm music status with cascading artwork lookup | -| `fetchKibunStatus(did)` | kibun.social mood status | -| `fetchTangledRepos(did)` | Tangled code repositories | -| `fetchPublications(did)` | Standard.site publications | -| `fetchDocuments(did, pubRkey)` | Standard.site documents for a publication | -| `fetchBlogPosts(did, pubRkey)` | Blog posts for a publication | -| `fetchRecentDocuments(did, limit)` | Most recent documents across all publications | -| `fetchLatestBlueskyPost(did)` | Latest non-reply Bluesky post with thread context | -| `fetchPostFromUri(did, uri)` | Single Bluesky post by AT URI | -| `fetchAllEngagement(uris)` | Like/repost counts via Constellation API | - -### Agents & Identity - -```typescript -import { resolveIdentity, getPublicAgent, getPDSAgent, createAgent, withFallback, resetAgents } from '@ewanc26/atproto'; -``` - -### Pagination - -```typescript -import { fetchAllRecords, fetchAllUserRecords } from '@ewanc26/atproto'; -``` - -### Cache - -```typescript -import { cache, ATProtoCache, CACHE_TTL } from '@ewanc26/atproto'; - -cache.clear(); -cache.delete('profile:did:plc:…'); -``` - -### Music Artwork - -```typescript -import { findArtwork, searchMusicBrainzRelease, buildCoverArtUrl } from '@ewanc26/atproto'; -// Cascading: MusicBrainz → iTunes → Deezer → Last.fm → PDS blob -``` - -### Media - -```typescript -import { buildPdsBlobUrl, extractCidFromImageObject, extractImageUrlsFromValue } from '@ewanc26/atproto'; -``` - -### Types - -All interfaces (`ProfileData`, `BlueskyPost`, `BlogPost`, `MusicStatusData`, `KibunStatusData`, `TangledRepo`, `StandardSiteDocument`, `StandardSitePublication`, `SiteInfoData`, `LinkData`, `ResolvedIdentity`, …) are exported from the package root. - -## Build - -```bash -pnpm build # tsc -pnpm dev # tsc --watch -pnpm check # tsc --noEmit -``` +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/atproto)**. ## Licence diff --git a/packages/atproto/package.json b/packages/atproto/package.json index 42b3319..33ab966 100644 --- a/packages/atproto/package.json +++ b/packages/atproto/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/atproto", - "version": "0.2.3", + "version": "0.2.4", "description": "AT Protocol service layer extracted from ewancroft.uk", "type": "module", "exports": { diff --git a/packages/llm-analyser/README.md b/packages/llm-analyser/README.md index 033ec13..14916a3 100644 --- a/packages/llm-analyser/README.md +++ b/packages/llm-analyser/README.md @@ -1,74 +1,5 @@ -# LLM Analyser +# llm-analyser -This project provides a tool to analyze `.docx` files and generate analytical essays using Ollama. +Analyse `.docx` files and generate essays using Ollama. Python — no npm install. -## Prerequisites - -Before you begin, ensure you have the following installed: - -- **Python 3.x**: You can download it from [python.org](https://www.python.org/downloads/). -- **Ollama**: Follow the installation instructions on the [Ollama website](https://ollama.com/). - -## Setup - -1. **Clone the repository (if you haven't already):** - - ```bash - git clone git@github.com:ewanc26/llm-analyser.git - cd llm-analyser - ``` - -2. **Create a virtual environment (recommended):** - - ```bash - python3 -m venv venv - source venv/bin/activate - ``` - -3. **Install the required Python packages:** - - ```bash - pip install -r requirements.txt - ``` - - *Note: If `requirements.txt` does not exist, you will need to create it based on the project's dependencies (e.g., `python-docx`, `ollama`).* - -4. **Pull the required Ollama model:** - - This project uses the `llama3.2` model by default. You can pull it using the Ollama CLI: - - ```bash - ollama create document-analyser -f './Modelfile' - ``` - - If you wish to use a different model, update the `Modelfile` and the `main.py` script accordingly. - -## Usage - -To analyze `.docx` files in a directory and generate essays, run the `main.py` script: - -```bash -python3 main.py -``` - -**Arguments:** - -- ``: The path to the directory containing the `.docx` files you want to analyze. - -**Example:** - -```bash -python main.py ~/Documents/Literature/Poetry -``` - -This command will analyze all `.docx` files in the `~/Documents/Literature/Poetry` directory and generate essays using the `llama3.2` model. The essays will be saved in a new folder named `Poetry_essays` within the project root. - -## Modifying the Ollama Model - -The `Modelfile` contains the system prompt and parameters for the Ollama model. You can customize this file to change the model's behavior or use a different base model. - -After modifying the `Modelfile`, you might need to rebuild the model if you've changed the `FROM` line or other core parameters. Refer to the Ollama documentation for details on building custom models. - -## ☕ Support - -If you found this useful, consider [buying me a ko-fi](https://ko-fi.com/ewancroft)! \ No newline at end of file +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/llm-analyser)**. diff --git a/packages/malachite/README.md b/packages/malachite/README.md index ec0662d..05294d8 100644 --- a/packages/malachite/README.md +++ b/packages/malachite/README.md @@ -1,827 +1,9 @@ -# Malachite +# malachite -Import your Last.fm and Spotify listening history to the AT Protocol network using the `fm.teal.alpha.feed.play` lexicon. +Import Last.fm and Spotify listening history to AT Protocol as `fm.teal.alpha.feed.play` records. Web interface at [malachite.croft.click](https://malachite.croft.click). -**Repository:** [ewanc26/pkgs — packages/malachite](https://github.com/ewanc26/pkgs/tree/main/packages/malachite) -[Also available on Tangled](https://tangled.org/did:plc:ofrbh253gwicbkc5nktqepol/atproto-lastfm-importer) +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/malachite)**. -## Table of Contents +## Licence -- [⚠️ Important: Rate Limits](#️-important-rate-limits) - - [How Dynamic Batch Sizing Works](#how-dynamic-batch-sizing-works) -- [What's with the name?](#whats-with-the-name) -- [Quick Start](#quick-start) - - [Interactive Mode (Recommended for First-Time Users)](#interactive-mode-recommended-for-first-time-users) - - [Command Line Mode](#command-line-mode) -- [Features](#features) - - [Import Capabilities](#import-capabilities) - - [Performance & Safety](#performance--safety) - - [User Experience](#user-experience) - - [Technical Features](#technical-features) -- [Usage Examples](#usage-examples) - - [Combined Import (Last.fm + Spotify)](#combined-import-lastfm--spotify) - - [Re-Sync Mode](#re-sync-mode) - - [Remove Duplicates](#remove-duplicates) - - [Import from Spotify](#import-from-spotify) - - [Import from Last.fm](#import-from-lastfm) - - [Advanced Options](#advanced-options) -- [Command Line Options](#command-line-options) - - [Required Options](#required-options) - - [Import Mode](#import-mode) - - [Additional Options](#additional-options) - - [PDS Override](#pds-override) - - [Legacy Flags (Backwards Compatible)](#legacy-flags-backwards-compatible) -- [Getting Your Data](#getting-your-data) - - [Last.fm Export](#lastfm-export) - - [Spotify Export](#spotify-export) -- [Data Format](#data-format) - - [Required Fields](#required-fields) - - [Optional Fields](#optional-fields) - - [Example Records](#example-records) -- [How It Works](#how-it-works) - - [Processing Flow](#processing-flow) - - [Automatic Duplicate Prevention](#automatic-duplicate-prevention) - - [Rate Limiting Algorithm](#rate-limiting-algorithm) - - [Multi-Day Imports](#multi-day-imports) -- [Logging and Output](#logging-and-output) - - [Verbosity Levels](#verbosity-levels) -- [Error Handling](#error-handling) -- [Troubleshooting](#troubleshooting) - - [Authentication Issues](#authentication-issues) - - [Performance Issues](#performance-issues) - - [Connection Issues](#connection-issues) - - [Output Control](#output-control) -- [Development](#development) -- [File Storage](#file-storage) - - [Credential Storage](#credential-storage) -- [Project Structure](#project-structure) -- [Technical Details](#technical-details) - - [Authentication](#authentication) - - [Batch Publishing](#batch-publishing) - - [Data Mapping](#data-mapping) - - [Lexicon Reference](#lexicon-reference) -- [Contributing](#contributing) -- [License](#license) -- [Credits](#credits) - -## ⚠️ Important: Rate Limits - -**CRITICAL**: Bluesky's AppView has rate limits on PDS instances. Exceeding 10K records per day can rate limit your **ENTIRE PDS**, affecting all users on your instance. - -This importer automatically protects your PDS by: - -- **Dynamic batch sizing** (1-200 records) that adapts to available quota in real-time -- **15% headroom buffer** prevents quota exhaustion before hitting the limit -- Limiting imports to **7,500 records per day** (with 75% safety margin) -- Calculating optimal batch sizes and delays -- **Graceful degradation** - scales down smoothly as quota depletes -- **Instant recovery** - immediately returns to maximum speed after quota resets -- Pausing 24 hours between days for large imports -- Providing clear progress tracking and time estimates -- Persisting state across restarts for safe resume - -### How Dynamic Batch Sizing Works - -Malachite continuously monitors your rate limit quota and automatically adjusts batch size: - -``` -Fresh Quota (5000 points) → Batch Size: 200 records (maximum speed) -Half Depleted (2500 points) → Batch Size: 200 records (still optimal) -Approaching Limit (1200) → Batch Size: 150 records (scaling down) -Near Headroom (900) → Batch Size: 50 records (conservative) -Below Headroom (700) → Batch Size: 1 record (minimal progress) -[Quota Resets] → Batch Size: 200 records (instant recovery) -``` - -**Benefits:** - -- ✅ **2x faster** when quota is fresh (200 vs 100 records/batch) -- ✅ **Never hits rate limits** - proactive scaling with 15% buffer -- ✅ **Always makes progress** - even with minimal quota (batch size 1) -- ✅ **Automatic recovery** - no manual intervention needed -- ✅ **Transparent** - logs all batch size changes with reasons - -For more details, see the [Bluesky Rate Limits Documentation](https://docs.bsky.app/blog/rate-limits-pds-v3). - -## What's with the name? - -It used to be called `atproto-lastfm-importer` — generic as fuck. That name told you what it did and nothing about why it mattered, and it sounded like a disposable weekend script. So I renamed it. - -At the moment, the repository is still called `atproto-lastfm-importer` on Tangled, but the GitHub link has been updated to `malachite`. I do not know if this can be resolved. - -**Malachite** is a greenish-blue copper mineral associated with preservation and transformation. That's exactly what this tool does: it preserves your scrobbles and transforms them into proper `fm.teal.alpha.feed.play` records on the AT Protocol. The colour match isn't an accident — malachite sits squarely in the teal/green range, a deliberate nod to the `teal` lexicon it publishes to. - -## Web Interface - -A browser-based UI is available at **[malachite.croft.click](https://malachite.croft.click)** — no installation required. It supports all five import modes and signs in via ATProto OAuth. - -## Quick Start - -**Note:** You must build the project first, then run with arguments. - -### Interactive Mode (Recommended for First-Time Users) - -Just run without any arguments and Malachite will guide you through the process: - -```bash -# Install dependencies and build -pnpm install -pnpm build - -# Run in interactive mode -pnpm start -``` - -The interactive mode will: - -- Present a menu of available actions -- Prompt for all required information (handle, password, files) -- Ask for optional settings (dry run, verbose logging, etc.) -- Provide helpful descriptions for each option - -### Command Line Mode - -For automation or if you prefer command-line arguments: - -```bash -# Show help -pnpm start --help - -# Run with command line arguments -pnpm start -i lastfm.csv -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -y - -# Alternative: run directly with node -node dist/index.js -i lastfm.csv -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -y -``` - -## Features - -### Import Capabilities - -- ✅ **Last.fm Import**: Full support for Last.fm CSV exports with MusicBrainz IDs -- ✅ **Spotify Import**: Import Extended Streaming History JSON files -- ✅ **Combined Import**: Merge Last.fm and Spotify exports with intelligent deduplication -- ✅ **Re-Sync Mode**: Import only new scrobbles without creating duplicates -- ✅ **Duplicate Removal**: Clean up accidentally imported duplicate records - -### Performance & Safety - -- ✅ **Automatic Duplicate Prevention**: Fetches your existing Teal records via CAR export and skips anything already imported -- ✅ **Input Deduplication**: Removes duplicate entries within the source file before submission -- ✅ **Dynamic Batch Sizing**: Automatically adjusts batch size (1-200 records) based on available rate limit quota -- ✅ **Batch Operations**: Uses `com.atproto.repo.applyWrites` for efficient batch publishing (up to 200 records per call) -- ✅ **Zero-cost sync check**: Existing record fetching uses `com.atproto.sync.getRepo` (CAR export) — a separate, far more generous rate-limit envelope that costs zero AppView write-quota points -- ✅ **Intelligent Rate Limiting**: Real-time quota monitoring with 15% headroom buffer prevents rate limit exhaustion -- ✅ **Adaptive Recovery**: Automatically scales back to maximum speed after quota resets -- ✅ **Multi-Day Imports**: Large imports automatically span multiple days with 24-hour pauses -- ✅ **Resume Support**: Safe to stop (Ctrl+C) and restart - continues from where it left off -- ✅ **Graceful Cancellation**: Press Ctrl+C to stop after the current batch completes - -### User Experience - -- ✅ **Structured Logging**: Color-coded output with debug/verbose modes -- ✅ **Progress Tracking**: Real-time progress with time estimates -- ✅ **Dry Run Mode**: Preview records without publishing -- ✅ **Interactive Mode**: Simple prompts guide you through the process -- ✅ **Command Line Mode**: Full automation support for scripting -- ✅ **Web Interface**: Browser-based UI at [malachite.croft.click](https://malachite.croft.click) with ATProto OAuth sign-in - -### Technical Features - -- ✅ **TID-based Record Keys**: Timestamp-based identifiers for chronological ordering -- ✅ **Identity Resolution**: Resolves ATProto handles/DIDs using Slingshot -- ✅ **PDS Auto-Discovery**: Automatically connects to your personal PDS -- ✅ **MusicBrainz Support**: Preserves MusicBrainz IDs when available (Last.fm) -- ✅ **Chronological Ordering**: Processes oldest first (or newest with `-r` flag) -- ✅ **Error Handling**: Continues on errors with detailed reporting - -## Usage Examples - -### Combined Import (Last.fm + Spotify) - -Merge your Last.fm and Spotify listening history into a single, deduplicated import: - -```bash -# Preview the merged import -pnpm start -i lastfm.csv --spotify-input spotify-export/ -m combined --dry-run - -# Perform the combined import -pnpm start -i lastfm.csv --spotify-input spotify-export/ -m combined -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -y -``` - -**What combined mode does:** - -1. Parses both Last.fm CSV and Spotify JSON exports -2. Normalizes track names and artist names for comparison -3. Identifies duplicate plays (same track within 5 minutes) -4. Chooses the best version of each play (prefers Last.fm with MusicBrainz IDs) -5. Merges into a single chronological timeline -6. Shows detailed statistics about the merge - -### Re-Sync Mode - -Sync your Last.fm export with Teal without creating duplicates: - -```bash -# Preview what will be synced -pnpm start -i lastfm.csv -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -m sync --dry-run - -# Perform the sync -pnpm start -i lastfm.csv -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -m sync -y -``` - -**Perfect for:** - -- Re-running imports with updated Last.fm exports -- Recovering from interrupted imports -- Adding recent scrobbles without duplicating old ones - -**Note:** Sync mode requires authentication even in dry-run mode to fetch existing records. - -### Remove Duplicates - -Clean up accidentally imported duplicate records: - -```bash -# Preview duplicates (dry run) -pnpm start -m deduplicate -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx --dry-run - -# Remove duplicates (keeps first occurrence) -pnpm start -m deduplicate -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -``` - -### Import from Spotify - -```bash -# Import single Spotify JSON file -pnpm start -i Streaming_History_Audio_2021-2023_0.json -m spotify -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -y - -# Import directory with multiple Spotify files (recommended) -pnpm start -i '/path/to/Spotify Extended Streaming History' -m spotify -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -y -``` - -### Import from Last.fm - -```bash -# Standard Last.fm import -pnpm start -i lastfm.csv -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -y - -# Preview without publishing -pnpm start -i lastfm.csv --dry-run - -# Process newest tracks first -pnpm start -i lastfm.csv -h alice.bsky.social -r -y - -# Verbose debug output -pnpm start -i lastfm.csv --dry-run -v - -# Quiet mode (only warnings and errors) -pnpm start -i lastfm.csv -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -q -y -``` - -### Advanced Options - -```bash -# Development mode (verbose + file logging + smaller batches for debugging) -pnpm start -i lastfm.csv --dev --dry-run - -# Custom batch settings (advanced users only) -pnpm start -i lastfm.csv -h alice.bsky.social -b 20 -d 3000 - -# Full automation with all flags -pnpm start -i lastfm.csv -h alice.bsky.social -p xxxx-xxxx-xxxx-xxxx -y -q -``` - -## Command Line Options - -**Note:** When importing data (not in deduplicate mode), you must provide `--input`, `--handle`, and `--password`. The `--yes` flag skips confirmation prompts for automation. - -### Required Options - -| Option | Short | Description | Example | -| ------------------- | ----- | -------------------------------------------------- | ------------------------ | -| `--input ` | `-i` | Path to Last.fm CSV or Spotify JSON file/directory | `-i lastfm.csv` | -| `--handle ` | `-h` | ATProto handle or DID | `-h alice.bsky.social` | -| `--password ` | `-p` | ATProto app password | `-p xxxx-xxxx-xxxx-xxxx` | - -### Import Mode - -| Option | Short | Description | Default | -| --------------- | ----- | ----------- | -------- | -| `--mode ` | `-m` | Import mode | `lastfm` | - -**Available modes:** - -- `lastfm` - Import Last.fm export only -- `spotify` - Import Spotify export only -- `combined` - Merge Last.fm + Spotify exports -- `sync` - Skip existing records (sync mode) -- `deduplicate` - Remove duplicate records - -### Additional Options - -| Option | Short | Description | Default | -| ------------------------ | ----- | ----------------------------------------------------------- | --------------- | -| `--spotify-input ` | | Path to Spotify export (for combined mode) | - | -| `--reverse` | `-r` | Process newest first | `false` | -| `--yes` | `-y` | Skip confirmation prompts | `false` | -| `--dry-run` | | Preview without importing | `false` | -| `--verbose` | `-v` | Enable debug logging | `false` | -| `--quiet` | `-q` | Suppress non-essential output | `false` | -| `--dev` | | Development mode (verbose + file logging + smaller batches) | `false` | -| `--batch-size ` | `-b` | Initial batch size (1-200, dynamically adjusted) | Auto-calculated | -| `--batch-delay ` | `-d` | Delay between batches in ms | `500` (min) | -| `--help` | | Show help message | - | - -### PDS Override - -If you already know the base URL of your Personal Data Server (PDS) you can bypass the Slingshot identity resolver and provide it directly with the `--pds` flag. This is useful for private instances, testing, or when the resolver is unreliable. - -| Option | Description | -| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--pds ` | PDS base URL to use for authentication and API calls (e.g. `https://pds.example.com`). When provided, Malachite will skip Slingshot lookup and use this URL directly. | - -Notes: - -- The `--pds` flag overrides the configured Slingshot resolver for identity lookup. If `--pds` is given, Malachite will attempt to authenticate directly against the supplied PDS using your handle/DID and app password. -- Use the full base URL (including scheme), e.g. `https://pds.example.com`. -- If authentication fails when using `--pds`, try removing the flag so Malachite can resolve your PDS automatically via Slingshot. - -### Legacy Flags (Backwards Compatible) - -These old flags still work but are deprecated: - -- `--file` → Use `--input` -- `--identifier` → Use `--handle` -- `--spotify-file` → Use `--spotify-input` -- `--reverse-chronological` → Use `--reverse` -- `--spotify` → Use `--mode spotify` -- `--combined` → Use `--mode combined` -- `--sync` → Use `--mode sync` -- `--remove-duplicates` → Use `--mode deduplicate` - -## Getting Your Data - -### Last.fm Export - -1. Visit [Last.fm Export Tool](https://lastfm.ghan.nl/export/) -2. Request your data export in CSV format -3. Download the CSV file when ready -4. Use the CSV file path with this importer - -### Spotify Export - -1. Go to [Spotify Privacy Settings](https://www.spotify.com/account/privacy/) -2. Scroll to "Download your data" and request your data -3. Select "Extended streaming history" (can take up to 30 days) -4. When ready, download and extract the ZIP file -5. Use either: - - A single JSON file: `Streaming_History_Audio_2021-2023_0.json` - - The entire extracted directory (recommended) - -**Note:** The importer automatically: - -- Reads all `Streaming_History_Audio_*.json` files in a directory -- Filters out podcasts, audiobooks, and non-music content -- Combines all music tracks into a single import - -## Data Format - -Each scrobble becomes an `fm.teal.alpha.feed.play` record with: - -### Required Fields - -- **trackName**: The name of the track -- **artists**: Array of artist objects (requires `artistName`, optional `artistMbId` for Last.fm) -- **playedTime**: ISO 8601 timestamp of when you listened -- **submissionClientAgent**: Identifies this importer (`malachite/v0.10.0` for CLI, `malachite/v0.3.0 (web)` for the web app) -- **musicServiceBaseDomain**: Set to `last.fm` or `spotify.com` - -### Optional Fields - -- **releaseName**: Album/release name -- **releaseMbId**: MusicBrainz release ID (Last.fm only) -- **recordingMbId**: MusicBrainz recording/track ID (Last.fm only) -- **originUrl**: Link to the track on Last.fm or Spotify - -### Example Records - -**Last.fm Record:** - -```json -{ - "$type": "fm.teal.alpha.feed.play", - "trackName": "Paint My Masterpiece", - "artists": [ - { - "artistName": "Cjbeards", - "artistMbId": "c8d4f4bf-1b82-4d4d-9d73-05909faaff89" - } - ], - "releaseName": "Masquerade", - "releaseMbId": "fdb2397b-78d5-4019-8fad-656d286e4d33", - "recordingMbId": "3a390ad3-fe56-45f2-a073-bebc45d6bde1", - "playedTime": "2025-11-13T23:49:36Z", - "originUrl": "https://www.last.fm/music/Cjbeards/_/Paint+My+Masterpiece", - "submissionClientAgent": "malachite/v0.10.0", - "musicServiceBaseDomain": "last.fm" -} -``` - -**Spotify Record:** - -```json -{ - "$type": "fm.teal.alpha.feed.play", - "trackName": "Don't Give Up", - "artists": [ - { - "artistName": "Chicane" - } - ], - "releaseName": "Twenty", - "playedTime": "2021-09-09T10:34:08Z", - "originUrl": "https://open.spotify.com/track/3gZqDJkMZipOYCRjlHWgOV", - "submissionClientAgent": "malachite/v0.10.0", - "musicServiceBaseDomain": "spotify.com" -} -``` - -## How It Works - -### Processing Flow - -1. **Parses input file(s)**: - - Last.fm: CSV using `csv-parse` library - - Spotify: JSON files (single or multiple in directory) -2. **Filters data**: - - Spotify: Automatically removes podcasts, audiobooks, and non-music content -3. **Converts to schema**: Maps to `fm.teal.alpha.feed.play` format -4. **Deduplicates input**: Removes duplicate entries from the source data (keeps first occurrence) -5. **Checks Teal**: Downloads the entire repo as a CAR file (`com.atproto.sync.getRepo`) and skips any records already imported — costs zero AppView write-quota points -6. **Sorts records**: Chronologically (oldest first) or reverse with `-r` flag -7. **Generates TID-based keys**: From `playedTime` for chronological ordering -8. **Validates fields**: Ensures required fields are present -9. **Publishes in batches**: Uses `com.atproto.repo.applyWrites` (up to 200 records per call) - -### Automatic Duplicate Prevention - -The importer has **two layers of duplicate prevention** to ensure you never import the same record twice: - -#### Step 1: Input File Deduplication - -Removes duplicates within your source file(s): - -**How duplicates are identified:** - -- Same track name (case-insensitive) -- Same artist name (case-insensitive) -- Same timestamp (exact match) - -**What happens:** - -- First occurrence is kept -- Subsequent duplicates are removed -- Shows message: "No duplicates found in input data" or "Removed X duplicate(s)" - -#### Step 2: Teal Comparison via CAR Export - -**Automatically checks your existing Teal records** by downloading your entire repo as a CARv1 file: - -- One HTTP request fetches the whole repo (`com.atproto.sync.getRepo`) -- The CAR file is parsed locally in memory — no AppView quota consumed -- Compares every record against your input and skips anything already imported -- Shows: "Skipped X already-imported record(s)" - -**This means:** - -- ✅ Safe to re-run imports with updated exports -- ✅ Won't create duplicates if you run the import twice -- ✅ Zero AppView write-quota cost for the sync check -- ✅ Works automatically - no special mode needed - -**Note:** - -- Credentials are required even for `--dry-run` to fetch the CAR export -- **Sync mode** (`-m sync`): Shows detailed statistics about what's being skipped -- **Deduplicate mode** (`-m deduplicate`): Removes duplicates from already-imported Teal records (cleanup tool) - -### Rate Limiting Algorithm - -1. Calculates safe daily limit (75% of 10K = 7,500 records/day by default) -2. Determines how many days needed for your import -3. **Monitors rate limit quota in real-time** before each batch -4. **Dynamically adjusts batch size** (1-200 records) based on available points -5. **Preserves 15% headroom buffer** to prevent exhaustion -6. **Automatically waits** when quota is exhausted (with countdown timer) -7. **Instantly scales back up** to maximum batch size after quota resets -8. Enforces minimum delay between batches -9. Shows clear schedule and real-time batch size adjustments - -### Multi-Day Imports - -For imports exceeding the daily limit, the importer automatically: - -1. **Calculates a schedule**: Splits your import across multiple days -2. **Shows the plan**: Displays which records will be imported each day -3. **Processes Day 1**: Imports the first batch of records -4. **Pauses 24 hours**: Waits a full day before continuing -5. **Repeats**: Continues until all records are imported - -**Important notes:** - -- You can safely stop (Ctrl+C) and restart -- Progress is preserved - continues where it left off -- Each day's progress is clearly displayed -- Time estimates account for multi-day duration - -## Logging and Output - -The importer uses color-coded output for clarity: - -- **Green (✓)**: Success messages -- **Cyan (→)**: Progress updates -- **Yellow (⚠️)**: Warnings -- **Red (✗)**: Errors -- **Bold Red (🛑)**: Fatal errors - -### Verbosity Levels - -**Default Mode**: Standard operational messages - -```bash -pnpm start -i lastfm.csv -h alice.bsky.social -p pass -``` - -**Verbose Mode** (`-v`): Detailed debug information including batch timing and API calls - -```bash -pnpm start -i lastfm.csv -h alice.bsky.social -p pass -v -``` - -**Quiet Mode** (`-q`): Only warnings and errors - -```bash -pnpm start -i lastfm.csv -h alice.bsky.social -p pass -q -``` - -**Development Mode** (`--dev`): Verbose logging + file logging to `~/.malachite/logs/` + smaller batch sizes - -```bash -pnpm start -i lastfm.csv --dev --dry-run -``` - -Development mode is perfect for: - -- Debugging import issues with detailed logs -- Testing changes with smaller batches (20 records max) -- Preserving logs for later analysis -- Troubleshooting problems with support - -## Error Handling - -The importer is designed to be resilient: - -- **Network errors**: Failed records are logged but don't stop the import -- **Invalid data**: Skipped with error messages -- **Authentication issues**: Clear error messages with suggested fixes -- **Rate limit hits**: Automatic adjustment and retry logic -- **Ctrl+C handling**: Gracefully stops after current batch - -## Troubleshooting - -### Authentication Issues - -**"Handle not found"** - -- Verify your ATProto handle is correct (e.g., `alice.bsky.social`) -- Ensure you're using a valid DID or handle - -**"Invalid credentials"** - -- Use an **app password**, not your main account password -- Generate app passwords in your account settings - -### Performance Issues - -**"Rate limit exceeded"** - -- The importer should prevent this automatically -- If you see this, wait 24 hours before retrying -- Consider reducing batch size with `-b` flag - -**Import seems stuck** - -- Check progress messages - large imports take time -- Multi-day imports pause for 24 hours between days -- You can safely stop (Ctrl+C) and resume later -- Use `--verbose` flag to see detailed progress - -### Connection Issues - -**"Connection refused"** - -- Check your internet connection -- Verify your PDS is accessible -- Some PDSs may have firewall rules - -### Output Control - -**Too much output** - -- Use `--quiet` flag to suppress non-essential messages -- Only warnings and errors will be shown - -**Need more details** - -- Use `--verbose` flag to see debug-level information -- Shows batch timing, API calls, and detailed progress - -## Development - -```bash -# Type checking -pnpm run type-check - -# Build -pnpm run build - -# Development mode (rebuild + run) -pnpm run dev - -# Run tests -pnpm run test - -# Clean build artifacts -pnpm run clean -``` - -## File Storage - -Malachite stores all its data in `~/.malachite/`: - -``` -~/.malachite/ -├── cache/ # Cached Teal records (24-hour TTL) -├── state/ # Import state for resume functionality -├── logs/ # Import logs (when file logging is enabled) -└── credentials.json # Encrypted credentials (optional, machine-specific) -``` - -This keeps your project directory clean and follows standard Unix conventions. - -### Credential Storage - -Malachite automatically saves your ATProto credentials after a successful login so you don't need to re-enter them on the next run: - -**Security Features:** - -- ✅ **AES-256-GCM encryption** - Military-grade encryption -- ✅ **Machine-specific** - Credentials are bound to your computer and can't be transferred -- ✅ **Secure key derivation** - Uses PBKDF2 with 100,000 iterations -- ✅ **File permissions** - Credentials file is readable only by you (Unix) - -**How It Works:** - -1. Credentials are encrypted using a key derived from your hostname + username and saved to `~/.malachite/credentials.json` after every successful login -2. On the next run, Malachite loads saved credentials automatically -3. In interactive mode, you'll be prompted whether to use the saved credentials or enter new ones - -**Managing Credentials:** - -```bash -# Clear saved credentials -pnpm start --clear-credentials - -# Or through interactive mode (option 7) -pnpm start -``` - -**Important Notes:** - -- Credentials are machine-specific and won't work if you copy the file to another computer -- This is a convenience feature - you can always enter credentials manually -- If you change your password, clear and re-save credentials - -## Project Structure - -``` -malachite/ -├── src/ -│ ├── lib/ -│ │ ├── auth.ts # Authentication & identity resolution -│ │ ├── cli.ts # Command line interface & argument parsing -│ │ ├── csv.ts # CSV parsing & record conversion -│ │ ├── publisher.ts # Batch publishing with rate limiting -│ │ ├── spotify.ts # Spotify JSON parsing -│ │ ├── merge.ts # Combined import deduplication -│ │ └── sync.ts # Re-sync mode & duplicate detection -│ ├── utils/ -│ │ ├── car-fetch.ts # CAR export fetcher (com.atproto.sync.getRepo) -│ │ ├── logger.ts # Structured logging system -│ │ ├── helpers.ts # Utility functions (timing, formatting) -│ │ ├── input.ts # User input handling (prompts, passwords) -│ │ ├── rate-limiter.ts # Rate limiting with server-learned quota -│ │ ├── killswitch.ts # Graceful shutdown handling -│ │ ├── tid.ts # TID generation from timestamps -│ │ └── ui.ts # UI elements (spinners, progress bars) -│ ├── config.ts # Configuration constants & version -│ └── types.ts # TypeScript type definitions -├── web/ # SvelteKit web app -│ └── src/lib/ -│ ├── core/ # Browser-safe equivalents of src/lib & src/utils -│ │ ├── auth.ts # Password-based ATProto login -│ │ ├── car-fetch.ts# CAR export fetcher (browser-safe) -│ │ ├── csv.ts # CSV parser (no csv-parse dep) -│ │ ├── import.ts # Import orchestration -│ │ ├── merge.ts # Combined import deduplication -│ │ ├── oauth.ts # ATProto OAuth client -│ │ ├── publisher.ts# Batch publisher with progress callbacks -│ │ ├── rate-limiter.ts # In-memory rate limiter -│ │ ├── spotify.ts # Spotify JSON parser -│ │ ├── sync.ts # CAR-based sync & dedup -│ │ └── tid.ts # TID generation (Web Crypto API) -│ ├── config.ts # Shared constants (version injected by Vite) -│ ├── modes.ts # Import mode definitions -│ └── types.ts # TypeScript type definitions -├── lexicons/ # fm.teal.alpha lexicon definitions -│ └── fm.teal.alpha/ -│ └── feed/ -│ └── play.json # Play record schema -├── package.json -├── tsconfig.json -└── README.md -``` - -## Technical Details - -### Authentication - -- Uses Slingshot resolver to discover your PDS from your handle/DID -- Requires an ATProto app password (not your main password) for the CLI -- Web app supports ATProto OAuth — no app password needed -- Automatically configures the agent for your personal PDS - -### Batch Publishing - -- Uses `com.atproto.repo.applyWrites` for efficiency (up to 20x faster than individual calls) -- Batches up to 200 records per API call (PDS maximum) -- **Dynamic batch sizing** (1-200 records) based on real-time rate limit quota -- **Intelligent quota monitoring** with 15% headroom buffer -- **Automatic adjustment** - scales down as quota depletes, scales up after reset -- Enforces minimum delays between batches for rate limit safety - -### CAR Export Sync - -All read paths (duplicate checks, sync, deduplicate) use `com.atproto.sync.getRepo` to download the user's entire repo as a CARv1 file. The CAR is parsed locally using `@ipld/car` and `@ipld/dag-cbor` — no AppView XRPC calls are made for reads, so the sync check costs zero write-quota points. - -### Data Mapping - -**Last.fm:** - -- Direct mapping from CSV columns -- Converts Unix timestamps to ISO 8601 -- Preserves MusicBrainz IDs when present -- Generates URLs from artist/track names -- Wraps artists in array format with optional MBID - -**Spotify:** - -- Extracts data from JSON fields -- Already in ISO 8601 format (`ts` field) -- Generates URLs from `spotify_track_uri` -- Automatically filters non-music content -- Extracts artist and album from metadata fields - -### Lexicon Reference - -This importer follows the official `fm.teal.alpha` lexicon defined in `/lexicons/fm.teal.alpha/feed/play.json`. - -The lexicon defines required and optional field types, string length constraints, array formats, timestamp formatting, and URL validation. - -## Contributing - -Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, architecture notes, and PR guidelines. - -## License - -AGPL-3.0-only - See LICENCE file for details - -## ☕ Support - -If you found this useful, consider [buying me a ko-fi](https://ko-fi.com/ewancroft)! - -## Credits - -- Uses [@atproto/api](https://www.npmjs.com/package/@atproto/api) for ATProto interactions -- CSV parsing via [csv-parse](https://www.npmjs.com/package/csv-parse) -- Identity resolution via [Slingshot](https://slingshot.danner.cloud) -- Follows the `fm.teal.alpha` lexicon standard -- Colored output via [chalk](https://www.npmjs.com/package/chalk) -- Progress indicators via [ora](https://www.npmjs.com/package/ora) and [cli-progress](https://www.npmjs.com/package/cli-progress) -- Web app built with [SvelteKit](https://kit.svelte.dev) and [Tailwind CSS](https://tailwindcss.com) -- ATProto OAuth via [@atproto/oauth-client-browser](https://www.npmjs.com/package/@atproto/oauth-client-browser) - ---- - -**Note**: This tool is for personal use. Respect the terms of service and rate limits when importing your data. +AGPL-3.0-only. diff --git a/packages/nix-config-tools/README.md b/packages/nix-config-tools/README.md index 998cd43..6f87a7d 100644 --- a/packages/nix-config-tools/README.md +++ b/packages/nix-config-tools/README.md @@ -1,130 +1,13 @@ -# Nix Config Tools +# nix-config-tools -Four Rust utilities for managing the nix config. Run via the flake — no -`cargo build` required: +Four Rust utilities for managing the nix config — `health-check`, `flake-bump`, `gen-diff`, `server-config`. Run via the flake, no cargo build required. ```bash nix run github:ewanc26/pkgs#health-check -nix run github:ewanc26/pkgs#flake-bump -nix run github:ewanc26/pkgs#gen-diff -nix run github:ewanc26/pkgs#server-config ``` -Or via the shell aliases set up by `home/programs/zsh.nix`: +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/nix-config-tools)**. -```bash -health-check # pre-rebuild preflight -flake-bump # inspect / update flake inputs -gen-diff # diff package changes between generations -``` - -For local dev (working tree, uncommitted changes): - -```bash -nix run ./packages/nix-config-tools#flake-bump -``` - ---- - -## `health-check` - -Pre-rebuild preflight. Run before `nrs` to catch problems early. - -```bash -health-check -``` - -Checks: -- Nix daemon is responding -- `flake.lock` is present and valid JSON -- Flake evaluates without errors -- Git working tree is clean -- Age key exists at `~/.config/age/keys.txt` -- SSH keys present in `modules/ssh-keys.nix` -- Disk space on `/nix/store` -- Homebrew installed (macOS only) - -Exits 0 when all hard checks pass (warnings are non-fatal). - ---- - -## `flake-bump` - -Shows how stale each flake input is and bumps them selectively. - -```bash -flake-bump # show staleness table -flake-bump --update nixpkgs # bump one input and commit flake.lock -flake-bump --update-all # bump everything and commit flake.lock -``` - ---- - -## `gen-diff` - -Shows what packages changed between NixOS/nix-darwin generations. +## Licence -```bash -gen-diff # diff last two generations -gen-diff --list # list all generations with dates -gen-diff --from 42 --to 43 # diff specific generation numbers -``` - -Wraps `nix store diff-closures` with a friendlier interface and generation listing. - ---- - -## `server-config` - -Interactive server configurator: service toggles, storage device, Cockpit, -Forgejo, Matrix, PDS, Cloudflare settings. - -```bash -server-config # interactive configuration -server-config --show # read-only summary -``` - ---- - -## Retired tools - -The following tools were removed. Their source files remain in `src/bin/` for -reference but are no longer compiled. - -| Tool | Reason removed | -|---|---| -| `darwin-export` | macOS settings are now fully declarative in `settings/darwin/default.nix` — nothing to export | -| `gnome-export` | GNOME/dconf settings are now fully declarative in `settings/gnome/dconf-settings.nix` — nothing to export | -| `secrets-setup` | Was a stub that only checked if `~/.config/age/keys.txt` existed; `health-check` covers this | -| `categorize-apps` | `nix search` / `brew info --cask` lookups produce false positives; `darwin.nix` is hand-maintained | -| `generate-app-config` | Same reasons as `categorize-apps` | -| `sync-apps` | Same reasons, plus it auto-mutated config files without review | - ---- - -## Development - -### Adding a new tool - -1. Create `src/bin/your-tool.rs` -2. Add to `Cargo.toml`: - ```toml - [[bin]] - name = "your-tool" - path = "src/bin/your-tool.rs" - ``` -3. Expose in the root `flake.nix` under `apps` -4. Add a shell alias in `home/programs/zsh.nix` if used regularly - -### Common utilities (`lib.rs`) - -```rust -use tools_common::*; - -let root = git_root(); // path to ~/.config/nix-config -let timestamp = get_timestamp(); // "2026-02-14 20:00:00" -let host = get_hostname(); // "macmini" - -// Commit a file and push -git_sync("flake.lock", "flake"); -``` +MIT. diff --git a/packages/noise-avatar/README.md b/packages/noise-avatar/README.md index 98006ea..0306661 100644 --- a/packages/noise-avatar/README.md +++ b/packages/noise-avatar/README.md @@ -1,63 +1,13 @@ # @ewanc26/noise-avatar -Deterministic value-noise avatar generation from a string seed. Thin opinionated wrapper around [`@ewanc26/noise`](../noise). Zero extra runtime dependencies, works in any environment with a Canvas API (browsers, jsdom). - -Part of the [`@ewanc26/pkgs`](https://github.com/ewanc26/pkgs) monorepo. - -## Install +Deterministic value-noise avatar generation from a string seed. Thin wrapper around [`@ewanc26/noise`](../noise). ```bash pnpm add @ewanc26/noise-avatar ``` -## Usage - -### Vanilla - -```ts -import { renderNoiseAvatar } from '@ewanc26/noise-avatar'; - -const canvas = document.querySelector('canvas'); -renderNoiseAvatar(canvas, 'Alice|Subscription'); -``` - -### Svelte action - -```svelte - - - -``` - -## API - -### `renderNoiseAvatar(canvas, seed, options?)` - -Renders a deterministic HSL value-noise texture onto `canvas` at `displaySize × displaySize` pixels. - -| Option | Type | Default | Description | -|---|---|---|---| -| `gridSize` | `number` | `5` | Noise grid resolution | -| `displaySize` | `number` | `64` | Canvas pixel size | -| `hueRange` | `number` | `60` | Hue spread in degrees around seed-derived base hue | -| `saturationRange` | `[number, number]` | `[45, 70]` | Saturation min/max (%) | -| `lightnessRange` | `[number, number]` | `[40, 70]` | Lightness min/max (%) | - -### `noiseAvatarAction(canvas, seed, options?)` - -Svelte action wrapper. Re-renders when `seed` changes via `update`. - -### Re-exported primitives - -`hash32`, `makePrng`, `hslToRgb`, `makeValueNoiseSampler`, and `generateNoisePixels` -are all re-exported from `@ewanc26/noise` for convenience. - -For full control over dimensions, FBM octaves, and colour modes, use -[`@ewanc26/noise`](../noise) directly. +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/noise-avatar)**. ## Licence -AGPL-3.0-only +AGPL-3.0-only. diff --git a/packages/noise-avatar/package.json b/packages/noise-avatar/package.json index 4a6f44a..c9e5143 100644 --- a/packages/noise-avatar/package.json +++ b/packages/noise-avatar/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/noise-avatar", - "version": "0.2.1", + "version": "0.2.2", "description": "Deterministic value-noise avatar generation from a string seed. Thin wrapper around @ewanc26/noise. Zero extra dependencies, works in any environment with a Canvas API.", "author": "Ewan Croft", "license": "AGPL-3.0-only", diff --git a/packages/noise/README.md b/packages/noise/README.md index 6c0f35f..5fadd6e 100644 --- a/packages/noise/README.md +++ b/packages/noise/README.md @@ -1,124 +1,13 @@ # @ewanc26/noise -Generic deterministic value-noise generation from a string seed. Arbitrary dimensions, multi-octave FBM, multiple colour modes. Zero runtime dependencies, works in any environment with a `Uint8ClampedArray` (browsers, Node.js, workers). - -Part of the [`@ewanc26/pkgs`](https://github.com/ewanc26/pkgs) monorepo. - -## Install +Deterministic value-noise generation from a string seed — arbitrary dimensions, multi-octave FBM, multiple colour modes. Zero dependencies. ```bash pnpm add @ewanc26/noise ``` -## Usage - -### Raw pixels (no DOM required) - -```ts -import { generateNoisePixels } from '@ewanc26/noise'; - -const pixels = generateNoisePixels(256, 256, 'my-seed', { - octaves: 4, - colorMode: { type: 'grayscale' }, -}); -// pixels is a Uint8ClampedArray of RGBA values -``` - -### Canvas - -```ts -import { renderNoise } from '@ewanc26/noise'; - -const canvas = document.querySelector('canvas'); -renderNoise(canvas, 'my-seed', { size: 128, octaves: 3 }); -``` - -### Svelte action - -```svelte - - - -``` - -## API - -### `generateNoisePixels(width, height, seed, options?)` - -Generates raw RGBA pixel data — no canvas or DOM needed. - -Returns a `Uint8ClampedArray` of length `width * height * 4`. - -### `renderNoise(canvas, seed, options?)` - -Renders noise onto an existing `HTMLCanvasElement` (resizes it to `width`/`height`/`size`). - -### `noiseAction(canvas, params)` - -Svelte action wrapper around `renderNoise`. Re-renders reactively when `params` changes via `update`. - -Params object: `{ seed, ...NoiseOptions, width?, height?, size? }` - ---- - -### Noise options - -| Option | Type | Default | Description | -|---|---|---|---| -| `gridSize` | `number` | `5` | Noise grid resolution | -| `octaves` | `number` | `1` | FBM octave count (1 = plain value noise) | -| `persistence` | `number` | `0.5` | FBM amplitude falloff per octave | -| `lacunarity` | `number` | `2` | FBM frequency multiplier per octave | -| `colorMode` | `ColorMode` | `{ type: 'hsl' }` | How noise values map to colours | - -### Render options (canvas/action only) - -| Option | Type | Default | Description | -|---|---|---|---| -| `width` | `number` | `64` | Canvas width in pixels | -| `height` | `number` | `64` | Canvas height in pixels | -| `size` | `number` | — | Shorthand to set width and height equally | - -### Colour modes - -**`{ type: 'hsl' }`** — hue derived from seed, noise shifts hue/saturation/lightness. - -| Option | Type | Default | -|---|---|---| -| `hueRange` | `number` | `60` | -| `saturationRange` | `[number, number]` | `[45, 70]` | -| `lightnessRange` | `[number, number]` | `[40, 70]` | - -**`{ type: 'grayscale' }`** — noise value maps to luminance. - -| Option | Type | Default | -|---|---|---| -| `range` | `[number, number]` | `[0, 255]` | - -**`{ type: 'palette', colors }`** — noise value interpolates through an ordered list of RGB colours. - -```ts -colorMode: { - type: 'palette', - colors: [[0, 0, 128], [0, 200, 255], [255, 255, 255]], -} -``` - ---- - -### Core primitives - -| Export | Description | -|---|---| -| `hash32(str)` | djb2 hash → unsigned 32-bit integer | -| `makePrng(seed)` | Seeded LCG PRNG → `() => float in [0, 1)` | -| `hslToRgb(h, s, l)` | HSL (components in `[0, 1]`) → RGB triple (`[0, 255]`) | -| `makeValueNoiseSampler(gridSize, rng)` | Returns `(nx, ny) → float in [0, 1]` value-noise sampler | +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/noise)**. ## Licence -AGPL-3.0-only +AGPL-3.0-only. diff --git a/packages/noise/package.json b/packages/noise/package.json index 97fc645..2ba8f20 100644 --- a/packages/noise/package.json +++ b/packages/noise/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/noise", - "version": "0.1.1", + "version": "0.1.2", "description": "Generic deterministic value-noise generation. Arbitrary dimensions, multi-octave FBM, multiple colour modes. Zero runtime dependencies, works in browsers and Node.js.", "author": "Ewan Croft", "license": "AGPL-3.0-only", diff --git a/packages/pds-landing/README.md b/packages/pds-landing/README.md index 4e6a84c..7249b47 100644 --- a/packages/pds-landing/README.md +++ b/packages/pds-landing/README.md @@ -2,131 +2,12 @@ Composable Svelte 5 components for an ATProto PDS landing page — terminal aesthetic, live status fetching, zero config to drop in. -## Install - ```bash pnpm add @ewanc26/pds-landing @ewanc26/ui ``` -## Quick start — full page - -```svelte - - - - -``` - -Import the PDS design tokens and base styles once in your layout: - -```css -/* app.css / layout.css */ -@import '@ewanc26/ui/styles/pds-tokens.css'; -``` - ---- - -## Mix-and-match — primitives - -All primitives are exported individually so you can compose custom layouts. - -```svelte - - - - - - - - - - - - - - - - - - - - - -``` - -### Use raw KV data +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/pds-landing)**. -```svelte - - - -``` - -### Fetch status yourself - -```ts -import { fetchPDSStatus } from '@ewanc26/pds-landing'; - -const { health, description, accountCount } = await fetchPDSStatus('https://pds.example.com'); -``` - ---- - -## Components - -| Component | Description | -|---|---| -| `PDSPage` | Full assembled landing page (convenience) | -| `TerminalCard` | Terminal window shell with traffic-light dots titlebar | -| `PromptLine` | `user@host:path $` bash prompt header | -| `Tagline` | Dimmed subtitle beneath the prompt | -| `SectionLabel` | Uppercase section heading | -| `Divider` | Thin green-tinted `
` | -| `KVGrid` | Key-value grid with ok/warn/err/loading states | -| `StatusGrid` | Live-fetching PDS status grid (wraps `KVGrid`) | -| `LinkList` | `→ link` list | -| `ContactSection` | Bluesky mention + optional email | -| `PDSFooter` | Footer with nixpkgs / atproto links | - -## Design tokens - -All components consume CSS custom properties from `@ewanc26/ui/styles/pds-tokens.css`: - -``` ---pds-font-mono ---pds-color-crust / mantle / base / surface-0 / surface-1 / overlay-0 ---pds-color-text / subtext-0 ---pds-color-green / red / yellow / shadow -``` +See the root [LICENSE](../../LICENSE). diff --git a/packages/pds-landing/package.json b/packages/pds-landing/package.json index c103d50..e47584c 100644 --- a/packages/pds-landing/package.json +++ b/packages/pds-landing/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/pds-landing", - "version": "2.0.6", + "version": "2.0.7", "description": "Composable Svelte components for an ATProto PDS landing page — terminal-aesthetic UI with live status fetching.", "scripts": { "dev": "vite dev", diff --git a/packages/supporters/README.md b/packages/supporters/README.md index c04c4f8..7e24301 100644 --- a/packages/supporters/README.md +++ b/packages/supporters/README.md @@ -2,113 +2,12 @@ SvelteKit component library for displaying Ko-fi supporters, backed by an ATProto PDS. -Ko-fi's webhook pushes payment events to your endpoint. Each event is stored as a record under the `uk.ewancroft.kofi.supporter` lexicon on your PDS, with a TID rkey derived from the transaction timestamp. The component reads those records and renders them. - ---- - -## How it works - -1. Ko-fi POSTs a webhook event to `/webhook` on each transaction -2. The handler verifies the `verification_token`, respects `is_public`, and calls `appendEvent` -3. `appendEvent` writes a record to your PDS under `uk.ewancroft.kofi.supporter` -4. `readStore` fetches all records and aggregates them into `KofiSupporter` objects -5. Pass the result to `` or `` - ---- - -## Setup - -### 1. Environment variables - -```env -# Required — copy from ko-fi.com/manage/webhooks → Advanced → Verification Token -KOFI_VERIFICATION_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - -# Required — your ATProto identity and a dedicated app password -ATPROTO_DID=did:plc:yourdidhex -ATPROTO_PDS_URL=https://your-pds.example.com -ATPROTO_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx -``` - -Generate an app password at your PDS under **Settings → App Passwords**. - -### 2. Register the webhook - -Go to **ko-fi.com/manage/webhooks** and set your webhook URL to: - -``` -https://your-domain.com/webhook -``` - -### 3. Add the route - -Copy `src/routes/webhook/+server.ts` into your SvelteKit app's routes directory. - -### 4. Use the component - -```ts -// +page.server.ts -import { readStore } from '@ewanc26/supporters'; - -export const load = async () => ({ - supporters: await readStore() -}); -``` - -```svelte - - - - -``` - ---- - -## Components - -### `` - -Displays all supporters with emoji type badges (☕ donation, ⭐ subscription, 🎨 commission, 🛍️ shop order). - -| Prop | Type | Default | -|---|---|---| -| `supporters` | `KofiSupporter[]` | `[]` | -| `heading` | `string` | `'Supporters'` | -| `description` | `string` | `'People who support my work on Ko-fi.'` | -| `filter` | `KofiEventType[]` | `undefined` (show all) | -| `loading` | `boolean` | `false` | -| `error` | `string \| null` | `null` | - -### `` - -Convenience wrapper around `` pre-filtered to `Subscription` events. - ---- - -## Importing historical data - -Export your transaction history from **ko-fi.com/manage/transactions → Export CSV**, then: - ```bash -ATPROTO_DID=... ATPROTO_PDS_URL=... ATPROTO_APP_PASSWORD=... \ - node node_modules/@ewanc26/supporters/scripts/import-history.mjs transactions.csv --dry-run +pnpm add @ewanc26/supporters ``` ---- - -## Lexicon +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/supporters)**. -Records are stored under `uk.ewancroft.kofi.supporter` (see `lexicons/`). Each record contains: - -```ts -{ - name: string // display name from Ko-fi - type: string // "Donation" | "Subscription" | "Commission" | "Shop Order" - tier?: string // subscription tier name, if applicable -} -``` +## Licence -rkeys are TIDs derived from the transaction timestamp via [`@ewanc26/tid`](https://npmjs.com/package/@ewanc26/tid). +See the root [LICENSE](../../LICENSE). diff --git a/packages/supporters/package.json b/packages/supporters/package.json index a1f4752..ecbe1c8 100644 --- a/packages/supporters/package.json +++ b/packages/supporters/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/supporters", - "version": "0.1.6", + "version": "0.1.7", "description": "SvelteKit component library for displaying Ko-fi supporters, backed by an ATProto PDS.", "author": "Ewan Croft", "license": "AGPL-3.0-only", diff --git a/packages/svelte-standard-site/README.md b/packages/svelte-standard-site/README.md index 15996c0..732957f 100644 --- a/packages/svelte-standard-site/README.md +++ b/packages/svelte-standard-site/README.md @@ -1,108 +1,13 @@ # @ewanc26/svelte-standard-site -> **Canonical source:** This package is now maintained in the [`@ewanc26/pkgs`](https://github.com/ewanc26/pkgs) monorepo under [`packages/svelte-standard-site`](https://github.com/ewanc26/pkgs/tree/main/packages/svelte-standard-site). This copy exists for historical context — please open issues and PRs there. - -A SvelteKit library for reading and writing AT Protocol longform content via `site.standard.*` records. Includes a complete design system, publishing tools, federated Bluesky comments, content verification helpers, and pre-built components. - -Part of the [`@ewanc26/pkgs`](https://github.com/ewanc26/pkgs) monorepo. - -## Installation +SvelteKit library for reading and writing AT Protocol longform content via `site.standard.*` records. Includes a design system, publishing tools, federated Bluesky comments, and pre-built components. ```bash pnpm add @ewanc26/svelte-standard-site zod ``` -Requires `svelte >= 5` and `@sveltejs/kit >= 2` as peer dependencies. - -## Features - -- **Reading** — Fetch `site.standard.document` and `site.standard.publication` records from AT Protocol -- **Writing** — Publish and manage documents via `StandardSitePublisher` -- **Comments** — Federated Bluesky replies as comments via the `` component -- **Verification** — `.well-known` endpoint helpers to prove content ownership -- **Design system** — Semantic colour tokens (ink, canvas, primary, secondary, accent) with automatic light/dark mode via Tailwind CSS 4 -- **Type-safe** — Full TypeScript with Zod validation -- **SSR-ready** — Works with SvelteKit's `fetch` for prerendering -- **Caching** — In-memory cache with configurable TTL - -## Quick Start - -### Reading - -```typescript -// src/routes/+page.server.ts -import { createClient } from '@ewanc26/svelte-standard-site'; - -export const load = async ({ fetch }) => { - const client = createClient({ did: 'did:plc:your-did' }); - const documents = await client.fetchAllDocuments(fetch); - return { documents }; -}; -``` - -### Publishing - -```typescript -import { StandardSitePublisher } from '@ewanc26/svelte-standard-site/publisher'; - -const publisher = new StandardSitePublisher({ - identifier: 'you.bsky.social', - password: process.env.ATPROTO_APP_PASSWORD -}); -await publisher.login(); -await publisher.publishDocument({ site, title, content, publishedAt }); -``` - -### Comments - -```svelte - - - -``` - -## Entry Points - -| Import | Description | -|--------|-------------| -| `@ewanc26/svelte-standard-site` | Components, client, stores, types, utilities | -| `@ewanc26/svelte-standard-site/publisher` | `StandardSitePublisher` for writing records | -| `@ewanc26/svelte-standard-site/content` | Markdown transformation utilities | -| `@ewanc26/svelte-standard-site/comments` | Comment fetching utilities | -| `@ewanc26/svelte-standard-site/verification` | `.well-known` and ownership verification | -| `@ewanc26/svelte-standard-site/schemas` | Zod schemas and `COLLECTIONS` constant | -| `@ewanc26/svelte-standard-site/config/env` | `getConfigFromEnv()` SvelteKit helper | -| `@ewanc26/svelte-standard-site/styles/base.css` | Base CSS | -| `@ewanc26/svelte-standard-site/styles/themes.css` | Theme CSS | - -## Development - -Development happens in the [`@ewanc26/pkgs`](https://github.com/ewanc26/pkgs) monorepo. Local commands (from `packages/svelte-standard-site`): - -```bash -pnpm build # svelte-package → dist/ -pnpm dev # svelte-package --watch -pnpm dev:app # vite dev (demo routes) -pnpm check # svelte-check -pnpm test # vitest run -``` - -## Environment Variables - -```env -PUBLIC_ATPROTO_DID=did:plc:your-did-here -PUBLIC_PUBLICATION_RKEY=3abc123xyz - -# For publishing (never commit) -ATPROTO_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx -ATPROTO_HANDLE=you.bsky.social -``` +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/svelte-standard-site)**. ## Licence -AGPL-3.0-only — see the [pkgs monorepo licence](https://github.com/ewanc26/pkgs/blob/main/LICENSE). +AGPL-3.0-only — see the [pkgs monorepo licence](../../LICENSE). diff --git a/packages/svelte-standard-site/package.json b/packages/svelte-standard-site/package.json index 22b7a9d..b25bcf7 100644 --- a/packages/svelte-standard-site/package.json +++ b/packages/svelte-standard-site/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/svelte-standard-site", - "version": "0.2.1", + "version": "0.2.2", "description": "SvelteKit library for reading and writing AT Protocol longform content via site.standard.* records — with a complete design system, federated comments, publishing tools, and content verification.", "license": "AGPL-3.0-only", "author": { diff --git a/packages/tangled-sync/README.md b/packages/tangled-sync/README.md index 73e01f2..cb2ef40 100644 --- a/packages/tangled-sync/README.md +++ b/packages/tangled-sync/README.md @@ -1,75 +1,14 @@ # @ewanc26/tangled-sync -CLI tool that clones all repositories under a GitHub user, pushes them to [Tangled](https://tangled.sh) mirrors, injects a Tangled mirror link into each README, and publishes `sh.tangled.repo` records to AT Protocol. - -Part of the [`@ewanc26/pkgs`](https://github.com/ewanc26/pkgs) monorepo. - -## Installation +CLI tool that clones all repositories under a GitHub user, pushes them to Tangled mirrors, injects a Tangled mirror link into each README, and publishes `sh.tangled.repo` records to AT Protocol. ```bash -npm install -g @ewanc26/tangled-sync -# or pnpm add -g @ewanc26/tangled-sync -``` - -Or run without installing: - -```bash +# or npx @ewanc26/tangled-sync ``` -## Setup - -Create a `.env` file in your working directory: - -```env -BASE_DIR=/path/to/local/clone/directory -GITHUB_USER=your-github-username -ATPROTO_DID=did:plc:your-did -BLUESKY_PDS=https://your-pds.example.com -BLUESKY_USERNAME=you.bsky.social -BLUESKY_PASSWORD=xxxx-xxxx-xxxx-xxxx -``` - -Ensure your Tangled SSH key is configured — the script will attempt to create Tangled remotes, which requires valid SSH authentication. - -## Usage - -Test your AT Protocol connection first: - -```bash -tangled-sync-test-atproto -``` - -Run the health check: - -```bash -tangled-sync-check -``` - -Run the sync: - -```bash -tangled-sync # sync new repos only -tangled-sync --force # force sync all repos -``` - -What happens: - -1. Authenticates with Bluesky -2. Clones all GitHub repos under `GITHUB_USER` (skips the `/` profile repo) -3. Adds a `tangled` remote to each repo if missing -4. Pushes the `main` branch to Tangled -5. Injects a Tangled mirror link into each README if not already present -6. Creates `sh.tangled.repo` ATProto records for each repo - -The sync is idempotent — existing remotes and ATProto records are checked before creation. - -## Notes - -- Record keys use TIDs (Timestamp Identifiers) to ensure uniqueness -- Repos that fail to push to Tangled are logged and skipped; the rest continue -- `BASE_DIR` is created automatically if it doesn't exist +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/tangled-sync)**. ## Licence diff --git a/packages/tangled-sync/package.json b/packages/tangled-sync/package.json index cd914cd..b226448 100644 --- a/packages/tangled-sync/package.json +++ b/packages/tangled-sync/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/tangled-sync", - "version": "1.0.1", + "version": "1.0.2", "description": "Sync GitHub repos to Tangled with ATProto records", "type": "module", "bin": { diff --git a/packages/tid/README.md b/packages/tid/README.md index 2145c8b..1e95a8d 100644 --- a/packages/tid/README.md +++ b/packages/tid/README.md @@ -1,141 +1,13 @@ # @ewanc26/tid -Zero-dependency [AT Protocol](https://atproto.com/) TID (Timestamp Identifier) generation for Node.js and browsers. - -This package is **written in TypeScript** and compiled to **plain JavaScript**, so it ships with type definitions for TypeScript users and runs anywhere the Web Crypto API is available — Node.js 20+, Deno, Bun, and modern browsers. - -TIDs are 13-character, lexicographically sortable record keys used across the AT Protocol and Bluesky. They’re monotonic identifiers derived from a microsecond timestamp and a 5-bit clock ID. When multiple TIDs would otherwise share the same microsecond, this package avoids collisions by nudging the clock ID (initialised per JS context) so each generated TID stays unique and strictly increasing within that runtime. - ---- - -## Why this package? - -Other TID implementations either require native bindings (e.g. `node-gyp`) or pull in large dependency trees. This package is **pure JavaScript**, has **no runtime dependencies**, ships with `.d.ts` typings, and is intentionally tiny — ideal for libraries, servers and client code where bundle size and portability matter. - ---- - -## Install +Zero-dependency AT Protocol TID (Timestamp Identifier) generation. Works in Node.js 20+, Deno, Bun, and browsers. ```bash -npm install @ewanc26/tid -# or pnpm add @ewanc26/tid ``` ---- - -## Usage - -### TypeScript (recommended) - -```ts -import { - generateTID, - generateNextTID, - validateTid, - decodeTid, - compareTids, -} from '@ewanc26/tid'; - -// From ISO string or Date -const tid: string = generateTID('2023-11-01T12:00:00Z'); -const tid2: string = generateTID(new Date('2024-03-15T09:30:00Z')); - -// Now -const currentTid: string = generateNextTID(); - -// Validate -const ok: boolean = validateTid('3jzfcijpj2z2a'); - -// Decode -const decoded = decodeTid('3jzfcijpj2z2a'); -console.log(decoded.timestampUs, decoded.clockId, decoded.date); - -// Sort -const tids: string[] = ['3jzfcijpj2z2a', '3jzfabc000022', '3jzfzzzzzzz2a']; -tids.sort(compareTids); -``` - -### JavaScript (ESM) - -```js -import { - generateTID, - generateNextTID, - validateTid, - decodeTid, - compareTids, -} from '@ewanc26/tid'; - -const tid = generateTID('2023-11-01T12:00:00Z'); -const currentTid = generateNextTID(); -console.log(validateTid(tid)); -``` - -### JavaScript (CommonJS) - -```js -const { - generateTID, - generateNextTID, - validateTid, - decodeTid, - compareTids, -} = require('@ewanc26/tid'); - -const tid = generateTID('2023-11-01T12:00:00Z'); -``` - ---- - -## API - -| Export | Signature | Description | | | -| ----------------- | ----------------------------- | ------------------------------------------------- | ----------------------------------------- | ------------------------------------ | -| `generateTID` | `(source: string | Date) => string` | Generate a TID for a historical timestamp | | -| `generateNextTID` | `() => string` | Generate a TID for the current wall-clock time | | | -| `validateTid` | `(tid: string) => boolean` | Returns `true` if the string is a well-formed TID | | | -| `decodeTid` | `(tid: string) => DecodedTid` | Decode a TID into timestamp, clockId, and Date | | | -| `compareTids` | `(a: string, b: string) => -1 | 0 | 1` | Lexicographic comparator for sorting | -| `resetTidClock` | `() => void` | Reset the monotonic clock (**tests only**) | | | - -### `DecodedTid` - -```ts -interface DecodedTid { - timestampUs: number; // microseconds since Unix epoch - clockId: number; // 0–31 - date: Date; // millisecond-precision equivalent -} -``` - ---- - -## Spec notes - -* TIDs are 13 characters in the AT Protocol base-32 alphabet: `234567abcdefghijklmnopqrstuvwxyz`. -* The first 11 characters encode a microsecond-precision Unix timestamp. -* The last 2 characters encode a 5-bit clock ID (0–31) which disambiguates TIDs generated on different machines or processes within the same microsecond. -* The clock ID is randomised once at module load time (per JS context). When multiple TIDs would collide at the same microsecond, the implementation adjusts (nudges) the clock ID so collisions are avoided while preserving lexicographic ordering and monotonicity within that runtime. -* Full specification: [https://atproto.com/specs/tid](https://atproto.com/specs/tid) - ---- - -## Behavioural notes - -* Monotonicity is maintained per JS context. If records arrive out of chronological order in the same runtime, the package bumps the timestamp forward to ensure every generated TID is strictly increasing. -* Clock ID collisions across processes or machines are extremely unlikely due to the randomised 5-bit clock ID; the clock-nudging only applies inside a JS context to disambiguate simultaneous generations. -* The package does not attempt cross-process coordination — if you need globally unique sequencing beyond the TID spec, consider a server-side sequencer or combining TIDs with per-host identifiers. - ---- - -## Testing & development - -* `resetTidClock()` is exported for tests to make deterministic TID generation possible. -* The library has zero runtime deps and uses the Web Crypto API for secure randomness. When running in older environments, provide a compatible Web Crypto polyfill if necessary. - ---- +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/tid)**. ## Licence -AGPL-3.0-only — same as [Malachite](https://github.com/ewanc26/malachite/tree/main). +AGPL-3.0-only. diff --git a/packages/tid/package.json b/packages/tid/package.json index 590bdbe..9dbb734 100644 --- a/packages/tid/package.json +++ b/packages/tid/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/tid", - "version": "1.1.1", + "version": "1.1.2", "description": "Zero-dependency AT Protocol TID generation for Node.js and browsers", "type": "module", "exports": { diff --git a/packages/ui/README.md b/packages/ui/README.md index c088543..65fc9c2 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -1,64 +1,12 @@ # @ewanc26/ui -Svelte UI component library extracted from [ewancroft.uk](https://ewancroft.uk). Provides layout and card components, UI primitives, SEO helpers, Svelte stores, post utilities, and a multi-theme configuration system. - -## Installation +Svelte UI component library — layout components, cards, UI primitives, SEO helpers, stores, and a multi-theme configuration system. ```bash pnpm add @ewanc26/ui ``` -Peer dependencies: `svelte >= 5`, `@sveltejs/kit >= 2`, `tailwindcss >= 4`. Optional: `@ewanc26/atproto` (for AT Protocol card components). - -## What's Exported - -### Components - -| Group | Components | -|-------|------------| -| Layout toggles | `ThemeToggle`, `WolfToggle` | -| Layout main | `DynamicLinks`, `ScrollToTop` | -| Cards | `ProfileCard`, `PostCard`, `BlueskyPostCard`, `LinkCard`, `MusicStatusCard`, `KibunStatusCard`, `TangledRepoCard` | -| UI primitives | `Card`, `InternalCard`, `Dropdown`, `Pagination`, `SearchBar`, `Tabs`, `PostsGroupedView`, `DocumentCard`, `BlogPostCard` | -| SEO | `MetaTags` | - -### Stores - -| Store | Type | Description | -|-------|------|-------------| -| `wolfMode` | `Writable` | Wolf mode text transformation | -| `colorTheme` | `Writable` | Active colour theme | -| `colorThemeDropdownOpen` | `Writable` | Theme picker open state | -| `happyMacStore` | `Writable` | Happy Mac easter egg | - -### Theme Configuration - -12 themes across four categories (neutral, warm, cool, vibrant) using OKLCH colour values. Default: `slate`. - -```typescript -import { THEMES, DEFAULT_THEME, getTheme, getThemesByCategory, CATEGORY_LABELS } from '@ewanc26/ui'; -``` - -### Helpers - -```typescript -import { getPostBadges, getBadgeClasses } from '@ewanc26/ui'; -import { filterPosts, groupPostsByDate, getSortedMonths, getSortedYears, getAllTags } from '@ewanc26/ui'; -``` - -### Types - -```typescript -import type { SiteMetadata, NavItem, ColorTheme, ThemeDefinition, PostBadge, MonthData, GroupedPosts } from '@ewanc26/ui'; -``` - -## Build - -```bash -pnpm build # svelte-package -pnpm dev # svelte-package --watch -pnpm check # svelte-check -``` +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/ui)**. ## Licence diff --git a/packages/ui/package.json b/packages/ui/package.json index 55be7f1..4f5535d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/ui", - "version": "0.1.8", + "version": "0.1.9", "description": "Svelte UI component library extracted from ewancroft.uk — pluggable layout, UI primitives, stores, and SEO components.", "type": "module", "exports": { diff --git a/packages/utils/README.md b/packages/utils/README.md index 8b731ce..cced10f 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -1,43 +1,12 @@ # @ewanc26/utils -Shared utility functions extracted from [ewancroft.uk](https://ewancroft.uk). Zero runtime dependencies. - -## Modules - -- **Date & Locale** — `formatRelativeTime`, `formatLocalizedDate`, `getUserLocale` -- **Number Formatting** — `formatCompactNumber`, `formatNumber` -- **URL Utilities** — `getDomain`, `atUriToBlueskyUrl`, `getBlueskyProfileUrl`, `isExternalUrl` -- **Validators & Text** — `isValidTid`, `isValidDid`, `truncateText`, `escapeHtml`, `getInitials`, `debounce`, `throttle` -- **RSS Generation** — `generateRSSFeed`, `generateRSSItem`, `createRSSResponse`, `escapeXml`, `normalizeCharacters`, `formatRSSDate` - -## Installation +Shared utility functions — date formatting, number formatting, URL utilities, validators, text helpers, and RSS generation. Zero runtime dependencies. ```bash pnpm add @ewanc26/utils ``` -## Quick Examples - -```typescript -import { formatRelativeTime, formatCompactNumber, getDomain, isValidDid, generateRSSFeed } from '@ewanc26/utils'; - -formatRelativeTime('2025-11-13T00:00:00Z'); // '3d ago' -formatCompactNumber(1500); // '1.5K' -getDomain('https://www.example.com/path'); // 'example.com' -isValidDid('did:plc:abc123'); // true - -const xml = generateRSSFeed({ title: 'My Blog', link: 'https://mysite.com', description: '…' }, items); -``` - -All functions are SSR-safe and fall back to `en-GB` when `navigator` / `window` are unavailable. - -## Build - -```bash -pnpm build # tsc -pnpm dev # tsc --watch -pnpm check # tsc --noEmit -``` +Full documentation at **[docs.ewancroft.uk](https://docs.ewancroft.uk/projects/utils)**. ## Licence diff --git a/packages/utils/package.json b/packages/utils/package.json index 6f79048..23239fe 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@ewanc26/utils", - "version": "0.1.3", + "version": "0.1.4", "description": "Shared utility functions extracted from ewancroft.uk", "type": "module", "exports": { -- 2.51.2