Release v1.4.2: Comprehensive UI customization and settings overhaul

Major Features:
- Added complete UI element visibility controls (show/hide any element independently)
- Added system tray icon with live usage percentage display
- Added customizable tray icon colors and update interval (10-300 seconds)
- Added application settings (start on boot, start minimized, close to tray)
- Added theme customization (backgrounds, text colors, borders, opacity controls)
- Added configurable update intervals for both UI and tray (10-300 seconds)
- Added static color mode option for progress bars
- Added dynamic window resizing based on visible elements

UI/UX Improvements:
- Improved settings organization with collapsible sections
- Consistent typography and spacing throughout settings panel
- Larger, bolder section headers for better visual hierarchy
- Removed donations button from settings
- Element visibility controls for both Current Session and Weekly Limit sections
- Master toggles for entire sections
- Independent toggles for labels, bars, percentages, circles, and time text

Bug Fixes:
- Fixed element centering and responsive layout
- Fixed timer container visibility handling when children are hidden
- Fixed slider overlap with text above
- Fixed window resizing to properly fit content
- Fixed manual resizing while maintaining centered content

Technical Changes:
- Changed UI update interval from 1-10 minutes to 10-300 seconds
- Standardized all settings to use .setting-group wrapper class
- Removed inline styles in favor of CSS classes
- Improved CSS organization with consistent spacing rules
- Added proper flexbox centering for responsive layouts
- Window constraints: 320-600px width, 96-180px height
- Resizable window with content-based auto-sizing

Documentation:
- Comprehensive README update with all v1.4.2 features
- Added screenshot placeholders for user documentation
- Detailed settings explanations
- Troubleshooting section expanded
- Version history updated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude 2025-12-24 19:37:35 -05:00
parent c4c758fbbd
commit 3b5ef59c32
9 changed files with 980 additions and 123 deletions

429
README.md
View file

@ -1,21 +1,44 @@
# Claude Usage Widget
A beautiful, standalone Windows desktop widget that displays your Claude.ai usage statistics in real-time.
A beautiful, highly customizable Windows desktop widget that displays your Claude.ai usage statistics in real-time with system tray integration.
![Claude Usage Widget](assets/claude-usage-screenshot.jpg)
**[SCREENSHOT: Main widget showing session and weekly usage]**
## Features
- 🎯 **Real-time Usage Tracking** - Monitor both session and weekly usage limits
- 📊 **Visual Progress Bars** - Clean, gradient progress indicators
- ⏱️ **Countdown Timers** - Circular timers showing time until reset
- 🔄 **Auto-refresh** - Updates every 5 minutes automatically
- 🎨 **Modern UI** - Sleek, draggable widget with dark theme
- 🎨 **Customizable Colors** - Personalize progress bar colors for each usage level
- ⚙️ **Settings Panel** - Easy-to-use settings window for customization
- 🔒 **Secure** - Encrypted credential storage
### Core Features
- 🎯 **Real-time Usage Tracking** - Monitor both 5-hour session and 7-day weekly usage limits
- 📊 **Visual Progress Bars** - Clean, gradient progress indicators with customizable colors
- ⏱️ **Countdown Timers** - Circular timers showing time until session/weekly reset
- 🔄 **Configurable Auto-refresh** - Adjustable update interval (10-300 seconds)
- 🎨 **Fully Customizable UI** - Toggle any element on/off, customize all colors and theme
- 📍 **Always on Top** - Stays visible across all workspaces
- 💾 **System Tray** - Minimizes to tray for easy access
- 🔒 **Secure** - Encrypted credential storage using electron-store
### 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
@ -54,71 +77,189 @@ The installer will be created in the `dist/` folder.
### First Launch
1. Launch the widget
2. Click "Login to Claude" when prompted
3. A browser window will open - login to your Claude.ai account
4. The widget will automatically capture your session
5. Usage data will start displaying immediately
2. The app will attempt to auto-login using stored credentials
3. If no credentials exist, click "Login to Claude"
4. A browser window will open - login to your Claude.ai account
5. The widget will automatically capture your session
6. Usage data will start displaying immediately
**[SCREENSHOT: Login screen]**
### Widget Controls
- **Drag** - Click and drag the title bar to move the widget
- **Refresh** - Click the refresh icon to update data immediately
- **Minimize** - Click the minus icon to hide to system tray
- **Close** - Click the X to minimize to tray (doesn't exit)
**Title Bar:**
- **Drag** - Click and drag anywhere on the title bar to move the widget
- **Settings Icon** - Open the comprehensive settings panel
- **Refresh Icon** - Update usage data immediately
- **Minimize** - Hide to system tray
- **Close** - Minimize to tray or exit (depending on settings)
### System Tray Menu
**[SCREENSHOT: Widget with controls highlighted]**
Right-click the tray icon for:
- Show/Hide widget
- Refresh usage data
- Re-login (if session expires)
- Settings - Open customization panel
- Exit application
### System Tray
**Tray Icon:**
- Displays real-time usage percentage
- Color changes based on usage level (normal/warning/danger)
- Switches between session and weekly usage (configurable in settings)
- 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
### Current Session
- **Progress Bar** - Shows usage from 0-100%
- **Timer** - Time remaining until 5-hour session resets
### Current Session (5-Hour Limit)
- **Progress Bar** - Visual representation of usage (0-100%)
- **Percentage** - Exact usage percentage
- **Countdown Circle** - Circular timer showing progress toward reset
- **Time Remaining** - Hours and minutes until session resets (e.g., "2h 34m")
- **Color Coding**:
- Purple: Normal usage (0-74%)
- Orange: High usage (75-89%)
- Red: Critical usage (90-100%)
### Weekly Limit
- **Progress Bar** - Shows weekly usage from 0-100%
- **Timer** - Time remaining until weekly reset (Wednesdays 7:00 AM)
- **Same color coding** as session usage
### Weekly Limit (7-Day Rolling Window)
- **Progress Bar** - Weekly usage from 0-100%
- **Percentage** - Exact weekly usage percentage
- **Countdown Circle** - Progress toward weekly reset
- **Time Remaining** - Days/hours until weekly reset (e.g., "3d 5h")
- Same color coding as session usage
## Customization
**[SCREENSHOT: Usage display explanation with labels]**
### Color Preferences
## Window Behavior
Customize the progress bar colors to match your preferences:
### Dynamic Resizing
- 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
1. Right-click the system tray icon
2. Select "Settings"
3. Use the color pickers to customize each usage level:
- **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
### Manual Resizing
- Drag edges to resize window
- Constraints: 320-600px width, 96-180px height
- Content stays centered at all sizes
![Settings Window - Color Customization](assets/settings-screenshot.png)
### Position Persistence
- Window position saved automatically when moved
- Restored on next launch
- Per-display awareness
### Auto-start on Windows Boot
## Keyboard Shortcuts
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)
```
Currently, the widget does not have keyboard shortcuts. This may be added in a future version.
## Troubleshooting
@ -126,16 +267,31 @@ const UPDATE_INTERVAL = 5 * 60 * 1000; // Change to your preference (in millisec
- Your Claude.ai session may have expired
- Click "Login to Claude" to re-authenticate
- Check that you're logging into the correct account
- Verify Claude.ai is accessible in your region
### Widget not updating
- Check your internet connection
- Click the refresh button manually
- Ensure Claude.ai is accessible in your region
- Try re-logging in from the system tray menu
- Ensure Claude.ai API is accessible
- Try increasing update interval if experiencing rate limits
- Re-login from system tray menu
### Widget position not saving
- Window position is now saved automatically when you drag it
- Position will be restored when you restart the app
### Tray icon not showing/updating
- Check that "Show Percentage Text" is enabled to see text
- Verify update interval isn't too long
- 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
```bash
@ -146,10 +302,11 @@ npm install
## Privacy & Security
- Your session credentials are stored **locally only** using encrypted storage
- No data is sent to any third-party servers
- The widget only communicates with Claude.ai official API
- Session cookies are stored using Electron's secure storage
- **Local Storage Only** - Your session credentials are stored locally using encrypted electron-store
- **No Third-Party Servers** - No data is sent to any servers except Claude.ai official API
- **Official API Only** - Widget only communicates with Claude.ai API endpoints
- **Secure Cookies** - Session cookies stored using Electron's secure storage
- **Open Source** - All code is publicly available for audit
## Technical Details
@ -157,11 +314,14 @@ npm install
- Electron 28.0.0
- Pure JavaScript (no framework overhead)
- Native Node.js APIs
- electron-store for secure storage
- electron-store for encrypted secure storage
- axios for HTTP requests
- Canvas API for tray icon generation
**API Endpoint:**
**API Endpoints:**
```
https://claude.ai/api/organizations/{org_id}/usage
https://claude.ai/api/organizations
```
**Storage Location:**
@ -169,37 +329,152 @@ https://claude.ai/api/organizations/{org_id}/usage
%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
### Planned Features
- [ ] macOS support
- [ ] Linux support
- [x] Custom color themes
- [ ] Notification alerts at usage thresholds
- [x] Remember window position
- [x] Settings panel
- [ ] 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] Remember window position
- [x] Settings panel
- [x] System tray integration
- [x] Tray icon with live usage
- [x] Element visibility controls
- [x] Theme customization
- [x] Configurable update intervals
- [x] Auto-start on boot
- [x] Static color mode
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Contributions are welcome! Here's how you can help:
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
MIT License - feel free to use and modify as needed.
MIT License - feel free to use, modify, and distribute as needed.
See [LICENSE](LICENSE) file for full details.
## Disclaimer
This is an unofficial tool and is not affiliated with or endorsed by Anthropic. Use at your own discretion.
**This is an unofficial tool and is not affiliated with, endorsed by, or connected to Anthropic or Claude.ai in any way.**
- 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
If you encounter issues:
1. Check the [Issues](issues) page
2. Create a new issue with details about your problem
3. Include your OS version and any error messages
1. **Check existing issues** - [Issues page](../../issues)
2. **Create a new issue** with:
- 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!**

97
main.js
View file

@ -31,10 +31,14 @@ function createMainWindow() {
const windowOptions = {
width: WIDGET_WIDTH,
height: WIDGET_HEIGHT,
minWidth: 320,
maxWidth: 600,
minHeight: 96,
maxHeight: 180,
frame: false,
transparent: true,
alwaysOnTop: true,
resizable: false,
resizable: true,
skipTaskbar: false,
icon: path.join(__dirname, 'assets/icon.ico'),
webPreferences: {
@ -579,7 +583,12 @@ ipcMain.on('minimize-window', () => {
});
ipcMain.on('close-window', () => {
app.quit();
const appSettings = store.get('appSettings', { closeToTray: false });
if (appSettings.closeToTray) {
mainWindow.hide();
} else {
app.quit();
}
});
ipcMain.handle('get-window-position', () => {
@ -597,6 +606,19 @@ ipcMain.handle('set-window-position', (event, { x, y }) => {
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) => {
shell.openExternal(url);
});
@ -735,13 +757,84 @@ ipcMain.handle('notify-theme-change', (event, theme) => {
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')) {
app.setLoginItemSettings({
openAtLogin: settings.startOnBoot,
path: process.execPath
});
}
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.whenReady().then(() => {
// Apply startup settings
const appSettings = store.get('appSettings', {
startOnBoot: false,
startMinimized: false,
closeToTray: false
});
// Set auto-launch
app.setLoginItemSettings({
openAtLogin: appSettings.startOnBoot,
path: process.execPath
});
createMainWindow();
createTray();
createIconGeneratorWindow();
startTrayUpdateTimer();
// Start minimized if enabled
if (appSettings.startMinimized) {
mainWindow.hide();
}
// Check if we have credentials
// const hasCredentials = store.get('sessionKey') && store.get('organizationId');
// if (!hasCredentials) {

View file

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

View file

@ -17,6 +17,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
// Window position
getWindowPosition: () => ipcRenderer.invoke('get-window-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
onLoginSuccess: (callback) => {
@ -60,5 +62,16 @@ contextBridge.exposeInMainWorld('electronAPI', {
notifyThemeChange: (theme) => ipcRenderer.invoke('notify-theme-change', theme),
onThemeChanged: (callback) => {
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,6 +43,10 @@ async function init() {
const themeSettings = await window.electronAPI.getThemeSettings();
applyThemePreferences(themeSettings);
// Load and apply UI visibility settings
const uiVisibility = await window.electronAPI.getUIVisibility();
applyUIVisibility(uiVisibility);
if (credentials.sessionKey && credentials.organizationId) {
showMainContent();
await fetchUsageData();
@ -124,6 +128,12 @@ function setupEventListeners() {
console.log('Theme changed, applying new 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
@ -434,12 +444,164 @@ 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
function startAutoUpdate() {
async function startAutoUpdate() {
stopAutoUpdate();
// Load update interval from settings
const appSettings = await window.electronAPI.getAppSettings();
const intervalMs = (appSettings.uiUpdateInterval || 300) * 1000; // Convert to milliseconds
updateInterval = setInterval(() => {
fetchUsageData();
}, UPDATE_INTERVAL);
}, intervalMs);
}
function stopAutoUpdate() {

View file

@ -95,10 +95,11 @@ body {
}
.settings-section h2 {
font-size: 16px;
font-weight: 600;
margin-bottom: 16px;
font-size: 20px;
font-weight: 700;
margin-bottom: 20px;
color: #e0e0e0;
letter-spacing: 0.3px;
}
/* 2-Column Grid for Color Pickers */
@ -233,7 +234,11 @@ body {
/* Setting Groups */
.setting-group {
margin-bottom: 25px;
margin-bottom: 24px;
}
.setting-group:last-child {
margin-bottom: 0;
}
.setting-group h3 {
@ -308,6 +313,7 @@ body {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 12px;
margin-bottom: 20px;
}
@ -436,7 +442,17 @@ body {
/* Collapsible Sections */
.collapsible-section {
margin-bottom: 16px;
margin-top: 20px;
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 {
@ -487,6 +503,22 @@ body {
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 {
display: grid;

View file

@ -149,6 +149,139 @@
<button class="btn-secondary" id="resetThemeBtn">Reset Theme</button>
</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>
<div class="divider"></div>
@ -282,26 +415,49 @@
<div class="divider"></div>
<!-- Support -->
<!-- Application Settings -->
<section class="settings-section">
<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>
<h2>Application</h2>
<!-- Disclaimer -->
<p class="disclaimer">
<strong>Disclaimer:</strong> Unofficial tool not affiliated with Anthropic. Use at your own
discretion.
</p>
<div class="setting-group">
<h3>Start on System Boot</h3>
<p class="setting-description">Automatically launch when Windows starts</p>
<label class="toggle-switch">
<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>
<!-- 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>

View file

@ -8,7 +8,6 @@ const elements = {
colorDangerEnd: document.getElementById('colorDangerEnd'),
resetColorsBtn: document.getElementById('resetColorsBtn'),
logoutBtn: document.getElementById('logoutBtn'),
coffeeBtn: document.getElementById('coffeeBtn'),
minimizeBtn: document.getElementById('minimizeBtn'),
closeBtn: document.getElementById('closeBtn'),
// Tray icon elements
@ -45,7 +44,28 @@ const elements = {
borderColor: document.getElementById('borderColor'),
borderOpacity: document.getElementById('borderOpacity'),
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
@ -65,6 +85,14 @@ async function init() {
const interval = await window.electronAPI.getTrayUpdateInterval();
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
await loadThemeSettings();
@ -114,10 +142,6 @@ function setupEventListeners() {
window.electronAPI.openLogin();
});
elements.coffeeBtn.addEventListener('click', () => {
window.electronAPI.openExternal('https://paypal.me/SlavomirDurej?country.x=GB&locale.x=en_GB');
});
// Window controls
elements.minimizeBtn.addEventListener('click', () => {
window.electronAPI.minimizeWindow();
@ -220,6 +244,35 @@ function setupEventListeners() {
// Reset theme button
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
@ -463,6 +516,69 @@ 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
function setupCollapsibleSections() {
const collapsibleHeaders = document.querySelectorAll('.collapsible-header');

View file

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