mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-28 12:34:50 +02:00
[eric] frontend: polish subscription + pro cards (hover lift, hover-reveal disconnect)
This commit is contained in:
@@ -105,7 +105,7 @@ const OpenSwarmProCard: React.FC = () => {
|
||||
desc: 'Your subscription ended. Pick a plan to get back in.',
|
||||
cta: 'Resubscribe',
|
||||
title: 'Resubscribe to OpenSwarm Pro',
|
||||
subtitle: 'Keep using Claude Sonnet, Opus, and Haiku without a Claude account.',
|
||||
subtitle: 'Keep using the latest Claude models without a Claude account.',
|
||||
currentPlan: lastPlan,
|
||||
};
|
||||
}
|
||||
@@ -119,7 +119,7 @@ const OpenSwarmProCard: React.FC = () => {
|
||||
};
|
||||
}
|
||||
return {
|
||||
desc: 'Claude Sonnet, Opus, and Haiku. No Claude account needed.',
|
||||
desc: 'The latest Claude models. No Claude account needed.',
|
||||
cta: 'Subscribe',
|
||||
title: 'Choose your plan',
|
||||
subtitle: 'One subscription, we handle everything behind the scenes. Cancel anytime.',
|
||||
@@ -129,22 +129,26 @@ const OpenSwarmProCard: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${c.border.subtle}` }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Box sx={{
|
||||
p: 1.5, borderRadius: `${c.radius.md}px`, border: `1px solid ${c.border.subtle}`,
|
||||
transition: c.transition,
|
||||
'&:hover': { borderColor: c.border.medium, transform: 'translateY(-1px)', boxShadow: c.shadow.sm },
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: 0 }}>
|
||||
<Box sx={{ width: 8, height: 8, borderRadius: '50%', flexShrink: 0, bgcolor: c.border.medium }} />
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.8 }}>
|
||||
<Box sx={{ minWidth: 0 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.7 }}>
|
||||
<Typography sx={{ fontSize: '0.78rem', fontWeight: 600, color: c.text.primary }}>
|
||||
OpenSwarm Pro
|
||||
</Typography>
|
||||
<Box sx={{ px: 0.7, py: 0.15, borderRadius: 999, bgcolor: `${c.accent.primary}15` }}>
|
||||
<Typography sx={{ fontSize: '0.6rem', color: c.accent.primary, fontWeight: 600 }}>
|
||||
<Box sx={{ px: 0.7, py: 0.15, borderRadius: 999, bgcolor: `${c.accent.primary}12` }}>
|
||||
<Typography sx={{ fontSize: '0.58rem', color: c.accent.primary, fontWeight: 600, letterSpacing: '0.03em' }}>
|
||||
RECOMMENDED
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Typography sx={{ fontSize: '0.65rem', color: c.text.muted }}>
|
||||
<Typography noWrap sx={{ fontSize: '0.65rem', color: c.text.muted }}>
|
||||
{copy.desc}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -9,59 +9,71 @@ import type { SubscriptionProvider } from './subscriptionProviders';
|
||||
const SubscriptionCard: React.FC<{ provider: SubscriptionProvider; connected: boolean; onConnect: () => void; onDisconnect: () => void; connecting: boolean; userCode?: string; disconnecting?: boolean }> = ({ provider, connected, onConnect, onDisconnect, connecting, userCode, disconnecting }) => {
|
||||
const c = useClaudeTokens();
|
||||
const isPreview = (provider as any).preview;
|
||||
const dotColor = connected ? c.status.success : connecting ? c.accent.primary : c.border.medium;
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
p: 1.5, borderRadius: `${c.radius.md}px`,
|
||||
border: `1px solid ${connected ? c.status.success + '30' : connecting ? c.accent.primary + '30' : c.border.subtle}`,
|
||||
bgcolor: connected ? `${c.status.success}04` : connecting ? `${c.accent.primary}04` : 'transparent',
|
||||
bgcolor: connected ? `${c.status.success}06` : connecting ? `${c.accent.primary}06` : 'transparent',
|
||||
opacity: isPreview ? 0.5 : 1,
|
||||
transition: 'all 0.3s ease',
|
||||
transition: c.transition,
|
||||
'&:hover': isPreview ? {} : {
|
||||
borderColor: connected ? c.status.success + '4d' : c.border.medium,
|
||||
transform: 'translateY(-1px)',
|
||||
boxShadow: c.shadow.sm,
|
||||
},
|
||||
// affirm "Connected" at rest, reveal "Disconnect" on hover so the undo never shouts
|
||||
'&:hover .sub-rest': { opacity: 0 },
|
||||
'&:hover .sub-undo': { opacity: 1, pointerEvents: 'auto' },
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: 0 }}>
|
||||
<Box sx={{
|
||||
width: 8, height: 8, borderRadius: '50%', flexShrink: 0,
|
||||
bgcolor: connected ? c.status.success : connecting ? c.accent.primary : c.border.medium,
|
||||
width: 8, height: 8, borderRadius: '50%', flexShrink: 0, bgcolor: dotColor,
|
||||
transition: 'background-color 0.3s ease',
|
||||
...(connecting ? {
|
||||
animation: 'pulse-dot 1.5s ease-in-out infinite',
|
||||
'@keyframes pulse-dot': {
|
||||
'0%, 100%': { opacity: 1, transform: 'scale(1)' },
|
||||
'50%': { opacity: 0.4, transform: 'scale(0.8)' },
|
||||
},
|
||||
animation: 'sub-pulse 1.4s ease-in-out infinite',
|
||||
'@keyframes sub-pulse': { '0%, 100%': { opacity: 1 }, '50%': { opacity: 0.35 } },
|
||||
} : {}),
|
||||
}} />
|
||||
<Box>
|
||||
<Box sx={{ minWidth: 0 }}>
|
||||
<Typography sx={{ fontSize: '0.78rem', fontWeight: 600, color: c.text.primary }}>{provider.name}</Typography>
|
||||
<Typography sx={{ fontSize: '0.65rem', color: connecting ? c.accent.primary : c.text.muted, transition: 'color 0.3s ease' }}>
|
||||
<Typography noWrap sx={{ fontSize: '0.65rem', color: connecting ? c.accent.primary : c.text.muted, transition: 'color 0.2s ease' }}>
|
||||
{connecting ? 'Waiting for authorization...' : provider.desc}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{isPreview ? (
|
||||
<Typography sx={{ fontSize: '0.65rem', color: c.text.ghost, fontStyle: 'italic' }}>
|
||||
<Typography sx={{ fontSize: '0.65rem', color: c.text.ghost, fontStyle: 'italic', flexShrink: 0 }}>
|
||||
Coming soon
|
||||
</Typography>
|
||||
) : connected ? (
|
||||
disconnecting ? (
|
||||
<CircularProgress size={14} sx={{ color: c.text.ghost }} />
|
||||
) : (
|
||||
<Typography onClick={onDisconnect} sx={{ fontSize: '0.68rem', color: c.text.tertiary, cursor: 'pointer', '&:hover': { color: c.status.error }, transition: 'color 0.2s ease' }}>
|
||||
Disconnect
|
||||
</Typography>
|
||||
<Box sx={{ position: 'relative', flexShrink: 0, minWidth: 72, height: 16 }}>
|
||||
<Typography className="sub-rest" sx={{ position: 'absolute', right: 0, top: 0, fontSize: '0.68rem', fontWeight: 500, color: c.status.success, transition: 'opacity 0.18s ease' }}>
|
||||
Connected
|
||||
</Typography>
|
||||
<Typography className="sub-undo" onClick={onDisconnect} sx={{ position: 'absolute', right: 0, top: 0, fontSize: '0.68rem', color: c.text.tertiary, cursor: 'pointer', opacity: 0, pointerEvents: 'none', transition: 'opacity 0.18s ease', '&:hover': { color: c.status.error } }}>
|
||||
Disconnect
|
||||
</Typography>
|
||||
</Box>
|
||||
)
|
||||
) : connecting && userCode ? (
|
||||
<Box sx={{ textAlign: 'right' }}>
|
||||
<Box sx={{ textAlign: 'right', flexShrink: 0 }}>
|
||||
<Typography sx={{ fontSize: '0.68rem', color: c.text.muted }}>Enter code:</Typography>
|
||||
<Typography sx={{ fontSize: '0.85rem', fontWeight: 700, color: c.accent.primary, fontFamily: 'monospace', letterSpacing: '0.1em' }}>{userCode}</Typography>
|
||||
<Typography sx={{ fontSize: '0.85rem', fontWeight: 700, color: c.accent.primary, fontFamily: c.font.mono, letterSpacing: '0.1em' }}>{userCode}</Typography>
|
||||
</Box>
|
||||
) : connecting ? (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.8 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.8, flexShrink: 0 }}>
|
||||
<CircularProgress size={14} sx={{ color: c.accent.primary }} />
|
||||
<Typography sx={{ fontSize: '0.68rem', color: c.accent.primary }}>Connecting...</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Button onClick={onConnect} variant="outlined" size="small" sx={{ textTransform: 'none', fontSize: '0.7rem', color: c.text.primary, borderColor: c.border.medium, minWidth: 70, '&:hover': { borderColor: c.accent.primary }, transition: 'all 0.2s ease' }}>
|
||||
<Button onClick={onConnect} variant="outlined" size="small" sx={{ textTransform: 'none', fontSize: '0.7rem', fontWeight: 600, color: c.text.primary, borderColor: c.border.medium, borderRadius: `${c.radius.sm}px`, minWidth: 72, flexShrink: 0, '&:hover': { borderColor: c.accent.primary, bgcolor: `${c.accent.primary}0a` }, transition: 'all 0.2s ease' }}>
|
||||
Connect
|
||||
</Button>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user