Contributing to ATlast#
Thank you for your interest in contributing! This guide will help you get started with local development.
Two Development Modes#
We support two development modes:
🎨 Mock Mode (No backend required)
Best for: Frontend development, UI/UX work, design changes
🔧 Full Mode (Complete backend)
Best for: Backend development, API work, OAuth testing, database changes
Requirements:
- PostgreSQL database (local or Neon)
- OAuth keys
- Environment configuration
Mock Mode Starting Guide#
Perfect for frontend contributors who want to jump in quickly!
- Clone and Install
git clone <repo-url>
cd atlast
pnpm install
- Create .env.local
# .env.mock
VITE_LOCAL_MOCK=true
VITE_ENABLE_OAUTH=false
VITE_ENABLE_DATABASE=false
- Start Development
pnpm run dev:mock
-
Open Your Browser
Go tohttp://localhost:5173 -
"Login" with Mock User Enter any handle - it will create a mock session.
-
Upload Test Data
Upload your TikTok or Instagram data file. The mock API will generate fake matches for testing the UI.
Full Mode Starting Guide#
For contributors working on backend features, OAuth, or database operations.
Prerequisites#
- Node.js 18+
- pnpm (install with
npm install -g pnpm) - PostgreSQL (or Neon account)
- OpenSSL (for key generation)
- Clone and Install
git clone <repo-url>
cd atlast
pnpm install
-
Database Setup
Option A: Neon (Recommended)
- Create account at https://neon.tech
- Create project "atlast-dev"
- Copy connection string
Option B: Local PostgreSQL
# macOS brew install postgresql@15 brew services start postgresql@15 createdb atlast_dev # Ubuntu sudo apt install postgresql sudo systemctl start postgresql sudo -u postgres createdb atlast_dev -
Generate OAuth Keys
# Generate private key
openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem
# Extract public key
openssl ec -in private-key.pem -pubout -out public-key.pem
# View private key (copy for .env)
cat private-key.pem
- Extract Public Key JWK
node -e "
const fs = require('fs');
const jose = require('jose');
const pem = fs.readFileSync('public-key.pem', 'utf8');
jose.importSPKI(pem, 'ES256').then(key => {
return jose.exportJWK(key);
}).then(jwk => {
console.log(JSON.stringify(jwk, null, 2));
});
"
-
Update netlify/functions/jwks.ts
Replace
PUBLIC_JWKwith the output from step 4. -
Create .env
VITE_LOCAL_MOCK=false
VITE_API_BASE=/.netlify/functions
# Database (choose one)
NETLIFY_DATABASE_URL=postgresql://user:pass@host/db # Neon
# NETLIFY_DATABASE_URL=postgresql://localhost/atlast_dev # Local
# OAuth (paste your private key)
OAUTH_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYOUR_KEY_HERE\n-----END PRIVATE KEY-----"
# Local URLs (MUST use 127.0.0.1 for OAuth)
URL=http://127.0.0.1:8888
DEPLOY_URL=http://127.0.0.1:8888
DEPLOY_PRIME_URL=http://127.0.0.1:8888
CONTEXT=dev
- Initialize Database
pnpm run init-db
- Start Development Server
npx netlify-cli dev --filter @atlast/web
# Or use the alias:
pnpm run dev
-
Test OAuth
- Open
http://127.0.0.1:8888(NOT localhost) - Enter your real Bluesky handle
- Authorize the app
- You should be redirected back and logged in
- Open
Project Structure#
Monorepo using pnpm workspaces:
atlast/
├── packages/
│ ├── web/ # Frontend React app
│ │ ├── src/
│ │ │ ├── assets/ # Logo
│ │ │ ├── components/ # UI components (React)
│ │ │ ├── pages/ # Page components
│ │ │ ├── hooks/ # Custom hooks
│ │ │ ├── lib/
│ │ │ │ ├── api/ # API client (real + mock)
│ │ │ │ ├── parsers/ # File parsing logic
│ │ │ │ └── config.ts # Environment config
│ │ │ └── types/ # TypeScript types
│ │ └── package.json
│ ├── functions/ # Netlify serverless functions
│ │ ├── src/
│ │ │ ├── core/ # Middleware, types, config
│ │ │ ├── infrastructure/ # Database, OAuth, cache
│ │ │ ├── services/ # Business logic
│ │ │ ├── repositories/ # Data access layer
│ │ │ └── utils/ # Shared utilities
│ │ └── package.json
│ ├── extension/ # Browser extension
│ │ ├── src/
│ │ │ ├── content/ # Content scripts, scrapers
│ │ │ ├── popup/ # Extension popup UI
│ │ │ ├── background/ # Service worker
│ │ │ └── lib/ # Extension utilities
│ │ └── package.json
│ └── shared/ # Shared types (future)
├── pnpm-workspace.yaml
└── netlify.toml
UI Color System#
| Element | Light Mode | Dark Mode | Notes |
|---|---|---|---|
| Text Primary | purple-950 | cyan-50 | Headings, labels |
| Text Secondary | purple-750 | cyan-250 | Body text, descriptions |
| Text Tertiary | purple-600 | cyan-400 | Metadata, hints, icons |
| Borders (Rest) | cyan-500/30 | purple-500/30 | Cards, inputs default |
| Borders (Hover) | cyan-400 | purple-400 | Interactive hover |
| Borders (Active/Selected) | cyan-500 | purple-500 | Active tabs, selected items |
| Backgrounds (Primary) | white | slate-900 | Modal/card base |
| Backgrounds (Secondary) | purple-50 | slate-900 (nested sections) | Nested cards, sections |
| Backgrounds (Selected) | cyan-50 | purple-950/30 | Selected platform cards |
| Buttons Primary | orange-600 | orange-600 | CTAs |
| Buttons Primary Hover | orange-500 | orange-500 | CTA hover |
| Buttons Secondary | slate-600 | slate-700 | Cancel, secondary actions |
| Buttons Secondary Hover | slate-700 | slate-600 | Secondary hover |
| Interactive Selected | bg-cyan-50 border-cyan-500 | bg-purple-950/30 border-purple-500 | Platform selection cards |
| Accent/Badge | orange-500 | orange-500 (or amber-500) | Match counts, checkmarks, progress |
| Progress Complete | orange-500 | orange-500 | Completed progress bars |
| Progress Incomplete | cyan-500/30 | purple-500/30 | Incomplete progress bars |
| Success/Green | green-100/800 | green-900/300 | Followed status |
| Error/Red | red-600 | red-400 | Logout, errors |
UI Color System: Patterns#
Disabled States:
- Light: Reduce opacity to 50%, use purple-500/50
- Dark: Reduce opacity to 50%, use cyan-500/50
Success/Match indicators: Both modes: amber-* or orange-* backgrounds with accessible text contrast
Tab Navigation:
- Inactive: Use text secondary colors
- Active border: orange-500 (light), amber-500 (dark)
- Active text: orange-650 (light), amber-400 (dark)
Gradient Banners:
- Both modes: from-amber-* via-orange-* to-pink-* (keep dramatic, adjust shades for mode)
Task Workflows#
Adding a New Social Platform Parser#
- Add parsing rules to
packages/web/src/lib/parsers/platformDefinitions.ts - Follow existing patterns (TikTok, Instagram)
- Test with real data export file
- Update platform selection UI if needed
Adding a New API Endpoint#
- Create
packages/functions/src/your-endpoint.ts - Add authentication check using
withAuthErrorHandling()middleware - Update
packages/web/src/lib/api/adapters/RealApiAdapter.ts - Update
packages/web/src/lib/api/adapters/MockApiAdapter.ts - Use in components via
apiClient.yourMethod()
Working with the Extension#
cd packages/extension
pnpm install
pnpm run build # Build for Chrome
pnpm run build:prod # Build for production
# Load in Chrome:
# 1. Go to chrome://extensions
# 2. Enable Developer mode
# 3. Click "Load unpacked"
# 4. Select packages/extension/dist/chrome/
Styling Changes#
- Use Tailwind utility classes
- Follow dark mode pattern:
class="bg-white dark:bg-gray-800" - Test in both light and dark modes
- Mobile-first responsive design
- Check accessibility (if implemented) is retained
Submitting Changes#
Before Submitting#
- Test in mock mode:
pnpm run dev:mock - Test in full mode (if backend changes):
npx netlify-cli dev --filter @atlast/web - Check both light and dark themes
- Test mobile responsiveness
- No console errors
- Code follows existing patterns
- Run
pnpm run buildsuccessfully
Pull Request Process#
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes
- Commit with clear messages
- Push to your fork
- Open a Pull Request
PR Description Should Include#
- What changes were made
- Why these changes are needed
- Screenshots (for UI changes)
- Testing steps
- Related issues
Resources#
Thank you for contributing to ATlast!