diff --git a/apps/site/package.json b/apps/site/package.json index 11ef729..420ae93 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -11,8 +11,7 @@ }, "dependencies": { "@base-ui/react": "^1.2.0", - "@hugeicons/core-free-icons": "^4.0.0", - "@hugeicons/react": "^1.1.6", + "@phosphor-icons/react": "^2.1.7", "@types/mdx": "^2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/apps/site/src/components/marketing/ChatDemo.tsx b/apps/site/src/components/marketing/ChatDemo.tsx index 1490cbc..c5b7e7d 100644 --- a/apps/site/src/components/marketing/ChatDemo.tsx +++ b/apps/site/src/components/marketing/ChatDemo.tsx @@ -2,23 +2,22 @@ import { useEffect, useRef, useState } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; -import { HugeiconsIcon } from '@hugeicons/react'; import { - Message02Icon, - ZapIcon, - Plug01Icon, - Image01Icon, - Wifi01Icon, - Settings02Icon, - Search01Icon, - PlusSignIcon, - FolderOpenIcon, - ArrowUp02Icon, - ArrowDown01Icon, - CommandLineIcon, - AiUserIcon, - SecurityCheckIcon, -} from '@hugeicons/core-free-icons'; + ChatCircle, + Lightning, + Plug, + Image, + WifiHigh, + Gear, + MagnifyingGlass, + Plus, + FolderOpen, + ArrowUp, + CaretDown, + Terminal, + UserCircle, + ShieldCheck, +} from '@phosphor-icons/react'; import { Check, FileText } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -135,10 +134,10 @@ const DEMO_THEME_VARS = { function BadgeIcon({ type }: { type: string }) { const cls = 'h-3 w-3'; switch (type) { - case 'skill': return ; - case 'mcp': return ; - case 'bridge': return ; - case 'agent': return ; + case 'skill': return ; + case 'mcp': return ; + case 'bridge': return ; + case 'agent': return ; default: return null; } } @@ -172,11 +171,11 @@ function TypingDots() { /* ------------------------------------------------------------------ */ const NAV_ITEMS = [ - { icon: Message02Icon, label: 'Chats', active: true }, - { icon: ZapIcon, label: 'Skills', active: false }, - { icon: Plug01Icon, label: 'MCP', active: false }, - { icon: Image01Icon, label: 'Gallery', active: false }, - { icon: Wifi01Icon, label: 'Bridge', active: false }, + { icon: ChatCircle, label: 'Chats', active: true }, + { icon: Lightning, label: 'Skills', active: false }, + { icon: Plug, label: 'MCP', active: false }, + { icon: Image, label: 'Gallery', active: false }, + { icon: WifiHigh, label: 'Bridge', active: false }, ]; function IconSidebar() { @@ -194,14 +193,14 @@ function IconSidebar() { : 'text-muted-foreground/50' }`} > - + ))}
@@ -223,20 +222,20 @@ function SessionSidebar() { {/* New chat button — real: h-8 outline with PlusSignIcon */}
{/* Search */}
- +
{/* Project folder — real: FolderOpenIcon, text-[13px] font-medium */}
- + My-Project
@@ -465,21 +464,21 @@ export function ChatDemo() {
Message Claude...
{/* PromptInputFooter — real: gap-1, icon buttons h-6 w-6 */}
claude-sonnet-4 - +
@@ -487,11 +486,11 @@ export function ChatDemo() { {/* ChatComposerActionBar — below input */}
- + Design Agent - + Default
diff --git a/package-lock.json b/package-lock.json index 45d2351..26794b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,10 +19,9 @@ "@ai-sdk/openai": "^3.0.34", "@anthropic-ai/claude-agent-sdk": "^0.2.62", "@google/genai": "^1.43.0", - "@hugeicons/core-free-icons": "^3.1.1", - "@hugeicons/react": "^1.1.4", "@larksuiteoapi/node-sdk": "^1.59.0", "@lobehub/icons": "^4.6.0", + "@phosphor-icons/react": "^2.1.10", "@radix-ui/react-use-controllable-state": "^1.2.2", "@streamdown/cjk": "^1.0.1", "@streamdown/code": "^1.0.1", @@ -89,8 +88,7 @@ "version": "0.1.0", "dependencies": { "@base-ui/react": "^1.2.0", - "@hugeicons/core-free-icons": "^4.0.0", - "@hugeicons/react": "^1.1.6", + "@phosphor-icons/react": "^2.1.7", "@types/mdx": "^2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -585,12 +583,6 @@ "node": ">=18" } }, - "apps/site/node_modules/@hugeicons/core-free-icons": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-4.0.0.tgz", - "integrity": "sha512-bzfbKumv3ke3ajbe2MyXi9i0I/cdsZ6n/mO9EfIPNSL++pHLqs7nSGRIVUtjF4xrrEyVkfhxssv4Jek8DPA6gA==", - "license": "MIT" - }, "apps/site/node_modules/@next/env": { "version": "15.3.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.6.tgz", @@ -3910,21 +3902,6 @@ "hono": "^4" } }, - "node_modules/@hugeicons/core-free-icons": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-3.1.1.tgz", - "integrity": "sha512-UpS2lUQFi5sKyJSWwM6rO+BnPLvVz1gsyCpPHeZyVuZqi89YH8ksliza4cwaODqKOZyeXmG8juo1ty4QtQofkg==", - "license": "MIT" - }, - "node_modules/@hugeicons/react": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@hugeicons/react/-/react-1.1.6.tgz", - "integrity": "sha512-c2LhXJMAW5wN1pC/smBXG0YPqUON6ceR/ZdXHCjEI9KvB+hjtqYjmzIxok5hAQOeXGz0WtORgCQMzqewFKAZwg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.0.0" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -5551,6 +5528,19 @@ "node": ">= 20.0.0" } }, + "node_modules/@phosphor-icons/react": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz", + "integrity": "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">= 16.8", + "react-dom": ">= 16.8" + } + }, "node_modules/@pierre/diffs": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@pierre/diffs/-/diffs-1.0.11.tgz", diff --git a/package.json b/package.json index 83a2b56..712e382 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,10 @@ "name": "codepilot", "version": "0.31.0", "private": true, - "workspaces": ["apps/*", "packages/*"], + "workspaces": [ + "apps/*", + "packages/*" + ], "author": { "name": "op7418", "email": "7418@openclaw.ai" @@ -40,10 +43,9 @@ "@ai-sdk/openai": "^3.0.34", "@anthropic-ai/claude-agent-sdk": "^0.2.62", "@google/genai": "^1.43.0", - "@hugeicons/core-free-icons": "^3.1.1", - "@hugeicons/react": "^1.1.4", "@larksuiteoapi/node-sdk": "^1.59.0", "@lobehub/icons": "^4.6.0", + "@phosphor-icons/react": "^2.1.10", "@radix-ui/react-use-controllable-state": "^1.2.2", "@streamdown/cjk": "^1.0.1", "@streamdown/code": "^1.0.1", diff --git a/src/app/bridge/page.tsx b/src/app/bridge/page.tsx index fe8968c..444ce9e 100644 --- a/src/app/bridge/page.tsx +++ b/src/app/bridge/page.tsx @@ -1,8 +1,7 @@ "use client"; import { Suspense } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap } from "@phosphor-icons/react"; import { BridgeLayout } from "@/components/bridge/BridgeLayout"; export default function BridgePage() { @@ -10,7 +9,7 @@ export default function BridgePage() { - +
} > diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 32464ae..328ce5b 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -4,8 +4,7 @@ import { useEffect, useState, useRef, useCallback, use } from 'react'; import Link from 'next/link'; import type { Message, MessagesResponse, ChatSession } from '@/types'; import { ChatView } from '@/components/chat/ChatView'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon, PencilEdit01Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap, PencilSimple } from "@phosphor-icons/react"; import { Input } from '@/components/ui/input'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { usePanel } from '@/hooks/usePanel'; @@ -156,7 +155,7 @@ export default function ChatSessionPage({ params }: ChatSessionPageProps) { if (loading) { return (
- +
); } @@ -237,7 +236,7 @@ export default function ChatSessionPage({ params }: ChatSessionPageProps) { onClick={handleStartEditTitle} className="opacity-0 group-hover:opacity-100 transition-opacity shrink-0 p-0.5 rounded hover:bg-muted" > - + )} diff --git a/src/app/gallery/page.tsx b/src/app/gallery/page.tsx index ff3f818..d1b68ab 100644 --- a/src/app/gallery/page.tsx +++ b/src/app/gallery/page.tsx @@ -1,14 +1,7 @@ 'use client'; import { useState, useEffect, useCallback, useRef } from 'react'; -import { HugeiconsIcon } from '@hugeicons/react'; -import { - PaintBrush01Icon, - SortingDownIcon, - FilterIcon, - Loading02Icon, - FavouriteIcon, -} from '@hugeicons/core-free-icons'; +import { PaintBrush, SortDescending, Funnel, SpinnerGap, Heart } from '@phosphor-icons/react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { GalleryGrid, type GalleryItem } from '@/components/gallery/GalleryGrid'; @@ -153,10 +146,10 @@ export default function GalleryPage() { size="sm" onClick={() => setFavoritesOnly((v) => !v)} > - {t('gallery.favoritesOnly' as TranslationKey)} @@ -167,7 +160,7 @@ export default function GalleryPage() { size="sm" onClick={() => setShowFilters(!showFilters)} > - + {t('gallery.filters' as TranslationKey)} @@ -177,7 +170,7 @@ export default function GalleryPage() { size="sm" onClick={() => setSort((s) => (s === 'newest' ? 'oldest' : 'newest'))} > - + {sort === 'newest' ? t('gallery.newestFirst' as TranslationKey) : t('gallery.oldestFirst' as TranslationKey)} @@ -229,11 +222,11 @@ export default function GalleryPage() {
{loading && items.length === 0 ? (
- +
) : items.length === 0 ? (
- +

{t('gallery.empty' as TranslationKey)}

{t('gallery.emptyHint' as TranslationKey)}

@@ -246,7 +239,7 @@ export default function GalleryPage() { {/* Sentinel for infinite scroll */}
{loading && ( - + )}
diff --git a/src/app/globals.css b/src/app/globals.css index 8531069..5f0e087 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -79,6 +79,8 @@ --sidebar-accent-foreground: oklch(0.216 0.006 56.043); --sidebar-border: oklch(0.923 0.003 48.717); --sidebar-ring: oklch(0.546 0.245 262.881); + --user-bubble: oklch(0.22 0.005 250); + --user-bubble-foreground: oklch(0.985 0.001 106.423); } .dark { @@ -113,6 +115,8 @@ --sidebar-accent-foreground: oklch(0.985 0.001 106.423); --sidebar-border: oklch(1 0 0 / 10%); --sidebar-ring: oklch(0.623 0.214 259.815); + --user-bubble: oklch(0.90 0.003 106); + --user-bubble-foreground: oklch(0.147 0.004 49.25); } @layer base { @@ -197,13 +201,3 @@ font-weight: 600; } } - -/* Global icon stroke weight — make Hugeicons strokes slightly thicker */ -svg.hugeicons, -svg.hugeicons path, -svg.hugeicons line, -svg.hugeicons circle, -svg.hugeicons polyline, -svg.hugeicons rect { - stroke-width: 2; -} diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 9b942b4..a4c2e33 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -1,8 +1,7 @@ "use client"; import { Suspense } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap } from "@phosphor-icons/react"; import { SettingsLayout } from "@/components/settings/SettingsLayout"; export default function SettingsPage() { @@ -10,7 +9,7 @@ export default function SettingsPage() { - + } > diff --git a/src/components/ai-elements/file-tree.tsx b/src/components/ai-elements/file-tree.tsx index b2b7015..c03ceff 100644 --- a/src/components/ai-elements/file-tree.tsx +++ b/src/components/ai-elements/file-tree.tsx @@ -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()} > - @@ -166,9 +165,9 @@ export const FileTreeFolder = ({ {isExpanded ? ( - + ) : ( - + )} {name} @@ -249,7 +248,7 @@ export const FileTreeFile = ({ {children ?? ( <> - {icon ?? } + {icon ?? } {name} {onAdd && ( @@ -259,7 +258,7 @@ export const FileTreeFile = ({ onClick={handleAdd} title="Add to chat" > - + )} diff --git a/src/components/ai-elements/message.tsx b/src/components/ai-elements/message.tsx index 44b1094..f4b1e8a 100644 --- a/src/components/ai-elements/message.tsx +++ b/src/components/ai-elements/message.tsx @@ -56,7 +56,7 @@ export const MessageContent = ({
+ ); case 'success': - return ; + return ; case 'error': - return ; + return ; } } @@ -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 (
- + {createElement(toolIcon, { size: 14, className: "shrink-0 text-muted-foreground" })} {label && ( {label} @@ -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" > - diff --git a/src/components/bridge/BridgeLayout.tsx b/src/components/bridge/BridgeLayout.tsx index fb5bdd4..f92c86f 100644 --- a/src/components/bridge/BridgeLayout.tsx +++ b/src/components/bridge/BridgeLayout.tsx @@ -1,9 +1,8 @@ "use client"; import { useState, useCallback, useSyncExternalStore } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import type { IconSvgElement } from "@hugeicons/react"; -import { Wifi01Icon, TelegramIcon, BubbleChatIcon, GameController01Icon, MessageMultiple02Icon } from "@hugeicons/core-free-icons"; +import { WifiHigh, TelegramLogo, ChatTeardrop, GameController, ChatsCircle } from "@phosphor-icons/react"; +import type { Icon } from "@phosphor-icons/react"; import { cn } from "@/lib/utils"; import { BridgeSection } from "./BridgeSection"; import { TelegramBridgeSection } from "./TelegramBridgeSection"; @@ -18,15 +17,15 @@ type Section = "bridge" | "telegram" | "feishu" | "discord" | "qq"; interface SidebarItem { id: Section; label: string; - icon: IconSvgElement; + icon: Icon; } const sidebarItems: SidebarItem[] = [ - { id: "bridge", label: "Bridge", icon: Wifi01Icon }, - { id: "telegram", label: "Telegram", icon: TelegramIcon }, - { id: "feishu", label: "Feishu", icon: BubbleChatIcon }, - { id: "discord", label: "Discord", icon: GameController01Icon }, - { id: "qq", label: "QQ", icon: MessageMultiple02Icon }, + { id: "bridge", label: "Bridge", icon: WifiHigh }, + { id: "telegram", label: "Telegram", icon: TelegramLogo }, + { id: "feishu", label: "Feishu", icon: ChatTeardrop }, + { id: "discord", label: "Discord", icon: GameController }, + { id: "qq", label: "QQ", icon: ChatsCircle }, ]; function getSectionFromHash(): Section { @@ -86,7 +85,7 @@ export function BridgeLayout() { : "text-muted-foreground hover:bg-accent/50 hover:text-foreground" )} > - + {t(bridgeLabelKeys[item.label])} ))} diff --git a/src/components/bridge/BridgeSection.tsx b/src/components/bridge/BridgeSection.tsx index 8bf4f51..795cdba 100644 --- a/src/components/bridge/BridgeSection.tsx +++ b/src/components/bridge/BridgeSection.tsx @@ -13,8 +13,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon, CheckmarkCircle02Icon, Alert02Icon, TelegramIcon, BubbleChatIcon, GameController01Icon, MessageMultiple02Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap, CheckCircle, Warning, TelegramLogo, ChatTeardrop, GameController, ChatsCircle } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; import type { ProviderModelGroup } from "@/types"; @@ -302,10 +301,7 @@ export function BridgeSection() { : "bg-muted text-muted-foreground" }`} > - + {isRunning ? : } {isRunning ? t("bridge.statusConnected") : t("bridge.statusDisconnected")} @@ -318,9 +314,9 @@ export function BridgeSection() { disabled={stopping} > {stopping ? ( - ) : null} {stopping ? t("bridge.stopping") : t("bridge.stop")} @@ -332,9 +328,9 @@ export function BridgeSection() { disabled={starting} > {starting ? ( - ) : null} {starting ? t("bridge.starting") : t("bridge.start")} @@ -358,9 +354,9 @@ export function BridgeSection() {
-

{t("bridge.telegramChannel")}

@@ -378,9 +374,9 @@ export function BridgeSection() {
-

{t("bridge.feishuChannel")}

@@ -398,9 +394,9 @@ export function BridgeSection() {
-

{t("bridge.discordChannel")}

@@ -418,9 +414,9 @@ export function BridgeSection() {
-

{t("bridge.qqChannel")}

diff --git a/src/components/bridge/DiscordBridgeSection.tsx b/src/components/bridge/DiscordBridgeSection.tsx index e02f5e2..26ce229 100644 --- a/src/components/bridge/DiscordBridgeSection.tsx +++ b/src/components/bridge/DiscordBridgeSection.tsx @@ -11,12 +11,7 @@ import { SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { - Loading02Icon, - CheckmarkCircle02Icon, - Alert02Icon, -} from "@hugeicons/core-free-icons"; +import { SpinnerGap, CheckCircle, Warning } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; interface DiscordBridgeSettings { @@ -198,9 +193,9 @@ export function DiscordBridgeSection() { disabled={verifying || !botToken} > {verifying ? ( - ) : null} {t("discord.verify")} @@ -215,10 +210,7 @@ export function DiscordBridgeSection() { : "bg-red-500/10 text-red-600 dark:text-red-400" }`} > - + {verifyResult.ok ? : } {verifyResult.message}
)} diff --git a/src/components/bridge/FeishuBridgeSection.tsx b/src/components/bridge/FeishuBridgeSection.tsx index 8e623a0..c8196da 100644 --- a/src/components/bridge/FeishuBridgeSection.tsx +++ b/src/components/bridge/FeishuBridgeSection.tsx @@ -11,12 +11,7 @@ import { SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { - Loading02Icon, - CheckmarkCircle02Icon, - Alert02Icon, -} from "@hugeicons/core-free-icons"; +import { SpinnerGap, CheckCircle, Warning } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; interface FeishuBridgeSettings { @@ -233,9 +228,9 @@ export function FeishuBridgeSection() { disabled={verifying || !appId} > {verifying ? ( - ) : null} {t("feishu.verify")} @@ -250,10 +245,7 @@ export function FeishuBridgeSection() { : "bg-red-500/10 text-red-600 dark:text-red-400" }`} > - + {verifyResult.ok ? : } {verifyResult.message}
)} diff --git a/src/components/bridge/QqBridgeSection.tsx b/src/components/bridge/QqBridgeSection.tsx index 58acec7..5c98e01 100644 --- a/src/components/bridge/QqBridgeSection.tsx +++ b/src/components/bridge/QqBridgeSection.tsx @@ -4,12 +4,7 @@ import { useState, useCallback, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { - Loading02Icon, - CheckmarkCircle02Icon, - Alert02Icon, -} from "@hugeicons/core-free-icons"; +import { SpinnerGap, CheckCircle, Warning } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; interface QqBridgeSettings { @@ -195,9 +190,9 @@ export function QqBridgeSection() { disabled={verifying || !appId} > {verifying ? ( - ) : null} {t("qq.verify")} @@ -212,10 +207,7 @@ export function QqBridgeSection() { : "bg-red-500/10 text-red-600 dark:text-red-400" }`} > - + {verifyResult.ok ? : } {verifyResult.message}
)} diff --git a/src/components/bridge/TelegramBridgeSection.tsx b/src/components/bridge/TelegramBridgeSection.tsx index bcd1d0d..bbf59ee 100644 --- a/src/components/bridge/TelegramBridgeSection.tsx +++ b/src/components/bridge/TelegramBridgeSection.tsx @@ -3,8 +3,7 @@ import { useState, useCallback, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon, CheckmarkCircle02Icon, Alert02Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap, CheckCircle, Warning } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; interface TelegramBridgeSettings { @@ -214,9 +213,9 @@ export function TelegramBridgeSection() { className="shrink-0" > {detecting ? ( - ) : null} {t("telegram.detectChatId")} @@ -243,9 +242,9 @@ export function TelegramBridgeSection() { disabled={verifying || !botToken} > {verifying ? ( - ) : null} {t("telegram.verify")} @@ -260,10 +259,7 @@ export function TelegramBridgeSection() { : "bg-red-500/10 text-red-600 dark:text-red-400" }`} > - + {verifyResult.ok ? : } {verifyResult.message}
)} diff --git a/src/components/chat/ChatPermissionSelector.tsx b/src/components/chat/ChatPermissionSelector.tsx index 703f7f5..25b650f 100644 --- a/src/components/chat/ChatPermissionSelector.tsx +++ b/src/components/chat/ChatPermissionSelector.tsx @@ -18,8 +18,7 @@ import { AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; -import { HugeiconsIcon } from '@hugeicons/react'; -import { LockIcon, SquareUnlock02Icon, ArrowDown01Icon } from '@hugeicons/core-free-icons'; +import { Lock, LockOpen, CaretDown } from '@phosphor-icons/react'; interface ChatPermissionSelectorProps { sessionId?: string; @@ -79,26 +78,24 @@ export function ChatPermissionSelector({ : 'bg-muted text-muted-foreground hover:bg-muted/80' }`} > - + {isFullAccess ? ( + + ) : ( + + )} {isFullAccess ? t('permission.fullAccess') : t('permission.default')} - + handleSelect('default')}> - + {t('permission.default')} handleSelect('full_access')}> - + {t('permission.fullAccess')} diff --git a/src/components/chat/CodeBlock.tsx b/src/components/chat/CodeBlock.tsx index 08e41f1..9ecfa45 100644 --- a/src/components/chat/CodeBlock.tsx +++ b/src/components/chat/CodeBlock.tsx @@ -1,24 +1,23 @@ 'use client'; -import { useState, useMemo, useRef } from 'react'; +import { useState, useMemo, useRef, createElement } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { useThemeFamily } from '@/lib/theme/context'; import { resolveCodeTheme, resolvePrismStyle } from '@/lib/theme/code-themes'; import { useTheme } from 'next-themes'; -import { HugeiconsIcon } from "@hugeicons/react"; -import type { IconSvgElement } from "@hugeicons/react"; +import type { Icon } from "@phosphor-icons/react"; import { - Copy01Icon, - Tick01Icon, - ArrowDown01Icon, - ArrowUp01Icon, - SourceCodeIcon, - CommandLineIcon, - CodeIcon, - File01Icon, - HashtagIcon, -} from "@hugeicons/core-free-icons"; + Copy, + Check, + CaretDown, + CaretUp, + FileCode, + Terminal, + Code, + File, + Hash, +} from "@phosphor-icons/react"; import { cn } from '@/lib/utils'; const COLLAPSE_THRESHOLD = 20; @@ -26,14 +25,14 @@ const VISIBLE_LINES = 10; const TERMINAL_LANGUAGES = new Set(['bash', 'sh', 'shell', 'terminal', 'zsh', 'console']); -function getLanguageIcon(language: string): IconSvgElement { +function getLanguageIcon(language: string): Icon { const lower = language.toLowerCase(); - if (TERMINAL_LANGUAGES.has(lower)) return CommandLineIcon; - if (['typescript', 'tsx', 'javascript', 'jsx'].includes(lower)) return CodeIcon; - if (['json', 'yaml', 'yml', 'toml', 'xml'].includes(lower)) return CodeIcon; - if (['python', 'ruby', 'go', 'rust', 'java', 'c', 'cpp'].includes(lower)) return HashtagIcon; - if (['css', 'scss', 'html'].includes(lower)) return File01Icon; - return SourceCodeIcon; + if (TERMINAL_LANGUAGES.has(lower)) return Terminal; + if (['typescript', 'tsx', 'javascript', 'jsx'].includes(lower)) return Code; + if (['json', 'yaml', 'yml', 'toml', 'xml'].includes(lower)) return Code; + if (['python', 'ruby', 'go', 'rust', 'java', 'c', 'cpp'].includes(lower)) return Hash; + if (['css', 'scss', 'html'].includes(lower)) return File; + return FileCode; } interface CodeBlockProps { @@ -126,7 +125,7 @@ export function CodeBlock({ } }; - const languageIconData = getLanguageIcon(language); + const languageIcon = getLanguageIcon(language); const theme = useCodeBlockTheme(isTerminal, isDark); @@ -145,10 +144,10 @@ export function CodeBlock({ : "bg-muted text-muted-foreground" )}>
- + ) })} {filename && ( {copied ? ( <> - + Copied ) : ( <> - + Copy )} @@ -198,12 +197,12 @@ export function CodeBlock({ > {copiedMarkdown ? ( <> - + Copied ) : ( <> - + Markdown )} @@ -269,12 +268,12 @@ export function CodeBlock({ > {expanded ? ( <> - + Collapse ) : ( <> - + Expand all {totalLines} lines )} diff --git a/src/components/chat/FileCard.tsx b/src/components/chat/FileCard.tsx index 03bed0d..7d289df 100644 --- a/src/components/chat/FileCard.tsx +++ b/src/components/chat/FileCard.tsx @@ -1,7 +1,6 @@ 'use client'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { File01Icon } from "@hugeicons/core-free-icons"; +import { File } from "@phosphor-icons/react"; import { formatFileSize } from '@/types'; interface FileCardProps { @@ -12,7 +11,7 @@ interface FileCardProps { export function FileCard({ name, size }: FileCardProps) { return (
- +
{name}
{formatFileSize(size)}
diff --git a/src/components/chat/FolderPicker.tsx b/src/components/chat/FolderPicker.tsx index af8d9fb..aad458f 100644 --- a/src/components/chat/FolderPicker.tsx +++ b/src/components/chat/FolderPicker.tsx @@ -1,8 +1,7 @@ 'use client'; import { useState, useEffect, useCallback } from 'react'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Folder01Icon, FolderOpenIcon, ArrowRight01Icon, ArrowUp01Icon } from "@hugeicons/core-free-icons"; +import { Folder, FolderOpen, ArrowRight, CaretUp } from "@phosphor-icons/react"; import { Button } from '@/components/ui/button'; import { Dialog, @@ -128,7 +127,7 @@ export function FolderPicker({ open, onOpenChange, onSelect, initialPath }: Fold disabled={!parentDir} className="shrink-0" > - + {drives.length > 0 && ( @@ -178,9 +177,9 @@ export function FolderPicker({ open, onOpenChange, onSelect, initialPath }: Fold className="flex w-full items-center gap-2 rounded-md px-3 py-1.5 text-sm hover:bg-accent transition-colors text-left" onClick={() => handleNavigate(dir.path)} > - + {dir.name} - + ))}
@@ -193,7 +192,7 @@ export function FolderPicker({ open, onOpenChange, onSelect, initialPath }: Fold {t('folderPicker.cancel')} diff --git a/src/components/chat/ImageGenCard.tsx b/src/components/chat/ImageGenCard.tsx index 5e26c31..359579f 100644 --- a/src/components/chat/ImageGenCard.tsx +++ b/src/components/chat/ImageGenCard.tsx @@ -1,8 +1,7 @@ 'use client'; import { useState, useCallback } from 'react'; -import { HugeiconsIcon } from '@hugeicons/react'; -import { Download04Icon, RepeatIcon, PaintBrush01Icon } from '@hugeicons/core-free-icons'; +import { DownloadSimple, ArrowsCounterClockwise, PaintBrush } from '@phosphor-icons/react'; import { cn } from '@/lib/utils'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -142,7 +141,7 @@ export function ImageGenCard({
{model && ( - + {model} )} @@ -165,7 +164,7 @@ export function ImageGenCard({ onClick={() => handleDownload(images[0], 0)} title={t('imageGen.download' as TranslationKey)} > - + {onRegenerate && ( )}
diff --git a/src/components/chat/ImageLightbox.tsx b/src/components/chat/ImageLightbox.tsx index c756e6d..5b93b39 100644 --- a/src/components/chat/ImageLightbox.tsx +++ b/src/components/chat/ImageLightbox.tsx @@ -1,8 +1,7 @@ 'use client'; import { useState, useCallback } from 'react'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { ArrowLeft01Icon, ArrowRight01Icon } from "@hugeicons/core-free-icons"; +import { ArrowLeft, ArrowRight } from "@phosphor-icons/react"; import { Dialog, DialogContent, @@ -66,14 +65,14 @@ export function ImageLightbox({ images, initialIndex, open, onOpenChange }: Imag onClick={goToPrev} className="absolute left-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white hover:bg-black/70 transition" > - +
{currentIndex + 1} / {images.length} diff --git a/src/components/chat/MessageInput.tsx b/src/components/chat/MessageInput.tsx index badad4e..43cd3ca 100644 --- a/src/components/chat/MessageInput.tsx +++ b/src/components/chat/MessageInput.tsx @@ -1,25 +1,25 @@ 'use client'; import { useRef, useState, useCallback, useEffect, type KeyboardEvent, type FormEvent } from 'react'; -import { HugeiconsIcon } from "@hugeicons/react"; import { - AtIcon, - HelpCircleIcon, - ArrowDown01Icon, - ArrowUp02Icon, - CommandLineIcon, - PlusSignIcon, - Cancel01Icon, - Delete02Icon, - Coins01Icon, - FileZipIcon, - Stethoscope02Icon, - FileEditIcon, - SearchList01Icon, - BrainIcon, - GlobalIcon, - StopIcon, -} from "@hugeicons/core-free-icons"; + At, + Question, + CaretDown, + ArrowUp, + Terminal, + Plus, + X, + Trash, + Coins, + FileZip, + Stethoscope, + NotePencil, + ListMagnifyingGlass, + Brain, + GlobeSimple, + Stop, +} from "@phosphor-icons/react"; +import type { Icon } from "@phosphor-icons/react"; import { cn } from '@/lib/utils'; import { useTranslation } from '@/hooks/useTranslation'; import type { TranslationKey } from '@/i18n'; @@ -115,7 +115,7 @@ interface PopoverItem { installedSource?: "agents" | "claude"; source?: "global" | "project" | "plugin" | "installed" | "sdk"; kind?: SkillKind; - icon?: typeof CommandLineIcon; + icon?: Icon; } interface CommandBadge { @@ -137,15 +137,15 @@ const COMMAND_PROMPTS: Record = { }; const BUILT_IN_COMMANDS: PopoverItem[] = [ - { label: 'help', value: '/help', description: 'Show available commands and tips', descriptionKey: 'messageInput.helpDesc', builtIn: true, immediate: true, icon: HelpCircleIcon }, - { label: 'clear', value: '/clear', description: 'Clear conversation history', descriptionKey: 'messageInput.clearDesc', builtIn: true, immediate: true, icon: Delete02Icon }, - { label: 'cost', value: '/cost', description: 'Show token usage statistics', descriptionKey: 'messageInput.costDesc', builtIn: true, immediate: true, icon: Coins01Icon }, - { label: 'compact', value: '/compact', description: 'Compress conversation context', descriptionKey: 'messageInput.compactDesc', builtIn: true, kind: 'sdk_command', icon: FileZipIcon }, - { label: 'doctor', value: '/doctor', description: 'Diagnose project health', descriptionKey: 'messageInput.doctorDesc', builtIn: true, kind: 'codepilot_command', icon: Stethoscope02Icon }, - { label: 'init', value: '/init', description: 'Initialize CLAUDE.md for project', descriptionKey: 'messageInput.initDesc', builtIn: true, kind: 'sdk_command', icon: FileEditIcon }, - { label: 'review', value: '/review', description: 'Review code quality', descriptionKey: 'messageInput.reviewDesc', builtIn: true, kind: 'sdk_command', icon: SearchList01Icon }, - { label: 'terminal-setup', value: '/terminal-setup', description: 'Configure terminal settings', descriptionKey: 'messageInput.terminalSetupDesc', builtIn: true, kind: 'codepilot_command', icon: CommandLineIcon }, - { label: 'memory', value: '/memory', description: 'Edit project memory file', descriptionKey: 'messageInput.memoryDesc', builtIn: true, kind: 'codepilot_command', icon: BrainIcon }, + { label: 'help', value: '/help', description: 'Show available commands and tips', descriptionKey: 'messageInput.helpDesc', builtIn: true, immediate: true, icon: Question }, + { label: 'clear', value: '/clear', description: 'Clear conversation history', descriptionKey: 'messageInput.clearDesc', builtIn: true, immediate: true, icon: Trash }, + { label: 'cost', value: '/cost', description: 'Show token usage statistics', descriptionKey: 'messageInput.costDesc', builtIn: true, immediate: true, icon: Coins }, + { label: 'compact', value: '/compact', description: 'Compress conversation context', descriptionKey: 'messageInput.compactDesc', builtIn: true, kind: 'sdk_command', icon: FileZip }, + { label: 'doctor', value: '/doctor', description: 'Diagnose project health', descriptionKey: 'messageInput.doctorDesc', builtIn: true, kind: 'codepilot_command', icon: Stethoscope }, + { label: 'init', value: '/init', description: 'Initialize CLAUDE.md for project', descriptionKey: 'messageInput.initDesc', builtIn: true, kind: 'sdk_command', icon: NotePencil }, + { label: 'review', value: '/review', description: 'Review code quality', descriptionKey: 'messageInput.reviewDesc', builtIn: true, kind: 'sdk_command', icon: ListMagnifyingGlass }, + { label: 'terminal-setup', value: '/terminal-setup', description: 'Configure terminal settings', descriptionKey: 'messageInput.terminalSetupDesc', builtIn: true, kind: 'codepilot_command', icon: Terminal }, + { label: 'memory', value: '/memory', description: 'Edit project memory file', descriptionKey: 'messageInput.memoryDesc', builtIn: true, kind: 'codepilot_command', icon: Brain }, ]; interface ModeOption { @@ -216,9 +216,9 @@ function FileAwareSubmitButton({ className="rounded-full" > {isStreaming ? ( - + ) : ( - + )} ); @@ -236,7 +236,7 @@ function AttachFileButton() { onClick={() => attachments.openFileDialog()} tooltip={t('messageInput.attachFiles')} > - + ); } @@ -356,7 +356,7 @@ function FileAttachmentsCapsules() { onClick={() => attachments.remove(file.id)} className="ml-0.5 rounded-full p-0.5 hover:bg-emerald-500/20 transition-colors" > - + ); @@ -1053,17 +1053,17 @@ export function MessageInput({ onMouseEnter={() => setSelectedIndex(idx)} > {popoverMode === 'file' ? ( - + ) : item.builtIn && item.icon ? ( - + (() => { const ItemIcon = item.icon; return ; })() ) : item.kind === 'agent_skill' ? ( - + ) : item.kind === 'slash_command' ? ( - + ) : !item.builtIn ? ( - + ) : ( - + )} {item.label} {(item.descriptionKey || item.description) && ( @@ -1170,7 +1170,7 @@ export function MessageInput({ {(aiSuggestions.length > 0 || aiSearchLoading) && ( <>
- + {t('messageInput.aiSuggested')} {aiSearchLoading && ( @@ -1243,7 +1243,7 @@ export function MessageInput({ onClick={() => setModelMenuOpen((prev) => !prev)} > {currentModelOption.label} - + {modelMenuOpen && ( @@ -1295,7 +1295,7 @@ export function MessageInput({ onClick={() => setEffortMenuOpen((prev) => !prev)} > {t(`messageInput.effort.${selectedEffort}` as TranslationKey)} - + {effortMenuOpen && ( diff --git a/src/components/chat/MessageItem.tsx b/src/components/chat/MessageItem.tsx index a4c1bd2..d1e6b15 100644 --- a/src/components/chat/MessageItem.tsx +++ b/src/components/chat/MessageItem.tsx @@ -8,8 +8,7 @@ import { MessageResponse, } from '@/components/ai-elements/message'; import { ToolActionsGroup } from '@/components/ai-elements/tool-actions-group'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Copy01Icon, Tick01Icon, ArrowDown01Icon, ArrowUp01Icon } from "@hugeicons/core-free-icons"; +import { Copy, Check, CaretDown, CaretUp } from "@phosphor-icons/react"; import { FileAttachmentDisplay } from './FileAttachmentDisplay'; import { ImageGenConfirmation } from './ImageGenConfirmation'; import { ImageGenCard } from './ImageGenCard'; @@ -288,9 +287,9 @@ function CopyButton({ text }: { text: string }) { title="Copy" > {copied ? ( - + ) : ( - + )} ); @@ -340,11 +339,6 @@ export const MessageItem = memo(function MessageItem({ message }: MessageItemPro return { files: [] as FileAttachment[], displayText: text }; }, [text, isUser]); - // Hide image-gen system notices — they exist in DB for Claude's context but shouldn't render - if (isUser && message.content.startsWith('[__IMAGE_GEN_NOTICE__')) { - return null; - } - useEffect(() => { if (isUser && contentRef.current) { setIsOverflowing(contentRef.current.scrollHeight > COLLAPSE_HEIGHT); @@ -361,6 +355,11 @@ export const MessageItem = memo(function MessageItem({ message }: MessageItemPro } }, [message.token_usage]); + // Hide image-gen system notices — they exist in DB for Claude's context but shouldn't render + if (isUser && message.content.startsWith('[__IMAGE_GEN_NOTICE__')) { + return null; + } + const timestamp = parseDBDate(message.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', @@ -413,12 +412,12 @@ export const MessageItem = memo(function MessageItem({ message }: MessageItemPro > {isExpanded ? ( <> - + 收起 ) : ( <> - + 展开 )} diff --git a/src/components/chat/ToolCallBlock.tsx b/src/components/chat/ToolCallBlock.tsx index 355a1f1..dba355a 100644 --- a/src/components/chat/ToolCallBlock.tsx +++ b/src/components/chat/ToolCallBlock.tsx @@ -1,20 +1,19 @@ 'use client'; -import { useState } from 'react'; -import { HugeiconsIcon } from "@hugeicons/react"; -import type { IconSvgElement } from "@hugeicons/react"; +import { useState, createElement } from 'react'; +import type { Icon } from "@phosphor-icons/react"; import { - File01Icon, - FileEditIcon, - CommandLineIcon, - Search01Icon, - Wrench01Icon, - ArrowDown01Icon, - ArrowRight01Icon, - Loading02Icon, - CheckmarkCircle02Icon, - CancelCircleIcon, -} from "@hugeicons/core-free-icons"; + File, + NotePencil, + Terminal, + MagnifyingGlass, + Wrench, + CaretDown, + CaretRight, + SpinnerGap, + CheckCircle, + XCircle, +} from "@phosphor-icons/react"; import { cn } from '@/lib/utils'; import { CodeBlock } from './CodeBlock'; @@ -44,13 +43,13 @@ function getToolCategory(name: string): 'read' | 'write' | 'bash' | 'search' | ' return 'other'; } -function getToolIcon(category: ReturnType): IconSvgElement { +function getToolIcon(category: ReturnType): 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; } } @@ -101,13 +100,13 @@ function StatusIndicator({ status }: { status: ToolStatus }) { return ( - + ); case 'success': - return ; + return ; case 'error': - return ; + return ; } } @@ -152,7 +151,7 @@ export function ToolCallBlock({ }: ToolCallBlockProps) { const [expanded, setExpanded] = useState(false); const category = getToolCategory(name); - const toolIconData = getToolIcon(category); + const toolIcon = getToolIcon(category); const summary = getToolSummary(name, input, category); const filePath = getFilePath(input); @@ -173,7 +172,7 @@ export function ToolCallBlock({ )} {!result && status === 'running' && (
- + Reading file...
)} @@ -224,7 +223,7 @@ export function ToolCallBlock({ )} {!result && status === 'running' && (
- + Executing...
)} @@ -303,18 +302,18 @@ export function ToolCallBlock({ )} > {expanded ? ( - + ) : ( - + )} - + ) })} {summary}
{duration !== undefined && ( diff --git a/src/components/gallery/GalleryDetail.tsx b/src/components/gallery/GalleryDetail.tsx index 68eaab6..87ba40d 100644 --- a/src/components/gallery/GalleryDetail.tsx +++ b/src/components/gallery/GalleryDetail.tsx @@ -1,17 +1,16 @@ 'use client'; -import { useState, useCallback, useEffect } from 'react'; +import { useState, useCallback, useRef } from 'react'; import { useRouter } from 'next/navigation'; -import { HugeiconsIcon } from '@hugeicons/react'; import { - Download04Icon, - Delete02Icon, - PaintBrush01Icon, - Message02Icon, - ArrowLeft01Icon, - ArrowRight01Icon, - FavouriteIcon, -} from '@hugeicons/core-free-icons'; + DownloadSimple, + Trash, + PaintBrush, + ChatCircle, + ArrowLeft, + ArrowRight, + Heart, +} from '@phosphor-icons/react'; import { cn, parseDBDate } from '@/lib/utils'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -69,11 +68,13 @@ export function GalleryDetail({ const [currentImageIndex, setCurrentImageIndex] = useState(0); const [confirmDelete, setConfirmDelete] = useState(false); - // Reset image index when item changes - useEffect(() => { + // Reset state when item changes (React-recommended ref pattern instead of useEffect+setState) + const prevItemId = useRef(item?.id); + if (item?.id !== prevItemId.current) { + prevItemId.current = item?.id; setCurrentImageIndex(0); setConfirmDelete(false); - }, [item?.id]); + } const handleDownload = useCallback(async () => { if (!item) return; @@ -144,14 +145,14 @@ export function GalleryDetail({ onClick={() => setCurrentImageIndex((i) => (i > 0 ? i - 1 : item.images.length - 1))} className="absolute left-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-1.5 text-white hover:bg-black/70 transition z-10" > - +
{currentImageIndex + 1} / {item.images.length} @@ -173,10 +174,9 @@ export function GalleryDetail({ : 'text-muted-foreground hover:text-red-500', )} > - {item.favorited ? t('gallery.removeFromFavorites' as TranslationKey) @@ -195,7 +195,7 @@ export function GalleryDetail({
{item.model && ( - + {item.model} )} @@ -250,12 +250,12 @@ export function GalleryDetail({ router.push(`/chat/${item.session_id}`); }} > - + {t('gallery.openChat' as TranslationKey)} )}
@@ -264,7 +264,7 @@ export function GalleryDetail({ size="sm" onClick={handleDelete} > - + {confirmDelete ? t('gallery.confirmDelete' as TranslationKey) : t('gallery.delete' as TranslationKey)} diff --git a/src/components/gallery/GalleryGrid.tsx b/src/components/gallery/GalleryGrid.tsx index cb3e26b..7b31ad5 100644 --- a/src/components/gallery/GalleryGrid.tsx +++ b/src/components/gallery/GalleryGrid.tsx @@ -1,7 +1,6 @@ 'use client'; -import { HugeiconsIcon } from '@hugeicons/react'; -import { PaintBrush01Icon, FavouriteIcon } from '@hugeicons/core-free-icons'; +import { PaintBrush, Heart } from '@phosphor-icons/react'; export interface GalleryItem { id: string; @@ -64,7 +63,7 @@ export function GalleryGrid({ items, onSelect }: GalleryGridProps) { /> ) : (
- +
)} {item.images.length > 1 && ( @@ -74,7 +73,7 @@ export function GalleryGrid({ items, onSelect }: GalleryGridProps) { )} {item.favorited && ( - + )}
diff --git a/src/components/gallery/TagManager.tsx b/src/components/gallery/TagManager.tsx index f5bbad9..a594077 100644 --- a/src/components/gallery/TagManager.tsx +++ b/src/components/gallery/TagManager.tsx @@ -1,8 +1,7 @@ 'use client'; import { useState, useCallback, useEffect } from 'react'; -import { HugeiconsIcon } from '@hugeicons/react'; -import { PlusSignIcon, Cancel01Icon } from '@hugeicons/core-free-icons'; +import { Plus, X } from '@phosphor-icons/react'; import { cn } from '@/lib/utils'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -94,7 +93,7 @@ export function TagManager({ }} className="ml-0.5 rounded-full p-0.5 opacity-0 group-hover:opacity-100 hover:bg-foreground/10 transition-opacity" > - + )} @@ -109,7 +108,7 @@ export function TagManager({ onClick={() => setAdding(true)} className="h-5 w-5" > - + )}
diff --git a/src/components/layout/ChatListPanel.tsx b/src/components/layout/ChatListPanel.tsx index 372cd78..6806725 100644 --- a/src/components/layout/ChatListPanel.tsx +++ b/src/components/layout/ChatListPanel.tsx @@ -3,19 +3,18 @@ import { usePathname, useRouter } from "next/navigation"; import Link from "next/link"; import { useEffect, useState, useCallback, useMemo, useRef } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; import { - Delete02Icon, - Search01Icon, - Notification02Icon, - FileImportIcon, - Folder01Icon, - ArrowDown01Icon, - ArrowRight01Icon, - PlusSignIcon, - FolderOpenIcon, - AiUserIcon, -} from "@hugeicons/core-free-icons"; + Trash, + MagnifyingGlass, + Bell, + FileArrowDown, + Folder, + CaretDown, + CaretRight, + Plus, + FolderOpen, + UserCircle, +} from "@phosphor-icons/react"; import { Columns2, X } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -392,7 +391,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { disabled={creatingChat} onClick={handleNewChat} > - + {t('chatList.newConversation')} @@ -403,7 +402,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { className="h-8 w-8 shrink-0" onClick={() => openFolderPicker()} > - + {t('chatList.addProjectFolder')} @@ -414,9 +413,9 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { {/* Search */}
- setImportDialogOpen(true)} > - + {t('chatList.importFromCli')}
@@ -487,7 +486,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { )} {needsApproval && ( - + )}
@@ -555,19 +554,21 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { } onMouseLeave={() => setHoveredFolder(null)} > - - + {isCollapsed ? ( + + ) : ( + + )} + {isCollapsed ? ( + + ) : ( + + )} {group.displayName} {workspacePath && group.workingDirectory === workspacePath && ( - + )} {/* New chat in project button (on hover) */} {group.workingDirectory !== "" && ( @@ -588,10 +589,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { ) } > - + New chat in {group.displayName} @@ -681,7 +679,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { "flex h-3.5 w-3.5 items-center justify-center rounded-full bg-amber-500/10 transition-opacity", isHovered && canSplit ? "opacity-0" : "opacity-100" )}> - + )} @@ -706,7 +704,7 @@ export function ChatListPanel({ open, width }: ChatListPanelProps) { onClick={(e) => handleDeleteSession(e, session.id)} disabled={isDeleting} > - +
diff --git a/src/components/layout/DocPreview.tsx b/src/components/layout/DocPreview.tsx index 86dc521..2507168 100644 --- a/src/components/layout/DocPreview.tsx +++ b/src/components/layout/DocPreview.tsx @@ -2,8 +2,7 @@ import { useState, useEffect, useMemo } from "react"; import { useTheme } from "next-themes"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Cancel01Icon, Copy01Icon, Tick01Icon, Loading02Icon } from "@hugeicons/core-free-icons"; +import { X, Copy, Check, SpinnerGap } from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; import { useThemeFamily } from "@/lib/theme/context"; @@ -132,15 +131,15 @@ export function DocPreview({
@@ -161,10 +160,7 @@ export function DocPreview({
{loading ? (
- +
) : error ? (
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 5e3df62..ae681cc 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -2,8 +2,7 @@ import { useTheme } from "next-themes"; import { useCallback, useSyncExternalStore } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Moon02Icon, Sun02Icon } from "@hugeicons/core-free-icons"; +import { Moon, Sun } from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import { Tooltip, @@ -29,9 +28,9 @@ export function Header() { className="h-7 w-7" > {theme === "dark" ? ( - + ) : ( - + )} Toggle theme diff --git a/src/components/layout/ImportSessionDialog.tsx b/src/components/layout/ImportSessionDialog.tsx index e65bc8d..22d7162 100644 --- a/src/components/layout/ImportSessionDialog.tsx +++ b/src/components/layout/ImportSessionDialog.tsx @@ -13,16 +13,15 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; -import { HugeiconsIcon } from "@hugeicons/react"; import { - Search01Icon, - Loading02Icon, - FolderOpenIcon, - GitBranchIcon, - ClockIcon, - FileImportIcon, - MessageAddIcon, -} from "@hugeicons/core-free-icons"; + MagnifyingGlass, + SpinnerGap, + FolderOpen, + GitBranch, + Clock, + FileArrowDown, + ChatCircleText, +} from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; import { cn, parseDBDate } from "@/lib/utils"; @@ -152,10 +151,7 @@ export function ImportSessionDialog({ - + {t('import.title')} @@ -166,9 +162,9 @@ export function ImportSessionDialog({ {/* Search */}
- {loading ? (
- + Scanning CLI sessions...
) : filteredSessions.length === 0 ? (
- +

{searchQuery ? t('import.noSessions') @@ -241,10 +231,7 @@ export function ImportSessionDialog({ variant="secondary" className="text-[10px] px-1.5 py-0 h-4 shrink-0" > - + {session.gitBranch} )} @@ -262,18 +249,12 @@ export function ImportSessionDialog({ > {isImporting ? ( <> - + {t('import.importing')} ) : ( <> - + {t('import.import')} )} @@ -286,24 +267,15 @@ export function ImportSessionDialog({ className="flex items-center gap-0.5 truncate" title={session.cwd} > - + {session.cwd} - + {t(totalMessages !== 1 ? 'import.messagesPlural' : 'import.messages', { n: totalMessages })} - + {(() => { const rel = formatRelativeTime(session.updatedAt); return typeof rel === 'string' ? rel : t(rel.key, rel.params); diff --git a/src/components/layout/InstallWizard.tsx b/src/components/layout/InstallWizard.tsx index b3c730f..4869aeb 100644 --- a/src/components/layout/InstallWizard.tsx +++ b/src/components/layout/InstallWizard.tsx @@ -11,16 +11,15 @@ import { } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import { HugeiconsIcon } from "@hugeicons/react"; import { - Tick01Icon, - Cancel01Icon, - MinusSignIcon, - Loading02Icon, - RecordIcon, - Copy01Icon, - Download04Icon, -} from "@hugeicons/core-free-icons"; + Check, + X, + Minus, + SpinnerGap, + Circle, + Copy, + DownloadSimple, +} from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; interface InstallProgress { @@ -68,15 +67,15 @@ function getInstallAPI() { function StepIcon({ status }: { status: string }) { switch (status) { case "success": - return ; + return ; case "running": - return ; + return ; case "failed": - return ; + return ; case "skipped": - return ; + return ; default: - return ; + return ; } } @@ -286,7 +285,7 @@ export function InstallWizard({ {/* Phase: checking */} {phase === "checking" && steps.length === 0 && (

- + {t('install.checkingPrereqs')}
)} @@ -318,7 +317,7 @@ export function InstallWizard({ } catch { /* clipboard not available */ } }} > - + {copiedBrew ? t('install.copied') : t('install.copy')}
@@ -360,7 +359,7 @@ export function InstallWizard({ {/* Phase: already-installed */} {phase === "already-installed" && (
- +

Already installed @@ -375,7 +374,7 @@ export function InstallWizard({ {/* Phase: success */} {phase === "success" && (

- +

{t('install.complete')} @@ -409,7 +408,7 @@ export function InstallWizard({ size="sm" onClick={handleCopyLogs} > - + {copied ? t('install.copied') : t('install.copyLogs')} )} @@ -422,7 +421,7 @@ export function InstallWizard({ )} {phase === "confirm" && !(prereqs && !prereqs.hasNode && !prereqs.hasHomebrew && prereqs.platform === "darwin") && ( )} diff --git a/src/components/layout/NavRail.tsx b/src/components/layout/NavRail.tsx index dc0c59e..247cb5a 100644 --- a/src/components/layout/NavRail.tsx +++ b/src/components/layout/NavRail.tsx @@ -2,15 +2,14 @@ import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; -import { HugeiconsIcon } from "@hugeicons/react"; import { - Message02Icon, - ZapIcon, - Plug01Icon, - Image01Icon, - Settings02Icon, - Wifi01Icon, -} from "@hugeicons/core-free-icons"; + ChatCircle, + Lightning, + Plug, + Image, + Gear, + WifiHigh, +} from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import { Tooltip, @@ -31,11 +30,11 @@ interface NavRailProps { } const navItems = [ - { href: "/chat", label: "Chats", icon: Message02Icon }, - { href: "/skills", label: "Skills", icon: ZapIcon }, - { href: "/mcp", label: "MCP", icon: Plug01Icon }, - { href: "/gallery", label: "Gallery", icon: Image01Icon }, - { href: "/bridge", label: "Bridge", icon: Wifi01Icon }, + { href: "/chat", label: "Chats", icon: ChatCircle }, + { href: "/skills", label: "Skills", icon: Lightning }, + { href: "/mcp", label: "MCP", icon: Plug }, + { href: "/gallery", label: "Gallery", icon: Image }, + { href: "/bridge", label: "Bridge", icon: WifiHigh }, ] as const; export function NavRail({ onToggleChatList, hasUpdate, readyToInstall, skipPermissionsActive }: NavRailProps) { @@ -83,7 +82,7 @@ export function NavRail({ onToggleChatList, hasUpdate, readyToInstall, skipPermi } }} > - + {t(navLabelKeys[item.label] ?? item.label as TranslationKey)} ) : ( @@ -98,7 +97,7 @@ export function NavRail({ onToggleChatList, hasUpdate, readyToInstall, skipPermi )} > - + {t(navLabelKeys[item.label] ?? item.label as TranslationKey)} @@ -139,7 +138,7 @@ export function NavRail({ onToggleChatList, hasUpdate, readyToInstall, skipPermi )} > - + {t('nav.settings')} diff --git a/src/components/layout/RightPanel.tsx b/src/components/layout/RightPanel.tsx index 16cee86..74ecd24 100644 --- a/src/components/layout/RightPanel.tsx +++ b/src/components/layout/RightPanel.tsx @@ -1,8 +1,7 @@ "use client"; import { useCallback } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { StructureFolderIcon, PanelRightCloseIcon } from "@hugeicons/core-free-icons"; +import { TreeStructure, SidebarSimple } from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import { Tooltip, @@ -58,7 +57,7 @@ export function RightPanel({ width }: RightPanelProps) { size="icon-sm" onClick={() => setPanelOpen(true)} > - + {t('panel.openPanel')} @@ -82,7 +81,7 @@ export function RightPanel({ width }: RightPanelProps) { size="icon-sm" onClick={() => setPanelOpen(false)} > - + {t('panel.closePanel')} diff --git a/src/components/plugins/McpManager.tsx b/src/components/plugins/McpManager.tsx index 4f9c3d1..02b8479 100644 --- a/src/components/plugins/McpManager.tsx +++ b/src/components/plugins/McpManager.tsx @@ -4,8 +4,7 @@ import { useState, useEffect, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { PlusSignIcon, ListViewIcon, CodeIcon, Loading02Icon, RefreshIcon, Wifi01Icon } from "@hugeicons/core-free-icons"; +import { Plus, List, Code, SpinnerGap, ArrowsClockwise, WifiHigh } from "@phosphor-icons/react"; import { McpServerList } from "@/components/plugins/McpServerList"; import { McpServerEditor } from "@/components/plugins/McpServerEditor"; import { ConfigEditor } from "@/components/plugins/ConfigEditor"; @@ -213,7 +212,7 @@ export function McpManager() {

@@ -227,11 +226,11 @@ export function McpManager() { setTab(v as "list" | "json")}> - + {t('mcp.listTab')} - + {t('mcp.jsonTab')} @@ -239,7 +238,7 @@ export function McpManager() { {loading ? (
- +

{t('mcp.loadingServers')}

) : ( @@ -283,7 +282,7 @@ export function McpManager() {
- +

{t('mcp.runtimeStatus' as TranslationKey)}

diff --git a/src/components/plugins/McpServerEditor.tsx b/src/components/plugins/McpServerEditor.tsx index 3ea7f12..1a01323 100644 --- a/src/components/plugins/McpServerEditor.tsx +++ b/src/components/plugins/McpServerEditor.tsx @@ -13,8 +13,7 @@ import { DialogTitle, DialogFooter, } from '@/components/ui/dialog'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { ServerStack01Icon, Wifi01Icon, GlobeIcon, CodeIcon } from "@hugeicons/core-free-icons"; +import { HardDrives, WifiHigh, Globe, Code } from "@phosphor-icons/react"; import { useTranslation } from '@/hooks/useTranslation'; import type { MCPServer } from '@/types'; @@ -237,7 +236,7 @@ export function McpServerEditor({ setError(null); }} > - + {t('mcp.jsonEditTab')}
@@ -268,15 +267,15 @@ export function McpServerEditor({ > - + stdio - + SSE - + HTTP diff --git a/src/components/plugins/McpServerList.tsx b/src/components/plugins/McpServerList.tsx index 9e456b0..eacfc34 100644 --- a/src/components/plugins/McpServerList.tsx +++ b/src/components/plugins/McpServerList.tsx @@ -3,8 +3,7 @@ import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Delete02Icon, PencilIcon, ServerStack01Icon, Wifi01Icon, GlobeIcon, RefreshIcon, Loading02Icon } from "@hugeicons/core-free-icons"; +import { Trash, PencilSimple, HardDrives, WifiHigh, Globe, ArrowsClockwise, SpinnerGap } from "@phosphor-icons/react"; import { useTranslation } from '@/hooks/useTranslation'; import type { TranslationKey } from '@/i18n'; import type { MCPServer } from '@/types'; @@ -28,11 +27,11 @@ function getServerTypeInfo(server: MCPServer) { const type = server.type || 'stdio'; switch (type) { case 'sse': - return { label: 'SSE', icon: Wifi01Icon, color: 'text-primary' }; + return { label: 'SSE', icon: WifiHigh, color: 'text-primary' }; case 'http': - return { label: 'HTTP', icon: GlobeIcon, color: 'text-green-500' }; + return { label: 'HTTP', icon: Globe, color: 'text-green-500' }; default: - return { label: 'stdio', icon: ServerStack01Icon, color: 'text-muted-foreground' }; + return { label: 'stdio', icon: HardDrives, color: 'text-muted-foreground' }; } } @@ -102,7 +101,7 @@ export function McpServerList({ servers, onEdit, onDelete, runtimeStatus, active if (entries.length === 0) { return (
- +

{t('mcp.noServers')}

{t('mcp.noServersDesc')} @@ -133,7 +132,7 @@ export function McpServerList({ servers, onEdit, onDelete, runtimeStatus, active

- + {name} {typeInfo.label} @@ -171,9 +170,9 @@ export function McpServerList({ servers, onEdit, onDelete, runtimeStatus, active title={t('mcp.reconnect' as TranslationKey)} > {isReconnecting ? ( - + ) : ( - + )} )} @@ -187,7 +186,7 @@ export function McpServerList({ servers, onEdit, onDelete, runtimeStatus, active onClick={() => handleToggle(name, true)} > {isToggling ? ( - + ) : null} {t('mcp.enable' as TranslationKey)} @@ -198,7 +197,7 @@ export function McpServerList({ servers, onEdit, onDelete, runtimeStatus, active className="h-8 w-8" onClick={() => onEdit(name, server)} > - +
diff --git a/src/components/plugins/PluginCard.tsx b/src/components/plugins/PluginCard.tsx index 040659c..d847966 100644 --- a/src/components/plugins/PluginCard.tsx +++ b/src/components/plugins/PluginCard.tsx @@ -4,8 +4,7 @@ import { useState } from 'react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { ArrowDown01Icon, ArrowUp01Icon, ZapIcon } from "@hugeicons/core-free-icons"; +import { CaretDown, CaretUp, Lightning } from "@phosphor-icons/react"; export interface SkillInfo { name: string; @@ -37,7 +36,7 @@ export function PluginCard({ plugin, onSelect }: PluginCardProps) { >
- + /{displayName} @@ -62,9 +61,9 @@ export function PluginCard({ plugin, onSelect }: PluginCardProps) { }} > {expanded ? ( - + ) : ( - + )} diff --git a/src/components/plugins/PluginDetail.tsx b/src/components/plugins/PluginDetail.tsx index b584105..772cefe 100644 --- a/src/components/plugins/PluginDetail.tsx +++ b/src/components/plugins/PluginDetail.tsx @@ -3,8 +3,7 @@ import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Separator } from '@/components/ui/separator'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Cancel01Icon, ZapIcon } from "@hugeicons/core-free-icons"; +import { X, Lightning } from "@phosphor-icons/react"; import type { SkillInfo } from './PluginCard'; interface PluginDetailProps { @@ -23,7 +22,7 @@ export function PluginDetail({ plugin, onClose }: PluginDetailProps) {
- +

/{displayName}

{isProject ? 'Project' : 'Global'} @@ -34,7 +33,7 @@ export function PluginDetail({ plugin, onClose }: PluginDetailProps) {

diff --git a/src/components/plugins/PluginList.tsx b/src/components/plugins/PluginList.tsx index ca6e425..c8ab96c 100644 --- a/src/components/plugins/PluginList.tsx +++ b/src/components/plugins/PluginList.tsx @@ -4,8 +4,7 @@ import { useState } from 'react'; import { Input } from '@/components/ui/input'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { PluginCard, type SkillInfo } from './PluginCard'; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Search01Icon, GlobeIcon, FolderOpenIcon, Plug01Icon } from "@hugeicons/core-free-icons"; +import { MagnifyingGlass, Globe, FolderOpen, Plug } from "@phosphor-icons/react"; interface PluginListProps { plugins: SkillInfo[]; @@ -31,7 +30,7 @@ export function PluginList({ plugins, onSelect }: PluginListProps) { return (
- + setSearch(e.target.value)} @@ -49,16 +48,16 @@ export function PluginList({ plugins, onSelect }: PluginListProps) { All ({plugins.length}) - + Global ({globalCount}) - + Project ({projectCount}) {pluginCount > 0 && ( - + Plugin ({pluginCount}) )} diff --git a/src/components/project/FilePreview.tsx b/src/components/project/FilePreview.tsx index 7c6a22e..672fd10 100644 --- a/src/components/project/FilePreview.tsx +++ b/src/components/project/FilePreview.tsx @@ -1,8 +1,7 @@ "use client"; import { useState, useEffect } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { ArrowLeft01Icon, Copy01Icon, Tick01Icon, Loading02Icon } from "@hugeicons/core-free-icons"; +import { ArrowLeft, Copy, Check, SpinnerGap } from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { ScrollArea } from "@/components/ui/scroll-area"; @@ -78,7 +77,7 @@ export function FilePreview({ filePath, onBack }: FilePreviewProps) { {/* Header */}
@@ -88,9 +87,9 @@ export function FilePreview({ filePath, onBack }: FilePreviewProps) {
@@ -112,7 +111,7 @@ export function FilePreview({ filePath, onBack }: FilePreviewProps) { {loading ? (
- +
) : error ? (
diff --git a/src/components/project/FileTree.tsx b/src/components/project/FileTree.tsx index 8fc2a1c..adc0cb0 100644 --- a/src/components/project/FileTree.tsx +++ b/src/components/project/FileTree.tsx @@ -1,8 +1,7 @@ "use client"; import { useState, useEffect, useCallback, useRef } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { RefreshIcon, Search01Icon, SourceCodeIcon, CodeIcon, File01Icon } from "@hugeicons/core-free-icons"; +import { ArrowsClockwise, MagnifyingGlass, FileCode, Code, File } from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { cn } from "@/lib/utils"; @@ -43,19 +42,19 @@ function getFileIcon(extension?: string): ReactNode { case "lua": case "php": case "zig": - return ; + return ; case "json": case "yaml": case "yml": case "toml": - return ; + return ; case "md": case "mdx": case "txt": case "csv": - return ; + return ; default: - return ; + return ; } } @@ -188,7 +187,7 @@ export function FileTree({ workingDirectory, onFileSelect, onFileAdd }: FileTree {/* Search + Refresh */}
- + - + {t('fileTree.refresh')}
@@ -212,7 +211,7 @@ export function FileTree({ workingDirectory, onFileSelect, onFileAdd }: FileTree
{loading && tree.length === 0 ? (
- +
) : tree.length === 0 ? (

diff --git a/src/components/project/TaskCard.tsx b/src/components/project/TaskCard.tsx index 2234e80..58248f9 100644 --- a/src/components/project/TaskCard.tsx +++ b/src/components/project/TaskCard.tsx @@ -1,8 +1,7 @@ "use client"; import { useState, useRef, useEffect } from "react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Delete02Icon } from "@hugeicons/core-free-icons"; +import { Trash } from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import { cn, parseDBDate } from "@/lib/utils"; import type { TaskItem, TaskStatus } from "@/types"; @@ -133,7 +132,7 @@ export function TaskCard({ task, onUpdate, onDelete }: TaskCardProps) { onClick={() => onDelete(task.id)} className="shrink-0 text-muted-foreground hover:text-destructive" > - + Delete task )} diff --git a/src/components/settings/AppearanceSection.tsx b/src/components/settings/AppearanceSection.tsx index 6081438..84b5df4 100644 --- a/src/components/settings/AppearanceSection.tsx +++ b/src/components/settings/AppearanceSection.tsx @@ -9,8 +9,7 @@ import { resolveShikiThemes, } from "@/lib/theme/code-themes"; import { useTranslation } from "@/hooks/useTranslation"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Sun02Icon, Moon02Icon, ComputerDesk01Icon } from "@hugeicons/core-free-icons"; +import { Sun, Moon, Desktop } from "@phosphor-icons/react"; import { Select, SelectContent, @@ -23,9 +22,9 @@ import { cn } from "@/lib/utils"; // ── Theme Mode Pill Selector ──────────────────────────────────────── const MODE_OPTIONS = [ - { value: "light", icon: Sun02Icon, labelKey: "settings.modeLight" as const }, - { value: "dark", icon: Moon02Icon, labelKey: "settings.modeDark" as const }, - { value: "system", icon: ComputerDesk01Icon, labelKey: "settings.modeSystem" as const }, + { value: "light", icon: Sun, labelKey: "settings.modeLight" as const }, + { value: "dark", icon: Moon, labelKey: "settings.modeDark" as const }, + { value: "system", icon: Desktop, labelKey: "settings.modeSystem" as const }, ] as const; function ThemeModePills({ @@ -54,7 +53,7 @@ function ThemeModePills({ : "text-muted-foreground hover:bg-accent hover:text-accent-foreground" )} > - + {t(opt.labelKey)} ); diff --git a/src/components/settings/AssistantWorkspaceSection.tsx b/src/components/settings/AssistantWorkspaceSection.tsx index 1903e75..dffb4f1 100644 --- a/src/components/settings/AssistantWorkspaceSection.tsx +++ b/src/components/settings/AssistantWorkspaceSection.tsx @@ -14,8 +14,7 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon, CheckmarkCircle02Icon, Cancel01Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap, CheckCircle, X } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; import type { WorkspaceInspectResult } from "@/types"; @@ -426,7 +425,7 @@ export function AssistantWorkspaceSection() { if (loading) { return (

- +
); } @@ -445,11 +444,11 @@ export function AssistantWorkspaceSection() { const renderValidationIcon = () => { switch (pathValidation) { case 'checking': - return ; + return ; case 'valid': - return ; + return ; case 'invalid': - return ; + return ; default: return null; } @@ -489,7 +488,7 @@ export function AssistantWorkspaceSection() { > {inspecting ? ( <> - + {t('assistant.inspecting')} ) : ( @@ -538,7 +537,7 @@ export function AssistantWorkspaceSection() { disabled={creatingSession} > {creatingSession ? ( - + ) : workspace.state?.onboardingComplete ? t('assistant.redoOnboarding') : t('assistant.startOnboarding') @@ -575,7 +574,7 @@ export function AssistantWorkspaceSection() { disabled={creatingSession} > {creatingSession ? ( - + ) : ( t('assistant.startCheckIn') )} @@ -639,7 +638,7 @@ export function AssistantWorkspaceSection() { > {refreshingDocs ? ( <> - + {t('assistant.refreshingDocs')} ) : ( @@ -714,7 +713,7 @@ export function AssistantWorkspaceSection() { > {reindexing ? ( <> - + {t('assistant.indexReindexing')} ) : ( @@ -738,7 +737,7 @@ export function AssistantWorkspaceSection() { > {archiving ? ( <> - + {t('assistant.organizeArchiving')} ) : ( @@ -767,7 +766,7 @@ export function AssistantWorkspaceSection() { executeSave(true)} disabled={initializing}> {initializing ? ( <> - + {t('assistant.initializing')} ) : ( @@ -792,7 +791,7 @@ export function AssistantWorkspaceSection() { executeSave(true)} disabled={initializing}> {initializing ? ( <> - + {t('assistant.initializing')} ) : ( @@ -820,7 +819,7 @@ export function AssistantWorkspaceSection() { executeSave(true)} disabled={initializing}> {initializing ? ( <> - + {t('assistant.initializing')} ) : ( @@ -868,7 +867,7 @@ export function AssistantWorkspaceSection() { {saveSuccess && ( @@ -291,18 +290,18 @@ export function CliSettingsSection() {
{saveSuccess && ( diff --git a/src/components/settings/GeneralSection.tsx b/src/components/settings/GeneralSection.tsx index c7c630d..a7e96a0 100644 --- a/src/components/settings/GeneralSection.tsx +++ b/src/components/settings/GeneralSection.tsx @@ -13,8 +13,7 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { ReloadIcon, Loading02Icon } from "@hugeicons/core-free-icons"; +import { ArrowClockwise, SpinnerGap } from "@phosphor-icons/react"; import { useUpdate } from "@/hooks/useUpdate"; import { useTranslation } from "@/hooks/useTranslation"; import { SUPPORTED_LOCALES, type Locale } from "@/i18n"; @@ -62,9 +61,9 @@ function UpdateCard() { className="gap-2" > {checking ? ( - + ) : ( - + )} {checking ? t('settings.checking') : t('settings.checkForUpdates')} diff --git a/src/components/settings/ProviderForm.tsx b/src/components/settings/ProviderForm.tsx index 8c412a9..71b1699 100644 --- a/src/components/settings/ProviderForm.tsx +++ b/src/components/settings/ProviderForm.tsx @@ -20,8 +20,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon, ArrowDown01Icon, ArrowUp01Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap, CaretDown, CaretUp } from "@phosphor-icons/react"; import type { ApiProvider } from "@/types"; import { useTranslation } from "@/hooks/useTranslation"; @@ -284,10 +283,7 @@ export function ProviderForm({ className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors" onClick={() => setShowAdvanced(!showAdvanced)} > - + {showAdvanced ? : } {t('provider.advancedOptions')} @@ -380,7 +376,7 @@ export function ProviderForm({ diff --git a/src/components/settings/ProviderManager.tsx b/src/components/settings/ProviderManager.tsx index 6ace6d4..c4e927d 100644 --- a/src/components/settings/ProviderManager.tsx +++ b/src/components/settings/ProviderManager.tsx @@ -31,15 +31,14 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { HugeiconsIcon } from "@hugeicons/react"; import { - Loading02Icon, - PencilEdit01Icon, - ArrowDown01Icon, - ArrowUp01Icon, - ServerStack01Icon, - Settings02Icon, -} from "@hugeicons/core-free-icons"; + SpinnerGap, + PencilSimple, + CaretDown, + CaretUp, + HardDrives, + Gear, +} from "@phosphor-icons/react"; import { ProviderForm } from "./ProviderForm"; import type { ProviderFormData } from "./ProviderForm"; import type { ApiProvider } from "@/types"; @@ -81,7 +80,7 @@ function getProviderIcon(name: string, baseUrl: string): ReactNode { if (lower.includes("aws")) return ; if (lower.includes("anthropic") || url.includes("anthropic")) return ; - return ; + return ; } // --------------------------------------------------------------------------- @@ -113,7 +112,7 @@ const QUICK_PRESETS: QuickPreset[] = [ name: "Custom API (OpenAI-compatible)", description: "OpenAI-compatible custom endpoint", descriptionZh: "自定义 OpenAI 兼容 API 端点", - icon: , + icon: , provider_type: "custom", protocol: "openai-compatible", base_url: "", @@ -284,7 +283,7 @@ const QUICK_PRESETS: QuickPreset[] = [ name: "LiteLLM", description: "LiteLLM proxy — local or remote", descriptionZh: "LiteLLM 代理 — 本地或远程", - icon: , + icon: , provider_type: "anthropic", protocol: "anthropic", base_url: "http://localhost:4000", @@ -686,10 +685,7 @@ function PresetConnectDialog({ className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors" onClick={() => setShowAdvanced(!showAdvanced)} > - + {showAdvanced ? : } {t('provider.advancedOptions')} {showAdvanced && ( @@ -793,7 +789,7 @@ function PresetConnectDialog({ {t('common.cancel')} @@ -983,7 +979,7 @@ export function ProviderManager() { {/* Loading */} {loading && (
- +

{t('common.loading')}

)} @@ -1046,7 +1042,7 @@ export function ProviderManager() { title="Edit" onClick={() => handleEdit(provider)} > - + ))} diff --git a/src/components/skills/CreateSkillDialog.tsx b/src/components/skills/CreateSkillDialog.tsx index 56a16d4..ecef2a6 100644 --- a/src/components/skills/CreateSkillDialog.tsx +++ b/src/components/skills/CreateSkillDialog.tsx @@ -12,8 +12,7 @@ import { import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon, GlobeIcon, FolderOpenIcon } from "@hugeicons/core-free-icons"; +import { SpinnerGap, Globe, FolderOpen } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; import { cn } from "@/lib/utils"; @@ -138,7 +137,7 @@ export function CreateSkillDialog({ : "border-border hover:bg-accent" )} > - + {t('skills.project')}
@@ -196,7 +195,7 @@ export function CreateSkillDialog({ {t('common.cancel')} diff --git a/src/components/skills/InstallProgressDialog.tsx b/src/components/skills/InstallProgressDialog.tsx index ef13cf3..bea1267 100644 --- a/src/components/skills/InstallProgressDialog.tsx +++ b/src/components/skills/InstallProgressDialog.tsx @@ -9,8 +9,7 @@ import { DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Loading02Icon, CheckmarkCircle02Icon, Cancel01Icon } from "@hugeicons/core-free-icons"; +import { SpinnerGap, CheckCircle, X } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; interface InstallProgressDialogProps { @@ -140,13 +139,13 @@ export function InstallProgressDialog({ {phase === "running" && ( - + )} {phase === "success" && ( - + )} {phase === "error" && ( - + )} {phase === "running" ? t('skills.installing') diff --git a/src/components/skills/MarketplaceBrowser.tsx b/src/components/skills/MarketplaceBrowser.tsx index a586d89..ccec0f0 100644 --- a/src/components/skills/MarketplaceBrowser.tsx +++ b/src/components/skills/MarketplaceBrowser.tsx @@ -2,8 +2,7 @@ import { useState, useEffect, useCallback, useRef } from "react"; import { Input } from "@/components/ui/input"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { Search01Icon, Loading02Icon, Store01Icon } from "@hugeicons/core-free-icons"; +import { MagnifyingGlass, SpinnerGap, Storefront } from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; import { MarketplaceSkillCard } from "./MarketplaceSkillCard"; import { MarketplaceSkillDetail } from "./MarketplaceSkillDetail"; @@ -72,9 +71,9 @@ export function MarketplaceBrowser({ onInstalled }: MarketplaceBrowserProps) {
- {loading && results.length === 0 && (
- +
)} {error && ( @@ -99,7 +98,7 @@ export function MarketplaceBrowser({ onInstalled }: MarketplaceBrowserProps) { )} {!loading && !error && results.length === 0 && (
- +

{t('skills.searchNoResults')}

)} @@ -125,7 +124,7 @@ export function MarketplaceBrowser({ onInstalled }: MarketplaceBrowserProps) { /> ) : (
- +

{t('skills.marketplaceHint')}

{t('skills.marketplaceHintDesc')}

diff --git a/src/components/skills/MarketplaceSkillCard.tsx b/src/components/skills/MarketplaceSkillCard.tsx index 4cae37c..688275b 100644 --- a/src/components/skills/MarketplaceSkillCard.tsx +++ b/src/components/skills/MarketplaceSkillCard.tsx @@ -1,7 +1,6 @@ "use client"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { ZapIcon, Download04Icon, CheckmarkCircle02Icon } from "@hugeicons/core-free-icons"; +import { Lightning, DownloadSimple, CheckCircle } from "@phosphor-icons/react"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { useTranslation } from "@/hooks/useTranslation"; @@ -30,7 +29,7 @@ export function MarketplaceSkillCard({ )} onClick={onSelect} > - +
{skill.name} @@ -39,7 +38,7 @@ export function MarketplaceSkillCard({ variant="outline" className="text-[10px] px-1.5 py-0 border-green-500/40 text-green-600 dark:text-green-400" > - + {t('skills.installed')} ) : null} @@ -48,7 +47,7 @@ export function MarketplaceSkillCard({ {skill.source} {skill.installs > 0 && ( - + {skill.installs.toLocaleString()} )} diff --git a/src/components/skills/MarketplaceSkillDetail.tsx b/src/components/skills/MarketplaceSkillDetail.tsx index 6f19ec5..0ce75fa 100644 --- a/src/components/skills/MarketplaceSkillDetail.tsx +++ b/src/components/skills/MarketplaceSkillDetail.tsx @@ -3,15 +3,14 @@ import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; -import { HugeiconsIcon } from "@hugeicons/react"; import { - Download04Icon, - Delete02Icon, - CheckmarkCircle02Icon, - LinkSquare01Icon, - ZapIcon, - Loading02Icon, -} from "@hugeicons/core-free-icons"; + DownloadSimple, + Trash, + CheckCircle, + ArrowSquareOut, + Lightning, + SpinnerGap, +} from "@phosphor-icons/react"; import { useTranslation } from "@/hooks/useTranslation"; import { InstallProgressDialog } from "./InstallProgressDialog"; import ReactMarkdown from "react-markdown"; @@ -85,7 +84,7 @@ export function MarketplaceSkillDetail({
- +
@@ -95,7 +94,7 @@ export function MarketplaceSkillDetail({ variant="outline" className="text-[10px] px-1.5 py-0 border-green-500/40 text-green-600 dark:text-green-400 shrink-0" > - + {t('skills.installed')} )} @@ -109,12 +108,12 @@ export function MarketplaceSkillDetail({ rel="noopener noreferrer" className="text-muted-foreground hover:text-foreground transition-colors shrink-0" > - + )} {skill.installs > 0 && ( - + {skill.installs.toLocaleString()} )} @@ -128,12 +127,12 @@ export function MarketplaceSkillDetail({ className="gap-1.5" onClick={handleUninstall} > - + {t('skills.uninstall')} ) : ( )} @@ -145,7 +144,7 @@ export function MarketplaceSkillDetail({
{readmeLoading ? (
- +
) : displayContent ? (
diff --git a/src/components/skills/SkillEditor.tsx b/src/components/skills/SkillEditor.tsx index 2362f05..46bc735 100644 --- a/src/components/skills/SkillEditor.tsx +++ b/src/components/skills/SkillEditor.tsx @@ -4,17 +4,16 @@ import { useState, useCallback, useRef, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Textarea } from "@/components/ui/textarea"; -import { HugeiconsIcon } from "@hugeicons/react"; import { - FloppyDiskIcon, - Delete02Icon, - EyeIcon, - Edit02Icon, - GlobeIcon, - FolderOpenIcon, - Loading02Icon, - LayoutTwoColumnIcon, -} from "@hugeicons/core-free-icons"; + FloppyDisk, + Trash, + Eye, + PencilLine, + Globe, + FolderOpen, + SpinnerGap, + Columns, +} from "@phosphor-icons/react"; import { Tooltip, TooltipContent, @@ -131,11 +130,11 @@ export function SkillEditor({ skill, onSave, onDelete }: SkillEditorProps) { )} > {skill.source === "global" ? ( - + ) : skill.source === "installed" ? ( - + ) : ( - + )} {skill.source === "installed" && skill.installedSource ? `installed:${skill.installedSource}` @@ -152,7 +151,7 @@ export function SkillEditor({ skill, onSave, onDelete }: SkillEditorProps) { size="icon-xs" onClick={() => setViewMode("edit")} > - + {t('skills.edit')} @@ -164,7 +163,7 @@ export function SkillEditor({ skill, onSave, onDelete }: SkillEditorProps) { size="icon-xs" onClick={() => setViewMode("preview")} > - + {t('skills.preview')} @@ -176,7 +175,7 @@ export function SkillEditor({ skill, onSave, onDelete }: SkillEditorProps) { size="icon-xs" onClick={() => setViewMode("split")} > - + {t('skills.splitView')} @@ -192,9 +191,9 @@ export function SkillEditor({ skill, onSave, onDelete }: SkillEditorProps) { className="gap-1" > {saving ? ( - + ) : ( - + )} {saving ? "Saving" : saved ? t('skills.saved') : t('skills.save')} @@ -205,7 +204,7 @@ export function SkillEditor({ skill, onSave, onDelete }: SkillEditorProps) { size="icon-xs" onClick={handleDelete} > - +
diff --git a/src/components/skills/SkillListItem.tsx b/src/components/skills/SkillListItem.tsx index 45232e4..98f4d13 100644 --- a/src/components/skills/SkillListItem.tsx +++ b/src/components/skills/SkillListItem.tsx @@ -1,7 +1,6 @@ "use client"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { ZapIcon, Delete02Icon, GlobeIcon, Plug01Icon, Download04Icon } from "@hugeicons/core-free-icons"; +import { Lightning, Trash } from "@phosphor-icons/react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { @@ -66,7 +65,7 @@ export function SkillListItem({ setConfirmDelete(false); }} > - +
/{skill.name}

@@ -82,7 +81,7 @@ export function SkillListItem({ className="shrink-0" onClick={handleDelete} > - + diff --git a/src/components/skills/SkillsManager.tsx b/src/components/skills/SkillsManager.tsx index b2ad687..cdf4b58 100644 --- a/src/components/skills/SkillsManager.tsx +++ b/src/components/skills/SkillsManager.tsx @@ -5,8 +5,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { usePanel } from "@/hooks/usePanel"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { PlusSignIcon, Search01Icon, ZapIcon, Loading02Icon } from "@hugeicons/core-free-icons"; +import { Plus, MagnifyingGlass, Lightning, SpinnerGap } from "@phosphor-icons/react"; import { SkillListItem } from "./SkillListItem"; import { SkillEditor } from "./SkillEditor"; import { CreateSkillDialog } from "./CreateSkillDialog"; @@ -143,7 +142,7 @@ export function SkillsManager() { if (loading) { return (

- + {t('skills.loadingSkills')} @@ -184,7 +183,7 @@ export function SkillsManager() {
{viewTab === "local" && ( )} @@ -199,7 +198,7 @@ export function SkillsManager() {
- + - +

{search ? t('skills.noSkillsFound') : t('skills.noSkillsFound')}

@@ -283,7 +282,7 @@ export function SkillsManager() { onClick={() => setShowCreate(true)} className="gap-1" > - + Create one )} @@ -304,7 +303,7 @@ export function SkillsManager() { /> ) : (
- +

{t('skills.noSelected')}

@@ -317,7 +316,7 @@ export function SkillsManager() { onClick={() => setShowCreate(true)} className="gap-1" > - + {t('skills.newSkill')}