Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export interface KeyboardSettings {
export interface InterfaceSettings {
autoRightSidebarBehavior?: boolean;
showResourceMonitor?: boolean;
theme?: 'light' | 'dark' | 'dark-black' | 'system';
theme?: 'light' | 'dark' | 'dark-black' | 'green' | 'system';
taskHoverAction?: 'delete' | 'archive';
}

Expand Down Expand Up @@ -545,7 +545,7 @@ export function normalizeSettings(input: AppSettings): AppSettings {
showResourceMonitor: Boolean(
iface?.showResourceMonitor ?? DEFAULT_SETTINGS.interface!.showResourceMonitor
),
theme: ['light', 'dark', 'dark-black', 'system'].includes(iface?.theme)
theme: ['light', 'dark', 'dark-black', 'green', 'system'].includes(iface?.theme)
? iface.theme
: DEFAULT_SETTINGS.interface!.theme,
taskHoverAction: iface?.taskHoverAction === 'archive' ? 'archive' : 'delete',
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/AgentLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const AgentLogo: React.FC<AgentLogoProps> = ({
grayscale,
}) => {
const { effectiveTheme } = useTheme();
const isDark = effectiveTheme === 'dark' || effectiveTheme === 'dark-black';
const isDark = effectiveTheme !== 'light';

const resolvedIsSvg = isSvg ?? logo.trimStart().startsWith('<svg');

Expand Down
57 changes: 5 additions & 52 deletions src/renderer/components/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { useWorkspaceConnection } from '../hooks/useWorkspaceConnection';
import { useTerminalSearch } from '../hooks/useTerminalSearch';
import { TerminalSearchOverlay } from './TerminalSearchOverlay';
import { getReviewConversationMetadata, parseConversationMetadata } from '@shared/reviewPreset';
import { getTerminalThemeOverride, getTerminalContainerClass } from '../lib/terminalThemeColors';
import {
getConversationTabLabel,
planConversationTitleUpdates,
Expand Down Expand Up @@ -1249,21 +1250,7 @@ const ChatInterface: React.FC<Props> = ({
>
<div
ref={terminalPanelRef}
className={`relative mx-auto h-full max-w-4xl overflow-hidden rounded-md ${
agent === 'charm'
? effectiveTheme === 'dark-black'
? 'bg-black'
: effectiveTheme === 'dark'
? 'bg-card'
: 'bg-white'
: agent === 'mistral'
? effectiveTheme === 'dark' || effectiveTheme === 'dark-black'
? effectiveTheme === 'dark-black'
? 'bg-[#141820]'
: 'bg-[#202938]'
: 'bg-white'
: ''
}`}
className={`relative mx-auto h-full max-w-4xl overflow-hidden rounded-md ${getTerminalContainerClass(effectiveTheme, agent)}`}
>
<TerminalSearchOverlay
isOpen={isSearchOpen}
Expand Down Expand Up @@ -1313,44 +1300,10 @@ const ChatInterface: React.FC<Props> = ({
markActiveReviewPromptSent();
}
}}
variant={
effectiveTheme === 'dark' || effectiveTheme === 'dark-black' ? 'dark' : 'light'
}
themeOverride={
agent === 'charm'
? {
background:
effectiveTheme === 'dark-black'
? '#0a0a0a'
: effectiveTheme === 'dark'
? '#1f2937'
: '#ffffff',
selectionBackground: 'rgba(96, 165, 250, 0.35)',
selectionForeground: effectiveTheme === 'light' ? '#0f172a' : '#f9fafb',
}
: agent === 'mistral'
? {
background:
effectiveTheme === 'dark-black'
? '#141820'
: effectiveTheme === 'dark'
? '#202938'
: '#ffffff',
selectionBackground: 'rgba(96, 165, 250, 0.35)',
selectionForeground: effectiveTheme === 'light' ? '#0f172a' : '#f9fafb',
}
: effectiveTheme === 'dark-black'
? {
background: '#000000',
selectionBackground: 'rgba(96, 165, 250, 0.35)',
selectionForeground: '#f9fafb',
}
: undefined
}
variant={effectiveTheme !== 'light' ? 'dark' : 'light'}
themeOverride={getTerminalThemeOverride(effectiveTheme, agent)}
contentFilter={
agent === 'charm' &&
effectiveTheme !== 'dark' &&
effectiveTheme !== 'dark-black'
agent === 'charm' && effectiveTheme === 'light'
? 'invert(1) hue-rotate(180deg) brightness(1.1) contrast(1.05)'
: undefined
}
Expand Down
5 changes: 2 additions & 3 deletions src/renderer/components/EditorMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getMonacoLanguageId } from '@/lib/diffUtils';
import { buildMonacoModelPath } from '@/lib/monacoModelPath';
import { registerActiveCodeEditor } from '@/lib/activeCodeEditor';
import { addMonacoKeyboardShortcuts } from '@/lib/monaco-config';
import { getMonacoTheme } from '@/lib/monaco-themes';
import { useTheme } from '@/hooks/useTheme';
import { useRightSidebar } from './ui/right-sidebar';

Expand Down Expand Up @@ -555,9 +556,7 @@ export default function EditorMode({ taskPath, taskName, onClose }: EditorModePr
value={fileContent}
onChange={(value) => setFileContent(value || '')}
onMount={handleEditorMount}
theme={
effectiveTheme === 'dark' || effectiveTheme === 'dark-black' ? 'vs-dark' : 'vs'
}
theme={getMonacoTheme(effectiveTheme)}
options={{
minimap: { enabled: true },
fontSize: 13,
Expand Down
10 changes: 3 additions & 7 deletions src/renderer/components/HomeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,16 @@ const HomeView: React.FC<HomeViewProps> = ({
<div className="logo-shimmer-container">
<img
key={effectiveTheme}
src={
effectiveTheme === 'dark' || effectiveTheme === 'dark-black'
? emdashLogoWhite
: emdashLogo
}
src={effectiveTheme !== 'light' ? emdashLogoWhite : emdashLogo}
alt="Emdash"
className="logo-shimmer-image"
/>
<span
className="logo-shimmer-overlay"
aria-hidden="true"
style={{
WebkitMaskImage: `url(${effectiveTheme === 'dark' || effectiveTheme === 'dark-black' ? emdashLogoWhite : emdashLogo})`,
maskImage: `url(${effectiveTheme === 'dark' || effectiveTheme === 'dark-black' ? emdashLogoWhite : emdashLogo})`,
WebkitMaskImage: `url(${effectiveTheme !== 'light' ? emdashLogoWhite : emdashLogo})`,
maskImage: `url(${effectiveTheme !== 'light' ? emdashLogoWhite : emdashLogo})`,
WebkitMaskRepeat: 'no-repeat',
maskRepeat: 'no-repeat',
WebkitMaskSize: 'contain',
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/IntegrationsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { INTEGRATION_REGISTRY, type IntegrationId } from './integrations/registr
/** Light mode: original SVG colors. Dark / dark-black: primary colour. */
const SvgLogo = ({ raw }: { raw: string }) => {
const { effectiveTheme } = useTheme();
const isDark = effectiveTheme === 'dark' || effectiveTheme === 'dark-black';
const isDark = effectiveTheme !== 'light';

const processed = isDark
? raw
Expand Down
34 changes: 4 additions & 30 deletions src/renderer/components/MultiAgentTask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { formatCommentsForAgent } from '@/lib/formatCommentsForAgent';
import { buildPromptInjectionPayload } from '@/lib/terminalInjection';
import { TaskScopeProvider } from './TaskScopeContext';
import TaskContextBadges from './TaskContextBadges';
import { getTerminalThemeOverride, getTerminalContainerClass } from '../lib/terminalThemeColors';

interface Props {
task: Task;
Expand Down Expand Up @@ -562,7 +563,7 @@ const MultiAgentTask: React.FC<Props> = ({
<TaskScopeProvider value={{ taskId: task.id, taskPath: activeVariantPath, projectPath }}>
<div className="relative flex h-full flex-col">
{variants.map((v, idx) => {
const isDark = effectiveTheme === 'dark' || effectiveTheme === 'dark-black';
const isDark = effectiveTheme !== 'light';
const isActive = idx === activeTabIndex;
return (
<div
Expand Down Expand Up @@ -637,15 +638,7 @@ const MultiAgentTask: React.FC<Props> = ({
onWheelCapture={handleTerminalViewportWheelForwarding}
>
<div
className={`mx-auto h-full max-w-4xl overflow-hidden rounded-md ${
v.agent === 'mistral'
? isDark
? 'bg-[#202938]'
: 'bg-white'
: isDark
? 'bg-card'
: 'bg-white'
}`}
className={`mx-auto h-full max-w-4xl overflow-hidden rounded-md ${getTerminalContainerClass(effectiveTheme, v.agent)}`}
>
<TerminalPane
ref={isActive ? activeTerminalRef : undefined}
Expand All @@ -671,26 +664,7 @@ const MultiAgentTask: React.FC<Props> = ({
keepAlive
mapShiftEnterToCtrlJ
variant={isDark ? 'dark' : 'light'}
themeOverride={
v.agent === 'mistral'
? {
background:
effectiveTheme === 'dark-black'
? '#141820'
: isDark
? '#202938'
: '#ffffff',
selectionBackground: 'rgba(96, 165, 250, 0.35)',
selectionForeground: isDark ? '#f9fafb' : '#0f172a',
}
: effectiveTheme === 'dark-black'
? {
background: '#000000',
selectionBackground: 'rgba(96, 165, 250, 0.35)',
selectionForeground: '#f9fafb',
}
: undefined
}
themeOverride={getTerminalThemeOverride(effectiveTheme, v.agent)}
className="h-full w-full"
onStartSuccess={() => {
if (
Expand Down
50 changes: 20 additions & 30 deletions src/renderer/components/TaskTerminalPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ import {
} from '@shared/lifecycle';
import { shouldDisablePlay } from '../lib/lifecycleUi';
import ExpandedTerminalModal from './ExpandedTerminalModal';
import {
GREEN_COLORS,
TERMINAL_BACKGROUNDS,
MISTRAL_BACKGROUNDS,
TERMINAL_SELECTION,
getTerminalContainerClass,
} from '../lib/terminalThemeColors';

interface Task {
id: string;
Expand Down Expand Up @@ -437,17 +444,16 @@ const TaskTerminalPanelComponent: React.FC<Props> = ({

const defaultTheme = useMemo(() => {
const isMistral = agent === 'mistral';
const darkBackground = isMistral ? '#202938' : '#1e1e1e';
const blackBackground = isMistral ? '#141820' : '#000000';
const backgrounds = isMistral ? MISTRAL_BACKGROUNDS : TERMINAL_BACKGROUNDS;
const bg = backgrounds[effectiveTheme] ?? backgrounds.dark;

return effectiveTheme === 'dark' || effectiveTheme === 'dark-black'
return effectiveTheme !== 'light'
? {
background: effectiveTheme === 'dark-black' ? blackBackground : darkBackground,
foreground: '#d4d4d4',
cursor: '#aeafad',
cursorAccent: effectiveTheme === 'dark-black' ? blackBackground : darkBackground,
selectionBackground: 'rgba(96, 165, 250, 0.35)',
selectionForeground: '#f9fafb',
background: bg,
foreground: effectiveTheme === 'green' ? GREEN_COLORS.foreground : '#d4d4d4',
cursor: effectiveTheme === 'green' ? GREEN_COLORS.foreground : '#aeafad',
cursorAccent: bg,
...TERMINAL_SELECTION.dark,
black: '#000000',
red: '#cd3131',
green: '#0dbc79',
Expand Down Expand Up @@ -741,9 +747,7 @@ const TaskTerminalPanelComponent: React.FC<Props> = ({
<LifecycleTerminalView
key={`${task?.id ?? 'no-task'}-${selection.selectedLifecycle}`}
content={lifecycleLogContent}
variant={
effectiveTheme === 'dark' || effectiveTheme === 'dark-black' ? 'dark' : 'light'
}
variant={effectiveTheme !== 'light' ? 'dark' : 'light'}
themeOverride={themeOverride}
className="flex-1"
/>
Expand All @@ -752,13 +756,7 @@ const TaskTerminalPanelComponent: React.FC<Props> = ({
<div
className={cn(
'bw-terminal relative flex-1 overflow-hidden',
effectiveTheme === 'dark' || effectiveTheme === 'dark-black'
? agent === 'mistral'
? effectiveTheme === 'dark-black'
? 'bg-[#141820]'
: 'bg-[#202938]'
: 'bg-card'
: 'bg-white'
getTerminalContainerClass(effectiveTheme, agent)
)}
>
<TerminalSearchOverlay
Expand Down Expand Up @@ -797,11 +795,7 @@ const TaskTerminalPanelComponent: React.FC<Props> = ({
remote?.connectionId ? { connectionId: remote.connectionId } : undefined
}
env={taskEnv}
variant={
effectiveTheme === 'dark' || effectiveTheme === 'dark-black'
? 'dark'
: 'light'
}
variant={effectiveTheme !== 'light' ? 'dark' : 'light'}
themeOverride={themeOverride}
className="h-full w-full"
keepAlive
Expand All @@ -828,11 +822,7 @@ const TaskTerminalPanelComponent: React.FC<Props> = ({
remote={
remote?.connectionId ? { connectionId: remote.connectionId } : undefined
}
variant={
effectiveTheme === 'dark' || effectiveTheme === 'dark-black'
? 'dark'
: 'light'
}
variant={effectiveTheme !== 'light' ? 'dark' : 'light'}
themeOverride={themeOverride}
className="h-full w-full"
keepAlive
Expand Down Expand Up @@ -861,7 +851,7 @@ const TaskTerminalPanelComponent: React.FC<Props> = ({
: 'Terminal'
}
onClose={handleCloseExpandedTerminal}
variant={effectiveTheme === 'dark' || effectiveTheme === 'dark-black' ? 'dark' : 'light'}
variant={effectiveTheme !== 'light' ? 'dark' : 'light'}
/>
)}
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/components/ThemeCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { useTheme } from '../hooks/useTheme';
import { Sun, Moon, Monitor, Circle } from 'lucide-react';
import { Sun, Moon, Monitor, Circle, TreePine } from 'lucide-react';

const ThemeCard: React.FC = () => {
const { theme, setTheme } = useTheme();
Expand All @@ -9,6 +9,7 @@ const ThemeCard: React.FC = () => {
{ value: 'light' as const, label: 'Light', icon: Sun },
{ value: 'dark' as const, label: 'Dark Navy', icon: Moon },
{ value: 'dark-black' as const, label: 'Dark Black', icon: Circle },
{ value: 'green' as const, label: 'Green', icon: TreePine },
{ value: 'system' as const, label: 'System', icon: Monitor },
];

Expand Down
17 changes: 13 additions & 4 deletions src/renderer/components/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useAppSettings } from '@/contexts/AppSettingsProvider';
import { createContext, useEffect, useState, type ReactNode } from 'react';

type Theme = 'light' | 'dark' | 'dark-black' | 'system';
type EffectiveTheme = 'light' | 'dark' | 'dark-black';
type Theme = 'light' | 'dark' | 'dark-black' | 'green' | 'system';
type EffectiveTheme = 'light' | 'dark' | 'dark-black' | 'green';

function getSystemTheme(): EffectiveTheme {
if (typeof window === 'undefined') return 'light';
Expand All @@ -15,12 +15,14 @@ function applyTheme(theme: Theme, systemTheme: EffectiveTheme) {
const root = document.documentElement;
const effectiveTheme = theme === 'system' ? systemTheme : theme;

root.classList.remove('dark', 'dark-black');
root.classList.remove('dark', 'dark-black', 'green');

if (effectiveTheme === 'dark') {
root.classList.add('dark');
} else if (effectiveTheme === 'dark-black') {
root.classList.add('dark', 'dark-black');
} else if (effectiveTheme === 'green') {
root.classList.add('dark', 'green');
}
}

Expand Down Expand Up @@ -59,7 +61,14 @@ export function ThemeProvider({ children }: { children: ReactNode }) {

const toggleTheme = () => {
const base = theme === 'system' ? effectiveTheme : theme;
const next: Theme = base === 'light' ? 'dark' : base === 'dark' ? 'dark-black' : 'light';
const next: Theme =
base === 'light'
? 'dark'
: base === 'dark'
? 'dark-black'
: base === 'dark-black'
? 'green'
: 'light';
setTheme(next);
};

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/diff-viewer/FileDiffView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const FileDiffView: React.FC<FileDiffViewProps> = ({
baseRef,
}) => {
const { effectiveTheme } = useTheme();
const isDark = effectiveTheme === 'dark' || effectiveTheme === 'dark-black';
const isDark = effectiveTheme !== 'light';

const [fileData, setFileData] = useState<{
original: string;
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/mcp/McpServerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface McpServerCardProps {

const McpIcon: React.FC<{ name: string; catalogKey?: string }> = ({ name, catalogKey }) => {
const { effectiveTheme } = useTheme();
const isDark = effectiveTheme === 'dark' || effectiveTheme === 'dark-black';
const isDark = effectiveTheme !== 'light';
const iconDef = catalogKey ? mcpIconMap[catalogKey] : undefined;

if (iconDef && iconDef.type === 'svg' && iconDef.data) {
Expand Down
Loading
Loading