mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
* feat: refactor Claude API Profiles to Claude Compatible Providers
- Rename ClaudeApiProfile to ClaudeCompatibleProvider with models[] array
- Each ProviderModel has mapsToClaudeModel field for Claude tier mapping
- Add providerType field for provider-specific icons (glm, minimax, openrouter)
- Add thinking level support for provider models in phase selectors
- Show all mapped Claude models per provider model (e.g., "Maps to Haiku, Sonnet, Opus")
- Add Bulk Replace feature to switch all phases to a provider at once
- Hide Bulk Replace button when no providers are enabled
- Fix project-level phaseModelOverrides not persisting after refresh
- Fix deleting last provider not persisting (remove empty array guard)
- Add getProviderByModelId() helper for all SDK routes
- Update all routes to pass provider config for provider models
- Update terminology from "profiles" to "providers" throughout UI
- Update documentation to reflect new provider system
* fix: atomic writer race condition and bulk replace reset to defaults
1. AtomicWriter Race Condition Fix (libs/utils/src/atomic-writer.ts):
- Changed temp file naming from Date.now() to Date.now() + random hex
- Uses crypto.randomBytes(4).toString('hex') for uniqueness
- Prevents ENOENT errors when multiple concurrent writes happen
within the same millisecond
2. Bulk Replace "Anthropic Direct" Reset (both dialogs):
- When selecting "Anthropic Direct", now uses DEFAULT_PHASE_MODELS
- Properly resets thinking levels and other settings to defaults
- Added thinkingLevel to the change detection comparison
- Affects both global and project-level bulk replace dialogs
* fix: update tests for new model resolver passthrough behavior
1. model-resolver tests:
- Unknown models now pass through unchanged (provider model support)
- Removed expectations for warnings on unknown models
- Updated case sensitivity and edge case tests accordingly
- Added tests for provider-like model names (GLM-4.7, MiniMax-M2.1)
2. atomic-writer tests:
- Updated regex to match new temp file format with random suffix
- Format changed from .tmp.{timestamp} to .tmp.{timestamp}.{hex}
* refactor: simplify getPhaseModelWithOverrides calls per code review
Address code review feedback on PR #629:
- Make settingsService parameter optional in getPhaseModelWithOverrides
- Function now handles undefined settingsService gracefully by returning defaults
- Remove redundant ternary checks in 4 call sites:
- apps/server/src/routes/context/routes/describe-file.ts
- apps/server/src/routes/context/routes/describe-image.ts
- apps/server/src/routes/worktree/routes/generate-commit-message.ts
- apps/server/src/services/auto-mode-service.ts
- Remove unused DEFAULT_PHASE_MODELS imports where applicable
* test: fix server tests for provider model passthrough behavior
- Update model-resolver.test.ts to expect unknown models to pass through
unchanged (supports ClaudeCompatibleProvider models like GLM-4.7)
- Remove warning expectations for unknown models (valid for providers)
- Add missing getCredentials and getGlobalSettings mocks to
ideation-service.test.ts for settingsService
* fix: address code review feedback for model providers
- Honor thinkingLevel in generate-commit-message.ts
- Pass claudeCompatibleProvider in ideation-service.ts for provider models
- Resolve provider configuration for model overrides in generate-suggestions.ts
- Update "Active Profile" to "Active Provider" label in project-claude-section
- Use substring instead of deprecated substr in api-profiles-section
- Preserve provider enabled state when editing in api-profiles-section
* fix: address CodeRabbit review issues for Claude Compatible Providers
- Fix TypeScript TS2339 error in generate-suggestions.ts where
settingsService was narrowed to 'never' type in else branch
- Use DEFAULT_PHASE_MODELS per-phase defaults instead of hardcoded
'sonnet' in settings-helpers.ts
- Remove duplicate eventHooks key in use-settings-migration.ts
- Add claudeCompatibleProviders to localStorage migration parsing
and merging functions
- Handle canonical claude-* model IDs (claude-haiku, claude-sonnet,
claude-opus) in project-models-section display names
This resolves the CI build failures and addresses code review feedback.
* fix: skip broken list-view-priority E2E test and add Priority column label
- Skip list-view-priority.spec.ts with TODO explaining the infrastructure
issue: setupRealProject only sets localStorage but server settings
take precedence with localStorageMigrated: true
- Add 'Priority' label to list-header.tsx for the priority column
(was empty string, now shows proper header text)
- Increase column width to accommodate the label
The E2E test issue is that tests create features in a temp directory,
but the server loads from the E2E Test Project fixture path set in
setup-e2e-fixtures.mjs. Needs infrastructure fix to properly switch
projects or create features through UI instead of on disk.
178 lines
6.1 KiB
TypeScript
178 lines
6.1 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { useAppStore } from '@/store/app-store';
|
|
import { Settings, FolderOpen, Menu, X } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { ProjectIdentitySection } from './project-identity-section';
|
|
import { ProjectThemeSection } from './project-theme-section';
|
|
import { WorktreePreferencesSection } from './worktree-preferences-section';
|
|
import { ProjectModelsSection } from './project-models-section';
|
|
import { DangerZoneSection } from '../settings-view/danger-zone/danger-zone-section';
|
|
import { DeleteProjectDialog } from '../settings-view/components/delete-project-dialog';
|
|
import { ProjectSettingsNavigation } from './components/project-settings-navigation';
|
|
import { useProjectSettingsView } from './hooks/use-project-settings-view';
|
|
import type { Project as ElectronProject } from '@/lib/electron';
|
|
|
|
// Breakpoint constant for mobile (matches Tailwind lg breakpoint)
|
|
const LG_BREAKPOINT = 1024;
|
|
|
|
// Convert to the shared types used by components
|
|
interface SettingsProject {
|
|
id: string;
|
|
name: string;
|
|
path: string;
|
|
theme?: string;
|
|
icon?: string;
|
|
customIconPath?: string;
|
|
}
|
|
|
|
export function ProjectSettingsView() {
|
|
const { currentProject, moveProjectToTrash } = useAppStore();
|
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
|
|
|
// Use project settings view navigation hook
|
|
const { activeView, navigateTo } = useProjectSettingsView();
|
|
|
|
// Mobile navigation state - default to showing on desktop, hidden on mobile
|
|
const [showNavigation, setShowNavigation] = useState(() => {
|
|
if (typeof window !== 'undefined') {
|
|
return window.innerWidth >= LG_BREAKPOINT;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
// Auto-close navigation on mobile when a section is selected
|
|
useEffect(() => {
|
|
if (typeof window !== 'undefined' && window.innerWidth < LG_BREAKPOINT) {
|
|
setShowNavigation(false);
|
|
}
|
|
}, [activeView]);
|
|
|
|
// Handle window resize to show/hide navigation appropriately
|
|
useEffect(() => {
|
|
const handleResize = () => {
|
|
if (window.innerWidth >= LG_BREAKPOINT) {
|
|
setShowNavigation(true);
|
|
}
|
|
};
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
return () => window.removeEventListener('resize', handleResize);
|
|
}, []);
|
|
|
|
// Convert electron Project to settings-view Project type
|
|
const convertProject = (project: ElectronProject | null): SettingsProject | null => {
|
|
if (!project) return null;
|
|
return {
|
|
id: project.id,
|
|
name: project.name,
|
|
path: project.path,
|
|
theme: project.theme,
|
|
icon: project.icon,
|
|
customIconPath: project.customIconPath,
|
|
};
|
|
};
|
|
|
|
const settingsProject = convertProject(currentProject);
|
|
|
|
// Render the active section based on current view
|
|
const renderActiveSection = () => {
|
|
if (!currentProject) return null;
|
|
|
|
switch (activeView) {
|
|
case 'identity':
|
|
return <ProjectIdentitySection project={currentProject} />;
|
|
case 'theme':
|
|
return <ProjectThemeSection project={currentProject} />;
|
|
case 'worktrees':
|
|
return <WorktreePreferencesSection project={currentProject} />;
|
|
case 'claude':
|
|
return <ProjectModelsSection project={currentProject} />;
|
|
case 'danger':
|
|
return (
|
|
<DangerZoneSection
|
|
project={settingsProject}
|
|
onDeleteClick={() => setShowDeleteDialog(true)}
|
|
/>
|
|
);
|
|
default:
|
|
return <ProjectIdentitySection project={currentProject} />;
|
|
}
|
|
};
|
|
|
|
// Show message if no project is selected
|
|
if (!currentProject) {
|
|
return (
|
|
<div
|
|
className="flex-1 flex flex-col overflow-hidden content-bg"
|
|
data-testid="project-settings-view"
|
|
>
|
|
<div className="flex-1 flex items-center justify-center p-8">
|
|
<div className="text-center max-w-md">
|
|
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-muted/50 flex items-center justify-center">
|
|
<FolderOpen className="w-8 h-8 text-muted-foreground/50" />
|
|
</div>
|
|
<h2 className="text-lg font-semibold text-foreground mb-2">No Project Selected</h2>
|
|
<p className="text-sm text-muted-foreground">
|
|
Select a project from the sidebar to configure project-specific settings.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="flex-1 flex flex-col overflow-hidden content-bg"
|
|
data-testid="project-settings-view"
|
|
>
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
|
|
<div className="flex items-center gap-3">
|
|
<Settings className="w-5 h-5 text-muted-foreground" />
|
|
<div>
|
|
<h1 className="text-xl font-bold">Project Settings</h1>
|
|
<p className="text-sm text-muted-foreground">
|
|
Configure settings for {currentProject.name}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
{/* Mobile menu button - far right */}
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => setShowNavigation(!showNavigation)}
|
|
className="lg:hidden h-8 w-8 p-0"
|
|
aria-label={showNavigation ? 'Close navigation menu' : 'Open navigation menu'}
|
|
>
|
|
{showNavigation ? <X className="w-4 h-4" /> : <Menu className="w-4 h-4" />}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Content Area with Sidebar */}
|
|
<div className="flex-1 flex overflow-hidden">
|
|
{/* Side Navigation */}
|
|
<ProjectSettingsNavigation
|
|
activeSection={activeView}
|
|
onNavigate={navigateTo}
|
|
isOpen={showNavigation}
|
|
onClose={() => setShowNavigation(false)}
|
|
/>
|
|
|
|
{/* Content Panel - Shows only the active section */}
|
|
<div className="flex-1 overflow-y-auto p-4 lg:p-8">
|
|
<div className="max-w-4xl mx-auto">{renderActiveSection()}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Delete Project Confirmation Dialog */}
|
|
<DeleteProjectDialog
|
|
open={showDeleteDialog}
|
|
onOpenChange={setShowDeleteDialog}
|
|
project={currentProject}
|
|
onConfirm={moveProjectToTrash}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|