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:
Bohdan Triapitsyn
2026-05-13 13:26:35 +03:00
parent b4cbd7f0d6
commit 14357257ae
173 changed files with 2342 additions and 2227 deletions
@@ -23,6 +23,7 @@ import { useI18n } from '@/lib/i18n';
import { ProviderLogo } from '@/components/ui/ProviderLogo';
import { UsageProgressBar } from '@/components/sections/usage/UsageProgressBar';
import { PaceIndicator } from '@/components/sections/usage/PaceIndicator';
import { Icon } from "@/components/icon/Icon";
import { formatQuotaValueLabel, formatWindowLabel, QUOTA_PROVIDERS, calculatePace, calculateExpectedUsagePercent } from '@/lib/quota';
import { useQuotaAutoRefresh, useQuotaStore } from '@/stores/useQuotaStore';
import { useUpdateStore } from '@/stores/useUpdateStore';
@@ -30,7 +31,6 @@ import { updateDesktopSettings } from '@/lib/persistence';
import { lazyWithChunkRecovery } from '@/lib/chunkLoadRecovery';
import type { UsageWindow } from '@/types';
import type { SessionContextUsage } from '@/stores/types/sessionTypes';
import { RiAddLine, RiArrowLeftLine, RiRefreshLine, RiRobot2Line, RiSettings3Line, RiTimerLine } from '@remixicon/react';
const SettingsView = lazyWithChunkRecovery(() => import('@/components/views/SettingsView').then(m => ({ default: m.SettingsView })));
@@ -220,7 +220,6 @@ export const VSCodeLayout: React.FC = () => {
setCurrentView('sessions');
}, []);
// Listen for connection status changes
React.useEffect(() => {
// Catch up with the latest status even if the extension posted the connection message
@@ -695,7 +694,7 @@ const VSCodeHeader: React.FC<VSCodeHeaderProps> = ({ title, showBack, onBack, on
className="inline-flex h-7 w-7 items-center justify-center text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
aria-label={t('vscodeLayout.actions.backToSessionsAria')}
>
<RiArrowLeftLine className="h-5 w-5" />
<Icon name="arrow-left" className="h-5 w-5" />
</button>
)}
<h1 className="text-sm font-medium truncate flex-1" title={title}>{title}</h1>
@@ -705,7 +704,7 @@ const VSCodeHeader: React.FC<VSCodeHeaderProps> = ({ title, showBack, onBack, on
className="inline-flex h-9 w-9 items-center justify-center p-2 text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
aria-label={t('vscodeLayout.actions.newSessionAria')}
>
<RiAddLine className="h-5 w-5" />
<Icon name="add" className="h-5 w-5" />
</button>
)}
{onAgentManager && (
@@ -714,7 +713,7 @@ const VSCodeHeader: React.FC<VSCodeHeaderProps> = ({ title, showBack, onBack, on
className="inline-flex h-9 w-9 items-center justify-center p-2 text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
aria-label={t('vscodeLayout.actions.openAgentManagerAria')}
>
<RiRobot2Line className="h-5 w-5" />
<Icon name="robot-2" className="h-5 w-5" />
</button>
)}
{showMcp && (
@@ -737,7 +736,7 @@ const VSCodeHeader: React.FC<VSCodeHeaderProps> = ({ title, showBack, onBack, on
className="inline-flex h-9 w-9 items-center justify-center p-2 text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
disabled={isQuotaLoading}
>
<RiTimerLine className="h-5 w-5" />
<Icon name="timer" className="h-5 w-5" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
@@ -785,7 +784,7 @@ const VSCodeHeader: React.FC<VSCodeHeaderProps> = ({ title, showBack, onBack, on
disabled={isQuotaLoading}
aria-label={t('vscodeLayout.quota.actions.refreshAria')}
>
<RiRefreshLine className="h-4 w-4" />
<Icon name="refresh" className="h-4 w-4" />
</button>
</div>
</DropdownMenuLabel>
@@ -870,7 +869,7 @@ const VSCodeHeader: React.FC<VSCodeHeaderProps> = ({ title, showBack, onBack, on
className="inline-flex h-9 w-9 items-center justify-center p-2 text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
aria-label={t('vscodeLayout.actions.settingsAria')}
>
<RiSettings3Line className="h-5 w-5" />
<Icon name="settings-3" className="h-5 w-5" />
</button>
)}
{showContextUsage && stableContextUsage && stableContextUsage.totalTokens > 0 && (