commits
- remove static OG tags from app.html so SSR tags take precedence
- add /@handle route that resolves to /profile/{did}
- update internal links to use /@handle format
- mobile-first CSS: header overflow, touch-friendly buttons, responsive sizing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
replace quickslice backend + vanilla JS SPA (split across Fly.io and
Cloudflare Pages) with hatk — SvelteKit frontend + typed XRPC backend,
single Fly.io deployment.
- svelte 5 components with tanstack query for data fetching
- server-side feeds (recent, actor) with hydration
- AT Protocol native OAuth via hatk
- SSR link previews with OG tags (no edge function needed)
- bufo emoji og:image support for status permalinks
- local dev env via docker-compose (PLC + PDS + postgres)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
swap public.api.bsky.app → typeahead.waow.tech
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the custom (🐸) tab is active, the search bar uses the
find-bufo API for semantic search instead of basic name filtering.
Results show relevance scores. 300ms debounce on API calls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the delete button was hidden (opacity: 0) because it only had styles
for the history list context (.status-item:hover). add it to the
current-status-actions button styles so it's visible and consistent
with the share/embed buttons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add handle typeahead using Bluesky's searchActorsTypeahead API
- show avatar, display name, and handle in suggestions dropdown
- add FAQ accordions explaining internet handles (matching plyr.fm)
- fix input/button width mismatch by removing conflicting CSS rules
- update styling to match slides/plyr.fm patterns
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- share button on status items (hover to reveal, copies permalink)
- new /status/{did}/{rkey} route for individual statuses
- cloudflare pages function injects og tags for social bots
- uses bufo.zone images for custom emoji og:image
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- lexicon: increase emoji field limit from 64 to 128 for long bufo names
- copy: "share your status on the atproto network" → "what's happening?"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
fixes oauth token refresh issue by upgrading to v0.17.4+
simplifies deployment by pulling ghcr.io/bigmoves/quickslice:latest directly
🤖 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>
fetch statuses first and get handle from actorHandle field,
eliminating PLC lookup for users with existing statuses.
falls back to PLC lookup only for new users with no statuses.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
quickslice provides actorHandle directly in the query response,
eliminating the need for parallel resolveDidToHandle calls on
every feed item.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
instead of showing "clears now" for already-expired statuses, now
displays "expired", "expired Xm ago", "expired Xh ago", or "expired Xd ago"
depending on how long ago the status expired.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
quickslice rewrite replaces the custom rust backend.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
rewrite: quickslice backend + cloudflare pages frontend
- build quickslice from source at v0.17.3 (includes sub claim fix)
- frontend on cloudflare pages, backend on fly.io
- add readme with deployment docs
- clean up old deployment artifacts
🤖 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>
- removed invalid lexicon app.status.record (app is not a valid TLD)
- removed unused xyz.statusphere.status lexicon
- removed unused dev_utils.rs module
- project now only uses io.zzstoatzz.status.record (the correct NSID)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- adds `just dev` command that runs cargo watch
- uses port 3000 to avoid conflicts with other local services
- watches src and templates directories for changes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Change emoji seeding to sync new files on every deploy
- Only copy if destination doesn't exist (preserves manual uploads)
- Fix CI workflow by adding rustfmt component to toolchain
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix emoji loading inefficiency and 404 errors
- Use placeholder images in emoji picker initially
- Load actual images after a frame to prevent race conditions
- Properly handle URL-encoded filenames like se%C3%B1or-bufo.png
- Add emoji-resolver.js that loads filename mappings from API
- Replace hardcoded .png/.gif extensions with placeholder images
- Eliminate 404 errors by using correct extensions from the start
- Support any file extension (.png, .gif, .jpg, .webp, etc.)
- Maintain backward compatibility for existing data
The resolver loads once, caches the mappings, and updates all emoji
images with their correct filenames, preventing unnecessary 404s.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Revert "Revert "Revert "Fix OAuth same-site issue with auth domain workaround"""
Revert "Revert "Fix OAuth same-site issue with auth domain workaround""
The automatic redirect was causing an infinite loop when accessing
auth.zzstoatzz.com/login. Now both domains serve the login page
directly without redirecting.
The OAuth flow will still use auth.zzstoatzz.com for the actual
authentication because that's configured in OAUTH_REDIRECT_BASE.
Revert "Fix OAuth same-site issue with auth domain workaround"
Fix OAuth same-site issue with auth domain workaround
- Fix host header injection vulnerability by removing host header check
- Add uses_separate_auth_domain() helper method for clarity
- Validate URLs at startup to fail fast on misconfiguration
- Simplify redirect logic using the new helper method
Security fix: No longer trusts Host header from requests, instead uses
configured URLs to determine redirect behavior.
- Add APP_URL to preview deployment secrets
- This ensures OAuth callback redirects to preview URL, not localhost
- Add APP_URL config for main app domain
- Redirect login from status domain to auth domain
- After OAuth success, redirect back to status domain
- Use config values instead of hardcoding URLs
This works around the PDS rejecting same-site OAuth requests by using auth.zzstoatzz.com for OAuth flow while keeping status.zzstoatzz.io as the main app.
Fix OAuth login issues for self-hosted PDS
- Fix SqliteStateStore and SqliteSessionStore to return Ok(None) instead of error when no state/session exists
- Add missing OAuth scopes to localhost configuration (profile and follows access)
- Remove unused NoSessionFound error variant
This fixes the OAuth flow failing when using a self-hosted PDS by ensuring the state stores properly handle missing entries and the localhost config declares all required scopes.
Immersive emoji picker modal
- Add position:relative and z-index to emoji-picker to ensure it properly blocks clicks
- Remove debug logging
- Replace undefined loadCustomEmojis with ensureCustomEmojis
- Remove unnecessary stopPropagation that was breaking modal
- Prevent modal from closing when clicking Custom tab
- Add event listener cleanup to prevent memory leaks
- Add CSS fallback for inset property for browser compatibility
- Stop event propagation on emoji picker to prevent accidental closes
replace quickslice backend + vanilla JS SPA (split across Fly.io and
Cloudflare Pages) with hatk — SvelteKit frontend + typed XRPC backend,
single Fly.io deployment.
- svelte 5 components with tanstack query for data fetching
- server-side feeds (recent, actor) with hydration
- AT Protocol native OAuth via hatk
- SSR link previews with OG tags (no edge function needed)
- bufo emoji og:image support for status permalinks
- local dev env via docker-compose (PLC + PDS + postgres)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add handle typeahead using Bluesky's searchActorsTypeahead API
- show avatar, display name, and handle in suggestions dropdown
- add FAQ accordions explaining internet handles (matching plyr.fm)
- fix input/button width mismatch by removing conflicting CSS rules
- update styling to match slides/plyr.fm patterns
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- share button on status items (hover to reveal, copies permalink)
- new /status/{did}/{rkey} route for individual statuses
- cloudflare pages function injects og tags for social bots
- uses bufo.zone images for custom emoji og:image
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
instead of showing "clears now" for already-expired statuses, now
displays "expired", "expired Xm ago", "expired Xh ago", or "expired Xd ago"
depending on how long ago the status expired.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- build quickslice from source at v0.17.3 (includes sub claim fix)
- frontend on cloudflare pages, backend on fly.io
- add readme with deployment docs
- clean up old deployment artifacts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- removed invalid lexicon app.status.record (app is not a valid TLD)
- removed unused xyz.statusphere.status lexicon
- removed unused dev_utils.rs module
- project now only uses io.zzstoatzz.status.record (the correct NSID)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add emoji-resolver.js that loads filename mappings from API
- Replace hardcoded .png/.gif extensions with placeholder images
- Eliminate 404 errors by using correct extensions from the start
- Support any file extension (.png, .gif, .jpg, .webp, etc.)
- Maintain backward compatibility for existing data
The resolver loads once, caches the mappings, and updates all emoji
images with their correct filenames, preventing unnecessary 404s.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix host header injection vulnerability by removing host header check
- Add uses_separate_auth_domain() helper method for clarity
- Validate URLs at startup to fail fast on misconfiguration
- Simplify redirect logic using the new helper method
Security fix: No longer trusts Host header from requests, instead uses
configured URLs to determine redirect behavior.
- Add APP_URL config for main app domain
- Redirect login from status domain to auth domain
- After OAuth success, redirect back to status domain
- Use config values instead of hardcoding URLs
This works around the PDS rejecting same-site OAuth requests by using auth.zzstoatzz.com for OAuth flow while keeping status.zzstoatzz.io as the main app.
- Fix SqliteStateStore and SqliteSessionStore to return Ok(None) instead of error when no state/session exists
- Add missing OAuth scopes to localhost configuration (profile and follows access)
- Remove unused NoSessionFound error variant
This fixes the OAuth flow failing when using a self-hosted PDS by ensuring the state stores properly handle missing entries and the localhost config declares all required scopes.