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:
@@ -7,21 +7,9 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import {
|
||||
RiCheckLine,
|
||||
RiCheckboxMultipleLine,
|
||||
RiChatNewLine,
|
||||
RiEqualizer2Line,
|
||||
RiFolderAddLine,
|
||||
RiLayoutLeftLine,
|
||||
RiSearchLine,
|
||||
RiCloseLine,
|
||||
RiContractUpDownLine,
|
||||
RiExpandUpDownLine,
|
||||
RiCalendarScheduleLine,
|
||||
} from '@remixicon/react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ArrowsMerge } from '@/components/icons/ArrowsMerge';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useSessionDisplayStore } from '@/stores/useSessionDisplayStore';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -118,7 +106,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md typography-ui-label font-medium text-foreground transition-colors hover:bg-interactive-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:pointer-events-none disabled:opacity-50"
|
||||
aria-label={t('sessions.sidebar.header.actions.closeSessions')}
|
||||
>
|
||||
<RiLayoutLeftLine className="h-[18px] w-[18px]" />
|
||||
<Icon name="layout-left" className="h-[18px] w-[18px]" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.closeSessions')}</p></TooltipContent>
|
||||
@@ -132,7 +120,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className={headerActionButtonClass}
|
||||
aria-label={t('sessions.sidebar.header.actions.addProject')}
|
||||
>
|
||||
<RiFolderAddLine className={headerActionIconClass} />
|
||||
<Icon name="folder-add" className={headerActionIconClass} />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.addProject')}</p></TooltipContent>
|
||||
@@ -145,7 +133,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className={headerActionButtonClass}
|
||||
aria-label={t('sessions.sidebar.header.actions.newSession')}
|
||||
>
|
||||
<RiChatNewLine className={headerActionIconClass} />
|
||||
<Icon name="chat-new" className={headerActionIconClass} />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.newSession')}</p></TooltipContent>
|
||||
@@ -176,7 +164,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className={headerActionButtonClass}
|
||||
aria-label={t('sessions.sidebar.header.actions.scheduledTasks')}
|
||||
>
|
||||
<RiCalendarScheduleLine className={headerActionIconClass} />
|
||||
<Icon name="calendar-schedule" className={headerActionIconClass} />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.scheduledTasks')}</p></TooltipContent>
|
||||
@@ -191,7 +179,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
aria-label={t('sessions.sidebar.header.actions.searchSessions')}
|
||||
aria-expanded={isSessionSearchOpen}
|
||||
>
|
||||
<RiSearchLine className={headerActionIconClass} />
|
||||
<Icon name="search" className={headerActionIconClass} />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.searchSessions')}</p></TooltipContent>
|
||||
@@ -208,7 +196,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
: t('sessions.sidebar.header.actions.selectSessions')}
|
||||
aria-pressed={selectionModeEnabled}
|
||||
>
|
||||
<RiCheckboxMultipleLine className={headerActionIconClass} />
|
||||
<Icon name="checkbox-multiple" className={headerActionIconClass} />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" sideOffset={4}>
|
||||
@@ -227,7 +215,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className={headerActionButtonClass}
|
||||
aria-label={t('sessions.sidebar.header.actions.sessionDisplayMode')}
|
||||
>
|
||||
<RiEqualizer2Line className={headerActionIconClass} />
|
||||
<Icon name="equalizer-2" className={headerActionIconClass} />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
@@ -239,14 +227,14 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.default')}</span>
|
||||
{displayMode === 'default' ? <RiCheckLine className="h-4 w-4 text-primary" /> : null}
|
||||
{displayMode === 'default' ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => setDisplayMode('minimal')}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.minimal')}</span>
|
||||
{displayMode === 'minimal' ? <RiCheckLine className="h-4 w-4 text-primary" /> : null}
|
||||
{displayMode === 'minimal' ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
@@ -254,15 +242,15 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.showRecent')}</span>
|
||||
{showRecentSection ? <RiCheckLine className="h-4 w-4 text-primary" /> : null}
|
||||
{showRecentSection ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={collapseAllProjects} className="flex items-center gap-2">
|
||||
<RiContractUpDownLine className="h-4 w-4" />
|
||||
<Icon name="contract-up-down" className="h-4 w-4" />
|
||||
<span>{t('sessions.sidebar.header.displayMode.collapseAll')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={expandAllProjects} className="flex items-center gap-2">
|
||||
<RiExpandUpDownLine className="h-4 w-4" />
|
||||
<Icon name="expand-up-down" className="h-4 w-4" />
|
||||
<span>{t('sessions.sidebar.header.displayMode.expandAll')}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
@@ -281,7 +269,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
<span>{t('sessions.sidebar.header.search.escapeHint')}</span>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<RiSearchLine className="pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Icon name="search" className="pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<input
|
||||
ref={sessionSearchInputRef}
|
||||
value={sessionSearchQuery}
|
||||
@@ -306,7 +294,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
className="absolute right-1 top-1/2 inline-flex h-6 w-6 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground hover:bg-interactive-hover/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
|
||||
aria-label={t('sessions.sidebar.header.search.clear')}
|
||||
>
|
||||
<RiCloseLine className="h-3.5 w-3.5" />
|
||||
<Icon name="close" className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user