mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
refactor: move configs and hooks to global locations for reusability
Move previously nested configs and hooks to global src/ folders to make them reusable across the application, reduce nesting, and establish clearer organization patterns. **New Global Structure:** - src/config/theme-options.ts (moved from appearance/config/) - src/config/api-providers.ts (moved from api-keys/config/) - src/hooks/use-scroll-tracking.ts (moved from settings-view/hooks/) **Changes:** - Move theme-options.ts to src/config/ - app-wide theme configuration - Move api-provider-config.ts to src/config/api-providers.ts - global API config - Move use-scroll-tracking.ts to src/hooks/ - reusable scroll navigation hook - Make useScrollTracking generic and more flexible with options object - Update all imports across settings-view components - Remove duplicate api-provider-config.ts from shared/ folder - Remove empty config/ folders (appearance/config, api-keys/config) **Benefits:** ✅ Single source of truth for themes and API providers ✅ Reusable scroll tracking hook available globally ✅ Cleaner structure with less nesting ✅ Better discoverability for developers ✅ No duplicate configuration files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import { useState } from "react";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useCliStatus } from "./settings-view/hooks/use-cli-status";
|
||||
import { useScrollTracking } from "./settings-view/hooks/use-scroll-tracking";
|
||||
import { useScrollTracking } from "@/hooks/use-scroll-tracking";
|
||||
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";
|
||||
@@ -76,7 +76,11 @@ export function SettingsView() {
|
||||
|
||||
// Use scroll tracking hook
|
||||
const { activeSection, scrollToSection, scrollContainerRef } =
|
||||
useScrollTracking(NAV_ITEMS, currentProject);
|
||||
useScrollTracking({
|
||||
items: NAV_ITEMS,
|
||||
filterFn: (item) => item.id !== "danger" || !!currentProject,
|
||||
initialSection: "api-keys",
|
||||
});
|
||||
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { AlertCircle, CheckCircle2, Eye, EyeOff, Loader2, Zap } from "lucide-react";
|
||||
import type { ProviderConfig } from "./shared/api-provider-config";
|
||||
import type { ProviderConfig } from "@/config/api-providers";
|
||||
|
||||
interface ApiKeyFieldProps {
|
||||
config: ProviderConfig;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useSetupStore } from "@/store/setup-store";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Key, CheckCircle2 } from "lucide-react";
|
||||
import { ApiKeyField } from "./api-key-field";
|
||||
import { buildProviderConfigs } from "./shared/api-provider-config";
|
||||
import { buildProviderConfigs } from "@/config/api-providers";
|
||||
import { AuthenticationStatusDisplay } from "./authentication-status-display";
|
||||
import { SecurityNotice } from "./security-notice";
|
||||
import { useApiKeyManagement } from "./hooks/use-api-key-management";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import type { ProviderConfigParams } from "../config/api-provider-config";
|
||||
import type { ProviderConfigParams } from "@/config/api-providers";
|
||||
|
||||
interface TestResult {
|
||||
success: boolean;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Palette } from "lucide-react";
|
||||
import { themeOptions } from "./config/theme-options";
|
||||
import { themeOptions } from "@/config/theme-options";
|
||||
import type { Theme, Project } from "../shared/types";
|
||||
|
||||
interface AppearanceSectionProps {
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import type { ApiKeys } from "@/store/app-store";
|
||||
|
||||
export type ProviderKey = "anthropic" | "google" | "openai";
|
||||
|
||||
export interface ProviderConfig {
|
||||
key: ProviderKey;
|
||||
label: string;
|
||||
inputId: string;
|
||||
placeholder: string;
|
||||
value: string;
|
||||
setValue: Dispatch<SetStateAction<string>>;
|
||||
showValue: boolean;
|
||||
setShowValue: Dispatch<SetStateAction<boolean>>;
|
||||
hasStoredKey: string | null | undefined;
|
||||
inputTestId: string;
|
||||
toggleTestId: string;
|
||||
testButton: {
|
||||
onClick: () => Promise<void> | void;
|
||||
disabled: boolean;
|
||||
loading: boolean;
|
||||
testId: string;
|
||||
};
|
||||
result: { success: boolean; message: string } | null;
|
||||
resultTestId: string;
|
||||
resultMessageTestId: string;
|
||||
descriptionPrefix: string;
|
||||
descriptionLinkHref: string;
|
||||
descriptionLinkText: string;
|
||||
descriptionSuffix?: string;
|
||||
}
|
||||
|
||||
export interface ProviderConfigParams {
|
||||
apiKeys: ApiKeys;
|
||||
anthropic: {
|
||||
value: string;
|
||||
setValue: Dispatch<SetStateAction<string>>;
|
||||
show: boolean;
|
||||
setShow: Dispatch<SetStateAction<boolean>>;
|
||||
testing: boolean;
|
||||
onTest: () => Promise<void>;
|
||||
result: { success: boolean; message: string } | null;
|
||||
};
|
||||
google: {
|
||||
value: string;
|
||||
setValue: Dispatch<SetStateAction<string>>;
|
||||
show: boolean;
|
||||
setShow: Dispatch<SetStateAction<boolean>>;
|
||||
testing: boolean;
|
||||
onTest: () => Promise<void>;
|
||||
result: { success: boolean; message: string } | null;
|
||||
};
|
||||
openai: {
|
||||
value: string;
|
||||
setValue: Dispatch<SetStateAction<string>>;
|
||||
show: boolean;
|
||||
setShow: Dispatch<SetStateAction<boolean>>;
|
||||
testing: boolean;
|
||||
onTest: () => Promise<void>;
|
||||
result: { success: boolean; message: string } | null;
|
||||
};
|
||||
}
|
||||
|
||||
export const buildProviderConfigs = ({
|
||||
apiKeys,
|
||||
anthropic,
|
||||
google,
|
||||
openai,
|
||||
}: ProviderConfigParams): ProviderConfig[] => [
|
||||
{
|
||||
key: "anthropic",
|
||||
label: "Anthropic API Key (Claude)",
|
||||
inputId: "anthropic-key",
|
||||
placeholder: "sk-ant-...",
|
||||
value: anthropic.value,
|
||||
setValue: anthropic.setValue,
|
||||
showValue: anthropic.show,
|
||||
setShowValue: anthropic.setShow,
|
||||
hasStoredKey: apiKeys.anthropic,
|
||||
inputTestId: "anthropic-api-key-input",
|
||||
toggleTestId: "toggle-anthropic-visibility",
|
||||
testButton: {
|
||||
onClick: anthropic.onTest,
|
||||
disabled: !anthropic.value || anthropic.testing,
|
||||
loading: anthropic.testing,
|
||||
testId: "test-claude-connection",
|
||||
},
|
||||
result: anthropic.result,
|
||||
resultTestId: "test-connection-result",
|
||||
resultMessageTestId: "test-connection-message",
|
||||
descriptionPrefix: "Used for Claude AI features. Get your key at",
|
||||
descriptionLinkHref: "https://console.anthropic.com/account/keys",
|
||||
descriptionLinkText: "console.anthropic.com",
|
||||
descriptionSuffix:
|
||||
". Alternatively, the CLAUDE_CODE_OAUTH_TOKEN environment variable can be used.",
|
||||
},
|
||||
{
|
||||
key: "google",
|
||||
label: "Google API Key (Gemini)",
|
||||
inputId: "google-key",
|
||||
placeholder: "AIza...",
|
||||
value: google.value,
|
||||
setValue: google.setValue,
|
||||
showValue: google.show,
|
||||
setShowValue: google.setShow,
|
||||
hasStoredKey: apiKeys.google,
|
||||
inputTestId: "google-api-key-input",
|
||||
toggleTestId: "toggle-google-visibility",
|
||||
testButton: {
|
||||
onClick: google.onTest,
|
||||
disabled: !google.value || google.testing,
|
||||
loading: google.testing,
|
||||
testId: "test-gemini-connection",
|
||||
},
|
||||
result: google.result,
|
||||
resultTestId: "gemini-test-connection-result",
|
||||
resultMessageTestId: "gemini-test-connection-message",
|
||||
descriptionPrefix:
|
||||
"Used for Gemini AI features (including image/design prompts). Get your key at",
|
||||
descriptionLinkHref: "https://makersuite.google.com/app/apikey",
|
||||
descriptionLinkText: "makersuite.google.com",
|
||||
},
|
||||
{
|
||||
key: "openai",
|
||||
label: "OpenAI API Key (Codex/GPT)",
|
||||
inputId: "openai-key",
|
||||
placeholder: "sk-...",
|
||||
value: openai.value,
|
||||
setValue: openai.setValue,
|
||||
showValue: openai.show,
|
||||
setShowValue: openai.setShow,
|
||||
hasStoredKey: apiKeys.openai,
|
||||
inputTestId: "openai-api-key-input",
|
||||
toggleTestId: "toggle-openai-visibility",
|
||||
testButton: {
|
||||
onClick: openai.onTest,
|
||||
disabled: !openai.value || openai.testing,
|
||||
loading: openai.testing,
|
||||
testId: "test-openai-connection",
|
||||
},
|
||||
result: openai.result,
|
||||
resultTestId: "openai-test-connection-result",
|
||||
resultMessageTestId: "openai-test-connection-message",
|
||||
descriptionPrefix: "Used for OpenAI Codex CLI and GPT models. Get your key at",
|
||||
descriptionLinkHref: "https://platform.openai.com/api-keys",
|
||||
descriptionLinkText: "platform.openai.com",
|
||||
},
|
||||
];
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Terminal,
|
||||
Trees,
|
||||
} from "lucide-react";
|
||||
import { Theme } from "../../shared/types";
|
||||
import { Theme } from "@/components/views/settings-view/shared/types";
|
||||
|
||||
export interface ThemeOption {
|
||||
value: Theme;
|
||||
@@ -1,17 +1,34 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import type { Project } from "@/store/app-store";
|
||||
import type { NavigationItem } from "../config/navigation";
|
||||
|
||||
interface ScrollTrackingItem {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface UseScrollTrackingOptions<T extends ScrollTrackingItem> {
|
||||
/** Navigation items with at least an id property */
|
||||
items: T[];
|
||||
/** Optional filter function to determine which items should be tracked */
|
||||
filterFn?: (item: T) => boolean;
|
||||
/** Optional initial active section (defaults to first item's id) */
|
||||
initialSection?: string;
|
||||
/** Optional offset from top when scrolling to section (defaults to 24) */
|
||||
scrollOffset?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for managing scroll-based navigation tracking
|
||||
* Generic custom hook for managing scroll-based navigation tracking
|
||||
* Automatically highlights the active section based on scroll position
|
||||
* and provides smooth scrolling to sections
|
||||
*/
|
||||
export function useScrollTracking(
|
||||
navItems: NavigationItem[],
|
||||
currentProject: Project | null
|
||||
) {
|
||||
const [activeSection, setActiveSection] = useState("api-keys");
|
||||
export function useScrollTracking<T extends ScrollTrackingItem>({
|
||||
items,
|
||||
filterFn = () => true,
|
||||
initialSection,
|
||||
scrollOffset = 24,
|
||||
}: UseScrollTrackingOptions<T>) {
|
||||
const [activeSection, setActiveSection] = useState(
|
||||
initialSection || items[0]?.id || ""
|
||||
);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Track scroll position to highlight active nav item
|
||||
@@ -20,8 +37,8 @@ export function useScrollTracking(
|
||||
if (!container) return;
|
||||
|
||||
const handleScroll = () => {
|
||||
const sections = navItems
|
||||
.filter((item) => item.id !== "danger" || currentProject)
|
||||
const sections = items
|
||||
.filter(filterFn)
|
||||
.map((item) => ({
|
||||
id: item.id,
|
||||
element: document.getElementById(item.id),
|
||||
@@ -57,24 +74,27 @@ export function useScrollTracking(
|
||||
|
||||
container.addEventListener("scroll", handleScroll);
|
||||
return () => container.removeEventListener("scroll", handleScroll);
|
||||
}, [currentProject, navItems]);
|
||||
}, [items, filterFn]);
|
||||
|
||||
// Scroll to a specific section with smooth animation
|
||||
const scrollToSection = useCallback((sectionId: string) => {
|
||||
const element = document.getElementById(sectionId);
|
||||
if (element && scrollContainerRef.current) {
|
||||
const container = scrollContainerRef.current;
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
const relativeTop =
|
||||
elementRect.top - containerRect.top + container.scrollTop;
|
||||
const scrollToSection = useCallback(
|
||||
(sectionId: string) => {
|
||||
const element = document.getElementById(sectionId);
|
||||
if (element && scrollContainerRef.current) {
|
||||
const container = scrollContainerRef.current;
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
const relativeTop =
|
||||
elementRect.top - containerRect.top + container.scrollTop;
|
||||
|
||||
container.scrollTo({
|
||||
top: relativeTop - 24,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
container.scrollTo({
|
||||
top: relativeTop - scrollOffset,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
},
|
||||
[scrollOffset]
|
||||
);
|
||||
|
||||
return {
|
||||
activeSection,
|
||||
Reference in New Issue
Block a user