Compare commits

..

No commits in common. "main" and "feature/settings-customization" have entirely different histories.

9 changed files with 124 additions and 985 deletions

431
README.md
View file

@ -1,44 +1,21 @@
# Claude Usage Widget # Claude Usage Widget
A beautiful, highly customizable Windows desktop widget that displays your Claude.ai usage statistics in real-time with system tray integration. A beautiful, standalone Windows desktop widget that displays your Claude.ai usage statistics in real-time.
**[SCREENSHOT: Main widget showing session and weekly usage]** ![Claude Usage Widget](assets/claude-usage-screenshot.jpg)
## Features ## Features
### Core Features - 🎯 **Real-time Usage Tracking** - Monitor both session and weekly usage limits
- 🎯 **Real-time Usage Tracking** - Monitor both 5-hour session and 7-day weekly usage limits - 📊 **Visual Progress Bars** - Clean, gradient progress indicators
- 📊 **Visual Progress Bars** - Clean, gradient progress indicators with customizable colors - ⏱️ **Countdown Timers** - Circular timers showing time until reset
- ⏱️ **Countdown Timers** - Circular timers showing time until session/weekly reset - 🔄 **Auto-refresh** - Updates every 5 minutes automatically
- 🔄 **Configurable Auto-refresh** - Adjustable update interval (10-300 seconds) - 🎨 **Modern UI** - Sleek, draggable widget with dark theme
- 🎨 **Fully Customizable UI** - Toggle any element on/off, customize all colors and theme - 🎨 **Customizable Colors** - Personalize progress bar colors for each usage level
- ⚙️ **Settings Panel** - Easy-to-use settings window for customization
- 🔒 **Secure** - Encrypted credential storage
- 📍 **Always on Top** - Stays visible across all workspaces - 📍 **Always on Top** - Stays visible across all workspaces
- 🔒 **Secure** - Encrypted credential storage using electron-store - 💾 **System Tray** - Minimizes to tray for easy access
### System Tray Integration
- 💾 **Live Tray Icon** - Real-time usage percentage displayed in system tray
- 🎨 **Customizable Tray Colors** - Independent color schemes for tray icon
- ⚙️ **Tray Update Interval** - Configurable refresh rate (10-300 seconds)
- 📊 **Switchable Metrics** - Display either session or weekly usage in tray
- 🔢 **Toggle Percentage Text** - Show/hide percentage number on tray icon
### Advanced Customization
- 🎨 **Theme Editor** - Customize background gradients, text colors, borders, and title bar
- 🖼️ **Element Visibility Controls** - Show/hide any UI element independently:
- Section labels (Current Session / Weekly Limit)
- Progress bars
- Percentage text
- Countdown circles
- Time remaining text
- 📏 **Dynamic Resizing** - Window automatically adjusts to fit visible elements
- 💎 **Static or Gradient Colors** - Choose between static colors or gradient progressions
- 🎯 **Color Coding by Usage** - Different colors for normal (0-74%), warning (75-89%), and danger (90-100%)
### Application Settings
- 🚀 **Start on System Boot** - Automatically launch when Windows starts
- 🔽 **Start Minimized** - Begin in system tray without showing window
- 📌 **Close to Tray** - Minimize to tray instead of exiting application
- 💾 **Persistent Window Position** - Remembers and restores window location
## Installation ## Installation
@ -77,189 +54,71 @@ The installer will be created in the `dist/` folder.
### First Launch ### First Launch
1. Launch the widget 1. Launch the widget
2. The app will attempt to auto-login using stored credentials 2. Click "Login to Claude" when prompted
3. If no credentials exist, click "Login to Claude" 3. A browser window will open - login to your Claude.ai account
4. A browser window will open - login to your Claude.ai account 4. The widget will automatically capture your session
5. The widget will automatically capture your session 5. Usage data will start displaying immediately
6. Usage data will start displaying immediately
**[SCREENSHOT: Login screen]**
### Widget Controls ### Widget Controls
**Title Bar:** - **Drag** - Click and drag the title bar to move the widget
- **Drag** - Click and drag anywhere on the title bar to move the widget - **Refresh** - Click the refresh icon to update data immediately
- **Settings Icon** - Open the comprehensive settings panel - **Minimize** - Click the minus icon to hide to system tray
- **Refresh Icon** - Update usage data immediately - **Close** - Click the X to minimize to tray (doesn't exit)
- **Minimize** - Hide to system tray
- **Close** - Minimize to tray or exit (depending on settings)
**[SCREENSHOT: Widget with controls highlighted]** ### System Tray Menu
### System Tray Right-click the tray icon for:
- Show/Hide widget
**Tray Icon:** - Refresh usage data
- Displays real-time usage percentage - Re-login (if session expires)
- Color changes based on usage level (normal/warning/danger) - Settings - Open customization panel
- Switches between session and weekly usage (configurable in settings) - Exit application
- Updates independently with configurable interval
**Tray Menu (Right-click):**
- **Show/Hide Widget** - Toggle main window visibility
- **Refresh Usage** - Update data immediately
- **Re-login** - Clear credentials and login again
- **Settings** - Open settings panel
- **Exit** - Quit application completely
**[SCREENSHOT: System tray icon and menu]**
## Settings
Access settings by clicking the gear icon on the widget or via the system tray menu.
### Main Window Settings
#### Colors
Customize progress bar colors with gradient or static options:
**Static Color Mode:**
- Enable "Use Static Color" toggle
- Choose a single color for the progress bar (ignores usage levels)
**Gradient Mode (Default):**
- **Normal** (0-74% usage) - Default: Purple gradient (#8b5cf6 → #a78bfa)
- **Warning** (75-89% usage) - Default: Orange gradient (#f59e0b → #fbbf24)
- **Danger** (90-100% usage) - Default: Red gradient (#ef4444 → #f87171)
Each level has independent start and end colors for smooth gradients.
**[SCREENSHOT: Color customization panel]**
#### Theme
Customize the entire widget appearance:
- **Background Gradient** - Start and end colors
- **Text Colors** - Primary and secondary text
- **Title Bar** - Background color and opacity (0-100%)
- **Borders** - Border color and opacity (0-100%)
**[SCREENSHOT: Theme customization panel]**
#### Update Interval
- Range: 10-300 seconds (default: 30 seconds)
- Controls how often the main widget refreshes usage data
- Lower values = more current data, higher API usage
- Slider with real-time value display
#### Current Session Elements
Control visibility of each element in the Current Session section:
- **Show Current Session** - Master toggle for entire section
- **Show Label Text** - "CURRENT SESSION" label
- **Show Progress Bar** - Usage progress bar
- **Show Percentage Text** - Usage percentage number
- **Show Countdown Circle** - Circular timer graphic
- **Show Time Text** - Time remaining until reset
**[SCREENSHOT: Element visibility controls]**
#### Weekly Limit Elements
Identical controls for the Weekly Limit section:
- Master toggle to show/hide entire section
- Individual toggles for label, bar, percentage, circle, and time
**Example Configurations:**
- Minimal: Just progress bars (hide everything else)
- Data-rich: All elements visible (default)
- Custom: Any combination you prefer
### Tray Icon Settings
#### Display Mode
Choose which metric to display in the system tray:
- **Session Usage** (5-hour limit)
- **Weekly Usage** (7-day limit)
#### Update Interval
- Range: 10-300 seconds (default: 30 seconds)
- Independent from main widget update interval
- Tray icon updates on its own schedule
#### Show Percentage Text
- Toggle percentage number on/off in tray icon
- When disabled, shows only the colored ring
#### Colors
Separate color customization for tray icon:
- Static or gradient mode (same as main widget)
- Independent color schemes
- Normal/Warning/Danger levels
**[SCREENSHOT: Tray icon settings]**
### Application Settings
#### Start on System Boot
- Automatically launch widget when Windows starts
- Registers with Windows startup items
- Disable to manually start the widget
#### Start Minimized
- Launch hidden in system tray
- Widget won't show window on startup
- Access via tray icon
#### Close to Tray
- When enabled: Clicking X minimizes to tray
- When disabled: Clicking X exits application
- Minimize button always goes to tray
**[SCREENSHOT: Application settings]**
### Account
**Log Out** - Clears stored credentials and returns to login screen
## Understanding the Display ## Understanding the Display
### Current Session (5-Hour Limit) ### Current Session
- **Progress Bar** - Visual representation of usage (0-100%) - **Progress Bar** - Shows usage from 0-100%
- **Percentage** - Exact usage percentage - **Timer** - Time remaining until 5-hour session resets
- **Countdown Circle** - Circular timer showing progress toward reset
- **Time Remaining** - Hours and minutes until session resets (e.g., "2h 34m")
- **Color Coding**: - **Color Coding**:
- Purple: Normal usage (0-74%) - Purple: Normal usage (0-74%)
- Orange: High usage (75-89%) - Orange: High usage (75-89%)
- Red: Critical usage (90-100%) - Red: Critical usage (90-100%)
### Weekly Limit (7-Day Rolling Window) ### Weekly Limit
- **Progress Bar** - Weekly usage from 0-100% - **Progress Bar** - Shows weekly usage from 0-100%
- **Percentage** - Exact weekly usage percentage - **Timer** - Time remaining until weekly reset (Wednesdays 7:00 AM)
- **Countdown Circle** - Progress toward weekly reset - **Same color coding** as session usage
- **Time Remaining** - Days/hours until weekly reset (e.g., "3d 5h")
- Same color coding as session usage
**[SCREENSHOT: Usage display explanation with labels]** ## Customization
## Window Behavior ### Color Preferences
### Dynamic Resizing Customize the progress bar colors to match your preferences:
- Window automatically resizes based on visible elements
- Shows only what you've enabled in settings
- Maintains minimum size for readability
- Cannot be manually resized smaller than content
### Manual Resizing 1. Right-click the system tray icon
- Drag edges to resize window 2. Select "Settings"
- Constraints: 320-600px width, 96-180px height 3. Use the color pickers to customize each usage level:
- Content stays centered at all sizes - **Normal** (0-74% usage) - Default: Purple gradient
- **Warning** (75-89% usage) - Default: Orange gradient
- **Danger** (90-100% usage) - Default: Red gradient
4. Changes apply instantly to the main widget
5. Click "Reset to Defaults" to restore original colors
### Position Persistence ![Settings Window - Color Customization](assets/settings-screenshot.png)
- Window position saved automatically when moved
- Restored on next launch
- Per-display awareness
## Keyboard Shortcuts ### Auto-start on Windows Boot
Currently, the widget does not have keyboard shortcuts. This may be added in a future version. 1. Press `Win + R`
2. Type `shell:startup` and press Enter
3. Create a shortcut to the widget executable in this folder
### Custom Refresh Interval
Edit `src/renderer/app.js`:
```javascript
const UPDATE_INTERVAL = 5 * 60 * 1000; // Change to your preference (in milliseconds)
```
## Troubleshooting ## Troubleshooting
@ -267,31 +126,16 @@ Currently, the widget does not have keyboard shortcuts. This may be added in a f
- Your Claude.ai session may have expired - Your Claude.ai session may have expired
- Click "Login to Claude" to re-authenticate - Click "Login to Claude" to re-authenticate
- Check that you're logging into the correct account - Check that you're logging into the correct account
- Verify Claude.ai is accessible in your region
### Widget not updating ### Widget not updating
- Check your internet connection - Check your internet connection
- Click the refresh button manually - Click the refresh button manually
- Ensure Claude.ai API is accessible - Ensure Claude.ai is accessible in your region
- Try increasing update interval if experiencing rate limits - Try re-logging in from the system tray menu
- Re-login from system tray menu
### Tray icon not showing/updating ### Widget position not saving
- Check that "Show Percentage Text" is enabled to see text - Window position is now saved automatically when you drag it
- Verify update interval isn't too long - Position will be restored when you restart the app
- Try changing display mode (session ↔ weekly)
- Restart the application
### Window too small/elements cut off
- Check element visibility settings
- Enable more elements to expand window
- Window automatically sizes to fit enabled elements
- Reset to defaults if layout seems broken
### Auto-start not working
- Verify "Start on System Boot" is enabled in settings
- Check Windows Task Manager > Startup tab
- May require administrator rights on some systems
### Build errors ### Build errors
```bash ```bash
@ -302,11 +146,10 @@ npm install
## Privacy & Security ## Privacy & Security
- **Local Storage Only** - Your session credentials are stored locally using encrypted electron-store - Your session credentials are stored **locally only** using encrypted storage
- **No Third-Party Servers** - No data is sent to any servers except Claude.ai official API - No data is sent to any third-party servers
- **Official API Only** - Widget only communicates with Claude.ai API endpoints - The widget only communicates with Claude.ai official API
- **Secure Cookies** - Session cookies stored using Electron's secure storage - Session cookies are stored using Electron's secure storage
- **Open Source** - All code is publicly available for audit
## Technical Details ## Technical Details
@ -314,14 +157,11 @@ npm install
- Electron 28.0.0 - Electron 28.0.0
- Pure JavaScript (no framework overhead) - Pure JavaScript (no framework overhead)
- Native Node.js APIs - Native Node.js APIs
- electron-store for encrypted secure storage - electron-store for secure storage
- axios for HTTP requests
- Canvas API for tray icon generation
**API Endpoints:** **API Endpoint:**
``` ```
https://claude.ai/api/organizations/{org_id}/usage https://claude.ai/api/organizations/{org_id}/usage
https://claude.ai/api/organizations
``` ```
**Storage Location:** **Storage Location:**
@ -329,152 +169,37 @@ https://claude.ai/api/organizations
%APPDATA%/claude-usage-widget/config.json (encrypted) %APPDATA%/claude-usage-widget/config.json (encrypted)
``` ```
**Configuration Keys:**
- `sessionKey` - Encrypted Claude.ai session token
- `organizationId` - Your Claude organization UUID
- `colorPreferences` - Custom color settings
- `traySettings` - Tray icon configuration
- `themeSettings` - Widget theme preferences
- `appSettings` - Application behavior settings
- `uiVisibility` - Element visibility toggles
- `windowPosition` - Saved window location
## Project Structure
```
claude-usage-widget/
├── main.js # Electron main process
├── preload.js # IPC bridge (security)
├── package.json # Dependencies and scripts
├── assets/
│ ├── icon.ico # Application icon
│ └── logo.png # Widget logo
└── src/
└── renderer/
├── index.html # Main widget UI
├── app.js # Widget logic
├── styles.css # Widget styles
├── settings.html # Settings panel UI
├── settings.js # Settings logic
└── settings-styles.css # Settings styles
```
## Version History
### v1.4.2 (Current)
- ✨ Added comprehensive UI element visibility controls
- ✨ Added system tray icon with live usage display
- ✨ Added customizable tray icon colors and update interval
- ✨ Added application settings (start on boot, start minimized, close to tray)
- ✨ Added theme customization (backgrounds, text colors, borders, opacity)
- ✨ Added configurable update intervals for UI and tray
- ✨ Added static color mode for progress bars
- ✨ Added dynamic window resizing based on visible elements
- 🎨 Improved settings organization with collapsible sections
- 🎨 Consistent typography and spacing throughout settings
- 🐛 Fixed element centering and responsive layout
- 🐛 Fixed timer container visibility handling
### v1.4.1
- Added color customization
- Added settings panel
- Improved window position persistence
### v1.4.0
- Added custom color themes
- Added settings window
- Remember window position
### v1.3.0
- Initial public release
- Real-time usage tracking
- System tray support
- Auto-refresh functionality
## Roadmap ## Roadmap
### Planned Features
- [ ] macOS support - [ ] macOS support
- [ ] Linux support - [ ] Linux support
- [ ] Notification alerts at usage thresholds
- [ ] Usage history graphs
- [ ] Export usage data (CSV/JSON)
- [ ] Multiple account support
- [ ] Keyboard shortcuts
- [ ] Mini mode (ultra-compact view)
- [ ] Desktop widget mode (frameless, always-on-desktop)
- [ ] Custom notification sounds
### Completed
- [x] Custom color themes - [x] Custom color themes
- [ ] Notification alerts at usage thresholds
- [x] Remember window position - [x] Remember window position
- [x] Settings panel - [x] Settings panel
- [x] System tray integration - [ ] Usage history graphs
- [x] Tray icon with live usage - [ ] Multiple account support
- [x] Element visibility controls - [ ] Keyboard shortcuts
- [x] Theme customization
- [x] Configurable update intervals
- [x] Auto-start on boot
- [x] Static color mode
## Contributing ## Contributing
Contributions are welcome! Here's how you can help: Contributions are welcome! Please feel free to submit a Pull Request.
1. **Fork the repository**
2. **Create a feature branch** (`git checkout -b feature/amazing-feature`)
3. **Commit your changes** (`git commit -m 'Add some amazing feature'`)
4. **Push to the branch** (`git push origin feature/amazing-feature`)
5. **Open a Pull Request**
### Development Guidelines
- Follow existing code style
- Test thoroughly before submitting PR
- Update README if adding features
- Add comments for complex logic
## License ## License
MIT License - feel free to use, modify, and distribute as needed. MIT License - feel free to use and modify as needed.
See [LICENSE](LICENSE) file for full details.
## Disclaimer ## Disclaimer
**This is an unofficial tool and is not affiliated with, endorsed by, or connected to Anthropic or Claude.ai in any way.** This is an unofficial tool and is not affiliated with or endorsed by Anthropic. Use at your own discretion.
- Use at your own risk and discretion
- No warranties or guarantees provided
- Not responsible for any issues arising from use
- May break if Claude.ai changes their API
- Respect Anthropic's Terms of Service
## Support ## Support
If you encounter issues: If you encounter issues:
1. Check the [Issues](issues) page
1. **Check existing issues** - [Issues page](../../issues) 2. Create a new issue with details about your problem
2. **Create a new issue** with: 3. Include your OS version and any error messages
- Your Windows version
- Widget version
- Steps to reproduce
- Error messages (if any)
- Screenshots (if relevant)
**Before reporting:**
- Try restarting the widget
- Try re-logging in
- Check if Claude.ai is accessible in browser
- Verify you have the latest version
## Acknowledgments
- Built for the Claude.ai community
- Inspired by the need for easy usage monitoring
- Thanks to all contributors and users
--- ---
**Made with ❤️ for the Claude.ai community** Made with ❤️ for the Claude.ai community
**Star ⭐ this repo if you find it useful!**

101
main.js
View file

@ -31,14 +31,10 @@ function createMainWindow() {
const windowOptions = { const windowOptions = {
width: WIDGET_WIDTH, width: WIDGET_WIDTH,
height: WIDGET_HEIGHT, height: WIDGET_HEIGHT,
minWidth: 320,
maxWidth: 600,
minHeight: 96,
maxHeight: 180,
frame: false, frame: false,
transparent: true, transparent: true,
alwaysOnTop: true, alwaysOnTop: true,
resizable: true, resizable: false,
skipTaskbar: false, skipTaskbar: false,
icon: path.join(__dirname, 'assets/icon.ico'), icon: path.join(__dirname, 'assets/icon.ico'),
webPreferences: { webPreferences: {
@ -583,12 +579,7 @@ ipcMain.on('minimize-window', () => {
}); });
ipcMain.on('close-window', () => { ipcMain.on('close-window', () => {
const appSettings = store.get('appSettings', { closeToTray: false }); app.quit();
if (appSettings.closeToTray) {
mainWindow.hide();
} else {
app.quit();
}
}); });
ipcMain.handle('get-window-position', () => { ipcMain.handle('get-window-position', () => {
@ -606,19 +597,6 @@ ipcMain.handle('set-window-position', (event, { x, y }) => {
return false; return false;
}); });
ipcMain.on('set-window-height', (event, height) => {
if (mainWindow) {
const currentBounds = mainWindow.getBounds();
mainWindow.setSize(currentBounds.width, Math.round(height));
}
});
ipcMain.on('set-window-size', (event, { width, height }) => {
if (mainWindow) {
mainWindow.setSize(Math.round(width), Math.round(height));
}
});
ipcMain.on('open-external', (event, url) => { ipcMain.on('open-external', (event, url) => {
shell.openExternal(url); shell.openExternal(url);
}); });
@ -757,88 +735,13 @@ ipcMain.handle('notify-theme-change', (event, theme) => {
return true; return true;
}); });
// App settings
ipcMain.handle('get-app-settings', () => {
return store.get('appSettings', {
startOnBoot: false,
startMinimized: false,
closeToTray: false,
uiUpdateInterval: 30 // 30 seconds
});
});
ipcMain.handle('set-app-settings', (event, settings) => {
const currentSettings = store.get('appSettings', {});
const newSettings = { ...currentSettings, ...settings };
store.set('appSettings', newSettings);
// Apply auto-launch setting
if (settings.hasOwnProperty('startOnBoot')) {
const isDev = !app.isPackaged;
app.setLoginItemSettings({
openAtLogin: settings.startOnBoot,
path: process.execPath,
args: isDev ? [app.getAppPath()] : []
});
}
return true;
});
// UI visibility settings
ipcMain.handle('get-ui-visibility', () => {
return store.get('uiVisibility', {
showSessionSection: true,
showWeeklySection: true,
sessionShowLabel: true,
sessionShowBar: true,
sessionShowPercentage: true,
sessionShowCircle: true,
sessionShowTime: true,
weeklyShowLabel: true,
weeklyShowBar: true,
weeklyShowPercentage: true,
weeklyShowCircle: true,
weeklyShowTime: true
});
});
ipcMain.handle('set-ui-visibility', (event, visibility) => {
store.set('uiVisibility', visibility);
// Notify main window to update visibility
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('ui-visibility-changed', visibility);
}
return true;
});
// App lifecycle // App lifecycle
app.whenReady().then(() => { app.whenReady().then(() => {
// Apply startup settings
const appSettings = store.get('appSettings', {
startOnBoot: false,
startMinimized: false,
closeToTray: false
});
// Set auto-launch
const isDev = !app.isPackaged;
app.setLoginItemSettings({
openAtLogin: appSettings.startOnBoot,
path: process.execPath,
args: isDev ? [app.getAppPath()] : []
});
createMainWindow(); createMainWindow();
createTray(); createTray();
createIconGeneratorWindow(); createIconGeneratorWindow();
startTrayUpdateTimer(); startTrayUpdateTimer();
// Start minimized if enabled
if (appSettings.startMinimized) {
mainWindow.hide();
}
// Check if we have credentials // Check if we have credentials
// const hasCredentials = store.get('sessionKey') && store.get('organizationId'); // const hasCredentials = store.get('sessionKey') && store.get('organizationId');
// if (!hasCredentials) { // if (!hasCredentials) {

View file

@ -1,6 +1,6 @@
{ {
"name": "claude-usage-widget", "name": "claude-usage-widget",
"version": "1.4.2", "version": "1.4.0",
"description": "Desktop widget for Claude.ai usage monitoring", "description": "Desktop widget for Claude.ai usage monitoring",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View file

@ -17,8 +17,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
// Window position // Window position
getWindowPosition: () => ipcRenderer.invoke('get-window-position'), getWindowPosition: () => ipcRenderer.invoke('get-window-position'),
setWindowPosition: (position) => ipcRenderer.invoke('set-window-position', position), setWindowPosition: (position) => ipcRenderer.invoke('set-window-position', position),
setWindowHeight: (height) => ipcRenderer.send('set-window-height', height),
setWindowSize: (size) => ipcRenderer.send('set-window-size', size),
// Event listeners // Event listeners
onLoginSuccess: (callback) => { onLoginSuccess: (callback) => {
@ -62,16 +60,5 @@ contextBridge.exposeInMainWorld('electronAPI', {
notifyThemeChange: (theme) => ipcRenderer.invoke('notify-theme-change', theme), notifyThemeChange: (theme) => ipcRenderer.invoke('notify-theme-change', theme),
onThemeChanged: (callback) => { onThemeChanged: (callback) => {
ipcRenderer.on('theme-changed', (event, theme) => callback(theme)); ipcRenderer.on('theme-changed', (event, theme) => callback(theme));
},
// App settings
getAppSettings: () => ipcRenderer.invoke('get-app-settings'),
setAppSettings: (settings) => ipcRenderer.invoke('set-app-settings', settings),
// UI visibility settings
getUIVisibility: () => ipcRenderer.invoke('get-ui-visibility'),
setUIVisibility: (visibility) => ipcRenderer.invoke('set-ui-visibility', visibility),
onUIVisibilityChanged: (callback) => {
ipcRenderer.on('ui-visibility-changed', (event, visibility) => callback(visibility));
} }
}); });

View file

@ -43,10 +43,6 @@ async function init() {
const themeSettings = await window.electronAPI.getThemeSettings(); const themeSettings = await window.electronAPI.getThemeSettings();
applyThemePreferences(themeSettings); applyThemePreferences(themeSettings);
// Load and apply UI visibility settings
const uiVisibility = await window.electronAPI.getUIVisibility();
applyUIVisibility(uiVisibility);
if (credentials.sessionKey && credentials.organizationId) { if (credentials.sessionKey && credentials.organizationId) {
showMainContent(); showMainContent();
await fetchUsageData(); await fetchUsageData();
@ -128,12 +124,6 @@ function setupEventListeners() {
console.log('Theme changed, applying new theme'); console.log('Theme changed, applying new theme');
applyThemePreferences(theme); applyThemePreferences(theme);
}); });
// Listen for UI visibility changes from settings window
window.electronAPI.onUIVisibilityChanged((visibility) => {
console.log('UI visibility changed, applying new settings');
applyUIVisibility(visibility);
});
} }
// Fetch usage data from Claude API // Fetch usage data from Claude API
@ -444,164 +434,12 @@ function applyThemePreferences(theme) {
} }
} }
// UI visibility management
function applyUIVisibility(visibility) {
const usageSections = document.querySelectorAll('.usage-section');
if (usageSections.length < 2) return;
const sessionSection = usageSections[0];
const weeklySection = usageSections[1];
// Default to true if not explicitly set to false
const showSessionSection = visibility.showSessionSection !== false;
const showWeeklySection = visibility.showWeeklySection !== false;
const sessionShowLabel = visibility.sessionShowLabel !== false;
const sessionShowBar = visibility.sessionShowBar !== false;
const sessionShowPercentage = visibility.sessionShowPercentage !== false;
const sessionShowCircle = visibility.sessionShowCircle !== false;
const sessionShowTime = visibility.sessionShowTime !== false;
const weeklyShowLabel = visibility.weeklyShowLabel !== false;
const weeklyShowBar = visibility.weeklyShowBar !== false;
const weeklyShowPercentage = visibility.weeklyShowPercentage !== false;
const weeklyShowCircle = visibility.weeklyShowCircle !== false;
const weeklyShowTime = visibility.weeklyShowTime !== false;
// Session elements
const sessionLabel = sessionSection.querySelector('.usage-label');
const sessionBar = sessionSection.querySelector('.progress-bar');
const sessionPercentage = sessionSection.querySelector('.usage-percentage');
const sessionTimerContainer = sessionSection.querySelector('.timer-container');
const sessionTimerSVG = sessionSection.querySelector('.mini-timer');
const sessionTimeText = sessionSection.querySelector('.timer-text');
if (sessionLabel) sessionLabel.style.display = sessionShowLabel ? 'block' : 'none';
if (sessionBar) sessionBar.style.display = sessionShowBar ? 'block' : 'none';
if (sessionPercentage) sessionPercentage.style.display = sessionShowPercentage ? 'block' : 'none';
if (sessionTimerSVG) sessionTimerSVG.style.display = sessionShowCircle ? 'block' : 'none';
if (sessionTimeText) sessionTimeText.style.display = sessionShowTime ? 'block' : 'none';
// Hide timer container if both circle and time are hidden
if (sessionTimerContainer) {
sessionTimerContainer.style.display = (sessionShowCircle || sessionShowTime) ? 'flex' : 'none';
}
// Weekly elements
const weeklyLabel = weeklySection.querySelector('.usage-label');
const weeklyBar = weeklySection.querySelector('.progress-bar');
const weeklyPercentage = weeklySection.querySelector('.usage-percentage');
const weeklyTimerContainer = weeklySection.querySelector('.timer-container');
const weeklyTimerSVG = weeklySection.querySelector('.mini-timer');
const weeklyTimeText = weeklySection.querySelector('.timer-text');
if (weeklyLabel) weeklyLabel.style.display = weeklyShowLabel ? 'block' : 'none';
if (weeklyBar) weeklyBar.style.display = weeklyShowBar ? 'block' : 'none';
if (weeklyPercentage) weeklyPercentage.style.display = weeklyShowPercentage ? 'block' : 'none';
if (weeklyTimerSVG) weeklyTimerSVG.style.display = weeklyShowCircle ? 'block' : 'none';
if (weeklyTimeText) weeklyTimeText.style.display = weeklyShowTime ? 'block' : 'none';
// Hide timer container if both circle and time are hidden
if (weeklyTimerContainer) {
weeklyTimerContainer.style.display = (weeklyShowCircle || weeklyShowTime) ? 'flex' : 'none';
}
// Show/hide entire sections
if (sessionSection) sessionSection.style.display = showSessionSection ? 'flex' : 'none';
if (weeklySection) weeklySection.style.display = showWeeklySection ? 'flex' : 'none';
// Adjust window height based on visible content
adjustWindowHeight();
}
// Adjust window size based on visible content
function adjustWindowHeight() {
const usageSections = document.querySelectorAll('.usage-section');
let visibleSections = 0;
let maxWidthNeeded = 0;
usageSections.forEach(section => {
if (section.style.display !== 'none') {
visibleSections++;
// Calculate width needed for this section
let sectionWidth = 60; // Base padding (30px left + 30px right for safety)
// Check what's visible in this section
const label = section.querySelector('.usage-label');
const bar = section.querySelector('.progress-bar');
const percentage = section.querySelector('.usage-percentage');
const timerContainer = section.querySelector('.timer-container');
// Count visible elements and calculate width
let visibleElements = 0;
if (label && label.style.display !== 'none') {
sectionWidth += 120; // Label width + buffer
visibleElements++;
}
if (bar && bar.style.display !== 'none') {
sectionWidth += 150; // Minimum readable bar width
visibleElements++;
}
if (percentage && percentage.style.display !== 'none') {
sectionWidth += 55; // Percentage width
visibleElements++;
}
if (timerContainer) {
const timerSVG = timerContainer.querySelector('.mini-timer');
const timeText = timerContainer.querySelector('.timer-text');
if ((timerSVG && timerSVG.style.display !== 'none') ||
(timeText && timeText.style.display !== 'none')) {
sectionWidth += 100; // Timer container width
visibleElements++;
}
}
// Add gaps between elements (16px each)
if (visibleElements > 1) {
sectionWidth += (visibleElements - 1) * 16;
}
// Ensure minimum width even if only one element visible
sectionWidth = Math.max(sectionWidth, 200);
maxWidthNeeded = Math.max(maxWidthNeeded, sectionWidth);
}
});
// Calculate height: title bar (36px) + padding + sections
const titleBarHeight = 36;
const contentPadding = 40; // 20px top + 20px bottom
const sectionHeight = 40;
const newHeight = titleBarHeight + contentPadding + (visibleSections * sectionHeight);
// Title bar needs space for "Claude Usage" + buttons
// Minimum: logo (19px) + gap (8px) + text (~110px) + buttons (4 * 28px + 3 * 8px gaps) = ~280px
const minTitleBarWidth = 300;
// Ensure we have content width or use minimum
const contentWidth = Math.max(maxWidthNeeded, 200);
// Final dimensions - use the larger of content width or title bar width
const finalWidth = Math.max(contentWidth, minTitleBarWidth);
const finalHeight = Math.max(newHeight, titleBarHeight + 60);
// Clamp to window constraints
const clampedWidth = Math.min(Math.max(finalWidth, 320), 600);
const clampedHeight = Math.min(Math.max(finalHeight, 96), 180);
window.electronAPI.setWindowSize({ width: clampedWidth, height: clampedHeight });
}
// Auto-update management // Auto-update management
async function startAutoUpdate() { function startAutoUpdate() {
stopAutoUpdate(); stopAutoUpdate();
// Load update interval from settings
const appSettings = await window.electronAPI.getAppSettings();
const intervalMs = (appSettings.uiUpdateInterval || 300) * 1000; // Convert to milliseconds
updateInterval = setInterval(() => { updateInterval = setInterval(() => {
fetchUsageData(); fetchUsageData();
}, intervalMs); }, UPDATE_INTERVAL);
} }
function stopAutoUpdate() { function stopAutoUpdate() {

View file

@ -95,11 +95,10 @@ body {
} }
.settings-section h2 { .settings-section h2 {
font-size: 20px; font-size: 16px;
font-weight: 700; font-weight: 600;
margin-bottom: 20px; margin-bottom: 16px;
color: #e0e0e0; color: #e0e0e0;
letter-spacing: 0.3px;
} }
/* 2-Column Grid for Color Pickers */ /* 2-Column Grid for Color Pickers */
@ -234,11 +233,7 @@ body {
/* Setting Groups */ /* Setting Groups */
.setting-group { .setting-group {
margin-bottom: 24px; margin-bottom: 25px;
}
.setting-group:last-child {
margin-bottom: 0;
} }
.setting-group h3 { .setting-group h3 {
@ -313,7 +308,6 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
margin-top: 12px;
margin-bottom: 20px; margin-bottom: 20px;
} }
@ -442,17 +436,7 @@ body {
/* Collapsible Sections */ /* Collapsible Sections */
.collapsible-section { .collapsible-section {
margin-top: 20px; margin-bottom: 16px;
margin-bottom: 20px;
}
.collapsible-section:first-child {
margin-top: 0;
}
/* Add extra space when a setting-group follows a collapsible-section */
.collapsible-section + .setting-group {
margin-top: 28px;
} }
.collapsible-header { .collapsible-header {
@ -503,22 +487,6 @@ body {
padding-top: 16px; padding-top: 16px;
} }
/* Setting Item Spacing */
.setting-item {
margin-bottom: 12px;
}
.setting-item:last-child {
margin-bottom: 0;
}
.setting-item .setting-description {
margin: 4px 0 0 60px;
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
line-height: 1.4;
}
/* Theme Grid */ /* Theme Grid */
.theme-grid { .theme-grid {
display: grid; display: grid;

View file

@ -149,139 +149,6 @@
<button class="btn-secondary" id="resetThemeBtn">Reset Theme</button> <button class="btn-secondary" id="resetThemeBtn">Reset Theme</button>
</div> </div>
</div> </div>
<!-- UI Update Interval -->
<div class="setting-group">
<h3>Update Interval</h3>
<p class="setting-description">How often to refresh usage data (in seconds)</p>
<div class="slider-container">
<input type="range" id="uiUpdateInterval" min="10" max="300" value="30" step="10">
<div class="slider-value">
<span id="uiUpdateIntervalValue">30</span> seconds
</div>
</div>
</div>
<!-- Current Session Elements -->
<div class="collapsible-section">
<button class="collapsible-header" id="sessionElementsHeader">
<span>Current Session Elements</span>
<svg class="chevron" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div class="collapsible-content" id="sessionElementsContent">
<div class="setting-item" style="margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid rgba(255, 255, 255, 0.1);">
<label class="toggle-switch">
<input type="checkbox" id="showSessionSection" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Current Session</span>
</label>
<p class="setting-description">Hide entire Current Session section</p>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="sessionShowLabel" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Label Text</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="sessionShowBar" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Progress Bar</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="sessionShowPercentage" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Percentage Text</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="sessionShowCircle" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Countdown Circle</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="sessionShowTime" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Time Text</span>
</label>
</div>
</div>
</div>
<!-- Weekly Limit Elements -->
<div class="collapsible-section">
<button class="collapsible-header" id="weeklyElementsHeader">
<span>Weekly Limit Elements</span>
<svg class="chevron" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div class="collapsible-content" id="weeklyElementsContent">
<div class="setting-item" style="margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid rgba(255, 255, 255, 0.1);">
<label class="toggle-switch">
<input type="checkbox" id="showWeeklySection" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Weekly Limit</span>
</label>
<p class="setting-description">Hide entire Weekly Limit section</p>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="weeklyShowLabel" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Label Text</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="weeklyShowBar" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Progress Bar</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="weeklyShowPercentage" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Percentage Text</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="weeklyShowCircle" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Countdown Circle</span>
</label>
</div>
<div class="setting-item">
<label class="toggle-switch">
<input type="checkbox" id="weeklyShowTime" checked>
<span class="toggle-slider"></span>
<span class="toggle-label">Show Time Text</span>
</label>
</div>
</div>
</div>
</section> </section>
<div class="divider"></div> <div class="divider"></div>
@ -415,49 +282,26 @@
<div class="divider"></div> <div class="divider"></div>
<!-- Application Settings --> <!-- Support -->
<section class="settings-section"> <section class="settings-section">
<h2>Application</h2> <button class="btn-coffee" id="coffeeBtn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M17 8h1a4 4 0 1 1 0 8h-1" />
<path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z" />
<line x1="6" y1="2" x2="6" y2="4" />
<line x1="10" y1="2" x2="10" y2="4" />
<line x1="14" y1="2" x2="14" y2="4" />
</svg>
If you find this useful, buy me a coffee!
</button>
<div class="setting-group"> <!-- Disclaimer -->
<h3>Start on System Boot</h3> <p class="disclaimer">
<p class="setting-description">Automatically launch when Windows starts</p> <strong>Disclaimer:</strong> Unofficial tool not affiliated with Anthropic. Use at your own
discretion.
<label class="toggle-switch"> </p>
<input type="checkbox" id="startOnBoot">
<span class="toggle-slider"></span>
<span class="toggle-label">Enabled</span>
</label>
</div>
<div class="setting-group">
<h3>Start Minimized</h3>
<p class="setting-description">Start hidden in system tray</p>
<label class="toggle-switch">
<input type="checkbox" id="startMinimized">
<span class="toggle-slider"></span>
<span class="toggle-label">Enabled</span>
</label>
</div>
<div class="setting-group">
<h3>Close to Tray</h3>
<p class="setting-description">Minimize to tray instead of closing</p>
<label class="toggle-switch">
<input type="checkbox" id="closeToTray">
<span class="toggle-slider"></span>
<span class="toggle-label">Enabled</span>
</label>
</div>
</section> </section>
<!-- Disclaimer -->
<p class="disclaimer" style="margin-top: 32px; text-align: center;">
<strong>Disclaimer:</strong> Unofficial tool not affiliated with Anthropic. Use at your own
discretion.
</p>
</div> </div>
</div> </div>

View file

@ -8,6 +8,7 @@ const elements = {
colorDangerEnd: document.getElementById('colorDangerEnd'), colorDangerEnd: document.getElementById('colorDangerEnd'),
resetColorsBtn: document.getElementById('resetColorsBtn'), resetColorsBtn: document.getElementById('resetColorsBtn'),
logoutBtn: document.getElementById('logoutBtn'), logoutBtn: document.getElementById('logoutBtn'),
coffeeBtn: document.getElementById('coffeeBtn'),
minimizeBtn: document.getElementById('minimizeBtn'), minimizeBtn: document.getElementById('minimizeBtn'),
closeBtn: document.getElementById('closeBtn'), closeBtn: document.getElementById('closeBtn'),
// Tray icon elements // Tray icon elements
@ -44,28 +45,7 @@ const elements = {
borderColor: document.getElementById('borderColor'), borderColor: document.getElementById('borderColor'),
borderOpacity: document.getElementById('borderOpacity'), borderOpacity: document.getElementById('borderOpacity'),
borderOpacityValue: document.getElementById('borderOpacityValue'), borderOpacityValue: document.getElementById('borderOpacityValue'),
resetThemeBtn: document.getElementById('resetThemeBtn'), resetThemeBtn: document.getElementById('resetThemeBtn')
// App settings
startOnBoot: document.getElementById('startOnBoot'),
startMinimized: document.getElementById('startMinimized'),
closeToTray: document.getElementById('closeToTray'),
uiUpdateInterval: document.getElementById('uiUpdateInterval'),
uiUpdateIntervalValue: document.getElementById('uiUpdateIntervalValue'),
// Section visibility
showSessionSection: document.getElementById('showSessionSection'),
showWeeklySection: document.getElementById('showWeeklySection'),
// Session element visibility
sessionShowLabel: document.getElementById('sessionShowLabel'),
sessionShowBar: document.getElementById('sessionShowBar'),
sessionShowPercentage: document.getElementById('sessionShowPercentage'),
sessionShowCircle: document.getElementById('sessionShowCircle'),
sessionShowTime: document.getElementById('sessionShowTime'),
// Weekly element visibility
weeklyShowLabel: document.getElementById('weeklyShowLabel'),
weeklyShowBar: document.getElementById('weeklyShowBar'),
weeklyShowPercentage: document.getElementById('weeklyShowPercentage'),
weeklyShowCircle: document.getElementById('weeklyShowCircle'),
weeklyShowTime: document.getElementById('weeklyShowTime')
}; };
// Initialize // Initialize
@ -85,14 +65,6 @@ async function init() {
const interval = await window.electronAPI.getTrayUpdateInterval(); const interval = await window.electronAPI.getTrayUpdateInterval();
loadTrayUpdateInterval(interval); loadTrayUpdateInterval(interval);
// Load app settings
const appSettings = await window.electronAPI.getAppSettings();
loadAppSettings(appSettings);
// Load UI visibility settings
const uiVisibility = await window.electronAPI.getUIVisibility();
loadUIVisibility(uiVisibility);
// Load theme settings // Load theme settings
await loadThemeSettings(); await loadThemeSettings();
@ -142,6 +114,10 @@ function setupEventListeners() {
window.electronAPI.openLogin(); window.electronAPI.openLogin();
}); });
elements.coffeeBtn.addEventListener('click', () => {
window.electronAPI.openExternal('https://paypal.me/SlavomirDurej?country.x=GB&locale.x=en_GB');
});
// Window controls // Window controls
elements.minimizeBtn.addEventListener('click', () => { elements.minimizeBtn.addEventListener('click', () => {
window.electronAPI.minimizeWindow(); window.electronAPI.minimizeWindow();
@ -244,35 +220,6 @@ function setupEventListeners() {
// Reset theme button // Reset theme button
elements.resetThemeBtn.addEventListener('click', resetTheme); elements.resetThemeBtn.addEventListener('click', resetTheme);
// App settings event listeners
elements.startOnBoot.addEventListener('change', saveAppSettings);
elements.startMinimized.addEventListener('change', saveAppSettings);
elements.closeToTray.addEventListener('change', saveAppSettings);
elements.uiUpdateInterval.addEventListener('input', () => {
elements.uiUpdateIntervalValue.textContent = elements.uiUpdateInterval.value;
});
elements.uiUpdateInterval.addEventListener('change', saveAppSettings);
// Section visibility event listeners
elements.showSessionSection.addEventListener('change', saveUIVisibility);
elements.showWeeklySection.addEventListener('change', saveUIVisibility);
// Session element visibility event listeners
elements.sessionShowLabel.addEventListener('change', saveUIVisibility);
elements.sessionShowBar.addEventListener('change', saveUIVisibility);
elements.sessionShowPercentage.addEventListener('change', saveUIVisibility);
elements.sessionShowCircle.addEventListener('change', saveUIVisibility);
elements.sessionShowTime.addEventListener('change', saveUIVisibility);
// Weekly element visibility event listeners
elements.weeklyShowLabel.addEventListener('change', saveUIVisibility);
elements.weeklyShowBar.addEventListener('change', saveUIVisibility);
elements.weeklyShowPercentage.addEventListener('change', saveUIVisibility);
elements.weeklyShowCircle.addEventListener('change', saveUIVisibility);
elements.weeklyShowTime.addEventListener('change', saveUIVisibility);
} }
// Helper functions // Helper functions
@ -516,69 +463,6 @@ function applyThemeToSettings(theme) {
}); });
} }
// App settings functions
function loadAppSettings(settings) {
elements.startOnBoot.checked = settings.startOnBoot || false;
elements.startMinimized.checked = settings.startMinimized || false;
elements.closeToTray.checked = settings.closeToTray || false;
elements.uiUpdateInterval.value = settings.uiUpdateInterval || 30;
elements.uiUpdateIntervalValue.textContent = settings.uiUpdateInterval || 30;
}
async function saveAppSettings() {
const settings = {
startOnBoot: elements.startOnBoot.checked,
startMinimized: elements.startMinimized.checked,
closeToTray: elements.closeToTray.checked,
uiUpdateInterval: parseInt(elements.uiUpdateInterval.value)
};
await window.electronAPI.setAppSettings(settings);
}
// UI visibility functions
function loadUIVisibility(visibility) {
// Section visibility
elements.showSessionSection.checked = visibility.showSessionSection !== false;
elements.showWeeklySection.checked = visibility.showWeeklySection !== false;
// Session elements
elements.sessionShowLabel.checked = visibility.sessionShowLabel !== false;
elements.sessionShowBar.checked = visibility.sessionShowBar !== false;
elements.sessionShowPercentage.checked = visibility.sessionShowPercentage !== false;
elements.sessionShowCircle.checked = visibility.sessionShowCircle !== false;
elements.sessionShowTime.checked = visibility.sessionShowTime !== false;
// Weekly elements
elements.weeklyShowLabel.checked = visibility.weeklyShowLabel !== false;
elements.weeklyShowBar.checked = visibility.weeklyShowBar !== false;
elements.weeklyShowPercentage.checked = visibility.weeklyShowPercentage !== false;
elements.weeklyShowCircle.checked = visibility.weeklyShowCircle !== false;
elements.weeklyShowTime.checked = visibility.weeklyShowTime !== false;
}
async function saveUIVisibility() {
const visibility = {
// Section visibility
showSessionSection: elements.showSessionSection.checked,
showWeeklySection: elements.showWeeklySection.checked,
// Session elements
sessionShowLabel: elements.sessionShowLabel.checked,
sessionShowBar: elements.sessionShowBar.checked,
sessionShowPercentage: elements.sessionShowPercentage.checked,
sessionShowCircle: elements.sessionShowCircle.checked,
sessionShowTime: elements.sessionShowTime.checked,
// Weekly elements
weeklyShowLabel: elements.weeklyShowLabel.checked,
weeklyShowBar: elements.weeklyShowBar.checked,
weeklyShowPercentage: elements.weeklyShowPercentage.checked,
weeklyShowCircle: elements.weeklyShowCircle.checked,
weeklyShowTime: elements.weeklyShowTime.checked
};
await window.electronAPI.setUIVisibility(visibility);
}
// Setup collapsible sections // Setup collapsible sections
function setupCollapsibleSections() { function setupCollapsibleSections() {
const collapsibleHeaders = document.querySelectorAll('.collapsible-header'); const collapsibleHeaders = document.querySelectorAll('.collapsible-header');

View file

@ -289,25 +289,17 @@ body {
/* Main Content */ /* Main Content */
.content { .content {
padding: 20px; padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
box-sizing: border-box;
} }
.usage-section { .usage-section {
display: flex; display: grid;
grid-template-columns: 125px 1fr 45px 100px;
/* Fixed width labels for alignment */
align-items: center; align-items: center;
justify-content: center; gap: 12px;
gap: 16px; margin-bottom: 2px;
margin-bottom: 8px; padding: 0 16px;
padding: 0 20px; height: 32px;
min-height: 32px;
width: 100%;
max-width: 100%;
box-sizing: border-box;
} }
.usage-section:last-of-type { .usage-section:last-of-type {
@ -339,8 +331,6 @@ body {
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
white-space: nowrap; white-space: nowrap;
flex-shrink: 0;
min-width: 110px;
} }
.usage-percentage { .usage-percentage {
@ -348,8 +338,6 @@ body {
font-size: 13px; font-size: 13px;
font-weight: 700; font-weight: 700;
text-align: right; text-align: right;
flex-shrink: 0;
min-width: 40px;
} }
/* Progress Bar */ /* Progress Bar */
@ -359,7 +347,7 @@ body {
border-radius: 3px; border-radius: 3px;
overflow: hidden; overflow: hidden;
margin: 0; margin: 0;
flex: 1; /* Remove margin as it is in grid */
min-width: 80px; min-width: 80px;
} }
@ -384,10 +372,12 @@ body {
.timer-container { .timer-container {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: space-between;
/* Text on left, chart on right */
gap: 8px; gap: 8px;
flex-shrink: 0; /* space between text and pie chart */
min-width: 90px; padding-left: 8px;
/* Alignment adjustment */
} }
.mini-timer { .mini-timer {