Merge pull request #118 from AutoMaker-Org/refactor/settings-view-separate-panels

refactor: convert settings page to separate view panels
This commit is contained in:
Web Dev Cody
2025-12-16 00:29:04 -05:00
committed by GitHub
13 changed files with 413 additions and 150 deletions

View File

@@ -2,21 +2,9 @@
import { useState } from "react";
import { useAppStore } from "@/store/app-store";
import { Label } from "@/components/ui/label";
import {
Key,
Palette,
Terminal,
FlaskConical,
Trash2,
Settings2,
Volume2,
VolumeX,
} from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";
import { useCliStatus } from "./settings-view/hooks/use-cli-status";
import { useScrollTracking } from "@/hooks/use-scroll-tracking";
import { useCliStatus, useSettingsView } from "./settings-view/hooks";
import { NAV_ITEMS } from "./settings-view/config/navigation";
import { SettingsHeader } from "./settings-view/components/settings-header";
import { KeyboardMapDialog } from "./settings-view/components/keyboard-map-dialog";
import { DeleteProjectDialog } from "./settings-view/components/delete-project-dialog";
@@ -24,6 +12,7 @@ import { SettingsNavigation } from "./settings-view/components/settings-navigati
import { ApiKeysSection } from "./settings-view/api-keys/api-keys-section";
import { ClaudeCliStatus } from "./settings-view/cli-status/claude-cli-status";
import { AppearanceSection } from "./settings-view/appearance/appearance-section";
import { AudioSection } from "./settings-view/audio/audio-section";
import { KeyboardShortcutsSection } from "./settings-view/keyboard-shortcuts/keyboard-shortcuts-section";
import { FeatureDefaultsSection } from "./settings-view/feature-defaults/feature-defaults-section";
import { DangerZoneSection } from "./settings-view/danger-zone/danger-zone-section";
@@ -33,17 +22,6 @@ import type {
} from "./settings-view/shared/types";
import type { Project as ElectronProject } from "@/lib/electron";
// Navigation items for the side panel
const NAV_ITEMS = [
{ id: "api-keys", label: "API Keys", icon: Key },
{ id: "claude", label: "Claude", icon: Terminal },
{ id: "appearance", label: "Appearance", icon: Palette },
{ 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 },
];
export function SettingsView() {
const {
theme,
@@ -91,23 +69,70 @@ export function SettingsView() {
};
// Use CLI status hook
const {
claudeCliStatus,
isCheckingClaudeCli,
handleRefreshClaudeCli,
} = useCliStatus();
const { claudeCliStatus, isCheckingClaudeCli, handleRefreshClaudeCli } =
useCliStatus();
// Use scroll tracking hook
const { activeSection, scrollToSection, scrollContainerRef } =
useScrollTracking({
items: NAV_ITEMS,
filterFn: (item) => item.id !== "danger" || !!currentProject,
initialSection: "api-keys",
});
// Use settings view navigation hook
const { activeView, navigateTo } = useSettingsView();
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
// Render the active section based on current view
const renderActiveSection = () => {
switch (activeView) {
case "claude":
return (
<ClaudeCliStatus
status={claudeCliStatus}
isChecking={isCheckingClaudeCli}
onRefresh={handleRefreshClaudeCli}
/>
);
case "appearance":
return (
<AppearanceSection
effectiveTheme={effectiveTheme}
currentProject={settingsProject}
onThemeChange={handleSetTheme}
/>
);
case "keyboard":
return (
<KeyboardShortcutsSection
onOpenKeyboardMap={() => setShowKeyboardMapDialog(true)}
/>
);
case "audio":
return (
<AudioSection
muteDoneSound={muteDoneSound}
onMuteDoneSoundChange={setMuteDoneSound}
/>
);
case "defaults":
return (
<FeatureDefaultsSection
showProfilesOnly={showProfilesOnly}
defaultSkipTests={defaultSkipTests}
useWorktrees={useWorktrees}
onShowProfilesOnlyChange={setShowProfilesOnly}
onDefaultSkipTestsChange={setDefaultSkipTests}
onUseWorktreesChange={setUseWorktrees}
/>
);
case "danger":
return (
<DangerZoneSection
project={settingsProject}
onDeleteClick={() => setShowDeleteDialog(true)}
/>
);
default:
return <ApiKeysSection />;
}
};
return (
<div
className="flex-1 flex flex-col overflow-hidden content-bg"
@@ -118,107 +143,17 @@ export function SettingsView() {
{/* Content Area with Sidebar */}
<div className="flex-1 flex overflow-hidden">
{/* Sticky Side Navigation */}
{/* Side Navigation - No longer scrolls, just switches views */}
<SettingsNavigation
navItems={NAV_ITEMS}
activeSection={activeSection}
activeSection={activeView}
currentProject={currentProject}
onNavigate={scrollToSection}
onNavigate={navigateTo}
/>
{/* Scrollable Content */}
<div ref={scrollContainerRef} className="flex-1 overflow-y-auto p-8">
<div className="max-w-4xl mx-auto space-y-6 pb-96">
{/* API Keys Section */}
<ApiKeysSection />
{/* Claude CLI Status Section */}
{claudeCliStatus && (
<ClaudeCliStatus
status={claudeCliStatus}
isChecking={isCheckingClaudeCli}
onRefresh={handleRefreshClaudeCli}
/>
)}
{/* Appearance Section */}
<AppearanceSection
effectiveTheme={effectiveTheme}
currentProject={settingsProject}
onThemeChange={handleSetTheme}
/>
{/* Keyboard Shortcuts Section */}
<KeyboardShortcutsSection
onOpenKeyboardMap={() => setShowKeyboardMapDialog(true)}
/>
{/* Audio Section */}
<div
id="audio"
className="rounded-2xl 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 overflow-hidden scroll-mt-6"
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
<div className="flex items-center gap-3 mb-2">
<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>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Configure audio and notification settings.
</p>
</div>
<div className="p-6 space-y-4">
{/* Mute Done Sound Setting */}
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
<Checkbox
id="mute-done-sound"
checked={muteDoneSound}
onCheckedChange={(checked) =>
setMuteDoneSound(checked === true)
}
className="mt-1"
data-testid="mute-done-sound-checkbox"
/>
<div className="space-y-1.5">
<Label
htmlFor="mute-done-sound"
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
>
<VolumeX className="w-4 h-4 text-brand-500" />
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.
</p>
</div>
</div>
</div>
</div>
{/* Feature Defaults Section */}
<FeatureDefaultsSection
showProfilesOnly={showProfilesOnly}
defaultSkipTests={defaultSkipTests}
useWorktrees={useWorktrees}
onShowProfilesOnlyChange={setShowProfilesOnly}
onDefaultSkipTestsChange={setDefaultSkipTests}
onUseWorktreesChange={setUseWorktrees}
/>
{/* Danger Zone Section - Only show when a project is selected */}
<DangerZoneSection
project={settingsProject}
onDeleteClick={() => setShowDeleteDialog(true)}
/>
</div>
{/* Content Panel - Shows only the active section */}
<div className="flex-1 overflow-y-auto p-8">
<div className="max-w-4xl mx-auto">{renderActiveSection()}</div>
</div>
</div>

View File

@@ -58,9 +58,8 @@ export function ApiKeysSection() {
return (
<div
id="api-keys"
className={cn(
"rounded-2xl overflow-hidden scroll-mt-6",
"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"

View File

@@ -18,9 +18,8 @@ export function AppearanceSection({
}: AppearanceSectionProps) {
return (
<div
id="appearance"
className={cn(
"rounded-2xl overflow-hidden scroll-mt-6",
"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"

View File

@@ -0,0 +1,64 @@
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) {
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"
)}
>
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
<div className="flex items-center gap-3 mb-2">
<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>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Configure audio and notification settings.
</p>
</div>
<div className="p-6 space-y-4">
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
<Checkbox
id="mute-done-sound"
checked={muteDoneSound}
onCheckedChange={onMuteDoneSoundChange}
className="mt-1"
data-testid="mute-done-sound-checkbox"
/>
<div className="space-y-1.5">
<Label
htmlFor="mute-done-sound"
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
>
<VolumeX className="w-4 h-4 text-brand-500" />
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.
</p>
</div>
</div>
</div>
</div>
);
}

View File

@@ -23,9 +23,8 @@ export function ClaudeCliStatus({
return (
<div
id="claude"
className={cn(
"rounded-2xl overflow-hidden scroll-mt-6",
"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"

View File

@@ -1,12 +1,13 @@
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[];
activeSection: string;
activeSection: SettingsViewId;
currentProject: Project | null;
onNavigate: (sectionId: string) => void;
onNavigate: (sectionId: SettingsViewId) => void;
}
export function SettingsNavigation({

View File

@@ -3,14 +3,15 @@ import {
Key,
Terminal,
Palette,
LayoutGrid,
Settings2,
Volume2,
FlaskConical,
Trash2,
} from "lucide-react";
import type { SettingsViewId } from "../hooks/use-settings-view";
export interface NavigationItem {
id: string;
id: SettingsViewId;
label: string;
icon: LucideIcon;
}
@@ -20,8 +21,8 @@ export const NAV_ITEMS: NavigationItem[] = [
{ id: "api-keys", label: "API Keys", icon: Key },
{ id: "claude", label: "Claude", icon: Terminal },
{ id: "appearance", label: "Appearance", icon: Palette },
{ id: "kanban", label: "Kanban Display", icon: LayoutGrid },
{ 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

@@ -16,9 +16,8 @@ export function DangerZoneSection({
return (
<div
id="danger"
className={cn(
"rounded-2xl overflow-hidden scroll-mt-6",
"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"

View File

@@ -22,9 +22,8 @@ export function FeatureDefaultsSection({
}: FeatureDefaultsSectionProps) {
return (
<div
id="defaults"
className={cn(
"rounded-2xl overflow-hidden scroll-mt-6",
"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"

View File

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

View File

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

View File

@@ -11,9 +11,8 @@ export function KeyboardShortcutsSection({
}: KeyboardShortcutsSectionProps) {
return (
<div
id="keyboard"
className={cn(
"rounded-2xl overflow-hidden scroll-mt-6",
"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"

237
docs/checkout-branch-pr.md Normal file
View File

@@ -0,0 +1,237 @@
# Git Workflow: Branch, Commit, Push, and Pull Request
This document outlines the standard workflow for creating a branch, committing changes, pushing to remote, and creating a pull request.
## Prerequisites
- Git installed and configured
- GitHub CLI (`gh`) installed (optional, but recommended for PR creation)
- Access to the repository
- Authentication configured (SSH keys or GitHub CLI authentication)
## Step-by-Step Workflow
### 1. Check Current Status
First, check what changes exist in your working directory:
```bash
git status
```
This shows:
- Modified files
- Deleted files
- Untracked files
- Current branch
### 2. Create a New Branch
Create and switch to a new branch for your changes:
```bash
git checkout -b <branch-name>
```
**Branch naming conventions:**
- `feature/` - for new features
- `fix/` or `bugfix/` - for bug fixes
- `refactor/` - for code refactoring
- `docs/` - for documentation changes
- `chore/` - for maintenance tasks
**Example:**
```bash
git checkout -b refactor/monorepo-restructure
```
### 3. Stage Changes
Stage all changes (including deletions and new files):
```bash
git add -A
```
Or stage specific files:
```bash
git add <file1> <file2>
```
### 4. Commit Changes
Create a commit with a descriptive message:
```bash
git commit -m "type: descriptive commit message"
```
**Commit message conventions:**
- Use conventional commits format: `type: description`
- Types: `feat`, `fix`, `refactor`, `docs`, `chore`, `test`, `style`
- Keep messages concise but descriptive
**Example:**
```bash
git commit -m "refactor: restructure project to monorepo with apps directory"
```
### 5. Push Branch to Remote
Push your branch to the remote repository:
```bash
git push -u origin <branch-name>
```
The `-u` flag sets up tracking so future pushes can use `git push` without specifying the branch.
**Example:**
```bash
git push -u origin refactor/monorepo-restructure
```
### 6. Create Pull Request
#### Option A: Using GitHub CLI (Recommended)
If you have GitHub CLI installed:
```bash
gh pr create --title "Your PR Title" --body "Description of changes"
```
To open in browser for review before creating:
```bash
gh pr create --title "Your PR Title" --body "Description" --web
```
#### Option B: Using GitHub Web Interface
After pushing, GitHub will provide a URL in the terminal output:
```
remote: Create a pull request for '<branch-name>' on GitHub by visiting:
remote: https://github.com/<org>/<repo>/pull/new/<branch-name>
```
Visit that URL to create the PR through the web interface.
#### Option C: Manual PR Creation
1. Go to your repository on GitHub
2. Click "Pull requests" tab
3. Click "New pull request"
4. Select your branch as the source
5. Select the target branch (usually `main` or `master`)
6. Fill in title and description
7. Click "Create pull request"
## Complete Example Workflow
```bash
# 1. Check status
git status
# 2. Create branch
git checkout -b feature/add-new-component
# 3. Make your changes (edit files, etc.)
# 4. Stage changes
git add -A
# 5. Commit
git commit -m "feat: add new user dashboard component"
# 6. Push
git push -u origin feature/add-new-component
# 7. Create PR
gh pr create --title "feat: add new user dashboard component" --body "Implements new dashboard component with user statistics and activity feed."
```
## Handling Additional Changes
If you need to make more changes after pushing:
```bash
# Make your changes
git add -A
git commit -m "fix: address review feedback"
git push
```
The PR will automatically update with the new commits.
## Troubleshooting
### Branch already exists
```bash
git checkout <existing-branch-name>
```
### Need to update from main before creating PR
```bash
git checkout main
git pull origin main
git checkout <your-branch>
git merge main
# Resolve conflicts if any
git push
```
### PR creation fails
- Ensure branch is pushed: `git push -u origin <branch-name>`
- Check GitHub CLI authentication: `gh auth status`
- Verify repository access permissions
- Try creating PR via web interface instead
## Best Practices
1. **Keep branches focused**: One branch = one feature/fix
2. **Write clear commit messages**: Help reviewers understand changes
3. **Keep PRs small**: Easier to review and merge
4. **Update before creating PR**: Merge latest `main` into your branch
5. **Add tests**: Include tests for new features
6. **Update documentation**: Keep docs in sync with code changes
7. **Request reviews**: Tag relevant team members for review
## Quick Reference Commands
```bash
# Status check
git status
# Create branch
git checkout -b <branch-name>
# Stage all changes
git add -A
# Commit
git commit -m "type: message"
# Push
git push -u origin <branch-name>
# Create PR (GitHub CLI)
gh pr create --title "Title" --body "Description"
# View PR
gh pr view
# List PRs
gh pr list
```