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,22 +10,6 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import {
|
||||
RiAddLine,
|
||||
RiArrowDownSLine,
|
||||
RiArrowRightLine,
|
||||
RiComputerLine,
|
||||
RiExternalLinkLine,
|
||||
RiFileCopyLine,
|
||||
RiInformationLine,
|
||||
RiPlug2Line,
|
||||
RiRefreshLine,
|
||||
RiServerLine,
|
||||
RiShuffleLine,
|
||||
RiTerminalWindowLine,
|
||||
RiDeleteBinLine,
|
||||
RiStopLine,
|
||||
} from '@remixicon/react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -39,6 +23,7 @@ import { SettingsPageLayout } from '@/components/sections/shared/SettingsPageLay
|
||||
import { useDesktopSshStore } from '@/stores/useDesktopSshStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||
import { openExternalUrl } from '@/lib/url';
|
||||
import { useI18n, type I18nKey } from '@/lib/i18n';
|
||||
@@ -152,7 +137,7 @@ const HintLabel: React.FC<{ label: string; hint: React.ReactNode }> = ({ label,
|
||||
<span>{label}</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">
|
||||
<div className="typography-meta text-foreground">{hint}</div>
|
||||
@@ -808,7 +793,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
onClick={handlePrimaryConnectionAction}
|
||||
disabled={isPrimaryActionPending || isRetryPending}
|
||||
>
|
||||
{canDisconnect ? <RiStopLine className="h-3.5 w-3.5" /> : <RiPlug2Line className="h-3.5 w-3.5" />}
|
||||
{canDisconnect ? <Icon name="stop" className="h-3.5 w-3.5" /> : <Icon name="plug-2" className="h-3.5 w-3.5" />}
|
||||
{primaryButtonLabel}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -819,7 +804,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
onClick={handleRetryAction}
|
||||
disabled={!canRetry}
|
||||
>
|
||||
<RiRefreshLine className={`h-3.5 w-3.5 ${isConnecting || (isReconnecting && !reconnectAppearsStuck) ? 'animate-spin' : ''}`} />
|
||||
<Icon name="refresh" className={`h-3.5 w-3.5 ${isConnecting || (isReconnecting && !reconnectAppearsStuck) ? 'animate-spin' : ''}`} />
|
||||
{retryButtonLabel}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -831,7 +816,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
void handleOpenLogs();
|
||||
}}
|
||||
>
|
||||
<RiTerminalWindowLine className="h-3.5 w-3.5" />
|
||||
<Icon name="terminal-window" className="h-3.5 w-3.5" />
|
||||
{t('settings.remoteInstances.page.actions.logs')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -854,7 +839,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<RiDeleteBinLine className="h-3.5 w-3.5" />
|
||||
<Icon name="delete-bin" className="h-3.5 w-3.5" />
|
||||
{t('settings.remoteInstances.sidebar.actions.remove')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -1148,7 +1133,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
}))
|
||||
}
|
||||
>
|
||||
<RiShuffleLine className="h-3.5 w-3.5" />
|
||||
<Icon name="shuffle" className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1265,7 +1250,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="min-w-0 flex items-center gap-2">
|
||||
<CollapsibleTrigger className="flex items-center gap-2 group">
|
||||
<RiArrowDownSLine className={`h-4 w-4 text-muted-foreground transition-transform ${isForwardOpen ? 'rotate-180' : ''}`} />
|
||||
<Icon name="arrow-down-s" className={`h-4 w-4 text-muted-foreground transition-transform ${isForwardOpen ? 'rotate-180' : ''}`} />
|
||||
<span className="typography-ui-label text-foreground truncate">{buildForwardLabel(forward)}</span>
|
||||
<span className="typography-micro text-muted-foreground/70 shrink-0">{typeLabel}</span>
|
||||
</CollapsibleTrigger>
|
||||
@@ -1284,7 +1269,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
}))
|
||||
}
|
||||
>
|
||||
<RiDeleteBinLine className="h-3.5 w-3.5" />
|
||||
<Icon name="delete-bin" className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1406,27 +1391,27 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
<div className="flex flex-wrap items-center gap-1 typography-micro text-muted-foreground/80">
|
||||
{forward.type === 'dynamic' ? (
|
||||
<>
|
||||
<RiComputerLine className="h-3.5 w-3.5" />
|
||||
<Icon name="computer" className="h-3.5 w-3.5" />
|
||||
<span className="font-mono text-foreground">{localEndpoint}</span>
|
||||
<span>{t('settings.remoteInstances.page.preview.localSocks5')}</span>
|
||||
</>
|
||||
) : forward.type === 'remote' ? (
|
||||
<>
|
||||
<RiServerLine className="h-3.5 w-3.5" />
|
||||
<Icon name="server" className="h-3.5 w-3.5" />
|
||||
<span className="font-mono text-foreground">{remoteEndpoint}</span>
|
||||
<span>{t('settings.remoteInstances.page.preview.remote')}</span>
|
||||
<RiArrowRightLine className="h-3.5 w-3.5" />
|
||||
<RiComputerLine className="h-3.5 w-3.5" />
|
||||
<Icon name="arrow-right" className="h-3.5 w-3.5" />
|
||||
<Icon name="computer" className="h-3.5 w-3.5" />
|
||||
<span className="font-mono text-foreground">{localEndpoint}</span>
|
||||
<span>{t('settings.remoteInstances.page.preview.local')}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RiComputerLine className="h-3.5 w-3.5" />
|
||||
<Icon name="computer" className="h-3.5 w-3.5" />
|
||||
<span className="font-mono text-foreground">{localEndpoint}</span>
|
||||
<span>{t('settings.remoteInstances.page.preview.local')}</span>
|
||||
<RiArrowRightLine className="h-3.5 w-3.5" />
|
||||
<RiServerLine className="h-3.5 w-3.5" />
|
||||
<Icon name="arrow-right" className="h-3.5 w-3.5" />
|
||||
<Icon name="server" className="h-3.5 w-3.5" />
|
||||
<span className="font-mono text-foreground">{remoteEndpoint}</span>
|
||||
<span>{t('settings.remoteInstances.page.preview.remote')}</span>
|
||||
</>
|
||||
@@ -1447,7 +1432,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<RiExternalLinkLine className="h-3.5 w-3.5" />
|
||||
<Icon name="external-link" className="h-3.5 w-3.5" />
|
||||
{t('settings.remoteInstances.page.actions.openLocal')}
|
||||
</Button>
|
||||
) : null}
|
||||
@@ -1475,7 +1460,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
}));
|
||||
}}
|
||||
>
|
||||
<RiAddLine className="h-3.5 w-3.5" />
|
||||
<Icon name="add" className="h-3.5 w-3.5" />
|
||||
{t('settings.remoteInstances.page.actions.addForward')}
|
||||
</Button>
|
||||
</section>
|
||||
@@ -1540,7 +1525,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<RiFileCopyLine className="h-3.5 w-3.5" />
|
||||
<Icon name="file-copy" className="h-3.5 w-3.5" />
|
||||
{t('settings.remoteInstances.page.actions.copyLocalUrl')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -1552,7 +1537,7 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
void handleOpenCurrentInstance();
|
||||
}}
|
||||
>
|
||||
<RiExternalLinkLine className="h-3.5 w-3.5" />
|
||||
<Icon name="external-link" className="h-3.5 w-3.5" />
|
||||
{t('settings.remoteInstances.page.actions.open')}
|
||||
</Button>
|
||||
</>
|
||||
@@ -1571,11 +1556,11 @@ export const RemoteInstancesPage: React.FC = () => {
|
||||
</DialogHeader>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button type="button" variant="outline" size="xs" className="!font-normal" onClick={handleCopyAllLogs} disabled={logDialogLoading || !logLinesText.trim()}>
|
||||
<RiFileCopyLine className="h-3.5 w-3.5" />
|
||||
<Icon name="file-copy" className="h-3.5 w-3.5" />
|
||||
{t('settings.common.actions.copyAll')}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="xs" className="!font-normal" onClick={() => void handleClearLogs()} disabled={logDialogLoading}>
|
||||
<RiDeleteBinLine className="h-3.5 w-3.5" />
|
||||
<Icon name="delete-bin" className="h-3.5 w-3.5" />
|
||||
{t('settings.common.actions.clear')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { RiAddLine, RiDeleteBinLine, RiPlug2Line, RiRefreshLine, RiStopLine } from '@remixicon/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SettingsSidebarLayout } from '@/components/sections/shared/SettingsSidebarLayout';
|
||||
import { SettingsSidebarItem } from '@/components/sections/shared/SettingsSidebarItem';
|
||||
import { useDesktopSshStore } from '@/stores/useDesktopSshStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import type { DesktopSshInstance } from '@/lib/desktopSsh';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -145,7 +145,7 @@ export const RemoteInstancesSidebar: React.FC<RemoteInstancesSidebarProps> = ({
|
||||
onClick={() => void handleAdd()}
|
||||
aria-label={t('settings.remoteInstances.sidebar.actions.addSshInstance')}
|
||||
>
|
||||
<RiAddLine className="size-4" />
|
||||
<Icon name="add" className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -172,7 +172,7 @@ export const RemoteInstancesSidebar: React.FC<RemoteInstancesSidebarProps> = ({
|
||||
actions={[
|
||||
{
|
||||
label: isReady ? t('settings.remoteInstances.sidebar.actions.disconnect') : t('settings.remoteInstances.sidebar.actions.connect'),
|
||||
icon: isReady ? RiStopLine : RiPlug2Line,
|
||||
icon: isReady ? 'stop' : 'plug-2',
|
||||
onClick: () => {
|
||||
const op = isReady ? disconnect(instance.id) : connectWithPortRecovery(instance);
|
||||
void op.catch((error) => {
|
||||
@@ -189,7 +189,7 @@ export const RemoteInstancesSidebar: React.FC<RemoteInstancesSidebarProps> = ({
|
||||
},
|
||||
{
|
||||
label: t('settings.remoteInstances.sidebar.actions.retry'),
|
||||
icon: RiRefreshLine,
|
||||
icon: "refresh",
|
||||
onClick: () => {
|
||||
if (!canRetry) return;
|
||||
void retry(instance.id).catch((error) => {
|
||||
@@ -201,7 +201,7 @@ export const RemoteInstancesSidebar: React.FC<RemoteInstancesSidebarProps> = ({
|
||||
},
|
||||
{
|
||||
label: t('settings.remoteInstances.sidebar.actions.remove'),
|
||||
icon: RiDeleteBinLine,
|
||||
icon: "delete-bin",
|
||||
destructive: true,
|
||||
onClick: () => {
|
||||
void removeInstance(instance.id).then(() => {
|
||||
|
||||
Reference in New Issue
Block a user