perf(ui): migrate icons to SVG sprite system
Replace @remixicon/react with a shared Icon component that renders via <use href> references to a single hidden SVG sprite. This reduces DOM node count by replacing inline SVGs with lightweight references. - Create Icon component with sprite injection (packages/ui/src/components/icon/) - Migrate all 164 files from @remixicon/react to Icon component - Auto-generate sprite data from remixicon bundle (scripts/generate-icon-sprite.mjs) - Add bun run icons:generate to package.json - Move @remixicon/react to devDependencies - Add icon usage instructions to theme-system skill
This commit is contained in:
@@ -10,7 +10,6 @@ import { useDirectorySync } from '@/sync/sync-context';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { opencodeClient } from '@/lib/opencode/client';
|
||||
import { RiCloseLine, RiInformationLine, RiRobot2Line, RiSubtractLine, RiUser3Line, RiFolderLine } from '@remixicon/react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ModelSelector } from './ModelSelector';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
|
||||
type PermissionAction = 'allow' | 'ask' | 'deny';
|
||||
type PermissionRule = { permission: string; pattern: string; action: PermissionAction };
|
||||
@@ -182,7 +182,6 @@ const buildPermissionConfigWithGlobal = (
|
||||
return result as AgentConfig['permission'];
|
||||
};
|
||||
|
||||
|
||||
export const AgentsPage: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const { isMobile } = useDeviceInfo();
|
||||
@@ -305,7 +304,6 @@ export const AgentsPage: React.FC = () => {
|
||||
currentRuleMap.get(buildRuleKey(permissionName, '*'))?.action
|
||||
), [currentRuleMap]);
|
||||
|
||||
|
||||
const getPatternRules = React.useCallback((permissionName: string): PermissionRule[] => (
|
||||
permissionRules
|
||||
.filter((rule) => rule.permission === permissionName && rule.pattern !== '*')
|
||||
@@ -323,7 +321,6 @@ export const AgentsPage: React.FC = () => {
|
||||
return Array.from(names).sort((a, b) => a.localeCompare(b));
|
||||
}, [knownPermissionNames]);
|
||||
|
||||
|
||||
const getPermissionSummary = React.useCallback((permissionName: string) => {
|
||||
const defaultAction = permissionName === '*'
|
||||
? globalPermission
|
||||
@@ -608,12 +605,11 @@ export const AgentsPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (!selectedAgentName) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="text-center text-muted-foreground">
|
||||
<RiRobot2Line className="mx-auto mb-3 h-12 w-12 opacity-50" />
|
||||
<Icon name="robot-2" className="mx-auto mb-3 h-12 w-12 opacity-50" />
|
||||
<p className="typography-body">{t('settings.agents.page.empty.title')}</p>
|
||||
<p className="typography-meta mt-1 opacity-75">{t('settings.agents.page.empty.description')}</p>
|
||||
</div>
|
||||
@@ -669,13 +665,13 @@ export const AgentsPage: React.FC = () => {
|
||||
<SelectContent align="end">
|
||||
<SelectItem value="user">
|
||||
<div className="flex items-center gap-2">
|
||||
<RiUser3Line className="h-3.5 w-3.5" />
|
||||
<Icon name="user-3" className="h-3.5 w-3.5" />
|
||||
<span>{t('settings.common.scope.global')}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="project">
|
||||
<div className="flex items-center gap-2">
|
||||
<RiFolderLine className="h-3.5 w-3.5" />
|
||||
<Icon name="folder" className="h-3.5 w-3.5" />
|
||||
<span>{t('settings.common.scope.project')}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
@@ -704,7 +700,7 @@ export const AgentsPage: React.FC = () => {
|
||||
<span className="typography-ui-label text-foreground">{t('settings.agents.page.field.mode')}</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
<Icon name="information" className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
{t('settings.agents.page.field.modeTooltip')}
|
||||
@@ -781,7 +777,7 @@ export const AgentsPage: React.FC = () => {
|
||||
<span className="typography-ui-label text-foreground">{t('settings.agents.page.field.temperature')}</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
<Icon name="information" className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
{t('settings.agents.page.field.temperatureTooltip')}
|
||||
@@ -813,7 +809,7 @@ export const AgentsPage: React.FC = () => {
|
||||
aria-label={t('settings.agents.page.field.clearTemperatureAria')}
|
||||
title={t('settings.common.actions.clear')}
|
||||
>
|
||||
<RiCloseLine className="h-3.5 w-3.5" />
|
||||
<Icon name="close" className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -825,7 +821,7 @@ export const AgentsPage: React.FC = () => {
|
||||
<span className="typography-ui-label text-foreground">{t('settings.agents.page.field.topP')}</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
<Icon name="information" className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
{t('settings.agents.page.field.topPTooltip')}
|
||||
@@ -857,7 +853,7 @@ export const AgentsPage: React.FC = () => {
|
||||
aria-label={t('settings.agents.page.field.clearTopPAria')}
|
||||
title={t('settings.common.actions.clear')}
|
||||
>
|
||||
<RiCloseLine className="h-3.5 w-3.5" />
|
||||
<Icon name="close" className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -981,7 +977,7 @@ export const AgentsPage: React.FC = () => {
|
||||
onClick={() => revertRule(permissionName, '*')}
|
||||
className="px-1.5 py-0 h-5"
|
||||
>
|
||||
<RiSubtractLine className="h-3.5 w-3.5 text-muted-foreground hover:text-foreground" />
|
||||
<Icon name="subtract" className="h-3.5 w-3.5 text-muted-foreground hover:text-foreground" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -1028,7 +1024,7 @@ export const AgentsPage: React.FC = () => {
|
||||
onClick={() => isAdded ? removeRule(rule.permission, rule.pattern) : revertRule(rule.permission, rule.pattern)}
|
||||
className="px-1.5 py-0 h-5"
|
||||
>
|
||||
<RiSubtractLine className="h-3.5 w-3.5 text-muted-foreground hover:text-foreground" />
|
||||
<Icon name="subtract" className="h-3.5 w-3.5 text-muted-foreground hover:text-foreground" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { RiAddLine, RiAiAgentFill, RiAiAgentLine, RiDeleteBinLine, RiFileCopyLine, RiMore2Line, RiRobot2Line, RiRobotLine, RiRestartLine, RiEditLine } from '@remixicon/react';
|
||||
import { useAgentsStore, isAgentBuiltIn, isAgentHidden, type AgentScope, type AgentDraft } from '@/stores/useAgentsStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -25,6 +24,7 @@ import type { Agent } from '@opencode-ai/sdk/v2';
|
||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
import { SettingsProjectSelector } from '@/components/sections/shared/SettingsProjectSelector';
|
||||
import { SidebarGroup } from '@/components/sections/shared/SidebarGroup';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
interface AgentsSidebarProps {
|
||||
@@ -228,7 +228,6 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
|
||||
});
|
||||
setSelectedAgent(newName);
|
||||
|
||||
|
||||
};
|
||||
|
||||
const handleOpenRenameDialog = (agent: Agent) => {
|
||||
@@ -293,11 +292,11 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
|
||||
const getAgentModeIcon = (mode?: string) => {
|
||||
switch (mode) {
|
||||
case 'primary':
|
||||
return <RiAiAgentLine className="h-3 w-3 text-primary" />;
|
||||
return <Icon name="ai-agent" className="h-3 w-3 text-primary" />;
|
||||
case 'all':
|
||||
return <RiAiAgentFill className="h-3 w-3 text-primary" />;
|
||||
return <Icon name="ai-agent-fill" className="h-3 w-3 text-primary" />;
|
||||
case 'subagent':
|
||||
return <RiRobotLine className="h-3 w-3 text-primary" />;
|
||||
return <Icon name="robot" className="h-3 w-3 text-primary" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -339,7 +338,7 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
|
||||
className="h-7 w-7 px-0 -my-1 text-muted-foreground"
|
||||
onClick={handleCreateNew}
|
||||
>
|
||||
<RiAddLine className="h-3.5 w-3.5" />
|
||||
<Icon name="add" className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -347,7 +346,7 @@ export const AgentsSidebar: React.FC<AgentsSidebarProps> = ({ onItemSelect }) =>
|
||||
<ScrollableOverlay outerClassName="flex-1 min-h-0" className="space-y-1 px-3 py-2 overflow-x-hidden">
|
||||
{visibleAgents.length === 0 ? (
|
||||
<div className="py-12 px-4 text-center text-muted-foreground">
|
||||
<RiRobot2Line className="mx-auto mb-3 h-10 w-10 opacity-50" />
|
||||
<Icon name="robot-2" className="mx-auto mb-3 h-10 w-10 opacity-50" />
|
||||
<p className="typography-ui-label font-medium">{t('settings.agents.sidebar.empty.title')}</p>
|
||||
<p className="typography-meta mt-1 opacity-75">{t('settings.agents.sidebar.empty.description')}</p>
|
||||
</div>
|
||||
@@ -580,7 +579,7 @@ const AgentListItem: React.FC<AgentListItemProps> = ({
|
||||
variant="ghost"
|
||||
className="h-6 w-6 px-0 flex-shrink-0 -mr-1 opacity-100 transition-opacity md:opacity-0 md:group-hover:opacity-100"
|
||||
>
|
||||
<RiMore2Line className="h-3.5 w-3.5" />
|
||||
<Icon name="more-2" className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-fit min-w-20">
|
||||
@@ -591,7 +590,7 @@ const AgentListItem: React.FC<AgentListItemProps> = ({
|
||||
onRename();
|
||||
}}
|
||||
>
|
||||
<RiEditLine className="h-4 w-4 mr-px" />
|
||||
<Icon name="edit" className="h-4 w-4 mr-px" />
|
||||
{t('settings.common.actions.rename')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
@@ -602,7 +601,7 @@ const AgentListItem: React.FC<AgentListItemProps> = ({
|
||||
onDuplicate();
|
||||
}}
|
||||
>
|
||||
<RiFileCopyLine className="h-4 w-4 mr-px" />
|
||||
<Icon name="file-copy" className="h-4 w-4 mr-px" />
|
||||
{t('settings.common.actions.duplicate')}
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -613,7 +612,7 @@ const AgentListItem: React.FC<AgentListItemProps> = ({
|
||||
onReset();
|
||||
}}
|
||||
>
|
||||
<RiRestartLine className="h-4 w-4 mr-px" />
|
||||
<Icon name="restart" className="h-4 w-4 mr-px" />
|
||||
{t('settings.common.actions.reset')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
@@ -626,7 +625,7 @@ const AgentListItem: React.FC<AgentListItemProps> = ({
|
||||
}}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<RiDeleteBinLine className="h-4 w-4 mr-px" />
|
||||
<Icon name="delete-bin" className="h-4 w-4 mr-px" />
|
||||
{t('settings.common.actions.delete')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
@@ -11,11 +11,11 @@ import { Input } from '@/components/ui/input';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { RiArrowDownSLine, RiArrowRightSLine, RiCheckLine, RiCloseLine, RiLoader4Line, RiPencilAiLine, RiSearchLine, RiStarFill, RiStarLine, RiTimeLine } from '@remixicon/react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel';
|
||||
import { ProviderLogo } from '@/components/ui/ProviderLogo';
|
||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useModelLists } from '@/hooks/useModelLists';
|
||||
import type { ModelMetadata } from '@/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
@@ -204,7 +204,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
</div>
|
||||
<div className="flex items-center gap-1 flex-shrink-0">
|
||||
{isSelected && (
|
||||
<RiCheckLine className="h-4 w-4 text-primary" />
|
||||
<Icon name="check" className="h-4 w-4 text-primary" />
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
@@ -220,9 +220,9 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
title={isFavorite ? t('settings.agents.modelSelector.actions.removeFromFavorites') : t('settings.agents.modelSelector.actions.addToFavorites')}
|
||||
>
|
||||
{isFavorite ? (
|
||||
<RiStarFill className="h-3.5 w-3.5" />
|
||||
<Icon name="star-fill" className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
<RiStarLine className="h-3.5 w-3.5" />
|
||||
<Icon name="star" className="h-3.5 w-3.5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
@@ -319,7 +319,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
className="model-favorite-button flex h-8 w-8 items-center justify-center text-primary hover:text-primary/80 active:scale-95 touch-manipulation"
|
||||
aria-label={t('settings.agents.modelSelector.actions.unfavorite')}
|
||||
>
|
||||
<RiStarFill className="h-4 w-4" />
|
||||
<Icon name="star-fill" className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -373,7 +373,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
className="model-favorite-button flex h-8 w-8 items-center justify-center text-muted-foreground/50 hover:text-primary/80 active:scale-95 touch-manipulation"
|
||||
aria-label={t('settings.agents.modelSelector.actions.favorite')}
|
||||
>
|
||||
<RiStarLine className="h-4 w-4" />
|
||||
<Icon name="star" className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -409,9 +409,9 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
)}
|
||||
</div>
|
||||
{isExpanded ? (
|
||||
<RiArrowDownSLine className="h-3 w-3 text-muted-foreground" />
|
||||
<Icon name="arrow-down-s" className="h-3 w-3 text-muted-foreground" />
|
||||
) : (
|
||||
<RiArrowRightSLine className="h-3 w-3 text-muted-foreground" />
|
||||
<Icon name="arrow-right-s" className="h-3 w-3 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
@@ -458,9 +458,9 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
: t('settings.agents.modelSelector.actions.favorite')}
|
||||
>
|
||||
{isFavoriteModel(provider.id as string, modelItem.id as string) ? (
|
||||
<RiStarFill className="h-4 w-4" />
|
||||
<Icon name="star-fill" className="h-4 w-4" />
|
||||
) : (
|
||||
<RiStarLine className="h-4 w-4" />
|
||||
<Icon name="star" className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
@@ -508,7 +508,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
<div className="flex items-center gap-2">
|
||||
{!isReady ? (
|
||||
<>
|
||||
<RiLoader4Line className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
|
||||
<Icon name="loader-4" className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
|
||||
<span className="typography-meta text-muted-foreground">{isUnavailable ? t('common.unavailable') : t('common.loading')}</span>
|
||||
</>
|
||||
) : providerId ? (
|
||||
@@ -517,7 +517,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
className="h-3.5 w-3.5"
|
||||
/>
|
||||
) : (
|
||||
<RiPencilAiLine className="h-3 w-3 text-muted-foreground" />
|
||||
<Icon name="pencil-ai" className="h-3 w-3 text-muted-foreground" />
|
||||
)}
|
||||
{isReady && (
|
||||
<span className="typography-meta font-medium text-foreground">
|
||||
@@ -525,7 +525,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<RiArrowDownSLine className="h-3 w-3 text-muted-foreground" />
|
||||
<Icon name="arrow-down-s" className="h-3 w-3 text-muted-foreground" />
|
||||
</button>
|
||||
) : (
|
||||
<DropdownMenu open={isReady && isDropdownOpen} onOpenChange={isReady ? setIsDropdownOpen : undefined}>
|
||||
@@ -536,7 +536,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
)}>
|
||||
{!isReady ? (
|
||||
<>
|
||||
<RiLoader4Line className="h-3.5 w-3.5 animate-spin text-muted-foreground flex-shrink-0" />
|
||||
<Icon name="loader-4" className="h-3.5 w-3.5 animate-spin text-muted-foreground flex-shrink-0" />
|
||||
<span className="typography-ui-label font-normal whitespace-nowrap text-muted-foreground">
|
||||
{isUnavailable ? t('common.unavailable') : t('common.loading')}
|
||||
</span>
|
||||
@@ -549,17 +549,17 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
providerId={providerId}
|
||||
className="h-3.5 w-3.5 flex-shrink-0"
|
||||
/>
|
||||
<RiPencilAiLine className="h-3 w-3 text-primary/60 hidden" />
|
||||
<Icon name="pencil-ai" className="h-3 w-3 text-primary/60 hidden" />
|
||||
</>
|
||||
) : (
|
||||
<RiPencilAiLine className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<Icon name="pencil-ai" className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
)}
|
||||
<span className="typography-ui-label font-normal whitespace-nowrap text-foreground">
|
||||
{providerId && modelId ? `${providerId}/${modelId}` : (placeholder || t('settings.agents.modelSelector.notSelected'))}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<RiArrowDownSLine className="h-4 w-4 flex-shrink-0 text-muted-foreground/50" />
|
||||
<Icon name="arrow-down-s" className="h-4 w-4 flex-shrink-0 text-muted-foreground/50" />
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-[min(380px,calc(100vw-2rem))] p-0 flex flex-col" align="start">
|
||||
@@ -619,7 +619,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
{/* Search Input */}
|
||||
<div className="p-2 border-b border-border/40">
|
||||
<div className="relative">
|
||||
<RiSearchLine className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
|
||||
<Icon name="search" className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t('settings.agents.modelSelector.searchPlaceholder')}
|
||||
@@ -642,10 +642,10 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
)}
|
||||
onSelect={() => handleProviderAndModelChange('', '')}
|
||||
>
|
||||
<RiCloseLine className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<Icon name="close" className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<span className="text-muted-foreground">{placeholder || t('settings.agents.modelSelector.notSelected')}</span>
|
||||
{!providerId && !modelId && (
|
||||
<RiCheckLine className="h-4 w-4 text-primary ml-auto" />
|
||||
<Icon name="check" className="h-4 w-4 text-primary ml-auto" />
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -661,7 +661,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
{filteredFavorites.length > 0 && (
|
||||
<div>
|
||||
<DropdownMenuLabel className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2 -mx-1 px-3 py-1.5 border-b border-border/30">
|
||||
<RiStarFill className="h-4 w-4 text-primary" />
|
||||
<Icon name="star-fill" className="h-4 w-4 text-primary" />
|
||||
{t('settings.agents.modelSelector.section.favorites')}
|
||||
</DropdownMenuLabel>
|
||||
{filteredFavorites.map(({ model, providerID, modelID }) => {
|
||||
@@ -676,7 +676,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
||||
<div>
|
||||
{filteredFavorites.length > 0 && <DropdownMenuSeparator />}
|
||||
<DropdownMenuLabel className="typography-micro font-semibold text-muted-foreground uppercase tracking-wider flex items-center gap-2 -mx-1 px-3 py-1.5 border-b border-border/30">
|
||||
<RiTimeLine className="h-4 w-4" />
|
||||
<Icon name="time" className="h-4 w-4" />
|
||||
{t('settings.agents.modelSelector.section.recent')}
|
||||
</DropdownMenuLabel>
|
||||
{filteredRecents.map(({ model, providerID, modelID }) => {
|
||||
|
||||
Reference in New Issue
Block a user