commits
Use no-cors HEAD requests to check if app domains are actually
reachable. Only mark as invalid for DNS/connection failures
(ERR_NAME_NOT_RESOLVED, ERR_CONNECTION_REFUSED, timeout).
CORS blocks indicate the server exists, so don't mark invalid.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wisp appends query strings to redirect targets, breaking file lookup.
Rely on view/index.html directory indexing instead.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wisp's cleanUrls and _redirects have issues with query params.
Using a directory with index.html instead.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Prepare for wisp.place subdomain hosting where cleanUrls works
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
wisp.place DID-path hosting doesn't support _redirects or cleanUrls
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove Rust/Axum backend (Cargo.toml, src/*.rs, Dockerfile, fly.toml)
- Add Vite build system with MPA configuration
- Split monolithic HTML into modular ES modules:
- src/landing/: Landing page with atmosphere visualization
- src/view/: Main app with ATProto visualization, filters, MST viewer
- Add tangled CI for wisp.place deployment
- Fix avatar centering and app circle spacing
- Simplify domain validation (trust reversed ATProto namespaces)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
search handles as you type using the Bluesky search API, showing
avatars, display names, and handles in a dropdown. selecting a
result navigates directly to that profile.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- add hide=... URL param to share links with filters applied
- URL params take precedence over localStorage
- wrap filter + watch live buttons in container that stacks vertically on mobile (<768px)
- adjust filter panel and firehose toast positions for mobile
- filter button with panel to toggle app visibility
- "all" shows all apps, "valid" hides unresolvable NSIDs, "none" hides all
- defaults to "valid" mode on first load
- persists filter preferences in localStorage per user
- visible apps reposition evenly around the circle when filtering
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
previously, any delete event (like unliking a post) would immediately
remove the app circle from the UI. now we lazily check the PDS to see
if the namespace still has records before removing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
clippy compiles the code to analyze it, so it needs gcc for linking
and openssl.dev + pkg-config for dependencies. use shell glob pattern
instead of find command to set PKG_CONFIG_PATH.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
cargo fmt and clippy don't compile dependencies, so they don't need
openssl, pkg-config, or gcc. the PKG_CONFIG_PATH setup wasn't working
anyway (find command not available in nixery containers).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
clippy correctly identified that calling .last() on split('/') needlessly
iterates the entire iterator. since split() returns a DoubleEndedIterator,
we can use .next_back() to get the last element directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
nixery containers don't automatically set PKG_CONFIG_PATH like nix-shell does.
dynamically find the openssl.dev path in /nix/store and set PKG_CONFIG_PATH
to point to its pkgconfig directory before running cargo commands.
tested locally in nixos/nix docker container with the same dependencies.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
the openssl.dev output provides development headers and pkg-config files
needed by cargo to build openssl-sys crate. the base openssl package only
contains runtime libraries.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
users can now leave an optional message when signing the guestbook. messages are displayed in handwritten font alongside the signature metadata.
- backend: added text field to visit records and signature struct
- frontend: added message input modal with 280 char limit
- docs: updated lexicon documentation with new field and credits to @thisismissem.social and @essentialrandom.bsky.social
inspired by https://github.com/FujoWebDev/lexicon-guestbook
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add pre-commit configuration with cargo fmt and clippy checks
- Remove obsolete .tangled/workflows/deploy.yaml
- Fix all clippy warnings:
- Replace redundant closures with function references
- Use .first() instead of .get(0)
- Use .or_default() instead of .or_insert_with(Vec::new)
- Use .unsigned_abs() instead of .abs() as u32
- Use .div_ceil() instead of manual ceiling division
- Simplify iterator patterns with .flatten()
- Remove needless return statements
- Replace useless format!() with .to_string()
- Use array literals instead of vec![] where appropriate
- Remove redundant field names in struct initialization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace in-memory cache with UFOs API for persistent global state
- Split architecture: query page owner's PDS for button state, use UFOs for global list
- Fix unauthenticated user flow to trigger identity confirmation on button click
- Add signature count display to guestbook modal
- Fix font consistency across all guestbook text (unified monospace)
- Implement optimistic cache updates for sign/unsign actions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
cleaned up all debug logging from app.js while preserving essential error logging with console.error. removed verbose firehose event logs, guestbook state logs, and app circle management logs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update POV indicator: change "point of view:" to "point of view of" and make handle a clickable Bluesky profile link
- Fix guestbook button avatar display: always show page owner's avatar, not authenticated user's avatar
- Add dynamic guestbook sign text: shows "you already signed" when page owner has signed, "sign the guest list" otherwise
- Update authentication success toast to mention both sign and unsign actions
- Add CSS styling for clickable POV handle with hover effects
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- populate global signatures cache from existing visit records on page load
- make guestbook truly global - shows all visitors regardless of page
- check page owner signature status without authentication
- refine particle animations: slower speed, gentler easing, fade in/out
- make PDS pulse more subtle with proper centering transform
- fix pdsls.dev links to include at:// prefix and /app.at-me.visit path
- update justfile to watch static directory for template changes
- remove tangled.org logo from info modal
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add drop shadow to guestbook avatar button
- show authenticated user's avatar in sign button (not page owner's)
- add confirmation modal for unauthenticated users: "are you @handle?"
- implement global guestbook signatures cache (all users, not per-DID)
- invalidate cache on sign/delete operations
- return handle and avatar in /api/auth/status
- add "point of view: @handle" indicator on left side with neon styling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
adds full-screen modal to view all guestbook signatures with avatars and timestamps. fixes critical firehose bug where guestbook collection wasn't being watched if it didn't exist yet.
backend changes:
- always include app.at-me.visit in watched collections (routes.rs:1184-1187)
- add /api/guestbook/signatures endpoint with caching (routes.rs:981-1096)
- cache invalidation on sign/unsign for real-time updates
frontend changes:
- add view guestbook button (👥 icon) positioned left of sign button
- full-screen modal with loading, empty, and error states
- fetch and display signatures sorted by most recent first
- modal shows handle, avatar, and formatted timestamp for each signer
fixes:
- guestbook collection now tracked even before first signature
- real-time particle animations work on first sign/unsign
- dynamic circle appearance/removal works correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
firehose now registers ALL collections from user's PDS instead of hardcoded list:
- firehose endpoint fetches collections from PDS via describeRepo
- passes collections to get_or_create_broadcaster()
- registers ingesters for every collection the user has
- removes hardcoded BSKY_COLLECTIONS constant (dead code)
before: only listened to hardcoded bluesky collections
after: listens to all collections (bluesky, whitewind, tangled, guestbook, etc.)
this fixes the original bug where non-bluesky apps wouldn't show real-time updates. now any app writing to your PDS will be visualized when watch live is enabled.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
app circle lifecycle:
- automatically remove app circle when delete particle animation completes
- added removeAppCircle() function to clean up DOM and globalApps state
- repositions remaining circles smoothly after removal
fixed dynamically added circles:
- app circles created via firehose now properly fetch and display records
- clicking circle loads record count from PDS
- expanding records shows full data with copy functionality
- fixes "loading..." state that never resolved
improved toast notifications:
- hide "view record" link for delete events (no record to view)
- collection names now use inline code formatting for better readability
- code style: monospace background, subtle padding, reduced font size
- changed to innerHTML to support formatted content
this completes the guestbook circle lifecycle: create → visualize → interact → delete → remove
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
the firehose toast notification now behaves consistently on both desktop and mobile:
- uses max-width: min(300px, calc(100vw - 2rem)) to respect both size limit and viewport
- width: max-content keeps toast compact, only as wide as needed
- removed mobile-specific left/right/max-width overrides that caused full-width stretch
- toast remains right-aligned and compact on all screen sizes
before: mobile toast spanned entire screen width, looked oversized
after: mobile toast is same compact size as desktop, just positioned lower to avoid button overlap
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
users can now "sign" the guestbook by writing app.at-me.visit records to their own PDS via OAuth authentication. changes are visualized in real-time through firehose integration with particle animations.
authentication & authorization:
- OAuth flow using atrium-oauth with proper scopes (repo:app.at-me.visit)
- session management via actix-session to track auth state
- redirect flow that returns users to page after authentication
- agent caching by DID to avoid re-authentication
api endpoints:
- POST /api/sign-guestbook - creates visit record in user's PDS
- DELETE /api/sign-guestbook - removes visit record (unsign)
- GET /api/auth/status - checks authentication and existing records
- /login and /callback - OAuth authentication flow
real-time visualization:
- firehose integration watches for new visit records
- particle animation flows from app circle to PDS on record creation
- dynamic app circle creation when new apps write to your PDS
- toast notifications showing firehose events
- proper namespace handling (app.at-me → display as at-me.app)
critical bug fix:
- firehose was only listening for hardcoded Bluesky collections (app.bsky.*)
- added app.at-me.visit to registered collections in src/firehose.rs
- this was preventing any non-Bluesky app events from appearing
- remaining: should register ALL collections dynamically, not hardcoded list
utilities:
- scripts/delete_visits.py - delete all visit records from your PDS
- just clean-up-my-visits - quick command to run cleanup script
- uses uv for python dependencies with inline script metadata
frontend improvements:
- guestbook button with 3 states (not authenticated, ready, signed)
- watch prompt modal encouraging users to enable live mode
- extensive debug logging for troubleshooting (to be removed)
- dynamic app circle creation in real-time
technical details:
- extracted constants to src/constants.rs (collections, buffer sizes)
- updated dependencies: actix-session, atrium-oauth
- DID-filtered jetstream connections per user
- proper error handling for OAuth and API calls
known issues to address:
- app.js is 1800+ lines, needs refactoring into modules
- remove debug console.logs before production
- better loading states during OAuth flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Split templates.rs (1713 lines) into separate HTML files using include_str!
- Add src/templates/landing.html and src/templates/app.html for better syntax highlighting
- Update sample handles: replace pfrazee.com with bad-example.com (phil) for representation
- Add void.comind.network (Cameron's bot) to sample handles
- Remove duplicate loading progress text from app page
- Configure cargo-watch to monitor all src/ files including HTML templates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- reduce HTTP requests by only resolving DIDs for known merge namespaces (app.bsky, chat.bsky)
- improve loading animation with fullscreen spinner and progress updates
- fix app count in identity panel to match visually displayed circles
- update demo handle from jay.bsky.team to baileytownsend.dev (32 namespaces)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
performance improvements:
- implement server-side avatar caching with 1-hour TTL
- add concurrent avatar fetching on backend and frontend
- reduce avatar load time from N×300ms to ~300ms total
identity panel improvements:
- change handle label to "You" for clarity
- move bluesky profile link out of technical details
- add bluesky logo for visual recognition
- prevent JavaScript from overwriting static handle text
technical details:
- add once_cell dependency for in-memory cache
- use futures_util::future::join_all for concurrent resolution
- use Promise.all for parallel frontend avatar fetching
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The field element uses id="field" not class="field", so the CSS
selector needs to use # not . for proper specificity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add adaptive handle text sizing that scales down for long handles
- improve mobile radial layout with better circle sizing and radius calculations
- add text overflow handling with ellipsis for app labels
- add "You" label below identity circle
- make port configurable via PORT environment variable
- speed up resize updates (100ms -> 50ms debounce)
- add conditional label hiding for mobile with 20+ apps
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- replace "third-party" with "atproto" terminology throughout
- improve app circle sizing and layout
- add window resize handler for responsive positioning
- remove redundant identity hint text
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- center input box by removing min-width constraint
- enable scrolling on mobile when info drawer is expanded
- change container to relative positioning on mobile for proper document flow
- allow body scrolling on mobile while keeping desktop 3D atmosphere effect
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- center identity/PDS circle properly on mobile using fixed positioning
- increase spacing on 'tap for details' hint to prevent overlap with circle border
- improve landing page ownership messaging for clarity
- add link to bluesky in explainer copy
- remove arrow indicator from unreachable third-party app links
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add external link arrow (↗) to all app names for clarity
- reverse namespace in detail panel header and make it clickable
- respect URL validation in detail panel (gray out invalid links)
- add ?watching=true URL parameter support for auto-starting firehose
- enable bookmarking with live watch mode active
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove session-based OAuth authentication in favor of a simpler
query-parameter approach where users view any handle via /view?handle=...
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
atmosphere:
- restored rotating 3D atmosphere with app logos
- fetches top atproto apps from UFOs API
- displays app avatars with tooltips
- 24-hour caching for performance
navigation:
- added home button at top right corner
- positioned next to watch-live button
- provides navigation back to landing page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- added home button at top right corner
- positioned to the right of watch-live button
- provides navigation back to search/landing page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- only show skip button on non-final steps
- last step now only shows "got it" button
- added null check for skip button event listener
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
info modal fixes:
- changed "you're looking at" to "this visualization shows" (more accurate)
- linked bluesky, whitewind, and tangled.org apps
- changed frontpage to tangled.org for code hosting
- emphasized social graph ownership and portability
- linked "open social" to overreacted.io blog post
identity/pds panel fixes:
- linked pds location directly to actual pds url
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
rewrote all user-facing copy to be accessible to average social media users:
- login page: replaced technical jargon with email analogy and relatable problems
- info modal: explained "open social" in concrete terms with real app examples
- identity/pds panel: clarified what a pds is, where it's located, added pdsls.dev link
- added breadcrumb links to atproto.com docs for all technical terms
philosophy: lead with relatable problems, use familiar analogies, focus on benefits over technology
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add max-width constraint to login form for better centering
- Add null checks to info toggle and demo button event listeners
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add collapsible educational section explaining atproto, data ownership, and the silo problem
- implement demo mode that loads paul frazee's account for exploration without login
- add demo banner with exit functionality
- adjust UI elements (info, watch live, logout buttons) to avoid overlapping with demo banner
- clear localStorage when exiting demo to prevent auto-restore behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add display: flex and align-items: center to .logout for consistent vertical alignment with .watch-live-btn
- add explicit font-family: inherit to .watch-live-btn to ensure monospace font usage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add clickable "view record" links to firehose toast notifications that open raw record JSON from user's PDS. Also fix particle animation direction to flow from apps to PDS (correctly showing data writes).
Changes:
- Add /api/record endpoint to fetch individual records
- Refactor firehose to use DID-specific connections via manager
- Add toast link element with hover styling
- Fetch record details for richer toast messages
- Reverse particle flow direction (app → PDS)
- Add "Your PDS" label to identity circle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
implements the visual layer for real-time firehose events:
- watch live toggle button in top-right corner
- toast notifications showing event actions (create/update/delete)
- particle animation system using canvas overlay
- colored particles flow from identity to app circles
- particles: green for create, blue for update, red for delete
- app circles pulse when receiving data
- auto-reconnects on connection drop
- clean shutdown when toggled off
complete implementation:
- backend: rust jetstream connector + SSE endpoint (src/firehose.rs, src/routes.rs)
- frontend: particle system + event handling (static/app.js)
- ui: button, toast, css animations (src/templates.rs)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use no-cors HEAD requests to check if app domains are actually
reachable. Only mark as invalid for DNS/connection failures
(ERR_NAME_NOT_RESOLVED, ERR_CONNECTION_REFUSED, timeout).
CORS blocks indicate the server exists, so don't mark invalid.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove Rust/Axum backend (Cargo.toml, src/*.rs, Dockerfile, fly.toml)
- Add Vite build system with MPA configuration
- Split monolithic HTML into modular ES modules:
- src/landing/: Landing page with atmosphere visualization
- src/view/: Main app with ATProto visualization, filters, MST viewer
- Add tangled CI for wisp.place deployment
- Fix avatar centering and app circle spacing
- Simplify domain validation (trust reversed ATProto namespaces)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- filter button with panel to toggle app visibility
- "all" shows all apps, "valid" hides unresolvable NSIDs, "none" hides all
- defaults to "valid" mode on first load
- persists filter preferences in localStorage per user
- visible apps reposition evenly around the circle when filtering
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
cargo fmt and clippy don't compile dependencies, so they don't need
openssl, pkg-config, or gcc. the PKG_CONFIG_PATH setup wasn't working
anyway (find command not available in nixery containers).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
clippy correctly identified that calling .last() on split('/') needlessly
iterates the entire iterator. since split() returns a DoubleEndedIterator,
we can use .next_back() to get the last element directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
nixery containers don't automatically set PKG_CONFIG_PATH like nix-shell does.
dynamically find the openssl.dev path in /nix/store and set PKG_CONFIG_PATH
to point to its pkgconfig directory before running cargo commands.
tested locally in nixos/nix docker container with the same dependencies.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
users can now leave an optional message when signing the guestbook. messages are displayed in handwritten font alongside the signature metadata.
- backend: added text field to visit records and signature struct
- frontend: added message input modal with 280 char limit
- docs: updated lexicon documentation with new field and credits to @thisismissem.social and @essentialrandom.bsky.social
inspired by https://github.com/FujoWebDev/lexicon-guestbook
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add pre-commit configuration with cargo fmt and clippy checks
- Remove obsolete .tangled/workflows/deploy.yaml
- Fix all clippy warnings:
- Replace redundant closures with function references
- Use .first() instead of .get(0)
- Use .or_default() instead of .or_insert_with(Vec::new)
- Use .unsigned_abs() instead of .abs() as u32
- Use .div_ceil() instead of manual ceiling division
- Simplify iterator patterns with .flatten()
- Remove needless return statements
- Replace useless format!() with .to_string()
- Use array literals instead of vec![] where appropriate
- Remove redundant field names in struct initialization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace in-memory cache with UFOs API for persistent global state
- Split architecture: query page owner's PDS for button state, use UFOs for global list
- Fix unauthenticated user flow to trigger identity confirmation on button click
- Add signature count display to guestbook modal
- Fix font consistency across all guestbook text (unified monospace)
- Implement optimistic cache updates for sign/unsign actions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
cleaned up all debug logging from app.js while preserving essential error logging with console.error. removed verbose firehose event logs, guestbook state logs, and app circle management logs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update POV indicator: change "point of view:" to "point of view of" and make handle a clickable Bluesky profile link
- Fix guestbook button avatar display: always show page owner's avatar, not authenticated user's avatar
- Add dynamic guestbook sign text: shows "you already signed" when page owner has signed, "sign the guest list" otherwise
- Update authentication success toast to mention both sign and unsign actions
- Add CSS styling for clickable POV handle with hover effects
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- populate global signatures cache from existing visit records on page load
- make guestbook truly global - shows all visitors regardless of page
- check page owner signature status without authentication
- refine particle animations: slower speed, gentler easing, fade in/out
- make PDS pulse more subtle with proper centering transform
- fix pdsls.dev links to include at:// prefix and /app.at-me.visit path
- update justfile to watch static directory for template changes
- remove tangled.org logo from info modal
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add drop shadow to guestbook avatar button
- show authenticated user's avatar in sign button (not page owner's)
- add confirmation modal for unauthenticated users: "are you @handle?"
- implement global guestbook signatures cache (all users, not per-DID)
- invalidate cache on sign/delete operations
- return handle and avatar in /api/auth/status
- add "point of view: @handle" indicator on left side with neon styling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
adds full-screen modal to view all guestbook signatures with avatars and timestamps. fixes critical firehose bug where guestbook collection wasn't being watched if it didn't exist yet.
backend changes:
- always include app.at-me.visit in watched collections (routes.rs:1184-1187)
- add /api/guestbook/signatures endpoint with caching (routes.rs:981-1096)
- cache invalidation on sign/unsign for real-time updates
frontend changes:
- add view guestbook button (👥 icon) positioned left of sign button
- full-screen modal with loading, empty, and error states
- fetch and display signatures sorted by most recent first
- modal shows handle, avatar, and formatted timestamp for each signer
fixes:
- guestbook collection now tracked even before first signature
- real-time particle animations work on first sign/unsign
- dynamic circle appearance/removal works correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
firehose now registers ALL collections from user's PDS instead of hardcoded list:
- firehose endpoint fetches collections from PDS via describeRepo
- passes collections to get_or_create_broadcaster()
- registers ingesters for every collection the user has
- removes hardcoded BSKY_COLLECTIONS constant (dead code)
before: only listened to hardcoded bluesky collections
after: listens to all collections (bluesky, whitewind, tangled, guestbook, etc.)
this fixes the original bug where non-bluesky apps wouldn't show real-time updates. now any app writing to your PDS will be visualized when watch live is enabled.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
app circle lifecycle:
- automatically remove app circle when delete particle animation completes
- added removeAppCircle() function to clean up DOM and globalApps state
- repositions remaining circles smoothly after removal
fixed dynamically added circles:
- app circles created via firehose now properly fetch and display records
- clicking circle loads record count from PDS
- expanding records shows full data with copy functionality
- fixes "loading..." state that never resolved
improved toast notifications:
- hide "view record" link for delete events (no record to view)
- collection names now use inline code formatting for better readability
- code style: monospace background, subtle padding, reduced font size
- changed to innerHTML to support formatted content
this completes the guestbook circle lifecycle: create → visualize → interact → delete → remove
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
the firehose toast notification now behaves consistently on both desktop and mobile:
- uses max-width: min(300px, calc(100vw - 2rem)) to respect both size limit and viewport
- width: max-content keeps toast compact, only as wide as needed
- removed mobile-specific left/right/max-width overrides that caused full-width stretch
- toast remains right-aligned and compact on all screen sizes
before: mobile toast spanned entire screen width, looked oversized
after: mobile toast is same compact size as desktop, just positioned lower to avoid button overlap
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
users can now "sign" the guestbook by writing app.at-me.visit records to their own PDS via OAuth authentication. changes are visualized in real-time through firehose integration with particle animations.
authentication & authorization:
- OAuth flow using atrium-oauth with proper scopes (repo:app.at-me.visit)
- session management via actix-session to track auth state
- redirect flow that returns users to page after authentication
- agent caching by DID to avoid re-authentication
api endpoints:
- POST /api/sign-guestbook - creates visit record in user's PDS
- DELETE /api/sign-guestbook - removes visit record (unsign)
- GET /api/auth/status - checks authentication and existing records
- /login and /callback - OAuth authentication flow
real-time visualization:
- firehose integration watches for new visit records
- particle animation flows from app circle to PDS on record creation
- dynamic app circle creation when new apps write to your PDS
- toast notifications showing firehose events
- proper namespace handling (app.at-me → display as at-me.app)
critical bug fix:
- firehose was only listening for hardcoded Bluesky collections (app.bsky.*)
- added app.at-me.visit to registered collections in src/firehose.rs
- this was preventing any non-Bluesky app events from appearing
- remaining: should register ALL collections dynamically, not hardcoded list
utilities:
- scripts/delete_visits.py - delete all visit records from your PDS
- just clean-up-my-visits - quick command to run cleanup script
- uses uv for python dependencies with inline script metadata
frontend improvements:
- guestbook button with 3 states (not authenticated, ready, signed)
- watch prompt modal encouraging users to enable live mode
- extensive debug logging for troubleshooting (to be removed)
- dynamic app circle creation in real-time
technical details:
- extracted constants to src/constants.rs (collections, buffer sizes)
- updated dependencies: actix-session, atrium-oauth
- DID-filtered jetstream connections per user
- proper error handling for OAuth and API calls
known issues to address:
- app.js is 1800+ lines, needs refactoring into modules
- remove debug console.logs before production
- better loading states during OAuth flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Split templates.rs (1713 lines) into separate HTML files using include_str!
- Add src/templates/landing.html and src/templates/app.html for better syntax highlighting
- Update sample handles: replace pfrazee.com with bad-example.com (phil) for representation
- Add void.comind.network (Cameron's bot) to sample handles
- Remove duplicate loading progress text from app page
- Configure cargo-watch to monitor all src/ files including HTML templates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- reduce HTTP requests by only resolving DIDs for known merge namespaces (app.bsky, chat.bsky)
- improve loading animation with fullscreen spinner and progress updates
- fix app count in identity panel to match visually displayed circles
- update demo handle from jay.bsky.team to baileytownsend.dev (32 namespaces)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
performance improvements:
- implement server-side avatar caching with 1-hour TTL
- add concurrent avatar fetching on backend and frontend
- reduce avatar load time from N×300ms to ~300ms total
identity panel improvements:
- change handle label to "You" for clarity
- move bluesky profile link out of technical details
- add bluesky logo for visual recognition
- prevent JavaScript from overwriting static handle text
technical details:
- add once_cell dependency for in-memory cache
- use futures_util::future::join_all for concurrent resolution
- use Promise.all for parallel frontend avatar fetching
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add adaptive handle text sizing that scales down for long handles
- improve mobile radial layout with better circle sizing and radius calculations
- add text overflow handling with ellipsis for app labels
- add "You" label below identity circle
- make port configurable via PORT environment variable
- speed up resize updates (100ms -> 50ms debounce)
- add conditional label hiding for mobile with 20+ apps
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- center input box by removing min-width constraint
- enable scrolling on mobile when info drawer is expanded
- change container to relative positioning on mobile for proper document flow
- allow body scrolling on mobile while keeping desktop 3D atmosphere effect
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- center identity/PDS circle properly on mobile using fixed positioning
- increase spacing on 'tap for details' hint to prevent overlap with circle border
- improve landing page ownership messaging for clarity
- add link to bluesky in explainer copy
- remove arrow indicator from unreachable third-party app links
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add external link arrow (↗) to all app names for clarity
- reverse namespace in detail panel header and make it clickable
- respect URL validation in detail panel (gray out invalid links)
- add ?watching=true URL parameter support for auto-starting firehose
- enable bookmarking with live watch mode active
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
atmosphere:
- restored rotating 3D atmosphere with app logos
- fetches top atproto apps from UFOs API
- displays app avatars with tooltips
- 24-hour caching for performance
navigation:
- added home button at top right corner
- positioned next to watch-live button
- provides navigation back to landing page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
info modal fixes:
- changed "you're looking at" to "this visualization shows" (more accurate)
- linked bluesky, whitewind, and tangled.org apps
- changed frontpage to tangled.org for code hosting
- emphasized social graph ownership and portability
- linked "open social" to overreacted.io blog post
identity/pds panel fixes:
- linked pds location directly to actual pds url
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
rewrote all user-facing copy to be accessible to average social media users:
- login page: replaced technical jargon with email analogy and relatable problems
- info modal: explained "open social" in concrete terms with real app examples
- identity/pds panel: clarified what a pds is, where it's located, added pdsls.dev link
- added breadcrumb links to atproto.com docs for all technical terms
philosophy: lead with relatable problems, use familiar analogies, focus on benefits over technology
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add collapsible educational section explaining atproto, data ownership, and the silo problem
- implement demo mode that loads paul frazee's account for exploration without login
- add demo banner with exit functionality
- adjust UI elements (info, watch live, logout buttons) to avoid overlapping with demo banner
- clear localStorage when exiting demo to prevent auto-restore behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- add display: flex and align-items: center to .logout for consistent vertical alignment with .watch-live-btn
- add explicit font-family: inherit to .watch-live-btn to ensure monospace font usage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add clickable "view record" links to firehose toast notifications that open raw record JSON from user's PDS. Also fix particle animation direction to flow from apps to PDS (correctly showing data writes).
Changes:
- Add /api/record endpoint to fetch individual records
- Refactor firehose to use DID-specific connections via manager
- Add toast link element with hover styling
- Fetch record details for richer toast messages
- Reverse particle flow direction (app → PDS)
- Add "Your PDS" label to identity circle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
implements the visual layer for real-time firehose events:
- watch live toggle button in top-right corner
- toast notifications showing event actions (create/update/delete)
- particle animation system using canvas overlay
- colored particles flow from identity to app circles
- particles: green for create, blue for update, red for delete
- app circles pulse when receiving data
- auto-reconnects on connection drop
- clean shutdown when toggled off
complete implementation:
- backend: rust jetstream connector + SSE endpoint (src/firehose.rs, src/routes.rs)
- frontend: particle system + event handling (static/app.js)
- ui: button, toast, css animations (src/templates.rs)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>