Merge branch: resolve conflict in worktree-actions-dropdown.tsx

This commit is contained in:
Kacper
2026-01-11 20:08:19 +01:00
118 changed files with 6327 additions and 1795 deletions

View File

@@ -1,15 +1,51 @@
import { useState } from 'react';
import { useNavigate } from '@tanstack/react-router';
import { Button } from '@/components/ui/button';
import { LogOut, User } from 'lucide-react';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { toast } from 'sonner';
import { LogOut, User, Code2, RefreshCw } from 'lucide-react';
import { cn } from '@/lib/utils';
import { logout } from '@/lib/http-api-client';
import { useAuthStore } from '@/store/auth-store';
import { useAppStore } from '@/store/app-store';
import {
useAvailableEditors,
useEffectiveDefaultEditor,
} from '@/components/views/board-view/worktree-panel/hooks/use-available-editors';
import { getEditorIcon } from '@/components/icons/editor-icons';
export function AccountSection() {
const navigate = useNavigate();
const [isLoggingOut, setIsLoggingOut] = useState(false);
// Editor settings
const { editors, isLoading: isLoadingEditors, isRefreshing, refresh } = useAvailableEditors();
const defaultEditorCommand = useAppStore((s) => s.defaultEditorCommand);
const setDefaultEditorCommand = useAppStore((s) => s.setDefaultEditorCommand);
// Use shared hook for effective default editor
const effectiveEditor = useEffectiveDefaultEditor(editors);
// Normalize Select value: if saved editor isn't found, show 'auto'
const hasSavedEditor =
!!defaultEditorCommand && editors.some((e) => e.command === defaultEditorCommand);
const selectValue = hasSavedEditor ? defaultEditorCommand : 'auto';
// Get icon component for the effective editor
const EffectiveEditorIcon = effectiveEditor ? getEditorIcon(effectiveEditor.command) : null;
const handleRefreshEditors = async () => {
await refresh();
toast.success('Editor list refreshed');
};
const handleLogout = async () => {
setIsLoggingOut(true);
try {
@@ -43,6 +79,81 @@ export function AccountSection() {
<p className="text-sm text-muted-foreground/80 ml-12">Manage your session and account.</p>
</div>
<div className="p-6 space-y-4">
{/* Default IDE */}
<div className="flex items-center justify-between gap-4 p-4 rounded-xl bg-muted/30 border border-border/30">
<div className="flex items-center gap-3.5 min-w-0">
<div className="w-11 h-11 rounded-xl bg-gradient-to-br from-muted/50 to-muted/30 border border-border/30 flex items-center justify-center shrink-0">
<Code2 className="w-5 h-5 text-muted-foreground" />
</div>
<div className="min-w-0">
<p className="font-medium text-foreground">Default IDE</p>
<p className="text-xs text-muted-foreground/70 mt-0.5">
Default IDE to use when opening branches or worktrees
</p>
</div>
</div>
<div className="flex items-center gap-2">
<Select
value={selectValue}
onValueChange={(value) => setDefaultEditorCommand(value === 'auto' ? null : value)}
disabled={isLoadingEditors || isRefreshing || editors.length === 0}
>
<SelectTrigger className="w-[180px] shrink-0">
<SelectValue placeholder="Select editor">
{effectiveEditor ? (
<span className="flex items-center gap-2">
{EffectiveEditorIcon && <EffectiveEditorIcon className="w-4 h-4" />}
{effectiveEditor.name}
{selectValue === 'auto' && (
<span className="text-muted-foreground text-xs">(Auto)</span>
)}
</span>
) : (
'Select editor'
)}
</SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="auto">
<span className="flex items-center gap-2">
<Code2 className="w-4 h-4" />
Auto-detect
</span>
</SelectItem>
{editors.map((editor) => {
const Icon = getEditorIcon(editor.command);
return (
<SelectItem key={editor.command} value={editor.command}>
<span className="flex items-center gap-2">
<Icon className="w-4 h-4" />
{editor.name}
</span>
</SelectItem>
);
})}
</SelectContent>
</Select>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleRefreshEditors}
disabled={isRefreshing || isLoadingEditors}
className="shrink-0 h-9 w-9"
>
<RefreshCw className={cn('w-4 h-4', isRefreshing && 'animate-spin')} />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Refresh available editors</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
{/* Logout */}
<div className="flex items-center justify-between gap-4 p-4 rounded-xl bg-muted/30 border border-border/30">
<div className="flex items-center gap-3.5 min-w-0">

View File

@@ -37,6 +37,7 @@ import {
QwenIcon,
MistralIcon,
MetaIcon,
getProviderIconForModel,
} from '@/components/ui/provider-icon';
import { Button } from '@/components/ui/button';
import {
@@ -513,27 +514,8 @@ export function PhaseModelSelector({
const isSelected = selectedModel === model.id;
const isFavorite = favoriteModels.includes(model.id);
// Get the appropriate icon based on provider
const ProviderIcon = (() => {
switch (model.provider) {
case 'opencode':
return OpenCodeIcon;
case 'amazon-bedrock-anthropic':
return AnthropicIcon;
case 'amazon-bedrock-deepseek':
return DeepSeekIcon;
case 'amazon-bedrock-amazon':
return NovaIcon;
case 'amazon-bedrock-meta':
return MetaIcon;
case 'amazon-bedrock-mistral':
return MistralIcon;
case 'amazon-bedrock-qwen':
return QwenIcon;
default:
return OpenCodeIcon;
}
})();
// Get the appropriate icon based on the specific model ID
const ProviderIcon = getProviderIconForModel(model.id);
return (
<CommandItem

View File

@@ -431,6 +431,16 @@ export function PromptCustomizationSection({
updatePrompt('enhancement', 'acceptanceSystemPrompt', value)
}
/>
<PromptField
label="User Experience Mode"
description="Review and enhance from a user experience and design perspective"
defaultValue={DEFAULT_ENHANCEMENT_PROMPTS.uxReviewerSystemPrompt}
customValue={promptCustomization?.enhancement?.uxReviewerSystemPrompt}
onCustomValueChange={(value) =>
updatePrompt('enhancement', 'uxReviewerSystemPrompt', value)
}
/>
</div>
</TabsContent>
</Tabs>

View File

@@ -19,6 +19,7 @@ import {
AnthropicIcon,
MistralIcon,
MetaIcon,
getProviderIconForModel,
} from '@/components/ui/provider-icon';
import type { ComponentType } from 'react';
@@ -31,27 +32,10 @@ interface OpencodeModelConfigurationProps {
}
/**
* Returns the appropriate icon component for a given OpenCode provider
* Returns the appropriate icon component for a given OpenCode model ID
*/
function getProviderIcon(provider: OpencodeProvider): ComponentType<{ className?: string }> {
switch (provider) {
case 'opencode':
return OpenCodeIcon;
case 'amazon-bedrock-anthropic':
return AnthropicIcon;
case 'amazon-bedrock-deepseek':
return DeepSeekIcon;
case 'amazon-bedrock-amazon':
return NovaIcon;
case 'amazon-bedrock-meta':
return MetaIcon;
case 'amazon-bedrock-mistral':
return MistralIcon;
case 'amazon-bedrock-qwen':
return QwenIcon;
default:
return OpenCodeIcon;
}
function getModelIcon(modelId: OpencodeModelId): ComponentType<{ className?: string }> {
return getProviderIconForModel(modelId);
}
/**
@@ -146,11 +130,11 @@ export function OpencodeModelConfiguration({
{enabledOpencodeModels.map((modelId) => {
const model = OPENCODE_MODEL_CONFIG_MAP[modelId];
if (!model) return null;
const ProviderIconComponent = getProviderIcon(model.provider);
const ModelIconComponent = getModelIcon(modelId);
return (
<SelectItem key={modelId} value={modelId}>
<div className="flex items-center gap-2">
<ProviderIconComponent className="w-4 h-4" />
<ModelIconComponent className="w-4 h-4" />
<span>{model.label}</span>
</div>
</SelectItem>
@@ -167,7 +151,9 @@ export function OpencodeModelConfiguration({
const models = modelsByProvider[provider];
if (!models || models.length === 0) return null;
const ProviderIconComponent = getProviderIcon(provider);
// Use the first model's icon as the provider icon
const ProviderIconComponent =
models.length > 0 ? getModelIcon(models[0].id) : OpenCodeIcon;
return (
<div key={provider} className="space-y-2">