- Fix text selection menu not showing: wire contentRef via inputRef on multiline TextField
- Fix blog title not truncating: add min-w-0 for flex item overflow
- Fix outline generation 500: escape curly braces in f-string prompt template
- Fix content generation 'NoneType not callable': replace SessionLocal() with get_session_for_user(), add db param to MediumBlogGenerator, fix signature mismatch in database_task_manager
- Fix writing assistant suggest 500: add auth + user_id to API endpoint and service, replace sync requests with httpx.AsyncClient
- Fix hallucination detector 404: explicitly include router in main.py and app.py
- Fix missing error_data in task failure responses
- Hide CopilotKit web inspector button
- Remove hardcoded fallback suggestions from SmartTypingAssist
- Fix stale closure refs in SmartTypingAssist handleTypingChange
- Add two-column editor layout, stats bar, section hover menu
- Various subscription, billing, and research module improvements
Backend:
- product_image_service.py: Replaced direct wavespeed_client.generate_image()
with generate_image() from main_image_generation (unified entry point)
- This ensures subscription pre-flight validation (_validate_image_operation)
and usage tracking (_track_image_operation_usage) are enforced
- Removed _generate_image_with_retry method and WaveSpeedClient dependency
- Animation/video/avatar services already route through ImageStudioManager - no changes needed
Frontend:
- useProductMarketing.ts: Added formatError() helper for 402/429 detection
across all 8 API operations
- useCampaignCreator.ts: Added formatError() helper for 402/429 detection
across all 13 API operations
- All error messages now surface subscription limits with upgrade prompts
Backend:
- New POST /api/image-studio/save-to-library endpoint
Saves generated base64 images to workspace disk and creates ContentAsset
record for the unified asset library. Returns asset_id, file_url, filename.
Frontend:
- Added saveImageToLibrary() to useImageStudio hook
- CreateStudio auto-saves generated images to asset library after creation
- All 8 API operations now use _formatErrorMessage() helper
for 402/429 subscription limit errors with upgrade prompts
instead of generic error messages
Extracted 2 endpoint groups into separate sub-router modules:
- edit.py: 4 endpoints (POST /edit/process, GET /edit/operations, GET /edit/models, POST /edit/recommend)
- face_swap.py: 3 endpoints (POST /face-swap/process, GET /face-swap/models, POST /face-swap/recommend)
All 33 routes preserved (10 extracted in B1, 7 extracted in B2, 16 remaining in legacy).
Extracted 4 endpoint groups into separate sub-router modules:
- health.py: 1 endpoint (GET /health)
- upscale.py: 1 endpoint (POST /upscale)
- control.py: 2 endpoints (POST /control/process, GET /control/operations)
- social.py: 2 endpoints (POST /social/optimize, GET /social/platforms/{platform}/formats)
__init__.py now composes these sub-routers into the legacy router.
All 33 routes preserved. No functional changes.
- Created routers/image_studio/models.py with all 40 Pydantic models
- Created routers/image_studio/deps.py with get_studio_manager() and _require_user_id()
- Renamed old monolithic image_studio.py -> image_studio_router.py
- Updated __init__.py to re-export the router for backward compatibility
- Old file now imports models and deps from new modules (no inline definitions)
Backward compatibility: from routers.image_studio import router still works.
Route count unchanged: 33 routes, prefix /api/image-studio.
Changes:
1. helpers.py (_track_image_operation_usage): Map provider name to DB columns
dynamically (stability→stability_calls, wavespeed→wavespeed_calls, etc.)
instead of hardcoding stability_calls/stability_cost.
2. upscale_service.py: Added _track_image_operation_usage() call after
successful Stability upscale completion.
3. control_service.py: Added _track_image_operation_usage() call after
successful Stability control operation completion.
4. edit_service.py: Added _track_image_operation_usage() call after
successful Stability edit operation (remove_background, inpaint,
outpaint, search_replace, search_recolor, relight).
Previously only Create Studio and Face Swap tracked usage. Now all five
studios correctly decrement subscription limits.
- Create FeatureRoute.tsx wrapper component for route-level feature gating
- Add FEATURE_KEYS constant map to demoMode.ts for type-safe feature references
- Wrap 47 feature-specific routes with <FeatureRoute> in App.tsx
- Core routes (dashboard, billing, pricing, auth callbacks) remain ungated
- Disabled features redirect to /dashboard and never load their lazy chunks
- Main bundle: +259 bytes (FeatureRoute is a lightweight component)
Closes Phase 1 Plan 01-02
- Replace all 31+ static route component imports in App.tsx with React.lazy() dynamic imports
- Add LazyLoadingFallback.tsx shared component for Suspense fallback
- Wrap <Routes> with <Suspense> for chunk loading states
- Handle named exports (ImageStudio, VideoStudio, ProductMarketing, StoryProjectList) with .then() wrapper
- Main bundle reduced from 8.42MB to 2.50MB (70% reduction)
- 190+ chunk files created for on-demand loading per route
Closes Phase 1 Plan 01-01
The podcast B-roll composer imports matplotlib for chart rendering, so adding it to backend requirements prevents import failures in fresh setups.
Co-authored-by: Cursor <cursoragent@cursor.com>
Frontend Changes:
- Added TrendingTopicsModal with tabs (Interest Chart, Regions, Related Topics, Related Queries)
- Reuses existing TrendsChart component from Research module
- Clickable chips for related topics/queries that populate the topic input
- Added 'Get Trending Topics' green button next to 'Enhance Topic With AI'
- Responsive layout: buttons stack on mobile, side-by-side on desktop
- Wired up modal state in CreateModal
- Backend endpoint and podcastApi method committed in prior push
- Move voice clone cache from module-level memory to localStorage
so it survives page refresh and works across browser tabs
- VoiceAvatarPlaceholder now syncs clone result to localStorage
immediately after creation (both design and clone paths)
- usePodcastProjectState auto-merges voice clone cache into project
knobs when loading a project (fills gap for projects created
before voice clone or when voice clone was created after)
- CreateModal now detects voice clone IDs by prefix (vc_*) not
just by VOICE_CLONE_ID constant, fixing the mismatch where
VoiceSelector passes the actual clone ID but CreateModal
expected the placeholder ID
- AudioRegenerateModal is intentionally per-scene override and
does not write back to knobs (by design)
- trends.py handler added for podcast topic trend analysis
Frontend Changes:
- Add scene numbering badge (1/N) next to scene titles
- Add inline status chips (Complete, Audio, Image, Voice, Why Script)
- Professional AI-like gradient styling for all chips with shadows
- Remove Script Editor header and 'Why This Script Format?' collapsible
- Move Voice and Why Script info to per-scene chips
- Make scene section mobile-responsive (responsive layout, button sizing)
- Rename 'B-Roll Charts' to 'Podcast Charts' with accordion (collapsed by default)
- Add sceneIndex prop to SceneEditor for scene numbering
- Enhanced accessibility with keyboard navigation and focus states
Backend Changes:
- Audio handler improvements
- B-roll handler enhancements
- Script handler updates
- B-roll composer and service improvements
- Removed temporary broll_temp files
Technical:
- Full mobile responsiveness for scene cards
- Gradient chip styling: vibrant colors with white text and shadows
- Non-breaking approval/generation flow preserved
- TypeScript compatibility maintained
- Restore auth on assets_serving.py using get_current_user_with_query_token
(supports ?token= query param for <audio> elements)
- Add proper MIME type detection on asset serving (fixes NotSupportedError)
- Use storage_paths for path resolution in assets_serving.py
- VoiceSelector: append auth token to preview URLs for /api/ endpoints
- VoiceAvatarPlaceholder: add authenticatedAudioUrl state with async token
resolution so <audio> elements get ?token= query param
- TestPersonaModal: same auth token pattern for voice preview audio