a standard.site publication renderer for SvelteKit.
1# CLAUDE.md
2
3## What
4
5SvelteKit library for ATProto longform publishing via the `standard.site` lexicon. Provides both read and write capabilities: display content from ATProto (Leaflet/WhiteWind), publish content TO ATProto, and aggregate federated comments.
6
7**Package:** `svelte-standard-site`
8
9## Project Structure
10
11```
12src/lib/
13 client.ts # Read from ATProto (fetch documents/publications)
14 publisher.ts # Write to ATProto (publish documents/publications)
15 schemas.ts # Zod schemas for validation
16 types.ts # TypeScript type definitions
17 components/
18 Comments.svelte # Federated comments from Bluesky
19 DocumentCard.svelte
20 PublicationCard.svelte
21 StandardSiteLayout.svelte
22 ThemeToggle.svelte
23 common/ # Reusable utility components
24 document/ # Document rendering components
25 utils/
26 content.ts # Markdown transformation (sidenotes, links, etc.)
27 comments.ts # Fetch Bluesky replies
28 verification.ts # Ownership verification helpers
29 at-uri.ts # AT-URI parsing and conversion
30 theme.ts # Theme utilities
31 cache.ts # Caching layer
32 stores/
33 theme.ts # Dark/light mode store
34 styles/
35 base.css # Core design system
36 themes.css # Theme definitions
37```
38
39## Commands
40
41```bash
42pnpm dev # Start dev server
43pnpm build # Build package
44pnpm test # Run tests
45pnpm check # Type check
46```
47
48## Critical: TID Format
49
50Record keys for `site.standard.document` and `site.standard.publication` MUST be TIDs. Schema validation will reject anything else.
51
52**TID requirements:**
53- 13 characters, base32-sortable charset: `234567abcdefghijklmnopqrstuvwxyz`
54- First char must be `234567abcdefghij` (top bit = 0)
55- Regex: `/^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/`
56
57See `generateTid()` in `src/lib/publisher.ts` — do not modify without reading https://atproto.com/specs/tid
58
59## Critical: ES Modules
60
61`package.json` must have `"type": "module"`. Without this, imports break.
62
63## Key Concepts
64
65### Read vs Write
66
67- **SiteStandardClient** (`client.ts`): Read-only. Fetches content from ATProto.
68- **StandardSitePublisher** (`publisher.ts`): Write operations. Publishes content to ATProto.
69
70### Content Transformation
71
72The `content.ts` utilities transform markdown for ATProto compatibility:
73- Convert HTML sidenotes → markdown blockquotes
74- Resolve relative links → absolute URLs
75- Extract plain text for search indexing
76- Calculate word count and reading time
77
78### Comments System
79
80The Comments component fetches Bluesky replies and displays them as comments on blog posts. It uses the ATProto API to recursively fetch threaded conversations.
81
82### Verification
83
84Verification helpers generate `.well-known` endpoints and `<link>` tags to prove content ownership. This allows platforms to verify that you control the content you've published.
85
86## Testing Against Real PDS
87
88```bash
89# Set your app password
90export ATPROTO_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"
91
92# Run publisher test
93node scripts/test-publisher.js
94```
95
96For integration testing, use `pds.rip` (throwaway test accounts).
97
98## Design System
99
100The library uses semantic color tokens that automatically adapt to light/dark mode:
101
102- **Ink**: Text colors (ink-50 to ink-950)
103- **Canvas**: Background colors (canvas-50 to canvas-950)
104- **Primary**: Brand colors (primary-50 to primary-950)
105- **Secondary**: Secondary brand (secondary-50 to secondary-950)
106- **Accent**: Accent colors (accent-50 to accent-950)
107
108All styled using Tailwind v4 with `light-dark()` function.
109
110## Publishing to ATProto
111
112```typescript
113import { StandardSitePublisher } from 'svelte-standard-site/publisher';
114
115const publisher = new StandardSitePublisher({
116 identifier: 'you.bsky.social',
117 password: process.env.ATPROTO_APP_PASSWORD!,
118});
119
120await publisher.login();
121
122await publisher.publishDocument({
123 site: 'https://yourblog.com',
124 title: 'My Post',
125 publishedAt: new Date().toISOString(),
126 content: {
127 $type: 'site.standard.content.markdown',
128 text: markdownContent,
129 version: '1.0',
130 },
131 textContent: plainTextContent,
132});
133```
134
135## Reading from ATProto
136
137```typescript
138import { createClient } from 'svelte-standard-site';
139import { getConfigFromEnv } from 'svelte-standard-site/config/env';
140
141const config = getConfigFromEnv();
142const client = createClient(config);
143
144const documents = await client.fetchAllDocuments(fetch);
145const publications = await client.fetchAllPublications(fetch);
146```
147
148## Comments
149
150```svelte
151<script>
152 import { Comments } from 'svelte-standard-site';
153</script>
154
155<Comments
156 bskyPostUri="at://did:plc:xxx/app.bsky.feed.post/abc123"
157 canonicalUrl="https://yourblog.com/posts/my-post"
158 maxDepth={3}
159/>
160```
161
162## Content Transformation
163
164```typescript
165import { transformContent } from 'svelte-standard-site/content';
166
167const result = transformContent(rawMarkdown, {
168 baseUrl: 'https://yourblog.com',
169});
170
171// result.markdown - cleaned for ATProto
172// result.textContent - plain text for search
173// result.wordCount
174// result.readingTime
175```
176
177## Verification
178
179```typescript
180// src/routes/.well-known/site.standard.publication/+server.ts
181import { generatePublicationWellKnown } from 'svelte-standard-site/verification';
182import { text } from '@sveltejs/kit';
183
184export function GET() {
185 return text(
186 generatePublicationWellKnown({
187 did: 'did:plc:xxx',
188 publicationRkey: '3abc123xyz',
189 })
190 );
191}
192```
193
194## Important Notes
195
1961. **App Passwords**: Always use app passwords, never main account passwords
1972. **PDS Resolution**: The publisher auto-resolves PDS from DID documents
1983. **Caching**: The client has built-in caching (5-minute TTL by default)
1994. **SSR**: All fetch operations support SvelteKit's `fetch` for SSR
2005. **Theme Store**: Call `themeStore.init()` in `onMount()` to enable theme toggle
2016. **Blob URLs**: Cover images and icons are converted from blob refs to HTTPS URLs
202
203## External References
204
205- ATProto specs: https://atproto.com/
206- standard.site: https://standard.site/
207- Lexicon explorer: https://pdsls.dev/
208- Bluesky: https://bsky.app/
209
210## License
211
212AGPL-3.0 (stricter than Astro version's MIT)