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:
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { BlogOutlineSection, BlogResearchResponse, BlogSEOMetadataResponse, BlogSEOAnalyzeResponse, SourceMappingStats, GroundingInsights, OptimizationResults, ResearchCoverage } from '../services/blogWriterApi';
|
||||
import { researchCache } from '../services/researchCache';
|
||||
import { blogWriterCache } from '../services/blogWriterCache';
|
||||
|
||||
const MINOR_TITLE_WORDS = new Set([
|
||||
'a', 'an', 'and', 'or', 'but', 'the', 'for', 'nor', 'on', 'at', 'to', 'from', 'by',
|
||||
@@ -94,36 +95,70 @@ export const useBlogWriterState = () => {
|
||||
|
||||
// Cache recovery - restore most recent research on page load
|
||||
useEffect(() => {
|
||||
const cachedEntries = researchCache.getAllCachedEntries();
|
||||
if (cachedEntries.length > 0) {
|
||||
// Get the most recent cached research
|
||||
const mostRecent = cachedEntries[0];
|
||||
console.log('Restoring cached research from page load:', mostRecent.keywords);
|
||||
setResearch(mostRecent.result);
|
||||
|
||||
// Also try to restore outline if it exists in localStorage
|
||||
try {
|
||||
const savedOutline = localStorage.getItem('blog_outline');
|
||||
const savedTitleOptions = localStorage.getItem('blog_title_options');
|
||||
const savedSelectedTitle = localStorage.getItem('blog_selected_title');
|
||||
const restoreState = async () => {
|
||||
const cachedEntries = researchCache.getAllCachedEntries();
|
||||
if (cachedEntries.length > 0) {
|
||||
// Get the most recent cached research
|
||||
const mostRecent = cachedEntries[0];
|
||||
console.log('Restoring cached research from page load:', mostRecent.keywords);
|
||||
setResearch(mostRecent.result);
|
||||
|
||||
if (savedOutline) {
|
||||
setOutline(JSON.parse(savedOutline));
|
||||
// Also try to restore outline if it exists in localStorage
|
||||
try {
|
||||
const savedOutline = localStorage.getItem('blog_outline');
|
||||
const savedTitleOptions = localStorage.getItem('blog_title_options');
|
||||
const savedSelectedTitle = localStorage.getItem('blog_selected_title');
|
||||
|
||||
if (savedOutline) {
|
||||
const parsedOutline = JSON.parse(savedOutline);
|
||||
setOutline(parsedOutline);
|
||||
|
||||
// Restore content sections from cache when outline is available
|
||||
const outlineIds = parsedOutline.map((s: any) => String(s.id));
|
||||
const cachedContent = blogWriterCache.getCachedContent(outlineIds);
|
||||
if (cachedContent && Object.keys(cachedContent).length > 0) {
|
||||
setSections(cachedContent);
|
||||
console.log('Restored content sections from cache', { sections: Object.keys(cachedContent).length });
|
||||
}
|
||||
}
|
||||
if (savedTitleOptions) {
|
||||
setTitleOptions(JSON.parse(savedTitleOptions));
|
||||
}
|
||||
if (savedSelectedTitle) {
|
||||
setSelectedTitle(savedSelectedTitle);
|
||||
}
|
||||
|
||||
// Restore contentConfirmed from localStorage
|
||||
const savedContentConfirmed = localStorage.getItem('blog_content_confirmed');
|
||||
if (savedContentConfirmed === 'true') {
|
||||
setContentConfirmed(true);
|
||||
}
|
||||
|
||||
console.log('Restored outline, content, and title data from localStorage');
|
||||
} catch (error) {
|
||||
console.error('Error restoring outline data:', error);
|
||||
}
|
||||
if (savedTitleOptions) {
|
||||
setTitleOptions(JSON.parse(savedTitleOptions));
|
||||
}
|
||||
if (savedSelectedTitle) {
|
||||
setSelectedTitle(savedSelectedTitle);
|
||||
}
|
||||
|
||||
console.log('Restored outline and title data from localStorage');
|
||||
} catch (error) {
|
||||
console.error('Error restoring outline data:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
restoreState();
|
||||
}, []);
|
||||
|
||||
// Persist contentConfirmed to localStorage whenever it changes
|
||||
useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem('blog_content_confirmed', String(contentConfirmed));
|
||||
} catch {}
|
||||
}, [contentConfirmed]);
|
||||
|
||||
// Persist sections to blogWriterCache whenever they change
|
||||
useEffect(() => {
|
||||
const outlineIds = outline.map(s => String(s.id));
|
||||
if (outlineIds.length > 0 && Object.keys(sections).length > 0) {
|
||||
blogWriterCache.cacheContent(sections, outlineIds);
|
||||
}
|
||||
}, [sections, outline]);
|
||||
|
||||
// Handle research completion
|
||||
const handleResearchComplete = useCallback((researchData: BlogResearchResponse) => {
|
||||
setResearch(researchData);
|
||||
|
||||
Reference in New Issue
Block a user