claude-usage-widget/preload.js
Claude c4c758fbbd Add dynamic tray icon with usage display and comprehensive theming system
Implemented several major enhancements to the Claude Usage Widget:

Features Added:
- Dynamic tray icon with circular progress pie chart showing usage percentage
- Configurable tray display mode (Session/Weekly) and refresh interval (10-300s, default 30s)
- Optional percentage text overlay in tray icon (default: off)
- Independent color customization for tray icon (normal/warning/danger states)
- Static color mode toggle for both main window and tray icon (single color vs gradients)
- Comprehensive theming system for main widget and settings window:
  * Background gradient customization
  * Primary and secondary text color controls
  * Title bar color with opacity slider
  * Border color with opacity slider
- Collapsible settings sections with smooth animations (minimized by default)
- Custom dark-themed scrollbar with purple accents matching app theme
- Real-time theme synchronization between main and settings windows

Technical Implementation:
- Zero-dependency tray icon generation using Electron's built-in Canvas API via hidden BrowserWindow
- IPC-based bi-directional communication for real-time theme updates
- Persistent settings storage using electron-store
- Conditional UI rendering for static vs gradient color modes
- All changes maintain upstream compatibility (no external npm dependencies added)

Files Modified:
- main.js: Added icon generator window, tray icon generation, theme IPC handlers
- preload.js: Added IPC methods for tray settings and theme management
- src/renderer/app.js: Added theme application and usage data forwarding
- src/renderer/settings.html: Added theming controls, collapsible sections, toggles
- src/renderer/settings.css: Added styles for new UI components and custom scrollbar
- src/renderer/settings.js: Added theme/tray settings logic with real-time updates

Files Added:
- src/icon-generator.html: Hidden window for canvas-based tray icon generation

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-24 16:09:31 -05:00

64 lines
2.7 KiB
JavaScript

const { contextBridge, ipcRenderer } = require('electron');
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('electronAPI', {
// Credentials management
getCredentials: () => ipcRenderer.invoke('get-credentials'),
saveCredentials: (credentials) => ipcRenderer.invoke('save-credentials', credentials),
deleteCredentials: () => ipcRenderer.invoke('delete-credentials'),
// Window controls
minimizeWindow: () => ipcRenderer.send('minimize-window'),
closeWindow: () => ipcRenderer.send('close-window'),
openLogin: () => ipcRenderer.send('open-login'),
openSettings: () => ipcRenderer.send('open-settings'),
// Window position
getWindowPosition: () => ipcRenderer.invoke('get-window-position'),
setWindowPosition: (position) => ipcRenderer.invoke('set-window-position', position),
// Event listeners
onLoginSuccess: (callback) => {
ipcRenderer.on('login-success', (event, data) => callback(data));
},
onRefreshUsage: (callback) => {
ipcRenderer.on('refresh-usage', () => callback());
},
onSessionExpired: (callback) => {
ipcRenderer.on('session-expired', () => callback());
},
onSilentLoginStarted: (callback) => {
ipcRenderer.on('silent-login-started', () => callback());
},
onSilentLoginFailed: (callback) => {
ipcRenderer.on('silent-login-failed', () => callback());
},
// API
fetchUsageData: () => ipcRenderer.invoke('fetch-usage-data'),
openExternal: (url) => ipcRenderer.send('open-external', url),
// Color preferences
getColorPreferences: () => ipcRenderer.invoke('get-color-preferences'),
setColorPreferences: (preferences) => ipcRenderer.invoke('set-color-preferences', preferences),
notifyColorChange: (preferences) => ipcRenderer.invoke('notify-color-change', preferences),
onColorsChanged: (callback) => {
ipcRenderer.on('colors-changed', (event, preferences) => callback(preferences));
},
// Tray icon
sendUsageToMain: (data) => ipcRenderer.send('usage-data-update', data),
getTraySettings: () => ipcRenderer.invoke('get-tray-settings'),
setTraySettings: (settings) => ipcRenderer.send('set-tray-settings', settings),
getTrayUpdateInterval: () => ipcRenderer.invoke('get-tray-update-interval'),
setTrayUpdateInterval: (seconds) => ipcRenderer.send('set-tray-update-interval', seconds),
// Theme settings
getThemeSettings: () => ipcRenderer.invoke('get-theme-settings'),
setThemeSettings: (theme) => ipcRenderer.invoke('set-theme-settings', theme),
notifyThemeChange: (theme) => ipcRenderer.invoke('notify-theme-change', theme),
onThemeChanged: (callback) => {
ipcRenderer.on('theme-changed', (event, theme) => callback(theme));
}
});