From c5a0535c46744165126781698d4d9f7dd7a0f245 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Fri, 28 Nov 2025 18:14:48 -0600 Subject: [PATCH] feat: Add accentForeground theme color for proper text contrast on accent backgrounds - Add accentForeground color to all 16 themes for proper text contrast when rendering text on accent-colored backgrounds - Update UI components to use accentForeground instead of hardcoded white/dark colors for buttons and highlighted elements - Refresh Maestro's Choice and Dre Synth theme palettes - Add Save button to Batch Runner modal for persisting custom prompts - Fix sidebar resize not saving width correctly during drag - Queue custom AI commands when agent is busy (like regular messages) - Update search placeholder in Scratchpad to show correct shortcuts - Update docs to reflect 13 theme colors (was 12) Claude ID: ac8e7811-8742-4991-b9ce-9c03629b8288 Maestro ID: 5a166b38-b7e9-47f0-a8ff-0113c65f2682 --- ARCHITECTURE.md | 27 +++++----- CLAUDE.md | 2 +- src/main/themes.ts | 54 ++++++++++++------- src/renderer/App.tsx | 36 +++++++++++-- src/renderer/components/AICommandsPanel.tsx | 2 +- src/renderer/components/AboutModal.tsx | 1 - .../components/AgentSessionsModal.tsx | 2 +- src/renderer/components/BatchRunnerModal.tsx | 24 ++++++++- src/renderer/components/CreateGroupModal.tsx | 4 +- src/renderer/components/FilePreview.tsx | 2 +- .../components/HistoryDetailModal.tsx | 2 +- src/renderer/components/InputArea.tsx | 4 +- src/renderer/components/MainPanel.tsx | 2 +- src/renderer/components/NewInstanceModal.tsx | 4 +- src/renderer/components/QuickActionsModal.tsx | 2 +- src/renderer/components/RenameGroupModal.tsx | 4 +- .../components/RenameSessionModal.tsx | 4 +- src/renderer/components/RightPanel.tsx | 7 +-- src/renderer/components/Scratchpad.tsx | 12 ++--- src/renderer/components/SessionList.tsx | 11 ++-- src/renderer/components/SettingsModal.tsx | 4 +- src/renderer/components/TerminalOutput.tsx | 2 +- src/renderer/constants/themes.ts | 16 ++++++ src/renderer/index.html | 8 +-- src/renderer/types/index.ts | 2 + src/shared/theme-types.ts | 2 + 26 files changed, 159 insertions(+), 81 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9f5ebb5c..7a8a1bcd 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -417,20 +417,21 @@ Themes defined in `src/renderer/constants/themes.ts`. interface Theme { id: ThemeId; name: string; - mode: 'light' | 'dark'; + mode: 'light' | 'dark' | 'vibe'; colors: { - bgMain: string; // Main content background - bgSidebar: string; // Sidebar background - bgActivity: string; // Accent background - border: string; // Border colors - textMain: string; // Primary text - textDim: string; // Secondary text - accent: string; // Accent color - accentDim: string; // Dimmed accent - accentText: string; // Accent text color - success: string; // Success state (green) - warning: string; // Warning state (yellow) - error: string; // Error state (red) + bgMain: string; // Main content background + bgSidebar: string; // Sidebar background + bgActivity: string; // Accent background + border: string; // Border colors + textMain: string; // Primary text + textDim: string; // Secondary text + accent: string; // Accent color + accentDim: string; // Dimmed accent + accentText: string; // Accent text color + accentForeground: string; // Text ON accent backgrounds (contrast) + success: string; // Success state (green) + warning: string; // Warning state (yellow) + error: string; // Error state (red) }; } ``` diff --git a/CLAUDE.md b/CLAUDE.md index 3b304242..9b4c9297 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -136,7 +136,7 @@ useEffect(() => { ### 5. Theme Colors -Themes have 12 required colors. Use inline styles for theme colors: +Themes have 13 required colors. Use inline styles for theme colors: ```typescript style={{ color: theme.colors.textMain }} // Correct className="text-gray-500" // Wrong for themed text diff --git a/src/main/themes.ts b/src/main/themes.ts index cdf78b32..5faedffc 100644 --- a/src/main/themes.ts +++ b/src/main/themes.ts @@ -23,6 +23,7 @@ export const THEMES: Record = { accent: '#6366f1', accentDim: 'rgba(99, 102, 241, 0.2)', accentText: '#a5b4fc', + accentForeground: '#ffffff', success: '#22c55e', warning: '#eab308', error: '#ef4444' @@ -42,6 +43,7 @@ export const THEMES: Record = { accent: '#fd971f', accentDim: 'rgba(253, 151, 31, 0.2)', accentText: '#fdbf6f', + accentForeground: '#1e1f1c', success: '#a6e22e', warning: '#e6db74', error: '#f92672' @@ -61,6 +63,7 @@ export const THEMES: Record = { accent: '#88c0d0', accentDim: 'rgba(136, 192, 208, 0.2)', accentText: '#8fbcbb', + accentForeground: '#2e3440', success: '#a3be8c', warning: '#ebcb8b', error: '#bf616a' @@ -80,6 +83,7 @@ export const THEMES: Record = { accent: '#7aa2f7', accentDim: 'rgba(122, 162, 247, 0.2)', accentText: '#7dcfff', + accentForeground: '#1a1b26', success: '#9ece6a', warning: '#e0af68', error: '#f7768e' @@ -99,6 +103,7 @@ export const THEMES: Record = { accent: '#89b4fa', accentDim: 'rgba(137, 180, 250, 0.2)', accentText: '#89dceb', + accentForeground: '#1e1e2e', success: '#a6e3a1', warning: '#f9e2af', error: '#f38ba8' @@ -118,6 +123,7 @@ export const THEMES: Record = { accent: '#83a598', accentDim: 'rgba(131, 165, 152, 0.2)', accentText: '#8ec07c', + accentForeground: '#1d2021', success: '#b8bb26', warning: '#fabd2f', error: '#fb4934' @@ -138,6 +144,7 @@ export const THEMES: Record = { accent: '#0969da', accentDim: 'rgba(9, 105, 218, 0.1)', accentText: '#0969da', + accentForeground: '#ffffff', success: '#1a7f37', warning: '#9a6700', error: '#cf222e' @@ -157,6 +164,7 @@ export const THEMES: Record = { accent: '#2aa198', accentDim: 'rgba(42, 161, 152, 0.1)', accentText: '#2aa198', + accentForeground: '#fdf6e3', success: '#859900', warning: '#b58900', error: '#dc322f' @@ -176,6 +184,7 @@ export const THEMES: Record = { accent: '#4078f2', accentDim: 'rgba(64, 120, 242, 0.1)', accentText: '#4078f2', + accentForeground: '#ffffff', success: '#50a14f', warning: '#c18401', error: '#e45649' @@ -195,6 +204,7 @@ export const THEMES: Record = { accent: '#458588', accentDim: 'rgba(69, 133, 136, 0.1)', accentText: '#076678', + accentForeground: '#fbf1c7', success: '#98971a', warning: '#d79921', error: '#cc241d' @@ -214,6 +224,7 @@ export const THEMES: Record = { accent: '#1e66f5', accentDim: 'rgba(30, 102, 245, 0.1)', accentText: '#1e66f5', + accentForeground: '#ffffff', success: '#40a02b', warning: '#df8e1d', error: '#d20f39' @@ -233,6 +244,7 @@ export const THEMES: Record = { accent: '#55b4d4', accentDim: 'rgba(85, 180, 212, 0.1)', accentText: '#399ee6', + accentForeground: '#1a1a1a', success: '#86b300', warning: '#f2ae49', error: '#f07171' @@ -253,6 +265,7 @@ export const THEMES: Record = { accent: '#ff69b4', accentDim: 'rgba(255, 105, 180, 0.25)', accentText: '#ff8dc7', + accentForeground: '#1a0f24', success: '#7cb342', warning: '#d4af37', error: '#da70d6' @@ -263,18 +276,19 @@ export const THEMES: Record = { name: "Maestro's Choice", mode: 'vibe', colors: { - bgMain: '#0a0a0f', - bgSidebar: '#05050a', - bgActivity: '#12121a', - border: '#2a2a3a', - textMain: '#f0e6d3', - textDim: '#8a8078', - accent: '#c9a227', - accentDim: 'rgba(201, 162, 39, 0.2)', - accentText: '#e6b830', - success: '#4a9c6d', - warning: '#c9a227', - error: '#8b2942' + bgMain: '#1a1a24', + bgSidebar: '#141420', + bgActivity: '#24243a', + border: '#3a3a5a', + textMain: '#fff8e8', + textDim: '#a8a0a0', + accent: '#f4c430', + accentDim: 'rgba(244, 196, 48, 0.25)', + accentText: '#ffd54f', + accentForeground: '#1a1a24', + success: '#66d9a0', + warning: '#f4c430', + error: '#e05070' } }, 'dre-synth': { @@ -285,14 +299,15 @@ export const THEMES: Record = { bgMain: '#0d0221', bgSidebar: '#0a0118', bgActivity: '#150530', - border: '#2a1050', + border: '#00d4aa', textMain: '#f0e6ff', - textDim: '#9080b0', - accent: '#ff2a6d', - accentDim: 'rgba(255, 42, 109, 0.25)', - accentText: '#ff6b9d', - success: '#05ffa1', - warning: '#00f5d4', + textDim: '#60e0d0', + accent: '#00ffcc', + accentDim: 'rgba(0, 255, 204, 0.25)', + accentText: '#40ffdd', + accentForeground: '#0d0221', + success: '#00ffcc', + warning: '#ff2a6d', error: '#ff2a6d' } }, @@ -310,6 +325,7 @@ export const THEMES: Record = { accent: '#cc0033', accentDim: 'rgba(204, 0, 51, 0.25)', accentText: '#ff3355', + accentForeground: '#ffffff', success: '#f5f5f5', warning: '#cc0033', error: '#cc0033' diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 26e70e29..5d86eb90 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1349,8 +1349,6 @@ export default function MaestroConsole() { activeBatchSessionIds, startBatchRun, stopBatchRun, - customPrompts, - setCustomPrompt } = useBatchProcessor({ sessions, onUpdateSession: (sessionId, updates) => { @@ -2432,6 +2430,30 @@ export default function MaestroConsole() { { session: activeSession, gitBranch } ); + // Queue the command if AI is busy (same as regular messages) + if (activeSession.state === 'busy') { + const queuedEntry: LogEntry = { + id: generateId(), + timestamp: Date.now(), + source: 'user', + text: substitutedPrompt, + aiCommand: { + command: matchingCustomCommand.command, + description: matchingCustomCommand.description + } + }; + + setSessions(prev => prev.map(s => { + if (s.id !== activeSessionId) return s; + return { + ...s, + messageQueue: [...s.messageQueue, queuedEntry], + aiCommandHistory: Array.from(new Set([...(s.aiCommandHistory || []), commandText])).slice(-50), + }; + })); + return; + } + // Add user log showing the command with its interpolated prompt // Also track this command for automatic synopsis on completion setSessions(prev => prev.map(s => { @@ -4193,12 +4215,16 @@ export default function MaestroConsole() { theme={theme} onClose={() => setBatchRunnerModalOpen(false)} onGo={(prompt) => { - // Save the custom prompt for this session - setCustomPrompt(activeSession.id, prompt); // Start the batch run handleStartBatchRun(prompt); }} - initialPrompt={customPrompts[activeSession.id] || ''} + onSave={(prompt) => { + // Save the custom prompt to the session (persisted across restarts) + setSessions(prev => prev.map(s => + s.id === activeSession.id ? { ...s, batchRunnerPrompt: prompt } : s + )); + }} + initialPrompt={activeSession.batchRunnerPrompt || ''} showConfirmation={showConfirmation} /> )} diff --git a/src/renderer/components/AICommandsPanel.tsx b/src/renderer/components/AICommandsPanel.tsx index 73d57f59..505765f2 100644 --- a/src/renderer/components/AICommandsPanel.tsx +++ b/src/renderer/components/AICommandsPanel.tsx @@ -152,7 +152,7 @@ export function AICommandsPanel({ theme, customAICommands, setCustomAICommands } className="flex items-center gap-2 px-4 py-2 rounded text-sm font-medium transition-all" style={{ backgroundColor: theme.colors.accent, - color: 'white' + color: theme.colors.accentForeground }} > diff --git a/src/renderer/components/AboutModal.tsx b/src/renderer/components/AboutModal.tsx index becdfdf0..c04e09dd 100644 --- a/src/renderer/components/AboutModal.tsx +++ b/src/renderer/components/AboutModal.tsx @@ -179,7 +179,6 @@ export function AboutModal({ theme, sessions, onClose }: AboutModalProps) {
Global Statistics - (all Claude projects)
{loading ? (
diff --git a/src/renderer/components/AgentSessionsModal.tsx b/src/renderer/components/AgentSessionsModal.tsx index 762dcdce..73f8c6a1 100644 --- a/src/renderer/components/AgentSessionsModal.tsx +++ b/src/renderer/components/AgentSessionsModal.tsx @@ -337,7 +337,7 @@ export function AgentSessionsModal({ className="flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors" style={{ backgroundColor: theme.colors.accent, - color: theme.colors.accentText, + color: theme.colors.accentForeground, }} > diff --git a/src/renderer/components/BatchRunnerModal.tsx b/src/renderer/components/BatchRunnerModal.tsx index 409735d9..0e8a6104 100644 --- a/src/renderer/components/BatchRunnerModal.tsx +++ b/src/renderer/components/BatchRunnerModal.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef } from 'react'; -import { X, RotateCcw, Play, Variable, ChevronDown, ChevronRight } from 'lucide-react'; +import { X, RotateCcw, Play, Variable, ChevronDown, ChevronRight, Save } from 'lucide-react'; import type { Theme } from '../types'; import { useLayerStack } from '../contexts/LayerStackContext'; import { MODAL_PRIORITIES } from '../constants/modalPriorities'; @@ -48,15 +48,17 @@ interface BatchRunnerModalProps { theme: Theme; onClose: () => void; onGo: (prompt: string) => void; + onSave: (prompt: string) => void; initialPrompt?: string; showConfirmation: (message: string, onConfirm: () => void) => void; } export function BatchRunnerModal(props: BatchRunnerModalProps) { - const { theme, onClose, onGo, initialPrompt, showConfirmation } = props; + const { theme, onClose, onGo, onSave, initialPrompt, showConfirmation } = props; const [prompt, setPrompt] = useState(initialPrompt || DEFAULT_BATCH_PROMPT); const [variablesExpanded, setVariablesExpanded] = useState(false); + const [savedPrompt, setSavedPrompt] = useState(initialPrompt || ''); const textareaRef = useRef(null); const { registerLayer, unregisterLayer, updateLayerHandler } = useLayerStack(); @@ -103,12 +105,20 @@ export function BatchRunnerModal(props: BatchRunnerModalProps) { ); }; + const handleSave = () => { + onSave(prompt); + setSavedPrompt(prompt); + }; + const handleGo = () => { + // Also save when running + onSave(prompt); onGo(prompt); onClose(); }; const isModified = prompt !== DEFAULT_BATCH_PROMPT; + const hasUnsavedChanges = prompt !== savedPrompt && prompt !== DEFAULT_BATCH_PROMPT; return (
Cancel + diff --git a/src/renderer/components/FilePreview.tsx b/src/renderer/components/FilePreview.tsx index 5ba98cad..8eb7f881 100644 --- a/src/renderer/components/FilePreview.tsx +++ b/src/renderer/components/FilePreview.tsx @@ -740,7 +740,7 @@ export function FilePreview({ file, onClose, theme, markdownRawMode, setMarkdown className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 px-6 py-4 rounded-lg shadow-2xl text-base font-bold animate-in fade-in zoom-in-95 duration-200 z-50" style={{ backgroundColor: theme.colors.accent, - color: '#FFFFFF', + color: theme.colors.accentForeground, textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)' }} > diff --git a/src/renderer/components/HistoryDetailModal.tsx b/src/renderer/components/HistoryDetailModal.tsx index 85560e51..554aa192 100644 --- a/src/renderer/components/HistoryDetailModal.tsx +++ b/src/renderer/components/HistoryDetailModal.tsx @@ -355,7 +355,7 @@ export function HistoryDetailModal({ className="px-4 py-2 rounded text-sm font-medium transition-colors hover:opacity-90" style={{ backgroundColor: theme.colors.accent, - color: 'white' + color: theme.colors.accentForeground }} > Close diff --git a/src/renderer/components/InputArea.tsx b/src/renderer/components/InputArea.tsx index 98e3e094..46254b16 100644 --- a/src/renderer/components/InputArea.tsx +++ b/src/renderer/components/InputArea.tsx @@ -435,8 +435,8 @@ export function InputArea(props: InputAreaProps) { ) : ( diff --git a/src/renderer/components/MainPanel.tsx b/src/renderer/components/MainPanel.tsx index 36d2f1ab..cb23c668 100644 --- a/src/renderer/components/MainPanel.tsx +++ b/src/renderer/components/MainPanel.tsx @@ -1166,7 +1166,7 @@ export function MainPanel(props: MainPanelProps) { className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 px-6 py-4 rounded-lg shadow-2xl text-base font-bold animate-in fade-in zoom-in-95 duration-200 z-50" style={{ backgroundColor: theme.colors.accent, - color: '#FFFFFF', + color: theme.colors.accentForeground, textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)' }} > diff --git a/src/renderer/components/NewInstanceModal.tsx b/src/renderer/components/NewInstanceModal.tsx index b6fa4557..8ab92f11 100644 --- a/src/renderer/components/NewInstanceModal.tsx +++ b/src/renderer/components/NewInstanceModal.tsx @@ -343,8 +343,8 @@ export function NewInstanceModal({ isOpen, onClose, onCreate, theme, defaultAgen diff --git a/src/renderer/components/QuickActionsModal.tsx b/src/renderer/components/QuickActionsModal.tsx index 93e5259d..88f8f23e 100644 --- a/src/renderer/components/QuickActionsModal.tsx +++ b/src/renderer/components/QuickActionsModal.tsx @@ -360,7 +360,7 @@ export function QuickActionsModal(props: QuickActionsModalProps) { className={`w-full text-left px-4 py-3 flex items-center gap-3 hover:bg-opacity-10 ${i === selectedIndex ? 'bg-opacity-10' : ''}`} style={{ backgroundColor: i === selectedIndex ? theme.colors.accent : 'transparent', - color: theme.colors.textMain + color: i === selectedIndex ? theme.colors.accentForeground : theme.colors.textMain }} > {showNumber ? ( diff --git a/src/renderer/components/RenameGroupModal.tsx b/src/renderer/components/RenameGroupModal.tsx index 96d00d2e..09697147 100644 --- a/src/renderer/components/RenameGroupModal.tsx +++ b/src/renderer/components/RenameGroupModal.tsx @@ -182,8 +182,8 @@ export function RenameGroupModal(props: RenameGroupModalProps) { diff --git a/src/renderer/components/RenameSessionModal.tsx b/src/renderer/components/RenameSessionModal.tsx index 1e8c7d30..6b1a4fe4 100644 --- a/src/renderer/components/RenameSessionModal.tsx +++ b/src/renderer/components/RenameSessionModal.tsx @@ -105,8 +105,8 @@ export function RenameSessionModal(props: RenameSessionModalProps) { diff --git a/src/renderer/components/RightPanel.tsx b/src/renderer/components/RightPanel.tsx index f6b96f0e..6376d344 100644 --- a/src/renderer/components/RightPanel.tsx +++ b/src/renderer/components/RightPanel.tsx @@ -120,15 +120,16 @@ export const RightPanel = forwardRef(function e.preventDefault(); const startX = e.clientX; const startWidth = rightPanelWidth; + let currentWidth = startWidth; const handleMouseMove = (e: MouseEvent) => { const delta = startX - e.clientX; // Reversed for right panel - const newWidth = Math.max(384, Math.min(800, startWidth + delta)); - setRightPanelWidthState(newWidth); + currentWidth = Math.max(384, Math.min(800, startWidth + delta)); + setRightPanelWidthState(currentWidth); }; const handleMouseUp = () => { - window.maestro.settings.set('rightPanelWidth', rightPanelWidth); + window.maestro.settings.set('rightPanelWidth', currentWidth); document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; diff --git a/src/renderer/components/Scratchpad.tsx b/src/renderer/components/Scratchpad.tsx index e21ff9e9..abe99520 100644 --- a/src/renderer/components/Scratchpad.tsx +++ b/src/renderer/components/Scratchpad.tsx @@ -808,7 +808,7 @@ export function Scratchpad({ className={`flex items-center gap-2 px-3 py-1.5 rounded text-xs transition-colors ${isAgentBusy ? 'opacity-50 cursor-not-allowed' : 'hover:opacity-90'}`} style={{ backgroundColor: theme.colors.accent, - color: 'white', + color: theme.colors.accentForeground, border: `1px solid ${theme.colors.accent}` }} title={isAgentBusy ? "Cannot run while agent is thinking" : "Run batch processing on scratchpad tasks"} @@ -888,7 +888,7 @@ export function Scratchpad({ goToPrevMatch(); } }} - placeholder={mode === 'edit' ? "Search... (Enter: next, Shift+Enter: prev)" : "Search... (press '/' to open, Enter: next)"} + placeholder={mode === 'edit' ? "Search... (⌘F to open, Enter: next, Shift+Enter: prev)" : "Search... (/ to open, Enter: next, Shift+Enter: prev)"} className="flex-1 bg-transparent outline-none text-sm" style={{ color: theme.colors.textMain }} autoFocus @@ -961,18 +961,12 @@ export function Scratchpad({ e.stopPropagation(); toggleMode(); } - // '/' to open search in preview mode + // '/' to open search in preview mode (mutually exclusive with Cmd+F in edit mode) if (e.key === '/' && !e.metaKey && !e.ctrlKey && !e.altKey) { e.preventDefault(); e.stopPropagation(); openSearch(); } - // CMD+F to open search - if ((e.metaKey || e.ctrlKey) && e.key === 'f') { - e.preventDefault(); - e.stopPropagation(); - openSearch(); - } }} onScroll={handlePreviewScroll} style={{ diff --git a/src/renderer/components/SessionList.tsx b/src/renderer/components/SessionList.tsx index 26da3986..52094982 100644 --- a/src/renderer/components/SessionList.tsx +++ b/src/renderer/components/SessionList.tsx @@ -265,15 +265,16 @@ export function SessionList(props: SessionListProps) { e.preventDefault(); const startX = e.clientX; const startWidth = leftSidebarWidthState; + let currentWidth = startWidth; const handleMouseMove = (e: MouseEvent) => { const delta = e.clientX - startX; - const newWidth = Math.max(256, Math.min(600, startWidth + delta)); - setLeftSidebarWidthState(newWidth); + currentWidth = Math.max(256, Math.min(600, startWidth + delta)); + setLeftSidebarWidthState(currentWidth); }; const handleMouseUp = () => { - window.maestro.settings.set('leftSidebarWidth', leftSidebarWidthState); + window.maestro.settings.set('leftSidebarWidth', currentWidth); document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; @@ -354,7 +355,7 @@ export function SessionList(props: SessionListProps) { className="w-full py-2 rounded text-xs font-medium transition-colors hover:opacity-90 flex items-center justify-center gap-2" style={{ backgroundColor: theme.colors.accent, - color: 'white' + color: theme.colors.accentForeground }} > @@ -1272,7 +1273,7 @@ export function SessionList(props: SessionListProps) { {leftSidebarOpen && ( - )} diff --git a/src/renderer/components/SettingsModal.tsx b/src/renderer/components/SettingsModal.tsx index f0ed3cd7..410f2eda 100644 --- a/src/renderer/components/SettingsModal.tsx +++ b/src/renderer/components/SettingsModal.tsx @@ -716,7 +716,7 @@ export function SettingsModal(props: SettingsModalProps) { @@ -1229,7 +1229,7 @@ export function SettingsModal(props: SettingsModalProps) { className="w-full py-3 rounded-lg font-bold text-sm transition-all disabled:opacity-50 disabled:cursor-not-allowed" style={{ backgroundColor: theme.colors.accent, - color: 'white', + color: theme.colors.accentForeground, }} > {testingLLM ? 'Testing Connection...' : 'Test Connection'} diff --git a/src/renderer/components/TerminalOutput.tsx b/src/renderer/components/TerminalOutput.tsx index 825ec2c7..d30bffa5 100644 --- a/src/renderer/components/TerminalOutput.tsx +++ b/src/renderer/components/TerminalOutput.tsx @@ -1264,7 +1264,7 @@ export const TerminalOutput = forwardRef((p className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 px-6 py-4 rounded-lg shadow-2xl text-base font-bold animate-in fade-in zoom-in-95 duration-200 z-50" style={{ backgroundColor: theme.colors.accent, - color: '#FFFFFF', + color: theme.colors.accentForeground, textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)' }} > diff --git a/src/renderer/constants/themes.ts b/src/renderer/constants/themes.ts index d1f39664..180208fc 100644 --- a/src/renderer/constants/themes.ts +++ b/src/renderer/constants/themes.ts @@ -16,6 +16,7 @@ export const THEMES: Record = { accent: '#6366f1', accentDim: 'rgba(99, 102, 241, 0.2)', accentText: '#a5b4fc', + accentForeground: '#ffffff', success: '#22c55e', warning: '#eab308', error: '#ef4444' @@ -35,6 +36,7 @@ export const THEMES: Record = { accent: '#fd971f', accentDim: 'rgba(253, 151, 31, 0.2)', accentText: '#fdbf6f', + accentForeground: '#1e1f1c', success: '#a6e22e', warning: '#e6db74', error: '#f92672' @@ -54,6 +56,7 @@ export const THEMES: Record = { accent: '#88c0d0', accentDim: 'rgba(136, 192, 208, 0.2)', accentText: '#8fbcbb', + accentForeground: '#2e3440', success: '#a3be8c', warning: '#ebcb8b', error: '#bf616a' @@ -73,6 +76,7 @@ export const THEMES: Record = { accent: '#7aa2f7', accentDim: 'rgba(122, 162, 247, 0.2)', accentText: '#7dcfff', + accentForeground: '#1a1b26', success: '#9ece6a', warning: '#e0af68', error: '#f7768e' @@ -92,6 +96,7 @@ export const THEMES: Record = { accent: '#89b4fa', accentDim: 'rgba(137, 180, 250, 0.2)', accentText: '#89dceb', + accentForeground: '#1e1e2e', success: '#a6e3a1', warning: '#f9e2af', error: '#f38ba8' @@ -111,6 +116,7 @@ export const THEMES: Record = { accent: '#83a598', accentDim: 'rgba(131, 165, 152, 0.2)', accentText: '#8ec07c', + accentForeground: '#1d2021', success: '#b8bb26', warning: '#fabd2f', error: '#fb4934' @@ -131,6 +137,7 @@ export const THEMES: Record = { accent: '#0969da', accentDim: 'rgba(9, 105, 218, 0.1)', accentText: '#0969da', + accentForeground: '#ffffff', success: '#1a7f37', warning: '#9a6700', error: '#cf222e' @@ -150,6 +157,7 @@ export const THEMES: Record = { accent: '#2aa198', accentDim: 'rgba(42, 161, 152, 0.1)', accentText: '#2aa198', + accentForeground: '#fdf6e3', success: '#859900', warning: '#b58900', error: '#dc322f' @@ -169,6 +177,7 @@ export const THEMES: Record = { accent: '#4078f2', accentDim: 'rgba(64, 120, 242, 0.1)', accentText: '#4078f2', + accentForeground: '#ffffff', success: '#50a14f', warning: '#c18401', error: '#e45649' @@ -188,6 +197,7 @@ export const THEMES: Record = { accent: '#458588', accentDim: 'rgba(69, 133, 136, 0.1)', accentText: '#076678', + accentForeground: '#fbf1c7', success: '#98971a', warning: '#d79921', error: '#cc241d' @@ -207,6 +217,7 @@ export const THEMES: Record = { accent: '#1e66f5', accentDim: 'rgba(30, 102, 245, 0.1)', accentText: '#1e66f5', + accentForeground: '#ffffff', success: '#40a02b', warning: '#df8e1d', error: '#d20f39' @@ -226,6 +237,7 @@ export const THEMES: Record = { accent: '#55b4d4', accentDim: 'rgba(85, 180, 212, 0.1)', accentText: '#399ee6', + accentForeground: '#1a1a1a', success: '#86b300', warning: '#f2ae49', error: '#f07171' @@ -246,6 +258,7 @@ export const THEMES: Record = { accent: '#ff69b4', accentDim: 'rgba(255, 105, 180, 0.25)', accentText: '#ff8dc7', + accentForeground: '#1a0f24', success: '#7cb342', warning: '#d4af37', error: '#da70d6' @@ -265,6 +278,7 @@ export const THEMES: Record = { accent: '#f4c430', accentDim: 'rgba(244, 196, 48, 0.25)', accentText: '#ffd54f', + accentForeground: '#1a1a24', success: '#66d9a0', warning: '#f4c430', error: '#e05070' @@ -284,6 +298,7 @@ export const THEMES: Record = { accent: '#00ffcc', accentDim: 'rgba(0, 255, 204, 0.25)', accentText: '#40ffdd', + accentForeground: '#0d0221', success: '#00ffcc', warning: '#ff2a6d', error: '#ff2a6d' @@ -303,6 +318,7 @@ export const THEMES: Record = { accent: '#cc0033', accentDim: 'rgba(204, 0, 51, 0.25)', accentText: '#ff3355', + accentForeground: '#ffffff', success: '#f5f5f5', warning: '#cc0033', error: '#cc0033' diff --git a/src/renderer/index.html b/src/renderer/index.html index c2d5d613..dbb0e750 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -54,13 +54,13 @@ display: flex; flex-direction: column; align-items: center; - gap: 2rem; + gap: 32px; } .splash-logo { width: 180px; height: 180px; - border-radius: 1.5rem; + border-radius: 24px; box-shadow: 0 25px 50px -12px rgba(139, 92, 246, 0.4); overflow: hidden; } @@ -74,7 +74,7 @@ .splash-title { font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; font-weight: 700; - font-size: 2.25rem; + font-size: 36px; letter-spacing: 0.25em; color: #ffffff; text-shadow: 0 2px 10px rgba(139, 92, 246, 0.3); @@ -103,7 +103,7 @@ .splash-text { font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace; - font-size: 0.875rem; + font-size: 14px; letter-spacing: 0.05em; color: rgba(255, 255, 255, 0.5); } diff --git a/src/renderer/types/index.ts b/src/renderer/types/index.ts index 1a411f4f..eac67729 100644 --- a/src/renderer/types/index.ts +++ b/src/renderer/types/index.ts @@ -173,6 +173,8 @@ export interface Session { recentClaudeSessions?: RecentClaudeSession[]; // Pending AI command that will trigger a synopsis on completion (e.g., '/commit') pendingAICommandForSynopsis?: string; + // Custom batch runner prompt (persisted per session) + batchRunnerPrompt?: string; } export interface Group { diff --git a/src/shared/theme-types.ts b/src/shared/theme-types.ts index 1bc7ba71..726fd8e9 100644 --- a/src/shared/theme-types.ts +++ b/src/shared/theme-types.ts @@ -58,6 +58,8 @@ export interface ThemeColors { accentDim: string; /** Text color for accent contexts */ accentText: string; + /** Text color for use ON accent backgrounds (contrasting color) */ + accentForeground: string; /** Success state color (green tones) */ success: string; /** Warning state color (yellow/orange tones) */