Complete overlay redesign with Windows Start Menu style
Features: - Collapsible search bar with logo button - Quick Access grid (6 plugin shortcuts) - Pinned plugins section (with pin/unpin) - All plugins list with search filter - Toggle switches for each plugin - Session timer display - Keyboard shortcut hints - Glassmorphism design with blur - Smooth expand/collapse animations - Proper styling with inline styles (no Tailwind dependency issues)
This commit is contained in:
parent
22230df27f
commit
3c5c7f2ed1
|
|
@ -0,0 +1,257 @@
|
|||
# EU-Utility V3 Implementation Plan
|
||||
|
||||
Based on original Python/PyQt6 EU-Utility analysis.
|
||||
|
||||
---
|
||||
|
||||
## Original Features Analysis
|
||||
|
||||
### 1. Architecture
|
||||
- **Original:** Python 3.11 + PyQt6
|
||||
- **New:** Rust + Tauri + React + TypeScript
|
||||
- **Plugin System:** 25+ built-in plugins
|
||||
|
||||
### 2. UI Structure
|
||||
```
|
||||
Main Overlay (Semi-transparent, always on top)
|
||||
├── Left Sidebar: Plugin tabs/icons
|
||||
├── Center: Active plugin content
|
||||
└── Bottom: Quick actions bar
|
||||
|
||||
Floating Icon (Screen overlay)
|
||||
├── Double-click: Toggle main overlay
|
||||
├── Right-click: Context menu
|
||||
└── Draggable position
|
||||
```
|
||||
|
||||
### 3. Plugin Categories
|
||||
|
||||
#### Dashboard & Utility
|
||||
- [ ] Dashboard - Customizable start page with stats
|
||||
- [ ] Calculator - Standard calculator
|
||||
- [ ] Settings - Preferences configuration
|
||||
- [ ] Plugin Store - Community marketplace
|
||||
|
||||
#### Search & Information
|
||||
- [ ] Universal Search - Nexus entities (items, mobs, locations)
|
||||
- [ ] Nexus Search - Items and market data
|
||||
- [ ] TP Runner - Teleporter route planner
|
||||
|
||||
#### Calculators
|
||||
- [ ] DPP Calculator - Damage Per PEC, weapon efficiency
|
||||
- [ ] Crafting Calc - Blueprint calculator with success rates
|
||||
- [ ] Enhancer Calc - Break rates and costs
|
||||
|
||||
#### Trackers
|
||||
- [ ] Loot Tracker - Hunting loot with ROI analysis
|
||||
- [ ] Skill Scanner - OCR-based skill tracking
|
||||
- [ ] Codex Tracker - Creature challenge progress
|
||||
- [ ] Mission Tracker - Mission and objective tracking
|
||||
- [ ] Global Tracker - Globals, HOFs, ATHs tracking
|
||||
- [ ] Mining Helper - Claims and hotspot tracking
|
||||
- [ ] Auction Tracker - Price and markup tracking
|
||||
- [ ] Inventory Manager - TT value and item management
|
||||
- [ ] Profession Scanner - Profession rank tracking
|
||||
|
||||
#### Game Integration
|
||||
- [ ] Game Reader - OCR for in-game menus/text
|
||||
- [ ] Chat Logger - Log, search, filter chat
|
||||
|
||||
#### External Integration
|
||||
- [ ] Spotify Controller - Control playback
|
||||
|
||||
---
|
||||
|
||||
## Hotkey Mapping
|
||||
|
||||
| Hotkey | Action | Plugin |
|
||||
|--------|--------|--------|
|
||||
| Ctrl+Shift+U | Toggle main overlay | Global |
|
||||
| Ctrl+Shift+H | Hide all overlays | Global |
|
||||
| Ctrl+Shift+F | Universal Search | Search |
|
||||
| Ctrl+Shift+N | Nexus Search | Search |
|
||||
| Ctrl+Shift+C | Calculator | Utility |
|
||||
| Ctrl+Shift+D | DPP Calculator | Calculator |
|
||||
| Ctrl+Shift+E | Enhancer Calc | Calculator |
|
||||
| Ctrl+Shift+B | Crafting Calc | Calculator |
|
||||
| Ctrl+Shift+L | Loot Tracker | Tracker |
|
||||
| Ctrl+Shift+S | Skill Scanner | Tracker |
|
||||
| Ctrl+Shift+X | Codex Tracker | Tracker |
|
||||
| Ctrl+Shift+R | Game Reader | Scanner |
|
||||
| Ctrl+Shift+M | Spotify Controller | Media |
|
||||
| Ctrl+Shift+Home | Dashboard | Overview |
|
||||
| Ctrl+Shift+, | Settings | Configuration |
|
||||
|
||||
---
|
||||
|
||||
## New UI Design (Windows Start Menu Style)
|
||||
|
||||
### Main Overlay (Bottom Center)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ [Logo] Search plugins, items... [Timer] [Expand] │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Quick Access [Session: 02:34:12] │
|
||||
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
|
||||
│ │Calc│ │Loot│ │Skill│ │Price│ │Search│ │Settings│ │
|
||||
│ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ │
|
||||
│ │
|
||||
│ Pinned Plugins │
|
||||
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
||||
│ [Icon] Loot Tracker [On] [Pin] │
|
||||
│ [Icon] Skill Scanner [On] [Pin] │
|
||||
│ [Icon] DPP Calculator [Off] [Pin] │
|
||||
│ │
|
||||
│ All Plugins │
|
||||
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
||||
│ [Icon] Codex Tracker [Off] [Pin] │
|
||||
│ [Icon] Game Reader [Off] [Pin] │
|
||||
│ [Icon] Mining Helper [Off] [Pin] │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ [Status: Online] Ctrl+Shift+U to toggle │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Design Elements
|
||||
- **Background:** Dark glassmorphism (rgba(32, 32, 32, 0.95))
|
||||
- **Blur:** 32px backdrop blur
|
||||
- **Border:** 1px rgba(255, 255, 255, 0.08)
|
||||
- **Shadow:** 0 8px 32px rgba(0, 0, 0, 0.4)
|
||||
- **Border Radius:** 32px (collapsed), 12px (expanded)
|
||||
- **Position:** Bottom center of screen
|
||||
- **Width:** 640px (compact), 720px (expanded)
|
||||
|
||||
### Features
|
||||
1. **Collapsible** - Click logo to expand/collapse
|
||||
2. **Search** - Type to filter plugins instantly
|
||||
3. **Pin System** - Pin favorite plugins to top
|
||||
4. **Quick Access** - 6 shortcut icons always visible
|
||||
5. **Plugin List** - All plugins with toggle switches
|
||||
6. **Session Timer** - Live hunt duration
|
||||
7. **Status Bar** - Online/offline indicator
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Core Framework (COMPLETE)
|
||||
- [x] Rust + Tauri setup
|
||||
- [x] React + TypeScript frontend
|
||||
- [x] Plugin system architecture
|
||||
- [x] Event bus
|
||||
- [x] Settings persistence
|
||||
- [x] Hotkey system
|
||||
- [x] Window management
|
||||
|
||||
### Phase 2: UI Redesign (IN PROGRESS)
|
||||
- [ ] Windows Start Menu overlay
|
||||
- [ ] Collapsible search bar
|
||||
- [ ] Plugin grid/list view
|
||||
- [ ] Pin/unpin functionality
|
||||
- [ ] Quick access icons
|
||||
- [ ] Session timer display
|
||||
|
||||
### Phase 3: Essential Plugins
|
||||
- [ ] Dashboard
|
||||
- [ ] Universal Search
|
||||
- [ ] Calculator
|
||||
- [ ] Loot Tracker
|
||||
- [ ] Settings
|
||||
|
||||
### Phase 4: Advanced Plugins
|
||||
- [ ] DPP Calculator
|
||||
- [ ] Skill Scanner (OCR)
|
||||
- [ ] Codex Tracker
|
||||
- [ ] Game Reader (OCR)
|
||||
|
||||
### Phase 5: Full Feature Set
|
||||
- [ ] All 25+ plugins
|
||||
- [ ] Spotify integration
|
||||
- [ ] Chat logger
|
||||
- [ ] Plugin marketplace
|
||||
|
||||
---
|
||||
|
||||
## Technical Decisions
|
||||
|
||||
### Plugin System
|
||||
```typescript
|
||||
interface Plugin {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: 'dashboard' | 'search' | 'calculator' | 'tracker' | 'game' | 'media';
|
||||
icon: string;
|
||||
version: string;
|
||||
author: string;
|
||||
hotkey?: string;
|
||||
pinned: boolean;
|
||||
active: boolean;
|
||||
component: React.ComponentType;
|
||||
}
|
||||
```
|
||||
|
||||
### Data Storage
|
||||
- SQLite for local data
|
||||
- Tauri settings API
|
||||
- Plugin data isolation
|
||||
|
||||
### OCR Integration
|
||||
- Tesseract for Windows (static link)
|
||||
- EasyOCR fallback
|
||||
- Screen region capture
|
||||
- Text recognition pipeline
|
||||
|
||||
### Screen Reading
|
||||
- Window capture (game window)
|
||||
- Region selection (drag to define)
|
||||
- OCR text extraction
|
||||
- HP bar detection
|
||||
- Coordinate reading
|
||||
- Mob name recognition
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ ├── Layout.tsx
|
||||
│ ├── Sidebar.tsx
|
||||
│ ├── PluginGrid.tsx
|
||||
│ ├── PluginList.tsx
|
||||
│ ├── SearchBar.tsx
|
||||
│ ├── QuickAccess.tsx
|
||||
│ └── PinnedPlugins.tsx
|
||||
├── plugins/
|
||||
│ ├── dashboard/
|
||||
│ ├── search/
|
||||
│ ├── calculator/
|
||||
│ ├── tracker/
|
||||
│ └── game/
|
||||
├── pages/
|
||||
│ ├── Dashboard.tsx
|
||||
│ ├── Plugins.tsx
|
||||
│ ├── Settings.tsx
|
||||
│ └── Overlay.tsx
|
||||
└── store/
|
||||
├── appStore.ts
|
||||
└── pluginStore.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Implement new overlay UI** (Windows Start Menu style)
|
||||
2. **Add pin/unpin functionality**
|
||||
3. **Create plugin grid component**
|
||||
4. **Build essential plugins** (Dashboard, Search, Calculator)
|
||||
5. **Add OCR integration** for Game Reader
|
||||
|
||||
---
|
||||
|
||||
*Implementation Plan - EU-Utility V3*
|
||||
|
|
@ -18,29 +18,146 @@ import {
|
|||
Maximize2,
|
||||
Minimize2,
|
||||
Grid,
|
||||
Command
|
||||
Command,
|
||||
Pin,
|
||||
PinOff,
|
||||
MoreVertical
|
||||
} from 'lucide-react'
|
||||
|
||||
interface MenuItem {
|
||||
interface Plugin {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
icon: React.ReactNode
|
||||
label: string
|
||||
description?: string
|
||||
category: 'dashboard' | 'search' | 'calculator' | 'tracker' | 'game' | 'media' | 'utility'
|
||||
hotkey?: string
|
||||
onClick: () => void
|
||||
category: 'tools' | 'plugins' | 'system'
|
||||
pinned: boolean
|
||||
active: boolean
|
||||
onToggle: () => void
|
||||
onPin: () => void
|
||||
}
|
||||
|
||||
export default function Overlay() {
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [activePlugins, setActivePlugins] = useState<string[]>(['loot', 'skills'])
|
||||
const [sessionTime, setSessionTime] = useState(0)
|
||||
const [currentPED, setCurrentPED] = useState(0)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
// Plugin state
|
||||
const [plugins, setPlugins] = useState<Plugin[]>([
|
||||
{
|
||||
id: 'dashboard',
|
||||
name: 'Dashboard',
|
||||
description: 'Overview and statistics',
|
||||
icon: <LayoutDashboard className="w-5 h-5" />,
|
||||
category: 'dashboard',
|
||||
hotkey: 'Ctrl+Shift+Home',
|
||||
pinned: true,
|
||||
active: true,
|
||||
onToggle: () => togglePlugin('dashboard'),
|
||||
onPin: () => togglePin('dashboard')
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
name: 'Universal Search',
|
||||
description: 'Search items, mobs, locations',
|
||||
icon: <Search className="w-5 h-5" />,
|
||||
category: 'search',
|
||||
hotkey: 'Ctrl+Shift+F',
|
||||
pinned: true,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('search'),
|
||||
onPin: () => togglePin('search')
|
||||
},
|
||||
{
|
||||
id: 'calculator',
|
||||
name: 'Calculator',
|
||||
description: 'Standard calculator',
|
||||
icon: <Calculator className="w-5 h-5" />,
|
||||
category: 'calculator',
|
||||
hotkey: 'Ctrl+Shift+C',
|
||||
pinned: true,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('calculator'),
|
||||
onPin: () => togglePin('calculator')
|
||||
},
|
||||
{
|
||||
id: 'dpp-calc',
|
||||
name: 'DPP Calculator',
|
||||
description: 'Damage per PEC efficiency',
|
||||
icon: <Zap className="w-5 h-5" />,
|
||||
category: 'calculator',
|
||||
hotkey: 'Ctrl+Shift+D',
|
||||
pinned: false,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('dpp-calc'),
|
||||
onPin: () => togglePin('dpp-calc')
|
||||
},
|
||||
{
|
||||
id: 'loot-tracker',
|
||||
name: 'Loot Tracker',
|
||||
description: 'Track hunting sessions',
|
||||
icon: <Target className="w-5 h-5" />,
|
||||
category: 'tracker',
|
||||
hotkey: 'Ctrl+Shift+L',
|
||||
pinned: true,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('loot-tracker'),
|
||||
onPin: () => togglePin('loot-tracker')
|
||||
},
|
||||
{
|
||||
id: 'skill-scanner',
|
||||
name: 'Skill Scanner',
|
||||
description: 'OCR skill tracking',
|
||||
icon: <TrendingUp className="w-5 h-5" />,
|
||||
category: 'tracker',
|
||||
hotkey: 'Ctrl+Shift+S',
|
||||
pinned: false,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('skill-scanner'),
|
||||
onPin: () => togglePin('skill-scanner')
|
||||
},
|
||||
{
|
||||
id: 'codex-tracker',
|
||||
name: 'Codex Tracker',
|
||||
description: 'Creature challenges',
|
||||
icon: <Grid className="w-5 h-5" />,
|
||||
category: 'tracker',
|
||||
hotkey: 'Ctrl+Shift+X',
|
||||
pinned: false,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('codex-tracker'),
|
||||
onPin: () => togglePin('codex-tracker')
|
||||
},
|
||||
{
|
||||
id: 'game-reader',
|
||||
name: 'Game Reader',
|
||||
description: 'OCR text recognition',
|
||||
icon: <BarChart3 className="w-5 h-5" />,
|
||||
category: 'game',
|
||||
hotkey: 'Ctrl+Shift+R',
|
||||
pinned: false,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('game-reader'),
|
||||
onPin: () => togglePin('game-reader')
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
name: 'Settings',
|
||||
description: 'Configure EU-Utility',
|
||||
icon: <Settings className="w-5 h-5" />,
|
||||
category: 'utility',
|
||||
hotkey: 'Ctrl+Shift+,',
|
||||
pinned: false,
|
||||
active: false,
|
||||
onToggle: () => togglePlugin('settings'),
|
||||
onPin: () => togglePin('settings')
|
||||
}
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
// Listen for overlay toggle
|
||||
const unlisten = listen('overlay:toggle', () => {
|
||||
setIsVisible(prev => {
|
||||
const newVisible = !prev
|
||||
|
|
@ -51,7 +168,6 @@ export default function Overlay() {
|
|||
})
|
||||
})
|
||||
|
||||
// Session timer
|
||||
const timer = setInterval(() => {
|
||||
setSessionTime(prev => prev + 1)
|
||||
}, 1000)
|
||||
|
|
@ -71,89 +187,44 @@ export default function Overlay() {
|
|||
const formatTime = (seconds: number) => {
|
||||
const h = Math.floor(seconds / 3600)
|
||||
const m = Math.floor((seconds % 3600) / 60)
|
||||
const s = seconds % 60
|
||||
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const menuItems: MenuItem[] = [
|
||||
{
|
||||
id: 'calculator',
|
||||
icon: <Calculator className="w-5 h-5" />,
|
||||
label: 'Calculator',
|
||||
description: 'Quick calculations',
|
||||
hotkey: 'Ctrl+Shift+C',
|
||||
onClick: () => invoke('open_calculator'),
|
||||
category: 'tools'
|
||||
},
|
||||
{
|
||||
id: 'loot-tracker',
|
||||
icon: <Target className="w-5 h-5" />,
|
||||
label: 'Loot Tracker',
|
||||
description: 'Track hunting sessions',
|
||||
hotkey: 'Ctrl+Shift+L',
|
||||
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',
|
||||
icon: <BarChart3 className="w-5 h-5" />,
|
||||
label: 'Price Check',
|
||||
description: 'Item market values',
|
||||
onClick: () => invoke('open_price_checker'),
|
||||
category: 'tools'
|
||||
},
|
||||
{
|
||||
id: 'dashboard',
|
||||
icon: <LayoutDashboard className="w-5 h-5" />,
|
||||
label: 'Dashboard',
|
||||
description: 'Main application window',
|
||||
onClick: () => invoke('show_main_window'),
|
||||
category: 'system'
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
icon: <Settings className="w-5 h-5" />,
|
||||
label: 'Settings',
|
||||
description: 'Configure application',
|
||||
onClick: () => invoke('show_settings_window'),
|
||||
category: 'system'
|
||||
},
|
||||
]
|
||||
|
||||
const togglePlugin = (pluginId: string) => {
|
||||
setActivePlugins(prev =>
|
||||
prev.includes(pluginId)
|
||||
? prev.filter(id => id !== pluginId)
|
||||
: [...prev, pluginId]
|
||||
)
|
||||
const togglePlugin = (id: string) => {
|
||||
setPlugins(prev => prev.map(p =>
|
||||
p.id === id ? { ...p, active: !p.active } : p
|
||||
))
|
||||
}
|
||||
|
||||
const filteredItems = searchQuery
|
||||
? menuItems.filter(item =>
|
||||
item.label.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
item.description?.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
: menuItems
|
||||
const togglePin = (id: string) => {
|
||||
setPlugins(prev => prev.map(p =>
|
||||
p.id === id ? { ...p, pinned: !p.pinned } : p
|
||||
))
|
||||
}
|
||||
|
||||
const groupedItems = filteredItems.reduce((acc, item) => {
|
||||
if (!acc[item.category]) acc[item.category] = []
|
||||
acc[item.category].push(item)
|
||||
return acc
|
||||
}, {} as Record<string, MenuItem[]>)
|
||||
const filteredPlugins = searchQuery
|
||||
? plugins.filter(p =>
|
||||
p.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
p.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
: plugins
|
||||
|
||||
const pinnedPlugins = filteredPlugins.filter(p => p.pinned)
|
||||
const unpinnedPlugins = filteredPlugins.filter(p => !p.pinned)
|
||||
const activeCount = plugins.filter(p => p.active).length
|
||||
|
||||
if (!isVisible) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none flex items-end justify-center pb-6">
|
||||
{/* Windows-Style Overlay Container */}
|
||||
<div
|
||||
className="fixed pointer-events-none flex items-end justify-center"
|
||||
style={{
|
||||
bottom: '40px',
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 9999
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="pointer-events-auto transition-all duration-300 ease-out"
|
||||
style={{
|
||||
|
|
@ -161,36 +232,36 @@ export default function Overlay() {
|
|||
maxWidth: '90vw',
|
||||
}}
|
||||
>
|
||||
{/* Main Search Bar - Windows Style */}
|
||||
{/* Main Container */}
|
||||
<div
|
||||
className="relative overflow-hidden"
|
||||
style={{
|
||||
background: 'rgba(32, 32, 32, 0.95)',
|
||||
background: 'rgba(32, 32, 32, 0.98)',
|
||||
backdropFilter: 'blur(32px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(32px) saturate(180%)',
|
||||
borderRadius: isExpanded ? '12px' : '32px',
|
||||
border: '1px solid rgba(255, 255, 255, 0.08)',
|
||||
borderRadius: isExpanded ? '16px' : '32px',
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
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)',
|
||||
? '0 32px 64px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(99, 102, 241, 0.1)'
|
||||
: '0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(99, 102, 241, 0.1)',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* Search Input Row */}
|
||||
{/* Header / Search Bar */}
|
||||
<div
|
||||
className="flex items-center gap-3 px-5 h-14"
|
||||
className="flex items-center gap-3 px-4 h-14"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
{/* Logo Icon - Windows Style */}
|
||||
<div
|
||||
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"
|
||||
{/* Logo Button */}
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%)',
|
||||
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.4)',
|
||||
}}
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
>
|
||||
<Layers className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Search Input */}
|
||||
<div className="flex-1 relative">
|
||||
|
|
@ -200,21 +271,26 @@ export default function Overlay() {
|
|||
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' }}
|
||||
placeholder="Type to search plugins..."
|
||||
className="w-full bg-transparent border-none text-white text-base focus:outline-none"
|
||||
style={{
|
||||
fontSize: '15px',
|
||||
color: 'rgba(255, 255, 255, 0.9)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right Side Actions */}
|
||||
{/* Right Side */}
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Session Timer */}
|
||||
<div
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded-full"
|
||||
style={{ background: 'rgba(255, 255, 255, 0.06)' }}
|
||||
style={{ background: 'rgba(255, 255, 255, 0.08)' }}
|
||||
>
|
||||
<Clock className="w-4 h-4 text-indigo-400" />
|
||||
<span className="text-sm font-mono text-white/80">{formatTime(sessionTime)}</span>
|
||||
<Clock className="w-4 h-4" style={{ color: '#818cf8' }} />
|
||||
<span className="text-sm font-mono" style={{ color: 'rgba(255, 255, 255, 0.8)' }}>
|
||||
{formatTime(sessionTime)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Expand/Collapse */}
|
||||
|
|
@ -223,9 +299,9 @@ export default function Overlay() {
|
|||
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" />
|
||||
<ChevronUp className="w-4 h-4" style={{ color: 'rgba(255, 255, 255, 0.6)' }} />
|
||||
) : (
|
||||
<Maximize2 className="w-4 h-4 text-white/60" />
|
||||
<Maximize2 className="w-4 h-4" style={{ color: 'rgba(255, 255, 255, 0.6)' }} />
|
||||
)}
|
||||
</button>
|
||||
|
||||
|
|
@ -234,102 +310,233 @@ export default function Overlay() {
|
|||
onClick={() => setIsVisible(false)}
|
||||
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" style={{ color: 'rgba(255, 255, 255, 0.6)' }} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Expanded Menu - Windows Start Menu Style */}
|
||||
{/* Expanded Content */}
|
||||
{isExpanded && (
|
||||
<div
|
||||
className="border-t border-white/10"
|
||||
style={{
|
||||
maxHeight: '480px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
className="border-t"
|
||||
style={{ borderColor: 'rgba(255, 255, 255, 0.08)' }}
|
||||
>
|
||||
{/* Quick Actions Grid */}
|
||||
{/* Quick Access */}
|
||||
<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>
|
||||
<span
|
||||
className="text-xs font-semibold uppercase tracking-wider"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.4)' }}
|
||||
>
|
||||
Quick Access
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.3)' }}>
|
||||
{activeCount} active
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-6 gap-2">
|
||||
{menuItems.slice(0, 6).map((item) => (
|
||||
{plugins.slice(0, 6).map((plugin) => (
|
||||
<button
|
||||
key={item.id}
|
||||
key={plugin.id}
|
||||
onClick={() => {
|
||||
item.onClick()
|
||||
if (!item.id.includes('tracker')) setIsVisible(false)
|
||||
plugin.onToggle()
|
||||
if (!plugin.active) setIsVisible(false)
|
||||
}}
|
||||
className="group flex flex-col items-center gap-2 p-3 rounded-xl transition-all hover:bg-white/10"
|
||||
className="flex flex-col items-center gap-2 p-3 rounded-xl transition-all hover:bg-white/10"
|
||||
>
|
||||
<div
|
||||
className={`w-12 h-12 rounded-xl flex items-center justify-center transition-all ${
|
||||
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'
|
||||
}`}
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center transition-all"
|
||||
style={{
|
||||
background: plugin.active
|
||||
? 'rgba(99, 102, 241, 0.2)'
|
||||
: 'rgba(255, 255, 255, 0.05)',
|
||||
color: plugin.active ? '#818cf8' : 'rgba(255, 255, 255, 0.6)'
|
||||
}}
|
||||
>
|
||||
{item.icon}
|
||||
{plugin.icon}
|
||||
</div>
|
||||
<span className="text-[11px] text-white/70 text-center leading-tight">{item.label}</span>
|
||||
<span
|
||||
className="text-[11px] text-center leading-tight"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.7)' }}
|
||||
>
|
||||
{plugin.name}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search Results or All Items */}
|
||||
<div className="px-5 pb-5" style={{ maxHeight: '280px', overflowY: 'auto' }}>
|
||||
{Object.entries(groupedItems).map(([category, items]) => (
|
||||
<div key={category} className="mb-4 last:mb-0">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs font-semibold uppercase tracking-wider text-white/40">
|
||||
{category === 'tools' ? 'Tools' : category === 'plugins' ? 'Plugins' : 'System'}
|
||||
{/* Pinned Plugins */}
|
||||
{pinnedPlugins.length > 0 && (
|
||||
<div className="px-5 pb-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span
|
||||
className="text-xs font-semibold uppercase tracking-wider"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.4)' }}
|
||||
>
|
||||
Pinned
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
{items.map((item) => (
|
||||
<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"
|
||||
{pinnedPlugins.map((plugin) => (
|
||||
<div
|
||||
key={plugin.id}
|
||||
className="flex items-center justify-between p-3 rounded-lg hover:bg-white/5 transition-all cursor-pointer group"
|
||||
onClick={plugin.onToggle}
|
||||
>
|
||||
<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'
|
||||
}`}>
|
||||
{item.icon}
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center"
|
||||
style={{
|
||||
background: plugin.active
|
||||
? 'rgba(99, 102, 241, 0.2)'
|
||||
: 'rgba(255, 255, 255, 0.05)',
|
||||
color: plugin.active ? '#818cf8' : 'rgba(255, 255, 255, 0.5)'
|
||||
}}
|
||||
>
|
||||
{plugin.icon}
|
||||
</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 style={{ color: 'rgba(255, 255, 255, 0.9)' }} className="text-sm font-medium">
|
||||
{plugin.name}
|
||||
</div>
|
||||
{item.hotkey && (
|
||||
<kbd className="px-2 py-1 text-xs rounded bg-white/5 text-white/40 font-mono">
|
||||
{item.hotkey}
|
||||
<div style={{ color: 'rgba(255, 255, 255, 0.4)' }} className="text-xs">
|
||||
{plugin.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{plugin.hotkey && (
|
||||
<kbd
|
||||
className="px-2 py-1 text-xs rounded font-mono"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.05)',
|
||||
color: 'rgba(255, 255, 255, 0.4)'
|
||||
}}
|
||||
>
|
||||
{plugin.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>
|
||||
))}
|
||||
|
||||
{filteredItems.length === 0 && (
|
||||
<div className="text-center py-8 text-white/40">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
plugin.onPin()
|
||||
}}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg hover:bg-white/10 transition-colors"
|
||||
>
|
||||
<Pin className="w-4 h-4" style={{ color: '#818cf8' }} />
|
||||
</button>
|
||||
|
||||
<div
|
||||
className="w-10 h-5 rounded-full relative cursor-pointer transition-colors"
|
||||
style={{ background: plugin.active ? '#6366f1' : 'rgba(255, 255, 255, 0.2)' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
plugin.onToggle()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all"
|
||||
style={{ left: plugin.active ? '22px' : '2px' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* All Plugins */}
|
||||
<div className="px-5 pb-5" style={{ maxHeight: '300px', overflowY: 'auto' }}>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span
|
||||
className="text-xs font-semibold uppercase tracking-wider"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.4)' }}
|
||||
>
|
||||
All Plugins
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
{unpinnedPlugins.map((plugin) => (
|
||||
<div
|
||||
key={plugin.id}
|
||||
className="flex items-center justify-between p-3 rounded-lg hover:bg-white/5 transition-all cursor-pointer group"
|
||||
onClick={plugin.onToggle}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center"
|
||||
style={{
|
||||
background: plugin.active
|
||||
? 'rgba(99, 102, 241, 0.2)'
|
||||
: 'rgba(255, 255, 255, 0.05)',
|
||||
color: plugin.active ? '#818cf8' : 'rgba(255, 255, 255, 0.5)'
|
||||
}}
|
||||
>
|
||||
{plugin.icon}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ color: 'rgba(255, 255, 255, 0.9)' }} className="text-sm font-medium">
|
||||
{plugin.name}
|
||||
</div>
|
||||
<div style={{ color: 'rgba(255, 255, 255, 0.4)' }} className="text-xs">
|
||||
{plugin.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{plugin.hotkey && (
|
||||
<kbd
|
||||
className="px-2 py-1 text-xs rounded font-mono"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.05)',
|
||||
color: 'rgba(255, 255, 255, 0.4)'
|
||||
}}
|
||||
>
|
||||
{plugin.hotkey}
|
||||
</kbd>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
plugin.onPin()
|
||||
}}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg hover:bg-white/10 transition-colors opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
<PinOff className="w-4 h-4" style={{ color: 'rgba(255, 255, 255, 0.4)' }} />
|
||||
</button>
|
||||
|
||||
<div
|
||||
className="w-10 h-5 rounded-full relative cursor-pointer transition-colors"
|
||||
style={{ background: plugin.active ? '#6366f1' : 'rgba(255, 255, 255, 0.2)' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
plugin.onToggle()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all"
|
||||
style={{ left: plugin.active ? '22px' : '2px' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredPlugins.length === 0 && (
|
||||
<div
|
||||
className="text-center py-8"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.4)' }}
|
||||
>
|
||||
<Search className="w-12 h-12 mx-auto mb-3 opacity-30" />
|
||||
<p className="text-sm">No results found for "{searchQuery}"</p>
|
||||
</div>
|
||||
|
|
@ -338,17 +545,34 @@ export default function Overlay() {
|
|||
|
||||
{/* 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)' }}
|
||||
className="flex items-center justify-between px-5 py-3 border-t"
|
||||
style={{
|
||||
borderColor: 'rgba(255, 255, 255, 0.08)',
|
||||
background: 'rgba(0, 0, 0, 0.3)'
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
|
||||
<span className="text-xs text-white/50">EU-Utility V3.0</span>
|
||||
<div
|
||||
className="w-2 h-2 rounded-full animate-pulse"
|
||||
style={{ background: '#10b981', boxShadow: '0 0 8px rgba(16, 185, 129, 0.5)' }}
|
||||
/>
|
||||
<span className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.5)' }}>
|
||||
EU-Utility V3.0
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-xs text-white/40">
|
||||
|
||||
<div
|
||||
className="flex items-center gap-1 text-xs"
|
||||
style={{ color: 'rgba(255, 255, 255, 0.4)' }}
|
||||
>
|
||||
<Command className="w-3 h-3" />
|
||||
<span>Press</span>
|
||||
<kbd className="px-1.5 py-0.5 rounded bg-white/10 text-white/60">Ctrl+Shift+U</kbd>
|
||||
<kbd
|
||||
className="px-1.5 py-0.5 rounded"
|
||||
style={{ background: 'rgba(255, 255, 255, 0.1)' }}
|
||||
>
|
||||
Ctrl+Shift+U
|
||||
</kbd>
|
||||
<span>to toggle</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,24 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--bg-primary: #0a0a0f;
|
||||
--bg-secondary: #13131a;
|
||||
--bg-card: #1a1a24;
|
||||
--bg-hover: #252530;
|
||||
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #64748b;
|
||||
|
||||
--accent-primary: #6366f1;
|
||||
--accent-secondary: #8b5cf6;
|
||||
--accent-gradient: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
|
||||
--border-color: rgba(255, 255, 255, 0.08);
|
||||
--glass-bg: rgba(19, 19, 26, 0.95);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
@ -18,249 +36,310 @@ html, body, #root {
|
|||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
background: linear-gradient(135deg, #0a0a0f 0%, #13131a 50%, #0a0a0f 100%);
|
||||
color: #e2e8f0;
|
||||
overflow: hidden;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
/* Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(30, 30, 46, 0.5);
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(99, 102, 241, 0.4);
|
||||
background: var(--accent-primary);
|
||||
border-radius: 3px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(99, 102, 241, 0.6);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Selection */
|
||||
::selection {
|
||||
background: rgba(99, 102, 241, 0.3);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Focus styles */
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
*:focus-visible {
|
||||
outline: 2px solid rgba(99, 102, 241, 0.5);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
@keyframes slideUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 20px rgba(99, 102, 241, 0.2);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 40px rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-slide-in {
|
||||
animation: slideIn 0.3s ease-out;
|
||||
.animate-slide-up {
|
||||
animation: slideUp 0.4s ease-out;
|
||||
}
|
||||
|
||||
.animate-pulse-glow {
|
||||
animation: pulse-glow 2s ease-in-out infinite;
|
||||
.animate-pulse {
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
/* Layout */
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Glassmorphism utilities */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.glass-dark {
|
||||
background: rgba(10, 10, 15, 0.8);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(99, 102, 241, 0.2);
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Gradient text */
|
||||
.text-gradient {
|
||||
background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Button styles */
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #6366f1, #4f46e5);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.gap-6 {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.py-2 {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
.text-2xl {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
.text-white {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.text-gray-400 {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.text-indigo-400 {
|
||||
color: #818cf8;
|
||||
}
|
||||
|
||||
.text-emerald-400 {
|
||||
color: #34d399;
|
||||
}
|
||||
|
||||
/* Backgrounds */
|
||||
.bg-primary {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.bg-secondary {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.bg-card {
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.bg-accent {
|
||||
background: var(--accent-gradient);
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
border-color: rgba(99, 102, 241, 0.3);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.3);
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent-gradient);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 6px 16px rgba(99, 102, 241, 0.5);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: #e2e8f0;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(99, 102, 241, 0.3);
|
||||
border-color: rgba(99, 102, 241, 0.5);
|
||||
}
|
||||
|
||||
/* Card styles */
|
||||
.card {
|
||||
background: rgba(19, 19, 26, 0.8);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 12px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
border-color: rgba(99, 102, 241, 0.2);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Input styles */
|
||||
/* Inputs */
|
||||
input, select, textarea {
|
||||
width: 100%;
|
||||
padding: 0.625rem 0.875rem;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
color: #e2e8f0;
|
||||
padding: 0.5rem 0.75rem;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
input:focus, select:focus, textarea:focus {
|
||||
border-color: rgba(99, 102, 241, 0.5);
|
||||
outline: none;
|
||||
border-color: var(--accent-primary);
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: rgba(148, 163, 184, 0.5);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Range input */
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
/* Grid */
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: linear-gradient(135deg, #6366f1, #4f46e5);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 10px rgba(99, 102, 241, 0.4);
|
||||
transition: transform 0.2s;
|
||||
.grid-cols-2 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.2);
|
||||
.grid-cols-3 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
/* Toggle switch */
|
||||
.toggle {
|
||||
appearance: none;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 12px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
.toggle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
transition: transform 0.2s;
|
||||
.gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.toggle:checked {
|
||||
background: #6366f1;
|
||||
/* Icons */
|
||||
.icon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.toggle:checked::after {
|
||||
transform: translateX(20px);
|
||||
.icon-sm {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.icon-lg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
/* Status indicators */
|
||||
|
|
@ -268,74 +347,133 @@ input[type="range"]::-webkit-slider-thumb:hover {
|
|||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.status-dot.online {
|
||||
background: #10b981;
|
||||
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
|
||||
box-shadow: 0 0 8px rgba(16, 185, 129, 0.5);
|
||||
}
|
||||
|
||||
.status-dot.offline {
|
||||
background: #64748b;
|
||||
background: var(--text-muted);
|
||||
}
|
||||
|
||||
.status-dot.warning {
|
||||
background: #f59e0b;
|
||||
box-shadow: 0 0 10px rgba(245, 158, 11, 0.5);
|
||||
/* Glass effect */
|
||||
.glass {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.status-dot.error {
|
||||
background: #ef4444;
|
||||
box-shadow: 0 0 10px rgba(239, 68, 68, 0.5);
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 260px;
|
||||
background: var(--bg-secondary);
|
||||
border-right: 1px solid var(--border-color);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Tooltip */
|
||||
.tooltip {
|
||||
position: relative;
|
||||
.sidebar-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 8px;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tooltip::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
.sidebar-link:hover,
|
||||
.sidebar-link.active {
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.sidebar-link.active {
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Utility classes */
|
||||
.rounded-lg {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.rounded-xl {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.rounded-full {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.shadow-lg {
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Overlay specific */
|
||||
.overlay-container {
|
||||
position: fixed;
|
||||
bottom: 40px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
color: white;
|
||||
font-size: 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.tooltip:hover::after {
|
||||
opacity: 1;
|
||||
.overlay-bar {
|
||||
background: rgba(32, 32, 32, 0.95);
|
||||
backdrop-filter: blur(32px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 32px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Draggable regions */
|
||||
[data-tauri-drag-region] {
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* Line clamp */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
.main-content {
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
/* Hide scrollbar but keep functionality */
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue