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:
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { RiFileCopyLine, RiCheckLine, RiExternalLinkLine, RiArrowDownSLine } from '@remixicon/react';
|
||||
import { isDesktopShell, isTauriShell, startDesktopWindowDrag } from '@/lib/desktop';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||
import { restartDesktopApp } from '@/lib/desktop';
|
||||
@@ -39,7 +39,7 @@ function BashCommand({ onCopy, copyTitle }: { onCopy: () => void; copyTitle: str
|
||||
title={copyTitle}
|
||||
aria-label={copyTitle}
|
||||
>
|
||||
<RiFileCopyLine className="h-4 w-4" />
|
||||
<Icon name="file-copy" className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -306,7 +306,7 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
|
||||
<div className="app-region-no-drag rounded-lg border border-border bg-background/60 backdrop-blur-sm px-4 py-3 font-mono text-sm">
|
||||
{copied ? (
|
||||
<div className="flex items-center gap-2" style={{ color: 'var(--status-success)' }}>
|
||||
<RiCheckLine className="h-4 w-4" />
|
||||
<Icon name="check" className="h-4 w-4" />
|
||||
{t('onboarding.common.status.copiedToClipboard')}
|
||||
</div>
|
||||
) : (
|
||||
@@ -322,7 +322,7 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
|
||||
className="text-xs text-muted-foreground hover:text-foreground transition-colors inline-flex items-center gap-1"
|
||||
>
|
||||
{platform === 'windows' ? t('onboarding.localSetup.docs.windows') : t('onboarding.localSetup.docs.default')}
|
||||
<RiExternalLinkLine className="h-3 w-3" />
|
||||
<Icon name="external-link" className="h-3 w-3" />
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
@@ -377,7 +377,7 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
|
||||
>
|
||||
<summary className="flex items-center justify-between cursor-pointer py-2.5 text-sm text-muted-foreground hover:text-foreground transition-colors list-none [&::-webkit-details-marker]:hidden">
|
||||
<span>{t('onboarding.localSetup.advanced.title')}</span>
|
||||
<RiArrowDownSLine className="h-4 w-4 transition-transform group-open:rotate-180" />
|
||||
<Icon name="arrow-down-s" className="h-4 w-4 transition-transform group-open:rotate-180" />
|
||||
</summary>
|
||||
<div className="pb-4 space-y-2">
|
||||
<div className="flex gap-2">
|
||||
@@ -419,7 +419,7 @@ export function ChooserScreen({ onCliAvailable }: ChooserScreenProps) {
|
||||
>
|
||||
<summary className="flex items-center justify-between cursor-pointer py-2.5 text-sm text-muted-foreground hover:text-foreground transition-colors list-none [&::-webkit-details-marker]:hidden">
|
||||
<span>{t('onboarding.localSetup.troubleshoot.title')}</span>
|
||||
<RiArrowDownSLine className="h-4 w-4 transition-transform group-open:rotate-180" />
|
||||
<Icon name="arrow-down-s" className="h-4 w-4 transition-transform group-open:rotate-180" />
|
||||
</summary>
|
||||
<ul className="pb-4 space-y-1.5 text-xs text-muted-foreground list-disc pl-4">
|
||||
{platform === 'windows' ? (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { RiRefreshLine, RiServerLine, RiMacbookLine } from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { redactSensitiveUrl } from '@/lib/desktopHosts';
|
||||
@@ -25,9 +25,9 @@ export type DesktopConnectionRecoveryProps = {
|
||||
function getRecoveryIcon(iconKey: 'local' | 'remote'): React.ReactNode {
|
||||
switch (iconKey) {
|
||||
case 'local':
|
||||
return <RiMacbookLine className="h-8 w-8" />;
|
||||
return <Icon name="macbook" className="h-8 w-8" />;
|
||||
case 'remote':
|
||||
return <RiServerLine className="h-8 w-8" />;
|
||||
return <Icon name="server" className="h-8 w-8" />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ export function DesktopConnectionRecovery({
|
||||
disabled={isRetrying}
|
||||
className="w-full"
|
||||
>
|
||||
<RiRefreshLine className={cn('h-4 w-4', isRetrying && 'animate-spin')} />
|
||||
<Icon name="refresh" className={cn('h-4 w-4', isRetrying && 'animate-spin')} />
|
||||
{isRetrying
|
||||
? t('onboarding.desktopRecovery.actions.retrying')
|
||||
: t(retryLabelKey)}
|
||||
@@ -114,7 +114,7 @@ export function DesktopConnectionRecovery({
|
||||
disabled={isRetrying}
|
||||
className="flex-1"
|
||||
>
|
||||
<RiMacbookLine className="h-4 w-4" />
|
||||
<Icon name="macbook" className="h-4 w-4" />
|
||||
{t(config.useLocalLabelKey as Parameters<typeof t>[0])}
|
||||
</Button>
|
||||
)}
|
||||
@@ -126,7 +126,7 @@ export function DesktopConnectionRecovery({
|
||||
disabled={isRetrying}
|
||||
className="flex-1"
|
||||
>
|
||||
<RiServerLine className="h-4 w-4" />
|
||||
<Icon name="server" className="h-4 w-4" />
|
||||
{t(config.useRemoteLabelKey as Parameters<typeof t>[0])}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { RiFileCopyLine, RiCheckLine, RiExternalLinkLine } from '@remixicon/react';
|
||||
import { isDesktopShell, isTauriShell } from '@/lib/desktop';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||
import { restartDesktopApp } from '@/lib/desktop';
|
||||
@@ -40,7 +40,7 @@ function BashCommand({ onCopy, copyTitle }: { onCopy: () => void; copyTitle: str
|
||||
className="inline-flex items-center text-muted-foreground hover:text-foreground transition-colors"
|
||||
title={copyTitle}
|
||||
>
|
||||
<RiFileCopyLine className="h-4 w-4" />
|
||||
<Icon name="file-copy" className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -264,7 +264,7 @@ export function LocalSetupScreen({
|
||||
<div className="bg-background/60 backdrop-blur-sm border border-border rounded-lg px-5 py-3 font-mono text-sm w-fit">
|
||||
{copied ? (
|
||||
<div className="flex items-center justify-center gap-2" style={{ color: 'var(--status-success)' }}>
|
||||
<RiCheckLine className="h-4 w-4" />
|
||||
<Icon name="check" className="h-4 w-4" />
|
||||
{t('onboarding.common.status.copiedToClipboard')}
|
||||
</div>
|
||||
) : (
|
||||
@@ -280,7 +280,7 @@ export function LocalSetupScreen({
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors inline-flex items-center gap-1 justify-center"
|
||||
>
|
||||
{platform === 'windows' ? t('onboarding.localSetup.docs.windows') : t('onboarding.localSetup.docs.default')}
|
||||
<RiExternalLinkLine className="h-3 w-3" />
|
||||
<Icon name="external-link" className="h-3 w-3" />
|
||||
</a>
|
||||
|
||||
{checkError && (
|
||||
|
||||
Reference in New Issue
Block a user