mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
refactor(settings): remove empty hooks directory
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Palette } from "lucide-react";
|
||||||
|
import { themeOptions } from "./shared/theme-options";
|
||||||
|
import type { Theme, Project } from "./types";
|
||||||
|
|
||||||
|
interface AppearanceSectionProps {
|
||||||
|
effectiveTheme: Theme;
|
||||||
|
currentProject: Project | null;
|
||||||
|
onThemeChange: (theme: Theme) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AppearanceSection({
|
||||||
|
effectiveTheme,
|
||||||
|
currentProject,
|
||||||
|
onThemeChange,
|
||||||
|
}: AppearanceSectionProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="appearance"
|
||||||
|
className="rounded-xl border border-border bg-card backdrop-blur-md overflow-hidden scroll-mt-6"
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Palette className="w-5 h-5 text-brand-500" />
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">Appearance</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Customize the look and feel of your application.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<Label className="text-foreground">
|
||||||
|
Theme{" "}
|
||||||
|
{currentProject ? `(for ${currentProject.name})` : "(Global)"}
|
||||||
|
</Label>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
|
{themeOptions.map(({ value, label, Icon, testId }) => {
|
||||||
|
const isActive = effectiveTheme === value;
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={value}
|
||||||
|
variant={isActive ? "secondary" : "outline"}
|
||||||
|
onClick={() => onThemeChange(value)}
|
||||||
|
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||||
|
isActive ? "border-brand-500 ring-1 ring-brand-500/50" : ""
|
||||||
|
}`}
|
||||||
|
data-testid={testId}
|
||||||
|
>
|
||||||
|
<Icon className="w-4 h-4" />
|
||||||
|
<span className="font-medium text-sm">{label}</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
304
app/src/components/views/settings-view/cli-status-section.tsx
Normal file
304
app/src/components/views/settings-view/cli-status-section.tsx
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Terminal,
|
||||||
|
CheckCircle2,
|
||||||
|
AlertCircle,
|
||||||
|
RefreshCw,
|
||||||
|
Atom,
|
||||||
|
} from "lucide-react";
|
||||||
|
import type { CliStatus } from "./types";
|
||||||
|
|
||||||
|
interface CliStatusProps {
|
||||||
|
status: CliStatus | null;
|
||||||
|
isChecking: boolean;
|
||||||
|
onRefresh: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ClaudeCliStatus({
|
||||||
|
status,
|
||||||
|
isChecking,
|
||||||
|
onRefresh,
|
||||||
|
}: CliStatusProps) {
|
||||||
|
if (!status) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="claude"
|
||||||
|
className="rounded-xl border border-border bg-card backdrop-blur-md overflow-hidden scroll-mt-6"
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Terminal className="w-5 h-5 text-brand-500" />
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
|
Claude Code CLI
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={onRefresh}
|
||||||
|
disabled={isChecking}
|
||||||
|
data-testid="refresh-claude-cli"
|
||||||
|
title="Refresh Claude CLI detection"
|
||||||
|
>
|
||||||
|
<RefreshCw
|
||||||
|
className={`w-4 h-4 ${isChecking ? "animate-spin" : ""}`}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
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" ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center gap-2 p-3 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||||
|
<CheckCircle2 className="w-5 h-5 text-green-500 shrink-0" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-green-400">
|
||||||
|
Claude Code CLI Installed
|
||||||
|
</p>
|
||||||
|
<div className="text-xs text-green-400/80 mt-1 space-y-1">
|
||||||
|
{status.method && (
|
||||||
|
<p>
|
||||||
|
Method: <span className="font-mono">{status.method}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{status.version && (
|
||||||
|
<p>
|
||||||
|
Version:{" "}
|
||||||
|
<span className="font-mono">{status.version}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{status.path && (
|
||||||
|
<p className="truncate" title={status.path}>
|
||||||
|
Path:{" "}
|
||||||
|
<span className="font-mono text-[10px]">
|
||||||
|
{status.path}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{status.recommendation && (
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{status.recommendation}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start gap-3 p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20">
|
||||||
|
<AlertCircle className="w-5 h-5 text-yellow-500 mt-0.5 shrink-0" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-yellow-400">
|
||||||
|
Claude Code CLI Not Detected
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-yellow-400/80 mt-1">
|
||||||
|
{status.recommendation ||
|
||||||
|
"Consider installing Claude Code CLI for optimal performance with ultrathink."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{status.installCommands && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-xs font-medium text-foreground-secondary">
|
||||||
|
Installation Commands:
|
||||||
|
</p>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{status.installCommands.npm && (
|
||||||
|
<div className="p-2 rounded bg-background border border-border-glass">
|
||||||
|
<p className="text-xs text-muted-foreground mb-1">npm:</p>
|
||||||
|
<code className="text-xs text-foreground-secondary font-mono break-all">
|
||||||
|
{status.installCommands.npm}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{status.installCommands.macos && (
|
||||||
|
<div className="p-2 rounded bg-background border border-border-glass">
|
||||||
|
<p className="text-xs text-muted-foreground mb-1">
|
||||||
|
macOS/Linux:
|
||||||
|
</p>
|
||||||
|
<code className="text-xs text-foreground-secondary font-mono break-all">
|
||||||
|
{status.installCommands.macos}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{status.installCommands.windows && (
|
||||||
|
<div className="p-2 rounded bg-background border border-border-glass">
|
||||||
|
<p className="text-xs text-muted-foreground mb-1">
|
||||||
|
Windows (PowerShell):
|
||||||
|
</p>
|
||||||
|
<code className="text-xs text-foreground-secondary font-mono break-all">
|
||||||
|
{status.installCommands.windows}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CodexCliStatus({
|
||||||
|
status,
|
||||||
|
isChecking,
|
||||||
|
onRefresh,
|
||||||
|
}: CliStatusProps) {
|
||||||
|
if (!status) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="codex"
|
||||||
|
className="rounded-xl border border-border bg-card backdrop-blur-md overflow-hidden scroll-mt-6"
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Terminal className="w-5 h-5 text-green-500" />
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
|
OpenAI Codex CLI
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={onRefresh}
|
||||||
|
disabled={isChecking}
|
||||||
|
data-testid="refresh-codex-cli"
|
||||||
|
title="Refresh Codex CLI detection"
|
||||||
|
>
|
||||||
|
<RefreshCw
|
||||||
|
className={`w-4 h-4 ${isChecking ? "animate-spin" : ""}`}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Codex CLI enables GPT-5.1 Codex models for autonomous coding tasks.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
{status.success && status.status === "installed" ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center gap-2 p-3 rounded-lg bg-green-500/10 border border-green-500/20">
|
||||||
|
<CheckCircle2 className="w-5 h-5 text-green-500 shrink-0" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-green-400">
|
||||||
|
Codex CLI Installed
|
||||||
|
</p>
|
||||||
|
<div className="text-xs text-green-400/80 mt-1 space-y-1">
|
||||||
|
{status.method && (
|
||||||
|
<p>
|
||||||
|
Method: <span className="font-mono">{status.method}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{status.version && (
|
||||||
|
<p>
|
||||||
|
Version:{" "}
|
||||||
|
<span className="font-mono">{status.version}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{status.path && (
|
||||||
|
<p className="truncate" title={status.path}>
|
||||||
|
Path:{" "}
|
||||||
|
<span className="font-mono text-[10px]">
|
||||||
|
{status.path}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{status.recommendation && (
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{status.recommendation}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : status.status === "api_key_only" ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start gap-3 p-3 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
|
<AlertCircle className="w-5 h-5 text-blue-500 mt-0.5 shrink-0" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-blue-400">
|
||||||
|
API Key Detected - CLI Not Installed
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-blue-400/80 mt-1">
|
||||||
|
{status.recommendation ||
|
||||||
|
"OPENAI_API_KEY found but Codex CLI not installed. Install the CLI for full agentic capabilities."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{status.installCommands && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-xs font-medium text-foreground-secondary">
|
||||||
|
Installation Commands:
|
||||||
|
</p>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{status.installCommands.npm && (
|
||||||
|
<div className="p-2 rounded bg-background border border-border-glass">
|
||||||
|
<p className="text-xs text-muted-foreground mb-1">npm:</p>
|
||||||
|
<code className="text-xs text-foreground-secondary font-mono break-all">
|
||||||
|
{status.installCommands.npm}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start gap-3 p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20">
|
||||||
|
<AlertCircle className="w-5 h-5 text-yellow-500 mt-0.5 shrink-0" />
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-yellow-400">
|
||||||
|
Codex CLI Not Detected
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-yellow-400/80 mt-1">
|
||||||
|
{status.recommendation ||
|
||||||
|
"Install OpenAI Codex CLI to use GPT-5.1 Codex models for autonomous coding."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{status.installCommands && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-xs font-medium text-foreground-secondary">
|
||||||
|
Installation Commands:
|
||||||
|
</p>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{status.installCommands.npm && (
|
||||||
|
<div className="p-2 rounded bg-background border border-border-glass">
|
||||||
|
<p className="text-xs text-muted-foreground mb-1">npm:</p>
|
||||||
|
<code className="text-xs text-foreground-secondary font-mono break-all">
|
||||||
|
{status.installCommands.npm}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{status.installCommands.macos && (
|
||||||
|
<div className="p-2 rounded bg-background border border-border-glass">
|
||||||
|
<p className="text-xs text-muted-foreground mb-1">
|
||||||
|
macOS (Homebrew):
|
||||||
|
</p>
|
||||||
|
<code className="text-xs text-foreground-secondary font-mono break-all">
|
||||||
|
{status.installCommands.macos}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Trash2, Folder } from "lucide-react";
|
||||||
|
import type { Project } from "./types";
|
||||||
|
|
||||||
|
interface DangerZoneSectionProps {
|
||||||
|
project: Project | null;
|
||||||
|
onDeleteClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DangerZoneSection({
|
||||||
|
project,
|
||||||
|
onDeleteClick,
|
||||||
|
}: DangerZoneSectionProps) {
|
||||||
|
if (!project) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="danger"
|
||||||
|
className="rounded-xl border border-destructive/30 bg-card backdrop-blur-md overflow-hidden scroll-mt-6"
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-destructive/30">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Trash2 className="w-5 h-5 text-destructive" />
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">Danger Zone</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Permanently remove this project from Automaker.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-sidebar-accent/20 border border-sidebar-border flex items-center justify-center shrink-0">
|
||||||
|
<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 truncate">
|
||||||
|
{project.path}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={onDeleteClick}
|
||||||
|
data-testid="delete-project-button"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
|
Delete Project
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { FlaskConical, Settings2, TestTube, GitBranch } from "lucide-react";
|
||||||
|
|
||||||
|
interface FeatureDefaultsSectionProps {
|
||||||
|
showProfilesOnly: boolean;
|
||||||
|
defaultSkipTests: boolean;
|
||||||
|
useWorktrees: boolean;
|
||||||
|
onShowProfilesOnlyChange: (value: boolean) => void;
|
||||||
|
onDefaultSkipTestsChange: (value: boolean) => void;
|
||||||
|
onUseWorktreesChange: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FeatureDefaultsSection({
|
||||||
|
showProfilesOnly,
|
||||||
|
defaultSkipTests,
|
||||||
|
useWorktrees,
|
||||||
|
onShowProfilesOnlyChange,
|
||||||
|
onDefaultSkipTestsChange,
|
||||||
|
onUseWorktreesChange,
|
||||||
|
}: FeatureDefaultsSectionProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="defaults"
|
||||||
|
className="rounded-xl border border-border bg-card backdrop-blur-md overflow-hidden scroll-mt-6"
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<FlaskConical className="w-5 h-5 text-brand-500" />
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
|
Feature Defaults
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Configure default settings for new features.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
{/* Profiles Only Setting */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Checkbox
|
||||||
|
id="show-profiles-only"
|
||||||
|
checked={showProfilesOnly}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
onShowProfilesOnlyChange(checked === true)
|
||||||
|
}
|
||||||
|
className="mt-0.5"
|
||||||
|
data-testid="show-profiles-only-checkbox"
|
||||||
|
/>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label
|
||||||
|
htmlFor="show-profiles-only"
|
||||||
|
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Settings2 className="w-4 h-4 text-brand-500" />
|
||||||
|
Show profiles only by default
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
When enabled, the Add Feature dialog will show only AI profiles
|
||||||
|
and hide advanced model tweaking options (Claude SDK, thinking
|
||||||
|
levels, and OpenAI Codex CLI). This creates a cleaner, less
|
||||||
|
overwhelming UI. You can always disable this to access advanced
|
||||||
|
settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
|
<div className="border-t border-border" />
|
||||||
|
|
||||||
|
{/* Skip Tests Setting */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Checkbox
|
||||||
|
id="default-skip-tests"
|
||||||
|
checked={defaultSkipTests}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
onDefaultSkipTestsChange(checked === true)
|
||||||
|
}
|
||||||
|
className="mt-0.5"
|
||||||
|
data-testid="default-skip-tests-checkbox"
|
||||||
|
/>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label
|
||||||
|
htmlFor="default-skip-tests"
|
||||||
|
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<TestTube className="w-4 h-4 text-brand-500" />
|
||||||
|
Skip automated testing by default
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
When enabled, new features will default to manual verification
|
||||||
|
instead of TDD (test-driven development). You can still override
|
||||||
|
this for individual features.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Worktree Isolation Setting */}
|
||||||
|
<div className="space-y-3 pt-2 border-t border-border">
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Checkbox
|
||||||
|
id="use-worktrees"
|
||||||
|
checked={useWorktrees}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
onUseWorktreesChange(checked === true)
|
||||||
|
}
|
||||||
|
className="mt-0.5"
|
||||||
|
data-testid="use-worktrees-checkbox"
|
||||||
|
/>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label
|
||||||
|
htmlFor="use-worktrees"
|
||||||
|
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<GitBranch className="w-4 h-4 text-brand-500" />
|
||||||
|
Enable Git Worktree Isolation (experimental)
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Creates isolated git branches for each feature. When disabled,
|
||||||
|
agents work directly in the main project directory. This feature
|
||||||
|
is experimental and may require additional setup like branch
|
||||||
|
selection and merge configuration.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { LayoutGrid, Minimize2, Square, Maximize2 } from "lucide-react";
|
||||||
|
import type { KanbanDetailLevel } from "./types";
|
||||||
|
|
||||||
|
interface KanbanDisplaySectionProps {
|
||||||
|
detailLevel: KanbanDetailLevel;
|
||||||
|
onChange: (level: KanbanDetailLevel) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function KanbanDisplaySection({
|
||||||
|
detailLevel,
|
||||||
|
onChange,
|
||||||
|
}: KanbanDisplaySectionProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="kanban"
|
||||||
|
className="rounded-xl border border-border bg-card backdrop-blur-md overflow-hidden scroll-mt-6"
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<LayoutGrid className="w-5 h-5 text-brand-500" />
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
|
Kanban Card Display
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Control how much information is displayed on Kanban cards.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<Label className="text-foreground">Detail Level</Label>
|
||||||
|
<div className="grid grid-cols-3 gap-3">
|
||||||
|
<Button
|
||||||
|
variant={detailLevel === "minimal" ? "secondary" : "outline"}
|
||||||
|
onClick={() => onChange("minimal")}
|
||||||
|
className={`flex flex-col items-center justify-center gap-2 px-4 py-4 h-auto ${
|
||||||
|
detailLevel === "minimal"
|
||||||
|
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
data-testid="kanban-detail-minimal"
|
||||||
|
>
|
||||||
|
<Minimize2 className="w-5 h-5" />
|
||||||
|
<span className="font-medium text-sm">Minimal</span>
|
||||||
|
<span className="text-xs text-muted-foreground text-center">
|
||||||
|
Title & category only
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={detailLevel === "standard" ? "secondary" : "outline"}
|
||||||
|
onClick={() => onChange("standard")}
|
||||||
|
className={`flex flex-col items-center justify-center gap-2 px-4 py-4 h-auto ${
|
||||||
|
detailLevel === "standard"
|
||||||
|
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
data-testid="kanban-detail-standard"
|
||||||
|
>
|
||||||
|
<Square className="w-5 h-5" />
|
||||||
|
<span className="font-medium text-sm">Standard</span>
|
||||||
|
<span className="text-xs text-muted-foreground text-center">
|
||||||
|
Steps & progress
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={detailLevel === "detailed" ? "secondary" : "outline"}
|
||||||
|
onClick={() => onChange("detailed")}
|
||||||
|
className={`flex flex-col items-center justify-center gap-2 px-4 py-4 h-auto ${
|
||||||
|
detailLevel === "detailed"
|
||||||
|
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
data-testid="kanban-detail-detailed"
|
||||||
|
>
|
||||||
|
<Maximize2 className="w-5 h-5" />
|
||||||
|
<span className="font-medium text-sm">Detailed</span>
|
||||||
|
<span className="text-xs text-muted-foreground text-center">
|
||||||
|
Model, tools & tasks
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
<strong>Minimal:</strong> Shows only title and category
|
||||||
|
<br />
|
||||||
|
<strong>Standard:</strong> Adds steps preview and progress bar
|
||||||
|
<br />
|
||||||
|
<strong>Detailed:</strong> Shows all info including model, tool
|
||||||
|
calls, task list, and summaries
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Settings2, Keyboard } from "lucide-react";
|
||||||
|
|
||||||
|
interface KeyboardShortcutsSectionProps {
|
||||||
|
onOpenKeyboardMap: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function KeyboardShortcutsSection({
|
||||||
|
onOpenKeyboardMap,
|
||||||
|
}: KeyboardShortcutsSectionProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="keyboard"
|
||||||
|
className="rounded-xl border border-border bg-card backdrop-blur-md overflow-hidden scroll-mt-6"
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Settings2 className="w-5 h-5 text-brand-500" />
|
||||||
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
|
Keyboard Shortcuts
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Customize keyboard shortcuts for navigation and actions using the
|
||||||
|
visual keyboard map.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-6">
|
||||||
|
{/* Centered message directing to keyboard map */}
|
||||||
|
<div className="flex flex-col items-center justify-center py-16 text-center space-y-4">
|
||||||
|
<div className="relative">
|
||||||
|
<Keyboard className="w-16 h-16 text-brand-500/30" />
|
||||||
|
<div className="absolute inset-0 bg-brand-500/10 blur-xl rounded-full" />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 max-w-md">
|
||||||
|
<h3 className="text-lg font-semibold text-foreground">
|
||||||
|
Use the Visual Keyboard Map
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Click the "View Keyboard Map" button above to customize
|
||||||
|
your keyboard shortcuts. The visual interface shows all available
|
||||||
|
keys and lets you easily edit shortcuts with single-modifier
|
||||||
|
restrictions.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
onClick={onOpenKeyboardMap}
|
||||||
|
className="gap-2 mt-4"
|
||||||
|
>
|
||||||
|
<Keyboard className="w-5 h-5" />
|
||||||
|
Open Keyboard Map
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
type LucideIcon,
|
||||||
|
Atom,
|
||||||
|
Cat,
|
||||||
|
Eclipse,
|
||||||
|
Flame,
|
||||||
|
Ghost,
|
||||||
|
Moon,
|
||||||
|
Radio,
|
||||||
|
Snowflake,
|
||||||
|
Sparkles,
|
||||||
|
Sun,
|
||||||
|
Terminal,
|
||||||
|
Trees,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { Theme } from "../types";
|
||||||
|
|
||||||
|
export interface ThemeOption {
|
||||||
|
value: Theme;
|
||||||
|
label: string;
|
||||||
|
Icon: LucideIcon;
|
||||||
|
testId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const themeOptions: ReadonlyArray<ThemeOption> = [
|
||||||
|
{ value: "dark", label: "Dark", Icon: Moon, testId: "dark-mode-button" },
|
||||||
|
{ value: "light", label: "Light", Icon: Sun, testId: "light-mode-button" },
|
||||||
|
{
|
||||||
|
value: "retro",
|
||||||
|
label: "Retro",
|
||||||
|
Icon: Terminal,
|
||||||
|
testId: "retro-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "dracula",
|
||||||
|
label: "Dracula",
|
||||||
|
Icon: Ghost,
|
||||||
|
testId: "dracula-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "nord",
|
||||||
|
label: "Nord",
|
||||||
|
Icon: Snowflake,
|
||||||
|
testId: "nord-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "monokai",
|
||||||
|
label: "Monokai",
|
||||||
|
Icon: Flame,
|
||||||
|
testId: "monokai-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "tokyonight",
|
||||||
|
label: "Tokyo Night",
|
||||||
|
Icon: Sparkles,
|
||||||
|
testId: "tokyonight-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "solarized",
|
||||||
|
label: "Solarized",
|
||||||
|
Icon: Eclipse,
|
||||||
|
testId: "solarized-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "gruvbox",
|
||||||
|
label: "Gruvbox",
|
||||||
|
Icon: Trees,
|
||||||
|
testId: "gruvbox-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "catppuccin",
|
||||||
|
label: "Catppuccin",
|
||||||
|
Icon: Cat,
|
||||||
|
testId: "catppuccin-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "onedark",
|
||||||
|
label: "One Dark",
|
||||||
|
Icon: Atom,
|
||||||
|
testId: "onedark-mode-button",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "synthwave",
|
||||||
|
label: "Synthwave",
|
||||||
|
Icon: Radio,
|
||||||
|
testId: "synthwave-mode-button",
|
||||||
|
},
|
||||||
|
];
|
||||||
47
app/src/components/views/settings-view/types.ts
Normal file
47
app/src/components/views/settings-view/types.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Shared TypeScript types for settings view components
|
||||||
|
|
||||||
|
export interface CliStatus {
|
||||||
|
success: boolean;
|
||||||
|
status?: string;
|
||||||
|
method?: string;
|
||||||
|
version?: string;
|
||||||
|
path?: string;
|
||||||
|
hasApiKey?: boolean;
|
||||||
|
recommendation?: string;
|
||||||
|
installCommands?: {
|
||||||
|
macos?: string;
|
||||||
|
windows?: string;
|
||||||
|
linux?: string;
|
||||||
|
npm?: string;
|
||||||
|
};
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Theme =
|
||||||
|
| "dark"
|
||||||
|
| "light"
|
||||||
|
| "retro"
|
||||||
|
| "dracula"
|
||||||
|
| "nord"
|
||||||
|
| "monokai"
|
||||||
|
| "tokyonight"
|
||||||
|
| "solarized"
|
||||||
|
| "gruvbox"
|
||||||
|
| "catppuccin"
|
||||||
|
| "onedark"
|
||||||
|
| "synthwave";
|
||||||
|
|
||||||
|
export type KanbanDetailLevel = "minimal" | "standard" | "detailed";
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
theme?: Theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiKeys {
|
||||||
|
anthropic: string;
|
||||||
|
google: string;
|
||||||
|
openai: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user