diff --git a/frontend/src/app/pages/Dashboard/canvas/DashboardGlyph.tsx b/frontend/src/app/pages/Dashboard/canvas/DashboardGlyph.tsx index 71aa8dc3..d59e2e91 100644 --- a/frontend/src/app/pages/Dashboard/canvas/DashboardGlyph.tsx +++ b/frontend/src/app/pages/Dashboard/canvas/DashboardGlyph.tsx @@ -4,7 +4,7 @@ import type { LucideIcon } from 'lucide-react'; import { Timer, Clock, Calendar, ListChecks, Wallet, BarChart3, Megaphone, Code, Terminal, Palette, PenLine, FileText, BookOpen, FlaskConical, Mail, - MessageSquare, Plane, Map, Dumbbell, HeartPulse, Utensils, ChefHat, Coffee, + MessageCircle, Plane, Map, Dumbbell, HeartPulse, Utensils, ChefHat, Coffee, Music, Video, Image, Camera, ShoppingCart, Bot, Gamepad2, Home, Shield, Users, Scale, Building2, Newspaper, Briefcase, Rocket, Globe, Database, Wrench, Lightbulb, Target, Trophy, Bell, Folder, Package, Truck, @@ -29,7 +29,7 @@ const KEYWORDS: Record = { research: BookOpen, study: BookOpen, learning: BookOpen, course: BookOpen, education: BookOpen, school: BookOpen, exam: BookOpen, thesis: BookOpen, science: FlaskConical, lab: FlaskConical, experiment: FlaskConical, chemistry: FlaskConical, biology: FlaskConical, physics: FlaskConical, mail: Mail, email: Mail, inbox: Mail, outreach: Mail, newsletter: Mail, - chat: MessageSquare, message: MessageSquare, messaging: MessageSquare, support: MessageSquare, dm: MessageSquare, + chat: MessageCircle, message: MessageCircle, messaging: MessageCircle, support: MessageCircle, dm: MessageCircle, travel: Plane, trip: Plane, flight: Plane, vacation: Plane, tour: Plane, itinerary: Plane, map: Map, location: Map, geo: Map, route: Map, navigation: Map, fitness: Dumbbell, workout: Dumbbell, gym: Dumbbell, exercise: Dumbbell, training: Dumbbell, diff --git a/frontend/src/app/pages/Dashboard/desktop/DesktopDock.tsx b/frontend/src/app/pages/Dashboard/desktop/DesktopDock.tsx index dba00d28..9dd9cb24 100644 --- a/frontend/src/app/pages/Dashboard/desktop/DesktopDock.tsx +++ b/frontend/src/app/pages/Dashboard/desktop/DesktopDock.tsx @@ -8,7 +8,7 @@ import EventRepeatIcon from '@mui/icons-material/EventRepeat'; import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined'; import HistoryIcon from '@mui/icons-material/History'; import { pickIcon } from '../canvas/DashboardGlyph'; -import { MessageSquare } from 'lucide-react'; +import { MessageCircle } from 'lucide-react'; import { openWorkflowsApp } from '@/shared/state/dashboardLayoutSlice'; import SettingsIcon from '@mui/icons-material/Settings'; import AppsRoundedIcon from '@mui/icons-material/AppsRounded'; @@ -98,6 +98,37 @@ function DesktopDock({ onAddNote, }: DesktopDockProps): React.ReactElement | null { const dispatch = useAppDispatch(); + const dockBodyRef = useRef(null); + // macOS Dock magnification: the tile under the cursor grows on a bell curve and its neighbors + // SLIDE AWAY to make room (no horizontal pop-out, the part that read as wobble). Each tile that + // grows by `extra` pushes every tile past it by extra/2, so the column opens up smoothly. + const applyDockMagnify = useCallback((clientY: number | null) => { + const root = dockBodyRef.current; + if (!root) return; + const tiles = Array.from(root.querySelectorAll('.osw-dock-tile')); + if (tiles.length === 0) return; + if (clientY == null) { + tiles.forEach((t) => { t.style.transform = ''; t.style.zIndex = ''; }); + return; + } + const rootTop = root.getBoundingClientRect().top; + const cy = clientY - rootTop; + const BOOST = 0.5; // hovered tile grows to 1.5x + const FALLOFF = 44; // px spread of the bell curve (how many neighbors react) + const bases = tiles.map((t) => t.offsetTop + t.offsetHeight / 2); + const scales = bases.map((b) => 1 + BOOST * Math.exp(-(((cy - b) / FALLOFF) ** 2))); + const extra = scales.map((s) => TILE * (s - 1)); + tiles.forEach((t, i) => { + let shift = 0; + for (let j = 0; j < tiles.length; j++) { + if (j === i) continue; + shift += (extra[j] / 2) * Math.sign(bases[i] - bases[j]); + } + t.style.transform = `translateY(${shift.toFixed(1)}px) scale(${scales[i].toFixed(3)})`; + t.style.transformOrigin = 'left center'; + t.style.zIndex = String(10 + Math.round((scales[i] - 1) * 100)); + }); + }, []); const [hovered, setHovered] = useState<{ id: string; top: number } | null>(null); const [liveShot, setLiveShot] = useState<{ id: string; dataUrl: string } | null>(null); const hoverTimer = useRef(null); @@ -108,7 +139,7 @@ function DesktopDock({ const session = sessions[card.session_id]; if (!session) continue; const title = displayChatTitle(session); - const ChatIcon = pickIcon(title) || MessageSquare; + const ChatIcon = pickIcon(title) || MessageCircle; list.push({ id: card.session_id, label: title, @@ -202,7 +233,9 @@ function DesktopDock({ return ( applyDockMagnify(e.clientY)} + onMouseLeave={() => { endHover(); applyDockMagnify(null); }} sx={{ position: 'absolute', left: 12, @@ -219,15 +252,13 @@ function DesktopDock({ backdropFilter: 'blur(20px) saturate(160%)', WebkitBackdropFilter: 'blur(20px) saturate(160%)', boxShadow: '0 8px 28px rgba(0,0,0,0.35)', - // One tasteful hover for every tile: a spring-y lift (replaces the fisheye that read as wobble), - // and every tile is a rounder squircle so they feel like real app icons, not lame little squares. + // Rounder squircle tiles (real app icons, not lame squares). The magnify transform is set + // imperatively per-frame by applyDockMagnify; a short ease smooths the chase + the reset. '& .osw-dock-tile': { borderRadius: '12px', - transition: 'transform 0.16s cubic-bezier(.34,1.56,.64,1)', + transition: 'transform 0.12s ease-out', willChange: 'transform', }, - '& .osw-dock-tile:hover': { transform: 'scale(1.16)' }, - '& .osw-dock-tile:active': { transform: 'scale(1.04)' }, }} > {entries.map((entry) => {