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
@@ -20,7 +20,6 @@ import { useThemeSystem } from '@/contexts/useThemeSystem';
import { generateSyntaxTheme } from '@/lib/theme/syntaxThemeGenerator';
import { createFlexokiCodeMirrorTheme } from '@/lib/codemirror/flexokiTheme';
import { languageByExtension } from '@/lib/codemirror/languageByExtension';
import { RiCheckLine, RiClipboardLine, RiCodeAiLine, RiLoopRightAiLine } from '@remixicon/react';
import { useSessionUIStore } from '@/sync/session-ui-store';
import { useSessions } from '@/sync/sync-context';
import { useDirectoryStore } from '@/stores/useDirectoryStore';
@@ -39,6 +38,7 @@ import { generateBranchName } from '@/lib/git/branchNameGenerator';
import { parseProjectPlanMarkdown } from '@/lib/openchamberConfig';
import { createWorktreeSessionForNewBranch } from '@/lib/worktreeSessionCreator';
import { TodoSendDialog, type TodoSendExecution } from '@/components/session/TodoSendDialog';
import { Icon } from "@/components/icon/Icon";
import { renderMagicPrompt } from '@/lib/magicPrompts';
import { useI18n } from '@/lib/i18n';
@@ -338,7 +338,6 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
};
}, [cancel, editingDraftId, isMobile, lineSelection]);
const editorExtensions = React.useMemo(() => {
const extensions = [createFlexokiCodeMirrorTheme(currentTheme)];
const language = languageByExtension(resolvedPath || 'plan.md');
@@ -638,7 +637,7 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
aria-label={t('planView.actions.improvePlanAria')}
disabled={!content.trim()}
>
<RiLoopRightAiLine className="size-4" />
<Icon name="loop-right-ai" className="size-4" />
</Button>
</DropdownMenuTrigger>
</TooltipTrigger>
@@ -667,7 +666,7 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
aria-label={t('planView.actions.implementPlanAria')}
disabled={!content.trim()}
>
<RiCodeAiLine className="size-4" />
<Icon name="code-ai" className="size-4" />
</Button>
</DropdownMenuTrigger>
</TooltipTrigger>
@@ -711,9 +710,9 @@ export const PlanView: React.FC<PlanViewProps> = ({ targetPath = null }) => {
aria-label={t('planView.actions.copyPlanContents')}
>
{copiedContent ? (
<RiCheckLine className="h-4 w-4 text-[color:var(--status-success)]" />
<Icon name="check" className="h-4 w-4 text-[color:var(--status-success)]" />
) : (
<RiClipboardLine className="h-4 w-4" />
<Icon name="clipboard" className="h-4 w-4" />
)}
</Button>
</div>