commits
Removed temporary storage approach and implemented proper authentication flow:
Extension changes:
- Added session check to popup init flow (checkSession in api-client)
- Added "not logged in" state with login prompts
- Updated uploadToATlast to include credentials for cookie-based auth
- Extension now requires user to be logged in BEFORE scanning
Backend changes:
- Converted extension-import to AuthenticatedHandler (requires auth)
- Now creates upload records immediately (no temporary storage)
- Removed extension_imports table from database schema
- Deleted get-extension-import function (no longer needed)
- Deleted import-store utility (temporary approach removed)
Frontend changes:
- Removed ExtensionImport page and /import/:id route
- Extension uploads now use same flow as file uploads
This matches the correct user flow: user logs in to ATlast first, then
extension creates permanent upload records directly (same as file uploads).
Built extension successfully for dev environment.
Fix CORS blocking extension health checks and API calls:
- Add http://127.0.0.1:8888/* (dev)
- Add http://localhost:8888/* (alt dev)
- Add https://atlast.byarielm.fyi/* (prod)
Extension can now make requests to ATlast servers without
CORS errors.
Features:
- Check server health on popup init (dev mode only)
- Show 'server offline' state with setup instructions
- 'Check Again' button to retry connection
- Display target server URL for debugging
- 3-second timeout for health checks
Fixes port 8888 conflict workflow - extension now prompts user
to start dev server instead of hanging silently.
Critical bug fix: extension-import and get-extension-import were using
separate in-memory Maps, causing 404 errors when fetching import data.
- Create shared utils/import-store.ts module
- Both functions now use same Map instance
- Add logging for debugging
- Note: In-memory storage only works for dev (single process)
Production needs database/Redis/Netlify Blobs
- Install react-router-dom
- Create Router component with / and /import/:id routes
- Update main.tsx to use Router
- Enables URL-based navigation for extension imports
- Add dev vs prod build modes using --prod flag
- Inject API URL at build time via esbuild define
- Dev: http://127.0.0.1:8888
- Prod: https://atlast.byarielm.fyi
- Add build:prod and package:prod scripts
Check if usernames array has items before attempting upload.
Shows clear error message instead of hanging.
Changed from [data-testid="UserName"] (doesn't exist) to
[data-testid="UserCell"] (actual DOM element). Extract username
from profile link href instead of span text.
Uses @media (prefers-color-scheme: dark) to match browser preference.
Dark theme: slate-900/indigo-950/blue-900 backgrounds, cyan-50 text,
cyan borders on buttons, matches web app dark mode colors.
The onMessage wrapper in messaging.ts was only sending {success: true}
instead of the actual handler return value. This caused the popup to
receive undefined state even though the background worker was correctly
storing it.
Changes:
- messaging.ts: Changed onMessage to forward handler return values
- background service-worker.ts: Added comprehensive logging
- popup.ts: Added state change listener and detailed logging
This fixes the issue where popup showed 'Go to...' even when on the
following page.
Created comprehensive README.md with:
- Build instructions
- Chrome loading steps
- Step-by-step testing guide
- Console logging documentation
- Common issues and solutions
- Architecture overview
- Future enhancements roadmap
Includes debugging tips for URL pattern detection issues.
Changed followingPathPattern from strict /^\/[^/]+\/following$/
to flexible /^\/?([^/]+\/)?following\/?$/ to handle:
- /username/following
- /following (if Twitter uses this)
- Optional trailing slashes
This fixes detection issue where extension wouldn't recognize
the following page and show 'ready' state.
Changed production API URL from atlast.app to atlast.byarielm.fyi in:
- api-client.ts: ATLAST_API_URL constant
- popup.html: footer link
Changed color scheme from purple/indigo to purple/cyan/orange:
- Header: gradient from yellow-400 → orange-500 → pink-600 (firefly banner)
- Background: purple-50 → white → cyan-50 gradient
- Primary button: orange-600 with hover state
- Secondary button: purple-800 border
- Progress bar: orange → pink gradient
- Links and accents: orange-600
Matches web app's tailwind.config.js color system.
Built ATlast Importer browser extension with:
- Twitter Following page scraper using stable selectors
- Extension popup UI with scan progress and status
- Background service worker for state management
- API client for uploading to ATlast
- Netlify functions: extension-import, get-extension-import
- Web app integration via importId URL parameter
- Build system with esbuild and TypeScript
Extension flow:
1. User visits x.com/{username}/following
2. Click extension icon -> Start Scan
3. Extension scrolls and collects usernames
4. POST to /extension-import endpoint
5. Redirect to ATlast with importId parameter
6. Web app fetches import data and starts Bluesky search
Extensible architecture ready for Threads/Instagram/TikTok.
Fixed Netlify CLI monorepo detection issue by using --filter flag:
- Updated root package.json scripts to use 'npx netlify-cli dev --filter @atlast/web'
- Updated netlify.toml [dev] section to use npm with --prefix for framework command
- Added monorepo development instructions to CLAUDE.md
- Documented Windows Git Bash compatibility issue with netlify command
Solution: Use 'npx netlify-cli dev --filter @atlast/web' to bypass monorepo
project selection prompt and specify which workspace package to run.
Dev server now runs successfully at http://localhost:8888 with all backend
functions loaded.
Restructured codebase into pnpm workspace with three packages:
- packages/web: React frontend (from src/)
- packages/functions: Netlify serverless functions (from netlify/functions/)
- packages/shared: Shared TypeScript types for Platform and Import APIs
Changes:
- Created pnpm-workspace.yaml for workspace configuration
- Moved all web app files to packages/web/
- Moved all Netlify functions to packages/functions/src/
- Created packages/shared with Platform enum and ExtensionImportRequest/Response types
- Updated netlify.toml to point to new paths
- Updated root package.json scripts to use pnpm workspace commands
- All dependencies split appropriately between packages
Phase 0 (Monorepo Migration) from PLAN.md completed successfully.
Builds and dev server tested and working.
These directories are already in .gitignore but were committed
before the ignore rules were added. Removed from tracking while
keeping local files intact.
Added guidance to run deciduous sync before commits and stage graph
updates together with code changes. Decision graph is part of code
history and should not be committed separately.
- Removed tooltip from HeroSection (ATmosphere now plain text)
- Added superscript info icon next to 'ATmosphere' in login form text
- Tooltip content left-aligned for better readability
- Maintains platform-agnostic design
actor-typeahead component doesn't expose avatar data via events or attributes.
Added debounced API fetch (300ms) to searchActorsTypeahead endpoint when
handle is entered. Avatar now displays for both typeahead selections and
manually entered handles.
Created useRotatingPlaceholder hook:
- Rotates through 4 platform examples every 3 seconds
- .bsky.social, .blacksky.app, .tgnl.sh, .com
- Demonstrates platform flexibility without overwhelming users
Created HandleInput component:
- Shows @ symbol by default
- Replaces @ with profile pic when handle selected from typeahead
- Extracts avatar from typeahead data-avatar or actor-select event
- Clears avatar when input is cleared
Reduced from 389 to 236 lines (39% reduction):
- Use HeroSection for logo/title/fireflies
- Use ValuePropsSection for 3 value prop cards
- Use HowItWorksSection for 4 step cards
Main component now focuses on form logic and state.
Created reusable components:
- ValuePropCard: icon + title + description card
- StepCard: numbered step with color variant
- HeroSection: logo, title, firefly animation
- ValuePropsSection: 3 value prop cards
- HowItWorksSection: 4 step cards
- dynamically measure height so it uses fixed pb-4 gap correctly
Remove redundant success/info toasts (logout, upload loaded, no results).
Keep only error toasts for critical feedback.
Add AriaLiveAnnouncer component for screen reader accessibility.
Typeahead fix:
- Add event listeners for input/change/blur to sync actor-typeahead selections with form state
- Ensures Enter/Tab selections from typeahead dropdown properly update form
- Allows Enter key to submit form after selection
@ stripping:
- Automatically remove leading @ from handle input
- Show helpful inline message when @ is stripped
- Inform users the @ symbol isn't needed
Add critical note that each commit should address ONE specific fix or feature.
Multiple unrelated changes should be committed separately for clearer history.
Removed backward compatibility for deprecated 'followed' field:
- Removed from AtprotoMatch type
- Updated 6 files to use only followStatus Record
- Replaced simple boolean check with multi-lexicon support
- Database schema preserved (no migration needed)
Removed backward-compatible useSettings hook:
- Updated App.tsx to use zustand store directly
- Smaller bundle size (284.11 KB vs 284.27 KB)
Updated CLAUDE.md with git commit message format guidelines.
Removed empty 'nul' file created accidentally.
Optimization #12:
- created rateLimit.middleware with in-memory rate limiting
- batch-search-actors: 5 requests/min (conservative)
- batch-follow-users: 8 requests/hr (conservative)
- calculated based on AT Protocol limits with 50% buffer
- DRY implementation with applyRateLimit helper function
- prevents users from exhausting AT Protocol API limits
- leaves buffer for likes, replies, posts
Note: In-memory (resets on cold starts). Upgrade to Upstash Redis for production-grade shared state.
Optimization #11:
- added float-1, float-2, float-3 animation variants to tailwind.config.js
- replaced inline animation and animationDelay styles with Tailwind classes
- use modulo pattern to cycle through 3 animation variants
- maintains visual variety without Math.random() for animation timing
- consistent with Tailwind conventions, easier to maintain
Optimization #10:
- created useSettingsStore with zustand persist middleware
- removed SettingsContext.tsx (88 lines) and provider wrapper
- added SSR-safe storage with cross-tab synchronization
- automatic JSON serialization, no manual parse/stringify
- maintained backward-compatible API (useSettings hook)
- bundle size: +2.3KB for zustand library
Optimization #9:
- created atproto.types.ts with proper AT Protocol interfaces
- replaced 7 any types in batch-search-actors.ts
- added ATProtoActor, ATProtoProfile, RankedActor, EnrichedActor types
- improves type safety, compile-time error catching, IDE support
Optimization #8:
- removed 3 duplicate type definitions (atprotoSession, SourceUser, SearchResult)
- import AtprotoSession and SearchResult from central types
- prevents type drift, establishes single source of truth
Optimizations #6 & #7:
- #6: verified early exit optimization already implemented in FollowService
- #7: created validation.utils.ts with Zod schemas for array validation
- replaced 3 duplicate validation blocks with reusable Zod schemas
- updated batch-follow-users, check-follow-status, batch-search-actors
Removed temporary storage approach and implemented proper authentication flow:
Extension changes:
- Added session check to popup init flow (checkSession in api-client)
- Added "not logged in" state with login prompts
- Updated uploadToATlast to include credentials for cookie-based auth
- Extension now requires user to be logged in BEFORE scanning
Backend changes:
- Converted extension-import to AuthenticatedHandler (requires auth)
- Now creates upload records immediately (no temporary storage)
- Removed extension_imports table from database schema
- Deleted get-extension-import function (no longer needed)
- Deleted import-store utility (temporary approach removed)
Frontend changes:
- Removed ExtensionImport page and /import/:id route
- Extension uploads now use same flow as file uploads
This matches the correct user flow: user logs in to ATlast first, then
extension creates permanent upload records directly (same as file uploads).
Built extension successfully for dev environment.
Features:
- Check server health on popup init (dev mode only)
- Show 'server offline' state with setup instructions
- 'Check Again' button to retry connection
- Display target server URL for debugging
- 3-second timeout for health checks
Fixes port 8888 conflict workflow - extension now prompts user
to start dev server instead of hanging silently.
Critical bug fix: extension-import and get-extension-import were using
separate in-memory Maps, causing 404 errors when fetching import data.
- Create shared utils/import-store.ts module
- Both functions now use same Map instance
- Add logging for debugging
- Note: In-memory storage only works for dev (single process)
Production needs database/Redis/Netlify Blobs
The onMessage wrapper in messaging.ts was only sending {success: true}
instead of the actual handler return value. This caused the popup to
receive undefined state even though the background worker was correctly
storing it.
Changes:
- messaging.ts: Changed onMessage to forward handler return values
- background service-worker.ts: Added comprehensive logging
- popup.ts: Added state change listener and detailed logging
This fixes the issue where popup showed 'Go to...' even when on the
following page.
Changed followingPathPattern from strict /^\/[^/]+\/following$/
to flexible /^\/?([^/]+\/)?following\/?$/ to handle:
- /username/following
- /following (if Twitter uses this)
- Optional trailing slashes
This fixes detection issue where extension wouldn't recognize
the following page and show 'ready' state.
Changed color scheme from purple/indigo to purple/cyan/orange:
- Header: gradient from yellow-400 → orange-500 → pink-600 (firefly banner)
- Background: purple-50 → white → cyan-50 gradient
- Primary button: orange-600 with hover state
- Secondary button: purple-800 border
- Progress bar: orange → pink gradient
- Links and accents: orange-600
Matches web app's tailwind.config.js color system.
Built ATlast Importer browser extension with:
- Twitter Following page scraper using stable selectors
- Extension popup UI with scan progress and status
- Background service worker for state management
- API client for uploading to ATlast
- Netlify functions: extension-import, get-extension-import
- Web app integration via importId URL parameter
- Build system with esbuild and TypeScript
Extension flow:
1. User visits x.com/{username}/following
2. Click extension icon -> Start Scan
3. Extension scrolls and collects usernames
4. POST to /extension-import endpoint
5. Redirect to ATlast with importId parameter
6. Web app fetches import data and starts Bluesky search
Extensible architecture ready for Threads/Instagram/TikTok.
Fixed Netlify CLI monorepo detection issue by using --filter flag:
- Updated root package.json scripts to use 'npx netlify-cli dev --filter @atlast/web'
- Updated netlify.toml [dev] section to use npm with --prefix for framework command
- Added monorepo development instructions to CLAUDE.md
- Documented Windows Git Bash compatibility issue with netlify command
Solution: Use 'npx netlify-cli dev --filter @atlast/web' to bypass monorepo
project selection prompt and specify which workspace package to run.
Dev server now runs successfully at http://localhost:8888 with all backend
functions loaded.
Restructured codebase into pnpm workspace with three packages:
- packages/web: React frontend (from src/)
- packages/functions: Netlify serverless functions (from netlify/functions/)
- packages/shared: Shared TypeScript types for Platform and Import APIs
Changes:
- Created pnpm-workspace.yaml for workspace configuration
- Moved all web app files to packages/web/
- Moved all Netlify functions to packages/functions/src/
- Created packages/shared with Platform enum and ExtensionImportRequest/Response types
- Updated netlify.toml to point to new paths
- Updated root package.json scripts to use pnpm workspace commands
- All dependencies split appropriately between packages
Phase 0 (Monorepo Migration) from PLAN.md completed successfully.
Builds and dev server tested and working.
Typeahead fix:
- Add event listeners for input/change/blur to sync actor-typeahead selections with form state
- Ensures Enter/Tab selections from typeahead dropdown properly update form
- Allows Enter key to submit form after selection
@ stripping:
- Automatically remove leading @ from handle input
- Show helpful inline message when @ is stripped
- Inform users the @ symbol isn't needed
Removed backward compatibility for deprecated 'followed' field:
- Removed from AtprotoMatch type
- Updated 6 files to use only followStatus Record
- Replaced simple boolean check with multi-lexicon support
- Database schema preserved (no migration needed)
Removed backward-compatible useSettings hook:
- Updated App.tsx to use zustand store directly
- Smaller bundle size (284.11 KB vs 284.27 KB)
Updated CLAUDE.md with git commit message format guidelines.
Removed empty 'nul' file created accidentally.
Optimization #12:
- created rateLimit.middleware with in-memory rate limiting
- batch-search-actors: 5 requests/min (conservative)
- batch-follow-users: 8 requests/hr (conservative)
- calculated based on AT Protocol limits with 50% buffer
- DRY implementation with applyRateLimit helper function
- prevents users from exhausting AT Protocol API limits
- leaves buffer for likes, replies, posts
Note: In-memory (resets on cold starts). Upgrade to Upstash Redis for production-grade shared state.
Optimization #11:
- added float-1, float-2, float-3 animation variants to tailwind.config.js
- replaced inline animation and animationDelay styles with Tailwind classes
- use modulo pattern to cycle through 3 animation variants
- maintains visual variety without Math.random() for animation timing
- consistent with Tailwind conventions, easier to maintain
Optimization #10:
- created useSettingsStore with zustand persist middleware
- removed SettingsContext.tsx (88 lines) and provider wrapper
- added SSR-safe storage with cross-tab synchronization
- automatic JSON serialization, no manual parse/stringify
- maintained backward-compatible API (useSettings hook)
- bundle size: +2.3KB for zustand library
Optimizations #6 & #7:
- #6: verified early exit optimization already implemented in FollowService
- #7: created validation.utils.ts with Zod schemas for array validation
- replaced 3 duplicate validation blocks with reusable Zod schemas
- updated batch-follow-users, check-follow-status, batch-search-actors