17 Commits

Author SHA1 Message Date
927d106eae Add provider-based web search with Tavily support
- Add `SEARCH_PROVIDER` config with Tavily/Brave API key validation in server and prod script
- Introduce unified `web_search` tool and shared search service with Tavily + Brave backends
- Update chat UI tool status/result labels to treat both search tools consistently
2026-03-16 20:18:27 -06:00
16bc3315eb feat: show full date/time and unix timestamp on time hover
Hovering over the message time now shows a tooltip with the full
human-readable date/time and the unix timestamp below it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:21:24 -06:00
b963c96915 feat: show abbreviated message hash in chat bubble header
Displays first 7 chars of SHA-256 hash after sender name and time.
Full hash visible on hover via title attribute.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:17:20 -06:00
66bbc44f75 fix: double messages on re-login, nostr profile fetch, show npub in profile
- Fix duplicate messages after logout→re-login: ws.disconnect() now clears
  event listeners so initChat() doesn't stack duplicate handlers
- Nostr profile fetch: race multiple relays (damus, nostr.band, nos.lol)
  for better reliability
- Add nostr_pubkey field to UserPublic — returned from me/login/rooms APIs
- Profile page shows truncated npub instead of email for Nostr users
- Avatar service handles external URLs (Nostr profile pictures)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:13:23 -06:00
1a2f0e7951 feat: add Nostr NIP-07 browser extension login and invite by pubkey
- Server: nostr crate, migration 008 (nostr_pubkey column), challenge/verify
  endpoints for Schnorr-signed NIP-07 auth, invite-by-nostr endpoint
- Client: NIP-07 extension detection, relay profile fetch, Nostr login button
  on login/register pages, Nostr tab in invite modal, profile page handles
  no-email Nostr users
- Sentinel emails (nostr:<prefix>) hidden at API boundary via public_email()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:43:01 -06:00
9634c275b3 style: add glowing border to permalink-highlighted messages
The linked message now gets a purple box-shadow glow and background
highlight that lasts 4 seconds, making it much more obvious which
message the user was linked to.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:48:03 -06:00
55c17b2999 fix: support hash-only permalink format with server-side resolution
Add /api/messages/hash/:hash endpoint that resolves a message hash to
its room ID (with membership check). The client now handles both
#roomId/hash and #hash formats - the latter calls the API to find
which room the message belongs to, then loads it and scrolls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:44:44 -06:00
e630cca6c6 fix: message permalinks work on fresh page load and handle permissions
- Stash permalink hash in sessionStorage before login so it survives
  the auth flow and navigates after login completes
- Wait for DOM render (double rAF) before scrolling to target message
- Skip scrollToBottom when navigating via permalink
- Show error screen for 403 (no access) and 404 (room not found)
- Attach HTTP status code to API errors for proper error differentiation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:40:18 -06:00
7210acf032 fix: include room ID in message permalink for cross-room navigation
Links now use #roomId/messageHash format so the app can load the correct
room before scrolling to the target message. Handles hashchange events
and auto-navigates on page load.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:31:12 -06:00
2e1a0ac858 feat: add SHA-256 integrity hashes to messages with copy/link buttons
Add a hash column to messages table computed from SHA-256(created_at + content)
to ensure message integrity. Existing messages get backfilled during migration.
All messages now show copy and permalink buttons on hover, with hash-based
URL fragments that auto-scroll and highlight the target message.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:11:03 -06:00
Jason Tudisco
6cb423b342 feat: add database backup on startup and isolate dev environment
Add automatic SQLite database backup before migrations on every server
start. Backups are timestamped and stored in backups/, with WAL/SHM
files included. Old backups are pruned to keep only the 10 most recent.

Update dev.sh to use separate ports and database from production so both
can run simultaneously on the same machine:
  - Production: server :3001, DB chat.db
  - Development: server :3002, Vite :3003, DB chat-dev.db

Make Vite config dynamic via VITE_PORT and VITE_API_PORT env vars.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:27:32 -06:00
Jason Tudisco
07b4df5544 feat: add profile page with avatar upload and image paste in chat
Add user profile page with custom avatar upload (crop/resize to 256px),
avatar display throughout the app, and MD5-based Gravatar fallback.

Add image paste/attach support in chat with vision model detection via
OpenRouter API. Images can be pasted from clipboard or selected via file
picker, uploaded to the server, and sent alongside messages. The AI
receives images as base64 data URLs for multimodal models. Models with
vision support are indicated with a badge in the model picker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:24:38 -06:00
Jason Tudisco
1c7d4d0510 feat: add production deployment and macOS dev scripts
Add dev.sh for running server + client in parallel on macOS, and prod.sh
for building and deploying in production. The Rust server now serves
static client files with SPA fallback, eliminating the need for Vite in
production. Also adds missing BRAVE_API_KEY to .env.example.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 07:27:06 -06:00
39aaa96a99 feat: add Gravatar avatars with monsterid fallback for user identification
- Add avatar_hash (MD5 of email) to MessagePayload for server-side hash computation
- Create avatar.js with Gravatar URL generation and client-side MD5 implementation
- Show sender names and unique avatars on all messages including own messages
- Use monsterid fallback for users without Gravatar, robot icons reserved for AI
- LEFT JOIN users table in message history queries for avatar hash lookup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 06:41:48 -06:00
df59accb81 feat: add room member list, AI agent naming, and fix invite flow
- Add clickable member count in room header that shows a dropdown with
  all room members (AI agent + humans) with role badges
- Give each room's AI agent a random name from 10 options (Nova, Atlas,
  Sage, etc.), editable when creating the room
- Replace AI text avatar with a robot SVG icon across all components
- Fix broken invite system: add client-side URL routing for /invite/:token,
  handle post-login invite acceptance via sessionStorage, and return
  room_id from accept_invite endpoint for auto-navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:27:17 -06:00
4a002c85d4 feat: add streaming AI responses with smooth token-by-token rendering
Replaces batch AI responses with real-time SSE streaming from OpenRouter.
Tokens are buffered client-side and drained via requestAnimationFrame for
a smooth typing effect instead of choppy chunk dumps.

Backend:
- Rewrite openrouter service for SSE streaming with incremental tool call accumulation
- Add AiStreamChunk/AiStreamEnd WebSocket event types
- Stream content deltas to clients during all tool call rounds
- Increase broadcast channel capacity (256 -> 4096) and handle Lagged errors gracefully

Frontend:
- Add StreamBuffer utility with adaptive rAF-based character draining
- Show streaming message-bubble with blinking cursor during generation
- Clean up buffer on room switch and final message replacement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:23:49 -06:00
01258fa958 feat: complete GroupChat app with AI tool calling, search, fetch, and UI
Full-stack real-time group chat with Rust/Axum backend and Riot.js frontend.

Features:
- Auth (register/login/JWT), rooms, invites, WebSocket messaging
- AI responses via OpenRouter with tool calling (Brave Search + web fetch)
- Real-time tool usage indicators (searching/reading page)
- Collapsible tool results in message bubbles
- AI stats bar (model, tokens, speed, response time) persisted to DB
- Room soft-delete, /clear command, dynamic model fetching
- Markdown rendering with code highlighting and copy buttons

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:50:52 -06:00