Redesign overlay to Windows Start Menu style - compact bottom bar with expand

- Windows-style search bar with rounded corners
- Positioned at bottom center of screen
- Logo button to expand/collapse (like Windows Start)
- Search input with type-to-search
- Expanded view shows Quick Access grid (6 items)
- Categorized list (Tools, Plugins, System)
- Keyboard shortcuts displayed
- Active plugin indicators
- Click-through when not focused
- Session timer in header
- Smooth expand/collapse animations
This commit is contained in:
Aether 2026-02-23 21:32:13 +00:00
parent b60b67f508
commit 5ec15e12f3
No known key found for this signature in database
GPG Key ID: 95AFEE837E39AFD2
2 changed files with 309 additions and 210 deletions

View File

@ -1,4 +1,4 @@
use tauri::{AppHandle, Manager, Window, WindowBuilder, WindowUrl}; use tauri::{AppHandle, Manager, Window, WindowBuilder, WindowUrl, Position, PhysicalPosition};
use tracing::info; use tracing::info;
pub fn create_main_window(app: &AppHandle) -> Window { pub fn create_main_window(app: &AppHandle) -> Window {
@ -16,43 +16,39 @@ pub fn create_main_window(app: &AppHandle) -> Window {
} }
pub fn setup_main_window(window: &Window) { pub fn setup_main_window(window: &Window) {
// Additional window setup if needed
info!("Main window setup complete"); info!("Main window setup complete");
} }
pub fn create_overlay_window(app: &AppHandle) { pub fn create_overlay_window(app: &AppHandle) {
// Get primary monitor size
let monitor = app.primary_monitor().unwrap();
let monitor_size = monitor.as_ref().map(|m| m.size()).unwrap_or_else(|| tauri::PhysicalSize::new(1920, 1080));
// Position overlay at bottom center like Windows Start Menu
let window_width = 720.0;
let window_height = 600.0; // Expanded height
let x = (monitor_size.width as f64 - window_width) / 2.0;
let y = monitor_size.height as f64 - window_height - 20.0; // 20px from bottom
let window = WindowBuilder::new( let window = WindowBuilder::new(
app, app,
"overlay", "overlay",
WindowUrl::App("/#/overlay".into()) WindowUrl::App("/#/overlay".into())
) )
.title("EU-Utility Overlay") .title("EU-Utility Overlay")
.inner_size(400.0, 600.0) .inner_size(window_width, window_height)
.position(100.0, 100.0) .position(x, y)
.transparent(true) .transparent(true)
.decorations(false) .decorations(false)
.always_on_top(true) .always_on_top(true)
.skip_taskbar(true) .skip_taskbar(true)
.visible(false) .visible(false)
// Enable click-through when not focused
.focus()
.build() .build()
.expect("Failed to create overlay window"); .expect("Failed to create overlay window");
// Make window click-through when not focused info!("Overlay window created at bottom center (Windows-style)");
// This allows clicking through to the game when overlay is not active
#[cfg(target_os = "windows")]
{
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::{SetWindowLongW, GetWindowLongW, GWL_EXSTYLE};
use windows::Win32::UI::WindowsAndMessaging::{WS_EX_LAYERED, WS_EX_TRANSPARENT};
unsafe {
let hwnd = HWND(window.hwnd().unwrap().0 as *mut _);
let ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
SetWindowLongW(hwnd, GWL_EXSTYLE, ex_style | WS_EX_LAYERED.0 as i32 | WS_EX_TRANSPARENT.0 as i32);
}
}
info!("Overlay window created with transparency and click-through");
} }
pub fn toggle_overlay_window(app: &AppHandle) { pub fn toggle_overlay_window(app: &AppHandle) {
@ -61,15 +57,42 @@ pub fn toggle_overlay_window(app: &AppHandle) {
if is_visible { if is_visible {
window.hide().ok(); window.hide().ok();
// Re-enable click-through
enable_click_through(&window, true);
} else { } else {
window.show().ok(); window.show().ok();
window.set_always_on_top(true).ok(); window.set_always_on_top(true).ok();
window.set_focus().ok(); window.set_focus().ok();
// Disable click-through when active
enable_click_through(&window, false);
} }
} else { } else {
create_overlay_window(app); create_overlay_window(app);
if let Some(window) = app.get_window("overlay") { if let Some(window) = app.get_window("overlay") {
window.show().ok(); window.show().ok();
enable_click_through(&window, false);
}
}
}
fn enable_click_through(window: &Window, enable: bool) {
#[cfg(target_os = "windows")]
unsafe {
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::{SetWindowLongW, GetWindowLongW, GWL_EXSTYLE};
use windows::Win32::UI::WindowsAndMessaging::{WS_EX_LAYERED, WS_EX_TRANSPARENT, WS_EX_NOACTIVATE};
let hwnd = HWND(window.hwnd().unwrap().0 as *mut _);
let ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
if enable {
// Enable click-through
SetWindowLongW(hwnd, GWL_EXSTYLE,
ex_style | WS_EX_LAYERED.0 as i32 | WS_EX_TRANSPARENT.0 as i32);
} else {
// Disable click-through
SetWindowLongW(hwnd, GWL_EXSTYLE,
ex_style & !(WS_EX_TRANSPARENT.0 as i32));
} }
} }
} }
@ -79,34 +102,13 @@ pub fn show_overlay_window(app: &AppHandle) {
window.show().ok(); window.show().ok();
window.set_always_on_top(true).ok(); window.set_always_on_top(true).ok();
window.set_focus().ok(); window.set_focus().ok();
enable_click_through(&window, false);
} }
} }
pub fn hide_overlay_window(app: &AppHandle) { pub fn hide_overlay_window(app: &AppHandle) {
if let Some(window) = app.get_window("overlay") { if let Some(window) = app.get_window("overlay") {
window.hide().ok(); window.hide().ok();
enable_click_through(&window, true);
} }
} }
pub fn setup_game_window_detection(app: &AppHandle) {
// Set up a timer to check if game window is focused
// Only show overlay when game is focused (or on hotkey)
let app_handle = app.clone();
std::thread::spawn(move || {
loop {
std::thread::sleep(std::time::Duration::from_secs(1));
// Check if overlay should auto-hide when game loses focus
// This is optional behavior - overlay can stay visible
if let Some(overlay) = app_handle.get_window("overlay") {
if let Ok(is_visible) = overlay.is_visible() {
if is_visible {
// Optional: Auto-hide when game not focused
// Check game focus here and hide if needed
}
}
}
}
});
}

View File

@ -1,50 +1,54 @@
import { useState, useEffect } from 'react' import { useState, useEffect, useRef } from 'react'
import { invoke } from '@tauri-apps/api/tauri' import { invoke } from '@tauri-apps/api/tauri'
import { listen } from '@tauri-apps/api/event' import { listen } from '@tauri-apps/api/event'
import { import {
Search,
LayoutDashboard, LayoutDashboard,
Calculator,
Target, Target,
TrendingUp, TrendingUp,
Calculator,
Settings, Settings,
X, X,
GripHorizontal, ChevronUp,
GripVertical,
Zap, Zap,
Search,
Grid,
Crosshair,
Clock,
BarChart3, BarChart3,
Layers Clock,
Layers,
Maximize2,
Minimize2,
Grid,
Command
} from 'lucide-react' } from 'lucide-react'
interface QuickAction { interface MenuItem {
id: string id: string
icon: React.ReactNode icon: React.ReactNode
label: string label: string
description?: string
hotkey?: string hotkey?: string
onClick: () => void onClick: () => void
} category: 'tools' | 'plugins' | 'system'
interface PluginWidget {
id: string
name: string
icon: React.ReactNode
active: boolean
onToggle: () => void
} }
export default function Overlay() { export default function Overlay() {
const [isVisible, setIsVisible] = useState(false) const [isVisible, setIsVisible] = useState(false)
const [activePlugins, setActivePlugins] = useState<string[]>([]) const [isExpanded, setIsExpanded] = useState(false)
const [sessionTime, setSessionTime] = useState(0)
const [currentPED, setCurrentPED] = useState(0)
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const [activePlugins, setActivePlugins] = useState<string[]>(['loot', 'skills'])
const [sessionTime, setSessionTime] = useState(0)
const inputRef = useRef<HTMLInputElement>(null)
useEffect(() => { useEffect(() => {
// Listen for overlay toggle // Listen for overlay toggle
const unlisten = listen('overlay:toggle', () => { const unlisten = listen('overlay:toggle', () => {
setIsVisible(prev => !prev) setIsVisible(prev => {
const newVisible = !prev
if (newVisible && inputRef.current) {
setTimeout(() => inputRef.current?.focus(), 100)
}
return newVisible
})
}) })
// Session timer // Session timer
@ -58,206 +62,299 @@ export default function Overlay() {
} }
}, []) }, [])
useEffect(() => {
if (isVisible && !isExpanded && inputRef.current) {
inputRef.current.focus()
}
}, [isVisible])
const formatTime = (seconds: number) => { const formatTime = (seconds: number) => {
const h = Math.floor(seconds / 3600) const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60) const m = Math.floor((seconds % 3600) / 60)
const s = seconds % 60 const s = seconds % 60
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}` return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
} }
const quickActions: QuickAction[] = [ const menuItems: MenuItem[] = [
{ {
id: 'calculator', id: 'calculator',
icon: <Calculator className="w-5 h-5" />, icon: <Calculator className="w-5 h-5" />,
label: 'Calc', label: 'Calculator',
description: 'Quick calculations',
hotkey: 'Ctrl+Shift+C', hotkey: 'Ctrl+Shift+C',
onClick: () => invoke('open_calculator') onClick: () => invoke('open_calculator'),
category: 'tools'
}, },
{ {
id: 'loot-tracker', id: 'loot-tracker',
icon: <Target className="w-5 h-5" />, icon: <Target className="w-5 h-5" />,
label: 'Loot', label: 'Loot Tracker',
description: 'Track hunting sessions',
hotkey: 'Ctrl+Shift+L', hotkey: 'Ctrl+Shift+L',
onClick: () => invoke('toggle_loot_tracker') onClick: () => togglePlugin('loot'),
category: 'plugins'
},
{
id: 'skill-tracker',
icon: <TrendingUp className="w-5 h-5" />,
label: 'Skill Tracker',
description: 'Monitor skill gains',
onClick: () => togglePlugin('skills'),
category: 'plugins'
}, },
{ {
id: 'price-check', id: 'price-check',
icon: <BarChart3 className="w-5 h-5" />, icon: <BarChart3 className="w-5 h-5" />,
label: 'Prices', label: 'Price Check',
onClick: () => invoke('open_price_checker') description: 'Item market values',
onClick: () => invoke('open_price_checker'),
category: 'tools'
}, },
{ {
id: 'skills', id: 'dashboard',
icon: <TrendingUp className="w-5 h-5" />, icon: <LayoutDashboard className="w-5 h-5" />,
label: 'Skills', label: 'Dashboard',
onClick: () => invoke('open_skill_tracker') description: 'Main application window',
}, onClick: () => invoke('show_main_window'),
{ category: 'system'
id: 'search',
icon: <Search className="w-5 h-5" />,
label: 'Search',
hotkey: 'Ctrl+Shift+F',
onClick: () => invoke('open_universal_search')
}, },
{ {
id: 'settings', id: 'settings',
icon: <Settings className="w-5 h-5" />, icon: <Settings className="w-5 h-5" />,
label: 'Settings', label: 'Settings',
onClick: () => invoke('show_settings_window') description: 'Configure application',
} onClick: () => invoke('show_settings_window'),
category: 'system'
},
] ]
const plugins: PluginWidget[] = [ const togglePlugin = (pluginId: string) => {
{ id: '1', name: 'Loot Tracker', icon: <Target className="w-4 h-4" />, active: true, onToggle: () => {} }, setActivePlugins(prev =>
{ id: '2', name: 'HP Monitor', icon: <Crosshair className="w-4 h-4" />, active: false, onToggle: () => {} }, prev.includes(pluginId)
{ id: '3', name: 'Skill Watch', icon: <TrendingUp className="w-4 h-4" />, active: true, onToggle: () => {} }, ? prev.filter(id => id !== pluginId)
{ id: '4', name: 'Coords', icon: <Grid className="w-4 h-4" />, active: false, onToggle: () => {} }, : [...prev, pluginId]
]
const filteredPlugins = plugins.filter(p =>
p.name.toLowerCase().includes(searchQuery.toLowerCase())
) )
}
const filteredItems = searchQuery
? menuItems.filter(item =>
item.label.toLowerCase().includes(searchQuery.toLowerCase()) ||
item.description?.toLowerCase().includes(searchQuery.toLowerCase())
)
: menuItems
const groupedItems = filteredItems.reduce((acc, item) => {
if (!acc[item.category]) acc[item.category] = []
acc[item.category].push(item)
return acc
}, {} as Record<string, MenuItem[]>)
if (!isVisible) return null if (!isVisible) return null
return ( return (
<div className="fixed inset-0 pointer-events-none flex items-start justify-center pt-4"> <div className="fixed inset-0 pointer-events-none flex items-end justify-center pb-6">
{/* Overlay Container */} {/* Windows-Style Overlay Container */}
<div <div
className="pointer-events-auto animate-in slide-in-from-top-4 duration-200" className="pointer-events-auto transition-all duration-300 ease-out"
style={{ style={{
background: 'rgba(10, 10, 15, 0.92)', width: isExpanded ? '720px' : '640px',
backdropFilter: 'blur(20px) saturate(180%)', maxWidth: '90vw',
WebkitBackdropFilter: 'blur(20px) saturate(180%)',
border: '1px solid rgba(99, 102, 241, 0.3)',
borderRadius: '16px',
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(99, 102, 241, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.05)',
minWidth: '380px',
maxWidth: '420px',
}} }}
> >
{/* Header - Drag Handle */} {/* Main Search Bar - Windows Style */}
<div <div
className="flex items-center justify-between px-4 py-3 border-b border-white/10 cursor-move" className="relative overflow-hidden"
style={{
background: 'rgba(32, 32, 32, 0.95)',
backdropFilter: 'blur(32px) saturate(180%)',
WebkitBackdropFilter: 'blur(32px) saturate(180%)',
borderRadius: isExpanded ? '12px' : '32px',
border: '1px solid rgba(255, 255, 255, 0.08)',
boxShadow: isExpanded
? '0 32px 64px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.1)'
: '0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05)',
}}
>
{/* Search Input Row */}
<div
className="flex items-center gap-3 px-5 h-14"
data-tauri-drag-region data-tauri-drag-region
> >
<div className="flex items-center gap-3"> {/* Logo Icon - Windows Style */}
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg shadow-indigo-500/20"> <div
<Layers className="w-4 h-4 text-white" /> className="flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center cursor-pointer transition-all hover:scale-105 active:scale-95"
</div> style={{
<div> background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%)',
<h3 className="text-sm font-semibold text-white">EU-Utility</h3> boxShadow: '0 4px 12px rgba(99, 102, 241, 0.4)',
<p className="text-xs text-white/50">App Drawer</p> }}
</div> onClick={() => setIsExpanded(!isExpanded)}
>
<Layers className="w-5 h-5 text-white" />
</div> </div>
{/* Search Input */}
<div className="flex-1 relative">
<input
ref={inputRef}
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onFocus={() => setIsExpanded(true)}
placeholder="Type to search plugins, tools..."
className="w-full bg-transparent border-none text-white text-base placeholder:text-white/40 focus:outline-none"
style={{ fontSize: '15px' }}
/>
</div>
{/* Right Side Actions */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{/* Session Timer */} {/* Session Timer */}
<div className="flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-white/5 border border-white/10"> <div
<Clock className="w-3 h-3 text-indigo-400" /> className="flex items-center gap-2 px-3 py-1.5 rounded-full"
<span className="text-xs font-mono text-white/80">{formatTime(sessionTime)}</span> style={{ background: 'rgba(255, 255, 255, 0.06)' }}
>
<Clock className="w-4 h-4 text-indigo-400" />
<span className="text-sm font-mono text-white/80">{formatTime(sessionTime)}</span>
</div> </div>
{/* Expand/Collapse */}
<button
onClick={() => setIsExpanded(!isExpanded)}
className="w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors"
>
{isExpanded ? (
<Minimize2 className="w-4 h-4 text-white/60" />
) : (
<Maximize2 className="w-4 h-4 text-white/60" />
)}
</button>
{/* Close */}
<button <button
onClick={() => setIsVisible(false)} onClick={() => setIsVisible(false)}
className="w-7 h-7 flex items-center justify-center rounded-lg hover:bg-white/10 transition-colors" className="w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors"
> >
<X className="w-4 h-4 text-white/60 hover:text-white" /> <X className="w-4 h-4 text-white/60 hover:text-white" />
</button> </button>
</div> </div>
</div> </div>
{/* Search Bar */} {/* Expanded Menu - Windows Start Menu Style */}
<div className="px-4 py-3 border-b border-white/10"> {isExpanded && (
<div className="relative"> <div
<Search className="w-4 h-4 text-white/40 absolute left-3 top-1/2 -translate-y-1/2" /> className="border-t border-white/10"
<input style={{
type="text" maxHeight: '480px',
value={searchQuery} overflow: 'hidden',
onChange={(e) => setSearchQuery(e.target.value)} }}
placeholder="Search plugins..." >
className="w-full pl-9 pr-3 py-2 bg-black/30 border border-white/10 rounded-lg text-sm text-white placeholder:text-white/30 focus:outline-none focus:border-indigo-500/50 transition-colors" {/* Quick Actions Grid */}
/> <div className="p-5">
<div className="flex items-center justify-between mb-4">
<span className="text-xs font-semibold uppercase tracking-wider text-white/40">Quick Access</span>
<div className="flex items-center gap-2">
<span className="text-xs text-white/30">{activePlugins.length} active</span>
</div> </div>
</div> </div>
{/* Quick Actions Grid */} <div className="grid grid-cols-6 gap-2">
<div className="px-4 py-3 border-b border-white/10"> {menuItems.slice(0, 6).map((item) => (
<p className="text-xs font-medium text-white/40 uppercase tracking-wider mb-3">Quick Actions</p>
<div className="grid grid-cols-3 gap-2">
{quickActions.map(action => (
<button <button
key={action.id} key={item.id}
onClick={action.onClick} onClick={() => {
className="group flex flex-col items-center gap-1.5 p-2.5 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 hover:border-indigo-500/30 transition-all duration-150" item.onClick()
if (!item.id.includes('tracker')) setIsVisible(false)
}}
className="group flex flex-col items-center gap-2 p-3 rounded-xl transition-all hover:bg-white/10"
> >
<div className="w-9 h-9 rounded-lg bg-gradient-to-br from-indigo-500/20 to-purple-500/20 group-hover:from-indigo-500/30 group-hover:to-purple-500/30 flex items-center justify-center transition-colors"> <div
<div className="text-indigo-400 group-hover:text-indigo-300 transition-colors"> className={`w-12 h-12 rounded-xl flex items-center justify-center transition-all ${
{action.icon} activePlugins.includes(item.id) || item.id === 'loot' && activePlugins.includes('loot')
? 'bg-indigo-500/20 text-indigo-400 ring-1 ring-indigo-500/30'
: 'bg-white/5 text-white/60 group-hover:bg-white/10 group-hover:text-white'
}`}
>
{item.icon}
</div> </div>
</div> <span className="text-[11px] text-white/70 text-center leading-tight">{item.label}</span>
<span className="text-[10px] font-medium text-white/70 group-hover:text-white transition-colors">
{action.label}
</span>
</button> </button>
))} ))}
</div> </div>
</div> </div>
{/* Active Plugins */} {/* Search Results or All Items */}
<div className="px-4 py-3"> <div className="px-5 pb-5" style={{ maxHeight: '280px', overflowY: 'auto' }}>
<div className="flex items-center justify-between mb-3"> {Object.entries(groupedItems).map(([category, items]) => (
<p className="text-xs font-medium text-white/40 uppercase tracking-wider">Plugins</p> <div key={category} className="mb-4 last:mb-0">
<span className="text-xs text-white/30">{filteredPlugins.filter(p => p.active).length} active</span> <div className="flex items-center justify-between mb-2">
</div> <span className="text-xs font-semibold uppercase tracking-wider text-white/40">
{category === 'tools' ? 'Tools' : category === 'plugins' ? 'Plugins' : 'System'}
<div className="space-y-1.5">
{filteredPlugins.map(plugin => (
<div
key={plugin.id}
className={`flex items-center justify-between p-2.5 rounded-lg border transition-all cursor-pointer ${
plugin.active
? 'bg-indigo-500/10 border-indigo-500/30'
: 'bg-white/5 border-white/5 hover:border-white/10'
}`}
onClick={plugin.onToggle}
>
<div className="flex items-center gap-2.5">
<div className={`w-7 h-7 rounded-md flex items-center justify-center ${
plugin.active ? 'bg-indigo-500/20 text-indigo-400' : 'bg-white/10 text-white/50'
}`}>
{plugin.icon}
</div>
<span className={`text-xs font-medium ${plugin.active ? 'text-white' : 'text-white/60'}`}>
{plugin.name}
</span> </span>
</div> </div>
<div className="space-y-1">
<div className={`w-8 h-4 rounded-full relative transition-colors ${ {items.map((item) => (
plugin.active ? 'bg-indigo-500' : 'bg-white/20' <button
key={item.id}
onClick={() => {
item.onClick()
if (!item.id.includes('tracker')) setIsVisible(false)
}}
className="w-full flex items-center gap-3 p-3 rounded-lg hover:bg-white/10 transition-all group"
>
<div className={`w-8 h-8 rounded-lg flex items-center justify-center ${
activePlugins.includes(item.id) || (item.id === 'loot-tracker' && activePlugins.includes('loot'))
? 'bg-indigo-500/20 text-indigo-400'
: 'bg-white/5 text-white/60 group-hover:text-white'
}`}> }`}>
<div className={`absolute top-0.5 w-3 h-3 rounded-full bg-white transition-all ${ {item.icon}
plugin.active ? 'left-4.5' : 'left-0.5'
}`} />
</div> </div>
<div className="flex-1 text-left">
<div className="text-sm font-medium text-white/90 group-hover:text-white">{item.label}</div>
{item.description && (
<div className="text-xs text-white/40">{item.description}</div>
)}
</div> </div>
{item.hotkey && (
<kbd className="px-2 py-1 text-xs rounded bg-white/5 text-white/40 font-mono">
{item.hotkey}
</kbd>
)}
{(activePlugins.includes(item.id) || (item.id === 'loot-tracker' && activePlugins.includes('loot'))) && (
<div className="w-2 h-2 rounded-full bg-emerald-400 shadow-[0_0_8px_rgba(52,211,153,0.5)]" />
)}
</button>
))} ))}
</div> </div>
</div> </div>
))}
{/* Stats Footer */} {filteredItems.length === 0 && (
<div className="px-4 py-3 border-t border-white/10 bg-white/5"> <div className="text-center py-8 text-white/40">
<div className="flex items-center justify-between"> <Search className="w-12 h-12 mx-auto mb-3 opacity-30" />
<p className="text-sm">No results found for "{searchQuery}"</p>
</div>
)}
</div>
{/* Footer */}
<div
className="flex items-center justify-between px-5 py-3 border-t border-white/10"
style={{ background: 'rgba(0, 0, 0, 0.3)' }}
>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" /> <div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
<span className="text-xs text-white/60">System Online</span> <span className="text-xs text-white/50">EU-Utility V3.0</span>
</div> </div>
<div className="flex items-center gap-1.5 text-xs text-white/50"> <div className="flex items-center gap-1 text-xs text-white/40">
<span className="text-emerald-400 font-medium">{currentPED.toFixed(2)}</span> <Command className="w-3 h-3" />
<span>PED/h</span> <span>Press</span>
<kbd className="px-1.5 py-0.5 rounded bg-white/10 text-white/60">Ctrl+Shift+U</kbd>
<span>to toggle</span>
</div> </div>
</div> </div>
</div> </div>
)}
</div>
</div> </div>
</div> </div>
) )