Commit Graph

53 Commits

Author SHA1 Message Date
Ryan Chen bddd741fcc Fix missing React import in AnswerBubble 2026-09-11 16:40:48 -04:00
Ryan Chen b23fd19c4b Copy button + hover highlight on assistant messages 2026-09-11 16:27:51 -04:00
Ryan Chen 20d44bb3b3 Autofocus textarea on new conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-10 14:14:59 -04:00
Ryan Chen 3318839551 Sort conversations by last activity
Bump Conversation.updated_at on every message (not just the first) so
actively-used conversations rise to the top of the list endpoint's
existing -updated_at sort. Refresh the sidebar after every send so the
just-used conversation reorders live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-10 01:33:44 -04:00
Ryan Chen 4fe5b8daea Persist streamed assistant answers even if the client disconnects
The first message in a new conversation navigated to /c/:id, which remounted
ChatScreen (two separate routes) and aborted the in-flight /stream-query fetch.
Server-side that surfaced as asyncio.CancelledError, which isn't an Exception,
so the except branch missed it and skipped the add_message_to_conversation
save — the fully-streamed answer was never persisted.

Backend: handle CancelledError and persist via a detached, shielded task so a
disconnect at any point (navigation, tab close, network blip) still commits the
answer. Frontend: serve "/" and "/c/:id" from a single catch-all ChatScreen
route so first-message navigation no longer remounts and aborts the stream;
derive the conversation id via useMatch instead of useParams.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-10 01:33:44 -04:00
Ryan Chen b918522816 Stream assistant answers token-by-token over SSE
The /stream-query endpoint previously emitted only tool-lifecycle events plus
one final complete message, so the answer still appeared all at once. Handle
on_chat_model_stream events to emit incremental `content` deltas, accumulate
them for persistence fallback, and consume them on the frontend to build a live
assistant bubble (reconciled with the authoritative final text). Hide the
redundant loading indicator once tokens start arriving.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 23:14:08 -04:00
Ryan Chen 09dd983c4e Replace inline mobile menu with slide-in drawer and paginate conversations
Rework mobile navigation into a proper left slide-in drawer with a dimmed
backdrop, sharing a new SidebarContent component with the desktop sidebar so
mobile users can reach Admin/Scheduler/Sign out. The sidebar now loads only the
10 most recent conversations; a dedicated /conversations page browses the full
list with server-side search and "Load more" pagination.

- Backend: limit/offset/search query params on GET /api/conversation/
- SidebarContent shared by desktop sidebar and mobile drawer
- useConversationBrowser hook powers the dedicated page
- Remove duplicated inline conversation lists from ChatScreen

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 22:32:19 -04:00
Ryan Chen 530a90f237 Render assistant replies as full-width text instead of bubbles
Strip the card, border, shadow, and accent bar from AnswerBubble so
Simba's replies read like Claude/ChatGPT — plain full-width markdown on
the page. User messages stay bubbled for turn contrast. Widen the reading
column to max-w-3xl with roomier spacing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-09 22:19:58 -04:00
Ryan Chen 6a62db2e85 Add per-conversation URLs with client-side routing
Give each conversation a deep-linkable URL (/c/:conversationId) so threads
survive refresh, support browser back/forward, and are shareable within a
session. The app was already an SPA but had no router.

- Add react-router-dom; wrap the app in BrowserRouter and define routes
  ("/" for the empty/new state, "/c/:conversationId" for a thread)
- Make the URL the source of truth for selection: selectedConversation is
  derived from the :conversationId param; create/delete navigate instead of
  mutating local selection state
- Load a thread's messages in a URL-keyed effect that clears immediately for
  an instant switch, with a stale-response guard
- Preserve the optimistic message + in-flight stream when the first message
  lazily creates a conversation and navigates into it (skip-ref keyed on the
  new id, guarded against the null/home-route collision)

No backend changes: app.py already serves index.html for unmatched paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 22:10:47 -04:00
Ryan Chen 32285891fc Clean up frontend chat interactions and scope conversation access to owner
Frontend:
- Add stop-generation button (wire up the unused AbortController)
- Surface stream/network errors in a dismissible banner instead of console
- Add rename and delete for conversations via a per-thread menu
- Make simba mode a single controlled source; fix miscast keydown handler
- Auto-grow the message textarea; remove dead ConversationMenu component

Backend:
- Scope get/query/stream-query/rename/delete conversation routes to the
  authenticated owner via a shared _get_owned_conversation helper (fixes IDOR)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 09:53:53 -04:00
Ryan Chen 7aef6484c3 Render GFM tables in chat replies
Enable remark-gfm in AnswerBubble so pipe-table syntax parses instead
of showing as raw text, and add table styling to markdown-content.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-09 09:38:54 -04:00
Ryan Chen 9bccac82f3 Add "Ask Simba" option to scheduled messages
When use_agent is enabled, the scheduler runs the message content as a
prompt through the LangChain agent and sends Simba's response instead of
the raw content. Frontend adds an Ask Simba toggle with visual indicator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-04 11:15:11 -04:00
Ryan Chen 467e752629 Add recurring scheduled messages (daily, weekly, monthly)
Extend scheduled messages with a recurrence field. After sending a
recurring message, the scheduler automatically creates the next pending
occurrence. Frontend adds repeat toggle (Once/Daily/Weekly/Monthly) and
displays recurrence in the messages table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-03 23:30:30 -04:00
Ryan Chen f5203e0466 Add scheduled messages and strip markdown from iMessage responses
Strip markdown formatting (bold, italic, headers, code, links, lists) from
LLM responses before sending via iMessage. Add scheduled messages feature
with CRUD API, background scheduler loop, and admin frontend panel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-03 23:25:10 -04:00
Ryan Chen 4ac0754ea7 Refactor frontend to hook-based architecture
Extract logic from god components into custom hooks (useAuthCheck,
useConversations, useChat, usePresignedUrl, useAdminUsers, useOIDCAuth).
Eliminate unnecessary useEffects per React guidelines — scroll is now
imperative, isAdmin comes from useAuthCheck instead of a separate fetch.
ConversationList becomes a pure presentational component. Wrap bubble
components in React.memo.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 09:11:57 -04:00
Ryan Chen f3ae76ce68 Fix mobile conversation launch resetting to homepage
Remove the useEffect on selectedConversation.id that race-conditions
with handleQuestionSubmit — it fetches the (still-empty) conversation
and wipes messages, sending the user back to the empty state. Refresh
conversation list after streaming completes instead to pick up the
auto-generated title.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 22:08:26 -04:00
Ryan Chen e644def141 Fix mobile performance degradation during typing and after image upload
Memoize blob URL creation to prevent leak on every keystroke, wrap
MessageInput in React.memo with stable useCallback props, remove
expensive backdrop-blur-sm from chat footer, and use instant scroll
during streaming to avoid queuing smooth scroll animations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 06:58:53 -04:00
ryan be600e78d6 Merge pull request 'Fix images not sending in existing conversations' (#19) from fix/image-in-existing-conversations into main
Reviewed-on: #19
2026-04-04 09:08:46 -04:00
Ryan Chen b6576fb2fd Fix images not sending in existing conversations
Add missing pendingImage, onImageSelect, and onClearImage props to the
MessageInput rendered in the active chat footer, matching the homepage version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 09:07:21 -04:00
Ryan Chen 30db71d134 Clean up presigned URL implementation: remove dead fields, fix error handling
- Remove unused image_url from upload response and TS type
- Remove bare except in serve_image that masked config errors as 404s
- Add error state and broken-image placeholder in QuestionBubble

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 08:52:26 -04:00
Ryan Chen 167d014ca5 Use presigned S3 URLs for serving images instead of proxying bytes
Browser <img> tags can't attach JWT headers, causing 401s. The image
endpoint now returns a time-limited presigned S3 URL via authenticated
API call, which the frontend fetches and uses directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 08:52:26 -04:00
Ryan Chen 0415610d64 Add image upload and vision analysis to Ask Simba chat
Users can now attach images in the web chat for Simba to analyze using
Ollama's gemma3 vision model. Images are stored in Garage (S3-compatible)
and displayed in chat history.

Also fixes aerich migration config by extracting TORTOISE_CONFIG into a
standalone config/db.py module, removing the stale aerich_config.py, and
adding missing MODELS_STATE to migration 3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 08:03:19 -04:00
ryan 0f88d211de Add PWA support for install-to-home-screen
Adds manifest.json, service worker with static asset caching,
resized cat icons, and meta tags for iOS/Android installability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 20:16:27 -04:00
ryan 6a7b1369ad Add email channel via Mailgun for Ask Simba
Users can now receive a unique email address (ask+<token>@domain) and
interact with Simba by sending emails. Inbound emails hit a Mailgun
webhook, are authenticated via HMAC token lookup, processed through the
LangChain agent, and replied to via the Mailgun API.

- Extract shared SIMBA_SYSTEM_PROMPT to blueprints/conversation/prompts.py
- Add email_enabled and email_hmac_token fields to User model
- Create blueprints/email with webhook, signature validation, rate limiting
- Add admin endpoints to enable/disable email per user
- Update AdminPanel with Email column, toggle, and copy-address button
- Add Mailgun env vars to .env.example
- Include database migration for new fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 16:21:18 -04:00
ryan 4621755c54 Make cat icons even bigger across all views
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 16:05:53 -04:00
ryan b6cd4e85f0 Fix mobile viewport scroll and enlarge cat icons
Use 100dvh for proper mobile browser chrome handling and increase
cat icon sizes across sidebar, mobile header, and empty state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 16:03:40 -04:00
ryan 30d7f0a060 Fix YNAB transaction fetching for SDK v2 compatibility
- Convert date strings to datetime.date objects before passing to API (strict Pydantic validation rejects strings)
- Use txn.var_date instead of txn.date (renamed in SDK v2 to avoid Python builtin conflict)
- Migrate BudgetsApi → PlansApi and update method names accordingly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 18:54:20 -04:00
ryan da9b52dda1 Add Claude.ai-style homepage with centered empty state
Show centered cat icon + "Ask me anything" + input when no messages
exist. Transition to scrollable messages + bottom input once chat
starts. Auto-create a conversation on first message if none selected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 09:47:37 -04:00
ryan d1cb55ff1a Frontend revamp: Animal Crossing × Claude design with shadcn components
- New palette: deep nook green sidebar, sage user bubbles, warm cream answer cards
- shadcn-style UI primitives: Button (CVA variants), Textarea, Input, Badge, Table
- cn() utility (clsx + tailwind-merge)
- lucide-react icons throughout (no more text-only buttons)
- Simba mode: custom CSS toggle switch
- Send button: circular amber button with arrow icon
- AnswerBubble: amber gradient accent bar, loading dots animation
- QuestionBubble: sage green pill with rounded-3xl
- ToolBubble: centered leaf-green badge pill
- ConversationList: active item highlighting, proper selectedId prop
- Sidebar: collapsible with PanelLeftClose/Open icons, icon-only collapsed state
- LoginScreen: decorative background blobs, refined rounded card
- AdminPanel: proper icon buttons, leaf-green save confirmation
- Fonts: Playfair Display (brand) + Nunito 800 weight added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 09:22:34 -04:00
ryan 53b2b3b366 Add admin panel and fix simba mode response display
- Add /me, /admin/users, and WhatsApp link/unlink endpoints
- Add AdminPanel component with user management UI
- Add userService methods for admin API calls
- Fix simba mode so cat responses appear in the message list
- Fetch userinfo endpoint for groups on OIDC callback (Authelia compat)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 09:06:59 -04:00
ryan 97be5262a8 new feature 2026-03-03 08:23:31 -05:00
ryan 86cc269b3a yeet 2026-03-03 08:23:31 -05:00
ryan 6d7d713532 Replace blue/indigo sidebar colors with warm stone neutrals
Align ConversationList colors with Anthropic design guidelines,
replacing indigo-300/blue-400 with stone-200/stone-300.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 08:18:08 -05:00
Ryan Chen 6ae36b51a0 ynab update 2026-01-31 22:47:43 -05:00
Ryan Chen ad39904dda reorganization 2026-01-31 17:13:27 -05:00
Ryan Chen 70799ffb7d refactor 2025-11-10 15:51:13 -05:00
Ryan Chen 7f1d4fbdda asdf 2025-10-29 22:17:45 -04:00
Ryan Chen 5ebdd60ea0 Making better 2025-10-29 21:28:23 -04:00
Ryan Chen 454fb1b52c Add authentication validation on login screen load
- Add validateToken() method to userService to check if refresh token is valid
- Automatically redirect to chat if user already has valid session
- Show 'Checking authentication...' loading state during validation
- Prevents unnecessary login if user is already authenticated
- Improves UX by skipping login screen when not needed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 12:24:10 -04:00
Ryan Chen c3f2501585 Clear text input immediately upon message submission
- Clear input field right after user sends message (before API call)
- Add validation to prevent submitting empty/whitespace-only messages
- Improve UX by allowing user to type next message while waiting for response
- Works for both simba mode and normal mode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 12:22:32 -04:00
Ryan Chen 1da21fabee Add auto-scroll to bottom for new messages
- Automatically scroll to latest message when new messages arrive
- Uses smooth scrolling behavior for better UX
- Triggers on message array changes
- Improves chat experience by keeping conversation in view

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 12:12:05 -04:00
Ryan Chen dd5690ee53 Add submit on Enter for chat textarea
- Press Enter to submit message
- Press Shift+Enter to insert new line
- Add helpful placeholder text explaining keyboard shortcuts
- Improve chat UX with standard messaging behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 12:07:47 -04:00
Ryan Chen 19d1df2f68 Improve mobile responsiveness with Tailwind breakpoints
- Replace fixed-width containers (min-w-xl max-w-xl) with responsive classes
- Mobile: full width with padding, Tablet: 90% max 768px, Desktop: max 1024px
- Make ChatScreen header stack vertically on mobile, horizontal on desktop
- Add touch-friendly button sizes (min 44x44px tap targets)
- Optimize textarea and form inputs for mobile keyboards
- Add text wrapping (break-words) to message bubbles to prevent overflow
- Apply responsive text sizing (text-sm sm:text-base) throughout
- Improve ConversationList with touch-friendly hit areas
- Add responsive padding/spacing across all components

All components now use standard Tailwind breakpoints:
- sm: 640px+ (tablet)
- md: 768px+ (larger tablet)
- lg: 1024px+ (desktop)
- xl: 1280px+ (large desktop)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 11:57:54 -04:00
Ryan Chen a56f752917 favicon 2025-10-26 10:48:59 -04:00
Ryan Chen f16e13fccc big uplift 2025-10-26 09:25:17 -04:00
Ryan Chen 29ac724d50 enabling login btw users 2025-10-25 09:30:54 -04:00
Ryan Chen 45a5e92aee Added conversation history (#4)
Reviewed-on: #4
Co-authored-by: Ryan Chen <ryan@torrtle.co>
Co-committed-by: Ryan Chen <ryan@torrtle.co>
2025-10-23 22:29:12 -04:00
Ryan Chen 376baccadb message-style frontend 2025-10-10 23:28:41 -04:00
Ryan Chen 0bb3e3172b adding image processing pipeline immich -> paperless 2025-10-04 08:54:10 -04:00
Ryan Chen 24b30bc8a3 Adding Simba mode 2025-10-03 20:25:57 -04:00