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,4 +1,21 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import ArticleIcon from '@mui/icons-material/Article';
|
||||
import HelpIcon from '@mui/icons-material/Help';
|
||||
import HeaderControls from '../../shared/HeaderControls';
|
||||
import PhaseNavigation, { PhaseActionHandlers } from '../PhaseNavigation';
|
||||
|
||||
interface HeaderBarProps {
|
||||
@@ -15,61 +32,154 @@ interface HeaderBarProps {
|
||||
hasSEOAnalysis?: boolean;
|
||||
seoRecommendationsApplied?: boolean;
|
||||
hasSEOMetadata?: boolean;
|
||||
onNewBlog?: () => void;
|
||||
onMyBlogs?: () => void;
|
||||
onHelp?: () => void;
|
||||
}
|
||||
|
||||
export const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
phases,
|
||||
currentPhase,
|
||||
onPhaseClick,
|
||||
copilotKitAvailable = true,
|
||||
actionHandlers,
|
||||
hasResearch = false,
|
||||
hasOutline = false,
|
||||
outlineConfirmed = false,
|
||||
hasContent = false,
|
||||
contentConfirmed = false,
|
||||
hasSEOAnalysis = false,
|
||||
seoRecommendationsApplied = false,
|
||||
hasSEOMetadata = false,
|
||||
phases, currentPhase, onPhaseClick, copilotKitAvailable = true, actionHandlers,
|
||||
hasResearch = false, hasOutline = false, outlineConfirmed = false,
|
||||
hasContent = false, contentConfirmed = false, hasSEOAnalysis = false,
|
||||
seoRecommendationsApplied = false, hasSEOMetadata = false,
|
||||
onNewBlog, onMyBlogs, onHelp,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const isMenuOpen = Boolean(anchorEl);
|
||||
|
||||
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => setAnchorEl(event.currentTarget);
|
||||
const handleMenuClose = () => setAnchorEl(null);
|
||||
|
||||
const handleNewBlog = () => { handleMenuClose(); onNewBlog?.(); };
|
||||
const handleMyBlogs = () => { handleMenuClose(); onMyBlogs?.(); };
|
||||
const handleHelp = () => { handleMenuClose(); onHelp?.(); };
|
||||
|
||||
return (
|
||||
<div style={{ padding: 16, borderBottom: '1px solid #eee' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
||||
<h2 style={{ margin: 0 }}>AI Blog Writer</h2>
|
||||
<div style={{
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
backgroundColor: '#f0f0f0',
|
||||
borderRadius: '8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '20px',
|
||||
fontWeight: 'bold',
|
||||
color: '#666'
|
||||
}}>
|
||||
A
|
||||
</div>
|
||||
</div>
|
||||
<PhaseNavigation
|
||||
phases={phases}
|
||||
currentPhase={currentPhase}
|
||||
onPhaseClick={onPhaseClick}
|
||||
copilotKitAvailable={copilotKitAvailable}
|
||||
actionHandlers={actionHandlers}
|
||||
hasResearch={hasResearch}
|
||||
hasOutline={hasOutline}
|
||||
outlineConfirmed={outlineConfirmed}
|
||||
hasContent={hasContent}
|
||||
contentConfirmed={contentConfirmed}
|
||||
hasSEOAnalysis={hasSEOAnalysis}
|
||||
seoRecommendationsApplied={seoRecommendationsApplied}
|
||||
hasSEOMetadata={hasSEOMetadata}
|
||||
/>
|
||||
</div>
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
background: 'linear-gradient(135deg, rgba(37, 99, 235, 0.08) 0%, rgba(59, 130, 246, 0.08) 100%)',
|
||||
borderRadius: 3,
|
||||
p: { xs: 1.5, md: 2.5 },
|
||||
border: '1px solid rgba(37, 99, 235, 0.15)',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: 0, left: 0, right: 0,
|
||||
height: '3px',
|
||||
background: 'linear-gradient(90deg, #2563eb 0%, #3b82f6 50%, #60a5fa 100%)',
|
||||
},
|
||||
}}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" flexWrap="wrap" gap={1}>
|
||||
<Stack direction="row" alignItems="center" gap={1.5}>
|
||||
<Box sx={{
|
||||
width: { xs: 36, md: 44 },
|
||||
height: { xs: 36, md: 44 },
|
||||
borderRadius: 2,
|
||||
background: 'linear-gradient(135deg, #2563eb 0%, #3b82f6 100%)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: '0 4px 12px rgba(37, 99, 235, 0.3)',
|
||||
}}>
|
||||
<ArticleIcon sx={{ color: '#fff', fontSize: { xs: 20, md: 24 } }} />
|
||||
</Box>
|
||||
<Typography variant="h5" sx={{
|
||||
background: 'linear-gradient(135deg, #1e293b 0%, #334155 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
fontWeight: 700,
|
||||
fontSize: { xs: '1.1rem', sm: '1.25rem', md: '1.5rem' },
|
||||
letterSpacing: '-0.02em',
|
||||
}}>
|
||||
AI Blog Writer
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<HeaderControls colorMode="light" showAlerts={true} showUser={true} />
|
||||
|
||||
<IconButton onClick={handleMenuOpen} sx={{
|
||||
background: isMenuOpen
|
||||
? 'linear-gradient(135deg, #2563eb 0%, #3b82f6 100%)'
|
||||
: 'rgba(37, 99, 235, 0.1)',
|
||||
border: '1px solid',
|
||||
borderColor: isMenuOpen ? 'transparent' : 'rgba(37, 99, 235, 0.3)',
|
||||
borderRadius: 2,
|
||||
p: 1,
|
||||
transition: 'all 0.2s ease',
|
||||
'&:hover': {
|
||||
background: 'linear-gradient(135deg, #2563eb 0%, #3b82f6 100%)',
|
||||
borderColor: 'transparent',
|
||||
transform: 'scale(1.05)',
|
||||
},
|
||||
}}>
|
||||
{isMenuOpen ? <CloseIcon sx={{ color: '#fff', fontSize: 20 }} /> : <MenuIcon sx={{ color: '#2563eb', fontSize: 20 }} />}
|
||||
</IconButton>
|
||||
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={isMenuOpen}
|
||||
onClose={handleMenuClose}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
mt: 1, minWidth: 220, borderRadius: 2,
|
||||
background: 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)',
|
||||
border: '1px solid rgba(37, 99, 235, 0.3)',
|
||||
boxShadow: '0 10px 40px rgba(37, 99, 235, 0.25)',
|
||||
'& .MuiMenuItem-root': {
|
||||
color: 'rgba(255, 255, 255, 0.85)',
|
||||
px: 2, py: 1.5,
|
||||
transition: 'all 0.15s ease',
|
||||
'&:hover': {
|
||||
background: 'linear-gradient(135deg, rgba(37, 99, 235, 0.2) 0%, rgba(59, 130, 246, 0.2) 100%)',
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
'& .MuiListItemIcon-root': { color: '#60a5fa', minWidth: 36 },
|
||||
'& .MuiDivider-root': { borderColor: 'rgba(37, 99, 235, 0.2)', my: 0.5 },
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleNewBlog}>
|
||||
<ListItemIcon><AddIcon fontSize="small" /></ListItemIcon>
|
||||
<ListItemText primary="New Blog" primaryTypographyProps={{ fontWeight: 600 }} />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleMyBlogs}>
|
||||
<ListItemIcon><ArticleIcon fontSize="small" /></ListItemIcon>
|
||||
<ListItemText primary="My Blogs" primaryTypographyProps={{ fontWeight: 500 }} />
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleHelp}>
|
||||
<ListItemIcon><HelpIcon fontSize="small" /></ListItemIcon>
|
||||
<ListItemText primary="Help & Docs" primaryTypographyProps={{ fontWeight: 500 }} />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Box sx={{ mt: 1 }}>
|
||||
<PhaseNavigation
|
||||
phases={phases}
|
||||
currentPhase={currentPhase}
|
||||
onPhaseClick={onPhaseClick}
|
||||
copilotKitAvailable={copilotKitAvailable}
|
||||
actionHandlers={actionHandlers}
|
||||
hasResearch={hasResearch}
|
||||
hasOutline={hasOutline}
|
||||
outlineConfirmed={outlineConfirmed}
|
||||
hasContent={hasContent}
|
||||
contentConfirmed={contentConfirmed}
|
||||
hasSEOAnalysis={hasSEOAnalysis}
|
||||
seoRecommendationsApplied={seoRecommendationsApplied}
|
||||
hasSEOMetadata={hasSEOMetadata}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderBar;
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user