diff --git a/.gitignore b/.gitignore index 61d41dc..a4c6aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ yarn-error.log* # Cache .cache/ .temp/ +CLAUDE_NOTES.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..0b8f0f0 --- /dev/null +++ b/CHANGES.md @@ -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) diff --git a/main.js b/main.js index e0b06f1..ea1539c 100644 --- a/main.js +++ b/main.js @@ -10,11 +10,14 @@ const store = new Store({ let mainWindow = null; let loginWindow = null; let silentLoginWindow = null; +let settingsWindow = null; let tray = null; // Window configuration const WIDGET_WIDTH = 480; const WIDGET_HEIGHT = 140; +const SETTINGS_WIDTH = 500; +const SETTINGS_HEIGHT = 680; function createMainWindow() { // Load saved position or use defaults @@ -347,7 +350,7 @@ function createTray() { { label: 'Settings', 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 ipcMain.handle('get-credentials', () => { return { @@ -416,6 +453,10 @@ ipcMain.on('open-login', () => { createLoginWindow(); }); +ipcMain.on('open-settings', () => { + createSettingsWindow(); +}); + ipcMain.on('minimize-window', () => { if (mainWindow) mainWindow.hide(); }); @@ -443,6 +484,31 @@ ipcMain.on('open-external', (event, 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 () => { console.log('[Main] fetch-usage-data handler called'); const sessionKey = store.get('sessionKey'); diff --git a/preload.js b/preload.js index 63ac749..be50ebf 100644 --- a/preload.js +++ b/preload.js @@ -12,6 +12,7 @@ contextBridge.exposeInMainWorld('electronAPI', { 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'), @@ -36,5 +37,13 @@ contextBridge.exposeInMainWorld('electronAPI', { // API 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)); + } }); diff --git a/src/renderer/app.js b/src/renderer/app.js index 7c3c51e..9c81fb2 100644 --- a/src/renderer/app.js +++ b/src/renderer/app.js @@ -27,11 +27,7 @@ const elements = { weeklyTimer: document.getElementById('weeklyTimer'), weeklyTimeText: document.getElementById('weeklyTimeText'), - settingsBtn: document.getElementById('settingsBtn'), - settingsOverlay: document.getElementById('settingsOverlay'), - closeSettingsBtn: document.getElementById('closeSettingsBtn'), - logoutBtn: document.getElementById('logoutBtn'), - coffeeBtn: document.getElementById('coffeeBtn') + settingsBtn: document.getElementById('settingsBtn') }; // Initialize @@ -39,6 +35,10 @@ async function init() { setupEventListeners(); credentials = await window.electronAPI.getCredentials(); + // Load and apply color preferences + const colorPrefs = await window.electronAPI.getColorPreferences(); + applyColorPreferences(colorPrefs); + if (credentials.sessionKey && credentials.organizationId) { showMainContent(); await fetchUsageData(); @@ -69,24 +69,9 @@ function setupEventListeners() { window.electronAPI.closeWindow(); // Exit application completely }); - // Settings calls + // Settings button elements.settingsBtn.addEventListener('click', () => { - elements.settingsOverlay.style.display = 'flex'; - }); - - 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'); + window.electronAPI.openSettings(); }); // Listen for login success @@ -123,6 +108,12 @@ function setupEventListeners() { console.log('Silent login failed, manual login required'); 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 @@ -381,6 +372,23 @@ function showError(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 function startAutoUpdate() { stopAutoUpdate(); diff --git a/src/renderer/index.html b/src/renderer/index.html index 9c3dc38..8fcaa77 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -120,32 +120,6 @@ - -
diff --git a/src/renderer/settings-styles.css b/src/renderer/settings-styles.css new file mode 100644 index 0000000..defc2f5 --- /dev/null +++ b/src/renderer/settings-styles.css @@ -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; +} diff --git a/src/renderer/settings.html b/src/renderer/settings.html new file mode 100644 index 0000000..3e72b72 --- /dev/null +++ b/src/renderer/settings.html @@ -0,0 +1,105 @@ + + + + + + ++ Disclaimer: Unofficial tool not affiliated with Anthropic. Use at your own + discretion. +
+