Add customizable color preferences with separate settings window
## New Features - **Customizable Progress Bar Colors**: Users can now customize the colors for normal, warning, and danger states through a dedicated settings window - **Separate Settings Window**: Settings now open in a frameless, resizable window (500x680px) with live preview of color changes - **CSS Variables**: Converted hardcoded colors to CSS custom properties for dynamic theming ## Files Modified - **main.js**: Added settings window creation, color preference storage (electron-store), and IPC handlers - **preload.js**: Exposed color preference and settings IPC methods to renderer - **app.js**: Implemented color preference loading and live updates from settings window - **styles.css**: Added CSS variables for customizable colors, updated scrollbar styling - **index.html**: Removed unused settings overlay - **.gitignore**: Added CLAUDE_NOTES.md to prevent credential leaks ## Files Added - **src/renderer/settings.html**: Settings window UI with 2-column color picker layout - **src/renderer/settings.js**: Settings window logic and color management - **src/renderer/settings-styles.css**: Settings window styling - **CHANGES.md**: Tracking document for modifications ## Technical Details - Color preferences stored in electron-store with encryption - Live color updates via IPC communication between settings and main windows - Default color scheme: Purple (normal), Orange (warning), Red (danger) - Settings accessible via tray menu or settings button 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6ea9251bfe
commit
0e46ef084e
10 changed files with 615 additions and 62 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -36,3 +36,4 @@ yarn-error.log*
|
||||||
# Cache
|
# Cache
|
||||||
.cache/
|
.cache/
|
||||||
.temp/
|
.temp/
|
||||||
|
CLAUDE_NOTES.md
|
||||||
|
|
|
||||||
39
CHANGES.md
Normal file
39
CHANGES.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Changes for Upstream PR
|
||||||
|
|
||||||
|
## Feature: Customizable Progress Bar Colors
|
||||||
|
|
||||||
|
### Modified Files
|
||||||
|
|
||||||
|
#### main.js
|
||||||
|
- Added `DEFAULT_COLOR_PREFERENCES` constant with default color scheme (normal, warning, danger states)
|
||||||
|
- Added IPC handler `get-color-preferences` to retrieve stored color preferences from electron-store
|
||||||
|
- Added IPC handler `set-color-preferences` to save color preferences to electron-store
|
||||||
|
- Storage schema: `colorPreferences: { normal: {start, end}, warning: {start, end}, danger: {start, end} }`
|
||||||
|
|
||||||
|
#### src/renderer/styles.css
|
||||||
|
- TODO: Add CSS custom properties (--color-normal-start, --color-normal-end, etc.)
|
||||||
|
- TODO: Convert hardcoded gradient colors to use CSS variables
|
||||||
|
- TODO: Apply to both .progress-fill and .timer-progress elements
|
||||||
|
|
||||||
|
#### src/renderer/app.js
|
||||||
|
- TODO: Add `applyColorPreferences()` function to set CSS custom properties
|
||||||
|
- TODO: Load and apply color preferences on init
|
||||||
|
- TODO: Add event listeners for color picker changes
|
||||||
|
|
||||||
|
#### src/renderer/index.html
|
||||||
|
- TODO: Add color picker UI in settings overlay (6 inputs for simplified scheme)
|
||||||
|
- TODO: Add "Reset to Defaults" button
|
||||||
|
|
||||||
|
#### preload.js
|
||||||
|
- TODO: Expose `getColorPreferences` and `setColorPreferences` IPC methods
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Feature: Dynamic Tray Icon with Usage Display (Planned)
|
||||||
|
- Not yet started
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Storage Changes
|
||||||
|
- Using existing electron-store, no additional files
|
||||||
|
- New keys: `colorPreferences`, `trayDisplayMode` (planned)
|
||||||
68
main.js
68
main.js
|
|
@ -10,11 +10,14 @@ const store = new Store({
|
||||||
let mainWindow = null;
|
let mainWindow = null;
|
||||||
let loginWindow = null;
|
let loginWindow = null;
|
||||||
let silentLoginWindow = null;
|
let silentLoginWindow = null;
|
||||||
|
let settingsWindow = null;
|
||||||
let tray = null;
|
let tray = null;
|
||||||
|
|
||||||
// Window configuration
|
// Window configuration
|
||||||
const WIDGET_WIDTH = 480;
|
const WIDGET_WIDTH = 480;
|
||||||
const WIDGET_HEIGHT = 140;
|
const WIDGET_HEIGHT = 140;
|
||||||
|
const SETTINGS_WIDTH = 500;
|
||||||
|
const SETTINGS_HEIGHT = 680;
|
||||||
|
|
||||||
function createMainWindow() {
|
function createMainWindow() {
|
||||||
// Load saved position or use defaults
|
// Load saved position or use defaults
|
||||||
|
|
@ -347,7 +350,7 @@ function createTray() {
|
||||||
{
|
{
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
click: () => {
|
click: () => {
|
||||||
// TODO: Open settings window
|
createSettingsWindow();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -380,6 +383,40 @@ function createTray() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createSettingsWindow() {
|
||||||
|
if (settingsWindow) {
|
||||||
|
settingsWindow.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsWindow = new BrowserWindow({
|
||||||
|
width: SETTINGS_WIDTH,
|
||||||
|
height: SETTINGS_HEIGHT,
|
||||||
|
title: 'Settings - Claude Usage Widget',
|
||||||
|
frame: false,
|
||||||
|
resizable: true,
|
||||||
|
minimizable: true,
|
||||||
|
maximizable: false,
|
||||||
|
icon: path.join(__dirname, 'assets/icon.ico'),
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
preload: path.join(__dirname, 'preload.js')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
settingsWindow.loadFile('src/renderer/settings.html');
|
||||||
|
|
||||||
|
settingsWindow.on('closed', () => {
|
||||||
|
settingsWindow = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Development tools
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
settingsWindow.webContents.openDevTools({ mode: 'detach' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IPC Handlers
|
// IPC Handlers
|
||||||
ipcMain.handle('get-credentials', () => {
|
ipcMain.handle('get-credentials', () => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -416,6 +453,10 @@ ipcMain.on('open-login', () => {
|
||||||
createLoginWindow();
|
createLoginWindow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('open-settings', () => {
|
||||||
|
createSettingsWindow();
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.on('minimize-window', () => {
|
ipcMain.on('minimize-window', () => {
|
||||||
if (mainWindow) mainWindow.hide();
|
if (mainWindow) mainWindow.hide();
|
||||||
});
|
});
|
||||||
|
|
@ -443,6 +484,31 @@ ipcMain.on('open-external', (event, url) => {
|
||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Color preferences handlers
|
||||||
|
const DEFAULT_COLOR_PREFERENCES = {
|
||||||
|
normal: { start: '#8b5cf6', end: '#a78bfa' },
|
||||||
|
warning: { start: '#f59e0b', end: '#fbbf24' },
|
||||||
|
danger: { start: '#ef4444', end: '#f87171' }
|
||||||
|
};
|
||||||
|
|
||||||
|
ipcMain.handle('get-color-preferences', () => {
|
||||||
|
const saved = store.get('colorPreferences');
|
||||||
|
return saved || DEFAULT_COLOR_PREFERENCES;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('set-color-preferences', (event, preferences) => {
|
||||||
|
store.set('colorPreferences', preferences);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('notify-color-change', (event, preferences) => {
|
||||||
|
// Notify main window to update colors
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
mainWindow.webContents.send('colors-changed', preferences);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('fetch-usage-data', async () => {
|
ipcMain.handle('fetch-usage-data', async () => {
|
||||||
console.log('[Main] fetch-usage-data handler called');
|
console.log('[Main] fetch-usage-data handler called');
|
||||||
const sessionKey = store.get('sessionKey');
|
const sessionKey = store.get('sessionKey');
|
||||||
|
|
|
||||||
11
preload.js
11
preload.js
|
|
@ -12,6 +12,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
minimizeWindow: () => ipcRenderer.send('minimize-window'),
|
minimizeWindow: () => ipcRenderer.send('minimize-window'),
|
||||||
closeWindow: () => ipcRenderer.send('close-window'),
|
closeWindow: () => ipcRenderer.send('close-window'),
|
||||||
openLogin: () => ipcRenderer.send('open-login'),
|
openLogin: () => ipcRenderer.send('open-login'),
|
||||||
|
openSettings: () => ipcRenderer.send('open-settings'),
|
||||||
|
|
||||||
// Window position
|
// Window position
|
||||||
getWindowPosition: () => ipcRenderer.invoke('get-window-position'),
|
getWindowPosition: () => ipcRenderer.invoke('get-window-position'),
|
||||||
|
|
@ -36,5 +37,13 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
|
||||||
// API
|
// API
|
||||||
fetchUsageData: () => ipcRenderer.invoke('fetch-usage-data'),
|
fetchUsageData: () => ipcRenderer.invoke('fetch-usage-data'),
|
||||||
openExternal: (url) => ipcRenderer.send('open-external', url)
|
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));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,7 @@ const elements = {
|
||||||
weeklyTimer: document.getElementById('weeklyTimer'),
|
weeklyTimer: document.getElementById('weeklyTimer'),
|
||||||
weeklyTimeText: document.getElementById('weeklyTimeText'),
|
weeklyTimeText: document.getElementById('weeklyTimeText'),
|
||||||
|
|
||||||
settingsBtn: document.getElementById('settingsBtn'),
|
settingsBtn: document.getElementById('settingsBtn')
|
||||||
settingsOverlay: document.getElementById('settingsOverlay'),
|
|
||||||
closeSettingsBtn: document.getElementById('closeSettingsBtn'),
|
|
||||||
logoutBtn: document.getElementById('logoutBtn'),
|
|
||||||
coffeeBtn: document.getElementById('coffeeBtn')
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
|
|
@ -39,6 +35,10 @@ async function init() {
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
credentials = await window.electronAPI.getCredentials();
|
credentials = await window.electronAPI.getCredentials();
|
||||||
|
|
||||||
|
// Load and apply color preferences
|
||||||
|
const colorPrefs = await window.electronAPI.getColorPreferences();
|
||||||
|
applyColorPreferences(colorPrefs);
|
||||||
|
|
||||||
if (credentials.sessionKey && credentials.organizationId) {
|
if (credentials.sessionKey && credentials.organizationId) {
|
||||||
showMainContent();
|
showMainContent();
|
||||||
await fetchUsageData();
|
await fetchUsageData();
|
||||||
|
|
@ -69,24 +69,9 @@ function setupEventListeners() {
|
||||||
window.electronAPI.closeWindow(); // Exit application completely
|
window.electronAPI.closeWindow(); // Exit application completely
|
||||||
});
|
});
|
||||||
|
|
||||||
// Settings calls
|
// Settings button
|
||||||
elements.settingsBtn.addEventListener('click', () => {
|
elements.settingsBtn.addEventListener('click', () => {
|
||||||
elements.settingsOverlay.style.display = 'flex';
|
window.electronAPI.openSettings();
|
||||||
});
|
|
||||||
|
|
||||||
elements.closeSettingsBtn.addEventListener('click', () => {
|
|
||||||
elements.settingsOverlay.style.display = 'none';
|
|
||||||
});
|
|
||||||
|
|
||||||
elements.logoutBtn.addEventListener('click', async () => {
|
|
||||||
await window.electronAPI.deleteCredentials();
|
|
||||||
elements.settingsOverlay.style.display = 'none';
|
|
||||||
showLoginRequired();
|
|
||||||
window.electronAPI.openLogin();
|
|
||||||
});
|
|
||||||
|
|
||||||
elements.coffeeBtn.addEventListener('click', () => {
|
|
||||||
window.electronAPI.openExternal('https://paypal.me/SlavomirDurej?country.x=GB&locale.x=en_GB');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for login success
|
// Listen for login success
|
||||||
|
|
@ -123,6 +108,12 @@ function setupEventListeners() {
|
||||||
console.log('Silent login failed, manual login required');
|
console.log('Silent login failed, manual login required');
|
||||||
showLoginRequired();
|
showLoginRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for color preference changes from settings window
|
||||||
|
window.electronAPI.onColorsChanged((preferences) => {
|
||||||
|
console.log('Colors changed, applying new preferences');
|
||||||
|
applyColorPreferences(preferences);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch usage data from Claude API
|
// Fetch usage data from Claude API
|
||||||
|
|
@ -381,6 +372,23 @@ function showError(message) {
|
||||||
console.error(message);
|
console.error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Color preference management
|
||||||
|
function applyColorPreferences(prefs) {
|
||||||
|
const root = document.documentElement;
|
||||||
|
|
||||||
|
// Apply normal colors
|
||||||
|
root.style.setProperty('--color-normal-start', prefs.normal.start);
|
||||||
|
root.style.setProperty('--color-normal-end', prefs.normal.end);
|
||||||
|
|
||||||
|
// Apply warning colors
|
||||||
|
root.style.setProperty('--color-warning-start', prefs.warning.start);
|
||||||
|
root.style.setProperty('--color-warning-end', prefs.warning.end);
|
||||||
|
|
||||||
|
// Apply danger colors
|
||||||
|
root.style.setProperty('--color-danger-start', prefs.danger.start);
|
||||||
|
root.style.setProperty('--color-danger-end', prefs.danger.end);
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-update management
|
// Auto-update management
|
||||||
function startAutoUpdate() {
|
function startAutoUpdate() {
|
||||||
stopAutoUpdate();
|
stopAutoUpdate();
|
||||||
|
|
|
||||||
|
|
@ -120,32 +120,6 @@
|
||||||
<span id="lastUpdate" style="display: none;"></span>
|
<span id="lastUpdate" style="display: none;"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Settings Overlay -->
|
|
||||||
<div class="settings-overlay" id="settingsOverlay" style="display: none;">
|
|
||||||
<div class="settings-content">
|
|
||||||
<button class="icon-close-settings-btn" id="closeSettingsBtn" title="Close">×</button>
|
|
||||||
<p class="disclaimer">
|
|
||||||
<strong>Disclaimer:</strong> Unofficial tool not affiliated with Anthropic. Use at your own
|
|
||||||
discretion.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<button class="coffee-btn" 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="settings-actions">
|
|
||||||
<button class="logout-btn" id="logoutBtn">Log Out</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
230
src/renderer/settings-styles.css
Normal file
230
src/renderer/settings-styles.css
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #1e1e2e 0%, #2a2a3e 100%);
|
||||||
|
color: #e0e0e0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Title Bar */
|
||||||
|
.title-bar {
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 8px 12px;
|
||||||
|
padding-left: 6px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-family: 'Libre Baskerville', serif;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-logo {
|
||||||
|
width: 19px;
|
||||||
|
height: 19px;
|
||||||
|
border-radius: 6px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-right: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: #a0a0a0;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover {
|
||||||
|
background: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings-app {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 24px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section h2 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2-Column Grid for Color Pickers */
|
||||||
|
.color-grid-2col {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-item label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: #a0a0a0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-item input[type="color"] {
|
||||||
|
width: 80px;
|
||||||
|
height: 50px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-item input[type="color"]:hover {
|
||||||
|
border-color: rgba(255, 255, 255, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-item input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-item input[type="color"]::-webkit-color-swatch {
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.btn-secondary,
|
||||||
|
.btn-danger,
|
||||||
|
.btn-coffee {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #e0e0e0;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background: transparent;
|
||||||
|
color: #ef4444;
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.15);
|
||||||
|
border-color: rgba(239, 68, 68, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-coffee {
|
||||||
|
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-coffee:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 16px rgba(255, 165, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-coffee svg {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Divider */
|
||||||
|
.divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
margin: 24px 0;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disclaimer */
|
||||||
|
.disclaimer {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #666;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disclaimer strong {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disclaimer spacing */
|
||||||
|
.disclaimer {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
105
src/renderer/settings.html
Normal file
105
src/renderer/settings.html
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Settings - Claude Usage Widget</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="settings-styles.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="settings-app">
|
||||||
|
<!-- Title Bar -->
|
||||||
|
<div class="title-bar" id="titleBar">
|
||||||
|
<div class="title">
|
||||||
|
<img src="../../assets/logo.png" alt="Logo" class="app-logo">
|
||||||
|
<span>Settings</span>
|
||||||
|
</div>
|
||||||
|
<div class="controls">
|
||||||
|
<button class="control-btn minimize-btn" id="minimizeBtn" title="Minimize">−</button>
|
||||||
|
<button class="control-btn close-btn" id="closeBtn" title="Close">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-container">
|
||||||
|
|
||||||
|
<!-- Color Customization -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2>Customize Colors</h2>
|
||||||
|
|
||||||
|
<div class="color-grid-2col">
|
||||||
|
<div class="color-item">
|
||||||
|
<label>Normal Start</label>
|
||||||
|
<input type="color" id="colorNormalStart" value="#8b5cf6">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="color-item">
|
||||||
|
<label>Normal End</label>
|
||||||
|
<input type="color" id="colorNormalEnd" value="#a78bfa">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="color-item">
|
||||||
|
<label>Warning Start</label>
|
||||||
|
<input type="color" id="colorWarningStart" value="#f59e0b">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="color-item">
|
||||||
|
<label>Warning End</label>
|
||||||
|
<input type="color" id="colorWarningEnd" value="#fbbf24">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="color-item">
|
||||||
|
<label>Danger Start</label>
|
||||||
|
<input type="color" id="colorDangerStart" value="#ef4444">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="color-item">
|
||||||
|
<label>Danger End</label>
|
||||||
|
<input type="color" id="colorDangerEnd" value="#f87171">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn-secondary" id="resetColorsBtn">Reset to Defaults</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<!-- Account Actions -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2>Account</h2>
|
||||||
|
<button class="btn-danger" id="logoutBtn">Log Out</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<!-- Support -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- Disclaimer -->
|
||||||
|
<p class="disclaimer">
|
||||||
|
<strong>Disclaimer:</strong> Unofficial tool not affiliated with Anthropic. Use at your own
|
||||||
|
discretion.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="settings.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
107
src/renderer/settings.js
Normal file
107
src/renderer/settings.js
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
// DOM elements
|
||||||
|
const elements = {
|
||||||
|
colorNormalStart: document.getElementById('colorNormalStart'),
|
||||||
|
colorNormalEnd: document.getElementById('colorNormalEnd'),
|
||||||
|
colorWarningStart: document.getElementById('colorWarningStart'),
|
||||||
|
colorWarningEnd: document.getElementById('colorWarningEnd'),
|
||||||
|
colorDangerStart: document.getElementById('colorDangerStart'),
|
||||||
|
colorDangerEnd: document.getElementById('colorDangerEnd'),
|
||||||
|
resetColorsBtn: document.getElementById('resetColorsBtn'),
|
||||||
|
logoutBtn: document.getElementById('logoutBtn'),
|
||||||
|
coffeeBtn: document.getElementById('coffeeBtn'),
|
||||||
|
minimizeBtn: document.getElementById('minimizeBtn'),
|
||||||
|
closeBtn: document.getElementById('closeBtn')
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
async function init() {
|
||||||
|
setupEventListeners();
|
||||||
|
|
||||||
|
// Load and apply color preferences
|
||||||
|
const colorPrefs = await window.electronAPI.getColorPreferences();
|
||||||
|
loadColorPickerValues(colorPrefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event Listeners
|
||||||
|
function setupEventListeners() {
|
||||||
|
// Color picker event listeners
|
||||||
|
const colorInputs = [
|
||||||
|
elements.colorNormalStart,
|
||||||
|
elements.colorNormalEnd,
|
||||||
|
elements.colorWarningStart,
|
||||||
|
elements.colorWarningEnd,
|
||||||
|
elements.colorDangerStart,
|
||||||
|
elements.colorDangerEnd
|
||||||
|
];
|
||||||
|
|
||||||
|
colorInputs.forEach(input => {
|
||||||
|
input.addEventListener('change', async () => {
|
||||||
|
await saveCurrentColors();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
elements.resetColorsBtn.addEventListener('click', async () => {
|
||||||
|
const defaults = {
|
||||||
|
normal: { start: '#8b5cf6', end: '#a78bfa' },
|
||||||
|
warning: { start: '#f59e0b', end: '#fbbf24' },
|
||||||
|
danger: { start: '#ef4444', end: '#f87171' }
|
||||||
|
};
|
||||||
|
await window.electronAPI.setColorPreferences(defaults);
|
||||||
|
loadColorPickerValues(defaults);
|
||||||
|
// Notify main window to update colors immediately
|
||||||
|
await window.electronAPI.notifyColorChange(defaults);
|
||||||
|
});
|
||||||
|
|
||||||
|
elements.logoutBtn.addEventListener('click', async () => {
|
||||||
|
await window.electronAPI.deleteCredentials();
|
||||||
|
window.close();
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
elements.closeBtn.addEventListener('click', () => {
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
function loadColorPickerValues(prefs) {
|
||||||
|
elements.colorNormalStart.value = prefs.normal.start;
|
||||||
|
elements.colorNormalEnd.value = prefs.normal.end;
|
||||||
|
elements.colorWarningStart.value = prefs.warning.start;
|
||||||
|
elements.colorWarningEnd.value = prefs.warning.end;
|
||||||
|
elements.colorDangerStart.value = prefs.danger.start;
|
||||||
|
elements.colorDangerEnd.value = prefs.danger.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCurrentColors() {
|
||||||
|
const prefs = {
|
||||||
|
normal: {
|
||||||
|
start: elements.colorNormalStart.value,
|
||||||
|
end: elements.colorNormalEnd.value
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
start: elements.colorWarningStart.value,
|
||||||
|
end: elements.colorWarningEnd.value
|
||||||
|
},
|
||||||
|
danger: {
|
||||||
|
start: elements.colorDangerStart.value,
|
||||||
|
end: elements.colorDangerEnd.value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await window.electronAPI.setColorPreferences(prefs);
|
||||||
|
// Notify main window to update colors
|
||||||
|
await window.electronAPI.notifyColorChange(prefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the application
|
||||||
|
init();
|
||||||
|
|
@ -1,3 +1,17 @@
|
||||||
|
:root {
|
||||||
|
/* Customizable color scheme - normal state */
|
||||||
|
--color-normal-start: #8b5cf6;
|
||||||
|
--color-normal-end: #a78bfa;
|
||||||
|
|
||||||
|
/* Warning state (>=75% usage) */
|
||||||
|
--color-warning-start: #f59e0b;
|
||||||
|
--color-warning-end: #fbbf24;
|
||||||
|
|
||||||
|
/* Danger state (>=90% usage) */
|
||||||
|
--color-danger-start: #ef4444;
|
||||||
|
--color-danger-end: #f87171;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -339,7 +353,7 @@ body {
|
||||||
|
|
||||||
.progress-fill {
|
.progress-fill {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(90deg, #8b5cf6 0%, #a78bfa 100%);
|
background: linear-gradient(90deg, var(--color-normal-start) 0%, var(--color-normal-end) 100%);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
transition: width 0.6s ease;
|
transition: width 0.6s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -349,8 +363,8 @@ body {
|
||||||
/* Shimmer removed */
|
/* Shimmer removed */
|
||||||
|
|
||||||
.progress-fill.weekly {
|
.progress-fill.weekly {
|
||||||
background: linear-gradient(90deg, #3b82f6 0%, #60a5fa 100%);
|
background: linear-gradient(90deg, var(--color-normal-start) 0%, var(--color-normal-end) 100%);
|
||||||
box-shadow: 0 0 10px rgba(59, 130, 246, 0.3);
|
box-shadow: 0 0 10px rgba(139, 92, 246, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -378,13 +392,13 @@ body {
|
||||||
|
|
||||||
.timer-progress {
|
.timer-progress {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: #8b5cf6;
|
stroke: var(--color-normal-start);
|
||||||
stroke-width: 4;
|
stroke-width: 4;
|
||||||
transition: stroke-dashoffset 0.6s ease;
|
transition: stroke-dashoffset 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timer-progress.weekly {
|
.timer-progress.weekly {
|
||||||
stroke: #3b82f6;
|
stroke: var(--color-normal-start);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timer-text {
|
.timer-text {
|
||||||
|
|
@ -545,31 +559,31 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.15);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Warning states */
|
/* Warning states */
|
||||||
.progress-fill.warning {
|
.progress-fill.warning {
|
||||||
background: linear-gradient(90deg, #f59e0b 0%, #fbbf24 100%);
|
background: linear-gradient(90deg, var(--color-warning-start) 0%, var(--color-warning-end) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-fill.danger {
|
.progress-fill.danger {
|
||||||
background: linear-gradient(90deg, #ef4444 0%, #f87171 100%);
|
background: linear-gradient(90deg, var(--color-danger-start) 0%, var(--color-danger-end) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timer-progress.warning {
|
.timer-progress.warning {
|
||||||
stroke: #f59e0b;
|
stroke: var(--color-warning-start);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timer-progress.danger {
|
.timer-progress.danger {
|
||||||
stroke: #ef4444;
|
stroke: var(--color-danger-start);
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue