feat: unified Claude API key and profile system with z.AI, MiniMax, OpenRouter support (#600)

* feat: add Claude API provider profiles for alternative endpoints

Add support for managing multiple Claude-compatible API endpoints
(z.AI GLM, AWS Bedrock, etc.) through provider profiles in settings.

Features:
- New ClaudeApiProfile type with base URL, API key, model mappings
- Pre-configured z.AI GLM template with correct model names
- Profile selector in Settings > Claude > API Profiles
- Clean switching between profiles and direct Anthropic API
- Immediate persistence to prevent data loss on restart

Profile support added to all execution paths:
- Agent service (chat)
- Ideation service
- Auto-mode service (feature agents, enhancements)
- Simple query service (title generation, descriptions, etc.)
- Backlog planning, commit messages, spec generation
- GitHub issue validation, suggestions

Environment variables set when profile is active:
- ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN/API_KEY
- ANTHROPIC_DEFAULT_HAIKU/SONNET/OPUS_MODEL
- API_TIMEOUT_MS, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC
This commit is contained in:
Stefan de Vogelaere
2026-01-19 20:36:58 +01:00
committed by GitHub
parent 63b8eb0991
commit d97c4b7b57
45 changed files with 2661 additions and 146 deletions

View File

@@ -30,17 +30,41 @@ import {
import { DndContext, closestCenter } from '@dnd-kit/core';
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { SortableProjectItem, ThemeMenuItem } from './';
import { PROJECT_DARK_THEMES, PROJECT_LIGHT_THEMES } from '../constants';
import { PROJECT_DARK_THEMES, PROJECT_LIGHT_THEMES, THEME_SUBMENU_CONSTANTS } from '../constants';
import { useProjectPicker, useDragAndDrop, useProjectTheme } from '../hooks';
import { useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
/**
* Props for the ProjectSelectorWithOptions component.
* Defines the interface for the project selector dropdown with additional options menu.
*/
interface ProjectSelectorWithOptionsProps {
/** Whether the sidebar is currently expanded */
sidebarOpen: boolean;
/** Whether the project picker dropdown is currently open */
isProjectPickerOpen: boolean;
/** Callback to control the project picker dropdown open state */
setIsProjectPickerOpen: (value: boolean | ((prev: boolean) => boolean)) => void;
/** Callback to show the delete project confirmation dialog */
setShowDeleteProjectDialog: (show: boolean) => void;
}
/**
* A project selector component with search, drag-and-drop reordering, and options menu.
*
* Features:
* - Searchable dropdown for quick project switching
* - Drag-and-drop reordering of projects
* - Project-specific theme selection with live preview
* - Project history navigation (previous/next)
* - Option to move project to trash
*
* The component uses viewport-aware positioning via THEME_SUBMENU_CONSTANTS
* for consistent submenu behavior across the application.
*
* @param props - Component props
* @returns The rendered project selector or null if sidebar is closed or no projects exist
*/
export function ProjectSelectorWithOptions({
sidebarOpen,
isProjectPickerOpen,
@@ -246,6 +270,7 @@ export function ProjectSelectorWithOptions({
<DropdownMenuSubContent
className="w-[420px] bg-popover/95 backdrop-blur-xl"
data-testid="project-theme-menu"
collisionPadding={THEME_SUBMENU_CONSTANTS.COLLISION_PADDING}
onPointerLeave={() => {
// Clear preview theme when leaving the dropdown
setPreviewTheme(null);
@@ -286,7 +311,8 @@ export function ProjectSelectorWithOptions({
</div>
<DropdownMenuSeparator />
{/* Two Column Layout */}
<div className="flex gap-2 p-2">
{/* Max height with scroll to ensure all themes are visible when menu is near screen edge */}
<div className="flex gap-2 p-2 max-h-[60vh] overflow-y-auto scrollbar-styled">
{/* Dark Themes Column */}
<div className="flex-1">
<div className="flex items-center gap-1.5 px-2 py-1.5 text-xs font-medium text-muted-foreground">

View File

@@ -1,5 +1,36 @@
import { darkThemes, lightThemes } from '@/config/theme-options';
/**
* Shared constants for theme submenu positioning and layout.
* Used across project-context-menu and project-selector-with-options components
* to ensure consistent viewport-aware positioning and styling.
*/
export const THEME_SUBMENU_CONSTANTS = {
/**
* Estimated total height of the theme submenu content in pixels.
* Includes all theme options, headers, padding, and "Use Global" button.
*/
ESTIMATED_SUBMENU_HEIGHT: 620,
/**
* Padding from viewport edges to prevent submenu overflow.
* Applied to both top and bottom edges when calculating available space.
*/
COLLISION_PADDING: 32,
/**
* Vertical offset from context menu top to the "Project Theme" button.
* Used for calculating submenu position relative to trigger button.
*/
THEME_BUTTON_OFFSET: 50,
/**
* Height reserved for submenu header area (includes "Use Global" button and separator).
* Subtracted from maxHeight to get scrollable content area height.
*/
SUBMENU_HEADER_HEIGHT: 80,
} as const;
export const PROJECT_DARK_THEMES = darkThemes.map((opt) => ({
value: opt.value,
label: opt.label,

View File

@@ -12,9 +12,7 @@ interface UseProjectCreationProps {
upsertAndSetCurrentProject: (path: string, name: string) => Project;
}
export function useProjectCreation({
upsertAndSetCurrentProject,
}: UseProjectCreationProps) {
export function useProjectCreation({ upsertAndSetCurrentProject }: UseProjectCreationProps) {
// Modal state
const [showNewProjectModal, setShowNewProjectModal] = useState(false);
const [isCreatingProject, setIsCreatingProject] = useState(false);