mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
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:
@@ -2,21 +2,9 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useAppStore } from "@/store/app-store";
|
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 { useCliStatus, useSettingsView } from "./settings-view/hooks";
|
||||||
import { useScrollTracking } from "@/hooks/use-scroll-tracking";
|
import { NAV_ITEMS } from "./settings-view/config/navigation";
|
||||||
import { SettingsHeader } from "./settings-view/components/settings-header";
|
import { SettingsHeader } from "./settings-view/components/settings-header";
|
||||||
import { KeyboardMapDialog } from "./settings-view/components/keyboard-map-dialog";
|
import { KeyboardMapDialog } from "./settings-view/components/keyboard-map-dialog";
|
||||||
import { DeleteProjectDialog } from "./settings-view/components/delete-project-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 { ApiKeysSection } from "./settings-view/api-keys/api-keys-section";
|
||||||
import { ClaudeCliStatus } from "./settings-view/cli-status/claude-cli-status";
|
import { ClaudeCliStatus } from "./settings-view/cli-status/claude-cli-status";
|
||||||
import { AppearanceSection } from "./settings-view/appearance/appearance-section";
|
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 { KeyboardShortcutsSection } from "./settings-view/keyboard-shortcuts/keyboard-shortcuts-section";
|
||||||
import { FeatureDefaultsSection } from "./settings-view/feature-defaults/feature-defaults-section";
|
import { FeatureDefaultsSection } from "./settings-view/feature-defaults/feature-defaults-section";
|
||||||
import { DangerZoneSection } from "./settings-view/danger-zone/danger-zone-section";
|
import { DangerZoneSection } from "./settings-view/danger-zone/danger-zone-section";
|
||||||
@@ -33,17 +22,6 @@ import type {
|
|||||||
} from "./settings-view/shared/types";
|
} from "./settings-view/shared/types";
|
||||||
import type { Project as ElectronProject } from "@/lib/electron";
|
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() {
|
export function SettingsView() {
|
||||||
const {
|
const {
|
||||||
theme,
|
theme,
|
||||||
@@ -91,23 +69,70 @@ export function SettingsView() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Use CLI status hook
|
// Use CLI status hook
|
||||||
const {
|
const { claudeCliStatus, isCheckingClaudeCli, handleRefreshClaudeCli } =
|
||||||
claudeCliStatus,
|
useCliStatus();
|
||||||
isCheckingClaudeCli,
|
|
||||||
handleRefreshClaudeCli,
|
|
||||||
} = useCliStatus();
|
|
||||||
|
|
||||||
// Use scroll tracking hook
|
// Use settings view navigation hook
|
||||||
const { activeSection, scrollToSection, scrollContainerRef } =
|
const { activeView, navigateTo } = useSettingsView();
|
||||||
useScrollTracking({
|
|
||||||
items: NAV_ITEMS,
|
|
||||||
filterFn: (item) => item.id !== "danger" || !!currentProject,
|
|
||||||
initialSection: "api-keys",
|
|
||||||
});
|
|
||||||
|
|
||||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = 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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex-1 flex flex-col overflow-hidden content-bg"
|
className="flex-1 flex flex-col overflow-hidden content-bg"
|
||||||
@@ -118,107 +143,17 @@ export function SettingsView() {
|
|||||||
|
|
||||||
{/* Content Area with Sidebar */}
|
{/* Content Area with Sidebar */}
|
||||||
<div className="flex-1 flex overflow-hidden">
|
<div className="flex-1 flex overflow-hidden">
|
||||||
{/* Sticky Side Navigation */}
|
{/* Side Navigation - No longer scrolls, just switches views */}
|
||||||
<SettingsNavigation
|
<SettingsNavigation
|
||||||
navItems={NAV_ITEMS}
|
navItems={NAV_ITEMS}
|
||||||
activeSection={activeSection}
|
activeSection={activeView}
|
||||||
currentProject={currentProject}
|
currentProject={currentProject}
|
||||||
onNavigate={scrollToSection}
|
onNavigate={navigateTo}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Scrollable Content */}
|
{/* Content Panel - Shows only the active section */}
|
||||||
<div ref={scrollContainerRef} className="flex-1 overflow-y-auto p-8">
|
<div className="flex-1 overflow-y-auto p-8">
|
||||||
<div className="max-w-4xl mx-auto space-y-6 pb-96">
|
<div className="max-w-4xl mx-auto">{renderActiveSection()}</div>
|
||||||
{/* 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 "ding" 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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -58,9 +58,8 @@ export function ApiKeysSection() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="api-keys"
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-2xl overflow-hidden scroll-mt-6",
|
"rounded-2xl overflow-hidden",
|
||||||
"border border-border/50",
|
"border border-border/50",
|
||||||
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
||||||
"shadow-sm shadow-black/5"
|
"shadow-sm shadow-black/5"
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ export function AppearanceSection({
|
|||||||
}: AppearanceSectionProps) {
|
}: AppearanceSectionProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="appearance"
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-2xl overflow-hidden scroll-mt-6",
|
"rounded-2xl overflow-hidden",
|
||||||
"border border-border/50",
|
"border border-border/50",
|
||||||
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
||||||
"shadow-sm shadow-black/5"
|
"shadow-sm shadow-black/5"
|
||||||
|
|||||||
@@ -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 "ding" 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -23,9 +23,8 @@ export function ClaudeCliStatus({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="claude"
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-2xl overflow-hidden scroll-mt-6",
|
"rounded-2xl overflow-hidden",
|
||||||
"border border-border/50",
|
"border border-border/50",
|
||||||
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
||||||
"shadow-sm shadow-black/5"
|
"shadow-sm shadow-black/5"
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { Project } from "@/lib/electron";
|
import type { Project } from "@/lib/electron";
|
||||||
import type { NavigationItem } from "../config/navigation";
|
import type { NavigationItem } from "../config/navigation";
|
||||||
|
import type { SettingsViewId } from "../hooks/use-settings-view";
|
||||||
|
|
||||||
interface SettingsNavigationProps {
|
interface SettingsNavigationProps {
|
||||||
navItems: NavigationItem[];
|
navItems: NavigationItem[];
|
||||||
activeSection: string;
|
activeSection: SettingsViewId;
|
||||||
currentProject: Project | null;
|
currentProject: Project | null;
|
||||||
onNavigate: (sectionId: string) => void;
|
onNavigate: (sectionId: SettingsViewId) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SettingsNavigation({
|
export function SettingsNavigation({
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import {
|
|||||||
Key,
|
Key,
|
||||||
Terminal,
|
Terminal,
|
||||||
Palette,
|
Palette,
|
||||||
LayoutGrid,
|
|
||||||
Settings2,
|
Settings2,
|
||||||
|
Volume2,
|
||||||
FlaskConical,
|
FlaskConical,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import type { SettingsViewId } from "../hooks/use-settings-view";
|
||||||
|
|
||||||
export interface NavigationItem {
|
export interface NavigationItem {
|
||||||
id: string;
|
id: SettingsViewId;
|
||||||
label: string;
|
label: string;
|
||||||
icon: LucideIcon;
|
icon: LucideIcon;
|
||||||
}
|
}
|
||||||
@@ -20,8 +21,8 @@ export const NAV_ITEMS: NavigationItem[] = [
|
|||||||
{ id: "api-keys", label: "API Keys", icon: Key },
|
{ id: "api-keys", label: "API Keys", icon: Key },
|
||||||
{ id: "claude", label: "Claude", icon: Terminal },
|
{ id: "claude", label: "Claude", icon: Terminal },
|
||||||
{ id: "appearance", label: "Appearance", icon: Palette },
|
{ id: "appearance", label: "Appearance", icon: Palette },
|
||||||
{ id: "kanban", label: "Kanban Display", icon: LayoutGrid },
|
|
||||||
{ id: "keyboard", label: "Keyboard Shortcuts", icon: Settings2 },
|
{ id: "keyboard", label: "Keyboard Shortcuts", icon: Settings2 },
|
||||||
|
{ id: "audio", label: "Audio", icon: Volume2 },
|
||||||
{ id: "defaults", label: "Feature Defaults", icon: FlaskConical },
|
{ id: "defaults", label: "Feature Defaults", icon: FlaskConical },
|
||||||
{ id: "danger", label: "Danger Zone", icon: Trash2 },
|
{ id: "danger", label: "Danger Zone", icon: Trash2 },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ export function DangerZoneSection({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="danger"
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-2xl overflow-hidden scroll-mt-6",
|
"rounded-2xl overflow-hidden",
|
||||||
"border border-destructive/30",
|
"border border-destructive/30",
|
||||||
"bg-gradient-to-br from-destructive/5 via-card/70 to-card/80 backdrop-blur-xl",
|
"bg-gradient-to-br from-destructive/5 via-card/70 to-card/80 backdrop-blur-xl",
|
||||||
"shadow-sm shadow-destructive/5"
|
"shadow-sm shadow-destructive/5"
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ export function FeatureDefaultsSection({
|
|||||||
}: FeatureDefaultsSectionProps) {
|
}: FeatureDefaultsSectionProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="defaults"
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-2xl overflow-hidden scroll-mt-6",
|
"rounded-2xl overflow-hidden",
|
||||||
"border border-border/50",
|
"border border-border/50",
|
||||||
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
||||||
"shadow-sm shadow-black/5"
|
"shadow-sm shadow-black/5"
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { useCliStatus } from "./use-cli-status";
|
||||||
|
export { useSettingsView, type SettingsViewId } from "./use-settings-view";
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,9 +11,8 @@ export function KeyboardShortcutsSection({
|
|||||||
}: KeyboardShortcutsSectionProps) {
|
}: KeyboardShortcutsSectionProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="keyboard"
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-2xl overflow-hidden scroll-mt-6",
|
"rounded-2xl overflow-hidden",
|
||||||
"border border-border/50",
|
"border border-border/50",
|
||||||
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
"bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl",
|
||||||
"shadow-sm shadow-black/5"
|
"shadow-sm shadow-black/5"
|
||||||
|
|||||||
237
docs/checkout-branch-pr.md
Normal file
237
docs/checkout-branch-pr.md
Normal 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
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user