mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +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 { useAppStore } from "@/store/app-store";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useCliStatus } from "./settings-view/hooks/use-cli-status";
|
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 { 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";
|
||||||
@@ -76,7 +76,11 @@ export function SettingsView() {
|
|||||||
|
|
||||||
// Use scroll tracking hook
|
// Use scroll tracking hook
|
||||||
const { activeSection, scrollToSection, scrollContainerRef } =
|
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 [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
|
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { AlertCircle, CheckCircle2, Eye, EyeOff, Loader2, Zap } from "lucide-react";
|
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 {
|
interface ApiKeyFieldProps {
|
||||||
config: ProviderConfig;
|
config: ProviderConfig;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useSetupStore } from "@/store/setup-store";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Key, CheckCircle2 } from "lucide-react";
|
import { Key, CheckCircle2 } from "lucide-react";
|
||||||
import { ApiKeyField } from "./api-key-field";
|
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 { AuthenticationStatusDisplay } from "./authentication-status-display";
|
||||||
import { SecurityNotice } from "./security-notice";
|
import { SecurityNotice } from "./security-notice";
|
||||||
import { useApiKeyManagement } from "./hooks/use-api-key-management";
|
import { useApiKeyManagement } from "./hooks/use-api-key-management";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useAppStore } from "@/store/app-store";
|
import { useAppStore } from "@/store/app-store";
|
||||||
import { getElectronAPI } from "@/lib/electron";
|
import { getElectronAPI } from "@/lib/electron";
|
||||||
import type { ProviderConfigParams } from "../config/api-provider-config";
|
import type { ProviderConfigParams } from "@/config/api-providers";
|
||||||
|
|
||||||
interface TestResult {
|
interface TestResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Palette } from "lucide-react";
|
import { Palette } from "lucide-react";
|
||||||
import { themeOptions } from "./config/theme-options";
|
import { themeOptions } from "@/config/theme-options";
|
||||||
import type { Theme, Project } from "../shared/types";
|
import type { Theme, Project } from "../shared/types";
|
||||||
|
|
||||||
interface AppearanceSectionProps {
|
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,
|
Terminal,
|
||||||
Trees,
|
Trees,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Theme } from "../../shared/types";
|
import { Theme } from "@/components/views/settings-view/shared/types";
|
||||||
|
|
||||||
export interface ThemeOption {
|
export interface ThemeOption {
|
||||||
value: Theme;
|
value: Theme;
|
||||||
@@ -1,17 +1,34 @@
|
|||||||
import { useState, useEffect, useRef, useCallback } from "react";
|
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
|
* Automatically highlights the active section based on scroll position
|
||||||
* and provides smooth scrolling to sections
|
* and provides smooth scrolling to sections
|
||||||
*/
|
*/
|
||||||
export function useScrollTracking(
|
export function useScrollTracking<T extends ScrollTrackingItem>({
|
||||||
navItems: NavigationItem[],
|
items,
|
||||||
currentProject: Project | null
|
filterFn = () => true,
|
||||||
) {
|
initialSection,
|
||||||
const [activeSection, setActiveSection] = useState("api-keys");
|
scrollOffset = 24,
|
||||||
|
}: UseScrollTrackingOptions<T>) {
|
||||||
|
const [activeSection, setActiveSection] = useState(
|
||||||
|
initialSection || items[0]?.id || ""
|
||||||
|
);
|
||||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Track scroll position to highlight active nav item
|
// Track scroll position to highlight active nav item
|
||||||
@@ -20,8 +37,8 @@ export function useScrollTracking(
|
|||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const sections = navItems
|
const sections = items
|
||||||
.filter((item) => item.id !== "danger" || currentProject)
|
.filter(filterFn)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
element: document.getElementById(item.id),
|
element: document.getElementById(item.id),
|
||||||
@@ -57,24 +74,27 @@ export function useScrollTracking(
|
|||||||
|
|
||||||
container.addEventListener("scroll", handleScroll);
|
container.addEventListener("scroll", handleScroll);
|
||||||
return () => container.removeEventListener("scroll", handleScroll);
|
return () => container.removeEventListener("scroll", handleScroll);
|
||||||
}, [currentProject, navItems]);
|
}, [items, filterFn]);
|
||||||
|
|
||||||
// Scroll to a specific section with smooth animation
|
// Scroll to a specific section with smooth animation
|
||||||
const scrollToSection = useCallback((sectionId: string) => {
|
const scrollToSection = useCallback(
|
||||||
const element = document.getElementById(sectionId);
|
(sectionId: string) => {
|
||||||
if (element && scrollContainerRef.current) {
|
const element = document.getElementById(sectionId);
|
||||||
const container = scrollContainerRef.current;
|
if (element && scrollContainerRef.current) {
|
||||||
const containerRect = container.getBoundingClientRect();
|
const container = scrollContainerRef.current;
|
||||||
const elementRect = element.getBoundingClientRect();
|
const containerRect = container.getBoundingClientRect();
|
||||||
const relativeTop =
|
const elementRect = element.getBoundingClientRect();
|
||||||
elementRect.top - containerRect.top + container.scrollTop;
|
const relativeTop =
|
||||||
|
elementRect.top - containerRect.top + container.scrollTop;
|
||||||
|
|
||||||
container.scrollTo({
|
container.scrollTo({
|
||||||
top: relativeTop - 24,
|
top: relativeTop - scrollOffset,
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, []);
|
},
|
||||||
|
[scrollOffset]
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeSection,
|
activeSection,
|
||||||
Reference in New Issue
Block a user