extracted terminal to a widget
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
export type DockItem = {
|
||||
label: string;
|
||||
to: string;
|
||||
icon: LucideIcon;
|
||||
color: string;
|
||||
};
|
||||
|
||||
type DockProps = {
|
||||
items: DockItem[];
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const ICON_SIZE = 48;
|
||||
const ICON_GAP = 24;
|
||||
const DOCK_PADDING = 12;
|
||||
const MAX_SCALE = 1.5;
|
||||
const MAX_DISTANCE = 150;
|
||||
|
||||
const getScale = (mouseX: number | null, iconCenterX: number) => {
|
||||
if (mouseX === null) return 1;
|
||||
const distance = Math.abs(mouseX - iconCenterX);
|
||||
if (distance > MAX_DISTANCE) return 1;
|
||||
return 1 + (MAX_SCALE - 1) * Math.cos((distance / MAX_DISTANCE) * (Math.PI / 2));
|
||||
};
|
||||
|
||||
export const Dock = ({ items, className }: DockProps) => {
|
||||
const [mouseX, setMouseX] = useState<number | null>(null);
|
||||
const dockRef = useRef<HTMLDivElement | null>(null);
|
||||
const location = useLocation();
|
||||
|
||||
const isActive = (to: string) => location.pathname.startsWith(to);
|
||||
|
||||
const handleMouseMove = (ev: React.MouseEvent) => {
|
||||
const rect = dockRef.current?.getBoundingClientRect();
|
||||
if (rect) setMouseX(ev.clientX - rect.left);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => setMouseX(null);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dockRef}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
className={`fixed bottom-4 left-1/2 -translate-x-1/2 z-5 items-end gap-2 md:gap-6 px-2 py-1.5 md:px-3 md:py-2 rounded-2xl border backdrop-blur-xl shadow-lg ${className ?? 'flex'}`}
|
||||
style={{ backgroundColor: 'var(--dock-bg)', borderColor: 'var(--dock-border)' }}
|
||||
>
|
||||
{items.map((item, index) => {
|
||||
const iconCenter = DOCK_PADDING + index * (ICON_SIZE + ICON_GAP) + ICON_SIZE / 2;
|
||||
const scale = getScale(mouseX, iconCenter);
|
||||
const active = isActive(item.to);
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
className="group relative flex flex-col items-center"
|
||||
style={{
|
||||
transform: `scale(${scale})`,
|
||||
transformOrigin: 'bottom center',
|
||||
transition: 'transform 150ms ease-out',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="absolute -top-9 px-2 py-1 rounded-md text-white text-xs whitespace-nowrap hidden md:block opacity-0 group-hover:opacity-100 transition-opacity duration-150 pointer-events-none"
|
||||
style={{ backgroundColor: 'var(--dock-tooltip-bg)' }}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
<div
|
||||
className="w-10 h-10 md:w-12 md:h-12 rounded-xl flex items-center justify-center transition-all"
|
||||
style={{
|
||||
background: active ? `${item.color}55` : `${item.color}30`,
|
||||
boxShadow: active ? `0 0 12px ${item.color}30` : 'none',
|
||||
}}
|
||||
>
|
||||
<item.icon className="h-5 w-5 md:h-6 md:w-6" style={{ color: active ? item.color : `${item.color}cc` }} />
|
||||
</div>
|
||||
{active && (
|
||||
<div className="absolute -bottom-1.5 w-1.5 h-1.5 rounded-full" style={{ background: item.color }} />
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
import { Terminal, TerminalSquare, FileText, FolderOpen } from 'lucide-react';
|
||||
import { Sparkles, ClipboardList, ScrollText, Workflow } from 'lucide-react';
|
||||
|
||||
export const dockItems: DockItem[] = [
|
||||
{ label: 'Chat', to: '/chat', icon: Terminal, color: '#60a5fa' },
|
||||
{ label: 'Files', to: '/files', icon: FolderOpen, color: '#fbbf24' },
|
||||
{ label: 'Terminal', to: '/terminal', icon: TerminalSquare, color: '#34d399' },
|
||||
{ label: 'Plans', to: '/plans', icon: FileText, color: '#f472b6' },
|
||||
{ label: 'Skills', to: '/skills', icon: Sparkles, color: '#c084fc' },
|
||||
{ label: 'Tasks', to: '/tasks', icon: ClipboardList, color: '#fb923c' },
|
||||
{ label: 'Processes', to: '/processes', icon: Workflow, color: '#2dd4bf' },
|
||||
{ label: 'Logs', to: '/task-logs', icon: ScrollText, color: '#94a3b8' },
|
||||
];
|
||||
Reference in New Issue
Block a user