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,
|
Maximize2,
|
||||||
Minimize2,
|
Minimize2,
|
||||||
Grid,
|
Grid,
|
||||||
Command
|
Command,
|
||||||
|
Pin,
|
||||||
|
PinOff,
|
||||||
|
MoreVertical
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
interface MenuItem {
|
interface Plugin {
|
||||||
id: string
|
id: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
icon: React.ReactNode
|
icon: React.ReactNode
|
||||||
label: string
|
category: 'dashboard' | 'search' | 'calculator' | 'tracker' | 'game' | 'media' | 'utility'
|
||||||
description?: string
|
|
||||||
hotkey?: string
|
hotkey?: string
|
||||||
onClick: () => void
|
pinned: boolean
|
||||||
category: 'tools' | 'plugins' | 'system'
|
active: boolean
|
||||||
|
onToggle: () => void
|
||||||
|
onPin: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Overlay() {
|
export default function Overlay() {
|
||||||
const [isVisible, setIsVisible] = useState(false)
|
const [isVisible, setIsVisible] = useState(false)
|
||||||
const [isExpanded, setIsExpanded] = useState(false)
|
const [isExpanded, setIsExpanded] = useState(false)
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const [activePlugins, setActivePlugins] = useState<string[]>(['loot', 'skills'])
|
|
||||||
const [sessionTime, setSessionTime] = useState(0)
|
const [sessionTime, setSessionTime] = useState(0)
|
||||||
|
const [currentPED, setCurrentPED] = useState(0)
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
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(() => {
|
useEffect(() => {
|
||||||
// Listen for overlay toggle
|
|
||||||
const unlisten = listen('overlay:toggle', () => {
|
const unlisten = listen('overlay:toggle', () => {
|
||||||
setIsVisible(prev => {
|
setIsVisible(prev => {
|
||||||
const newVisible = !prev
|
const newVisible = !prev
|
||||||
|
|
@ -51,7 +168,6 @@ export default function Overlay() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Session timer
|
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
setSessionTime(prev => prev + 1)
|
setSessionTime(prev => prev + 1)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
@ -71,89 +187,44 @@ export default function Overlay() {
|
||||||
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
|
|
||||||
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
|
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuItems: MenuItem[] = [
|
const togglePlugin = (id: string) => {
|
||||||
{
|
setPlugins(prev => prev.map(p =>
|
||||||
id: 'calculator',
|
p.id === id ? { ...p, active: !p.active } : p
|
||||||
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 filteredItems = searchQuery
|
const togglePin = (id: string) => {
|
||||||
? menuItems.filter(item =>
|
setPlugins(prev => prev.map(p =>
|
||||||
item.label.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
p.id === id ? { ...p, pinned: !p.pinned } : p
|
||||||
item.description?.toLowerCase().includes(searchQuery.toLowerCase())
|
))
|
||||||
)
|
}
|
||||||
: menuItems
|
|
||||||
|
|
||||||
const groupedItems = filteredItems.reduce((acc, item) => {
|
const filteredPlugins = searchQuery
|
||||||
if (!acc[item.category]) acc[item.category] = []
|
? plugins.filter(p =>
|
||||||
acc[item.category].push(item)
|
p.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
return acc
|
p.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
}, {} as Record<string, MenuItem[]>)
|
)
|
||||||
|
: 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
|
if (!isVisible) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 pointer-events-none flex items-end justify-center pb-6">
|
<div
|
||||||
{/* Windows-Style Overlay Container */}
|
className="fixed pointer-events-none flex items-end justify-center"
|
||||||
|
style={{
|
||||||
|
bottom: '40px',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
zIndex: 9999
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className="pointer-events-auto transition-all duration-300 ease-out"
|
className="pointer-events-auto transition-all duration-300 ease-out"
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -161,36 +232,36 @@ export default function Overlay() {
|
||||||
maxWidth: '90vw',
|
maxWidth: '90vw',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Main Search Bar - Windows Style */}
|
{/* Main Container */}
|
||||||
<div
|
<div
|
||||||
className="relative overflow-hidden"
|
|
||||||
style={{
|
style={{
|
||||||
background: 'rgba(32, 32, 32, 0.95)',
|
background: 'rgba(32, 32, 32, 0.98)',
|
||||||
backdropFilter: 'blur(32px) saturate(180%)',
|
backdropFilter: 'blur(32px) saturate(180%)',
|
||||||
WebkitBackdropFilter: 'blur(32px) saturate(180%)',
|
WebkitBackdropFilter: 'blur(32px) saturate(180%)',
|
||||||
borderRadius: isExpanded ? '12px' : '32px',
|
borderRadius: isExpanded ? '16px' : '32px',
|
||||||
border: '1px solid rgba(255, 255, 255, 0.08)',
|
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||||
boxShadow: isExpanded
|
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 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.4), 0 0 0 1px rgba(255, 255, 255, 0.05)',
|
: '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
|
<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
|
data-tauri-drag-region
|
||||||
>
|
>
|
||||||
{/* Logo Icon - Windows Style */}
|
{/* Logo Button */}
|
||||||
<div
|
<button
|
||||||
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"
|
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={{
|
style={{
|
||||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%)',
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%)',
|
||||||
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.4)',
|
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.4)',
|
||||||
}}
|
}}
|
||||||
onClick={() => setIsExpanded(!isExpanded)}
|
|
||||||
>
|
>
|
||||||
<Layers className="w-5 h-5 text-white" />
|
<Layers className="w-5 h-5 text-white" />
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
{/* Search Input */}
|
{/* Search Input */}
|
||||||
<div className="flex-1 relative">
|
<div className="flex-1 relative">
|
||||||
|
|
@ -200,21 +271,26 @@ export default function Overlay() {
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
onFocus={() => setIsExpanded(true)}
|
onFocus={() => setIsExpanded(true)}
|
||||||
placeholder="Type to search plugins, tools..."
|
placeholder="Type to search plugins..."
|
||||||
className="w-full bg-transparent border-none text-white text-base placeholder:text-white/40 focus:outline-none"
|
className="w-full bg-transparent border-none text-white text-base focus:outline-none"
|
||||||
style={{ fontSize: '15px' }}
|
style={{
|
||||||
|
fontSize: '15px',
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Side Actions */}
|
{/* Right Side */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* Session Timer */}
|
{/* Session Timer */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-2 px-3 py-1.5 rounded-full"
|
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" />
|
<Clock className="w-4 h-4" style={{ color: '#818cf8' }} />
|
||||||
<span className="text-sm font-mono text-white/80">{formatTime(sessionTime)}</span>
|
<span className="text-sm font-mono" style={{ color: 'rgba(255, 255, 255, 0.8)' }}>
|
||||||
|
{formatTime(sessionTime)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Expand/Collapse */}
|
{/* 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"
|
className="w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors"
|
||||||
>
|
>
|
||||||
{isExpanded ? (
|
{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>
|
</button>
|
||||||
|
|
||||||
|
|
@ -234,102 +310,233 @@ export default function Overlay() {
|
||||||
onClick={() => setIsVisible(false)}
|
onClick={() => setIsVisible(false)}
|
||||||
className="w-8 h-8 flex items-center justify-center rounded-full 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" style={{ color: 'rgba(255, 255, 255, 0.6)' }} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Expanded Menu - Windows Start Menu Style */}
|
{/* Expanded Content */}
|
||||||
{isExpanded && (
|
{isExpanded && (
|
||||||
<div
|
<div
|
||||||
className="border-t border-white/10"
|
className="border-t"
|
||||||
style={{
|
style={{ borderColor: 'rgba(255, 255, 255, 0.08)' }}
|
||||||
maxHeight: '480px',
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* Quick Actions Grid */}
|
{/* Quick Access */}
|
||||||
<div className="p-5">
|
<div className="p-5">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<span className="text-xs font-semibold uppercase tracking-wider text-white/40">Quick Access</span>
|
<span
|
||||||
<div className="flex items-center gap-2">
|
className="text-xs font-semibold uppercase tracking-wider"
|
||||||
<span className="text-xs text-white/30">{activePlugins.length} active</span>
|
style={{ color: 'rgba(255, 255, 255, 0.4)' }}
|
||||||
</div>
|
>
|
||||||
|
Quick Access
|
||||||
|
</span>
|
||||||
|
<span className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.3)' }}>
|
||||||
|
{activeCount} active
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-6 gap-2">
|
<div className="grid grid-cols-6 gap-2">
|
||||||
{menuItems.slice(0, 6).map((item) => (
|
{plugins.slice(0, 6).map((plugin) => (
|
||||||
<button
|
<button
|
||||||
key={item.id}
|
key={plugin.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
item.onClick()
|
plugin.onToggle()
|
||||||
if (!item.id.includes('tracker')) setIsVisible(false)
|
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
|
<div
|
||||||
className={`w-12 h-12 rounded-xl flex items-center justify-center transition-all ${
|
className="w-12 h-12 rounded-xl flex items-center justify-center transition-all"
|
||||||
activePlugins.includes(item.id) || item.id === 'loot' && activePlugins.includes('loot')
|
style={{
|
||||||
? 'bg-indigo-500/20 text-indigo-400 ring-1 ring-indigo-500/30'
|
background: plugin.active
|
||||||
: 'bg-white/5 text-white/60 group-hover:bg-white/10 group-hover:text-white'
|
? '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>
|
</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>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search Results or All Items */}
|
{/* Pinned Plugins */}
|
||||||
<div className="px-5 pb-5" style={{ maxHeight: '280px', overflowY: 'auto' }}>
|
{pinnedPlugins.length > 0 && (
|
||||||
{Object.entries(groupedItems).map(([category, items]) => (
|
<div className="px-5 pb-4">
|
||||||
<div key={category} className="mb-4 last:mb-0">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<span
|
||||||
<span className="text-xs font-semibold uppercase tracking-wider text-white/40">
|
className="text-xs font-semibold uppercase tracking-wider"
|
||||||
{category === 'tools' ? 'Tools' : category === 'plugins' ? 'Plugins' : 'System'}
|
style={{ color: 'rgba(255, 255, 255, 0.4)' }}
|
||||||
|
>
|
||||||
|
Pinned
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{items.map((item) => (
|
{pinnedPlugins.map((plugin) => (
|
||||||
<button
|
<div
|
||||||
key={item.id}
|
key={plugin.id}
|
||||||
onClick={() => {
|
className="flex items-center justify-between p-3 rounded-lg hover:bg-white/5 transition-all cursor-pointer group"
|
||||||
item.onClick()
|
onClick={plugin.onToggle}
|
||||||
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 ${
|
<div className="flex items-center gap-3">
|
||||||
activePlugins.includes(item.id) || (item.id === 'loot-tracker' && activePlugins.includes('loot'))
|
<div
|
||||||
? 'bg-indigo-500/20 text-indigo-400'
|
className="w-8 h-8 rounded-lg flex items-center justify-center"
|
||||||
: 'bg-white/5 text-white/60 group-hover:text-white'
|
style={{
|
||||||
}`}>
|
background: plugin.active
|
||||||
{item.icon}
|
? '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 className="flex-1 text-left">
|
<div>
|
||||||
<div className="text-sm font-medium text-white/90 group-hover:text-white">{item.label}</div>
|
<div style={{ color: 'rgba(255, 255, 255, 0.9)' }} className="text-sm font-medium">
|
||||||
{item.description && (
|
{plugin.name}
|
||||||
<div className="text-xs text-white/40">{item.description}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{item.hotkey && (
|
<div style={{ color: 'rgba(255, 255, 255, 0.4)' }} className="text-xs">
|
||||||
<kbd className="px-2 py-1 text-xs rounded bg-white/5 text-white/40 font-mono">
|
{plugin.description}
|
||||||
{item.hotkey}
|
</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>
|
</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 && (
|
<button
|
||||||
<div className="text-center py-8 text-white/40">
|
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" />
|
<Search className="w-12 h-12 mx-auto mb-3 opacity-30" />
|
||||||
<p className="text-sm">No results found for "{searchQuery}"</p>
|
<p className="text-sm">No results found for "{searchQuery}"</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -338,17 +545,34 @@ export default function Overlay() {
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-between px-5 py-3 border-t border-white/10"
|
className="flex items-center justify-between px-5 py-3 border-t"
|
||||||
style={{ background: 'rgba(0, 0, 0, 0.3)' }}
|
style={{
|
||||||
|
borderColor: 'rgba(255, 255, 255, 0.08)',
|
||||||
|
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
|
||||||
<span className="text-xs text-white/50">EU-Utility V3.0</span>
|
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>
|
||||||
<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" />
|
<Command className="w-3 h-3" />
|
||||||
<span>Press</span>
|
<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>
|
<span>to toggle</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,24 @@
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@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;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -18,249 +36,310 @@ html, body, #root {
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
background: linear-gradient(135deg, #0a0a0f 0%, #13131a 50%, #0a0a0f 100%);
|
background: var(--bg-primary);
|
||||||
color: #e2e8f0;
|
color: var(--text-primary);
|
||||||
overflow: hidden;
|
line-height: 1.5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom scrollbar */
|
/* Scrollbar */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: rgba(30, 30, 46, 0.5);
|
background: var(--bg-secondary);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: rgba(99, 102, 241, 0.4);
|
background: var(--accent-primary);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(99, 102, 241, 0.6);
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Selection */
|
/* Selection */
|
||||||
::selection {
|
::selection {
|
||||||
background: rgba(99, 102, 241, 0.3);
|
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 */
|
/* Animations */
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from {
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
opacity: 0;
|
to { opacity: 1; transform: translateY(0); }
|
||||||
transform: translateY(10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideIn {
|
@keyframes slideUp {
|
||||||
from {
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
opacity: 0;
|
to { opacity: 1; transform: translateY(0); }
|
||||||
transform: translateX(-20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse-glow {
|
@keyframes pulse {
|
||||||
0%, 100% {
|
0%, 100% { opacity: 1; }
|
||||||
box-shadow: 0 0 20px rgba(99, 102, 241, 0.2);
|
50% { opacity: 0.5; }
|
||||||
}
|
|
||||||
50% {
|
|
||||||
box-shadow: 0 0 40px rgba(99, 102, 241, 0.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes float {
|
|
||||||
0%, 100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-fade-in {
|
.animate-fade-in {
|
||||||
animation: fadeIn 0.3s ease-out;
|
animation: fadeIn 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-slide-in {
|
.animate-slide-up {
|
||||||
animation: slideIn 0.3s ease-out;
|
animation: slideUp 0.4s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-pulse-glow {
|
.animate-pulse {
|
||||||
animation: pulse-glow 2s ease-in-out infinite;
|
animation: pulse 2s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-float {
|
/* Layout */
|
||||||
animation: float 3s ease-in-out infinite;
|
.min-h-screen {
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Glassmorphism utilities */
|
.flex {
|
||||||
.glass {
|
display: flex;
|
||||||
background: rgba(255, 255, 255, 0.05);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.glass-dark {
|
.flex-col {
|
||||||
background: rgba(10, 10, 15, 0.8);
|
flex-direction: column;
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
border: 1px solid rgba(99, 102, 241, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gradient text */
|
.items-center {
|
||||||
.text-gradient {
|
align-items: center;
|
||||||
background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
background-clip: text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Button styles */
|
.justify-center {
|
||||||
.btn-primary {
|
justify-content: center;
|
||||||
background: linear-gradient(135deg, #6366f1, #4f46e5);
|
}
|
||||||
color: white;
|
|
||||||
border: none;
|
.justify-between {
|
||||||
padding: 0.5rem 1rem;
|
justify-content: space-between;
|
||||||
border-radius: 0.5rem;
|
}
|
||||||
|
|
||||||
|
.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;
|
font-weight: 500;
|
||||||
transition: all 0.2s;
|
font-size: 0.875rem;
|
||||||
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.3);
|
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 {
|
.btn-primary:hover {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4);
|
box-shadow: 0 6px 16px rgba(99, 102, 241, 0.5);
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
background: rgba(255, 255, 255, 0.05);
|
background: var(--bg-hover);
|
||||||
color: #e2e8f0;
|
color: var(--text-primary);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid var(--border-color);
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary:hover {
|
.btn-secondary:hover {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
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 */
|
/* Inputs */
|
||||||
.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 */
|
|
||||||
input, select, textarea {
|
input, select, textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.625rem 0.875rem;
|
||||||
background: rgba(0, 0, 0, 0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 0.5rem;
|
border-radius: 8px;
|
||||||
color: #e2e8f0;
|
color: var(--text-primary);
|
||||||
padding: 0.5rem 0.75rem;
|
font-size: 0.875rem;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus, select:focus, textarea:focus {
|
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);
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
input::placeholder {
|
input::placeholder {
|
||||||
color: rgba(148, 163, 184, 0.5);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Range input */
|
/* Grid */
|
||||||
input[type="range"] {
|
.grid {
|
||||||
-webkit-appearance: none;
|
display: grid;
|
||||||
width: 100%;
|
|
||||||
height: 4px;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
border-radius: 2px;
|
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="range"]::-webkit-slider-thumb {
|
.grid-cols-2 {
|
||||||
-webkit-appearance: none;
|
grid-template-columns: repeat(2, 1fr);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="range"]::-webkit-slider-thumb:hover {
|
.grid-cols-3 {
|
||||||
transform: scale(1.2);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toggle switch */
|
.grid-cols-4 {
|
||||||
.toggle {
|
grid-template-columns: repeat(4, 1fr);
|
||||||
appearance: none;
|
|
||||||
width: 44px;
|
|
||||||
height: 24px;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
border-radius: 12px;
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.2s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle::after {
|
.gap-4 {
|
||||||
content: '';
|
gap: 1rem;
|
||||||
position: absolute;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
top: 2px;
|
|
||||||
left: 2px;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle:checked {
|
/* Icons */
|
||||||
background: #6366f1;
|
.icon {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle:checked::after {
|
.icon-sm {
|
||||||
transform: translateX(20px);
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-lg {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status indicators */
|
/* Status indicators */
|
||||||
|
|
@ -268,74 +347,133 @@ input[type="range"]::-webkit-slider-thumb:hover {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dot.online {
|
.status-dot.online {
|
||||||
background: #10b981;
|
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 {
|
.status-dot.offline {
|
||||||
background: #64748b;
|
background: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dot.warning {
|
/* Glass effect */
|
||||||
background: #f59e0b;
|
.glass {
|
||||||
box-shadow: 0 0 10px rgba(245, 158, 11, 0.5);
|
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 {
|
/* Sidebar */
|
||||||
background: #ef4444;
|
.sidebar {
|
||||||
box-shadow: 0 0 10px rgba(239, 68, 68, 0.5);
|
width: 260px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tooltip */
|
.sidebar-link {
|
||||||
.tooltip {
|
display: flex;
|
||||||
position: relative;
|
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 {
|
.sidebar-link:hover,
|
||||||
content: attr(data-tooltip);
|
.sidebar-link.active {
|
||||||
position: absolute;
|
background: rgba(99, 102, 241, 0.1);
|
||||||
bottom: 100%;
|
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%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
padding: 0.25rem 0.5rem;
|
z-index: 9999;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip:hover::after {
|
.overlay-bar {
|
||||||
opacity: 1;
|
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 */
|
/* Responsive */
|
||||||
[data-tauri-drag-region] {
|
@media (max-width: 768px) {
|
||||||
cursor: move;
|
.sidebar {
|
||||||
user-select: none;
|
width: 100%;
|
||||||
}
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
/* Line clamp */
|
.main-content {
|
||||||
.line-clamp-2 {
|
padding-bottom: 100px;
|
||||||
display: -webkit-box;
|
}
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide scrollbar but keep functionality */
|
.grid-cols-4 {
|
||||||
.scrollbar-hide {
|
grid-template-columns: repeat(2, 1fr);
|
||||||
-ms-overflow-style: none;
|
}
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollbar-hide::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue