Merge branch 'main' into feat/notifications

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
simosmik
2026-02-27 14:47:27 +00:00
44 changed files with 2400 additions and 797 deletions

View File

@@ -12,7 +12,7 @@ type AgentListItemProps = {
type AgentConfig = {
name: string;
color: 'blue' | 'purple' | 'gray';
color: 'blue' | 'purple' | 'gray' | 'indigo';
};
const agentConfig: Record<AgentProvider, AgentConfig> = {
@@ -28,6 +28,10 @@ const agentConfig: Record<AgentProvider, AgentConfig> = {
name: 'Codex',
color: 'gray',
},
gemini: {
name: 'Gemini',
color: 'indigo',
}
};
const colorClasses = {
@@ -49,6 +53,12 @@ const colorClasses = {
bg: 'bg-gray-100 dark:bg-gray-800/50',
dot: 'bg-gray-700 dark:bg-gray-300',
},
indigo: {
border: 'border-l-indigo-500 md:border-l-indigo-500',
borderBottom: 'border-b-indigo-500',
bg: 'bg-indigo-50 dark:bg-indigo-900/20',
dot: 'bg-indigo-500',
},
} as const;
export default function AgentListItem({
@@ -66,11 +76,10 @@ export default function AgentListItem({
return (
<button
onClick={onClick}
className={`flex-1 text-center py-3 px-2 border-b-2 transition-colors ${
isSelected
? `${colors.borderBottom} ${colors.bg}`
: 'border-transparent hover:bg-gray-50 dark:hover:bg-gray-800'
}`}
className={`flex-1 text-center py-3 px-2 border-b-2 transition-colors ${isSelected
? `${colors.borderBottom} ${colors.bg}`
: 'border-transparent hover:bg-gray-50 dark:hover:bg-gray-800'
}`}
>
<div className="flex flex-col items-center gap-1">
<SessionProviderLogo provider={agentId} className="w-5 h-5" />
@@ -86,11 +95,10 @@ export default function AgentListItem({
return (
<button
onClick={onClick}
className={`w-full text-left p-3 border-l-4 transition-colors ${
isSelected
? `${colors.border} ${colors.bg}`
: 'border-transparent hover:bg-gray-50 dark:hover:bg-gray-800'
}`}
className={`w-full text-left p-3 border-l-4 transition-colors ${isSelected
? `${colors.border} ${colors.bg}`
: 'border-transparent hover:bg-gray-50 dark:hover:bg-gray-800'
}`}
>
<div className="flex items-center gap-2 mb-1">
<SessionProviderLogo provider={agentId} className="w-4 h-4" />

View File

@@ -9,15 +9,19 @@ export default function AgentsSettingsTab({
claudeAuthStatus,
cursorAuthStatus,
codexAuthStatus,
geminiAuthStatus,
onClaudeLogin,
onCursorLogin,
onCodexLogin,
onGeminiLogin,
claudePermissions,
onClaudePermissionsChange,
cursorPermissions,
onCursorPermissionsChange,
codexPermissionMode,
onCodexPermissionModeChange,
geminiPermissionMode,
onGeminiPermissionModeChange,
mcpServers,
cursorMcpServers,
codexMcpServers,
@@ -48,13 +52,19 @@ export default function AgentsSettingsTab({
authStatus: codexAuthStatus,
onLogin: onCodexLogin,
},
gemini: {
authStatus: geminiAuthStatus,
onLogin: onGeminiLogin,
},
}), [
claudeAuthStatus,
codexAuthStatus,
cursorAuthStatus,
geminiAuthStatus,
onClaudeLogin,
onCodexLogin,
onCursorLogin,
onGeminiLogin,
]);
return (
@@ -81,6 +91,8 @@ export default function AgentsSettingsTab({
onCursorPermissionsChange={onCursorPermissionsChange}
codexPermissionMode={codexPermissionMode}
onCodexPermissionModeChange={onCodexPermissionModeChange}
geminiPermissionMode={geminiPermissionMode}
onGeminiPermissionModeChange={onGeminiPermissionModeChange}
mcpServers={mcpServers}
cursorMcpServers={cursorMcpServers}
codexMcpServers={codexMcpServers}

View File

@@ -18,6 +18,7 @@ type AgentVisualConfig = {
textClass: string;
subtextClass: string;
buttonClass: string;
description?: string;
};
const agentConfig: Record<AgentProvider, AgentVisualConfig> = {
@@ -45,6 +46,15 @@ const agentConfig: Record<AgentProvider, AgentVisualConfig> = {
subtextClass: 'text-gray-700 dark:text-gray-300',
buttonClass: 'bg-gray-800 hover:bg-gray-900 dark:bg-gray-700 dark:hover:bg-gray-600',
},
gemini: {
name: 'Gemini',
description: 'Google Gemini AI assistant',
bgClass: 'bg-indigo-50 dark:bg-indigo-900/20',
borderClass: 'border-indigo-200 dark:border-indigo-800',
textClass: 'text-indigo-900 dark:text-indigo-100',
subtextClass: 'text-indigo-700 dark:text-indigo-300',
buttonClass: 'bg-indigo-600 hover:bg-indigo-700',
},
};
export default function AccountContent({ agent, authStatus, onLogin }: AccountContentProps) {

View File

@@ -3,7 +3,7 @@ import { AlertTriangle, Plus, Shield, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Button } from '../../../../../../ui/button';
import { Input } from '../../../../../../ui/input';
import type { CodexPermissionMode } from '../../../../../types/types';
import type { CodexPermissionMode, GeminiPermissionMode } from '../../../../../types/types';
const COMMON_CLAUDE_TOOLS = [
'Bash(git log:*)',
@@ -490,11 +490,10 @@ function CodexPermissions({ permissionMode, onPermissionModeChange }: Omit<Codex
<p className="text-sm text-muted-foreground">{t('permissions.codex.description')}</p>
<div
className={`border rounded-lg p-4 cursor-pointer transition-all ${
permissionMode === 'default'
? 'bg-gray-100 dark:bg-gray-800 border-gray-400 dark:border-gray-500'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
className={`border rounded-lg p-4 cursor-pointer transition-all ${permissionMode === 'default'
? 'bg-gray-100 dark:bg-gray-800 border-gray-400 dark:border-gray-500'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
onClick={() => onPermissionModeChange('default')}
>
<label className="flex items-start gap-3 cursor-pointer">
@@ -515,11 +514,10 @@ function CodexPermissions({ permissionMode, onPermissionModeChange }: Omit<Codex
</div>
<div
className={`border rounded-lg p-4 cursor-pointer transition-all ${
permissionMode === 'acceptEdits'
? 'bg-green-50 dark:bg-green-900/20 border-green-400 dark:border-green-600'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
className={`border rounded-lg p-4 cursor-pointer transition-all ${permissionMode === 'acceptEdits'
? 'bg-green-50 dark:bg-green-900/20 border-green-400 dark:border-green-600'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
onClick={() => onPermissionModeChange('acceptEdits')}
>
<label className="flex items-start gap-3 cursor-pointer">
@@ -540,11 +538,10 @@ function CodexPermissions({ permissionMode, onPermissionModeChange }: Omit<Codex
</div>
<div
className={`border rounded-lg p-4 cursor-pointer transition-all ${
permissionMode === 'bypassPermissions'
? 'bg-orange-50 dark:bg-orange-900/20 border-orange-400 dark:border-orange-600'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
className={`border rounded-lg p-4 cursor-pointer transition-all ${permissionMode === 'bypassPermissions'
? 'bg-orange-50 dark:bg-orange-900/20 border-orange-400 dark:border-orange-600'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
onClick={() => onPermissionModeChange('bypassPermissions')}
>
<label className="flex items-start gap-3 cursor-pointer">
@@ -583,7 +580,111 @@ function CodexPermissions({ permissionMode, onPermissionModeChange }: Omit<Codex
);
}
type PermissionsContentProps = ClaudePermissionsProps | CursorPermissionsProps | CodexPermissionsProps;
type GeminiPermissionsProps = {
agent: 'gemini';
permissionMode: GeminiPermissionMode;
onPermissionModeChange: (value: GeminiPermissionMode) => void;
};
// Gemini Permissions
function GeminiPermissions({ permissionMode, onPermissionModeChange }: Omit<GeminiPermissionsProps, 'agent'>) {
const { t } = useTranslation(['settings', 'chat']);
return (
<div className="space-y-6">
<div className="space-y-4">
<div className="flex items-center gap-3">
<Shield className="w-5 h-5 text-green-500" />
<h3 className="text-lg font-medium text-foreground">
{t('gemini.permissionMode')}
</h3>
</div>
<p className="text-sm text-muted-foreground">
{t('gemini.description')}
</p>
{/* Default Mode */}
<div
className={`border rounded-lg p-4 cursor-pointer transition-all ${permissionMode === 'default'
? 'bg-gray-100 dark:bg-gray-800 border-gray-400 dark:border-gray-500'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
onClick={() => onPermissionModeChange('default')}
>
<label className="flex items-start gap-3 cursor-pointer">
<input
type="radio"
name="geminiPermissionMode"
checked={permissionMode === 'default'}
onChange={() => onPermissionModeChange('default')}
className="mt-1 w-4 h-4 text-green-600"
/>
<div>
<div className="font-medium text-foreground">{t('gemini.modes.default.title')}</div>
<div className="text-sm text-muted-foreground">
{t('gemini.modes.default.description')}
</div>
</div>
</label>
</div>
{/* Auto Edit Mode */}
<div
className={`border rounded-lg p-4 cursor-pointer transition-all ${permissionMode === 'auto_edit'
? 'bg-green-50 dark:bg-green-900/20 border-green-400 dark:border-green-600'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
onClick={() => onPermissionModeChange('auto_edit')}
>
<label className="flex items-start gap-3 cursor-pointer">
<input
type="radio"
name="geminiPermissionMode"
checked={permissionMode === 'auto_edit'}
onChange={() => onPermissionModeChange('auto_edit')}
className="mt-1 w-4 h-4 text-green-600"
/>
<div>
<div className="font-medium text-green-900 dark:text-green-100">{t('gemini.modes.autoEdit.title')}</div>
<div className="text-sm text-green-700 dark:text-green-300">
{t('gemini.modes.autoEdit.description')}
</div>
</div>
</label>
</div>
{/* YOLO Mode */}
<div
className={`border rounded-lg p-4 cursor-pointer transition-all ${permissionMode === 'yolo'
? 'bg-orange-50 dark:bg-orange-900/20 border-orange-400 dark:border-orange-600'
: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
}`}
onClick={() => onPermissionModeChange('yolo')}
>
<label className="flex items-start gap-3 cursor-pointer">
<input
type="radio"
name="geminiPermissionMode"
checked={permissionMode === 'yolo'}
onChange={() => onPermissionModeChange('yolo')}
className="mt-1 w-4 h-4 text-orange-600"
/>
<div>
<div className="font-medium text-orange-900 dark:text-orange-100 flex items-center gap-2">
{t('gemini.modes.yolo.title')}
<AlertTriangle className="w-4 h-4" />
</div>
<div className="text-sm text-orange-700 dark:text-orange-300">
{t('gemini.modes.yolo.description')}
</div>
</div>
</label>
</div>
</div>
</div>
);
}
type PermissionsContentProps = ClaudePermissionsProps | CursorPermissionsProps | CodexPermissionsProps | GeminiPermissionsProps;
export default function PermissionsContent(props: PermissionsContentProps) {
if (props.agent === 'claude') {
@@ -594,5 +695,9 @@ export default function PermissionsContent(props: PermissionsContentProps) {
return <CursorPermissions {...props} />;
}
if (props.agent === 'gemini') {
return <GeminiPermissions {...props} />;
}
return <CodexPermissions {...props} />;
}

View File

@@ -3,8 +3,9 @@ import type {
AuthStatus,
AgentCategory,
ClaudePermissionsState,
CodexPermissionMode,
CursorPermissionsState,
CodexPermissionMode,
GeminiPermissionMode,
McpServer,
McpToolsResult,
McpTestResult,
@@ -21,15 +22,19 @@ export type AgentsSettingsTabProps = {
claudeAuthStatus: AuthStatus;
cursorAuthStatus: AuthStatus;
codexAuthStatus: AuthStatus;
geminiAuthStatus: AuthStatus;
onClaudeLogin: () => void;
onCursorLogin: () => void;
onCodexLogin: () => void;
onGeminiLogin: () => void;
claudePermissions: ClaudePermissionsState;
onClaudePermissionsChange: (value: ClaudePermissionsState) => void;
cursorPermissions: CursorPermissionsState;
onCursorPermissionsChange: (value: CursorPermissionsState) => void;
codexPermissionMode: CodexPermissionMode;
onCodexPermissionModeChange: (value: CodexPermissionMode) => void;
geminiPermissionMode: GeminiPermissionMode;
onGeminiPermissionModeChange: (value: GeminiPermissionMode) => void;
mcpServers: McpServer[];
cursorMcpServers: McpServer[];
codexMcpServers: McpServer[];
@@ -66,6 +71,8 @@ export type AgentCategoryContentSectionProps = {
onCursorPermissionsChange: (value: CursorPermissionsState) => void;
codexPermissionMode: CodexPermissionMode;
onCodexPermissionModeChange: (value: CodexPermissionMode) => void;
geminiPermissionMode: GeminiPermissionMode;
onGeminiPermissionModeChange: (value: GeminiPermissionMode) => void;
mcpServers: McpServer[];
cursorMcpServers: McpServer[];
codexMcpServers: McpServer[];