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
This commit is contained in:
Pedram Amini
2025-11-28 18:14:48 -06:00
parent ecf92ffa12
commit c5a0535c46
26 changed files with 159 additions and 81 deletions

View File

@@ -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)
};
}
```

View File

@@ -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

View File

@@ -23,6 +23,7 @@ export const THEMES: Record<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
accent: '#cc0033',
accentDim: 'rgba(204, 0, 51, 0.25)',
accentText: '#ff3355',
accentForeground: '#ffffff',
success: '#f5f5f5',
warning: '#cc0033',
error: '#cc0033'

View File

@@ -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}
/>
)}

View File

@@ -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
}}
>
<Plus className="w-4 h-4" />

View File

@@ -179,7 +179,6 @@ export function AboutModal({ theme, sessions, onClose }: AboutModalProps) {
<div className="flex items-center gap-2 mb-3">
<BarChart3 className="w-4 h-4" style={{ color: theme.colors.accent }} />
<span className="text-sm font-bold" style={{ color: theme.colors.textMain }}>Global Statistics</span>
<span className="text-[10px]" style={{ color: theme.colors.textDim }}>(all Claude projects)</span>
</div>
{loading ? (
<div className="flex items-center justify-center py-4 gap-2">

View File

@@ -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,
}}
>
<Play className="w-4 h-4" />

View File

@@ -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<HTMLTextAreaElement>(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 (
<div
@@ -232,6 +242,16 @@ export function BatchRunnerModal(props: BatchRunnerModalProps) {
>
Cancel
</button>
<button
onClick={handleSave}
disabled={!hasUnsavedChanges}
className="flex items-center gap-2 px-4 py-2 rounded border hover:bg-white/5 transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
style={{ borderColor: theme.colors.border, color: theme.colors.success }}
title={hasUnsavedChanges ? 'Save prompt for this session' : 'No unsaved changes'}
>
<Save className="w-4 h-4" />
Save
</button>
<button
onClick={handleGo}
className="flex items-center gap-2 px-4 py-2 rounded text-white font-bold"

View File

@@ -218,8 +218,8 @@ export function CreateGroupModal(props: CreateGroupModalProps) {
<button
onClick={handleCreate}
disabled={!groupName.trim()}
className="px-4 py-2 rounded text-white disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent }}
className="px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent, color: theme.colors.accentForeground }}
>
Create
</button>

View File

@@ -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)'
}}
>

View File

@@ -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

View File

@@ -435,8 +435,8 @@ export function InputArea(props: InputAreaProps) {
) : (
<button
onClick={processInput}
className="p-2 rounded-md text-white shadow-sm transition-all hover:opacity-90"
style={{ backgroundColor: theme.colors.accent }}
className="p-2 rounded-md shadow-sm transition-all hover:opacity-90"
style={{ backgroundColor: theme.colors.accent, color: theme.colors.accentForeground }}
>
<ArrowUp className="w-4 h-4" />
</button>

View File

@@ -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)'
}}
>

View File

@@ -343,8 +343,8 @@ export function NewInstanceModal({ isOpen, onClose, onCreate, theme, defaultAgen
<button
onClick={handleCreate}
disabled={!selectedAgent || !agents.find(a => a.id === selectedAgent)?.available}
className="px-4 py-2 rounded text-white disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent }}
className="px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent, color: theme.colors.accentForeground }}
>
Create Agent
</button>

View File

@@ -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 ? (

View File

@@ -182,8 +182,8 @@ export function RenameGroupModal(props: RenameGroupModalProps) {
<button
onClick={handleRename}
disabled={!groupName.trim()}
className="px-4 py-2 rounded text-white disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent }}
className="px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent, color: theme.colors.accentForeground }}
>
Rename
</button>

View File

@@ -105,8 +105,8 @@ export function RenameSessionModal(props: RenameSessionModalProps) {
<button
onClick={handleRename}
disabled={!value.trim()}
className="px-4 py-2 rounded text-white disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent }}
className="px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
style={{ backgroundColor: theme.colors.accent, color: theme.colors.accentForeground }}
>
Rename
</button>

View File

@@ -120,15 +120,16 @@ export const RightPanel = forwardRef<RightPanelHandle, RightPanelProps>(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);
};

View File

@@ -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={{

View File

@@ -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
}}
>
<ExternalLink className="w-3 h-3" />
@@ -1272,7 +1273,7 @@ export function SessionList(props: SessionListProps) {
</button>
{leftSidebarOpen && (
<button onClick={addNewSession} className="flex-1 flex items-center justify-center gap-2 py-2 rounded text-xs font-bold transition-colors text-white" style={{ backgroundColor: theme.colors.accent }}>
<button onClick={addNewSession} className="flex-1 flex items-center justify-center gap-2 py-2 rounded text-xs font-bold transition-colors" style={{ backgroundColor: theme.colors.accent, color: theme.colors.accentForeground }}>
<Plus className="w-3 h-3" /> New Agent
</button>
)}

View File

@@ -716,7 +716,7 @@ export function SettingsModal(props: SettingsModalProps) {
<button
onClick={addCustomFont}
className="px-3 py-2 rounded text-xs font-bold"
style={{ backgroundColor: theme.colors.accent, color: 'white' }}
style={{ backgroundColor: theme.colors.accent, color: theme.colors.accentForeground }}
>
Add
</button>
@@ -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'}

View File

@@ -1264,7 +1264,7 @@ export const TerminalOutput = forwardRef<HTMLDivElement, TerminalOutputProps>((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)'
}}
>

View File

@@ -16,6 +16,7 @@ export const THEMES: Record<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
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<ThemeId, Theme> = {
accent: '#cc0033',
accentDim: 'rgba(204, 0, 51, 0.25)',
accentText: '#ff3355',
accentForeground: '#ffffff',
success: '#f5f5f5',
warning: '#cc0033',
error: '#cc0033'

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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) */