Replaced inner Textarea component (defined inside StudioCustomizeModal)
with a plain renderTextarea() function called directly — prevents React
from unmounting/remounting the textarea on each re-render.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Consistent #0D0D0D dark theme throughout, no white sections
- Unified action buttons: dark surface with FFCB05/FF5C00 icon accents
- Studio grid: refined cards with hover lift, pencil shows on hover
- All modals/panels (share, edit, podcast, upload) match dark surface
- Processing/failed banners: dark blue/red tints with proper contrast
- Documents list, synthesis, studio viewer all dark themed
- Studio sub-views (flashcards, quiz, report, datatable) dark themed
- globals.css .dk class resets forced color override for dark sections
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FastAPI HTTPBearer returns 403 when no Authorization header is present,
but the frontend interceptor only handles 401 to redirect to login.
Switching to auto_error=False and throwing 401 manually fixes the redirect.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Browser preview:
- White background for content slides, dark (#1A1A1A) for title slide
- Yellow (#FFCB05) top bar, title underline, slide number highlight
- Orange (#FF5C00) bullet dots
- Montserrat font via CSS, responsive clamp() sizing
PPTX download:
- Blank layout with programmatic shapes (no default theme artifacts)
- Dark title slide with yellow left bar and separator line
- White content slides with yellow top/accent bars
- Orange bullet markers, Montserrat font, proper sizing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Studio is now a 5th button in the main action bar. Clicking it expands a second row of 7 colored buttons (Mind Map, Flashcards, Quiz, Slide Deck, Report, Infographic, Data Table) styled to match existing Chat/Share/Analysis/Podcast buttons. Removes the old card grid section.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- README: rewrite for Docker-first setup, add Studio features, update schema/endpoints, add troubleshooting for common Docker issues
- CLAUDE.md: new file with architecture overview, deployment commands, DB schema, common issues for AI assistants
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- studio_generators.py: 7 Pydantic output models + async generators using LLM factory
- database.py: add studio_data TEXT column with safe ALTER TABLE migration
- notebooks.py: GET /studio, POST /studio/{type}, GET /studio/{slides,report}/download
- types/index.ts, api.ts: Studio type interfaces and API calls
- page.tsx: Studio grid section, modal overlay, 7 viewer sub-components (SVG mind map, 3D flashcard flip, scored quiz, slide carousel, report PDF, infographic, data table)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
database.py: use pgql_host/pgql_port env vars (default: localhost:5433)
docker-compose.yml: set pgql_host=postgres, pgql_port=5432 for backend service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add git stash before git pull to handle locally created directories
- Replace invalid ROW_COUNT() with grep on psql command tag output
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous commit changed get_db() to a generator for FastAPI
dependency injection. This broke code that called get_db() directly.
- Add get_db_session() that returns Session directly for non-FastAPI code
- Update all manager files to use get_db_session()
- Update all route files to use get_db_session() for direct calls
- Update Streamlit pages to use get_db_session()
- Keep get_db() as generator for FastAPI Depends() usage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix get_db() to use generator pattern with proper session cleanup
- Increase pool size from 10+20 to 20+30 with pool_pre_ping
- Refactor admin routes to use FastAPI dependency injection
- Eliminate double-session issue in check_admin()
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace broken useEffect-based refresh (which had race conditions) with
simpler refetchInterval approach. Now the notebook query polls every 2s
while any tasks are processing, then stops when all complete.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace deprecated llama-index-llms-gemini with the new google-genai package
to match the SDK migration in llm_factory.py.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When deleting a notebook using the shared pipeline, now only deletes that
notebook's document files instead of the entire pipeline. Legacy notebooks
with dedicated pipelines still get full pipeline deletion.
Added is_shared_pipeline_async() with fallback lookup for server restarts
and delete_notebook_documents_from_pipeline() for selective file deletion.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added useEffect that tracks completed task count and invalidates the
notebook query when new tasks finish. This ensures documents appear
in the list immediately after processing without requiring a manual
page refresh.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed gpt-5.1 to gpt-5-pro since llama-index-llms-openai 0.6.10 doesn't
have gpt-5.1 in its supported models list yet. gpt-5-pro is the closest
available model in the supported list.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add specific detection for "pipeline not found" errors in query engine
- Log helpful message explaining the documents need re-uploading
- Update chat error message to guide users when index doesn't exist
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace one-pipeline-per-notebook architecture with a single shared pipeline
to avoid hitting LlamaCloud's pipeline limit. Notebook isolation is now
achieved through metadata filtering:
- Add get_or_create_shared_pipeline() to auto-create/find shared pipeline
- Set notebook_id as custom_metadata when uploading documents
- Filter queries by notebook_id metadata for shared pipeline notebooks
- Legacy notebooks with dedicated pipelines continue to work unchanged
Files modified:
- pipeline_manager.py: Add shared pipeline mgmt, metadata on upload/query
- notebooks.py: Use shared pipeline for new notebooks
- background_tasks.py: Pass notebook_id when uploading documents
- chat.py: Pass notebook_id when querying pipeline
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Users can now preview ElevenLabs voices before selecting them for podcast
generation. Added play/stop buttons next to each voice dropdown that play
the official ElevenLabs preview audio samples.
- Added previewUrl to each voice in the voices array
- Added playPreview function with HTML5 Audio API
- Added play/stop buttons with visual feedback when playing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added logging at:
- API endpoint: logs raw request values before defaults applied
- audio.py: logs exact voice_id being sent to ElevenLabs for each segment
This will help diagnose if voice IDs are being lost somewhere in the pipeline.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added logging to track target_length parameter from task execution through
outline and script generation. This helps verify that the user's selected
podcast duration is being correctly passed through the system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The frontend was sending voice IDs in a JSON body, but the FastAPI endpoint
was defining them as query parameters. Created a PodcastRequest Pydantic
model to properly receive the JSON body. Also changed logging level from
DEBUG to INFO.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add proper logging throughout the podcast generation flow to track which
voices are selected by users and used with the ElevenLabs API. Creates a
centralized voice_config.py with voice ID to name mapping and updates all
podcast-related files to use the logging module instead of print statements.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Made email lookups case-insensitive to resolve issue where users couldn't see shared notebooks due to email casing mismatches between SSO registration (lowercase) and manual sharing input.
Changes:
- Updated get_user_by_email() to use ilike for case-insensitive matching
- Normalized email input to lowercase in share endpoint
- Added logging for debugging share operations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Security improvements:
- Add JWT token authentication for all API endpoints
- Create jwt_auth middleware with token generation and validation
- Update auth routes to return access tokens on login/signup
- Add Authorization header interceptor in frontend API client
- Store JWT tokens in Zustand auth store
- Remove insecure user_id query parameters
Sharing enhancements:
- Add user search API endpoint (GET /api/auth/users/search)
- Implement autocomplete in share modal with debounced search
- Show email and username suggestions in dropdown
- Add keyboard navigation (arrows, Enter, Escape)
- Filter out already-shared users and current user from results
- Add email format validation on frontend and backend
Developer experience:
- Make basePath environment-specific (empty in dev, /notebookllama in prod)
- Update next.config.js to support local development without basePath
- Fix routing issues in local development (login/signup redirects)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>