mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +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