diff --git a/frontend/src/app/components/Layout/AppShell.tsx b/frontend/src/app/components/Layout/AppShell.tsx index 6064e4df..f95b132a 100644 --- a/frontend/src/app/components/Layout/AppShell.tsx +++ b/frontend/src/app/components/Layout/AppShell.tsx @@ -23,7 +23,7 @@ import { LayoutGrid } from 'lucide-react'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { Settings as LucideSettings } from 'lucide-react'; import { Palette } from 'lucide-react'; -import { ArrowLeft, ArrowRight, Plus } from 'lucide-react'; +import { ArrowLeft, ArrowRight, Plus, Sparkles } from 'lucide-react'; import { AnimatedPanelLeft } from './animatedIcons'; import RestartAltIcon from '@mui/icons-material/RestartAlt'; import SystemUpdateAltIcon from '@mui/icons-material/SystemUpdateAlt'; @@ -151,6 +151,12 @@ const AppShell: React.FC = () => { const d = s.settings.data as any; return !!(d && d.connection_mode === 'free-trial' && d.free_trial_token); }); + // Trial just ran dry (had an allotment, now 0, off the free lane): nudge a connect, but warmly, + // an upsell after the win, never the red error wall. Runs refill, so frame it as "for now". + const freeTrialSpent = useAppSelector((s) => { + const d = s.settings.data as any; + return !!(d && (d.free_trial_runs_limit ?? 0) > 0 && d.free_trial_remaining === 0 && d.connection_mode !== 'free-trial'); + }); // Hold the banner until the boot free-trial mint settles, else a brand-new user sees it // flash red for the ~1-3s the trial takes to arm. (Offline shows immediately, it's its own signal.) const freeTrialArmSettled = useAppSelector((s) => s.settings.freeTrialArmSettled); @@ -512,8 +518,8 @@ const AppShell: React.FC = () => { gap: 1.5, px: 2, py: 0.6, - bgcolor: 'rgba(239, 68, 68, 0.08)', - borderBottom: '1px solid rgba(239, 68, 68, 0.18)', + bgcolor: isOnline && freeTrialSpent ? `${c.accent.primary}14` : 'rgba(239, 68, 68, 0.08)', + borderBottom: isOnline && freeTrialSpent ? `1px solid ${c.accent.primary}30` : '1px solid rgba(239, 68, 68, 0.18)', flexShrink: 0, animation: showWarningBanner ? 'warning-fade-in 0.4s ease-out' : undefined, '@keyframes warning-fade-in': { @@ -522,10 +528,32 @@ const AppShell: React.FC = () => { }, }} > - - + {isOnline && freeTrialSpent + ? + : } + {!isOnline ? 'No internet connection; agents cannot reach AI models or external services' + : freeTrialSpent + ? ( + <> + Your free runs are used up for now.{' '} + dispatch(openSettingsModal('models'))} + sx={{ + textDecoration: 'underline', + cursor: 'pointer', + fontWeight: 600, + '&:hover': { opacity: 0.8 }, + transition: 'opacity 0.15s', + }} + > + Connect your own Claude, ChatGPT, or Gemini + + {' '}to keep going. + + ) : ( <> No AI model connected.{' '}