mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-02 14:28:59 +02:00
[eric] ui: docked tour collapses to a minimal right-edge tab; restore serif font
This commit is contained in:
@@ -33,7 +33,6 @@ import CircularProgress from '@mui/material/CircularProgress';
|
||||
// Settings modal lazy-loaded so its 2.3K LOC + Stripe/OAuth helpers don't ship on first paint.
|
||||
const Settings = React.lazy(() => import('@/app/pages/Settings/Settings'));
|
||||
import DynamicIsland from '@/app/components/DynamicIsland';
|
||||
import OnboardingSidebarEntry from '@/app/components/Onboarding/OnboardingSidebarEntry';
|
||||
import Dashboard from '@/app/pages/Dashboard/Dashboard';
|
||||
import DashboardHost from '@/app/components/Layout/DashboardHost';
|
||||
import { useLastDashboardId } from '@/shared/hooks/useLastDashboardId';
|
||||
@@ -987,8 +986,6 @@ const AppShell: React.FC = () => {
|
||||
|
||||
</Box>
|
||||
|
||||
<OnboardingSidebarEntry />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
px: 1,
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// Docked onboarding home: a small, quiet handle on the right edge that reopens the tour.
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Box, IconButton, Typography, CircularProgress } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { useOnboardingProgress } from './hooks/useOnboardingProgress';
|
||||
import { STEPS } from './steps';
|
||||
import { report } from './telemetry';
|
||||
|
||||
const OnboardingDockedTab: React.FC = () => {
|
||||
const c = useClaudeTokens();
|
||||
const progress = useOnboardingProgress();
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
const total = STEPS.length;
|
||||
const done = progress.completedSteps.length;
|
||||
// Hide while the cursor is driving a step, same as the panel does.
|
||||
const show =
|
||||
progress.initialized &&
|
||||
progress.panelMode === 'docked' &&
|
||||
!progress.running &&
|
||||
done < total;
|
||||
const pct = total > 0 ? (done / total) * 100 : 0;
|
||||
|
||||
const reopen = () => {
|
||||
report('panel_reopened', { from: 'docked_tab' });
|
||||
progress.setPanelMode('expanded');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
top: '50%',
|
||||
right: 0,
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 1200,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{show && (
|
||||
<motion.div
|
||||
key="onboarding-docked-tab"
|
||||
initial={{ opacity: 0, x: 16 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: 16 }}
|
||||
transition={{ type: 'spring', stiffness: 320, damping: 32 }}
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
>
|
||||
<Box
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Resume setup"
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onClick={reopen}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
reopen();
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: hovered ? 0.7 : 0,
|
||||
cursor: 'pointer',
|
||||
bgcolor: c.bg.surface,
|
||||
border: `1px solid ${c.border.subtle}`,
|
||||
borderRight: 'none',
|
||||
borderTopLeftRadius: 9,
|
||||
borderBottomLeftRadius: 9,
|
||||
pl: hovered ? 1 : 0.5,
|
||||
pr: 0.5,
|
||||
py: 0.5,
|
||||
boxShadow: hovered ? c.shadow.md : '-1px 0 5px rgba(0,0,0,0.07)',
|
||||
transition:
|
||||
'gap 0.18s ease, padding 0.18s ease, box-shadow 0.18s ease, background 0.15s',
|
||||
'&:hover': { bgcolor: c.bg.elevated ?? c.bg.surface },
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: hovered ? 20 : 0,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
transition: 'width 0.18s ease',
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="Dismiss setup"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
report('panel_dismissed', { from: 'docked_tab' });
|
||||
progress.setPanelMode('hidden');
|
||||
}}
|
||||
sx={{
|
||||
p: 0.1,
|
||||
color: c.text.tertiary,
|
||||
'&:hover': { color: c.text.primary },
|
||||
}}
|
||||
>
|
||||
<CloseIcon sx={{ fontSize: 13 }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: hovered ? 72 : 0,
|
||||
opacity: hovered ? 1 : 0,
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
transition: 'width 0.18s ease, opacity 0.18s ease',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ fontSize: 12.5, fontWeight: 600, color: c.text.secondary }}
|
||||
>
|
||||
Finish setup
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
|
||||
<CircularProgress
|
||||
variant="determinate"
|
||||
value={100}
|
||||
size={18}
|
||||
thickness={3.5}
|
||||
sx={{ color: `${c.text.tertiary}26` }}
|
||||
/>
|
||||
<CircularProgress
|
||||
variant="determinate"
|
||||
value={pct}
|
||||
size={18}
|
||||
thickness={3.5}
|
||||
sx={{ color: c.accent.primary, position: 'absolute', left: 0 }}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingDockedTab;
|
||||
@@ -17,6 +17,7 @@ import AgenticCursor, { type AgenticCursorHandle } from './ac/AgenticCursor';
|
||||
import { onboardingDirector } from './OnboardingDirector';
|
||||
import { STEPS } from './steps';
|
||||
import OnboardingPanel from './OnboardingPanel';
|
||||
import OnboardingDockedTab from './OnboardingDockedTab';
|
||||
import { onboardingBus } from './eventBus';
|
||||
import { report } from './telemetry';
|
||||
|
||||
@@ -217,6 +218,7 @@ const OnboardingRoot: React.FC = () => {
|
||||
<>
|
||||
<AgenticCursor ref={acRef} />
|
||||
<OnboardingPanel />
|
||||
<OnboardingDockedTab />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
// Docked onboarding home: a compact row at the bottom of the sidebar that springs the tour back open.
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
Box,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
IconButton,
|
||||
Typography,
|
||||
CircularProgress,
|
||||
} from '@mui/material';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { useOnboardingProgress } from './hooks/useOnboardingProgress';
|
||||
import { STEPS } from './steps';
|
||||
import { report } from './telemetry';
|
||||
|
||||
const OnboardingSidebarEntry: React.FC = () => {
|
||||
const c = useClaudeTokens();
|
||||
const progress = useOnboardingProgress();
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
const total = STEPS.length;
|
||||
const done = progress.completedSteps.length;
|
||||
const show =
|
||||
progress.initialized && progress.panelMode === 'docked' && done < total;
|
||||
const pct = total > 0 ? (done / total) * 100 : 0;
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{show && (
|
||||
<motion.div
|
||||
key="onboarding-docked"
|
||||
initial={{ opacity: 0, scale: 0.85, y: 6 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.9, y: 6 }}
|
||||
transition={{ type: 'spring', stiffness: 380, damping: 30 }}
|
||||
>
|
||||
<Box sx={{ px: 1, pt: 1 }}>
|
||||
<ListItemButton
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onClick={() => {
|
||||
report('panel_reopened', { from: 'sidebar' });
|
||||
progress.setPanelMode('expanded');
|
||||
}}
|
||||
sx={{
|
||||
borderRadius: 1.5,
|
||||
py: 0.6,
|
||||
px: 1.25,
|
||||
bgcolor: `${c.accent.primary}0C`,
|
||||
'&:hover': { bgcolor: `${c.accent.primary}1A` },
|
||||
transition: 'background-color 0.15s',
|
||||
}}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{ minWidth: 32, position: 'relative', display: 'inline-flex' }}
|
||||
>
|
||||
<CircularProgress
|
||||
variant="determinate"
|
||||
value={100}
|
||||
size={20}
|
||||
thickness={4}
|
||||
sx={{ color: `${c.text.tertiary}33` }}
|
||||
/>
|
||||
<CircularProgress
|
||||
variant="determinate"
|
||||
value={pct}
|
||||
size={20}
|
||||
thickness={4}
|
||||
sx={{ color: c.accent.primary, position: 'absolute', left: 0 }}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Finish setup"
|
||||
sx={{
|
||||
'& .MuiListItemText-primary': {
|
||||
color: c.text.primary,
|
||||
fontSize: '0.82rem',
|
||||
fontWeight: 500,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{hovered ? (
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label="Dismiss setup"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
report('panel_dismissed', { from: 'sidebar' });
|
||||
progress.setPanelMode('hidden');
|
||||
}}
|
||||
sx={{
|
||||
p: 0.2,
|
||||
color: c.text.tertiary,
|
||||
'&:hover': { color: c.text.primary },
|
||||
}}
|
||||
>
|
||||
<CloseIcon sx={{ fontSize: 15 }} />
|
||||
</IconButton>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.3 }}>
|
||||
<Typography
|
||||
sx={{ fontSize: '0.72rem', color: c.text.muted, fontWeight: 500 }}
|
||||
>
|
||||
{done}/{total}
|
||||
</Typography>
|
||||
<ChevronRightIcon sx={{ fontSize: 16, color: c.text.tertiary }} />
|
||||
</Box>
|
||||
)}
|
||||
</ListItemButton>
|
||||
</Box>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingSidebarEntry;
|
||||
@@ -56,7 +56,7 @@ export const lightTokens: ClaudeTokens = {
|
||||
},
|
||||
user: { bubble: '#DDD9CE' },
|
||||
font: {
|
||||
sans: '"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||
sans: '"Anthropic Sans", ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
|
||||
mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
||||
},
|
||||
transition: 'all 150ms cubic-bezier(0.165, 0.85, 0.45, 1)',
|
||||
@@ -107,7 +107,7 @@ export const darkTokens: ClaudeTokens = {
|
||||
},
|
||||
user: { bubble: '#393937' },
|
||||
font: {
|
||||
sans: '"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||
sans: '"Anthropic Sans", ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
|
||||
mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
||||
},
|
||||
transition: 'all 150ms cubic-bezier(0.165, 0.85, 0.45, 1)',
|
||||
|
||||
Reference in New Issue
Block a user