feat: migrate icon library from Hugeicons to Phosphor + UI polish
Icon library migration: - Replace @hugeicons/react with @phosphor-icons/react across 60+ components (Phosphor supports 6 weight variants: thin/light/regular/bold/fill/duotone) - Update apps/site (marketing) to use Phosphor icons - Remove leftover svg.hugeicons CSS rules from globals.css UI improvements: - NavRail: selected tab icons use filled weight, unselected use regular - User chat bubble: dark bg + white text in light mode, light bg + dark text in dark mode (new --user-bubble / --user-bubble-foreground CSS variables) - Fix expand/collapse icons: use CaretRight (triangle) instead of ArrowRight in file-tree.tsx and tool-actions-group.tsx Lint fixes: - Fix eslint react-hooks/static-components in CodeBlock, ToolCallBlock, tool-actions-group — use createElement() instead of PascalCase variable - Fix react-hooks/rules-of-hooks in MessageItem — move early return after hooks - Fix react-hooks/set-state-in-effect in GalleryDetail — use ref pattern Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
5adeb3548e
commit
e5a48ca6af
61 files changed
+606
-752
No files matched your search
@@ -8,14 +8,13 @@ import {
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import {
|
||||
Folder01Icon,
|
||||
FolderOpenIcon,
|
||||
File01Icon,
|
||||
ArrowRight01Icon,
|
||||
PlusSignIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
Folder,
|
||||
FolderOpen,
|
||||
File,
|
||||
CaretRight,
|
||||
Plus,
|
||||
} from "@phosphor-icons/react";
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
@@ -155,10 +154,10 @@ export const FileTreeFolder = ({
|
||||
className="shrink-0 rounded p-0.5 hover:bg-muted"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<HugeiconsIcon
|
||||
icon={ArrowRight01Icon}
|
||||
<CaretRight
|
||||
size={16}
|
||||
className={cn(
|
||||
"size-4 text-muted-foreground transition-transform",
|
||||
"text-muted-foreground transition-transform",
|
||||
isExpanded && "rotate-90"
|
||||
)}
|
||||
/>
|
||||
@@ -166,9 +165,9 @@ export const FileTreeFolder = ({
|
||||
</CollapsibleTrigger>
|
||||
<FileTreeIcon>
|
||||
{isExpanded ? (
|
||||
<HugeiconsIcon icon={FolderOpenIcon} className="size-4 text-muted-foreground" />
|
||||
<FolderOpen size={16} className="text-muted-foreground" />
|
||||
) : (
|
||||
<HugeiconsIcon icon={Folder01Icon} className="size-4 text-muted-foreground" />
|
||||
<Folder size={16} className="text-muted-foreground" />
|
||||
)}
|
||||
</FileTreeIcon>
|
||||
<FileTreeName>{name}</FileTreeName>
|
||||
@@ -249,7 +248,7 @@ export const FileTreeFile = ({
|
||||
{children ?? (
|
||||
<>
|
||||
<FileTreeIcon>
|
||||
{icon ?? <HugeiconsIcon icon={File01Icon} className="size-4 text-muted-foreground" />}
|
||||
{icon ?? <File size={16} className="text-muted-foreground" />}
|
||||
</FileTreeIcon>
|
||||
<FileTreeName>{name}</FileTreeName>
|
||||
{onAdd && (
|
||||
@@ -259,7 +258,7 @@ export const FileTreeFile = ({
|
||||
onClick={handleAdd}
|
||||
title="Add to chat"
|
||||
>
|
||||
<HugeiconsIcon icon={PlusSignIcon} className="size-3 text-muted-foreground" />
|
||||
<Plus size={12} className="text-muted-foreground" />
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -56,7 +56,7 @@ export const MessageContent = ({
|
||||
<div
|
||||
className={cn(
|
||||
"is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",
|
||||
"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
|
||||
"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-[var(--user-bubble)] group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-[var(--user-bubble-foreground)]",
|
||||
"group-[.is-assistant]:w-full group-[.is-assistant]:text-foreground",
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, createElement } from 'react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import type { IconSvgElement } from "@hugeicons/react";
|
||||
import type { Icon } from "@phosphor-icons/react";
|
||||
import {
|
||||
File01Icon,
|
||||
FileEditIcon,
|
||||
CommandLineIcon,
|
||||
Search01Icon,
|
||||
Wrench01Icon,
|
||||
Loading02Icon,
|
||||
CheckmarkCircle02Icon,
|
||||
CancelCircleIcon,
|
||||
ArrowRight01Icon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
File,
|
||||
NotePencil,
|
||||
Terminal,
|
||||
MagnifyingGlass,
|
||||
Wrench,
|
||||
SpinnerGap,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
CaretRight,
|
||||
} from "@phosphor-icons/react";
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -61,13 +60,13 @@ function getToolCategory(name: string): ToolCategory {
|
||||
return 'other';
|
||||
}
|
||||
|
||||
function getToolIcon(category: ToolCategory): IconSvgElement {
|
||||
function getToolIcon(category: ToolCategory): Icon {
|
||||
switch (category) {
|
||||
case 'read': return File01Icon;
|
||||
case 'write': return FileEditIcon;
|
||||
case 'bash': return CommandLineIcon;
|
||||
case 'search': return Search01Icon;
|
||||
case 'other': return Wrench01Icon;
|
||||
case 'read': return File;
|
||||
case 'write': return NotePencil;
|
||||
case 'bash': return Terminal;
|
||||
case 'search': return MagnifyingGlass;
|
||||
case 'other': return Wrench;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,12 +129,12 @@ function StatusDot({ status }: { status: ToolStatus }) {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return (
|
||||
<HugeiconsIcon icon={Loading02Icon} className="h-3.5 w-3.5 shrink-0 animate-spin text-muted-foreground/50" />
|
||||
<SpinnerGap size={14} className="shrink-0 animate-spin text-muted-foreground/50" />
|
||||
);
|
||||
case 'success':
|
||||
return <HugeiconsIcon icon={CheckmarkCircle02Icon} className="h-3.5 w-3.5 shrink-0 text-green-500" />;
|
||||
return <CheckCircle size={14} className="shrink-0 text-green-500" />;
|
||||
case 'error':
|
||||
return <HugeiconsIcon icon={CancelCircleIcon} className="h-3.5 w-3.5 shrink-0 text-red-500" />;
|
||||
return <XCircle size={14} className="shrink-0 text-red-500" />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +144,7 @@ function StatusDot({ status }: { status: ToolStatus }) {
|
||||
|
||||
function ToolActionRow({ tool }: { tool: ToolAction }) {
|
||||
const category = getToolCategory(tool.name);
|
||||
const icon = getToolIcon(category);
|
||||
const toolIcon = getToolIcon(category);
|
||||
const summary = getToolSummary(tool.name, tool.input, category);
|
||||
const filePath = getFilePath(tool.input);
|
||||
const status = getStatus(tool);
|
||||
@@ -154,7 +153,7 @@ function ToolActionRow({ tool }: { tool: ToolAction }) {
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 px-2 py-1 min-h-[28px] text-xs hover:bg-muted/30 rounded-sm transition-colors">
|
||||
<HugeiconsIcon icon={icon} className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
{createElement(toolIcon, { size: 14, className: "shrink-0 text-muted-foreground" })}
|
||||
|
||||
{label && (
|
||||
<span className="font-medium text-muted-foreground shrink-0">{label}</span>
|
||||
@@ -229,10 +228,10 @@ export function ToolActionsGroup({
|
||||
onClick={handleToggle}
|
||||
className="flex w-full items-center gap-2 py-1 text-xs rounded-sm hover:bg-muted/30 transition-colors"
|
||||
>
|
||||
<HugeiconsIcon
|
||||
icon={ArrowRight01Icon}
|
||||
<CaretRight
|
||||
size={12}
|
||||
className={cn(
|
||||
"h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform duration-200",
|
||||
"shrink-0 text-muted-foreground/60 transition-transform duration-200",
|
||||
expanded && "rotate-90"
|
||||
)}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user