style: fix formatting with Prettier

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
SuperComboGamer
2025-12-21 20:31:57 -05:00
parent 584f5a3426
commit 8d578558ff
295 changed files with 9088 additions and 10546 deletions

View File

@@ -1,8 +1,8 @@
import { Label } from "@/components/ui/label";
import { Sparkles } from "lucide-react";
import { cn } from "@/lib/utils";
import { useAppStore } from "@/store/app-store";
import { CLAUDE_MODELS } from "@/components/views/board-view/shared/model-constants";
import { Label } from '@/components/ui/label';
import { Sparkles } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useAppStore } from '@/store/app-store';
import { CLAUDE_MODELS } from '@/components/views/board-view/shared/model-constants';
export function AIEnhancementSection() {
const { enhancementModel, setEnhancementModel } = useAppStore();
@@ -10,10 +10,10 @@ export function AIEnhancementSection() {
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-border/50",
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-black/5"
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
@@ -29,9 +29,7 @@ export function AIEnhancementSection() {
</div>
<div className="p-6 space-y-4">
<div className="space-y-4">
<Label className="text-foreground font-medium">
Enhancement Model
</Label>
<Label className="text-foreground font-medium">Enhancement Model</Label>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
{CLAUDE_MODELS.map(({ id, label, description, badge }) => {
const isActive = enhancementModel === id;
@@ -40,46 +38,48 @@ export function AIEnhancementSection() {
key={id}
onClick={() => setEnhancementModel(id)}
className={cn(
"group flex flex-col items-start gap-2 px-4 py-4 rounded-xl text-left",
"transition-all duration-200 ease-out",
'group flex flex-col items-start gap-2 px-4 py-4 rounded-xl text-left',
'transition-all duration-200 ease-out',
isActive
? [
"bg-gradient-to-br from-brand-500/15 to-brand-600/10",
"border-2 border-brand-500/40",
"text-foreground",
"shadow-md shadow-brand-500/10",
'bg-gradient-to-br from-brand-500/15 to-brand-600/10',
'border-2 border-brand-500/40',
'text-foreground',
'shadow-md shadow-brand-500/10',
]
: [
"bg-accent/30 hover:bg-accent/50",
"border border-border/50 hover:border-border",
"text-muted-foreground hover:text-foreground",
"hover:shadow-sm",
'bg-accent/30 hover:bg-accent/50',
'border border-border/50 hover:border-border',
'text-muted-foreground hover:text-foreground',
'hover:shadow-sm',
],
"hover:scale-[1.02] active:scale-[0.98]"
'hover:scale-[1.02] active:scale-[0.98]'
)}
data-testid={`enhancement-model-${id}`}
>
<div className="flex items-center gap-2 w-full">
<span className={cn(
"font-medium text-sm",
isActive ? "text-foreground" : "group-hover:text-foreground"
)}>
<span
className={cn(
'font-medium text-sm',
isActive ? 'text-foreground' : 'group-hover:text-foreground'
)}
>
{label}
</span>
{badge && (
<span className={cn(
"ml-auto text-xs px-2 py-0.5 rounded-full",
isActive
? "bg-brand-500/20 text-brand-500"
: "bg-accent text-muted-foreground"
)}>
<span
className={cn(
'ml-auto text-xs px-2 py-0.5 rounded-full',
isActive
? 'bg-brand-500/20 text-brand-500'
: 'bg-accent text-muted-foreground'
)}
>
{badge}
</span>
)}
</div>
<span className="text-xs text-muted-foreground/80">
{description}
</span>
<span className="text-xs text-muted-foreground/80">{description}</span>
</button>
);
})}

View File

@@ -1 +1 @@
export { AIEnhancementSection } from "./ai-enhancement-section";
export { AIEnhancementSection } from './ai-enhancement-section';

View File

@@ -1,8 +1,8 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { AlertCircle, CheckCircle2, Eye, EyeOff, Loader2, Zap } from "lucide-react";
import type { ProviderConfig } from "@/config/api-providers";
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { AlertCircle, CheckCircle2, Eye, EyeOff, Loader2, Zap } from 'lucide-react';
import type { ProviderConfig } from '@/config/api-providers';
interface ApiKeyFieldProps {
config: ProviderConfig;
@@ -42,7 +42,7 @@ export function ApiKeyField({ config }: ApiKeyFieldProps) {
<div className="relative flex-1">
<Input
id={inputId}
type={showValue ? "text" : "password"}
type={showValue ? 'text' : 'password'}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={placeholder}
@@ -82,7 +82,7 @@ export function ApiKeyField({ config }: ApiKeyFieldProps) {
</Button>
</div>
<p className="text-xs text-muted-foreground">
{descriptionPrefix}{" "}
{descriptionPrefix}{' '}
<a
href={descriptionLinkHref}
target="_blank"
@@ -97,8 +97,8 @@ export function ApiKeyField({ config }: ApiKeyFieldProps) {
<div
className={`flex items-center gap-2 p-3 rounded-lg ${
result.success
? "bg-green-500/10 border border-green-500/20 text-green-400"
: "bg-red-500/10 border border-red-500/20 text-red-400"
? 'bg-green-500/10 border border-green-500/20 text-green-400'
: 'bg-red-500/10 border border-red-500/20 text-red-400'
}`}
data-testid={resultTestId}
>

View File

@@ -1,17 +1,17 @@
import { useAppStore } from "@/store/app-store";
import { useSetupStore } from "@/store/setup-store";
import { Button } from "@/components/ui/button";
import { Key, CheckCircle2, Settings, Trash2, Loader2 } from "lucide-react";
import { ApiKeyField } from "./api-key-field";
import { buildProviderConfigs } from "@/config/api-providers";
import { AuthenticationStatusDisplay } from "./authentication-status-display";
import { SecurityNotice } from "./security-notice";
import { useApiKeyManagement } from "./hooks/use-api-key-management";
import { cn } from "@/lib/utils";
import { useState, useCallback } from "react";
import { getElectronAPI } from "@/lib/electron";
import { toast } from "sonner";
import { useNavigate } from "@tanstack/react-router";
import { useAppStore } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store';
import { Button } from '@/components/ui/button';
import { Key, CheckCircle2, Settings, Trash2, Loader2 } from 'lucide-react';
import { ApiKeyField } from './api-key-field';
import { buildProviderConfigs } from '@/config/api-providers';
import { AuthenticationStatusDisplay } from './authentication-status-display';
import { SecurityNotice } from './security-notice';
import { useApiKeyManagement } from './hooks/use-api-key-management';
import { cn } from '@/lib/utils';
import { useState, useCallback } from 'react';
import { getElectronAPI } from '@/lib/electron';
import { toast } from 'sonner';
import { useNavigate } from '@tanstack/react-router';
export function ApiKeysSection() {
const { apiKeys, setApiKeys } = useAppStore();
@@ -19,8 +19,7 @@ export function ApiKeysSection() {
const [isDeletingAnthropicKey, setIsDeletingAnthropicKey] = useState(false);
const navigate = useNavigate();
const { providerConfigParams, apiKeyStatus, handleSave, saved } =
useApiKeyManagement();
const { providerConfigParams, apiKeyStatus, handleSave, saved } = useApiKeyManagement();
const providerConfigs = buildProviderConfigs(providerConfigParams);
@@ -30,24 +29,24 @@ export function ApiKeysSection() {
try {
const api = getElectronAPI();
if (!api.setup?.deleteApiKey) {
toast.error("Delete API not available");
toast.error('Delete API not available');
return;
}
const result = await api.setup.deleteApiKey("anthropic");
const result = await api.setup.deleteApiKey('anthropic');
if (result.success) {
setApiKeys({ ...apiKeys, anthropic: "" });
setApiKeys({ ...apiKeys, anthropic: '' });
setClaudeAuthStatus({
authenticated: false,
method: "none",
method: 'none',
hasCredentialsFile: claudeAuthStatus?.hasCredentialsFile || false,
});
toast.success("Anthropic API key deleted");
toast.success('Anthropic API key deleted');
} else {
toast.error(result.error || "Failed to delete API key");
toast.error(result.error || 'Failed to delete API key');
}
} catch (error) {
toast.error("Failed to delete API key");
toast.error('Failed to delete API key');
} finally {
setIsDeletingAnthropicKey(false);
}
@@ -56,16 +55,16 @@ export function ApiKeysSection() {
// Open setup wizard
const openSetupWizard = useCallback(() => {
setSetupComplete(false);
navigate({ to: "/setup" });
navigate({ to: '/setup' });
}, [setSetupComplete, navigate]);
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-border/50",
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-black/5"
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
@@ -101,13 +100,13 @@ export function ApiKeysSection() {
onClick={handleSave}
data-testid="save-settings"
className={cn(
"min-w-[140px] h-10",
"bg-gradient-to-r from-brand-500 to-brand-600",
"hover:from-brand-600 hover:to-brand-600",
"text-white font-medium border-0",
"shadow-md shadow-brand-500/20 hover:shadow-lg hover:shadow-brand-500/25",
"transition-all duration-200 ease-out",
"hover:scale-[1.02] active:scale-[0.98]"
'min-w-[140px] h-10',
'bg-gradient-to-r from-brand-500 to-brand-600',
'hover:from-brand-600 hover:to-brand-600',
'text-white font-medium border-0',
'shadow-md shadow-brand-500/20 hover:shadow-lg hover:shadow-brand-500/25',
'transition-all duration-200 ease-out',
'hover:scale-[1.02] active:scale-[0.98]'
)}
>
{saved ? (
@@ -116,7 +115,7 @@ export function ApiKeysSection() {
Saved!
</>
) : (
"Save API Keys"
'Save API Keys'
)}
</Button>

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from "react";
import { useAppStore } from "@/store/app-store";
import { getElectronAPI } from "@/lib/electron";
import type { ProviderConfigParams } from "@/config/api-providers";
import { useState, useEffect } from 'react';
import { useAppStore } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import type { ProviderConfigParams } from '@/config/api-providers';
interface TestResult {
success: boolean;
@@ -32,9 +32,7 @@ export function useApiKeyManagement() {
const [testingConnection, setTestingConnection] = useState(false);
const [testResult, setTestResult] = useState<TestResult | null>(null);
const [testingGeminiConnection, setTestingGeminiConnection] = useState(false);
const [geminiTestResult, setGeminiTestResult] = useState<TestResult | null>(
null
);
const [geminiTestResult, setGeminiTestResult] = useState<TestResult | null>(null);
// API key status from environment
const [apiKeyStatus, setApiKeyStatus] = useState<ApiKeyStatus | null>(null);
@@ -62,7 +60,7 @@ export function useApiKeyManagement() {
});
}
} catch (error) {
console.error("Failed to check API key status:", error);
console.error('Failed to check API key status:', error);
}
}
};
@@ -76,23 +74,23 @@ export function useApiKeyManagement() {
try {
const api = getElectronAPI();
const data = await api.setup.verifyClaudeAuth("api_key");
const data = await api.setup.verifyClaudeAuth('api_key');
if (data.success && data.authenticated) {
setTestResult({
success: true,
message: "Connection successful! Claude responded.",
message: 'Connection successful! Claude responded.',
});
} else {
setTestResult({
success: false,
message: data.error || "Failed to connect to Claude API.",
message: data.error || 'Failed to connect to Claude API.',
});
}
} catch {
setTestResult({
success: false,
message: "Network error. Please check your connection.",
message: 'Network error. Please check your connection.',
});
} finally {
setTestingConnection(false);
@@ -109,7 +107,7 @@ export function useApiKeyManagement() {
if (!googleKey || googleKey.trim().length < 10) {
setGeminiTestResult({
success: false,
message: "Please enter a valid API key.",
message: 'Please enter a valid API key.',
});
setTestingGeminiConnection(false);
return;
@@ -119,7 +117,7 @@ export function useApiKeyManagement() {
// Full verification requires a backend endpoint
setGeminiTestResult({
success: true,
message: "API key saved. Connection test not yet available.",
message: 'API key saved. Connection test not yet available.',
});
setTestingGeminiConnection(false);
};

View File

@@ -1,4 +1,4 @@
import { AlertCircle } from "lucide-react";
import { AlertCircle } from 'lucide-react';
interface SecurityNoticeProps {
title?: string;
@@ -6,7 +6,7 @@ interface SecurityNoticeProps {
}
export function SecurityNotice({
title = "Security Notice",
title = 'Security Notice',
message = "API keys are stored in your browser's local storage. Never share your API keys or commit them to version control.",
}: SecurityNoticeProps) {
return (

View File

@@ -1,24 +1,21 @@
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { Volume2, VolumeX } from "lucide-react";
import { cn } from "@/lib/utils";
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import { Volume2, VolumeX } from 'lucide-react';
import { cn } from '@/lib/utils';
interface AudioSectionProps {
muteDoneSound: boolean;
onMuteDoneSoundChange: (value: boolean) => void;
}
export function AudioSection({
muteDoneSound,
onMuteDoneSoundChange,
}: AudioSectionProps) {
export function AudioSection({ muteDoneSound, onMuteDoneSoundChange }: AudioSectionProps) {
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-border/50",
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-black/5"
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
@@ -26,9 +23,7 @@ export function AudioSection({
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-brand-500/20 to-brand-600/10 flex items-center justify-center border border-brand-500/20">
<Volume2 className="w-5 h-5 text-brand-500" />
</div>
<h2 className="text-lg font-semibold text-foreground tracking-tight">
Audio
</h2>
<h2 className="text-lg font-semibold text-foreground tracking-tight">Audio</h2>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Configure audio and notification settings.
@@ -52,9 +47,9 @@ export function AudioSection({
Mute notification sound when agents complete
</Label>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
When enabled, disables the &quot;ding&quot; sound that plays when
an agent completes a feature. The feature will still move to the
completed column, but without audio notification.
When enabled, disables the &quot;ding&quot; sound that plays when an agent completes a
feature. The feature will still move to the completed column, but without audio
notification.
</p>
</div>
</div>

View File

@@ -1,12 +1,7 @@
import { Button } from "@/components/ui/button";
import {
Terminal,
CheckCircle2,
AlertCircle,
RefreshCw,
} from "lucide-react";
import { cn } from "@/lib/utils";
import type { CliStatus } from "../shared/types";
import { Button } from '@/components/ui/button';
import { Terminal, CheckCircle2, AlertCircle, RefreshCw } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { CliStatus } from '../shared/types';
interface CliStatusProps {
status: CliStatus | null;
@@ -14,20 +9,16 @@ interface CliStatusProps {
onRefresh: () => void;
}
export function ClaudeCliStatus({
status,
isChecking,
onRefresh,
}: CliStatusProps) {
export function ClaudeCliStatus({ status, isChecking, onRefresh }: CliStatusProps) {
if (!status) return null;
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-border/50",
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-black/5"
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
@@ -48,32 +39,28 @@ export function ClaudeCliStatus({
data-testid="refresh-claude-cli"
title="Refresh Claude CLI detection"
className={cn(
"h-9 w-9 rounded-lg",
"hover:bg-accent/50 hover:scale-105",
"transition-all duration-200"
'h-9 w-9 rounded-lg',
'hover:bg-accent/50 hover:scale-105',
'transition-all duration-200'
)}
>
<RefreshCw
className={cn("w-4 h-4", isChecking && "animate-spin")}
/>
<RefreshCw className={cn('w-4 h-4', isChecking && 'animate-spin')} />
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Claude Code CLI provides better performance for long-running tasks,
especially with ultrathink.
Claude Code CLI provides better performance for long-running tasks, especially with
ultrathink.
</p>
</div>
<div className="p-6 space-y-4">
{status.success && status.status === "installed" ? (
{status.success && status.status === 'installed' ? (
<div className="space-y-3">
<div className="flex items-center gap-3 p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20">
<div className="w-10 h-10 rounded-xl bg-emerald-500/15 flex items-center justify-center border border-emerald-500/20 shrink-0">
<CheckCircle2 className="w-5 h-5 text-emerald-500" />
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-emerald-400">
Claude Code CLI Installed
</p>
<p className="text-sm font-medium text-emerald-400">Claude Code CLI Installed</p>
<div className="text-xs text-emerald-400/70 mt-1.5 space-y-0.5">
{status.method && (
<p>
@@ -94,9 +81,7 @@ export function ClaudeCliStatus({
</div>
</div>
{status.recommendation && (
<p className="text-xs text-muted-foreground/70 ml-1">
{status.recommendation}
</p>
<p className="text-xs text-muted-foreground/70 ml-1">{status.recommendation}</p>
)}
</div>
) : (
@@ -106,24 +91,22 @@ export function ClaudeCliStatus({
<AlertCircle className="w-5 h-5 text-amber-500" />
</div>
<div className="flex-1">
<p className="text-sm font-medium text-amber-400">
Claude Code CLI Not Detected
</p>
<p className="text-sm font-medium text-amber-400">Claude Code CLI Not Detected</p>
<p className="text-xs text-amber-400/70 mt-1">
{status.recommendation ||
"Consider installing Claude Code CLI for optimal performance with ultrathink."}
'Consider installing Claude Code CLI for optimal performance with ultrathink.'}
</p>
</div>
</div>
{status.installCommands && (
<div className="space-y-3">
<p className="text-xs font-medium text-foreground/80">
Installation Commands:
</p>
<p className="text-xs font-medium text-foreground/80">Installation Commands:</p>
<div className="space-y-2">
{status.installCommands.npm && (
<div className="p-3 rounded-xl bg-accent/30 border border-border/50">
<p className="text-[10px] text-muted-foreground mb-1.5 font-medium uppercase tracking-wider">npm</p>
<p className="text-[10px] text-muted-foreground mb-1.5 font-medium uppercase tracking-wider">
npm
</p>
<code className="text-xs text-foreground/80 font-mono break-all">
{status.installCommands.npm}
</code>
@@ -131,7 +114,9 @@ export function ClaudeCliStatus({
)}
{status.installCommands.macos && (
<div className="p-3 rounded-xl bg-accent/30 border border-border/50">
<p className="text-[10px] text-muted-foreground mb-1.5 font-medium uppercase tracking-wider">macOS/Linux</p>
<p className="text-[10px] text-muted-foreground mb-1.5 font-medium uppercase tracking-wider">
macOS/Linux
</p>
<code className="text-xs text-foreground/80 font-mono break-all">
{status.installCommands.macos}
</code>
@@ -139,7 +124,9 @@ export function ClaudeCliStatus({
)}
{status.installCommands.windows && (
<div className="p-3 rounded-xl bg-accent/30 border border-border/50">
<p className="text-[10px] text-muted-foreground mb-1.5 font-medium uppercase tracking-wider">Windows (PowerShell)</p>
<p className="text-[10px] text-muted-foreground mb-1.5 font-medium uppercase tracking-wider">
Windows (PowerShell)
</p>
<code className="text-xs text-foreground/80 font-mono break-all">
{status.installCommands.windows}
</code>

View File

@@ -1,12 +1,12 @@
import { Keyboard } from "lucide-react";
import { Keyboard } from 'lucide-react';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { KeyboardMap, ShortcutReferencePanel } from "@/components/ui/keyboard-map";
} from '@/components/ui/dialog';
import { KeyboardMap, ShortcutReferencePanel } from '@/components/ui/keyboard-map';
interface KeyboardMapDialogProps {
open: boolean;
@@ -23,8 +23,8 @@ export function KeyboardMapDialog({ open, onOpenChange }: KeyboardMapDialogProps
Keyboard Shortcut Map
</DialogTitle>
<DialogDescription className="text-muted-foreground">
Visual overview of all keyboard shortcuts. Keys in color are bound to
shortcuts. Click on any shortcut below to edit it.
Visual overview of all keyboard shortcuts. Keys in color are bound to shortcuts. Click
on any shortcut below to edit it.
</DialogDescription>
</DialogHeader>

View File

@@ -1,5 +1,5 @@
import { Settings } from "lucide-react";
import { cn } from "@/lib/utils";
import { Settings } from 'lucide-react';
import { cn } from '@/lib/utils';
interface SettingsHeaderProps {
title?: string;
@@ -7,23 +7,27 @@ interface SettingsHeaderProps {
}
export function SettingsHeader({
title = "Settings",
description = "Configure your API keys and preferences",
title = 'Settings',
description = 'Configure your API keys and preferences',
}: SettingsHeaderProps) {
return (
<div className={cn(
"shrink-0",
"border-b border-border/50",
"bg-gradient-to-r from-card/90 via-card/70 to-card/80 backdrop-blur-xl"
)}>
<div
className={cn(
'shrink-0',
'border-b border-border/50',
'bg-gradient-to-r from-card/90 via-card/70 to-card/80 backdrop-blur-xl'
)}
>
<div className="px-8 py-6">
<div className="flex items-center gap-4">
<div className={cn(
"w-12 h-12 rounded-2xl flex items-center justify-center",
"bg-gradient-to-br from-brand-500 to-brand-600",
"shadow-lg shadow-brand-500/25",
"ring-1 ring-white/10"
)}>
<div
className={cn(
'w-12 h-12 rounded-2xl flex items-center justify-center',
'bg-gradient-to-br from-brand-500 to-brand-600',
'shadow-lg shadow-brand-500/25',
'ring-1 ring-white/10'
)}
>
<Settings className="w-6 h-6 text-white" />
</div>
<div>

View File

@@ -1,7 +1,7 @@
import { cn } from "@/lib/utils";
import type { Project } from "@/lib/electron";
import type { NavigationItem } from "../config/navigation";
import type { SettingsViewId } from "../hooks/use-settings-view";
import { cn } from '@/lib/utils';
import type { Project } from '@/lib/electron';
import type { NavigationItem } from '../config/navigation';
import type { SettingsViewId } from '../hooks/use-settings-view';
interface SettingsNavigationProps {
navItems: NavigationItem[];
@@ -17,14 +17,16 @@ export function SettingsNavigation({
onNavigate,
}: SettingsNavigationProps) {
return (
<nav className={cn(
"hidden lg:block w-52 shrink-0",
"border-r border-border/50",
"bg-gradient-to-b from-card/80 via-card/60 to-card/40 backdrop-blur-xl"
)}>
<nav
className={cn(
'hidden lg:block w-52 shrink-0',
'border-r border-border/50',
'bg-gradient-to-b from-card/80 via-card/60 to-card/40 backdrop-blur-xl'
)}
>
<div className="sticky top-0 p-4 space-y-1.5">
{navItems
.filter((item) => item.id !== "danger" || currentProject)
.filter((item) => item.id !== 'danger' || currentProject)
.map((item) => {
const Icon = item.icon;
const isActive = activeSection === item.id;
@@ -33,20 +35,20 @@ export function SettingsNavigation({
key={item.id}
onClick={() => onNavigate(item.id)}
className={cn(
"group w-full flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-200 ease-out text-left relative overflow-hidden",
'group w-full flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-200 ease-out text-left relative overflow-hidden',
isActive
? [
"bg-gradient-to-r from-brand-500/15 via-brand-500/10 to-brand-600/5",
"text-foreground",
"border border-brand-500/25",
"shadow-sm shadow-brand-500/5",
'bg-gradient-to-r from-brand-500/15 via-brand-500/10 to-brand-600/5',
'text-foreground',
'border border-brand-500/25',
'shadow-sm shadow-brand-500/5',
]
: [
"text-muted-foreground hover:text-foreground",
"hover:bg-accent/50",
"border border-transparent hover:border-border/40",
'text-muted-foreground hover:text-foreground',
'hover:bg-accent/50',
'border border-transparent hover:border-border/40',
],
"hover:scale-[1.01] active:scale-[0.98]"
'hover:scale-[1.01] active:scale-[0.98]'
)}
>
{/* Active indicator bar */}
@@ -55,10 +57,8 @@ export function SettingsNavigation({
)}
<Icon
className={cn(
"w-4 h-4 shrink-0 transition-all duration-200",
isActive
? "text-brand-500"
: "group-hover:text-brand-400 group-hover:scale-110"
'w-4 h-4 shrink-0 transition-all duration-200',
isActive ? 'text-brand-500' : 'group-hover:text-brand-400 group-hover:scale-110'
)}
/>
<span className="truncate">{item.label}</span>

View File

@@ -1,4 +1,4 @@
import type { LucideIcon } from "lucide-react";
import type { LucideIcon } from 'lucide-react';
import {
Key,
Terminal,
@@ -9,8 +9,8 @@ import {
FlaskConical,
Trash2,
Sparkles,
} from "lucide-react";
import type { SettingsViewId } from "../hooks/use-settings-view";
} from 'lucide-react';
import type { SettingsViewId } from '../hooks/use-settings-view';
export interface NavigationItem {
id: SettingsViewId;
@@ -20,13 +20,13 @@ export interface NavigationItem {
// Navigation items for the settings side panel
export const NAV_ITEMS: NavigationItem[] = [
{ id: "api-keys", label: "API Keys", icon: Key },
{ id: "claude", label: "Claude", icon: Terminal },
{ id: "ai-enhancement", label: "AI Enhancement", icon: Sparkles },
{ id: "appearance", label: "Appearance", icon: Palette },
{ id: "terminal", label: "Terminal", icon: SquareTerminal },
{ id: "keyboard", label: "Keyboard Shortcuts", icon: Settings2 },
{ id: "audio", label: "Audio", icon: Volume2 },
{ id: "defaults", label: "Feature Defaults", icon: FlaskConical },
{ id: "danger", label: "Danger Zone", icon: Trash2 },
{ id: 'api-keys', label: 'API Keys', icon: Key },
{ id: 'claude', label: 'Claude', icon: Terminal },
{ id: 'ai-enhancement', label: 'AI Enhancement', icon: Sparkles },
{ id: 'appearance', label: 'Appearance', icon: Palette },
{ id: 'terminal', label: 'Terminal', icon: SquareTerminal },
{ id: 'keyboard', label: 'Keyboard Shortcuts', icon: Settings2 },
{ id: 'audio', label: 'Audio', icon: Volume2 },
{ id: 'defaults', label: 'Feature Defaults', icon: FlaskConical },
{ id: 'danger', label: 'Danger Zone', icon: Trash2 },
];

View File

@@ -1,26 +1,23 @@
import { Button } from "@/components/ui/button";
import { Trash2, Folder, AlertTriangle } from "lucide-react";
import { cn } from "@/lib/utils";
import type { Project } from "../shared/types";
import { Button } from '@/components/ui/button';
import { Trash2, Folder, AlertTriangle } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { Project } from '../shared/types';
interface DangerZoneSectionProps {
project: Project | null;
onDeleteClick: () => void;
}
export function DangerZoneSection({
project,
onDeleteClick,
}: DangerZoneSectionProps) {
export function DangerZoneSection({ project, onDeleteClick }: DangerZoneSectionProps) {
if (!project) return null;
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-destructive/30",
"bg-gradient-to-br from-destructive/5 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-destructive/5"
'rounded-2xl overflow-hidden',
'border border-destructive/30',
'bg-gradient-to-br from-destructive/5 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-destructive/5'
)}
>
<div className="p-6 border-b border-destructive/20 bg-gradient-to-r from-destructive/5 via-transparent to-transparent">
@@ -41,12 +38,8 @@ export function DangerZoneSection({
<Folder className="w-5 h-5 text-brand-500" />
</div>
<div className="min-w-0">
<p className="font-medium text-foreground truncate">
{project.name}
</p>
<p className="text-xs text-muted-foreground/70 truncate mt-0.5">
{project.path}
</p>
<p className="font-medium text-foreground truncate">{project.name}</p>
<p className="text-xs text-muted-foreground/70 truncate mt-0.5">{project.path}</p>
</div>
</div>
<Button
@@ -54,10 +47,10 @@ export function DangerZoneSection({
onClick={onDeleteClick}
data-testid="delete-project-button"
className={cn(
"shrink-0",
"shadow-md shadow-destructive/20 hover:shadow-lg hover:shadow-destructive/25",
"transition-all duration-200 ease-out",
"hover:scale-[1.02] active:scale-[0.98]"
'shrink-0',
'shadow-md shadow-destructive/20 hover:shadow-lg hover:shadow-destructive/25',
'transition-all duration-200 ease-out',
'hover:scale-[1.02] active:scale-[0.98]'
)}
>
<Trash2 className="w-4 h-4 mr-2" />

View File

@@ -1,18 +1,27 @@
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import {
FlaskConical, Settings2, TestTube, GitBranch, AlertCircle,
Zap, ClipboardList, FileText, ScrollText, ShieldCheck, User
} from "lucide-react";
import { cn } from "@/lib/utils";
FlaskConical,
Settings2,
TestTube,
GitBranch,
AlertCircle,
Zap,
ClipboardList,
FileText,
ScrollText,
ShieldCheck,
User,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import type { AIProfile } from "@/store/app-store";
} from '@/components/ui/select';
import type { AIProfile } from '@/store/app-store';
type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
@@ -58,10 +67,10 @@ export function FeatureDefaultsSection({
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-border/50",
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-black/5"
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
@@ -69,9 +78,7 @@ export function FeatureDefaultsSection({
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-brand-500/20 to-brand-600/10 flex items-center justify-center border border-brand-500/20">
<FlaskConical className="w-5 h-5 text-brand-500" />
</div>
<h2 className="text-lg font-semibold text-foreground tracking-tight">
Feature Defaults
</h2>
<h2 className="text-lg font-semibold text-foreground tracking-tight">Feature Defaults</h2>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Configure default settings for new features.
@@ -80,13 +87,18 @@ export function FeatureDefaultsSection({
<div className="p-6 space-y-5">
{/* Planning Mode Default */}
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
<div className={cn(
"w-10 h-10 mt-0.5 rounded-xl flex items-center justify-center shrink-0",
defaultPlanningMode === 'skip' ? "bg-emerald-500/10" :
defaultPlanningMode === 'lite' ? "bg-blue-500/10" :
defaultPlanningMode === 'spec' ? "bg-purple-500/10" :
"bg-amber-500/10"
)}>
<div
className={cn(
'w-10 h-10 mt-0.5 rounded-xl flex items-center justify-center shrink-0',
defaultPlanningMode === 'skip'
? 'bg-emerald-500/10'
: defaultPlanningMode === 'lite'
? 'bg-blue-500/10'
: defaultPlanningMode === 'spec'
? 'bg-purple-500/10'
: 'bg-amber-500/10'
)}
>
{defaultPlanningMode === 'skip' && <Zap className="w-5 h-5 text-emerald-500" />}
{defaultPlanningMode === 'lite' && <ClipboardList className="w-5 h-5 text-blue-500" />}
{defaultPlanningMode === 'spec' && <FileText className="w-5 h-5 text-purple-500" />}
@@ -94,17 +106,12 @@ export function FeatureDefaultsSection({
</div>
<div className="flex-1 space-y-2">
<div className="flex items-center justify-between">
<Label className="text-foreground font-medium">
Default Planning Mode
</Label>
<Label className="text-foreground font-medium">Default Planning Mode</Label>
<Select
value={defaultPlanningMode}
onValueChange={(v: string) => onDefaultPlanningModeChange(v as PlanningMode)}
>
<SelectTrigger
className="w-[160px] h-8"
data-testid="default-planning-mode-select"
>
<SelectTrigger className="w-[160px] h-8" data-testid="default-planning-mode-select">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -137,10 +144,14 @@ export function FeatureDefaultsSection({
</Select>
</div>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
{defaultPlanningMode === 'skip' && "Jump straight to implementation without upfront planning."}
{defaultPlanningMode === 'lite' && "Create a quick planning outline with tasks before building."}
{defaultPlanningMode === 'spec' && "Generate a specification with acceptance criteria for approval."}
{defaultPlanningMode === 'full' && "Create comprehensive spec with phased implementation plan."}
{defaultPlanningMode === 'skip' &&
'Jump straight to implementation without upfront planning.'}
{defaultPlanningMode === 'lite' &&
'Create a quick planning outline with tasks before building.'}
{defaultPlanningMode === 'spec' &&
'Generate a specification with acceptance criteria for approval.'}
{defaultPlanningMode === 'full' &&
'Create comprehensive spec with phased implementation plan.'}
</p>
</div>
</div>
@@ -152,9 +163,7 @@ export function FeatureDefaultsSection({
<Checkbox
id="default-require-plan-approval"
checked={defaultRequirePlanApproval}
onCheckedChange={(checked) =>
onDefaultRequirePlanApprovalChange(checked === true)
}
onCheckedChange={(checked) => onDefaultRequirePlanApprovalChange(checked === true)}
className="mt-1"
data-testid="default-require-plan-approval-checkbox"
/>
@@ -187,17 +196,12 @@ export function FeatureDefaultsSection({
</div>
<div className="flex-1 space-y-2">
<div className="flex items-center justify-between">
<Label className="text-foreground font-medium">
Default AI Profile
</Label>
<Label className="text-foreground font-medium">Default AI Profile</Label>
<Select
value={defaultAIProfileId ?? "none"}
onValueChange={(v: string) => onDefaultAIProfileIdChange(v === "none" ? null : v)}
value={defaultAIProfileId ?? 'none'}
onValueChange={(v: string) => onDefaultAIProfileIdChange(v === 'none' ? null : v)}
>
<SelectTrigger
className="w-[180px] h-8"
data-testid="default-ai-profile-select"
>
<SelectTrigger className="w-[180px] h-8" data-testid="default-ai-profile-select">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -215,7 +219,7 @@ export function FeatureDefaultsSection({
<p className="text-xs text-muted-foreground/80 leading-relaxed">
{selectedProfile
? `New features will use the "${selectedProfile.name}" profile (${selectedProfile.model}, ${selectedProfile.thinkingLevel} thinking).`
: "Pre-select an AI profile when creating new features. Choose \"None\" to pick manually each time."}
: 'Pre-select an AI profile when creating new features. Choose "None" to pick manually each time.'}
</p>
</div>
</div>
@@ -228,9 +232,7 @@ export function FeatureDefaultsSection({
<Checkbox
id="show-profiles-only"
checked={showProfilesOnly}
onCheckedChange={(checked) =>
onShowProfilesOnlyChange(checked === true)
}
onCheckedChange={(checked) => onShowProfilesOnlyChange(checked === true)}
className="mt-1"
data-testid="show-profiles-only-checkbox"
/>
@@ -243,9 +245,8 @@ export function FeatureDefaultsSection({
Show profiles only by default
</Label>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
When enabled, the Add Feature dialog will show only AI profiles
and hide advanced model tweaking options. This creates a cleaner, less
overwhelming UI.
When enabled, the Add Feature dialog will show only AI profiles and hide advanced
model tweaking options. This creates a cleaner, less overwhelming UI.
</p>
</div>
</div>
@@ -258,9 +259,7 @@ export function FeatureDefaultsSection({
<Checkbox
id="default-skip-tests"
checked={!defaultSkipTests}
onCheckedChange={(checked) =>
onDefaultSkipTestsChange(checked !== true)
}
onCheckedChange={(checked) => onDefaultSkipTestsChange(checked !== true)}
className="mt-1"
data-testid="default-skip-tests-checkbox"
/>
@@ -273,8 +272,8 @@ export function FeatureDefaultsSection({
Enable automated testing by default
</Label>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
When enabled, new features will use TDD with automated tests. When disabled, features will
require manual verification.
When enabled, new features will use TDD with automated tests. When disabled, features
will require manual verification.
</p>
</div>
</div>
@@ -287,9 +286,7 @@ export function FeatureDefaultsSection({
<Checkbox
id="enable-dependency-blocking"
checked={enableDependencyBlocking}
onCheckedChange={(checked) =>
onEnableDependencyBlockingChange(checked === true)
}
onCheckedChange={(checked) => onEnableDependencyBlockingChange(checked === true)}
className="mt-1"
data-testid="enable-dependency-blocking-checkbox"
/>
@@ -302,9 +299,9 @@ export function FeatureDefaultsSection({
Enable Dependency Blocking
</Label>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
When enabled, features with incomplete dependencies will show blocked badges
and warnings. Auto mode and backlog ordering always respect dependencies
regardless of this setting.
When enabled, features with incomplete dependencies will show blocked badges and
warnings. Auto mode and backlog ordering always respect dependencies regardless of
this setting.
</p>
</div>
</div>
@@ -317,9 +314,7 @@ export function FeatureDefaultsSection({
<Checkbox
id="use-worktrees"
checked={useWorktrees}
onCheckedChange={(checked) =>
onUseWorktreesChange(checked === true)
}
onCheckedChange={(checked) => onUseWorktreesChange(checked === true)}
className="mt-1"
data-testid="use-worktrees-checkbox"
/>
@@ -335,8 +330,8 @@ export function FeatureDefaultsSection({
</span>
</Label>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
Creates isolated git branches for each feature. When disabled,
agents work directly in the main project directory.
Creates isolated git branches for each feature. When disabled, agents work directly in
the main project directory.
</p>
</div>
</div>

View File

@@ -1,2 +1,2 @@
export { useCliStatus } from "./use-cli-status";
export { useSettingsView, type SettingsViewId } from "./use-settings-view";
export { useCliStatus } from './use-cli-status';
export { useSettingsView, type SettingsViewId } from './use-settings-view';

View File

@@ -1,6 +1,6 @@
import { useState, useEffect, useCallback } from "react";
import { useSetupStore } from "@/store/setup-store";
import { getElectronAPI } from "@/lib/electron";
import { useState, useEffect, useCallback } from 'react';
import { useSetupStore } from '@/store/setup-store';
import { getElectronAPI } from '@/lib/electron';
interface CliStatusResult {
success: boolean;
@@ -25,8 +25,7 @@ interface CliStatusResult {
export function useCliStatus() {
const { setClaudeAuthStatus } = useSetupStore();
const [claudeCliStatus, setClaudeCliStatus] =
useState<CliStatusResult | null>(null);
const [claudeCliStatus, setClaudeCliStatus] = useState<CliStatusResult | null>(null);
const [isCheckingClaudeCli, setIsCheckingClaudeCli] = useState(false);
@@ -41,7 +40,7 @@ export function useCliStatus() {
const status = await api.checkClaudeCli();
setClaudeCliStatus(status);
} catch (error) {
console.error("Failed to check Claude CLI status:", error);
console.error('Failed to check Claude CLI status:', error);
}
}
@@ -57,16 +56,27 @@ export function useCliStatus() {
};
// Map server method names to client method types
// Server returns: oauth_token_env, oauth_token, api_key_env, api_key, credentials_file, cli_authenticated, none
const validMethods = ["oauth_token_env", "oauth_token", "api_key", "api_key_env", "credentials_file", "cli_authenticated", "none"] as const;
type AuthMethod = typeof validMethods[number];
const validMethods = [
'oauth_token_env',
'oauth_token',
'api_key',
'api_key_env',
'credentials_file',
'cli_authenticated',
'none',
] as const;
type AuthMethod = (typeof validMethods)[number];
const method: AuthMethod = validMethods.includes(auth.method as AuthMethod)
? (auth.method as AuthMethod)
: auth.authenticated ? "api_key" : "none"; // Default authenticated to api_key, not none
: auth.authenticated
? 'api_key'
: 'none'; // Default authenticated to api_key, not none
const authStatus = {
authenticated: auth.authenticated,
method,
hasCredentialsFile: auth.hasCredentialsFile ?? false,
oauthTokenValid: auth.oauthTokenValid || auth.hasStoredOAuthToken || auth.hasEnvOAuthToken,
oauthTokenValid:
auth.oauthTokenValid || auth.hasStoredOAuthToken || auth.hasEnvOAuthToken,
apiKeyValid: auth.apiKeyValid || auth.hasStoredApiKey || auth.hasEnvApiKey,
hasEnvOAuthToken: auth.hasEnvOAuthToken,
hasEnvApiKey: auth.hasEnvApiKey,
@@ -74,7 +84,7 @@ export function useCliStatus() {
setClaudeAuthStatus(authStatus);
}
} catch (error) {
console.error("Failed to check Claude auth status:", error);
console.error('Failed to check Claude auth status:', error);
}
}
};
@@ -92,7 +102,7 @@ export function useCliStatus() {
setClaudeCliStatus(status);
}
} catch (error) {
console.error("Failed to refresh Claude CLI status:", error);
console.error('Failed to refresh Claude CLI status:', error);
} finally {
setIsCheckingClaudeCli(false);
}

View File

@@ -1,23 +1,21 @@
import { useState, useCallback } from "react";
import { useState, useCallback } from 'react';
export type SettingsViewId =
| "api-keys"
| "claude"
| "ai-enhancement"
| "appearance"
| "terminal"
| "keyboard"
| "audio"
| "defaults"
| "danger";
| 'api-keys'
| 'claude'
| 'ai-enhancement'
| 'appearance'
| 'terminal'
| 'keyboard'
| 'audio'
| 'defaults'
| 'danger';
interface UseSettingsViewOptions {
initialView?: SettingsViewId;
}
export function useSettingsView({
initialView = "api-keys",
}: UseSettingsViewOptions = {}) {
export function useSettingsView({ initialView = 'api-keys' }: UseSettingsViewOptions = {}) {
const [activeView, setActiveView] = useState<SettingsViewId>(initialView);
const navigateTo = useCallback((viewId: SettingsViewId) => {

View File

@@ -1,21 +1,19 @@
import { Button } from "@/components/ui/button";
import { Settings2, Keyboard } from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from '@/components/ui/button';
import { Settings2, Keyboard } from 'lucide-react';
import { cn } from '@/lib/utils';
interface KeyboardShortcutsSectionProps {
onOpenKeyboardMap: () => void;
}
export function KeyboardShortcutsSection({
onOpenKeyboardMap,
}: KeyboardShortcutsSectionProps) {
export function KeyboardShortcutsSection({ onOpenKeyboardMap }: KeyboardShortcutsSectionProps) {
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-border/50",
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-black/5"
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
@@ -28,8 +26,7 @@ export function KeyboardShortcutsSection({
</h2>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Customize keyboard shortcuts for navigation and actions using the
visual keyboard map.
Customize keyboard shortcuts for navigation and actions using the visual keyboard map.
</p>
</div>
<div className="p-6">
@@ -42,12 +39,10 @@ export function KeyboardShortcutsSection({
<div className="absolute inset-0 bg-brand-500/10 blur-2xl rounded-full -z-10" />
</div>
<div className="space-y-2 max-w-md">
<h3 className="text-lg font-semibold text-foreground">
Use the Visual Keyboard Map
</h3>
<h3 className="text-lg font-semibold text-foreground">Use the Visual Keyboard Map</h3>
<p className="text-sm text-muted-foreground/80">
Click the button below to customize your keyboard shortcuts. The visual
interface shows all available keys and lets you easily edit shortcuts.
Click the button below to customize your keyboard shortcuts. The visual interface
shows all available keys and lets you easily edit shortcuts.
</p>
</div>
<Button
@@ -55,13 +50,13 @@ export function KeyboardShortcutsSection({
size="lg"
onClick={onOpenKeyboardMap}
className={cn(
"gap-2.5 mt-2 h-11 px-6",
"bg-gradient-to-r from-brand-500 to-brand-600",
"hover:from-brand-600 hover:to-brand-600",
"text-white font-medium border-0",
"shadow-md shadow-brand-500/20 hover:shadow-lg hover:shadow-brand-500/25",
"transition-all duration-200 ease-out",
"hover:scale-[1.02] active:scale-[0.98]"
'gap-2.5 mt-2 h-11 px-6',
'bg-gradient-to-r from-brand-500 to-brand-600',
'hover:from-brand-600 hover:to-brand-600',
'text-white font-medium border-0',
'shadow-md shadow-brand-500/20 hover:shadow-lg hover:shadow-brand-500/25',
'transition-all duration-200 ease-out',
'hover:scale-[1.02] active:scale-[0.98]'
)}
>
<Keyboard className="w-5 h-5" />

View File

@@ -1,12 +1,12 @@
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Slider } from "@/components/ui/slider";
import { Input } from "@/components/ui/input";
import { SquareTerminal } from "lucide-react";
import { cn } from "@/lib/utils";
import { useAppStore } from "@/store/app-store";
import { toast } from "sonner";
import { TERMINAL_FONT_OPTIONS } from "@/config/terminal-themes";
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Slider } from '@/components/ui/slider';
import { Input } from '@/components/ui/input';
import { SquareTerminal } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useAppStore } from '@/store/app-store';
import { toast } from 'sonner';
import { TERMINAL_FONT_OPTIONS } from '@/config/terminal-themes';
export function TerminalSection() {
const {
@@ -31,10 +31,10 @@ export function TerminalSection() {
return (
<div
className={cn(
"rounded-2xl overflow-hidden",
"border border-border/50",
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
"shadow-sm shadow-black/5"
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
@@ -45,7 +45,8 @@ export function TerminalSection() {
<h2 className="text-lg font-semibold text-foreground tracking-tight">Terminal</h2>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Customize terminal appearance and behavior. Theme follows your app theme in Appearance settings.
Customize terminal appearance and behavior. Theme follows your app theme in Appearance
settings.
</p>
</div>
<div className="p-6 space-y-6">
@@ -56,15 +57,15 @@ export function TerminalSection() {
value={fontFamily}
onChange={(e) => {
setTerminalFontFamily(e.target.value);
toast.info("Font family changed", {
description: "Restart terminal for changes to take effect",
toast.info('Font family changed', {
description: 'Restart terminal for changes to take effect',
});
}}
className={cn(
"w-full px-3 py-2 rounded-lg",
"bg-accent/30 border border-border/50",
"text-foreground text-sm",
"focus:outline-none focus:ring-2 focus:ring-green-500/30"
'w-full px-3 py-2 rounded-lg',
'bg-accent/30 border border-border/50',
'text-foreground text-sm',
'focus:outline-none focus:ring-2 focus:ring-green-500/30'
)}
>
{TERMINAL_FONT_OPTIONS.map((font) => (
@@ -106,8 +107,8 @@ export function TerminalSection() {
setTerminalLineHeight(value);
}}
onValueCommit={() => {
toast.info("Line height changed", {
description: "Restart terminal for changes to take effect",
toast.info('Line height changed', {
description: 'Restart terminal for changes to take effect',
});
}}
className="flex-1"
@@ -129,8 +130,8 @@ export function TerminalSection() {
step={1000}
onValueChange={([value]) => setTerminalScrollbackLines(value)}
onValueCommit={() => {
toast.info("Scrollback changed", {
description: "Restart terminal for changes to take effect",
toast.info('Scrollback changed', {
description: 'Restart terminal for changes to take effect',
});
}}
className="flex-1"
@@ -163,9 +164,12 @@ export function TerminalSection() {
checked={screenReaderMode}
onCheckedChange={(checked) => {
setTerminalScreenReaderMode(checked);
toast.success(checked ? "Screen reader mode enabled" : "Screen reader mode disabled", {
description: "Restart terminal for changes to take effect",
});
toast.success(
checked ? 'Screen reader mode enabled' : 'Screen reader mode disabled',
{
description: 'Restart terminal for changes to take effect',
}
);
}}
/>
</div>