[eric] ui: docked tour collapses to a minimal right-edge tab; restore serif font

This commit is contained in:
ciregenz
2026-05-24 12:33:22 -07:00
parent edec6e0f9c
commit 5fb4d02648
5 changed files with 153 additions and 127 deletions
@@ -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;
+2 -2
View File
@@ -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)',