Social Annotations in the Atmosphere
15
fork

Configure Feed

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

fix: chrome login, button styling, firefox icon

+379 -110
+17
.beads/beads.base.jsonl
··· 1 + {"id":"seams.so-07q","content_hash":"386ef54d1462f33d5e65cdb1a2b9d1adb74640412777b76f113b2c1d0e2c81d2","title":"Phase 4: Align PDS Client/Backend with ATProto","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-18T23:32:46.142847363-08:00","updated_at":"2025-11-18T23:32:46.142847363-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-07q","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:46.144931665-08:00","created_by":"daemon"}]} 2 + {"id":"seams.so-08d","content_hash":"b14f3600f09d0bf61086d546668cc94878363f9d44cab24262bb22ddf3751574","title":"Core Architecture Simplification \u0026 Reliability","description":"","status":"open","priority":1,"issue_type":"epic","created_at":"2025-11-18T23:32:41.019119236-08:00","updated_at":"2025-11-18T23:32:41.019119236-08:00","source_repo":"."} 3 + {"id":"seams.so-4gg","content_hash":"6b7e6442d767c13291cec04766dc7c6059429446b9066aa60adbc05828997fcb","title":"Phase 2: Integrate SeamsAnnotationCard into Sidebar","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-22T17:29:17.92387007-08:00","updated_at":"2025-11-22T17:35:53.934213606-08:00","closed_at":"2025-11-22T17:35:53.934213606-08:00","source_repo":"."} 4 + {"id":"seams.so-65m","content_hash":"b247c701f65c4001f54f81265e9ef4bae694b7493138c1015085fef08db8a2f4","title":"Audit and fix unsafe innerHTML usage and sanitization","description":"","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-18T23:30:21.237774884-08:00","updated_at":"2025-11-18T23:30:21.237774884-08:00","source_repo":"."} 5 + {"id":"seams.so-6as","content_hash":"7e7d1573b80b93207b8974585859de2db8d31cf42f9c0e3144416d9049553553","title":"Phase 2: Unify Message Bus with Type-Safe Messenger","description":"","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-18T23:32:46.040086584-08:00","updated_at":"2025-11-18T23:32:46.040086584-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-6as","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:46.042717511-08:00","created_by":"daemon"}]} 6 + {"id":"seams.so-72x","content_hash":"08c2e80c8a3cba96c44b099ab52c2f3effa5139ae3ad4a066b86d544b8557075","title":"Phase 2: Don't require login for sidebar functionality","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-22T17:29:17.84004417-08:00","updated_at":"2025-11-22T17:35:53.866272321-08:00","closed_at":"2025-11-22T17:35:53.866272321-08:00","source_repo":"."} 7 + {"id":"seams.so-btm","content_hash":"cc23540734a41c32c1bfab62a7d682eeda62166167d038a47afc22dea5626273","title":"Implement URL Share Intent for Proxy PWA","description":"","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-18T23:26:19.717576506-08:00","updated_at":"2025-11-18T23:26:19.717576506-08:00","source_repo":"."} 8 + {"id":"seams.so-dug","content_hash":"c3509e6514e78aba53504a93fb12647f38e44d8d90512e96eb6f4c3e8fa26b97","title":"Phase 3: Enforce Storage-First Architecture","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-18T23:32:46.095602221-08:00","updated_at":"2025-11-18T23:32:46.095602221-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-dug","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:46.097918119-08:00","created_by":"daemon"}]} 9 + {"id":"seams.so-iji","content_hash":"3f105051d1c42a39ebc3af2d850ed6b60a58bfefa2753ce246692a44b3f57ab3","title":"Update AT Protocol OAuth config with Chrome Web Store extension ID","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-14T20:54:15.969349171-08:00","updated_at":"2025-11-14T21:10:31.150138092-08:00","closed_at":"2025-11-14T21:10:31.150138092-08:00","source_repo":"."} 10 + {"id":"seams.so-k2j","content_hash":"26a5e0323e09c27208e45815b2084472d19af3a87bb3c8dc324457853f1d9a4a","title":"Phase 1: Define Core Protocol Constants \u0026 Message Types","description":"","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-18T23:32:45.993817558-08:00","updated_at":"2025-11-18T23:32:45.993817558-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-k2j","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:45.99657691-08:00","created_by":"daemon"}]} 11 + {"id":"seams.so-kcg","content_hash":"75a66d69bc7f3dc40cb2c6cca2db45e78b260c34781292b5a174026abc46039d","title":"Add logo and favicon to landing pages","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-22T00:05:35.119547229-08:00","updated_at":"2025-11-22T00:05:35.119547229-08:00","source_repo":"."} 12 + {"id":"seams.so-lsh","content_hash":"2009b4fb73ed49a81e0b96aea89ff8520d314e5158ca520fba5e5bb0de26a915","title":"Phase 2: Create SeamsAnnotationCard Web Component","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-22T17:29:17.898246534-08:00","updated_at":"2025-11-22T17:35:53.912911367-08:00","closed_at":"2025-11-22T17:35:53.912911367-08:00","source_repo":"."} 13 + {"id":"seams.so-mp2","content_hash":"0887e7d649172ad85faa41cb44ed20f1d200ba8cf32eabb55bc9d85ccaf0affd","title":"Phase 2: Integrate SeamsAnnotationCard into Landing","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-22T17:29:17.951124595-08:00","updated_at":"2025-11-22T17:35:53.956915437-08:00","closed_at":"2025-11-22T17:35:53.956915437-08:00","source_repo":"."} 14 + {"id":"seams.so-ocf","content_hash":"0a3c72f541f09652ba69bbc01dcad00953aa69cb1450072637edcc39de0085fa","title":"Phase 2: Fix floating annotation button login check","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-22T17:29:17.873279627-08:00","updated_at":"2025-11-22T17:35:53.892298912-08:00","closed_at":"2025-11-22T17:35:53.892298912-08:00","source_repo":"."} 15 + {"id":"seams.so-rlq","content_hash":"1812949c8de2a7f3465bee85295f92c3d923568d47e9b4df15dc25c01ddc798f","title":"Configure Proxy Service as PWA","description":"","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-18T23:26:16.152745718-08:00","updated_at":"2025-11-18T23:26:16.152745718-08:00","source_repo":"."} 16 + {"id":"seams.so-s8c","content_hash":"be3aca226e4e9749d46f83c0efa735455b25a44d019b4a005bc0669acb0f3596","title":"Ensure persistent login for Proxy PWA","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-18T23:26:19.859051558-08:00","updated_at":"2025-11-18T23:26:19.859051558-08:00","source_repo":"."} 17 + {"id":"seams.so-vp1","content_hash":"3ba0850cb5f709e86796104a6804d665d1de9f9eddb63f919728ad711f8d4ed6","title":"Add Shadow DOM wrapper for sidebar iframe container","description":"","status":"open","priority":3,"issue_type":"task","created_at":"2025-11-15T02:07:42.906585543-08:00","updated_at":"2025-11-15T02:07:42.906585543-08:00","source_repo":"."}
+1
.beads/beads.base.meta.json
··· 1 + {"version":"0.23.0","timestamp":"2025-12-23T21:18:50.715615869-08:00","commit":"8677285"}
+17
.beads/beads.left.jsonl
··· 1 + {"id":"seams.so-07q","content_hash":"386ef54d1462f33d5e65cdb1a2b9d1adb74640412777b76f113b2c1d0e2c81d2","title":"Phase 4: Align PDS Client/Backend with ATProto","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-18T23:32:46.142847363-08:00","updated_at":"2025-11-18T23:32:46.142847363-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-07q","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:46.144931665-08:00","created_by":"daemon"}]} 2 + {"id":"seams.so-08d","content_hash":"b14f3600f09d0bf61086d546668cc94878363f9d44cab24262bb22ddf3751574","title":"Core Architecture Simplification \u0026 Reliability","description":"","status":"open","priority":1,"issue_type":"epic","created_at":"2025-11-18T23:32:41.019119236-08:00","updated_at":"2025-11-18T23:32:41.019119236-08:00","source_repo":"."} 3 + {"id":"seams.so-4gg","content_hash":"6b7e6442d767c13291cec04766dc7c6059429446b9066aa60adbc05828997fcb","title":"Phase 2: Integrate SeamsAnnotationCard into Sidebar","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-22T17:29:17.92387007-08:00","updated_at":"2025-11-22T17:35:53.934213606-08:00","closed_at":"2025-11-22T17:35:53.934213606-08:00","source_repo":"."} 4 + {"id":"seams.so-65m","content_hash":"b247c701f65c4001f54f81265e9ef4bae694b7493138c1015085fef08db8a2f4","title":"Audit and fix unsafe innerHTML usage and sanitization","description":"","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-18T23:30:21.237774884-08:00","updated_at":"2025-11-18T23:30:21.237774884-08:00","source_repo":"."} 5 + {"id":"seams.so-6as","content_hash":"7e7d1573b80b93207b8974585859de2db8d31cf42f9c0e3144416d9049553553","title":"Phase 2: Unify Message Bus with Type-Safe Messenger","description":"","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-18T23:32:46.040086584-08:00","updated_at":"2025-11-18T23:32:46.040086584-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-6as","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:46.042717511-08:00","created_by":"daemon"}]} 6 + {"id":"seams.so-72x","content_hash":"08c2e80c8a3cba96c44b099ab52c2f3effa5139ae3ad4a066b86d544b8557075","title":"Phase 2: Don't require login for sidebar functionality","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-22T17:29:17.84004417-08:00","updated_at":"2025-11-22T17:35:53.866272321-08:00","closed_at":"2025-11-22T17:35:53.866272321-08:00","source_repo":"."} 7 + {"id":"seams.so-btm","content_hash":"cc23540734a41c32c1bfab62a7d682eeda62166167d038a47afc22dea5626273","title":"Implement URL Share Intent for Proxy PWA","description":"","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-18T23:26:19.717576506-08:00","updated_at":"2025-11-18T23:26:19.717576506-08:00","source_repo":"."} 8 + {"id":"seams.so-dug","content_hash":"c3509e6514e78aba53504a93fb12647f38e44d8d90512e96eb6f4c3e8fa26b97","title":"Phase 3: Enforce Storage-First Architecture","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-18T23:32:46.095602221-08:00","updated_at":"2025-11-18T23:32:46.095602221-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-dug","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:46.097918119-08:00","created_by":"daemon"}]} 9 + {"id":"seams.so-iji","content_hash":"3f105051d1c42a39ebc3af2d850ed6b60a58bfefa2753ce246692a44b3f57ab3","title":"Update AT Protocol OAuth config with Chrome Web Store extension ID","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-14T20:54:15.969349171-08:00","updated_at":"2025-11-14T21:10:31.150138092-08:00","closed_at":"2025-11-14T21:10:31.150138092-08:00","source_repo":"."} 10 + {"id":"seams.so-k2j","content_hash":"26a5e0323e09c27208e45815b2084472d19af3a87bb3c8dc324457853f1d9a4a","title":"Phase 1: Define Core Protocol Constants \u0026 Message Types","description":"","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-18T23:32:45.993817558-08:00","updated_at":"2025-11-18T23:32:45.993817558-08:00","source_repo":".","dependencies":[{"issue_id":"seams.so-k2j","depends_on_id":"seams.so-08d","type":"discovered-from","created_at":"2025-11-18T23:32:45.99657691-08:00","created_by":"daemon"}]} 11 + {"id":"seams.so-kcg","content_hash":"75a66d69bc7f3dc40cb2c6cca2db45e78b260c34781292b5a174026abc46039d","title":"Add logo and favicon to landing pages","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-22T00:05:35.119547229-08:00","updated_at":"2025-11-22T00:05:35.119547229-08:00","source_repo":"."} 12 + {"id":"seams.so-lsh","content_hash":"2009b4fb73ed49a81e0b96aea89ff8520d314e5158ca520fba5e5bb0de26a915","title":"Phase 2: Create SeamsAnnotationCard Web Component","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-22T17:29:17.898246534-08:00","updated_at":"2025-11-22T17:35:53.912911367-08:00","closed_at":"2025-11-22T17:35:53.912911367-08:00","source_repo":"."} 13 + {"id":"seams.so-mp2","content_hash":"0887e7d649172ad85faa41cb44ed20f1d200ba8cf32eabb55bc9d85ccaf0affd","title":"Phase 2: Integrate SeamsAnnotationCard into Landing","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-22T17:29:17.951124595-08:00","updated_at":"2025-11-22T17:35:53.956915437-08:00","closed_at":"2025-11-22T17:35:53.956915437-08:00","source_repo":"."} 14 + {"id":"seams.so-ocf","content_hash":"0a3c72f541f09652ba69bbc01dcad00953aa69cb1450072637edcc39de0085fa","title":"Phase 2: Fix floating annotation button login check","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-22T17:29:17.873279627-08:00","updated_at":"2025-11-22T17:35:53.892298912-08:00","closed_at":"2025-11-22T17:35:53.892298912-08:00","source_repo":"."} 15 + {"id":"seams.so-rlq","content_hash":"1812949c8de2a7f3465bee85295f92c3d923568d47e9b4df15dc25c01ddc798f","title":"Configure Proxy Service as PWA","description":"","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-18T23:26:16.152745718-08:00","updated_at":"2025-11-18T23:26:16.152745718-08:00","source_repo":"."} 16 + {"id":"seams.so-s8c","content_hash":"be3aca226e4e9749d46f83c0efa735455b25a44d019b4a005bc0669acb0f3596","title":"Ensure persistent login for Proxy PWA","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-18T23:26:19.859051558-08:00","updated_at":"2025-11-18T23:26:19.859051558-08:00","source_repo":"."} 17 + {"id":"seams.so-vp1","content_hash":"3ba0850cb5f709e86796104a6804d665d1de9f9eddb63f919728ad711f8d4ed6","title":"Add Shadow DOM wrapper for sidebar iframe container","description":"","status":"open","priority":3,"issue_type":"task","created_at":"2025-11-15T02:07:42.906585543-08:00","updated_at":"2025-11-15T02:07:42.906585543-08:00","source_repo":"."}
+1
.beads/beads.left.meta.json
··· 1 + {"version":"0.23.0","timestamp":"2025-12-23T21:18:48.367777668-08:00","commit":"8677285"}
+174 -106
AGENTS.md
··· 1 1 # AGENTS.md - seams.so 2 2 3 - ## Project Overview 4 - **Hypothesis clone built on AT Protocol** - Web annotation system similar to Hypothesis but using ATProto/Bluesky infrastructure. 3 + Web annotation system built on AT Protocol (Hypothesis clone using Bluesky infrastructure). 5 4 6 - ## Issue Tracking with bd (beads) 5 + ## Commands 7 6 8 - **IMPORTANT**: This project uses **bd (beads)** for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods. 7 + ```bash 8 + pnpm install # Install dependencies 9 + pnpm dev # Start WXT dev server with hot reload 10 + pnpm build # Build extension for production 11 + pnpm zip # Package for Chrome/Firefox stores 12 + pnpm build:via # Build proxy client 13 + pnpm dev:via # Watch mode for proxy client 14 + pnpm build:landing # Build landing page 15 + ``` 9 16 10 - ### Why bd? 17 + ## Project Structure 11 18 12 - - Dependency-aware: Track blockers and relationships between issues 13 - - Git-friendly: Auto-syncs to JSONL for version control 14 - - Agent-optimized: JSON output, ready work detection, discovered-from links 15 - - Prevents duplicate tracking systems and confusion 19 + ``` 20 + seams.so/ 21 + ├── entrypoints/ # Extension entry points (WXT) 22 + │ ├── background.ts # Service worker 23 + │ ├── content.ts # Content script 24 + │ ├── sidepanel/ # Sidebar UI 25 + │ └── via-client/ # Proxy sidebar client 26 + ├── packages/core/ # Shared core logic (@seams/core) 27 + │ └── src/ 28 + │ ├── background/ # Background worker logic 29 + │ ├── content/ # Content script logic 30 + │ ├── oauth/ # AT Protocol OAuth 31 + │ ├── pds/ # PDS client (annotations/comments) 32 + │ ├── sidebar/ # Sidebar UI logic 33 + │ ├── storage/ # Storage adapters 34 + │ ├── utils/ # Utilities (highlights, selectors) 35 + │ └── types.ts # TypeScript interfaces 36 + ├── landing/ # Public landing page 37 + ├── proxy/ # pywb proxy service 38 + └── history/ # Architecture docs and planning 39 + ``` 16 40 17 - ### Quick Start 41 + ## Issue Tracking (bd/beads) 18 42 19 - **Check for ready work:** 43 + **IMPORTANT**: Use `bd` for ALL issue tracking. Do NOT use markdown TODOs. 44 + 20 45 ```bash 21 - bd ready --json 46 + bd ready --json # Show ready work 47 + bd create "Title" -t bug -p 1 --json # Create issue 48 + bd update bd-42 --status in_progress # Claim work 49 + bd close bd-42 --reason "Done" # Complete work 22 50 ``` 23 51 24 - **Create new issues:** 25 - ```bash 26 - bd create "Issue title" -t bug|feature|task -p 0-4 --json 27 - bd create "Issue title" -p 1 --deps discovered-from:bd-123 --json 28 - ``` 52 + Priorities: 0=critical, 1=high, 2=medium, 3=low, 4=backlog 53 + 54 + ## AT Protocol OAuth Primer 29 55 30 - **Claim and update:** 31 - ```bash 32 - bd update bd-42 --status in_progress --json 33 - bd update bd-42 --priority 1 --json 34 - ``` 56 + ### Key Concepts 35 57 36 - **Complete work:** 37 - ```bash 38 - bd close bd-42 --reason "Completed" --json 58 + **AT Protocol is NOT just Bluesky.** It's a decentralized protocol where: 59 + - Users have a **DID** (decentralized identifier) and a **handle** (e.g., `user.bsky.social`) 60 + - User data lives on a **PDS** (Personal Data Server), not necessarily bsky.social 61 + - Apps authenticate via OAuth to read/write records on the user's PDS 62 + 63 + ### OAuth Flow Overview 64 + 65 + 1. **Handle Resolution**: User enters handle → resolve to DID + PDS endpoint 66 + 2. **Authorization**: Redirect user to their PDS's authorization server 67 + 3. **Token Exchange**: Exchange auth code for access/refresh tokens (with PKCE) 68 + 4. **Authenticated Requests**: Use tokens to call XRPC endpoints on user's PDS 69 + 70 + ### Required Security Features 71 + 72 + - **PKCE** (Proof Key for Code Exchange): Prevents auth code interception 73 + - **State Parameter**: Prevents CSRF attacks 74 + - **DPoP** (Demonstration of Proof-of-Possession): Binds tokens to client 75 + 76 + ### Available Scopes 77 + 78 + | Scope | Description | Risk Level | 79 + |-------|-------------|------------| 80 + | `atproto` | Basic AT Protocol access (required) | Low | 81 + | `transition:generic` | Read/write posts, follows, blocks | High | 82 + | `transition:chat.bsky` | Access to Bluesky DMs | High | 83 + 84 + **Always request minimal scopes.** This app uses custom lexicons (`community.lexicon.annotation.*`) 85 + so we only need `atproto` scope - we don't need `transition:generic` for Bluesky social features. 86 + 87 + ### Handle Resolution 88 + 89 + Handles are resolved via DNS TXT records or `.well-known/atproto-did`: 90 + ``` 91 + user.bsky.social → DNS TXT _atproto.user.bsky.social → did:plc:xyz 92 + did:plc:xyz → plc.directory → PDS endpoint (e.g., https://bsky.social) 39 93 ``` 40 94 41 - ### Issue Types 95 + ### Common Mistakes to Avoid 42 96 43 - - `bug` - Something broken 44 - - `feature` - New functionality 45 - - `task` - Work item (tests, docs, refactoring) 46 - - `epic` - Large feature with subtasks 47 - - `chore` - Maintenance (dependencies, tooling) 97 + - **Don't hardcode bsky.social** - users can be on any PDS 98 + - **Don't request `transition:generic`** unless you need Bluesky social features 99 + - **Don't store tokens in localStorage** - use secure HTTP-only cookies or server sessions 100 + - **Don't skip PKCE/state validation** - these prevent real attacks 48 101 49 - ### Priorities 102 + ## Code Style 50 103 51 - - `0` - Critical (security, data loss, broken builds) 52 - - `1` - High (major features, important bugs) 53 - - `2` - Medium (default, nice-to-have) 54 - - `3` - Low (polish, optimization) 55 - - `4` - Backlog (future ideas) 104 + ### TypeScript Config 105 + - Strict mode enabled, ES2020 target, ESNext modules with bundler resolution 56 106 57 - ### Important Rules 107 + ### Naming Conventions 108 + ```typescript 109 + const currentUrl = normalizeUrl(url); // camelCase for functions/variables 110 + interface Annotation {} // PascalCase for types/classes 111 + const OAUTH_SESSION_KEY = "session"; // SCREAMING_SNAKE_CASE for constants 112 + ``` 58 113 59 - - ✅ Use bd for ALL task tracking 60 - - ✅ Always use `--json` flag for programmatic use 61 - - ✅ Link discovered work with `discovered-from` dependencies 62 - - ✅ Check `bd ready` before asking "what should I work on?" 63 - - ✅ Store AI planning docs in `history/` directory 64 - - ❌ Do NOT create markdown TODO lists 65 - - ❌ Do NOT use external issue trackers 66 - - ❌ Do NOT duplicate tracking systems 67 - - ❌ Do NOT clutter repo root with planning documents 114 + ### Imports 115 + ```typescript 116 + // Named imports from external packages 117 + import { configureOAuth } from "@atcute/oauth-browser-client"; 118 + import type { OAuthSession } from "@atcute/oauth-browser-client"; 68 119 69 - For more details, see README.md and QUICKSTART.md. 120 + // Workspace imports 121 + import { BrowserStorageAdapter } from '@seams/core'; 70 122 71 - ## Backend Integration 72 - - **Go backend** (`server/`): SQLite-backed annotation indexing service 73 - - **Backend API**: Indexes annotations from users' PDSs, provides GET endpoint for querying by URL 74 - - **Storage-first architecture**: Extension uses `browser.storage.local` as cache, background worker manages fetches 75 - - **DID resolution**: Backend resolves DIDs via plc.directory to query any PDS (not just bsky.social) 76 - - **Rate limiting**: 100 req/min (GET), 10 req/min (POST) per IP 77 - - **See**: `server/README.md` for backend documentation, `PENDING_TASKS.md` for known issues 123 + // Relative imports for internal modules 124 + import type { StorageAdapter } from '../storage/adapter'; 125 + ``` 78 126 79 - ## Future Architecture Plans 80 - - **sure.seams.so proxy**: Proxy service for annotation injection (like Hypothesis's via.hypothes.is) 81 - - **Firehose integration**: Real-time indexing via Jetstream subscriber 82 - - **Comments backend**: Index and serve comments (currently only user's own comments synced from PDS) 127 + ### Error Handling 128 + ```typescript 129 + try { 130 + const response = await fetch(url); 131 + if (!response.ok) throw new Error(`Backend error: ${response.status}`); 132 + return await response.json(); 133 + } catch (error) { 134 + console.error('[module] Failed to fetch:', error); 135 + throw error; 136 + } 137 + ``` 83 138 84 - ## Commands 139 + ### Console Logging 140 + Use bracketed prefixes to identify modules: 141 + ```typescript 142 + console.log('[oauth] Starting login process'); 143 + console.error('[content] Failed to generate selectors:', err); 144 + console.warn('[synthesis] Could not anchor annotation:', annotation); 145 + ``` 85 146 86 - ### Extension Development 87 - - **Dev**: `pnpm dev` - Start WXT development server with hot reload 88 - - **Build**: `pnpm build` - Build extension for production 89 - - **Package**: `pnpm zip` - Create extension zip file for store submission 90 - - **Test**: Manual testing in browser (see `STORAGE_FIRST_ARCHITECTURE.md` for testing checklist) 147 + ### Browser APIs 148 + Use `browser.*` namespace (not `chrome.*`) for cross-browser compatibility: 149 + ```typescript 150 + browser.storage.local.get('key'); 151 + browser.tabs.query({ active: true }); 152 + browser.runtime.sendMessage({ type: 'SYNC_CACHE' }); 153 + ``` 91 154 92 - ### Backend Development 93 - - **Dev env**: `cd server && nix develop` - Enter Nix shell with Go toolchain 94 - - **Run (dev)**: `cd server && air` - Start backend with hot reload (port 8080) 95 - - **Run (prod)**: `go run cmd/server/main.go` - Start backend server (port 8080) 96 - - **Test**: `go test ./internal/service -v` - Run integration tests 97 - - **Build**: `go build -o seams-server ./cmd/server` - Build binary 155 + ### Class Structure 156 + ```typescript 157 + export class PDSClient { 158 + private oauth: OAuthManager; 159 + constructor(oauth: OAuthManager) { this.oauth = oauth; } 160 + private async authenticatedAgent() { /* ... */ } 161 + async createAnnotation(annotation: Annotation): Promise<Annotation> { /* ... */ } 162 + } 163 + ``` 98 164 99 - ## Architecture 165 + ## Architecture Patterns 100 166 101 - ### Browser Extension (TypeScript/WXT) 102 - - **entrypoints/background.ts**: Service worker - tab event listeners, annotation fetching, PDS sync 103 - - **entrypoints/content.ts**: Content script - reads from storage, renders highlights, tracks selection 104 - - **entrypoints/sidepanel/main.ts**: Sidepanel UI - annotation creation, display, storage.onChanged listener 105 - - **lib/**: Core logic 106 - - `oauth.ts`: AT Protocol OAuth flow 107 - - `pds.ts`: PDS interaction (create/delete annotations, query backend) 108 - - `highlights/`: Highlight rendering and popover 109 - - `selectors/`: Text selection and anchoring 110 - - `types/`: TypeScript interfaces 167 + ### Storage-First 168 + Extension uses `browser.storage.local` as the source of truth: 169 + 1. Background worker fetches from backend, writes to storage 170 + 2. Content script reads from storage, renders highlights 171 + 3. Sidebar reads/writes storage, sends sync messages 111 172 112 - ### Backend (Go) 113 - - **cmd/server/main.go**: HTTP server entry point 114 - - **internal/api/**: HTTP handlers and rate limiting 115 - - **internal/atproto/**: AT Protocol client (fetch records, resolve DIDs) 116 - - **internal/db/**: SQLite database with migrations 117 - - **internal/service/**: Core indexing logic (shared by HTTP and future firehose) 118 - - **internal/models/**: Data models 173 + ### Message Passing 174 + ```typescript 175 + browser.runtime.sendMessage({ type: 'SELECTION_CHANGED', selection }); 176 + browser.runtime.sendMessage({ type: 'SYNC_CACHE' }); 177 + ``` 119 178 120 - ### Data Flow 179 + ### Storage Adapter Pattern 180 + Both extension and web proxy implement the same interface: 181 + ```typescript 182 + interface StorageAdapter { 183 + get(keys: string | string[]): Promise<any>; 184 + set(key: string, value: any): Promise<void>; 185 + onChange(callback: (change: StorageChange) => void): void; 186 + } 121 187 ``` 122 - User creates annotation 123 - → PDS (via OAuth) 124 - → Backend POST /api/annotations/index (uri, cid) 125 - → Backend fetches full record from PDS 126 - → Backend stores in SQLite 127 - → Extension optimistically adds to storage.local 128 - → Background worker fetches from backend on tab change 129 - → Content script renders highlights from storage.local 188 + 189 + ## Environment Variables 190 + ```bash 191 + BACKEND_URL=http://localhost:8080 # Extension - Production: https://seams.so 192 + VITE_OAUTH_CLIENT_ID=... # Via client OAuth 193 + VITE_OAUTH_REDIRECT_URI=... 194 + VITE_BACKEND_URL=... 130 195 ``` 131 196 132 - ## Code Style 133 - - **TypeScript**: Strict mode enabled, ES2020 target, ESNext modules 134 - - **Imports**: ES6 imports, prefer named imports from `@atcute/oauth-browser-client` and `@atproto/api` 135 - - **Types**: Define interfaces in `lib/types/`, use TypeScript strict type checking 136 - - **Naming**: camelCase for functions/variables, PascalCase for types, SCREAMING_SNAKE_CASE for constants (e.g., `OAUTH_SESSION_KEY`) 137 - - **Error handling**: Use try/catch with console.error for logging, propagate errors upward 138 - - **Browser APIs**: Use `browser.*` namespace (not `chrome.*`) for cross-browser compatibility 139 - - **Environment**: Nix-based development, uses `pnpm` for package management 197 + ## Key Files 198 + 199 + | File | Purpose | 200 + |------|---------| 201 + | `wxt.config.ts` | Extension manifest and Vite config | 202 + | `packages/core/src/types.ts` | Core TypeScript interfaces | 203 + | `packages/core/src/pds/index.ts` | PDS client (create/delete annotations) | 204 + | `packages/core/src/oauth/index.ts` | AT Protocol OAuth flow | 205 + | `packages/core/src/storage/adapter.ts` | Storage interface definition | 206 + | `entrypoints/background.ts` | Extension background worker | 207 + | `entrypoints/content.ts` | Content script entry |
+120
assets/Seams.shortcut
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>WFWorkflowClientVersion</key> 6 + <string>2605.0.5</string> 7 + <key>WFWorkflowHasOutputFallback</key> 8 + <false/> 9 + <key>WFWorkflowHasShortcutInputVariables</key> 10 + <true/> 11 + <key>WFWorkflowIcon</key> 12 + <dict> 13 + <key>WFWorkflowIconGlyphNumber</key> 14 + <integer>59511</integer> 15 + <key>WFWorkflowIconStartColor</key> 16 + <integer>2846468607</integer> 17 + </dict> 18 + <key>WFWorkflowImportQuestions</key> 19 + <array/> 20 + <key>WFWorkflowInputContentItemClasses</key> 21 + <array> 22 + <string>WFURLContentItem</string> 23 + <string>WFArticleContentItem</string> 24 + <string>WFSafariWebPageContentItem</string> 25 + <string>WFStringContentItem</string> 26 + </array> 27 + <key>WFWorkflowMinimumClientVersion</key> 28 + <integer>900</integer> 29 + <key>WFWorkflowMinimumClientVersionString</key> 30 + <string>900</string> 31 + <key>WFWorkflowName</key> 32 + <string>Seams</string> 33 + <key>WFWorkflowNoInputBehavior</key> 34 + <dict> 35 + <key>Name</key> 36 + <string>WFWorkflowNoInputBehaviorAskForInput</string> 37 + <key>Parameters</key> 38 + <dict> 39 + <key>ItemClass</key> 40 + <string>WFURLContentItem</string> 41 + </dict> 42 + </dict> 43 + <key>WFWorkflowOutputContentItemClasses</key> 44 + <array/> 45 + <key>WFWorkflowTypes</key> 46 + <array> 47 + <string>ActionExtension</string> 48 + </array> 49 + <key>WFWorkflowActions</key> 50 + <array> 51 + <dict> 52 + <key>WFWorkflowActionIdentifier</key> 53 + <string>is.workflow.actions.detect.link</string> 54 + <key>WFWorkflowActionParameters</key> 55 + <dict> 56 + <key>WFInput</key> 57 + <dict> 58 + <key>Value</key> 59 + <dict> 60 + <key>Type</key> 61 + <string>ExtensionInput</string> 62 + </dict> 63 + <key>WFSerializationType</key> 64 + <string>WFTextTokenAttachment</string> 65 + </dict> 66 + </dict> 67 + </dict> 68 + <dict> 69 + <key>WFWorkflowActionIdentifier</key> 70 + <string>is.workflow.actions.gettext</string> 71 + <key>WFWorkflowActionParameters</key> 72 + <dict> 73 + <key>WFTextActionText</key> 74 + <dict> 75 + <key>Value</key> 76 + <dict> 77 + <key>attachmentsByRange</key> 78 + <dict> 79 + <key>{27, 1}</key> 80 + <dict> 81 + <key>Type</key> 82 + <string>ActionOutput</string> 83 + <key>OutputName</key> 84 + <string>URLs</string> 85 + <key>OutputUUID</key> 86 + <string>E8A3B2C1-1234-5678-9ABC-DEF012345678</string> 87 + </dict> 88 + </dict> 89 + <key>string</key> 90 + <string>https://sure.seams.so/proxy/</string> 91 + </dict> 92 + <key>WFSerializationType</key> 93 + <string>WFTextTokenString</string> 94 + </dict> 95 + </dict> 96 + </dict> 97 + <dict> 98 + <key>WFWorkflowActionIdentifier</key> 99 + <string>is.workflow.actions.openurl</string> 100 + <key>WFWorkflowActionParameters</key> 101 + <dict> 102 + <key>WFInput</key> 103 + <dict> 104 + <key>Value</key> 105 + <dict> 106 + <key>Type</key> 107 + <string>ActionOutput</string> 108 + <key>OutputName</key> 109 + <string>Text</string> 110 + <key>OutputUUID</key> 111 + <string>F9B4C3D2-2345-6789-ABCD-EF0123456789</string> 112 + </dict> 113 + <key>WFSerializationType</key> 114 + <string>WFTextTokenAttachment</string> 115 + </dict> 116 + </dict> 117 + </dict> 118 + </array> 119 + </dict> 120 + </plist>
db/annotations.db

This is a binary file and will not be displayed.

db/annotations.db-shm

This is a binary file and will not be displayed.

db/annotations.db-wal

This is a binary file and will not be displayed.

+10 -3
entrypoints/sidepanel/style.css
··· 44 44 button { 45 45 background: var(--forest-green); 46 46 color: white; 47 - border: 1px dashed var(--forest-green-dark); 47 + border: none; 48 48 padding: 8px 16px; 49 49 border-radius: 2px; 50 50 cursor: pointer; ··· 231 231 232 232 .profile-menu { 233 233 position: fixed; 234 - bottom: 8px; 235 - right: 6px; 234 + bottom: 16px; 235 + left: 50%; 236 + transform: translateX(-50%); 236 237 z-index: 100; 238 + } 239 + 240 + .profile-menu #login-trigger-btn { 241 + padding: 10px 24px; 242 + font-size: 14px; 243 + white-space: nowrap; 237 244 } 238 245 239 246 .profile-dropdown {
+1 -1
packages/core/src/sidebar/index.ts
··· 184 184 <button id="logout-btn">Logout</button> 185 185 </div> 186 186 ` : ` 187 - <button id="login-trigger-btn" style="border-radius: 20px;">Login</button> 187 + <button id="login-trigger-btn">Login with ATProto</button> 188 188 `} 189 189 </div> 190 190 </div>
+32
public/extension-callback.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>OAuth Callback</title> 6 + </head> 7 + <body> 8 + <p>Redirecting...</p> 9 + <script> 10 + // Relay to Chromium extension callback 11 + const extensionId = 'kjdnjfgcikmlbloojphbkmknfpmfofio'; 12 + const extRedirect = `https://${extensionId}.chromiumapp.org/extension-callback.html`; 13 + 14 + // Check if we are already in the extension context to avoid infinite loops 15 + const isExtension = window.location.protocol === 'chrome-extension:' || 16 + window.location.hostname.endsWith('.chromiumapp.org') || 17 + window.location.protocol === 'moz-extension:'; 18 + 19 + if (!isExtension) { 20 + // We are on the web server (relay), so redirect to the extension 21 + console.log('Relaying to extension:', extRedirect); 22 + window.location.href = extRedirect + window.location.search + window.location.hash; 23 + } else { 24 + // We are in the extension 25 + console.log('Authentication successful!'); 26 + // The browser should handle closing this window via launchWebAuthFlow, 27 + // but we can show a message just in case. 28 + document.querySelector('p').textContent = 'Authenticated. This window will close automatically.'; 29 + } 30 + </script> 31 + </body> 32 + </html>
+6
wxt.config.ts
··· 23 23 name: "Seams", 24 24 url: "https://seams.so", 25 25 }, 26 + sidebar_action: { 27 + default_icon: { 28 + "16": "icon-16.png", 29 + "32": "icon-32.png", 30 + }, 31 + }, 26 32 }), 27 33 permissions: [ 28 34 'storage',