Merge branch 'v0.9.0rc' into remove-sandbox-as-it-is-broken

This commit is contained in:
webdevcody
2026-01-07 15:01:31 -05:00
115 changed files with 8734 additions and 401 deletions

View File

@@ -19,10 +19,12 @@ import {
import {
CLAUDE_MODELS,
CURSOR_MODELS,
CODEX_MODELS,
THINKING_LEVELS,
THINKING_LEVEL_LABELS,
} from '@/components/views/board-view/shared/model-constants';
import { Check, ChevronsUpDown, Star, Brain, Sparkles, ChevronRight } from 'lucide-react';
import { Check, ChevronsUpDown, Star, ChevronRight } from 'lucide-react';
import { AnthropicIcon, CursorIcon, OpenAIIcon } from '@/components/ui/provider-icon';
import { Button } from '@/components/ui/button';
import {
Command,
@@ -140,14 +142,14 @@ export function PhaseModelSelector({
return {
...claudeModel,
label: `${claudeModel.label}${thinkingLabel}`,
icon: Brain,
icon: AnthropicIcon,
};
}
const cursorModel = availableCursorModels.find(
(m) => stripProviderPrefix(m.id) === selectedModel
);
if (cursorModel) return { ...cursorModel, icon: Sparkles };
if (cursorModel) return { ...cursorModel, icon: CursorIcon };
// Check if selectedModel is part of a grouped model
const group = getModelGroup(selectedModel as CursorModelId);
@@ -158,10 +160,14 @@ export function PhaseModelSelector({
label: `${group.label} (${variant?.label || 'Unknown'})`,
description: group.description,
provider: 'cursor' as const,
icon: Sparkles,
icon: CursorIcon,
};
}
// Check Codex models
const codexModel = CODEX_MODELS.find((m) => m.id === selectedModel);
if (codexModel) return { ...codexModel, icon: OpenAIIcon };
return null;
}, [selectedModel, selectedThinkingLevel, availableCursorModels]);
@@ -199,10 +205,11 @@ export function PhaseModelSelector({
}, [availableCursorModels, enabledCursorModels]);
// Group models
const { favorites, claude, cursor } = React.useMemo(() => {
const { favorites, claude, cursor, codex } = React.useMemo(() => {
const favs: typeof CLAUDE_MODELS = [];
const cModels: typeof CLAUDE_MODELS = [];
const curModels: typeof CURSOR_MODELS = [];
const codModels: typeof CODEX_MODELS = [];
// Process Claude Models
CLAUDE_MODELS.forEach((model) => {
@@ -222,9 +229,71 @@ export function PhaseModelSelector({
}
});
return { favorites: favs, claude: cModels, cursor: curModels };
// Process Codex Models
CODEX_MODELS.forEach((model) => {
if (favoriteModels.includes(model.id)) {
favs.push(model);
} else {
codModels.push(model);
}
});
return { favorites: favs, claude: cModels, cursor: curModels, codex: codModels };
}, [favoriteModels, availableCursorModels]);
// Render Codex model item (no thinking level needed)
const renderCodexModelItem = (model: (typeof CODEX_MODELS)[0]) => {
const isSelected = selectedModel === model.id;
const isFavorite = favoriteModels.includes(model.id);
return (
<CommandItem
key={model.id}
value={model.label}
onSelect={() => {
onChange({ model: model.id });
setOpen(false);
}}
className="group flex items-center justify-between py-2"
>
<div className="flex items-center gap-3 overflow-hidden">
<OpenAIIcon
className={cn(
'h-4 w-4 shrink-0',
isSelected ? 'text-primary' : 'text-muted-foreground'
)}
/>
<div className="flex flex-col truncate">
<span className={cn('truncate font-medium', isSelected && 'text-primary')}>
{model.label}
</span>
<span className="truncate text-xs text-muted-foreground">{model.description}</span>
</div>
</div>
<div className="flex items-center gap-1 ml-2">
<Button
variant="ghost"
size="icon"
className={cn(
'h-6 w-6 hover:bg-transparent hover:text-yellow-500 focus:ring-0',
isFavorite
? 'text-yellow-500 opacity-100'
: 'opacity-0 group-hover:opacity-100 text-muted-foreground'
)}
onClick={(e) => {
e.stopPropagation();
toggleFavoriteModel(model.id);
}}
>
<Star className={cn('h-3.5 w-3.5', isFavorite && 'fill-current')} />
</Button>
{isSelected && <Check className="h-4 w-4 text-primary shrink-0" />}
</div>
</CommandItem>
);
};
// Render Cursor model item (no thinking level needed)
const renderCursorModelItem = (model: (typeof CURSOR_MODELS)[0]) => {
const modelValue = stripProviderPrefix(model.id);
@@ -242,7 +311,7 @@ export function PhaseModelSelector({
className="group flex items-center justify-between py-2"
>
<div className="flex items-center gap-3 overflow-hidden">
<Sparkles
<CursorIcon
className={cn(
'h-4 w-4 shrink-0',
isSelected ? 'text-primary' : 'text-muted-foreground'
@@ -311,7 +380,7 @@ export function PhaseModelSelector({
)}
>
<div className="flex items-center gap-3 overflow-hidden">
<Brain
<AnthropicIcon
className={cn(
'h-4 w-4 shrink-0',
isSelected ? 'text-primary' : 'text-muted-foreground'
@@ -445,7 +514,7 @@ export function PhaseModelSelector({
)}
>
<div className="flex items-center gap-3 overflow-hidden">
<Sparkles
<CursorIcon
className={cn(
'h-4 w-4 shrink-0',
groupIsSelected ? 'text-primary' : 'text-muted-foreground'
@@ -603,6 +672,10 @@ export function PhaseModelSelector({
// Standalone Cursor model
return renderCursorModelItem(model);
}
// Codex model
if (model.provider === 'codex') {
return renderCodexModelItem(model);
}
// Claude model
return renderClaudeModelItem(model);
});
@@ -626,6 +699,12 @@ export function PhaseModelSelector({
{standaloneCursorModels.map((model) => renderCursorModelItem(model))}
</CommandGroup>
)}
{codex.length > 0 && (
<CommandGroup heading="Codex Models">
{codex.map((model) => renderCodexModelItem(model))}
</CommandGroup>
)}
</CommandList>
</Command>
</PopoverContent>