fix: WYSIWYG editor, content generation, and writing assistant bug fixes

- 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
This commit is contained in:
ajaysi
2026-05-14 09:11:30 +05:30
parent 7385100017
commit 928c2f20aa
113 changed files with 4344 additions and 10064 deletions

View File

@@ -64,7 +64,8 @@ interface UsageLimits {
}
interface DashboardData {
current_usage: UsageStats;
total_usage: UsageStats;
current_period_usage: UsageStats;
limits: UsageLimits;
projections: {
projected_monthly_cost: number;
@@ -248,14 +249,15 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
if (!dashboardData) return null;
const currentUsage = dashboardData.current_usage;
const totalUsage = dashboardData.total_usage;
const currentPeriodUsage = dashboardData.current_period_usage;
const limits = dashboardData.limits;
if (compact) {
// Compact view - show key metrics as chips
// Use current_usage for accurate cost (properly coerced from provider breakdown)
// Fallback to summary if current_usage is not available
const usageData = dashboardData?.current_usage || {
// Use total_usage for accurate cost (properly coerced from provider breakdown)
// Fallback to summary if total_usage is not available
const usageData = dashboardData?.total_usage || {
total_calls: dashboardData?.summary?.total_api_calls_this_month || 0,
total_cost: dashboardData?.summary?.total_cost_this_month || 0,
usage_status: dashboardData?.summary?.usage_status || 'active',
@@ -267,37 +269,49 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
const monthlyLimit = dashboardData?.limits?.limits?.monthly_cost || 0;
const usagePercentage = monthlyLimit > 0 ? (totalCost / monthlyLimit) * 100 : 0;
// Build per-category usage summaries from provider_breakdown and limits
const providerBreakdown = usageData.provider_breakdown || {};
// Use current_period provider_breakdown for budget bars, total_usage for total display
const periodBreakdown = currentPeriodUsage?.provider_breakdown || {};
const totalBreakdown = usageData.provider_breakdown || {};
const providerLimits = dashboardData?.limits?.limits || {};
// Aggregate AI text calls (gemini + openai + anthropic + mistral)
const aiCalls = (providerBreakdown.gemini?.calls || 0) + (providerBreakdown.openai?.calls || 0) + (providerBreakdown.anthropic?.calls || 0) + (providerBreakdown.mistral?.calls || 0) + (providerBreakdown.huggingface?.calls || 0) + (providerBreakdown.wavespeed?.calls || 0);
// Aggregate AI text calls (gemini + openai + anthropic + mistral) — from current period
const aiCalls = (periodBreakdown.gemini?.calls || 0) + (periodBreakdown.openai?.calls || 0) + (periodBreakdown.anthropic?.calls || 0) + (periodBreakdown.mistral?.calls || 0) + (periodBreakdown.huggingface?.calls || 0) + (periodBreakdown.wavespeed?.calls || 0);
const aiCallLimit = providerLimits.ai_text_generation_calls || providerLimits.gemini_calls || 0;
// Image calls (stability + wavespeed image)
const imageCalls = (providerBreakdown.stability?.calls || 0) + (providerBreakdown.image_edit?.calls || 0);
// Image calls (stability + wavespeed image) — from current period
const imageCalls = (periodBreakdown.stability?.calls || 0) + (periodBreakdown.image_edit?.calls || 0);
const imageCallLimit = providerLimits.stability_calls || 0;
const imageTotal = (totalBreakdown.stability?.calls || 0) + (totalBreakdown.image_edit?.calls || 0);
// Audio calls
const audioCalls = providerBreakdown.audio?.calls || 0;
// Audio calls — from current period
const audioCalls = periodBreakdown.audio?.calls || 0;
const audioCallLimit = providerLimits.audio_calls || 0;
const audioTotal = totalBreakdown.audio?.calls || 0;
// Video calls
const videoCalls = providerBreakdown.video?.calls || 0;
// Video calls — from current period
const videoCalls = periodBreakdown.video?.calls || 0;
const videoCallLimit = providerLimits.video_calls || 0;
const videoTotal = totalBreakdown.video?.calls || 0;
// Research calls (exa + tavily + serper + firecrawl)
const researchCalls = (providerBreakdown.exa?.calls || 0) + (providerBreakdown.tavily?.calls || 0) + (providerBreakdown.serper?.calls || 0) + (providerBreakdown.firecrawl?.calls || 0);
// Research calls (exa + tavily + serper + firecrawl) — from current period
const researchCalls = (periodBreakdown.exa?.calls || 0) + (periodBreakdown.tavily?.calls || 0) + (periodBreakdown.serper?.calls || 0) + (periodBreakdown.firecrawl?.calls || 0);
const researchCallLimit = (providerLimits.exa_calls || 0) + (providerLimits.tavily_calls || 0) + (providerLimits.serper_calls || 0) + (providerLimits.firecrawl_calls || 0);
// WaveSpeed calls (all WaveSpeed API calls)
const wavespeedCalls = providerBreakdown.wavespeed?.calls || 0;
// WaveSpeed calls (all WaveSpeed API calls) — from current period
const wavespeedCalls = periodBreakdown.wavespeed?.calls || 0;
const wavespeedCallLimit = providerLimits.wavespeed_calls || 0;
const wavespeedTotal = totalBreakdown.wavespeed?.calls || 0;
const formatLimit = (used: number, limit: number) => {
if (limit === 0) return `${used} / ∞`;
return `${used} / ${limit}`;
// All-time totals for rows without separate total variables
const aiTotal = (totalBreakdown.gemini?.calls || 0) + (totalBreakdown.openai?.calls || 0) + (totalBreakdown.anthropic?.calls || 0) + (totalBreakdown.mistral?.calls || 0) + (totalBreakdown.huggingface?.calls || 0) + (totalBreakdown.wavespeed?.calls || 0);
const researchTotal = (totalBreakdown.exa?.calls || 0) + (totalBreakdown.tavily?.calls || 0) + (totalBreakdown.serper?.calls || 0) + (totalBreakdown.firecrawl?.calls || 0);
const formatLimit = (used: number, limit: number, total?: number) => {
const periodStr = limit === 0 ? `${used} / ∞` : `${used} / ${limit}`;
if (total !== undefined && total !== used) {
return `${periodStr} • Total: ${total}`;
}
return periodStr;
};
return (
@@ -434,7 +448,7 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
sx={{ flex: 1, height: 4, borderRadius: 2, bgcolor: '#e5e7eb', '& .MuiLinearProgress-bar': { bgcolor: getUsageColor(aiCalls, aiCallLimit), borderRadius: 2 } }}
/>
<Typography variant="caption" sx={{ fontSize: '0.65rem', fontWeight: 600, color: getUsageColor(aiCalls, aiCallLimit), minWidth: 55, textAlign: 'right' }}>
{formatLimit(aiCalls, aiCallLimit)}
{formatLimit(aiCalls, aiCallLimit, aiTotal)}
</Typography>
</Box>
</Box>
@@ -449,7 +463,7 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
sx={{ flex: 1, height: 4, borderRadius: 2, bgcolor: '#e5e7eb', '& .MuiLinearProgress-bar': { bgcolor: getUsageColor(imageCalls, imageCallLimit), borderRadius: 2 } }}
/>
<Typography variant="caption" sx={{ fontSize: '0.65rem', fontWeight: 600, color: getUsageColor(imageCalls, imageCallLimit), minWidth: 55, textAlign: 'right' }}>
{formatLimit(imageCalls, imageCallLimit)}
{formatLimit(imageCalls, imageCallLimit, imageTotal)}
</Typography>
</Box>
</Box>
@@ -464,7 +478,7 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
sx={{ flex: 1, height: 4, borderRadius: 2, bgcolor: '#e5e7eb', '& .MuiLinearProgress-bar': { bgcolor: getUsageColor(audioCalls, audioCallLimit), borderRadius: 2 } }}
/>
<Typography variant="caption" sx={{ fontSize: '0.65rem', fontWeight: 600, color: getUsageColor(audioCalls, audioCallLimit), minWidth: 55, textAlign: 'right' }}>
{formatLimit(audioCalls, audioCallLimit)}
{formatLimit(audioCalls, audioCallLimit, audioTotal)}
</Typography>
</Box>
</Box>
@@ -479,7 +493,7 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
sx={{ flex: 1, height: 4, borderRadius: 2, bgcolor: '#e5e7eb', '& .MuiLinearProgress-bar': { bgcolor: getUsageColor(videoCalls, videoCallLimit), borderRadius: 2 } }}
/>
<Typography variant="caption" sx={{ fontSize: '0.65rem', fontWeight: 600, color: getUsageColor(videoCalls, videoCallLimit), minWidth: 55, textAlign: 'right' }}>
{formatLimit(videoCalls, videoCallLimit)}
{formatLimit(videoCalls, videoCallLimit, videoTotal)}
</Typography>
</Box>
</Box>
@@ -494,7 +508,7 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
sx={{ flex: 1, height: 4, borderRadius: 2, bgcolor: '#e5e7eb', '& .MuiLinearProgress-bar': { bgcolor: getUsageColor(researchCalls, researchCallLimit), borderRadius: 2 } }}
/>
<Typography variant="caption" sx={{ fontSize: '0.65rem', fontWeight: 600, color: getUsageColor(researchCalls, researchCallLimit), minWidth: 55, textAlign: 'right' }}>
{formatLimit(researchCalls, researchCallLimit)}
{formatLimit(researchCalls, researchCallLimit, researchTotal)}
</Typography>
</Box>
</Box>
@@ -509,7 +523,7 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
sx={{ flex: 1, height: 4, borderRadius: 2, bgcolor: '#e5e7eb', '& .MuiLinearProgress-bar': { bgcolor: getUsageColor(wavespeedCalls, wavespeedCallLimit), borderRadius: 2 } }}
/>
<Typography variant="caption" sx={{ fontSize: '0.65rem', fontWeight: 600, color: getUsageColor(wavespeedCalls, wavespeedCallLimit), minWidth: 55, textAlign: 'right' }}>
{formatLimit(wavespeedCalls, wavespeedCallLimit)}
{formatLimit(wavespeedCalls, wavespeedCallLimit, wavespeedTotal)}
</Typography>
</Box>
</Box>
@@ -552,7 +566,7 @@ const UsageDashboard: React.FC<UsageDashboardProps> = ({
}
// Full dashboard view (for dedicated usage page)
const usageData = dashboardData?.current_usage || {
const usageData = dashboardData?.total_usage || {
total_calls: dashboardData?.summary?.total_api_calls_this_month || 0,
total_cost: dashboardData?.summary?.total_cost_this_month || 0,
provider_breakdown: {}